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 <cmath> |
4 | |
5 | #include <base/math.h> |
6 | |
7 | #include "animstate.h" |
8 | #include "render.h" |
9 | |
10 | #include <engine/graphics.h> |
11 | #include <engine/shared/config.h> |
12 | |
13 | #include <game/generated/client_data.h> |
14 | #include <game/generated/client_data7.h> |
15 | #include <game/generated/protocol.h> |
16 | |
17 | #include <game/mapitems.h> |
18 | |
19 | static float gs_SpriteWScale; |
20 | static float gs_SpriteHScale; |
21 | |
22 | void CRenderTools::Init(IGraphics *pGraphics, ITextRender *pTextRender) |
23 | { |
24 | m_pGraphics = pGraphics; |
25 | m_pTextRender = pTextRender; |
26 | m_TeeQuadContainerIndex = Graphics()->CreateQuadContainer(AutomaticUpload: false); |
27 | Graphics()->SetColor(r: 1.f, g: 1.f, b: 1.f, a: 1.f); |
28 | |
29 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
30 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f); |
31 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
32 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f); |
33 | |
34 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
35 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f); |
36 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
37 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f); |
38 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
39 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f); |
40 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
41 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f); |
42 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
43 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f); |
44 | |
45 | // Feet |
46 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
47 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f); |
48 | Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1); |
49 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f); |
50 | |
51 | // Mirrored Feet |
52 | Graphics()->QuadsSetSubsetFree(x0: 1, y0: 0, x1: 0, y1: 0, x2: 0, y2: 1, x3: 1, y3: 1); |
53 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f); |
54 | Graphics()->QuadsSetSubsetFree(x0: 1, y0: 0, x1: 0, y1: 0, x2: 0, y2: 1, x3: 1, y3: 1); |
55 | QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f); |
56 | |
57 | Graphics()->QuadContainerUpload(ContainerIndex: m_TeeQuadContainerIndex); |
58 | } |
59 | |
60 | void CRenderTools::SelectSprite(CDataSprite *pSpr, int Flags, int sx, int sy) const |
61 | { |
62 | int x = pSpr->m_X + sx; |
63 | int y = pSpr->m_Y + sy; |
64 | int w = pSpr->m_W; |
65 | int h = pSpr->m_H; |
66 | int cx = pSpr->m_pSet->m_Gridx; |
67 | int cy = pSpr->m_pSet->m_Gridy; |
68 | |
69 | GetSpriteScaleImpl(Width: w, Height: h, ScaleX&: gs_SpriteWScale, ScaleY&: gs_SpriteHScale); |
70 | |
71 | float x1 = x / (float)cx + 0.5f / (float)(cx * 32); |
72 | float x2 = (x + w) / (float)cx - 0.5f / (float)(cx * 32); |
73 | float y1 = y / (float)cy + 0.5f / (float)(cy * 32); |
74 | float y2 = (y + h) / (float)cy - 0.5f / (float)(cy * 32); |
75 | |
76 | if(Flags & SPRITE_FLAG_FLIP_Y) |
77 | std::swap(a&: y1, b&: y2); |
78 | |
79 | if(Flags & SPRITE_FLAG_FLIP_X) |
80 | std::swap(a&: x1, b&: x2); |
81 | |
82 | Graphics()->QuadsSetSubset(TopLeftU: x1, TopLeftV: y1, BottomRightU: x2, BottomRightV: y2); |
83 | } |
84 | |
85 | void CRenderTools::SelectSprite(int Id, int Flags, int sx, int sy) const |
86 | { |
87 | if(Id < 0 || Id >= g_pData->m_NumSprites) |
88 | return; |
89 | SelectSprite(pSpr: &g_pData->m_aSprites[Id], Flags, sx, sy); |
90 | } |
91 | |
92 | void CRenderTools::GetSpriteScale(const CDataSprite *pSprite, float &ScaleX, float &ScaleY) const |
93 | { |
94 | int w = pSprite->m_W; |
95 | int h = pSprite->m_H; |
96 | GetSpriteScaleImpl(Width: w, Height: h, ScaleX, ScaleY); |
97 | } |
98 | |
99 | void CRenderTools::GetSpriteScale(int Id, float &ScaleX, float &ScaleY) const |
100 | { |
101 | GetSpriteScale(pSprite: &g_pData->m_aSprites[Id], ScaleX, ScaleY); |
102 | } |
103 | |
104 | void CRenderTools::GetSpriteScaleImpl(int Width, int Height, float &ScaleX, float &ScaleY) const |
105 | { |
106 | const float f = length(a: vec2(Width, Height)); |
107 | ScaleX = Width / f; |
108 | ScaleY = Height / f; |
109 | } |
110 | |
111 | void CRenderTools::DrawSprite(float x, float y, float Size) const |
112 | { |
113 | IGraphics::CQuadItem QuadItem(x, y, Size * gs_SpriteWScale, Size * gs_SpriteHScale); |
114 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
115 | } |
116 | |
117 | void CRenderTools::DrawSprite(float x, float y, float ScaledWidth, float ScaledHeight) const |
118 | { |
119 | IGraphics::CQuadItem QuadItem(x, y, ScaledWidth, ScaledHeight); |
120 | Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1); |
121 | } |
122 | |
123 | void CRenderTools::RenderCursor(vec2 Center, float Size) const |
124 | { |
125 | Graphics()->WrapClamp(); |
126 | Graphics()->TextureSet(Texture: g_pData->m_aImages[IMAGE_CURSOR].m_Id); |
127 | Graphics()->QuadsBegin(); |
128 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
129 | IGraphics::CQuadItem QuadItem(Center.x, Center.y, Size, Size); |
130 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
131 | Graphics()->QuadsEnd(); |
132 | Graphics()->WrapNormal(); |
133 | } |
134 | |
135 | void CRenderTools::RenderIcon(int ImageId, int SpriteId, const CUIRect *pRect, const ColorRGBA *pColor) const |
136 | { |
137 | Graphics()->TextureSet(Texture: g_pData->m_aImages[ImageId].m_Id); |
138 | Graphics()->QuadsBegin(); |
139 | SelectSprite(Id: SpriteId); |
140 | if(pColor) |
141 | Graphics()->SetColor(r: pColor->r * pColor->a, g: pColor->g * pColor->a, b: pColor->b * pColor->a, a: pColor->a); |
142 | IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h); |
143 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
144 | Graphics()->QuadsEnd(); |
145 | } |
146 | |
147 | int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float x, float y, float Size) const |
148 | { |
149 | IGraphics::CQuadItem QuadItem(x, y, Size, Size); |
150 | return Graphics()->QuadContainerAddQuads(ContainerIndex: QuadContainerIndex, pArray: &QuadItem, Num: 1); |
151 | } |
152 | |
153 | int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float Size) const |
154 | { |
155 | IGraphics::CQuadItem QuadItem(-(Size) / 2.f, -(Size) / 2.f, (Size), (Size)); |
156 | return Graphics()->QuadContainerAddQuads(ContainerIndex: QuadContainerIndex, pArray: &QuadItem, Num: 1); |
157 | } |
158 | |
159 | int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float Width, float Height) const |
160 | { |
161 | IGraphics::CQuadItem QuadItem(-(Width) / 2.f, -(Height) / 2.f, (Width), (Height)); |
162 | return Graphics()->QuadContainerAddQuads(ContainerIndex: QuadContainerIndex, pArray: &QuadItem, Num: 1); |
163 | } |
164 | |
165 | int CRenderTools::QuadContainerAddSprite(int QuadContainerIndex, float X, float Y, float Width, float Height) const |
166 | { |
167 | IGraphics::CQuadItem QuadItem(X, Y, Width, Height); |
168 | return Graphics()->QuadContainerAddQuads(ContainerIndex: QuadContainerIndex, pArray: &QuadItem, Num: 1); |
169 | } |
170 | |
171 | void CRenderTools::GetRenderTeeAnimScaleAndBaseSize(const CTeeRenderInfo *pInfo, float &AnimScale, float &BaseSize) |
172 | { |
173 | AnimScale = pInfo->m_Size * 1.0f / 64.0f; |
174 | BaseSize = pInfo->m_Size; |
175 | } |
176 | |
177 | void CRenderTools::GetRenderTeeBodyScale(float BaseSize, float &BodyScale) |
178 | { |
179 | BodyScale = g_Config.m_ClFatSkins ? BaseSize * 1.3f : BaseSize; |
180 | BodyScale /= 64.0f; |
181 | } |
182 | |
183 | void CRenderTools::GetRenderTeeFeetScale(float BaseSize, float &FeetScaleWidth, float &FeetScaleHeight) |
184 | { |
185 | FeetScaleWidth = BaseSize / 64.0f; |
186 | FeetScaleHeight = (BaseSize / 2) / 32.0f; |
187 | } |
188 | |
189 | void CRenderTools::GetRenderTeeBodySize(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &BodyOffset, float &Width, float &Height) |
190 | { |
191 | float AnimScale, BaseSize; |
192 | GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize); |
193 | |
194 | float BodyScale; |
195 | GetRenderTeeBodyScale(BaseSize, BodyScale); |
196 | |
197 | Width = pInfo->m_SkinMetrics.m_Body.WidthNormalized() * 64.0f * BodyScale; |
198 | Height = pInfo->m_SkinMetrics.m_Body.HeightNormalized() * 64.0f * BodyScale; |
199 | BodyOffset.x = pInfo->m_SkinMetrics.m_Body.OffsetXNormalized() * 64.0f * BodyScale; |
200 | BodyOffset.y = pInfo->m_SkinMetrics.m_Body.OffsetYNormalized() * 64.0f * BodyScale; |
201 | } |
202 | |
203 | void CRenderTools::GetRenderTeeFeetSize(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &FeetOffset, float &Width, float &Height) |
204 | { |
205 | float AnimScale, BaseSize; |
206 | GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize); |
207 | |
208 | float FeetScaleWidth, FeetScaleHeight; |
209 | GetRenderTeeFeetScale(BaseSize, FeetScaleWidth, FeetScaleHeight); |
210 | |
211 | Width = pInfo->m_SkinMetrics.m_Feet.WidthNormalized() * 64.0f * FeetScaleWidth; |
212 | Height = pInfo->m_SkinMetrics.m_Feet.HeightNormalized() * 32.0f * FeetScaleHeight; |
213 | FeetOffset.x = pInfo->m_SkinMetrics.m_Feet.OffsetXNormalized() * 64.0f * FeetScaleWidth; |
214 | FeetOffset.y = pInfo->m_SkinMetrics.m_Feet.OffsetYNormalized() * 32.0f * FeetScaleHeight; |
215 | } |
216 | |
217 | void CRenderTools::GetRenderTeeOffsetToRenderedTee(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid) |
218 | { |
219 | float AnimScale, BaseSize; |
220 | GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize); |
221 | vec2 BodyPos = vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale; |
222 | |
223 | float AssumedScale = BaseSize / 64.0f; |
224 | |
225 | // just use the lowest feet |
226 | vec2 FeetPos; |
227 | const CAnimKeyframe * = pAnim->GetFrontFoot(); |
228 | FeetPos = vec2(pFoot->m_X * AnimScale, pFoot->m_Y * AnimScale); |
229 | pFoot = pAnim->GetBackFoot(); |
230 | FeetPos = vec2(FeetPos.x, maximum(a: FeetPos.y, b: pFoot->m_Y * AnimScale)); |
231 | |
232 | vec2 BodyOffset; |
233 | float BodyWidth, BodyHeight; |
234 | GetRenderTeeBodySize(pAnim, pInfo, BodyOffset, Width&: BodyWidth, Height&: BodyHeight); |
235 | |
236 | // -32 is the assumed min relative position for the quad |
237 | float MinY = -32.0f * AssumedScale; |
238 | // the body pos shifts the body away from center |
239 | MinY += BodyPos.y; |
240 | // the actual body is smaller though, because it doesn't use the full skin image in most cases |
241 | MinY += BodyOffset.y; |
242 | |
243 | vec2 FeetOffset; |
244 | float FeetWidth, FeetHeight; |
245 | GetRenderTeeFeetSize(pAnim, pInfo, FeetOffset, Width&: FeetWidth, Height&: FeetHeight); |
246 | |
247 | // MaxY builds up from the MinY |
248 | float MaxY = MinY + BodyHeight; |
249 | // if the body is smaller than the total feet offset, use feet |
250 | // since feet are smaller in height, respect the assumed relative position |
251 | MaxY = maximum(a: MaxY, b: (-16.0f * AssumedScale + FeetPos.y) + FeetOffset.y + FeetHeight); |
252 | |
253 | // now we got the full rendered size |
254 | float FullHeight = (MaxY - MinY); |
255 | |
256 | // next step is to calculate the offset that was created compared to the assumed relative position |
257 | float MidOfRendered = MinY + FullHeight / 2.0f; |
258 | |
259 | // TODO: x coordinate is ignored for now, bcs it's not really used yet anyway |
260 | TeeOffsetToMid.x = 0; |
261 | // negative value, because the calculation that uses this offset should work with addition. |
262 | TeeOffsetToMid.y = -MidOfRendered; |
263 | } |
264 | |
265 | void CRenderTools::RenderTee(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha) const |
266 | { |
267 | vec2 Direction = Dir; |
268 | vec2 Position = Pos; |
269 | |
270 | const CSkin::SSkinTextures *pSkinTextures = pInfo->m_CustomColoredSkin ? &pInfo->m_ColorableRenderSkin : &pInfo->m_OriginalRenderSkin; |
271 | |
272 | // first pass we draw the outline |
273 | // second pass we draw the filling |
274 | for(int p = 0; p < 2; p++) |
275 | { |
276 | int OutLine = p == 0 ? 1 : 0; |
277 | |
278 | for(int f = 0; f < 2; f++) |
279 | { |
280 | float AnimScale, BaseSize; |
281 | GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize); |
282 | if(f == 1) |
283 | { |
284 | Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2); |
285 | |
286 | // draw body |
287 | Graphics()->SetColor(r: pInfo->m_ColorBody.r, g: pInfo->m_ColorBody.g, b: pInfo->m_ColorBody.b, a: Alpha); |
288 | vec2 BodyPos = Position + vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale; |
289 | float BodyScale; |
290 | GetRenderTeeBodyScale(BaseSize, BodyScale); |
291 | Graphics()->TextureSet(Texture: OutLine == 1 ? pSkinTextures->m_BodyOutline : pSkinTextures->m_Body); |
292 | Graphics()->RenderQuadContainerAsSprite(ContainerIndex: m_TeeQuadContainerIndex, QuadOffset: OutLine, X: BodyPos.x, Y: BodyPos.y, ScaleX: BodyScale, ScaleY: BodyScale); |
293 | |
294 | // draw eyes |
295 | if(p == 1) |
296 | { |
297 | int QuadOffset = 2; |
298 | int EyeQuadOffset = 0; |
299 | int TeeEye = 0; |
300 | |
301 | switch(Emote) |
302 | { |
303 | case EMOTE_PAIN: |
304 | EyeQuadOffset = 0; |
305 | TeeEye = SPRITE_TEE_EYE_PAIN - SPRITE_TEE_EYE_NORMAL; |
306 | break; |
307 | case EMOTE_HAPPY: |
308 | EyeQuadOffset = 1; |
309 | TeeEye = SPRITE_TEE_EYE_HAPPY - SPRITE_TEE_EYE_NORMAL; |
310 | break; |
311 | case EMOTE_SURPRISE: |
312 | EyeQuadOffset = 2; |
313 | TeeEye = SPRITE_TEE_EYE_SURPRISE - SPRITE_TEE_EYE_NORMAL; |
314 | break; |
315 | case EMOTE_ANGRY: |
316 | EyeQuadOffset = 3; |
317 | TeeEye = SPRITE_TEE_EYE_ANGRY - SPRITE_TEE_EYE_NORMAL; |
318 | break; |
319 | default: |
320 | EyeQuadOffset = 4; |
321 | break; |
322 | } |
323 | |
324 | float EyeScale = BaseSize * 0.40f; |
325 | float h = Emote == EMOTE_BLINK ? BaseSize * 0.15f : EyeScale; |
326 | float EyeSeparation = (0.075f - 0.010f * absolute(a: Direction.x)) * BaseSize; |
327 | vec2 Offset = vec2(Direction.x * 0.125f, -0.05f + Direction.y * 0.10f) * BaseSize; |
328 | |
329 | Graphics()->TextureSet(Texture: pSkinTextures->m_aEyes[TeeEye]); |
330 | Graphics()->RenderQuadContainerAsSprite(ContainerIndex: m_TeeQuadContainerIndex, QuadOffset: QuadOffset + EyeQuadOffset, X: BodyPos.x - EyeSeparation + Offset.x, Y: BodyPos.y + Offset.y, ScaleX: EyeScale / (64.f * 0.4f), ScaleY: h / (64.f * 0.4f)); |
331 | Graphics()->RenderQuadContainerAsSprite(ContainerIndex: m_TeeQuadContainerIndex, QuadOffset: QuadOffset + EyeQuadOffset, X: BodyPos.x + EyeSeparation + Offset.x, Y: BodyPos.y + Offset.y, ScaleX: -EyeScale / (64.f * 0.4f), ScaleY: h / (64.f * 0.4f)); |
332 | } |
333 | } |
334 | |
335 | // draw feet |
336 | const CAnimKeyframe * = f ? pAnim->GetFrontFoot() : pAnim->GetBackFoot(); |
337 | |
338 | float w = BaseSize; |
339 | float h = BaseSize / 2; |
340 | |
341 | int QuadOffset = 7; |
342 | if(Dir.x < 0 && pInfo->m_FeetFlipped) |
343 | { |
344 | QuadOffset += 2; |
345 | } |
346 | |
347 | Graphics()->QuadsSetRotation(Angle: pFoot->m_Angle * pi * 2); |
348 | |
349 | bool Indicate = !pInfo->m_GotAirJump && g_Config.m_ClAirjumpindicator; |
350 | float ColorScale = 1.0f; |
351 | |
352 | if(!OutLine) |
353 | { |
354 | ++QuadOffset; |
355 | if(Indicate) |
356 | ColorScale = 0.5f; |
357 | } |
358 | |
359 | Graphics()->SetColor(r: pInfo->m_ColorFeet.r * ColorScale, g: pInfo->m_ColorFeet.g * ColorScale, b: pInfo->m_ColorFeet.b * ColorScale, a: Alpha); |
360 | |
361 | Graphics()->TextureSet(Texture: OutLine == 1 ? pSkinTextures->m_FeetOutline : pSkinTextures->m_Feet); |
362 | Graphics()->RenderQuadContainerAsSprite(ContainerIndex: m_TeeQuadContainerIndex, QuadOffset, X: Position.x + pFoot->m_X * AnimScale, Y: Position.y + pFoot->m_Y * AnimScale, ScaleX: w / 64.f, ScaleY: h / 32.f); |
363 | } |
364 | } |
365 | |
366 | Graphics()->SetColor(r: 1.f, g: 1.f, b: 1.f, a: 1.f); |
367 | Graphics()->QuadsSetRotation(Angle: 0); |
368 | } |
369 | |
370 | void CRenderTools::CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight) |
371 | { |
372 | const float Amount = 1150 * 1000; |
373 | const float WMax = 1500; |
374 | const float HMax = 1050; |
375 | |
376 | const float f = std::sqrt(x: Amount) / std::sqrt(x: Aspect); |
377 | *pWidth = f * Aspect; |
378 | *pHeight = f; |
379 | |
380 | // limit the view |
381 | if(*pWidth > WMax) |
382 | { |
383 | *pWidth = WMax; |
384 | *pHeight = *pWidth / Aspect; |
385 | } |
386 | |
387 | if(*pHeight > HMax) |
388 | { |
389 | *pHeight = HMax; |
390 | *pWidth = *pHeight * Aspect; |
391 | } |
392 | |
393 | *pWidth *= Zoom; |
394 | *pHeight *= Zoom; |
395 | } |
396 | |
397 | void CRenderTools::MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, |
398 | float ParallaxZoom, float OffsetX, float OffsetY, float Aspect, float Zoom, float *pPoints) |
399 | { |
400 | float Width, Height; |
401 | CalcScreenParams(Aspect, Zoom, pWidth: &Width, pHeight: &Height); |
402 | |
403 | float Scale = (ParallaxZoom * (Zoom - 1.0f) + 100.0f) / 100.0f / Zoom; |
404 | Width *= Scale; |
405 | Height *= Scale; |
406 | |
407 | CenterX *= ParallaxX / 100.0f; |
408 | CenterY *= ParallaxY / 100.0f; |
409 | pPoints[0] = OffsetX + CenterX - Width / 2; |
410 | pPoints[1] = OffsetY + CenterY - Height / 2; |
411 | pPoints[2] = pPoints[0] + Width; |
412 | pPoints[3] = pPoints[1] + Height; |
413 | } |
414 | |
415 | void CRenderTools::MapScreenToGroup(float CenterX, float CenterY, CMapItemGroup *pGroup, float Zoom) |
416 | { |
417 | float ParallaxZoom = clamp(val: (double)(maximum(a: pGroup->m_ParallaxX, b: pGroup->m_ParallaxY)), lo: 0., hi: 100.); |
418 | float aPoints[4]; |
419 | MapScreenToWorld(CenterX, CenterY, ParallaxX: pGroup->m_ParallaxX, ParallaxY: pGroup->m_ParallaxY, ParallaxZoom, |
420 | OffsetX: pGroup->m_OffsetX, OffsetY: pGroup->m_OffsetY, Aspect: Graphics()->ScreenAspect(), Zoom, pPoints: aPoints); |
421 | Graphics()->MapScreen(TopLeftX: aPoints[0], TopLeftY: aPoints[1], BottomRightX: aPoints[2], BottomRightY: aPoints[3]); |
422 | } |
423 | |
424 | void CRenderTools::MapScreenToInterface(float CenterX, float CenterY) |
425 | { |
426 | float aPoints[4]; |
427 | MapScreenToWorld(CenterX, CenterY, ParallaxX: 100.0f, ParallaxY: 100.0f, ParallaxZoom: 100.0f, |
428 | OffsetX: 0, OffsetY: 0, Aspect: Graphics()->ScreenAspect(), Zoom: 1.0f, pPoints: aPoints); |
429 | Graphics()->MapScreen(TopLeftX: aPoints[0], TopLeftY: aPoints[1], BottomRightX: aPoints[2], BottomRightY: aPoints[3]); |
430 | } |
431 | |