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 /***************************************************************************/
21 /*
22  * pieBlitFunc.c
23  *
24  */
25 /***************************************************************************/
26 
27 #include "lib/framework/frame.h"
28 #include "lib/framework/fixedpoint.h"
29 #include "lib/gamelib/gtime.h"
30 #include <time.h>
31 
32 #include "lib/ivis_opengl/bitimage.h"
33 #include "lib/ivis_opengl/pieblitfunc.h"
34 #include "lib/ivis_opengl/piedef.h"
35 #include "lib/ivis_opengl/piemode.h"
36 #include "lib/ivis_opengl/piestate.h"
37 #include "lib/ivis_opengl/pieclip.h"
38 #include "lib/ivis_opengl/piefunc.h"
39 #include "lib/ivis_opengl/piepalette.h"
40 #include "lib/ivis_opengl/tex.h"
41 #include "piematrix.h"
42 #include "screen.h"
43 #include <glm/gtc/type_ptr.hpp>
44 #ifndef GLM_ENABLE_EXPERIMENTAL
45 	#define GLM_ENABLE_EXPERIMENTAL
46 #endif
47 #include <glm/gtx/transform.hpp>
48 
49 /***************************************************************************/
50 /*
51  *	Local Variables
52  */
53 /***************************************************************************/
54 
55 static GFX *radarGfx = nullptr;
56 
57 /***************************************************************************/
58 /*
59  *	Static function forward declarations
60  */
61 /***************************************************************************/
62 
63 static bool assertValidImage(IMAGEFILE *imageFile, unsigned id);
64 static Vector2i makePieImage(IMAGEFILE *imageFile, unsigned id, PIERECT *dest = nullptr, int x = 0, int y = 0);
65 
66 /***************************************************************************/
67 /*
68  *	Source
69  */
70 /***************************************************************************/
71 
GFX(GFXTYPE type,int coordsPerVertex)72 GFX::GFX(GFXTYPE type, int coordsPerVertex) : mType(type), mCoordsPerVertex(coordsPerVertex), mSize(0)
73 {
74 }
75 
loadTexture(const char * filename,int maxWidth,int maxHeight)76 void GFX::loadTexture(const char *filename, int maxWidth /*= -1*/, int maxHeight /*= -1*/)
77 {
78 	ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
79 	const char *extension = strrchr(filename, '.'); // determine the filetype
80 	iV_Image image;
81 	if (!extension || strcmp(extension, ".png") != 0)
82 	{
83 		debug(LOG_ERROR, "Bad image filename: %s", filename);
84 		return;
85 	}
86 	if (iV_loadImage_PNG(filename, &image))
87 	{
88 		scaleImageMaxSize(&image, maxWidth, maxHeight);
89 		makeTexture(image.width, image.height, iV_getPixelFormat(&image), image.bmp);
90 		iV_unloadImage(&image);
91 	}
92 }
93 
makeTexture(size_t width,size_t height,const gfx_api::pixel_format & format,const void * image)94 void GFX::makeTexture(size_t width, size_t height, const gfx_api::pixel_format& format, const void *image)
95 {
96 	ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
97 	if (mTexture)
98 		delete mTexture;
99 	if (width > 0 && height > 0)
100 	{
101 		mTexture = gfx_api::context::get().create_texture(1, width, height, format);
102 		if (image != nullptr)
103 			mTexture->upload(0u, 0u, 0u, width, height, format, image);
104 	}
105 	mWidth = width;
106 	mHeight = height;
107 	mFormat = format;
108 }
109 
updateTexture(const void * image,size_t width,size_t height)110 void GFX::updateTexture(const void *image, size_t width, size_t height)
111 {
112 	ASSERT(mType == GFX_TEXTURE, "Wrong GFX type");
113 	if (width == 0)
114 	{
115 		width = mWidth;
116 	}
117 	if (height == 0)
118 	{
119 		height = mHeight;
120 	}
121 	mTexture->upload(0u, 0u, 0u, width, height, mFormat, image);
122 }
123 
buffers(int vertices,const void * vertBuf,const void * auxBuf)124 void GFX::buffers(int vertices, const void *vertBuf, const void *auxBuf)
125 {
126 	if (!mBuffers[VBO_VERTEX])
127 		mBuffers[VBO_VERTEX] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer);
128 	mBuffers[VBO_VERTEX]->upload(vertices * mCoordsPerVertex * sizeof(gfx_api::gfxFloat), vertBuf);
129 
130 	if (mType == GFX_TEXTURE)
131 	{
132 		if (!mBuffers[VBO_TEXCOORD])
133 			mBuffers[VBO_TEXCOORD] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer);
134 		mBuffers[VBO_TEXCOORD]->upload(vertices * 2 * sizeof(gfx_api::gfxFloat), auxBuf);
135 	}
136 	else if (mType == GFX_COLOUR)
137 	{
138 		// reusing texture buffer for colours for now
139 		if (!mBuffers[VBO_TEXCOORD])
140 			mBuffers[VBO_TEXCOORD] = gfx_api::context::get().create_buffer_object(gfx_api::buffer::usage::vertex_buffer);
141 		mBuffers[VBO_TEXCOORD]->upload(vertices * 4 * sizeof(gfx_api::gfxByte), auxBuf);
142 	}
143 	mSize = vertices;
144 }
145 
~GFX()146 GFX::~GFX()
147 {
148 	for (auto* buffer : mBuffers)
149 	{
150 		delete buffer;
151 	}
152 	if (mTexture)
153 		delete mTexture;
154 }
155 
iV_Line(int x0,int y0,int x1,int y1,PIELIGHT colour)156 void iV_Line(int x0, int y0, int x1, int y1, PIELIGHT colour)
157 {
158 	const glm::vec4 color(
159 		colour.vector[0] / 255.f,
160 		colour.vector[1] / 255.f,
161 		colour.vector[2] / 255.f,
162 		colour.vector[3] / 255.f
163 	);
164 	const auto &mat = glm::ortho(0.f, static_cast<float>(pie_GetVideoBufferWidth()), static_cast<float>(pie_GetVideoBufferHeight()), 0.f);
165 	gfx_api::LinePSO::get().bind();
166 	gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(x0, y0), glm::vec2(x1, y1), color });
167 	gfx_api::LinePSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
168 	gfx_api::LinePSO::get().draw(2, 0);
169 	gfx_api::LinePSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
170 }
171 
iV_Lines(const std::vector<glm::ivec4> & lines,PIELIGHT colour)172 void iV_Lines(const std::vector<glm::ivec4> &lines, PIELIGHT colour)
173 {
174 	const glm::vec4 color(
175 		colour.vector[0] / 255.f,
176 		colour.vector[1] / 255.f,
177 		colour.vector[2] / 255.f,
178 		colour.vector[3] / 255.f
179 	);
180 	const auto &mat = glm::ortho(0.f, static_cast<float>(pie_GetVideoBufferWidth()), static_cast<float>(pie_GetVideoBufferHeight()), 0.f);
181 	gfx_api::LinePSO::get().bind();
182 	gfx_api::LinePSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
183 	for (const auto &line : lines)
184 	{
185 		gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(line.x, line.y), glm::vec2(line.z, line.w), color });
186 		gfx_api::LinePSO::get().draw(2, 0);
187 	}
188 	gfx_api::LinePSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
189 }
190 
191 /**
192  *	Assumes render mode set up externally, draws filled rectangle.
193  */
194 template<typename PSO>
pie_DrawRect(float x0,float y0,float x1,float y1,PIELIGHT colour)195 static void pie_DrawRect(float x0, float y0, float x1, float y1, PIELIGHT colour)
196 {
197 	if (x0 > x1)
198 	{
199 		std::swap(x0, x1);
200 	}
201 	if (y0 > y1)
202 	{
203 		std::swap(y0, y1);
204 	}
205 	const auto& center = Vector2f(x0, y0);
206 	const auto& mvp = defaultProjectionMatrix() * glm::translate(Vector3f(center, 0.f)) * glm::scale(glm::vec3(x1 - x0, y1 - y0, 1.f));
207 
208 	PSO::get().bind();
209 	PSO::get().bind_constants({ mvp, glm::vec2{}, glm::vec2{},
210 		glm::vec4(colour.vector[0] / 255.f, colour.vector[1] / 255.f, colour.vector[2] / 255.f, colour.vector[3] / 255.f) });
211 	PSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
212 	PSO::get().draw(4, 0);
213 	PSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
214 }
215 
pie_DrawMultiRect(std::vector<PIERECT_DrawRequest> rects)216 void pie_DrawMultiRect(std::vector<PIERECT_DrawRequest> rects)
217 {
218 	if (rects.empty()) { return; }
219 
220 	const auto projectionMatrix = defaultProjectionMatrix();
221 	bool didEnableRect = false;
222 	gfx_api::BoxFillPSO::get().bind();
223 
224 	for (auto it = rects.begin(); it != rects.end(); ++it)
225 	{
226 		if (it->x0 > it->x1)
227 		{
228 			std::swap(it->x0, it->x1);
229 		}
230 		if (it->y0 > it->y1)
231 		{
232 			std::swap(it->y0, it->y1);
233 		}
234 		const auto& colour = it->color;
235 		const auto& center = Vector2f(it->x0, it->y0);
236 		const auto& mvp = projectionMatrix * glm::translate(Vector3f(center, 0.f)) * glm::scale(glm::vec3(it->x1 - it->x0, it->y1 - it->y0, 1.f));
237 		gfx_api::BoxFillPSO::get().bind_constants({ mvp, glm::vec2(0.f), glm::vec2(0.f),
238 			glm::vec4(colour.vector[0] / 255.f, colour.vector[1] / 255.f, colour.vector[2] / 255.f, colour.vector[3] / 255.f) });
239 		if (!didEnableRect)
240 		{
241 			gfx_api::BoxFillPSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
242 			didEnableRect = true;
243 		}
244 
245 		gfx_api::BoxFillPSO::get().draw(4, 0);
246 	}
247 	gfx_api::BoxFillPSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
248 }
249 
iV_ShadowBox(int x0,int y0,int x1,int y1,int pad,PIELIGHT first,PIELIGHT second,PIELIGHT fill)250 void iV_ShadowBox(int x0, int y0, int x1, int y1, int pad, PIELIGHT first, PIELIGHT second, PIELIGHT fill)
251 {
252 	pie_DrawRect<gfx_api::ShadowBox2DPSO>(x0 + pad, y0 + pad, x1 - pad, y1 - pad, fill);
253 	iV_Box2(x0, y0, x1, y1, first, second);
254 }
255 
256 /***************************************************************************/
257 
iV_Box2(int x0,int y0,int x1,int y1,PIELIGHT first,PIELIGHT second)258 void iV_Box2(int x0, int y0, int x1, int y1, PIELIGHT first, PIELIGHT second)
259 {
260 	const glm::mat4 mat = glm::ortho(0.f, static_cast<float>(pie_GetVideoBufferWidth()), static_cast<float>(pie_GetVideoBufferHeight()), 0.f);
261 	const glm::vec4 firstColor(
262 		first.vector[0] / 255.f,
263 		first.vector[1] / 255.f,
264 		first.vector[2] / 255.f,
265 		first.vector[3] / 255.f
266 	);
267 	gfx_api::LinePSO::get().bind();
268 	gfx_api::LinePSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
269 	gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(x0, y1), glm::vec2(x0, y0), firstColor });
270 	gfx_api::LinePSO::get().draw(2, 0);
271 	gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(x0, y0), glm::vec2(x1, y0), firstColor });
272 	gfx_api::LinePSO::get().draw(2, 0);
273 
274 	const glm::vec4 secondColor(
275 		second.vector[0] / 255.f,
276 		second.vector[1] / 255.f,
277 		second.vector[2] / 255.f,
278 		second.vector[3] / 255.f
279 	);
280 	gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(x1, y0), glm::vec2(x1, y1), secondColor });
281 	gfx_api::LinePSO::get().draw(2, 0);
282 	gfx_api::LinePSO::get().bind_constants({ mat, glm::vec2(x0, y1), glm::vec2(x1, y1), secondColor });
283 	gfx_api::LinePSO::get().draw(2, 0);
284 	gfx_api::LinePSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
285 }
286 
287 /***************************************************************************/
288 
pie_BoxFill(int x0,int y0,int x1,int y1,PIELIGHT colour)289 void pie_BoxFill(int x0, int y0, int x1, int y1, PIELIGHT colour)
290 {
291 	pie_DrawRect<gfx_api::BoxFillPSO>(x0, y0, x1, y1, colour);
292 }
293 
pie_BoxFill_alpha(int x0,int y0,int x1,int y1,PIELIGHT colour)294 void pie_BoxFill_alpha(int x0, int y0, int x1, int y1, PIELIGHT colour)
295 {
296 	pie_DrawRect<gfx_api::BoxFillAlphaPSO>(x0, y0, x1, y1, colour);
297 }
298 
299 /***************************************************************************/
300 
iV_TransBoxFill(float x0,float y0,float x1,float y1)301 void iV_TransBoxFill(float x0, float y0, float x1, float y1)
302 {
303 	pie_UniTransBoxFill(x0, y0, x1, y1, WZCOL_TRANSPARENT_BOX);
304 }
305 
306 /***************************************************************************/
307 
pie_UniTransBoxFill(float x0,float y0,float x1,float y1,PIELIGHT light)308 void pie_UniTransBoxFill(float x0, float y0, float x1, float y1, PIELIGHT light)
309 {
310 	pie_DrawRect<gfx_api::UniTransBoxPSO>(x0, y0, x1, y1, light);
311 }
312 
313 /***************************************************************************/
314 
assertValidImage(IMAGEFILE * imageFile,unsigned id)315 static bool assertValidImage(IMAGEFILE *imageFile, unsigned id)
316 {
317 	ASSERT_OR_RETURN(false, id < imageFile->imageDefs.size(), "Out of range 1: %u/%d", id, (int)imageFile->imageDefs.size());
318 	ASSERT_OR_RETURN(false, imageFile->imageDefs[id].TPageID < imageFile->pages.size(), "Out of range 2: %zu", imageFile->imageDefs[id].TPageID);
319 	return true;
320 }
321 
322 template<typename PSO>
iv_DrawImageImpl(gfx_api::texture & TextureID,Vector2f offset,Vector2f size,Vector2f TextureUV,Vector2f TextureSize,PIELIGHT colour,const glm::mat4 & modelViewProjection,SHADER_MODE mode=SHADER_TEXRECT)323 static void iv_DrawImageImpl(gfx_api::texture& TextureID, Vector2f offset, Vector2f size, Vector2f TextureUV, Vector2f TextureSize, PIELIGHT colour, const glm::mat4 &modelViewProjection, SHADER_MODE mode = SHADER_TEXRECT)
324 {
325 	glm::mat4 transformMat = modelViewProjection * glm::translate(glm::vec3(offset.x, offset.y, 0.f)) * glm::scale(glm::vec3(size.x, size.y, 1.f));
326 
327 	PSO::get().bind();
328 	PSO::get().bind_constants({ transformMat,
329 		TextureUV,
330 		TextureSize,
331 		glm::vec4(colour.vector[0] / 255.f, colour.vector[1] / 255.f, colour.vector[2] / 255.f, colour.vector[3] / 255.f), 0});
332 	PSO::get().bind_textures(&TextureID);
333 	PSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
334 	PSO::get().draw(4, 0);
335 	PSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
336 }
337 
iV_DrawImageAnisotropic(gfx_api::texture & TextureID,Vector2i Position,Vector2f offset,Vector2f size,float angle,PIELIGHT colour)338 void iV_DrawImageAnisotropic(gfx_api::texture& TextureID, Vector2i Position, Vector2f offset, Vector2f size, float angle, PIELIGHT colour)
339 {
340 	glm::mat4 mvp = defaultProjectionMatrix() * glm::translate(glm::vec3(Position.x, Position.y, 0)) * glm::rotate(angle, glm::vec3(0.f, 0.f, 1.f));
341 
342 	iv_DrawImageImpl<gfx_api::DrawImageAnisotropicPSO>(TextureID, offset, size, Vector2f(0.f, 0.f), Vector2f(1.f, 1.f), colour, mvp);
343 }
344 
iV_DrawImageText(gfx_api::texture & TextureID,Vector2i Position,Vector2f offset,Vector2f size,float angle,PIELIGHT colour)345 void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i Position, Vector2f offset, Vector2f size, float angle, PIELIGHT colour)
346 {
347 	glm::mat4 mvp = defaultProjectionMatrix() * glm::translate(glm::vec3(Position.x, Position.y, 0)) * glm::rotate(RADIANS(angle), glm::vec3(0.f, 0.f, 1.f));
348 
349 	iv_DrawImageImpl<gfx_api::DrawImageTextPSO>(TextureID, offset, size, Vector2f(0.f, 0.f), Vector2f(1.f, 1.f), colour, mvp, SHADER_TEXT);
350 }
351 
iV_DrawImageTextClipped(gfx_api::texture & TextureID,Vector2i textureSize,Vector2i Position,Vector2f offset,Vector2f size,float angle,PIELIGHT colour,WzRect clippingRect)352 void iV_DrawImageTextClipped(gfx_api::texture& TextureID, Vector2i textureSize, Vector2i Position, Vector2f offset, Vector2f size, float angle, PIELIGHT colour, WzRect clippingRect)
353 {
354 	glm::mat4 mvp = defaultProjectionMatrix() * glm::translate(glm::vec3(Position.x, Position.y, 0)) * glm::rotate(RADIANS(angle), glm::vec3(0.f, 0.f, 1.f));
355 
356 	gfx_api::gfxFloat invTextureSizeX = 1.f / textureSize.x;
357 	gfx_api::gfxFloat invTextureSizeY = 1.f / textureSize.y;
358 	float tu = (float)(clippingRect.x()) * invTextureSizeX;
359 	float tv = (float)(clippingRect.y()) * invTextureSizeY;
360 	float su = (float)(clippingRect.x() + clippingRect.width()) * invTextureSizeX;
361 	float sv = (float)(clippingRect.y() + clippingRect.height()) * invTextureSizeY;
362 
363 	iv_DrawImageImpl<gfx_api::DrawImageTextPSO>(TextureID, offset, size, Vector2f(tu, tv), Vector2f(su, sv), colour, mvp, SHADER_TEXT);
364 }
365 
pie_DrawImage(IMAGEFILE * imageFile,int id,Vector2i size,const PIERECT * dest,PIELIGHT colour,const glm::mat4 & modelViewProjection,Vector2i textureInset=Vector2i (0,0))366 static void pie_DrawImage(IMAGEFILE *imageFile, int id, Vector2i size, const PIERECT *dest, PIELIGHT colour, const glm::mat4 &modelViewProjection, Vector2i textureInset = Vector2i(0, 0))
367 {
368 	ImageDef const &image2 = imageFile->imageDefs[id];
369 	size_t texPage = imageFile->pages[image2.TPageID].id;
370 	gfx_api::gfxFloat invTextureSize = 1.f / (float)imageFile->pages[image2.TPageID].size;
371 	float tu = (float)(image2.Tu + textureInset.x) * invTextureSize;
372 	float tv = (float)(image2.Tv + textureInset.y) * invTextureSize;
373 	float su = (float)(size.x - (textureInset.x * 2)) * invTextureSize;
374 	float sv = (float)(size.y - (textureInset.y * 2)) * invTextureSize;
375 
376 	glm::mat4 mvp = modelViewProjection * glm::translate(glm::vec3((float)dest->x, (float)dest->y, 0.f));
377 
378 	iv_DrawImageImpl<gfx_api::DrawImagePSO>(pie_Texture(texPage), Vector2i(0, 0), Vector2i(dest->w, dest->h), Vector2f(tu, tv), Vector2f(su, sv), colour, mvp);
379 }
380 
pie_DrawMultipleImages(const std::list<PieDrawImageRequest> & requests)381 static void pie_DrawMultipleImages(const std::list<PieDrawImageRequest>& requests)
382 {
383 	if (requests.empty()) { return; }
384 
385 	bool didEnableRect = false;
386 	gfx_api::DrawImagePSO::get().bind();
387 
388 	for (auto& request : requests)
389 	{
390 		// The following is the equivalent of:
391 		// pie_DrawImage(request.imageFile, request.ID, request.size, &request.dest, request.colour, request.modelViewProjection, request.textureInset)
392 		// but is tweaked to use custom implementation of iv_DrawImageImpl that does not disable the shader after every glDrawArrays call.
393 
394 		ImageDef const &image2 = request.imageFile->imageDefs[request.ID];
395 		size_t texPage = request.imageFile->pages[image2.TPageID].id;
396 		gfx_api::gfxFloat invTextureSize = 1.f / (float)request.imageFile->pages[image2.TPageID].size;
397 		float tu = (float)(image2.Tu + request.textureInset.x) * invTextureSize;
398 		float tv = (float)(image2.Tv + request.textureInset.y) * invTextureSize;
399 		float su = (float)(request.size.x - (request.textureInset.x * 2)) * invTextureSize;
400 		float sv = (float)(request.size.y - (request.textureInset.y * 2)) * invTextureSize;
401 
402 		glm::mat4 mvp = request.modelViewProjection * glm::translate(glm::vec3((float)request.dest.x, (float)request.dest.y, 0.f));
403 
404 		gfx_api::texture& TextureID = pie_Texture(texPage);
405 		Vector2f offset = Vector2f(0.f, 0.f);
406 		Vector2f size = Vector2f(request.dest.w, request.dest.h);
407 		Vector2f TextureUV = Vector2f(tu, tv);
408 		Vector2f TextureSize = Vector2f(su, sv);
409 		glm::mat4 transformMat = mvp * glm::translate(glm::vec3(offset.x, offset.y, 0.f)) * glm::scale(glm::vec3(size.x, size.y, 1.f));
410 
411 		gfx_api::DrawImagePSO::get().bind_constants({ transformMat,
412 			TextureUV,
413 			TextureSize,
414 			glm::vec4(request.colour.vector[0] / 255.f, request.colour.vector[1] / 255.f, request.colour.vector[2] / 255.f, request.colour.vector[3] / 255.f), 0});
415 
416 		gfx_api::DrawImagePSO::get().bind_textures(&TextureID);
417 
418 		if (!didEnableRect)
419 		{
420 			gfx_api::DrawImagePSO::get().bind_vertex_buffers(pie_internal::rectBuffer);
421 			didEnableRect = true;
422 		}
423 
424 		gfx_api::DrawImagePSO::get().draw(4, 0);
425 	}
426 	gfx_api::DrawImagePSO::get().unbind_vertex_buffers(pie_internal::rectBuffer);
427 }
428 
makePieImage(IMAGEFILE * imageFile,unsigned id,PIERECT * dest,int x,int y)429 static Vector2i makePieImage(IMAGEFILE *imageFile, unsigned id, PIERECT *dest, int x, int y)
430 {
431 	ImageDef const &image = imageFile->imageDefs[id];
432 	Vector2i pieImage;
433 	pieImage.x = image.Width;
434 	pieImage.y = image.Height;
435 	if (dest != nullptr)
436 	{
437 		dest->x = x + image.XOffset;
438 		dest->y = y + image.YOffset;
439 		dest->w = image.Width;
440 		dest->h = image.Height;
441 	}
442 	return pieImage;
443 }
444 
iV_DrawImage2(const WzString & filename,float x,float y,float width,float height)445 void iV_DrawImage2(const WzString &filename, float x, float y, float width, float height)
446 {
447 	if (filename.isEmpty()) { return; }
448 	ImageDef *image = iV_GetImage(filename);
449 	ASSERT_OR_RETURN(, image != nullptr, "No image found for filename: %s", filename.toUtf8().c_str());
450 	const gfx_api::gfxFloat invTextureSize = image->invTextureSize;
451 	const int tu = image->Tu;
452 	const int tv = image->Tv;
453 	const int w = width > 0 ? width : image->Width;
454 	const int h = height > 0 ? height : image->Height;
455 	x += image->XOffset;
456 	y += image->YOffset;
457 
458 	glm::mat4 mvp = defaultProjectionMatrix() * glm::translate(glm::vec3(x, y, 0));
459 	iv_DrawImageImpl<gfx_api::DrawImagePSO>(pie_Texture(image->textureId), Vector2i(0, 0), Vector2i(w, h),
460 		Vector2f(tu * invTextureSize, tv * invTextureSize),
461 		Vector2f(image->Width * invTextureSize, image->Height * invTextureSize),
462 		WZCOL_WHITE, mvp);
463 }
464 
iV_DrawImage(IMAGEFILE * ImageFile,UWORD ID,int x,int y,const glm::mat4 & modelViewProjection,BatchedImageDrawRequests * pBatchedRequests)465 void iV_DrawImage(IMAGEFILE *ImageFile, UWORD ID, int x, int y, const glm::mat4 &modelViewProjection, BatchedImageDrawRequests* pBatchedRequests)
466 {
467 	if (!assertValidImage(ImageFile, ID))
468 	{
469 		return;
470 	}
471 
472 	PIERECT dest;
473 	Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
474 
475 	if (pBatchedRequests == nullptr)
476 	{
477 		gfx_api::DrawImagePSO::get().bind();
478 		pie_DrawImage(ImageFile, ID, pieImage, &dest, WZCOL_WHITE, modelViewProjection);
479 	}
480 	else
481 	{
482 		pBatchedRequests->queuePieImageDraw(REND_ALPHA, ImageFile, ID, pieImage, dest, WZCOL_WHITE, modelViewProjection);
483 		pBatchedRequests->draw(); // draw only if not deferred
484 	}
485 }
486 
iV_DrawImageTc(Image image,Image imageTc,int x,int y,PIELIGHT colour,const glm::mat4 & modelViewProjection)487 void iV_DrawImageTc(Image image, Image imageTc, int x, int y, PIELIGHT colour, const glm::mat4 &modelViewProjection)
488 {
489 	if (!assertValidImage(image.images, image.id) || !assertValidImage(imageTc.images, imageTc.id))
490 	{
491 		return;
492 	}
493 
494 	PIERECT dest;
495 	Vector2i pieImage   = makePieImage(image.images, image.id, &dest, x, y);
496 	Vector2i pieImageTc = makePieImage(imageTc.images, imageTc.id);
497 
498 	gfx_api::DrawImagePSO::get().bind();
499 
500 	pie_DrawImage(image.images, image.id, pieImage, &dest, WZCOL_WHITE, modelViewProjection);
501 	pie_DrawImage(imageTc.images, imageTc.id, pieImageTc, &dest, colour, modelViewProjection);
502 }
503 
504 // Repeat a texture
iV_DrawImageRepeatX(IMAGEFILE * ImageFile,UWORD ID,int x,int y,int Width,const glm::mat4 & modelViewProjection,bool enableHorizontalTilingSeamWorkaround,BatchedImageDrawRequests * pBatchedRequests)505 void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width, const glm::mat4 &modelViewProjection, bool enableHorizontalTilingSeamWorkaround, BatchedImageDrawRequests *pBatchedRequests)
506 {
507 	static BatchedImageDrawRequests localBatch;
508 	if (pBatchedRequests == nullptr)
509 	{
510 		localBatch.clear();
511 		pBatchedRequests = &localBatch;
512 	}
513 
514 	assertValidImage(ImageFile, ID);
515 	const ImageDef *Image = &ImageFile->imageDefs[ID];
516 
517 	REND_MODE mode = REND_OPAQUE;
518 
519 	PIERECT dest;
520 	Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
521 
522 	unsigned int usableImageWidth = Image->Width;
523 	unsigned int imageXInset = 0;
524 	if (enableHorizontalTilingSeamWorkaround)
525 	{
526 		// Inset the portion of the image that is used by 1 on both the left + right sides
527 		usableImageWidth -= 2;
528 		imageXInset = 1;
529 		dest.w -= 2;
530 	}
531 	assert(usableImageWidth > 0);
532 
533 	unsigned hRemainder = (Width % usableImageWidth);
534 
535 	for (unsigned hRep = 0; hRep < Width / usableImageWidth; hRep++)
536 	{
537 		pBatchedRequests->queuePieImageDraw(mode, ImageFile, ID, pieImage, dest, WZCOL_WHITE, modelViewProjection, Vector2i(imageXInset, 0));
538 		dest.x += usableImageWidth;
539 	}
540 
541 	// draw remainder
542 	if (hRemainder > 0)
543 	{
544 		pieImage.x = hRemainder;
545 		dest.w = hRemainder;
546 		pBatchedRequests->queuePieImageDraw(mode, ImageFile, ID, pieImage, dest, WZCOL_WHITE, modelViewProjection, Vector2i(imageXInset, 0));
547 	}
548 
549 	// draw batched requests (unless batch is deferred)
550 	pBatchedRequests->draw();
551 }
552 
iV_DrawImageRepeatY(IMAGEFILE * ImageFile,UWORD ID,int x,int y,int Height,const glm::mat4 & modelViewProjection,BatchedImageDrawRequests * pBatchedRequests)553 void iV_DrawImageRepeatY(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Height, const glm::mat4 &modelViewProjection, BatchedImageDrawRequests* pBatchedRequests)
554 {
555 	static BatchedImageDrawRequests localBatch;
556 	if (pBatchedRequests == nullptr)
557 	{
558 		localBatch.clear();
559 		pBatchedRequests = &localBatch;
560 	}
561 
562 	assertValidImage(ImageFile, ID);
563 	const ImageDef *Image = &ImageFile->imageDefs[ID];
564 
565 	REND_MODE mode = REND_OPAQUE;
566 
567 	PIERECT dest;
568 	Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);
569 
570 	unsigned vRemainder = Height % Image->Height;
571 
572 	for (unsigned vRep = 0; vRep < Height / Image->Height; vRep++)
573 	{
574 		pBatchedRequests->queuePieImageDraw(mode, ImageFile, ID, pieImage, dest, WZCOL_WHITE, modelViewProjection);
575 		dest.y += Image->Height;
576 	}
577 
578 	// draw remainder
579 	if (vRemainder > 0)
580 	{
581 		pieImage.y = vRemainder;
582 		dest.h = vRemainder;
583 		pBatchedRequests->queuePieImageDraw(mode, ImageFile, ID, pieImage, dest, WZCOL_WHITE, modelViewProjection);
584 	}
585 
586 	// draw batched requests (unless batch is deferred)
587 	pBatchedRequests->draw();
588 }
589 
pie_InitRadar()590 bool pie_InitRadar()
591 {
592 	radarGfx = new GFX(GFX_TEXTURE, 2);
593 	return true;
594 }
595 
pie_ShutdownRadar()596 bool pie_ShutdownRadar()
597 {
598 	delete radarGfx;
599 	radarGfx = nullptr;
600 	pie_ViewingWindow_Shutdown();
601 	return true;
602 }
603 
pie_SetRadar(gfx_api::gfxFloat x,gfx_api::gfxFloat y,gfx_api::gfxFloat width,gfx_api::gfxFloat height,size_t twidth,size_t theight)604 void pie_SetRadar(gfx_api::gfxFloat x, gfx_api::gfxFloat y, gfx_api::gfxFloat width, gfx_api::gfxFloat height, size_t twidth, size_t theight)
605 {
606 	radarGfx->makeTexture(twidth, theight);
607 	//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  // Want GL_LINEAR (or GL_LINEAR_MIPMAP_NEAREST) for min filter, but GL_NEAREST for mag filter. // TODO: Add a gfx_api::sampler_type to handle this case? bilinear, but nearest for mag?
608 	gfx_api::gfxFloat texcoords[] = { 0.0f, 0.0f,  1.0f, 0.0f,  0.0f, 1.0f,  1.0f, 1.0f };
609 	gfx_api::gfxFloat vertices[] = { x, y,  x + width, y,  x, y + height,  x + width, y + height };
610 	radarGfx->buffers(4, vertices, texcoords);
611 }
612 
613 /** Store radar texture with given width and height. */
pie_DownLoadRadar(UDWORD * buffer)614 void pie_DownLoadRadar(UDWORD *buffer)
615 {
616 	radarGfx->updateTexture(buffer);
617 }
618 
619 /** Display radar texture using the given height and width, depending on zoom level. */
pie_RenderRadar(const glm::mat4 & modelViewProjectionMatrix)620 void pie_RenderRadar(const glm::mat4 &modelViewProjectionMatrix)
621 {
622 	gfx_api::RadarPSO::get().bind();
623 	gfx_api::RadarPSO::get().bind_constants({ modelViewProjectionMatrix, glm::vec2(0), glm::vec2(0), glm::vec4(1), 0 });
624 	radarGfx->draw<gfx_api::RadarPSO>(modelViewProjectionMatrix);
625 }
626 
627 /// Load and display a random backdrop picture.
pie_LoadBackDrop(SCREENTYPE screenType)628 void pie_LoadBackDrop(SCREENTYPE screenType)
629 {
630 	switch (screenType)
631 	{
632 	case SCREEN_RANDOMBDROP:
633 		screen_SetRandomBackdrop("texpages/bdrops/", "backdrop");
634 		break;
635 	case SCREEN_MISSIONEND:
636 		screen_SetRandomBackdrop("texpages/bdrops/", "missionend");
637 		break;
638 	}
639 }
640 
defaultProjectionMatrix()641 glm::mat4 defaultProjectionMatrix()
642 {
643         float w = pie_GetVideoBufferWidth();
644         float h = pie_GetVideoBufferHeight();
645 
646         return glm::ortho(0.f, static_cast<float>(w), static_cast<float>(h), 0.f);
647 }
648 
649 
clear()650 void BatchedImageDrawRequests::clear()
651 {
652 	ASSERT(_imageDrawRequests.empty(), "Clearing a BatchedImageDrawRequests that isn't empty. Images have not been drawn!");
653 	_imageDrawRequests.clear();
654 }
655 
draw(bool force)656 bool BatchedImageDrawRequests::draw(bool force /*= false*/)
657 {
658 	if (deferRender && !force) { return false; }
659 	pie_DrawMultipleImages(_imageDrawRequests);
660 	_imageDrawRequests.clear();
661 	return true;
662 }
663