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