1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 1999-2004  Eidos Interactive
4 	Copyright (C) 2005-2020  Warzone 2100 Project
5 
6 	Warzone 2100 is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	Warzone 2100 is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with Warzone 2100; if not, write to the Free Software
18 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /** \file
21  *  Render routines for 3D coloured and shaded transparency rendering.
22  */
23 
24 #include <unordered_map>
25 #include <string.h>
26 
27 #include "lib/framework/frame.h"
28 #include "lib/ivis_opengl/ivisdef.h"
29 #include "lib/ivis_opengl/imd.h"
30 #include "lib/ivis_opengl/piefunc.h"
31 #include "lib/ivis_opengl/tex.h"
32 #include "lib/ivis_opengl/piedef.h"
33 #include "lib/ivis_opengl/piestate.h"
34 #include "lib/ivis_opengl/piepalette.h"
35 #include "lib/ivis_opengl/pieclip.h"
36 #include "lib/ivis_opengl/pieblitfunc.h"
37 #include "piematrix.h"
38 #include "screen.h"
39 
40 #include <string.h>
41 #include <vector>
42 #include <algorithm>
43 
44 #include <glm/glm.hpp>
45 #include <glm/gtc/matrix_transform.hpp>
46 
47 #define BUFFER_OFFSET(i) (reinterpret_cast<char *>(i))
48 #define SHADOW_END_DISTANCE (8000*8000) // Keep in sync with lighting.c:FOG_END
49 
50 /*
51  *	Local Variables
52  */
53 
54 static size_t pieCount = 0;
55 static size_t polyCount = 0;
56 static bool shadows = false;
57 static gfx_api::gfxFloat lighting0[LIGHT_MAX][4];
58 
59 /*
60  *	Source
61  */
62 
pie_InitLighting()63 void pie_InitLighting()
64 {
65 	// set scene color, ambient, diffuse and specular light intensities of sun
66 	// diffuse lighting is turned off because players dislike it
67 	const gfx_api::gfxFloat defaultLight[LIGHT_MAX][4] = {
68 		{0.0f, 0.0f, 0.0f, 1.0f}, // LIGHT_EMISSIVE
69 		{0.5f, 0.5f, 0.5f, 1.0f}, // LIGHT_AMBIENT
70 		{1.0f, 1.0f, 1.0f, 1.0f}, // LIGHT_DIFFUSE
71 		{1.0f, 1.0f, 1.0f, 1.0f}  // LIGHT_SPECULAR
72 	};
73 	memcpy(lighting0, defaultLight, sizeof(lighting0));
74 }
75 
pie_Lighting0(LIGHTING_TYPE entry,const float value[4])76 void pie_Lighting0(LIGHTING_TYPE entry, const float value[4])
77 {
78 	lighting0[entry][0] = value[0];
79 	lighting0[entry][1] = value[1];
80 	lighting0[entry][2] = value[2];
81 	lighting0[entry][3] = value[3];
82 }
83 
pie_setShadows(bool drawShadows)84 void pie_setShadows(bool drawShadows)
85 {
86 	shadows = drawShadows;
87 }
88 
89 static Vector3f currentSunPosition(0.f, 0.f, 0.f);
90 
pie_BeginLighting(const Vector3f & light)91 void pie_BeginLighting(const Vector3f &light)
92 {
93 	currentSunPosition = light;
94 }
95 
96 /***************************************************************************
97  * pie_Draw3dShape
98  *
99  * Project and render a pumpkin image to render surface
100  * Will support zbuffering, texturing, coloured lighting and alpha effects
101  * Avoids recalculating vertex projections for every poly
102  ***************************************************************************/
103 
104 struct ShadowcastingShape
105 {
106 	glm::mat4	matrix;
107 	iIMDShape	*shape;
108 	int		flag;
109 	int		flag_data;
110 	glm::vec4	light;
111 };
112 
113 struct SHAPE
114 {
115 	glm::mat4	matrix;
116 	iIMDShape	*shape;
117 	int		frame;
118 	PIELIGHT	colour;
119 	PIELIGHT	teamcolour;
120 	int		flag;
121 	int		flag_data;
122 	float		stretch;
123 };
124 
125 static std::vector<ShadowcastingShape> scshapes;
126 static std::vector<SHAPE> tshapes;
127 static std::vector<SHAPE> shapes;
128 static gfx_api::buffer* pZeroedVertexBuffer = nullptr;
129 
getZeroedVertexBuffer(size_t size)130 static gfx_api::buffer* getZeroedVertexBuffer(size_t size)
131 {
132 	static size_t currentSize = 0;
133 	if (!pZeroedVertexBuffer || (currentSize < size))
134 	{
135 		if (pZeroedVertexBuffer)
136 		{
137 			delete pZeroedVertexBuffer;
138 		}
139 		pZeroedVertexBuffer = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer);
140 		std::vector<UBYTE> tempZeroes(size, 0);
141 		pZeroedVertexBuffer->upload(size, tempZeroes.data());
142 		currentSize = size;
143 	}
144 	return pZeroedVertexBuffer;
145 }
146 
pie_Draw3DButton(iIMDShape * shape,PIELIGHT teamcolour,const glm::mat4 & matrix)147 static void pie_Draw3DButton(iIMDShape *shape, PIELIGHT teamcolour, const glm::mat4 &matrix)
148 {
149 	auto* tcmask = shape->tcmaskpage != iV_TEX_INVALID ? &pie_Texture(shape->tcmaskpage) : nullptr;
150 	auto* normalmap = shape->normalpage != iV_TEX_INVALID ? &pie_Texture(shape->normalpage) : nullptr;
151 	auto* specularmap = shape->specularpage != iV_TEX_INVALID ? &pie_Texture(shape->specularpage) : nullptr;
152 
153 	gfx_api::buffer* pTangentBuffer = (shape->buffers[VBO_TANGENT] != nullptr) ? shape->buffers[VBO_TANGENT] : getZeroedVertexBuffer(shape->vertexCount * 4 * sizeof(gfx_api::gfxFloat));
154 
155 	const PIELIGHT colour = WZCOL_WHITE;
156 	gfx_api::Draw3DButtonPSO::get().bind();
157 	gfx_api::constant_buffer_type<SHADER_BUTTON> cbuf{
158 		pal_PIELIGHTtoVec4(colour), pal_PIELIGHTtoVec4(teamcolour), 0.f, tcmask ? 1 : 0, 0, normalmap != nullptr, specularmap != nullptr, 0, 0, 0.f, matrix, pie_PerspectiveGet() * matrix, glm::transpose(glm::inverse(matrix)),
159 		glm::vec4(0.f), glm::vec4(0.f), glm::vec4(0.f), glm::vec4(0.f), glm::vec4(0.f), glm::vec4(0.f), 0.f, 0.f, shape->buffers[VBO_TANGENT] != nullptr };
160 	gfx_api::Draw3DButtonPSO::get().bind_constants(cbuf);
161 
162 	gfx_api::Draw3DButtonPSO::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);
163 	gfx_api::Draw3DButtonPSO::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
164 	gfx_api::context::get().bind_index_buffer(*shape->buffers[VBO_INDEX], gfx_api::index_type::u16);
165 	gfx_api::Draw3DButtonPSO::get().draw_elements(shape->polys.size() * 3, 0);
166 	polyCount += shape->polys.size();
167 	gfx_api::Draw3DButtonPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
168 	gfx_api::context::get().unbind_index_buffer(*shape->buffers[VBO_INDEX]);
169 }
170 
171 struct templatedState
172 {
173 	SHADER_MODE shader = SHADER_NONE;
174 	const iIMDShape * shape = nullptr;
175 	int pieFlag = 0;
176 
templatedStatetemplatedState177 	templatedState()
178 	: shader(SHADER_NONE), shape(nullptr), pieFlag(0)
179 	{ }
180 
templatedStatetemplatedState181 	templatedState(SHADER_MODE shader, const iIMDShape * shape, int pieFlag)
182 	: shader(shader), shape(shape), pieFlag(pieFlag)
183 	{ }
184 
operator ==templatedState185 	bool operator==(const templatedState& rhs) const
186 	{
187 		return (shader == rhs.shader)
188 		&& (shape == rhs.shape)
189 		&& (pieFlag == rhs.pieFlag);
190 	}
operator !=templatedState191 	bool operator!=(const templatedState& rhs) const
192 	{
193 		return !operator==(rhs);
194 	}
195 };
196 
197 template<SHADER_MODE shader, typename AdditivePSO, typename AlphaPSO, typename PremultipliedPSO, typename OpaquePSO>
draw3dShapeTemplated(const templatedState & lastState,const PIELIGHT & colour,const PIELIGHT & teamcolour,const float & stretch,const int & ecmState,const float & timestate,const glm::mat4 & matrix,glm::vec4 & sceneColor,glm::vec4 & ambient,glm::vec4 & diffuse,glm::vec4 & specular,const iIMDShape * shape,int pieFlag,int frame)198 static void draw3dShapeTemplated(const templatedState &lastState, const PIELIGHT &colour, const PIELIGHT &teamcolour, const float& stretch, const int& ecmState, const float& timestate, const glm::mat4 & matrix, glm::vec4 &sceneColor, glm::vec4 &ambient, glm::vec4 &diffuse, glm::vec4 &specular, const iIMDShape * shape, int pieFlag, int frame)
199 {
200 	templatedState currentState = templatedState(shader, shape, pieFlag);
201 
202 	auto* tcmask = shape->tcmaskpage != iV_TEX_INVALID ? &pie_Texture(shape->tcmaskpage) : nullptr;
203 	auto* normalmap = shape->normalpage != iV_TEX_INVALID ? &pie_Texture(shape->normalpage) : nullptr;
204 	auto* specularmap = shape->specularpage != iV_TEX_INVALID ? &pie_Texture(shape->specularpage) : nullptr;
205 
206 	gfx_api::constant_buffer_type<shader> cbuf{
207 		pal_PIELIGHTtoVec4(colour), pal_PIELIGHTtoVec4(teamcolour), stretch, tcmask ? 1 : 0, 0, normalmap != nullptr, specularmap != nullptr, ecmState, !(pieFlag & pie_PREMULTIPLIED), timestate, matrix, pie_PerspectiveGet() * matrix, glm::transpose(glm::inverse(matrix)),
208 		glm::vec4(currentSunPosition, 0.f), sceneColor, ambient, diffuse, specular, glm::vec4(0.f), 0.f, 0.f, shape->buffers[VBO_TANGENT] != nullptr };
209 
210 	gfx_api::buffer* pTangentBuffer = (shape->buffers[VBO_TANGENT] != nullptr) ? shape->buffers[VBO_TANGENT] : getZeroedVertexBuffer(shape->vertexCount * 4 * sizeof(gfx_api::gfxFloat));
211 
212 	/* Set tranlucency */
213 	if (pieFlag & pie_ADDITIVE)
214 	{
215 		AdditivePSO::get().bind();
216 		AdditivePSO::get().bind_constants(cbuf);
217 		if (currentState != lastState)
218 		{
219 			AdditivePSO::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
220 			AdditivePSO::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);
221 		}
222 		AdditivePSO::get().draw_elements(shape->polys.size() * 3, frame * shape->polys.size() * 3 * sizeof(uint16_t));
223 //		AdditivePSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
224 	}
225 	else if (pieFlag & pie_TRANSLUCENT)
226 	{
227 		AlphaPSO::get().bind();
228 		AlphaPSO::get().bind_constants(cbuf);
229 		if (currentState != lastState)
230 		{
231 			AlphaPSO::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
232 			AlphaPSO::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);
233 		}
234 		AlphaPSO::get().draw_elements(shape->polys.size() * 3, frame * shape->polys.size() * 3 * sizeof(uint16_t));
235 //		AlphaPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
236 	}
237 	else if (pieFlag & pie_PREMULTIPLIED)
238 	{
239 		PremultipliedPSO::get().bind();
240 		PremultipliedPSO::get().bind_constants(cbuf);
241 		if (currentState != lastState)
242 		{
243 			PremultipliedPSO::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
244 			PremultipliedPSO::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);
245 		}
246 		PremultipliedPSO::get().draw_elements(shape->polys.size() * 3, frame * shape->polys.size() * 3 * sizeof(uint16_t));
247 //		PremultipliedPSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
248 	}
249 	else
250 	{
251 		OpaquePSO::get().bind();
252 		OpaquePSO::get().bind_constants(cbuf);
253 		if (currentState != lastState)
254 		{
255 			OpaquePSO::get().bind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD], pTangentBuffer);
256 			OpaquePSO::get().bind_textures(&pie_Texture(shape->texpage), tcmask, normalmap, specularmap);
257 		}
258 		OpaquePSO::get().draw_elements(shape->polys.size() * 3, frame * shape->polys.size() * 3 * sizeof(uint16_t));
259 //		OpaquePSO::get().unbind_vertex_buffers(shape->buffers[VBO_VERTEX], shape->buffers[VBO_NORMAL], shape->buffers[VBO_TEXCOORD]);
260 	}
261 }
262 
pie_Draw3DShape2(const templatedState & lastState,const iIMDShape * shape,int frame,PIELIGHT colour,PIELIGHT teamcolour,int pieFlag,int pieFlagData,glm::mat4 const & matrix)263 static templatedState pie_Draw3DShape2(const templatedState &lastState, const iIMDShape *shape, int frame, PIELIGHT colour, PIELIGHT teamcolour, int pieFlag, int pieFlagData, glm::mat4 const &matrix)
264 {
265 	bool light = true;
266 
267 	/* Set fog status */
268 	if (!(pieFlag & pie_FORCE_FOG) && (pieFlag & pie_ADDITIVE || pieFlag & pie_TRANSLUCENT || pieFlag & pie_PREMULTIPLIED))
269 	{
270 		pie_SetFogStatus(false);
271 	}
272 	else
273 	{
274 		pie_SetFogStatus(true);
275 	}
276 
277 	/* Set translucency */
278 	if (pieFlag & pie_ADDITIVE)
279 	{
280 		colour.byte.a = (UBYTE)pieFlagData;
281 		light = false;
282 	}
283 	else if (pieFlag & pie_TRANSLUCENT)
284 	{
285 		colour.byte.a = (UBYTE)pieFlagData;
286 		light = false;
287 	}
288 	else if (pieFlag & pie_PREMULTIPLIED)
289 	{
290 		light = false;
291 	}
292 
293 	if (pieFlag & pie_ECM)
294 	{
295 		light = true;
296 		pie_SetShaderEcmEffect(true);
297 	}
298 
299 	glm::vec4 sceneColor(lighting0[LIGHT_EMISSIVE][0], lighting0[LIGHT_EMISSIVE][1], lighting0[LIGHT_EMISSIVE][2], lighting0[LIGHT_EMISSIVE][3]);
300 	glm::vec4 ambient(lighting0[LIGHT_AMBIENT][0], lighting0[LIGHT_AMBIENT][1], lighting0[LIGHT_AMBIENT][2], lighting0[LIGHT_AMBIENT][3]);
301 	glm::vec4 diffuse(lighting0[LIGHT_DIFFUSE][0], lighting0[LIGHT_DIFFUSE][1], lighting0[LIGHT_DIFFUSE][2], lighting0[LIGHT_DIFFUSE][3]);
302 	glm::vec4 specular(lighting0[LIGHT_SPECULAR][0], lighting0[LIGHT_SPECULAR][1], lighting0[LIGHT_SPECULAR][2], lighting0[LIGHT_SPECULAR][3]);
303 
304 	frame %= std::max<int>(1, shape->numFrames);
305 
306 	templatedState currentState = templatedState((light) ? SHADER_COMPONENT : SHADER_NOLIGHT, shape, pieFlag);
307 	if (currentState != lastState)
308 	{
309 		gfx_api::context::get().bind_index_buffer(*shape->buffers[VBO_INDEX], gfx_api::index_type::u16);
310 	}
311 
312 	if (light)
313 	{
314 		draw3dShapeTemplated<SHADER_COMPONENT, gfx_api::Draw3DShapeAdditive, gfx_api::Draw3DShapeAlpha, gfx_api::Draw3DShapePremul, gfx_api::Draw3DShapeOpaque>(lastState, colour, teamcolour, pie_GetShaderStretchDepth(), pie_GetShaderEcmEffect(), pie_GetShaderTime(), matrix, sceneColor, ambient, diffuse, specular, shape, pieFlag, frame);
315 	}
316 	else
317 	{
318 		draw3dShapeTemplated<SHADER_NOLIGHT, gfx_api::Draw3DShapeNoLightAdditive, gfx_api::Draw3DShapeNoLightAlpha, gfx_api::Draw3DShapeNoLightPremul, gfx_api::Draw3DShapeNoLightOpaque>(lastState, colour, teamcolour, pie_GetShaderStretchDepth(), pie_GetShaderEcmEffect(), pie_GetShaderTime(), matrix, sceneColor, ambient, diffuse, specular, shape, pieFlag, frame);
319 	}
320 
321 	polyCount += shape->polys.size();
322 
323 	pie_SetShaderEcmEffect(false);
324 
325 	return currentState;
326 }
327 
edgeLessThan(EDGE const & e1,EDGE const & e2)328 static inline bool edgeLessThan(EDGE const &e1, EDGE const &e2)
329 {
330 	if (e1.from != e2.from)
331 	{
332 		return e1.from < e2.from;
333 	}
334 	return e1.to < e2.to;
335 }
336 
flipEdge(EDGE & e)337 static inline void flipEdge(EDGE &e)
338 {
339 	std::swap(e.from, e.to);
340 }
341 
342 /// scale the height according to the flags
scale_y(float y,int flag,int flag_data)343 static inline float scale_y(float y, int flag, int flag_data)
344 {
345 	float tempY = y;
346 	if (flag & pie_RAISE)
347 	{
348 		tempY = y - flag_data;
349 		if (y - flag_data < 0)
350 		{
351 			tempY = 0;
352 		}
353 	}
354 	else if (flag & pie_HEIGHT_SCALED)
355 	{
356 		if (y > 0)
357 		{
358 			tempY = (y * flag_data) / pie_RAISE_SCALE;
359 		}
360 	}
361 	return tempY;
362 }
363 
hash_combine(std::size_t & seed)364 inline void hash_combine(std::size_t& seed) { }
365 
366 template <typename T, typename... Rest>
hash_combine(std::size_t & seed,const T & v,Rest...rest)367 inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
368 	std::hash<T> hasher;
369 #if SIZE_MAX >= UINT64_MAX
370 	seed ^= hasher(v) + 0x9e3779b97f4a7c15L + (seed<<6) + (seed>>2);
371 #else
372 	seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
373 #endif
374 	hash_combine(seed, rest...);
375 }
376 
hash_vec4(const glm::vec4 & vec)377 std::size_t hash_vec4(const glm::vec4& vec)
378 {
379 	std::size_t h=0;
380 	hash_combine(h, vec[0], vec[1], vec[2], vec[3]);
381 	return h;
382 }
383 
384 struct ShadowDrawParameters {
385 	int flag;
386 	int flag_data;
387 	//		glm::mat4 modelViewMatrix; // the modelViewMatrix doesn't change any of the vertex / buffer calculations
388 	glm::vec4 light;
389 
ShadowDrawParametersShadowDrawParameters390 	ShadowDrawParameters(int flag, int flag_data, const glm::vec4 &light)
391 	: flag(flag)
392 	, flag_data(flag_data)
393 	, light(light)
394 	{ }
395 
operator ==ShadowDrawParameters396 	bool operator ==(const ShadowDrawParameters &b) const
397 	{
398 		return (flag == b.flag) && (flag_data == b.flag_data) && (light == b.light);
399 	}
400 };
401 
402 namespace std {
403 	template <>
404 	struct hash<ShadowDrawParameters>
405 	{
operator ()std::hash406 		std::size_t operator()(const ShadowDrawParameters& k) const
407 		{
408 			std::size_t h = 0;
409 			hash_combine(h, k.flag, k.flag_data, hash_vec4(k.light));
410 			return h;
411 		}
412 	};
413 }
414 
415 struct ShadowCache {
416 
417 	struct CachedShadowData {
418 		uint64_t lastQueriedFrameCount = 0;
419 		std::vector<Vector3f> vertexes;
420 
CachedShadowDataShadowCache::CachedShadowData421 		CachedShadowData() { }
422 	};
423 
424 	typedef std::unordered_map<ShadowDrawParameters, CachedShadowData> ShadowDrawParametersToCachedDataMap;
425 	typedef std::unordered_map<iIMDShape *, ShadowDrawParametersToCachedDataMap, std::hash<iIMDShape *>> ShapeMap;
426 
findCacheForShadowDrawShadowCache427 	const CachedShadowData* findCacheForShadowDraw(iIMDShape *shape, int flag, int flag_data, const glm::vec4 &light)
428 	{
429 		auto it = shapeMap.find(shape);
430 		if (it == shapeMap.end()) {
431 			return nullptr;
432 		}
433 		auto it_cachedData = it->second.find(ShadowDrawParameters(flag, flag_data, light));
434 		if (it_cachedData == it->second.end()) {
435 			return nullptr;
436 		}
437 		// update the frame in which we requested this shadow cache
438 		it_cachedData->second.lastQueriedFrameCount = _currentFrame;
439 		return &(it_cachedData->second);
440 	}
441 
createCacheForShadowDrawShadowCache442 	CachedShadowData& createCacheForShadowDraw(iIMDShape *shape, int flag, int flag_data, const glm::vec4 &light)
443 	{
444 		auto result = shapeMap[shape].emplace(ShadowDrawParameters(flag, flag_data, light), CachedShadowData());
445 		result.first->second.lastQueriedFrameCount = _currentFrame;
446 		return result.first->second;
447 	}
448 
addPremultipliedVertexesShadowCache449 	void addPremultipliedVertexes(const CachedShadowData& cachedData, const glm::mat4 &modelViewMatrix)
450 	{
451 		float mat_a = modelViewMatrix[0].x;
452 		float mat_b = modelViewMatrix[1].x;
453 		float mat_c = modelViewMatrix[2].x;
454 		float mat_d = modelViewMatrix[3].x;
455 		float mat_e = modelViewMatrix[0].y;
456 		float mat_f = modelViewMatrix[1].y;
457 		float mat_g = modelViewMatrix[2].y;
458 		float mat_h = modelViewMatrix[3].y;
459 		float mat_i = modelViewMatrix[0].z;
460 		float mat_j = modelViewMatrix[1].z;
461 		float mat_k = modelViewMatrix[2].z;
462 		float mat_l = modelViewMatrix[3].z;
463 		float premult_x;
464 		float premult_y;
465 		float premult_z;
466 		for (auto &vertex : cachedData.vertexes)
467 		{
468 			premult_x = vertex.x*mat_a + vertex.y*mat_b + vertex.z*mat_c + mat_d;
469 			premult_y = vertex.x*mat_e + vertex.y*mat_f + vertex.z*mat_g + mat_h;
470 			premult_z = vertex.x*mat_i + vertex.y*mat_j + vertex.z*mat_k + mat_l;
471 			vertexes.push_back(Vector3f(premult_x, premult_y, premult_z));
472 		}
473 	}
474 
getPremultipliedVertexesShadowCache475 	const std::vector<Vector3f>& getPremultipliedVertexes()
476 	{
477 		return vertexes;
478 	}
479 
clearPremultipliedVertexesShadowCache480 	void clearPremultipliedVertexes()
481 	{
482 		vertexes.clear();
483 	}
484 
setCurrentFrameShadowCache485 	void setCurrentFrame(uint64_t currentFrame)
486 	{
487 		_currentFrame = currentFrame;
488 	}
489 
removeUnusedShadowCache490 	size_t removeUnused()
491 	{
492 		std::vector<ShapeMap::iterator> unusedShapes;
493 		size_t oldItemsRemoved = 0;
494 		for (auto it_shape = shapeMap.begin(); it_shape != shapeMap.end(); ++it_shape)
495 		{
496 			std::vector<ShadowDrawParametersToCachedDataMap::iterator> unusedBuffersForShape;
497 			for (auto it_shadowDrawParams = it_shape->second.begin(); it_shadowDrawParams != it_shape->second.end(); ++it_shadowDrawParams)
498 			{
499 				if (it_shadowDrawParams->second.lastQueriedFrameCount != _currentFrame)
500 				{
501 					unusedBuffersForShape.push_back(it_shadowDrawParams);
502 				}
503 			}
504 			for (auto &item : unusedBuffersForShape)
505 			{
506 				it_shape->second.erase(item);
507 				++oldItemsRemoved;
508 			}
509 			if (it_shape->second.empty())
510 			{
511 				// remove from the root shapeMap
512 				unusedShapes.push_back(it_shape);
513 			}
514 		}
515 		for (auto &item : unusedShapes)
516 		{
517 			shapeMap.erase(item);
518 		}
519 		return oldItemsRemoved;
520 	}
521 private:
522 	uint64_t _currentFrame = 0;
523 	ShapeMap shapeMap;
524 	std::vector<Vector3f> vertexes;
525 };
526 
527 enum DrawShadowResult {
528 	DRAW_SUCCESS_CACHED,
529 	DRAW_SUCCESS_UNCACHED
530 };
531 
532 /// Draw the shadow for a shape
533 /// Prequisite:
534 ///		Caller must call the following before all calls to pie_DrawShadow():
535 ///			const auto &program = pie_ActivateShader(SHADER_GENERIC_COLOR, pie_PerspectiveGet(), glm::vec4());
536 ///			glEnableVertexAttribArray(program.locVertex);
537 ///		and must call the following after all calls to pie_DrawShadow():
538 ///			glDisableVertexAttribArray(program.locVertex);
539 ///			pie_DeactivateShader();
540 ///		The only place this is currently called is pie_ShadowDrawLoop(), which handles this properly.
pie_DrawShadow(ShadowCache & shadowCache,iIMDShape * shape,int flag,int flag_data,const glm::vec4 & light,const glm::mat4 & modelViewMatrix)541 static inline DrawShadowResult pie_DrawShadow(ShadowCache &shadowCache, iIMDShape *shape, int flag, int flag_data, const glm::vec4 &light, const glm::mat4 &modelViewMatrix)
542 {
543 	static std::vector<EDGE> edgelist;  // Static, to save allocations.
544 	static std::vector<EDGE> edgelistFlipped;  // Static, to save allocations.
545 	static std::vector<EDGE> edgelistFiltered;  // Static, to save allocations.
546 	EDGE *drawlist = nullptr;
547 
548 	size_t edge_count;
549 	DrawShadowResult result;
550 
551 	// Find cached data (if available)
552 	// Note: The modelViewMatrix is not used for calculating the sorted / filtered vertices, so it's not included
553 	const ShadowCache::CachedShadowData *pCached = shadowCache.findCacheForShadowDraw(shape, flag, flag_data, light);
554 	if (pCached == nullptr)
555 	{
556 		const Vector3f *pVertices = shape->pShadowPoints->data();
557 		if (flag & pie_STATIC_SHADOW && shape->shadowEdgeList)
558 		{
559 			drawlist = shape->shadowEdgeList;
560 			edge_count = shape->nShadowEdges;
561 		}
562 		else
563 		{
564 			edgelist.clear();
565 			glm::vec3 p[3];
566 			for (const iIMDPoly &poly : *(shape->pShadowPolys))
567 			{
568 				for (int j = 0; j < 3; ++j)
569 				{
570 					uint32_t current = poly.pindex[j];
571 					p[j] = glm::vec3(pVertices[current].x, scale_y(pVertices[current].y, flag, flag_data), pVertices[current].z);
572 				}
573 				if (glm::dot(glm::cross(p[2] - p[0], p[1] - p[0]), glm::vec3(light)) > 0.0f)
574 				{
575 					for (int n = 0; n < 3; ++n)
576 					{
577 						// Add the edges
578 						edgelist.push_back({poly.pindex[n], poly.pindex[(n + 1)%3]});
579 					}
580 				}
581 			}
582 
583 			// Remove duplicate pairs from the edge list. For example, in the list ((1 2), (2 6), (6 2), (3, 4)), remove (2 6) and (6 2).
584 			edgelistFlipped = edgelist;
585 			std::for_each(edgelistFlipped.begin(), edgelistFlipped.end(), flipEdge);
586 			std::sort(edgelist.begin(), edgelist.end(), edgeLessThan);
587 			std::sort(edgelistFlipped.begin(), edgelistFlipped.end(), edgeLessThan);
588 			edgelistFiltered.resize(edgelist.size());
589 			edgelistFiltered.erase(std::set_difference(edgelist.begin(), edgelist.end(), edgelistFlipped.begin(), edgelistFlipped.end(), edgelistFiltered.begin(), edgeLessThan), edgelistFiltered.end());
590 
591 			drawlist = &edgelistFiltered[0];
592 			edge_count = edgelistFiltered.size();
593 			//debug(LOG_WARNING, "we have %i edges", edge_count);
594 
595 			if (flag & pie_STATIC_SHADOW)
596 			{
597 				// then store it in the imd
598 				shape->nShadowEdges = edge_count;
599 				shape->shadowEdgeList = (EDGE *)realloc(shape->shadowEdgeList, sizeof(EDGE) * shape->nShadowEdges);
600 				std::copy(drawlist, drawlist + edge_count, shape->shadowEdgeList);
601 			}
602 		}
603 
604 		std::vector<Vector3f> vertexes;
605 		vertexes.reserve(edge_count * 6);
606 		for (size_t i = 0; i < edge_count; i++)
607 		{
608 			int a = drawlist[i].from, b = drawlist[i].to;
609 
610 			glm::vec3 v1(pVertices[b].x, scale_y(pVertices[b].y, flag, flag_data), pVertices[b].z);
611 			glm::vec3 v3(pVertices[a].x + light[0], scale_y(pVertices[a].y, flag, flag_data) + light[1], pVertices[a].z + light[2]);
612 
613 			vertexes.push_back(v1);
614 			vertexes.push_back(glm::vec3(pVertices[b].x + light[0], scale_y(pVertices[b].y, flag, flag_data) + light[1], pVertices[b].z + light[2])); //v2
615 			vertexes.push_back(v3);
616 
617 			vertexes.push_back(v3);
618 			vertexes.push_back(glm::vec3(pVertices[a].x, scale_y(pVertices[a].y, flag, flag_data), pVertices[a].z)); //v4
619 			vertexes.push_back(v1);
620 		}
621 
622 		ShadowCache::CachedShadowData& cache = shadowCache.createCacheForShadowDraw(shape, flag, flag_data, light);
623 		cache.vertexes = std::move(vertexes);
624 		result = DRAW_SUCCESS_UNCACHED;
625 		pCached = &cache;
626 	}
627 	else
628 	{
629 		result = DRAW_SUCCESS_CACHED;
630 	}
631 
632 	// Aggregate the vertexes (pre-computed with the modelViewMatrix)
633 	shadowCache.addPremultipliedVertexes(*pCached, modelViewMatrix);
634 
635 	return result;
636 }
637 
pie_CleanUp()638 void pie_CleanUp()
639 {
640 	tshapes.clear();
641 	shapes.clear();
642 	scshapes.clear();
643 	if (pZeroedVertexBuffer)
644 	{
645 		delete pZeroedVertexBuffer;
646 		pZeroedVertexBuffer = nullptr;
647 	}
648 }
649 
pie_Draw3DShape(iIMDShape * shape,int frame,int team,PIELIGHT colour,int pieFlag,int pieFlagData,const glm::mat4 & modelView)650 bool pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int pieFlag, int pieFlagData, const glm::mat4 &modelView)
651 {
652 	pieCount++;
653 
654 	ASSERT(frame >= 0, "Negative frame %d", frame);
655 	ASSERT(team >= 0, "Negative team %d", team);
656 
657 	const PIELIGHT teamcolour = pal_GetTeamColour(team);
658 	if (pieFlag & pie_BUTTON)
659 	{
660 		pie_Draw3DButton(shape, teamcolour, modelView);
661 	}
662 	else
663 	{
664 		SHAPE tshape;
665 		tshape.shape = shape;
666 		tshape.frame = frame;
667 		tshape.colour = colour;
668 		tshape.teamcolour = teamcolour;
669 		tshape.flag = pieFlag;
670 		tshape.flag_data = pieFlagData;
671 		tshape.stretch = pie_GetShaderStretchDepth();
672 		tshape.matrix = modelView;
673 
674 		if (pieFlag & pie_HEIGHT_SCALED)	// construct
675 		{
676 			tshape.matrix = glm::scale(tshape.matrix, glm::vec3(1.0f, (float)pieFlagData / (float)pie_RAISE_SCALE, 1.0f));
677 		}
678 		if (pieFlag & pie_RAISE)		// collapse
679 		{
680 			tshape.matrix = glm::translate(tshape.matrix, glm::vec3(1.0f, (-shape->max.y * (pie_RAISE_SCALE - pieFlagData)) * (1.0f / pie_RAISE_SCALE), 1.0f));
681 		}
682 
683 		if (pieFlag & (pie_ADDITIVE | pie_TRANSLUCENT | pie_PREMULTIPLIED))
684 		{
685 			tshapes.push_back(tshape);
686 		}
687 		else
688 		{
689 			if (shadows && (pieFlag & pie_SHADOW || pieFlag & pie_STATIC_SHADOW))
690 			{
691 				float distance;
692 
693 				// draw a shadow
694 				ShadowcastingShape scshape;
695 				scshape.matrix = modelView;
696 				distance = scshape.matrix[3][0] * scshape.matrix[3][0];
697 				distance += scshape.matrix[3][1] * scshape.matrix[3][1];
698 				distance += scshape.matrix[3][2] * scshape.matrix[3][2];
699 
700 				// if object is too far in the fog don't generate a shadow.
701 				if (distance < SHADOW_END_DISTANCE)
702 				{
703 					// Calculate the light position relative to the object
704 					glm::vec4 pos_light0 = glm::vec4(currentSunPosition, 0.f);
705 					glm::mat4 invmat = glm::inverse(scshape.matrix);
706 
707 					scshape.light = invmat * pos_light0;
708 					scshape.shape = shape;
709 					scshape.flag = pieFlag;
710 					scshape.flag_data = pieFlagData;
711 
712 					scshapes.push_back(scshape);
713 				}
714 			}
715 			shapes.push_back(tshape);
716 		}
717 	}
718 
719 	return true;
720 }
721 
pie_ShadowDrawLoop(ShadowCache & shadowCache)722 static void pie_ShadowDrawLoop(ShadowCache &shadowCache)
723 {
724 	size_t cachedShadowDraws = 0;
725 	size_t uncachedShadowDraws = 0;
726 	for (unsigned i = 0; i < scshapes.size(); i++)
727 	{
728 		DrawShadowResult result = pie_DrawShadow(shadowCache, scshapes[i].shape, scshapes[i].flag, scshapes[i].flag_data, scshapes[i].light, scshapes[i].matrix);
729 		if (result == DRAW_SUCCESS_CACHED)
730 		{
731 			++cachedShadowDraws;
732 		}
733 		else
734 		{
735 			++uncachedShadowDraws;
736 		}
737 	}
738 
739 	const auto &premultipliedVertexes = shadowCache.getPremultipliedVertexes();
740 	if (premultipliedVertexes.size() > 0)
741 	{
742 		// Draw the shadow volume
743 		gfx_api::DrawStencilShadow::get().bind();
744 		// The vertexes returned by shadowCache.getPremultipliedVertexes() are pre-multiplied by the modelViewMatrix
745 		// Thus we only need to include the perspective matrix
746 		gfx_api::DrawStencilShadow::get().bind_constants({ pie_PerspectiveGet(), glm::vec2(0.f), glm::vec2(0.f), glm::vec4(0.f) });
747 		gfx_api::context::get().bind_streamed_vertex_buffers(premultipliedVertexes.data(), sizeof(Vector3f) * premultipliedVertexes.size());
748 
749 		// Batch into glDrawArrays calls of <= SHADOW_BATCH_MAX
750 		static const size_t SHADOW_BATCH_MAX = 8192 * 3; // must be divisible by 3
751 		size_t vertex_count = premultipliedVertexes.size();
752 		for (size_t startingIndex = 0; startingIndex < vertex_count; startingIndex += SHADOW_BATCH_MAX)
753 		{
754 			gfx_api::DrawStencilShadow::get().draw(std::min(vertex_count - startingIndex, SHADOW_BATCH_MAX), startingIndex);
755 		}
756 
757 		gfx_api::context::get().disable_all_vertex_buffers();
758 	}
759 
760 	shadowCache.clearPremultipliedVertexes();
761 
762 //	debug(LOG_INFO, "Cached shadow draws: %lu, uncached shadow draws: %lu", cachedShadowDraws, uncachedShadowDraws);
763 }
764 
765 static ShadowCache shadowCache;
766 
pie_DrawShadows(uint64_t currentGameFrame)767 static void pie_DrawShadows(uint64_t currentGameFrame)
768 {
769 	const float width = pie_GetVideoBufferWidth();
770 	const float height = pie_GetVideoBufferHeight();
771 	shadowCache.setCurrentFrame(currentGameFrame);
772 
773 	pie_ShadowDrawLoop(shadowCache);
774 
775 	PIELIGHT grey;
776 	grey.byte = { 0, 0, 0, 128 };
777 	pie_BoxFill_alpha(0, 0, width, height, grey);
778 
779 	scshapes.resize(0);
780 	shadowCache.removeUnused();
781 }
782 
783 struct less_than_shape
784 {
operator ()less_than_shape785 	inline bool operator() (const SHAPE& shape1, const SHAPE& shape2)
786 	{
787 		return (shape1.shape < shape2.shape);
788 	}
789 };
790 
pie_RemainingPasses(uint64_t currentGameFrame)791 void pie_RemainingPasses(uint64_t currentGameFrame)
792 {
793 	// Draw models
794 	// sort list to reduce state changes
795 	std::sort(shapes.begin(), shapes.end(), less_than_shape());
796 	gfx_api::context::get().debugStringMarker("Remaining passes - opaque models");
797 	templatedState lastState;
798 	for (SHAPE const &shape : shapes)
799 	{
800 		pie_SetShaderStretchDepth(shape.stretch);
801 		lastState = pie_Draw3DShape2(lastState, shape.shape, shape.frame, shape.colour, shape.teamcolour, shape.flag, shape.flag_data, shape.matrix);
802 	}
803 	gfx_api::context::get().disable_all_vertex_buffers();
804 	if (!shapes.empty())
805 	{
806 		// unbind last index buffer bound inside pie_Draw3DShape2
807 		gfx_api::context::get().unbind_index_buffer(*((shapes.back().shape)->buffers[VBO_INDEX]));
808 	}
809 	gfx_api::context::get().debugStringMarker("Remaining passes - shadows");
810 	// Draw shadows
811 	if (shadows)
812 	{
813 		pie_DrawShadows(currentGameFrame);
814 	}
815 	// Draw translucent models last
816 	// TODO, sort list by Z order to do translucency correctly
817 	gfx_api::context::get().debugStringMarker("Remaining passes - translucent models");
818 	lastState = templatedState();
819 	for (SHAPE const &shape : tshapes)
820 	{
821 		pie_SetShaderStretchDepth(shape.stretch);
822 		lastState = pie_Draw3DShape2(lastState, shape.shape, shape.frame, shape.colour, shape.teamcolour, shape.flag, shape.flag_data, shape.matrix);
823 	}
824 	gfx_api::context::get().disable_all_vertex_buffers();
825 	if (!tshapes.empty())
826 	{
827 		// unbind last index buffer bound inside pie_Draw3DShape2
828 		gfx_api::context::get().unbind_index_buffer(*((tshapes.back().shape)->buffers[VBO_INDEX]));
829 	}
830 	pie_SetShaderStretchDepth(0);
831 	tshapes.clear();
832 	shapes.clear();
833 	gfx_api::context::get().debugStringMarker("Remaining passes - done");
834 }
835 
pie_GetResetCounts(size_t * pPieCount,size_t * pPolyCount)836 void pie_GetResetCounts(size_t *pPieCount, size_t *pPolyCount)
837 {
838 	*pPieCount  = pieCount;
839 	*pPolyCount = polyCount;
840 
841 	pieCount = 0;
842 	polyCount = 0;
843 }
844