1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <base/math.h>
4
5#include <engine/graphics.h>
6#include <engine/map.h>
7#include <engine/textrender.h>
8
9#include <engine/shared/config.h>
10#include <engine/shared/datafile.h>
11#include <engine/shared/map.h>
12
13#include "render.h"
14
15#include <game/generated/client_data.h>
16
17#include <game/mapitems.h>
18#include <game/mapitems_ex.h>
19
20#include <chrono>
21#include <cmath>
22
23using namespace std::chrono_literals;
24
25CMapBasedEnvelopePointAccess::CMapBasedEnvelopePointAccess(CDataFileReader *pReader)
26{
27 bool FoundBezierEnvelope = false;
28 int EnvStart, EnvNum;
29 pReader->GetType(Type: MAPITEMTYPE_ENVELOPE, pStart: &EnvStart, pNum: &EnvNum);
30 for(int EnvIndex = 0; EnvIndex < EnvNum; EnvIndex++)
31 {
32 CMapItemEnvelope *pEnvelope = static_cast<CMapItemEnvelope *>(pReader->GetItem(Index: EnvStart + EnvIndex));
33 if(pEnvelope->m_Version >= CMapItemEnvelope_v3::CURRENT_VERSION)
34 {
35 FoundBezierEnvelope = true;
36 break;
37 }
38 }
39
40 if(FoundBezierEnvelope)
41 {
42 m_pPoints = nullptr;
43 m_pPointsBezier = nullptr;
44
45 int EnvPointStart, FakeEnvPointNum;
46 pReader->GetType(Type: MAPITEMTYPE_ENVPOINTS, pStart: &EnvPointStart, pNum: &FakeEnvPointNum);
47 if(FakeEnvPointNum > 0)
48 m_pPointsBezierUpstream = static_cast<CEnvPointBezier_upstream *>(pReader->GetItem(Index: EnvPointStart));
49 else
50 m_pPointsBezierUpstream = nullptr;
51
52 m_NumPointsMax = pReader->GetItemSize(Index: EnvPointStart) / sizeof(CEnvPointBezier_upstream);
53 }
54 else
55 {
56 int EnvPointStart, FakeEnvPointNum;
57 pReader->GetType(Type: MAPITEMTYPE_ENVPOINTS, pStart: &EnvPointStart, pNum: &FakeEnvPointNum);
58 if(FakeEnvPointNum > 0)
59 m_pPoints = static_cast<CEnvPoint *>(pReader->GetItem(Index: EnvPointStart));
60 else
61 m_pPoints = nullptr;
62
63 m_NumPointsMax = pReader->GetItemSize(Index: EnvPointStart) / sizeof(CEnvPoint);
64
65 int EnvPointBezierStart, FakeEnvPointBezierNum;
66 pReader->GetType(Type: MAPITEMTYPE_ENVPOINTS_BEZIER, pStart: &EnvPointBezierStart, pNum: &FakeEnvPointBezierNum);
67 const int NumPointsBezier = pReader->GetItemSize(Index: EnvPointBezierStart) / sizeof(CEnvPointBezier);
68 if(FakeEnvPointBezierNum > 0 && m_NumPointsMax == NumPointsBezier)
69 m_pPointsBezier = static_cast<CEnvPointBezier *>(pReader->GetItem(Index: EnvPointBezierStart));
70 else
71 m_pPointsBezier = nullptr;
72
73 m_pPointsBezierUpstream = nullptr;
74 }
75
76 SetPointsRange(StartPoint: 0, NumPoints: m_NumPointsMax);
77}
78
79CMapBasedEnvelopePointAccess::CMapBasedEnvelopePointAccess(IMap *pMap) :
80 CMapBasedEnvelopePointAccess(static_cast<CMap *>(pMap)->GetReader())
81{
82}
83
84void CMapBasedEnvelopePointAccess::SetPointsRange(int StartPoint, int NumPoints)
85{
86 m_StartPoint = clamp(val: StartPoint, lo: 0, hi: m_NumPointsMax);
87 m_NumPoints = clamp(val: NumPoints, lo: 0, hi: maximum(a: m_NumPointsMax - StartPoint, b: 0));
88}
89
90int CMapBasedEnvelopePointAccess::StartPoint() const
91{
92 return m_StartPoint;
93}
94
95int CMapBasedEnvelopePointAccess::NumPoints() const
96{
97 return m_NumPoints;
98}
99
100int CMapBasedEnvelopePointAccess::NumPointsMax() const
101{
102 return m_NumPointsMax;
103}
104
105const CEnvPoint *CMapBasedEnvelopePointAccess::GetPoint(int Index) const
106{
107 if(Index < 0 || Index >= m_NumPoints)
108 return nullptr;
109 if(m_pPoints != nullptr)
110 return &m_pPoints[Index + m_StartPoint];
111 if(m_pPointsBezierUpstream != nullptr)
112 return &m_pPointsBezierUpstream[Index + m_StartPoint];
113 return nullptr;
114}
115
116const CEnvPointBezier *CMapBasedEnvelopePointAccess::GetBezier(int Index) const
117{
118 if(Index < 0 || Index >= m_NumPoints)
119 return nullptr;
120 if(m_pPointsBezier != nullptr)
121 return &m_pPointsBezier[Index + m_StartPoint];
122 if(m_pPointsBezierUpstream != nullptr)
123 return &m_pPointsBezierUpstream[Index + m_StartPoint].m_Bezier;
124 return nullptr;
125}
126
127static double CubicRoot(double x)
128{
129 if(x == 0.0)
130 return 0.0;
131 else if(x < 0.0)
132 return -std::exp(x: std::log(x: -x) / 3.0);
133 else
134 return std::exp(x: std::log(x: x) / 3.0);
135}
136
137static float SolveBezier(float x, float p0, float p1, float p2, float p3)
138{
139 const double x3 = -p0 + 3.0 * p1 - 3.0 * p2 + p3;
140 const double x2 = 3.0 * p0 - 6.0 * p1 + 3.0 * p2;
141 const double x1 = -3.0 * p0 + 3.0 * p1;
142 const double x0 = p0 - x;
143
144 if(x3 == 0.0 && x2 == 0.0)
145 {
146 // linear
147 // a * t + b = 0
148 const double a = x1;
149 const double b = x0;
150
151 if(a == 0.0)
152 return 0.0f;
153 return -b / a;
154 }
155 else if(x3 == 0.0)
156 {
157 // quadratic
158 // t * t + b * t + c = 0
159 const double b = x1 / x2;
160 const double c = x0 / x2;
161
162 if(c == 0.0)
163 return 0.0f;
164
165 const double D = b * b - 4.0 * c;
166 const double SqrtD = std::sqrt(x: D);
167
168 const double t = (-b + SqrtD) / 2.0;
169
170 if(0.0 <= t && t <= 1.0001)
171 return t;
172 return (-b - SqrtD) / 2.0;
173 }
174 else
175 {
176 // cubic
177 // t * t * t + a * t * t + b * t * t + c = 0
178 const double a = x2 / x3;
179 const double b = x1 / x3;
180 const double c = x0 / x3;
181
182 // substitute t = y - a / 3
183 const double sub = a / 3.0;
184
185 // depressed form x^3 + px + q = 0
186 // cardano's method
187 const double p = b / 3.0 - a * a / 9.0;
188 const double q = (2.0 * a * a * a / 27.0 - a * b / 3.0 + c) / 2.0;
189
190 const double D = q * q + p * p * p;
191
192 if(D > 0.0)
193 {
194 // only one 'real' solution
195 const double s = std::sqrt(x: D);
196 return CubicRoot(x: s - q) - CubicRoot(x: s + q) - sub;
197 }
198 else if(D == 0.0)
199 {
200 // one single, one double solution or triple solution
201 const double s = CubicRoot(x: -q);
202 const double t = 2.0 * s - sub;
203
204 if(0.0 <= t && t <= 1.0001)
205 return t;
206 return (-s - sub);
207 }
208 else
209 {
210 // Casus irreducibilis ... ,_,
211 const double phi = std::acos(x: -q / std::sqrt(x: -(p * p * p))) / 3.0;
212 const double s = 2.0 * std::sqrt(x: -p);
213
214 const double t1 = s * std::cos(x: phi) - sub;
215
216 if(0.0 <= t1 && t1 <= 1.0001)
217 return t1;
218
219 const double t2 = -s * std::cos(x: phi + pi / 3.0) - sub;
220
221 if(0.0 <= t2 && t2 <= 1.0001)
222 return t2;
223 return -s * std::cos(x: phi - pi / 3.0) - sub;
224 }
225 }
226}
227
228void CRenderTools::RenderEvalEnvelope(const IEnvelopePointAccess *pPoints, std::chrono::nanoseconds TimeNanos, ColorRGBA &Result, size_t Channels)
229{
230 const int NumPoints = pPoints->NumPoints();
231 if(NumPoints == 0)
232 {
233 return;
234 }
235
236 if(NumPoints == 1)
237 {
238 const CEnvPoint *pFirstPoint = pPoints->GetPoint(Index: 0);
239 for(size_t c = 0; c < Channels; c++)
240 {
241 Result[c] = fx2f(v: pFirstPoint->m_aValues[c]);
242 }
243 return;
244 }
245
246 const CEnvPoint *pLastPoint = pPoints->GetPoint(Index: NumPoints - 1);
247 const int64_t MaxPointTime = (int64_t)pLastPoint->m_Time * std::chrono::nanoseconds(1ms).count();
248 if(MaxPointTime > 0) // TODO: remove this check when implementing a IO check for maps(in this case broken envelopes)
249 TimeNanos = std::chrono::nanoseconds(TimeNanos.count() % MaxPointTime);
250 else
251 TimeNanos = decltype(TimeNanos)::zero();
252
253 const double TimeMillis = TimeNanos.count() / (double)std::chrono::nanoseconds(1ms).count();
254 for(int i = 0; i < NumPoints - 1; i++)
255 {
256 const CEnvPoint *pCurrentPoint = pPoints->GetPoint(Index: i);
257 const CEnvPoint *pNextPoint = pPoints->GetPoint(Index: i + 1);
258 if(TimeMillis >= pCurrentPoint->m_Time && TimeMillis < pNextPoint->m_Time)
259 {
260 const float Delta = pNextPoint->m_Time - pCurrentPoint->m_Time;
261 float a = (float)(TimeMillis - pCurrentPoint->m_Time) / Delta;
262
263 switch(pCurrentPoint->m_Curvetype)
264 {
265 case CURVETYPE_STEP:
266 a = 0.0f;
267 break;
268
269 case CURVETYPE_SLOW:
270 a = a * a * a;
271 break;
272
273 case CURVETYPE_FAST:
274 a = 1.0f - a;
275 a = 1.0f - a * a * a;
276 break;
277
278 case CURVETYPE_SMOOTH:
279 a = -2.0f * a * a * a + 3.0f * a * a; // second hermite basis
280 break;
281
282 case CURVETYPE_BEZIER:
283 {
284 const CEnvPointBezier *pCurrentPointBezier = pPoints->GetBezier(Index: i);
285 const CEnvPointBezier *pNextPointBezier = pPoints->GetBezier(Index: i + 1);
286 if(pCurrentPointBezier == nullptr || pNextPointBezier == nullptr)
287 break; // fallback to linear
288 for(size_t c = 0; c < Channels; c++)
289 {
290 // monotonic 2d cubic bezier curve
291 const vec2 p0 = vec2(pCurrentPoint->m_Time, fx2f(v: pCurrentPoint->m_aValues[c]));
292 const vec2 p3 = vec2(pNextPoint->m_Time, fx2f(v: pNextPoint->m_aValues[c]));
293
294 const vec2 OutTang = vec2(pCurrentPointBezier->m_aOutTangentDeltaX[c], fx2f(v: pCurrentPointBezier->m_aOutTangentDeltaY[c]));
295 const vec2 InTang = vec2(pNextPointBezier->m_aInTangentDeltaX[c], fx2f(v: pNextPointBezier->m_aInTangentDeltaY[c]));
296
297 vec2 p1 = p0 + OutTang;
298 vec2 p2 = p3 + InTang;
299
300 // validate bezier curve
301 p1.x = clamp(val: p1.x, lo: p0.x, hi: p3.x);
302 p2.x = clamp(val: p2.x, lo: p0.x, hi: p3.x);
303
304 // solve x(a) = time for a
305 a = clamp(val: SolveBezier(x: TimeMillis, p0: p0.x, p1: p1.x, p2: p2.x, p3: p3.x), lo: 0.0f, hi: 1.0f);
306
307 // value = y(t)
308 Result[c] = bezier(p0: p0.y, p1: p1.y, p2: p2.y, p3: p3.y, amount: a);
309 }
310 return;
311 }
312
313 case CURVETYPE_LINEAR: [[fallthrough]];
314 default:
315 break;
316 }
317
318 for(size_t c = 0; c < Channels; c++)
319 {
320 const float v0 = fx2f(v: pCurrentPoint->m_aValues[c]);
321 const float v1 = fx2f(v: pNextPoint->m_aValues[c]);
322 Result[c] = v0 + (v1 - v0) * a;
323 }
324
325 return;
326 }
327 }
328
329 for(size_t c = 0; c < Channels; c++)
330 {
331 Result[c] = fx2f(v: pLastPoint->m_aValues[c]);
332 }
333}
334
335static void Rotate(const CPoint *pCenter, CPoint *pPoint, float Rotation)
336{
337 int x = pPoint->x - pCenter->x;
338 int y = pPoint->y - pCenter->y;
339 pPoint->x = (int)(x * std::cos(x: Rotation) - y * std::sin(x: Rotation) + pCenter->x);
340 pPoint->y = (int)(x * std::sin(x: Rotation) + y * std::cos(x: Rotation) + pCenter->y);
341}
342
343void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser) const
344{
345 if(!g_Config.m_ClShowQuads || g_Config.m_ClOverlayEntities == 100)
346 return;
347
348 ForceRenderQuads(pQuads, NumQuads, Flags: RenderFlags, pfnEval, pUser, Alpha: (100 - g_Config.m_ClOverlayEntities) / 100.0f);
349}
350
351void CRenderTools::ForceRenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha) const
352{
353 Graphics()->TrianglesBegin();
354 float Conv = 1 / 255.0f;
355 for(int i = 0; i < NumQuads; i++)
356 {
357 CQuad *pQuad = &pQuads[i];
358
359 ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
360 pfnEval(pQuad->m_ColorEnvOffset, pQuad->m_ColorEnv, Color, 4, pUser);
361
362 if(Color.a <= 0.0f)
363 continue;
364
365 bool Opaque = false;
366 /* TODO: Analyze quadtexture
367 if(a < 0.01f || (q->m_aColors[0].a < 0.01f && q->m_aColors[1].a < 0.01f && q->m_aColors[2].a < 0.01f && q->m_aColors[3].a < 0.01f))
368 Opaque = true;
369 */
370 if(Opaque && !(RenderFlags & LAYERRENDERFLAG_OPAQUE))
371 continue;
372 if(!Opaque && !(RenderFlags & LAYERRENDERFLAG_TRANSPARENT))
373 continue;
374
375 Graphics()->QuadsSetSubsetFree(
376 x0: fx2f(v: pQuad->m_aTexcoords[0].x), y0: fx2f(v: pQuad->m_aTexcoords[0].y),
377 x1: fx2f(v: pQuad->m_aTexcoords[1].x), y1: fx2f(v: pQuad->m_aTexcoords[1].y),
378 x2: fx2f(v: pQuad->m_aTexcoords[2].x), y2: fx2f(v: pQuad->m_aTexcoords[2].y),
379 x3: fx2f(v: pQuad->m_aTexcoords[3].x), y3: fx2f(v: pQuad->m_aTexcoords[3].y));
380
381 ColorRGBA Position = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
382 pfnEval(pQuad->m_PosEnvOffset, pQuad->m_PosEnv, Position, 3, pUser);
383 const vec2 Offset = vec2(Position.r, Position.g);
384 const float Rotation = Position.b / 180.0f * pi;
385
386 IGraphics::CColorVertex Array[4] = {
387 IGraphics::CColorVertex(0, pQuad->m_aColors[0].r * Conv * Color.r, pQuad->m_aColors[0].g * Conv * Color.g, pQuad->m_aColors[0].b * Conv * Color.b, pQuad->m_aColors[0].a * Conv * Color.a * Alpha),
388 IGraphics::CColorVertex(1, pQuad->m_aColors[1].r * Conv * Color.r, pQuad->m_aColors[1].g * Conv * Color.g, pQuad->m_aColors[1].b * Conv * Color.b, pQuad->m_aColors[1].a * Conv * Color.a * Alpha),
389 IGraphics::CColorVertex(2, pQuad->m_aColors[2].r * Conv * Color.r, pQuad->m_aColors[2].g * Conv * Color.g, pQuad->m_aColors[2].b * Conv * Color.b, pQuad->m_aColors[2].a * Conv * Color.a * Alpha),
390 IGraphics::CColorVertex(3, pQuad->m_aColors[3].r * Conv * Color.r, pQuad->m_aColors[3].g * Conv * Color.g, pQuad->m_aColors[3].b * Conv * Color.b, pQuad->m_aColors[3].a * Conv * Color.a * Alpha)};
391 Graphics()->SetColorVertex(pArray: Array, Num: 4);
392
393 CPoint *pPoints = pQuad->m_aPoints;
394
395 CPoint aRotated[4];
396 if(Rotation != 0.0f)
397 {
398 for(size_t p = 0; p < std::size(aRotated); ++p)
399 {
400 aRotated[p] = pQuad->m_aPoints[p];
401 Rotate(pCenter: &pQuad->m_aPoints[4], pPoint: &aRotated[p], Rotation);
402 }
403 pPoints = aRotated;
404 }
405
406 IGraphics::CFreeformItem Freeform(
407 fx2f(v: pPoints[0].x) + Offset.x, fx2f(v: pPoints[0].y) + Offset.y,
408 fx2f(v: pPoints[1].x) + Offset.x, fx2f(v: pPoints[1].y) + Offset.y,
409 fx2f(v: pPoints[2].x) + Offset.x, fx2f(v: pPoints[2].y) + Offset.y,
410 fx2f(v: pPoints[3].x) + Offset.x, fx2f(v: pPoints[3].y) + Offset.y);
411 Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1);
412 }
413 Graphics()->TrianglesEnd();
414}
415
416void CRenderTools::RenderTileRectangle(int RectX, int RectY, int RectW, int RectH,
417 unsigned char IndexIn, unsigned char IndexOut,
418 float Scale, ColorRGBA Color, int RenderFlags) const
419{
420 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
421 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
422
423 // calculate the final pixelsize for the tiles
424 float TilePixelSize = 1024 / 32.0f;
425 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
426 float FinalTilesetScale = FinalTileSize / TilePixelSize;
427
428 if(Graphics()->HasTextureArraysSupport())
429 Graphics()->QuadsTex3DBegin();
430 else
431 Graphics()->QuadsBegin();
432 Graphics()->SetColor(Color);
433
434 int StartY = (int)(ScreenY0 / Scale) - 1;
435 int StartX = (int)(ScreenX0 / Scale) - 1;
436 int EndY = (int)(ScreenY1 / Scale) + 1;
437 int EndX = (int)(ScreenX1 / Scale) + 1;
438
439 // adjust the texture shift according to mipmap level
440 float TexSize = 1024.0f;
441 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
442 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
443
444 for(int y = StartY; y < EndY; y++)
445 {
446 for(int x = StartX; x < EndX; x++)
447 {
448 unsigned char Index = (x >= RectX && x < RectX + RectW && y >= RectY && y < RectY + RectH) ? IndexIn : IndexOut;
449 if(Index)
450 {
451 bool Render = false;
452 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
453 Render = true;
454
455 if(Render)
456 {
457 int tx = Index % 16;
458 int ty = Index / 16;
459 int Px0 = tx * (1024 / 16);
460 int Py0 = ty * (1024 / 16);
461 int Px1 = Px0 + (1024 / 16) - 1;
462 int Py1 = Py0 + (1024 / 16) - 1;
463
464 float x0 = Nudge + Px0 / TexSize + Frac;
465 float y0 = Nudge + Py0 / TexSize + Frac;
466 float x1 = Nudge + Px1 / TexSize - Frac;
467 float y1 = Nudge + Py0 / TexSize + Frac;
468 float x2 = Nudge + Px1 / TexSize - Frac;
469 float y2 = Nudge + Py1 / TexSize - Frac;
470 float x3 = Nudge + Px0 / TexSize + Frac;
471 float y3 = Nudge + Py1 / TexSize - Frac;
472
473 if(Graphics()->HasTextureArraysSupport())
474 {
475 x0 = 0;
476 y0 = 0;
477 x1 = x0 + 1;
478 y1 = y0;
479 x2 = x0 + 1;
480 y2 = y0 + 1;
481 x3 = x0;
482 y3 = y0 + 1;
483 }
484
485 if(Graphics()->HasTextureArraysSupport())
486 {
487 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
488 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
489 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
490 }
491 else
492 {
493 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
494 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
495 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
496 }
497 }
498 }
499 }
500 }
501
502 if(Graphics()->HasTextureArraysSupport())
503 Graphics()->QuadsTex3DEnd();
504 else
505 Graphics()->QuadsEnd();
506 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
507}
508
509void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) const
510{
511 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
512 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
513
514 // calculate the final pixelsize for the tiles
515 float TilePixelSize = 1024 / 32.0f;
516 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
517 float FinalTilesetScale = FinalTileSize / TilePixelSize;
518
519 if(Graphics()->HasTextureArraysSupport())
520 Graphics()->QuadsTex3DBegin();
521 else
522 Graphics()->QuadsBegin();
523 Graphics()->SetColor(Color);
524 const bool ColorOpaque = Color.a > 254.0f / 255.0f;
525
526 int StartY = (int)(ScreenY0 / Scale) - 1;
527 int StartX = (int)(ScreenX0 / Scale) - 1;
528 int EndY = (int)(ScreenY1 / Scale) + 1;
529 int EndX = (int)(ScreenX1 / Scale) + 1;
530
531 // adjust the texture shift according to mipmap level
532 float TexSize = 1024.0f;
533 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
534 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
535
536 for(int y = StartY; y < EndY; y++)
537 {
538 for(int x = StartX; x < EndX; x++)
539 {
540 int mx = x;
541 int my = y;
542
543 if(RenderFlags & TILERENDERFLAG_EXTEND)
544 {
545 if(mx < 0)
546 mx = 0;
547 if(mx >= w)
548 mx = w - 1;
549 if(my < 0)
550 my = 0;
551 if(my >= h)
552 my = h - 1;
553 }
554 else
555 {
556 if(mx < 0)
557 continue; // mx = 0;
558 if(mx >= w)
559 continue; // mx = w-1;
560 if(my < 0)
561 continue; // my = 0;
562 if(my >= h)
563 continue; // my = h-1;
564 }
565
566 int c = mx + my * w;
567
568 unsigned char Index = pTiles[c].m_Index;
569 if(Index)
570 {
571 unsigned char Flags = pTiles[c].m_Flags;
572
573 bool Render = false;
574 if(ColorOpaque && Flags & TILEFLAG_OPAQUE)
575 {
576 if(RenderFlags & LAYERRENDERFLAG_OPAQUE)
577 Render = true;
578 }
579 else
580 {
581 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
582 Render = true;
583 }
584
585 if(Render)
586 {
587 int tx = Index % 16;
588 int ty = Index / 16;
589 int Px0 = tx * (1024 / 16);
590 int Py0 = ty * (1024 / 16);
591 int Px1 = Px0 + (1024 / 16) - 1;
592 int Py1 = Py0 + (1024 / 16) - 1;
593
594 float x0 = Nudge + Px0 / TexSize + Frac;
595 float y0 = Nudge + Py0 / TexSize + Frac;
596 float x1 = Nudge + Px1 / TexSize - Frac;
597 float y1 = Nudge + Py0 / TexSize + Frac;
598 float x2 = Nudge + Px1 / TexSize - Frac;
599 float y2 = Nudge + Py1 / TexSize - Frac;
600 float x3 = Nudge + Px0 / TexSize + Frac;
601 float y3 = Nudge + Py1 / TexSize - Frac;
602
603 if(Graphics()->HasTextureArraysSupport())
604 {
605 x0 = 0;
606 y0 = 0;
607 x1 = x0 + 1;
608 y1 = y0;
609 x2 = x0 + 1;
610 y2 = y0 + 1;
611 x3 = x0;
612 y3 = y0 + 1;
613 }
614
615 if(Flags & TILEFLAG_XFLIP)
616 {
617 x0 = x2;
618 x1 = x3;
619 x2 = x3;
620 x3 = x0;
621 }
622
623 if(Flags & TILEFLAG_YFLIP)
624 {
625 y0 = y3;
626 y2 = y1;
627 y3 = y1;
628 y1 = y0;
629 }
630
631 if(Flags & TILEFLAG_ROTATE)
632 {
633 float Tmp = x0;
634 x0 = x3;
635 x3 = x2;
636 x2 = x1;
637 x1 = Tmp;
638 Tmp = y0;
639 y0 = y3;
640 y3 = y2;
641 y2 = y1;
642 y1 = Tmp;
643 }
644
645 if(Graphics()->HasTextureArraysSupport())
646 {
647 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
648 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
649 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
650 }
651 else
652 {
653 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
654 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
655 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
656 }
657 }
658 }
659 x += pTiles[c].m_Skip;
660 }
661 }
662
663 if(Graphics()->HasTextureArraysSupport())
664 Graphics()->QuadsTex3DEnd();
665 else
666 Graphics()->QuadsEnd();
667 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
668}
669
670void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha) const
671{
672 if(!g_Config.m_ClTextEntities)
673 return;
674
675 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
676 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
677
678 int StartY = (int)(ScreenY0 / Scale) - 1;
679 int StartX = (int)(ScreenX0 / Scale) - 1;
680 int EndY = (int)(ScreenY1 / Scale) + 1;
681 int EndX = (int)(ScreenX1 / Scale) + 1;
682
683 if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
684 return; // its useless to render text at this distance
685
686 float Size = g_Config.m_ClTextEntitiesSize / 100.f;
687 char aBuf[16];
688
689 TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
690 for(int y = StartY; y < EndY; y++)
691 {
692 for(int x = StartX; x < EndX; x++)
693 {
694 int mx = x;
695 int my = y;
696
697 if(mx < 0)
698 continue; // mx = 0;
699 if(mx >= w)
700 continue; // mx = w-1;
701 if(my < 0)
702 continue; // my = 0;
703 if(my >= h)
704 continue; // my = h-1;
705
706 int c = mx + my * w;
707
708 unsigned char Index = pTele[c].m_Number;
709 if(Index && IsTeleTileNumberUsedAny(Index: pTele[c].m_Type))
710 {
711 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", Index);
712 // Auto-resize text to fit inside the tile
713 float ScaledWidth = TextRender()->TextWidth(Size: Size * Scale, pText: aBuf, StrLength: -1);
714 float Factor = clamp(val: Scale / ScaledWidth, lo: 0.0f, hi: 1.0f);
715 float LocalSize = Size * Factor;
716 float ToCenterOffset = (1 - LocalSize) / 2.f;
717 TextRender()->Text(x: (mx + 0.5f) * Scale - (ScaledWidth * Factor) / 2.0f, y: (my + ToCenterOffset) * Scale, Size: LocalSize * Scale, pText: aBuf);
718 }
719 }
720 }
721 TextRender()->TextColor(rgb: TextRender()->DefaultTextColor());
722 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
723}
724
725void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, float Scale, float Alpha) const
726{
727 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
728 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
729
730 int StartY = (int)(ScreenY0 / Scale) - 1;
731 int StartX = (int)(ScreenX0 / Scale) - 1;
732 int EndY = (int)(ScreenY1 / Scale) + 1;
733 int EndX = (int)(ScreenX1 / Scale) + 1;
734
735 if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
736 return; // its useless to render text at this distance
737
738 float Size = g_Config.m_ClTextEntitiesSize / 100.f;
739 float ToCenterOffset = (1 - Size) / 2.f;
740 char aBuf[16];
741
742 TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
743 for(int y = StartY; y < EndY; y++)
744 {
745 for(int x = StartX; x < EndX; x++)
746 {
747 int mx = x;
748 int my = y;
749
750 if(mx < 0)
751 continue; // mx = 0;
752 if(mx >= w)
753 continue; // mx = w-1;
754 if(my < 0)
755 continue; // my = 0;
756 if(my >= h)
757 continue; // my = h-1;
758
759 int c = mx + my * w;
760
761 int Force = (int)pSpeedup[c].m_Force;
762 int MaxSpeed = (int)pSpeedup[c].m_MaxSpeed;
763 if(Force)
764 {
765 // draw arrow
766 Graphics()->TextureSet(Texture: g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_Id);
767 Graphics()->QuadsBegin();
768 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
769 SelectSprite(Id: SPRITE_SPEEDUP_ARROW);
770 Graphics()->QuadsSetRotation(Angle: pSpeedup[c].m_Angle * (pi / 180.0f));
771 DrawSprite(x: mx * Scale + 16, y: my * Scale + 16, Size: 35.0f);
772 Graphics()->QuadsEnd();
773
774 // draw force and max speed
775 if(g_Config.m_ClTextEntities)
776 {
777 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", Force);
778 TextRender()->Text(x: mx * Scale, y: (my + 0.5f + ToCenterOffset / 2) * Scale, Size: Size * Scale / 2.f, pText: aBuf);
779 if(MaxSpeed)
780 {
781 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", MaxSpeed);
782 TextRender()->Text(x: mx * Scale, y: (my + ToCenterOffset / 2) * Scale, Size: Size * Scale / 2.f, pText: aBuf);
783 }
784 }
785 }
786 }
787 }
788 TextRender()->TextColor(rgb: TextRender()->DefaultTextColor());
789 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
790}
791
792void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale, float Alpha) const
793{
794 if(!g_Config.m_ClTextEntities)
795 return;
796
797 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
798 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
799
800 int StartY = (int)(ScreenY0 / Scale) - 1;
801 int StartX = (int)(ScreenX0 / Scale) - 1;
802 int EndY = (int)(ScreenY1 / Scale) + 1;
803 int EndX = (int)(ScreenX1 / Scale) + 1;
804
805 if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
806 return; // its useless to render text at this distance
807
808 float Size = g_Config.m_ClTextEntitiesSize / 100.f;
809 float ToCenterOffset = (1 - Size) / 2.f;
810 char aBuf[16];
811
812 TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
813 for(int y = StartY; y < EndY; y++)
814 {
815 for(int x = StartX; x < EndX; x++)
816 {
817 int mx = x;
818 int my = y;
819
820 if(mx < 0)
821 continue; // mx = 0;
822 if(mx >= w)
823 continue; // mx = w-1;
824 if(my < 0)
825 continue; // my = 0;
826 if(my >= h)
827 continue; // my = h-1;
828
829 int c = mx + my * w;
830
831 unsigned char Index = pSwitch[c].m_Number;
832 if(Index && IsSwitchTileNumberUsed(Index: pSwitch[c].m_Type))
833 {
834 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", Index);
835 TextRender()->Text(x: mx * Scale, y: (my + ToCenterOffset / 2) * Scale, Size: Size * Scale / 2.f, pText: aBuf);
836 }
837
838 unsigned char Delay = pSwitch[c].m_Delay;
839 if(Delay && IsSwitchTileDelayUsed(Index: pSwitch[c].m_Type))
840 {
841 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", Delay);
842 TextRender()->Text(x: mx * Scale, y: (my + 0.5f + ToCenterOffset / 2) * Scale, Size: Size * Scale / 2.f, pText: aBuf);
843 }
844 }
845 }
846 TextRender()->TextColor(rgb: TextRender()->DefaultTextColor());
847 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
848}
849
850void CRenderTools::RenderTuneOverlay(CTuneTile *pTune, int w, int h, float Scale, float Alpha) const
851{
852 if(!g_Config.m_ClTextEntities)
853 return;
854
855 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
856 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
857
858 int StartY = (int)(ScreenY0 / Scale) - 1;
859 int StartX = (int)(ScreenX0 / Scale) - 1;
860 int EndY = (int)(ScreenY1 / Scale) + 1;
861 int EndX = (int)(ScreenX1 / Scale) + 1;
862
863 if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay)
864 return; // its useless to render text at this distance
865
866 float Size = g_Config.m_ClTextEntitiesSize / 100.f;
867 char aBuf[16];
868
869 TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
870 for(int y = StartY; y < EndY; y++)
871 {
872 for(int x = StartX; x < EndX; x++)
873 {
874 int mx = x;
875 int my = y;
876
877 if(mx < 0)
878 continue; // mx = 0;
879 if(mx >= w)
880 continue; // mx = w-1;
881 if(my < 0)
882 continue; // my = 0;
883 if(my >= h)
884 continue; // my = h-1;
885
886 int c = mx + my * w;
887
888 unsigned char Index = pTune[c].m_Number;
889 if(Index)
890 {
891 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d", Index);
892 TextRender()->Text(x: mx * Scale + 11.f, y: my * Scale + 6.f, Size: Size * Scale / 1.5f - 5.f, pText: aBuf); // numbers shouldn't be too big and in the center of the tile
893 }
894 }
895 }
896 TextRender()->TextColor(rgb: TextRender()->DefaultTextColor());
897 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
898}
899
900void CRenderTools::RenderTelemap(CTeleTile *pTele, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) const
901{
902 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
903 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
904
905 // calculate the final pixelsize for the tiles
906 float TilePixelSize = 1024 / 32.0f;
907 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
908 float FinalTilesetScale = FinalTileSize / TilePixelSize;
909
910 if(Graphics()->HasTextureArraysSupport())
911 Graphics()->QuadsTex3DBegin();
912 else
913 Graphics()->QuadsBegin();
914 Graphics()->SetColor(Color);
915
916 int StartY = (int)(ScreenY0 / Scale) - 1;
917 int StartX = (int)(ScreenX0 / Scale) - 1;
918 int EndY = (int)(ScreenY1 / Scale) + 1;
919 int EndX = (int)(ScreenX1 / Scale) + 1;
920
921 // adjust the texture shift according to mipmap level
922 float TexSize = 1024.0f;
923 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
924 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
925
926 for(int y = StartY; y < EndY; y++)
927 for(int x = StartX; x < EndX; x++)
928 {
929 int mx = x;
930 int my = y;
931
932 if(RenderFlags & TILERENDERFLAG_EXTEND)
933 {
934 if(mx < 0)
935 mx = 0;
936 if(mx >= w)
937 mx = w - 1;
938 if(my < 0)
939 my = 0;
940 if(my >= h)
941 my = h - 1;
942 }
943 else
944 {
945 if(mx < 0)
946 continue; // mx = 0;
947 if(mx >= w)
948 continue; // mx = w-1;
949 if(my < 0)
950 continue; // my = 0;
951 if(my >= h)
952 continue; // my = h-1;
953 }
954
955 int c = mx + my * w;
956
957 unsigned char Index = pTele[c].m_Type;
958 if(Index)
959 {
960 bool Render = false;
961 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
962 Render = true;
963
964 if(Render)
965 {
966 int tx = Index % 16;
967 int ty = Index / 16;
968 int Px0 = tx * (1024 / 16);
969 int Py0 = ty * (1024 / 16);
970 int Px1 = Px0 + (1024 / 16) - 1;
971 int Py1 = Py0 + (1024 / 16) - 1;
972
973 float x0 = Nudge + Px0 / TexSize + Frac;
974 float y0 = Nudge + Py0 / TexSize + Frac;
975 float x1 = Nudge + Px1 / TexSize - Frac;
976 float y1 = Nudge + Py0 / TexSize + Frac;
977 float x2 = Nudge + Px1 / TexSize - Frac;
978 float y2 = Nudge + Py1 / TexSize - Frac;
979 float x3 = Nudge + Px0 / TexSize + Frac;
980 float y3 = Nudge + Py1 / TexSize - Frac;
981
982 if(Graphics()->HasTextureArraysSupport())
983 {
984 x0 = 0;
985 y0 = 0;
986 x1 = x0 + 1;
987 y1 = y0;
988 x2 = x0 + 1;
989 y2 = y0 + 1;
990 x3 = x0;
991 y3 = y0 + 1;
992 }
993
994 if(Graphics()->HasTextureArraysSupport())
995 {
996 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
997 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
998 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
999 }
1000 else
1001 {
1002 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
1003 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1004 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
1005 }
1006 }
1007 }
1008 }
1009
1010 if(Graphics()->HasTextureArraysSupport())
1011 Graphics()->QuadsTex3DEnd();
1012 else
1013 Graphics()->QuadsEnd();
1014 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
1015}
1016
1017void CRenderTools::RenderSpeedupmap(CSpeedupTile *pSpeedupTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) const
1018{
1019 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
1020 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
1021
1022 // calculate the final pixelsize for the tiles
1023 float TilePixelSize = 1024 / 32.0f;
1024 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
1025 float FinalTilesetScale = FinalTileSize / TilePixelSize;
1026
1027 if(Graphics()->HasTextureArraysSupport())
1028 Graphics()->QuadsTex3DBegin();
1029 else
1030 Graphics()->QuadsBegin();
1031 Graphics()->SetColor(Color);
1032
1033 int StartY = (int)(ScreenY0 / Scale) - 1;
1034 int StartX = (int)(ScreenX0 / Scale) - 1;
1035 int EndY = (int)(ScreenY1 / Scale) + 1;
1036 int EndX = (int)(ScreenX1 / Scale) + 1;
1037
1038 // adjust the texture shift according to mipmap level
1039 float TexSize = 1024.0f;
1040 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
1041 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
1042
1043 for(int y = StartY; y < EndY; y++)
1044 for(int x = StartX; x < EndX; x++)
1045 {
1046 int mx = x;
1047 int my = y;
1048
1049 if(RenderFlags & TILERENDERFLAG_EXTEND)
1050 {
1051 if(mx < 0)
1052 mx = 0;
1053 if(mx >= w)
1054 mx = w - 1;
1055 if(my < 0)
1056 my = 0;
1057 if(my >= h)
1058 my = h - 1;
1059 }
1060 else
1061 {
1062 if(mx < 0)
1063 continue; // mx = 0;
1064 if(mx >= w)
1065 continue; // mx = w-1;
1066 if(my < 0)
1067 continue; // my = 0;
1068 if(my >= h)
1069 continue; // my = h-1;
1070 }
1071
1072 int c = mx + my * w;
1073
1074 unsigned char Index = pSpeedupTile[c].m_Type;
1075 if(Index)
1076 {
1077 bool Render = false;
1078 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
1079 Render = true;
1080
1081 if(Render)
1082 {
1083 int tx = Index % 16;
1084 int ty = Index / 16;
1085 int Px0 = tx * (1024 / 16);
1086 int Py0 = ty * (1024 / 16);
1087 int Px1 = Px0 + (1024 / 16) - 1;
1088 int Py1 = Py0 + (1024 / 16) - 1;
1089
1090 float x0 = Nudge + Px0 / TexSize + Frac;
1091 float y0 = Nudge + Py0 / TexSize + Frac;
1092 float x1 = Nudge + Px1 / TexSize - Frac;
1093 float y1 = Nudge + Py0 / TexSize + Frac;
1094 float x2 = Nudge + Px1 / TexSize - Frac;
1095 float y2 = Nudge + Py1 / TexSize - Frac;
1096 float x3 = Nudge + Px0 / TexSize + Frac;
1097 float y3 = Nudge + Py1 / TexSize - Frac;
1098
1099 if(Graphics()->HasTextureArraysSupport())
1100 {
1101 x0 = 0;
1102 y0 = 0;
1103 x1 = x0 + 1;
1104 y1 = y0;
1105 x2 = x0 + 1;
1106 y2 = y0 + 1;
1107 x3 = x0;
1108 y3 = y0 + 1;
1109 }
1110
1111 if(Graphics()->HasTextureArraysSupport())
1112 {
1113 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
1114 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1115 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
1116 }
1117 else
1118 {
1119 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
1120 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1121 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
1122 }
1123 }
1124 }
1125 }
1126
1127 if(Graphics()->HasTextureArraysSupport())
1128 Graphics()->QuadsTex3DEnd();
1129 else
1130 Graphics()->QuadsEnd();
1131 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
1132}
1133
1134void CRenderTools::RenderSwitchmap(CSwitchTile *pSwitchTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) const
1135{
1136 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
1137 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
1138
1139 // calculate the final pixelsize for the tiles
1140 float TilePixelSize = 1024 / 32.0f;
1141 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
1142 float FinalTilesetScale = FinalTileSize / TilePixelSize;
1143
1144 if(Graphics()->HasTextureArraysSupport())
1145 Graphics()->QuadsTex3DBegin();
1146 else
1147 Graphics()->QuadsBegin();
1148 Graphics()->SetColor(Color);
1149
1150 int StartY = (int)(ScreenY0 / Scale) - 1;
1151 int StartX = (int)(ScreenX0 / Scale) - 1;
1152 int EndY = (int)(ScreenY1 / Scale) + 1;
1153 int EndX = (int)(ScreenX1 / Scale) + 1;
1154
1155 // adjust the texture shift according to mipmap level
1156 float TexSize = 1024.0f;
1157 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
1158 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
1159
1160 for(int y = StartY; y < EndY; y++)
1161 for(int x = StartX; x < EndX; x++)
1162 {
1163 int mx = x;
1164 int my = y;
1165
1166 if(RenderFlags & TILERENDERFLAG_EXTEND)
1167 {
1168 if(mx < 0)
1169 mx = 0;
1170 if(mx >= w)
1171 mx = w - 1;
1172 if(my < 0)
1173 my = 0;
1174 if(my >= h)
1175 my = h - 1;
1176 }
1177 else
1178 {
1179 if(mx < 0)
1180 continue; // mx = 0;
1181 if(mx >= w)
1182 continue; // mx = w-1;
1183 if(my < 0)
1184 continue; // my = 0;
1185 if(my >= h)
1186 continue; // my = h-1;
1187 }
1188
1189 int c = mx + my * w;
1190
1191 unsigned char Index = pSwitchTile[c].m_Type;
1192 if(Index)
1193 {
1194 if(Index == TILE_SWITCHTIMEDOPEN)
1195 Index = 8;
1196
1197 unsigned char Flags = pSwitchTile[c].m_Flags;
1198
1199 bool Render = false;
1200 if(Flags & TILEFLAG_OPAQUE)
1201 {
1202 if(RenderFlags & LAYERRENDERFLAG_OPAQUE)
1203 Render = true;
1204 }
1205 else
1206 {
1207 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
1208 Render = true;
1209 }
1210
1211 if(Render)
1212 {
1213 int tx = Index % 16;
1214 int ty = Index / 16;
1215 int Px0 = tx * (1024 / 16);
1216 int Py0 = ty * (1024 / 16);
1217 int Px1 = Px0 + (1024 / 16) - 1;
1218 int Py1 = Py0 + (1024 / 16) - 1;
1219
1220 float x0 = Nudge + Px0 / TexSize + Frac;
1221 float y0 = Nudge + Py0 / TexSize + Frac;
1222 float x1 = Nudge + Px1 / TexSize - Frac;
1223 float y1 = Nudge + Py0 / TexSize + Frac;
1224 float x2 = Nudge + Px1 / TexSize - Frac;
1225 float y2 = Nudge + Py1 / TexSize - Frac;
1226 float x3 = Nudge + Px0 / TexSize + Frac;
1227 float y3 = Nudge + Py1 / TexSize - Frac;
1228
1229 if(Graphics()->HasTextureArraysSupport())
1230 {
1231 x0 = 0;
1232 y0 = 0;
1233 x1 = x0 + 1;
1234 y1 = y0;
1235 x2 = x0 + 1;
1236 y2 = y0 + 1;
1237 x3 = x0;
1238 y3 = y0 + 1;
1239 }
1240
1241 if(Flags & TILEFLAG_XFLIP)
1242 {
1243 x0 = x2;
1244 x1 = x3;
1245 x2 = x3;
1246 x3 = x0;
1247 }
1248
1249 if(Flags & TILEFLAG_YFLIP)
1250 {
1251 y0 = y3;
1252 y2 = y1;
1253 y3 = y1;
1254 y1 = y0;
1255 }
1256
1257 if(Flags & TILEFLAG_ROTATE)
1258 {
1259 float Tmp = x0;
1260 x0 = x3;
1261 x3 = x2;
1262 x2 = x1;
1263 x1 = Tmp;
1264 Tmp = y0;
1265 y0 = y3;
1266 y3 = y2;
1267 y2 = y1;
1268 y1 = Tmp;
1269 }
1270
1271 if(Graphics()->HasTextureArraysSupport())
1272 {
1273 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
1274 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1275 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
1276 }
1277 else
1278 {
1279 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
1280 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1281 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
1282 }
1283 }
1284 }
1285 }
1286
1287 if(Graphics()->HasTextureArraysSupport())
1288 Graphics()->QuadsTex3DEnd();
1289 else
1290 Graphics()->QuadsEnd();
1291 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
1292}
1293
1294void CRenderTools::RenderTunemap(CTuneTile *pTune, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) const
1295{
1296 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
1297 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
1298
1299 // calculate the final pixelsize for the tiles
1300 float TilePixelSize = 1024 / 32.0f;
1301 float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth();
1302 float FinalTilesetScale = FinalTileSize / TilePixelSize;
1303
1304 if(Graphics()->HasTextureArraysSupport())
1305 Graphics()->QuadsTex3DBegin();
1306 else
1307 Graphics()->QuadsBegin();
1308 Graphics()->SetColor(Color);
1309
1310 int StartY = (int)(ScreenY0 / Scale) - 1;
1311 int StartX = (int)(ScreenX0 / Scale) - 1;
1312 int EndY = (int)(ScreenY1 / Scale) + 1;
1313 int EndX = (int)(ScreenX1 / Scale) + 1;
1314
1315 // adjust the texture shift according to mipmap level
1316 float TexSize = 1024.0f;
1317 float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale);
1318 float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale);
1319
1320 for(int y = StartY; y < EndY; y++)
1321 for(int x = StartX; x < EndX; x++)
1322 {
1323 int mx = x;
1324 int my = y;
1325
1326 if(RenderFlags & TILERENDERFLAG_EXTEND)
1327 {
1328 if(mx < 0)
1329 mx = 0;
1330 if(mx >= w)
1331 mx = w - 1;
1332 if(my < 0)
1333 my = 0;
1334 if(my >= h)
1335 my = h - 1;
1336 }
1337 else
1338 {
1339 if(mx < 0)
1340 continue; // mx = 0;
1341 if(mx >= w)
1342 continue; // mx = w-1;
1343 if(my < 0)
1344 continue; // my = 0;
1345 if(my >= h)
1346 continue; // my = h-1;
1347 }
1348
1349 int c = mx + my * w;
1350
1351 unsigned char Index = pTune[c].m_Type;
1352 if(Index)
1353 {
1354 bool Render = false;
1355 if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)
1356 Render = true;
1357
1358 if(Render)
1359 {
1360 int tx = Index % 16;
1361 int ty = Index / 16;
1362 int Px0 = tx * (1024 / 16);
1363 int Py0 = ty * (1024 / 16);
1364 int Px1 = Px0 + (1024 / 16) - 1;
1365 int Py1 = Py0 + (1024 / 16) - 1;
1366
1367 float x0 = Nudge + Px0 / TexSize + Frac;
1368 float y0 = Nudge + Py0 / TexSize + Frac;
1369 float x1 = Nudge + Px1 / TexSize - Frac;
1370 float y1 = Nudge + Py0 / TexSize + Frac;
1371 float x2 = Nudge + Px1 / TexSize - Frac;
1372 float y2 = Nudge + Py1 / TexSize - Frac;
1373 float x3 = Nudge + Px0 / TexSize + Frac;
1374 float y3 = Nudge + Py1 / TexSize - Frac;
1375
1376 if(Graphics()->HasTextureArraysSupport())
1377 {
1378 x0 = 0;
1379 y0 = 0;
1380 x1 = x0 + 1;
1381 y1 = y0;
1382 x2 = x0 + 1;
1383 y2 = y0 + 1;
1384 x3 = x0;
1385 y3 = y0 + 1;
1386 }
1387
1388 if(Graphics()->HasTextureArraysSupport())
1389 {
1390 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index);
1391 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1392 Graphics()->QuadsTex3DDrawTL(pArray: &QuadItem, Num: 1);
1393 }
1394 else
1395 {
1396 Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3);
1397 IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale);
1398 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
1399 }
1400 }
1401 }
1402 }
1403
1404 if(Graphics()->HasTextureArraysSupport())
1405 Graphics()->QuadsTex3DEnd();
1406 else
1407 Graphics()->QuadsEnd();
1408 Graphics()->MapScreen(TopLeftX: ScreenX0, TopLeftY: ScreenY0, BottomRightX: ScreenX1, BottomRightY: ScreenY1);
1409}
1410