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 "render.h"
4
5#include "animstate.h"
6
7#include <base/math.h>
8
9#include <engine/graphics.h>
10#include <engine/shared/config.h>
11
12#include <generated/client_data.h>
13#include <generated/client_data7.h>
14#include <generated/protocol.h>
15#include <generated/protocol7.h>
16
17#include <game/mapitems.h>
18
19#include <cmath>
20
21CSkinDescriptor::CSkinDescriptor()
22{
23 Reset();
24}
25
26void CSkinDescriptor::Reset()
27{
28 m_Flags = 0;
29 m_aSkinName[0] = '\0';
30 for(auto &Sixup : m_aSixup)
31 {
32 Sixup.Reset();
33 }
34}
35
36bool CSkinDescriptor::IsValid() const
37{
38 return (m_Flags & (FLAG_SIX | FLAG_SEVEN)) != 0;
39}
40
41bool CSkinDescriptor::operator==(const CSkinDescriptor &Other) const
42{
43 if(m_Flags != Other.m_Flags)
44 {
45 return false;
46 }
47
48 if(m_Flags & FLAG_SIX)
49 {
50 if(str_comp(a: m_aSkinName, b: Other.m_aSkinName) != 0)
51 {
52 return false;
53 }
54 }
55
56 if(m_Flags & FLAG_SEVEN)
57 {
58 for(int Dummy = 0; Dummy < NUM_DUMMIES; Dummy++)
59 {
60 if(m_aSixup[Dummy] != Other.m_aSixup[Dummy])
61 {
62 return false;
63 }
64 }
65 }
66
67 return true;
68}
69
70void CSkinDescriptor::CSixup::Reset()
71{
72 for(auto &aSkinPartName : m_aaSkinPartNames)
73 {
74 aSkinPartName[0] = '\0';
75 }
76 m_BotDecoration = false;
77 m_XmasHat = false;
78}
79
80bool CSkinDescriptor::CSixup::operator==(const CSixup &Other) const
81{
82 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
83 {
84 if(str_comp(a: m_aaSkinPartNames[Part], b: Other.m_aaSkinPartNames[Part]) != 0)
85 {
86 return false;
87 }
88 }
89 return m_BotDecoration == Other.m_BotDecoration &&
90 m_XmasHat == Other.m_XmasHat;
91}
92
93void CRenderTools::Init(IGraphics *pGraphics, ITextRender *pTextRender)
94{
95 m_pGraphics = pGraphics;
96 m_pTextRender = pTextRender;
97 m_TeeQuadContainerIndex = Graphics()->CreateQuadContainer(AutomaticUpload: false);
98 Graphics()->SetColor(r: 1.f, g: 1.f, b: 1.f, a: 1.f);
99
100 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
101 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f);
102 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
103 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f);
104
105 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
106 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f);
107 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
108 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f);
109 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
110 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f);
111 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
112 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f);
113 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
114 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, Size: 64.f * 0.4f);
115
116 // Feet
117 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
118 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f);
119 Graphics()->QuadsSetSubset(TopLeftU: 0, TopLeftV: 0, BottomRightU: 1, BottomRightV: 1);
120 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f);
121
122 // Mirrored Feet
123 Graphics()->QuadsSetSubsetFree(x0: 1, y0: 0, x1: 0, y1: 0, x2: 0, y2: 1, x3: 1, y3: 1);
124 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f);
125 Graphics()->QuadsSetSubsetFree(x0: 1, y0: 0, x1: 0, y1: 0, x2: 0, y2: 1, x3: 1, y3: 1);
126 Graphics()->QuadContainerAddSprite(QuadContainerIndex: m_TeeQuadContainerIndex, X: -32.f, Y: -16.f, Width: 64.f, Height: 32.f);
127
128 Graphics()->QuadContainerUpload(ContainerIndex: m_TeeQuadContainerIndex);
129}
130
131void CRenderTools::RenderCursor(vec2 Center, float Size) const
132{
133 Graphics()->WrapClamp();
134 Graphics()->TextureSet(Texture: g_pData->m_aImages[IMAGE_CURSOR].m_Id);
135 Graphics()->QuadsBegin();
136 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f);
137 IGraphics::CQuadItem QuadItem(Center.x, Center.y, Size, Size);
138 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
139 Graphics()->QuadsEnd();
140 Graphics()->WrapNormal();
141}
142
143void CRenderTools::RenderIcon(int ImageId, int SpriteId, const CUIRect *pRect, const ColorRGBA *pColor) const
144{
145 Graphics()->TextureSet(Texture: g_pData->m_aImages[ImageId].m_Id);
146 Graphics()->QuadsBegin();
147 Graphics()->SelectSprite(Id: SpriteId);
148 if(pColor)
149 Graphics()->SetColor(r: pColor->r * pColor->a, g: pColor->g * pColor->a, b: pColor->b * pColor->a, a: pColor->a);
150 IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h);
151 Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1);
152 Graphics()->QuadsEnd();
153}
154
155void CRenderTools::GetRenderTeeAnimScaleAndBaseSize(const CTeeRenderInfo *pInfo, float &AnimScale, float &BaseSize)
156{
157 AnimScale = pInfo->m_Size * 1.0f / 64.0f;
158 BaseSize = pInfo->m_Size;
159}
160
161void CRenderTools::GetRenderTeeBodyScale(float BaseSize, float &BodyScale)
162{
163 BodyScale = g_Config.m_ClFatSkins ? BaseSize * 1.3f : BaseSize;
164 BodyScale /= 64.0f;
165}
166
167void CRenderTools::GetRenderTeeFeetScale(float BaseSize, float &FeetScaleWidth, float &FeetScaleHeight)
168{
169 FeetScaleWidth = BaseSize / 64.0f;
170 FeetScaleHeight = (BaseSize / 2) / 32.0f;
171}
172
173void CRenderTools::GetRenderTeeBodySize(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &BodyOffset, float &Width, float &Height)
174{
175 float AnimScale, BaseSize;
176 GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize);
177
178 float BodyScale;
179 GetRenderTeeBodyScale(BaseSize, BodyScale);
180
181 Width = pInfo->m_SkinMetrics.m_Body.WidthNormalized() * 64.0f * BodyScale;
182 Height = pInfo->m_SkinMetrics.m_Body.HeightNormalized() * 64.0f * BodyScale;
183 BodyOffset.x = pInfo->m_SkinMetrics.m_Body.OffsetXNormalized() * 64.0f * BodyScale;
184 BodyOffset.y = pInfo->m_SkinMetrics.m_Body.OffsetYNormalized() * 64.0f * BodyScale;
185}
186
187void CRenderTools::GetRenderTeeFeetSize(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &FeetOffset, float &Width, float &Height)
188{
189 float AnimScale, BaseSize;
190 GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize);
191
192 float FeetScaleWidth, FeetScaleHeight;
193 GetRenderTeeFeetScale(BaseSize, FeetScaleWidth, FeetScaleHeight);
194
195 Width = pInfo->m_SkinMetrics.m_Feet.WidthNormalized() * 64.0f * FeetScaleWidth;
196 Height = pInfo->m_SkinMetrics.m_Feet.HeightNormalized() * 32.0f * FeetScaleHeight;
197 FeetOffset.x = pInfo->m_SkinMetrics.m_Feet.OffsetXNormalized() * 64.0f * FeetScaleWidth;
198 FeetOffset.y = pInfo->m_SkinMetrics.m_Feet.OffsetYNormalized() * 32.0f * FeetScaleHeight;
199}
200
201void CRenderTools::GetRenderTeeOffsetToRenderedTee(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, vec2 &TeeOffsetToMid)
202{
203 if(pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_BODY).IsValid())
204 {
205 TeeOffsetToMid = vec2(0.0f, pInfo->m_Size * 0.12f);
206 return;
207 }
208
209 float AnimScale, BaseSize;
210 GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize);
211 vec2 BodyPos = vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale;
212
213 float AssumedScale = BaseSize / 64.0f;
214
215 // just use the lowest feet
216 vec2 FeetPos;
217 const CAnimKeyframe *pFoot = pAnim->GetFrontFoot();
218 FeetPos = vec2(pFoot->m_X * AnimScale, pFoot->m_Y * AnimScale);
219 pFoot = pAnim->GetBackFoot();
220 FeetPos = vec2(FeetPos.x, maximum(a: FeetPos.y, b: pFoot->m_Y * AnimScale));
221
222 vec2 BodyOffset;
223 float BodyWidth, BodyHeight;
224 GetRenderTeeBodySize(pAnim, pInfo, BodyOffset, Width&: BodyWidth, Height&: BodyHeight);
225
226 // -32 is the assumed min relative position for the quad
227 float MinY = -32.0f * AssumedScale;
228 // the body pos shifts the body away from center
229 MinY += BodyPos.y;
230 // the actual body is smaller though, because it doesn't use the full skin image in most cases
231 MinY += BodyOffset.y;
232
233 vec2 FeetOffset;
234 float FeetWidth, FeetHeight;
235 GetRenderTeeFeetSize(pAnim, pInfo, FeetOffset, Width&: FeetWidth, Height&: FeetHeight);
236
237 // MaxY builds up from the MinY
238 float MaxY = MinY + BodyHeight;
239 // if the body is smaller than the total feet offset, use feet
240 // since feet are smaller in height, respect the assumed relative position
241 MaxY = maximum(a: MaxY, b: (-16.0f * AssumedScale + FeetPos.y) + FeetOffset.y + FeetHeight);
242
243 // now we got the full rendered size
244 float FullHeight = (MaxY - MinY);
245
246 // next step is to calculate the offset that was created compared to the assumed relative position
247 float MidOfRendered = MinY + FullHeight / 2.0f;
248
249 // TODO: x coordinate is ignored for now, bcs it's not really used yet anyway
250 TeeOffsetToMid.x = 0;
251 // negative value, because the calculation that uses this offset should work with addition.
252 TeeOffsetToMid.y = -MidOfRendered;
253}
254
255void CRenderTools::RenderTee(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha) const
256{
257 if(pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_BODY).IsValid())
258 RenderTee7(pAnim, pInfo, Emote, Dir, Pos, Alpha);
259 else
260 RenderTee6(pAnim, pInfo, Emote, Dir, Pos, Alpha);
261
262 Graphics()->SetColor(r: 1.f, g: 1.f, b: 1.f, a: 1.f);
263 Graphics()->QuadsSetRotation(Angle: 0);
264}
265
266void CRenderTools::RenderTee7(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha) const
267{
268 vec2 Direction = Dir;
269 vec2 Position = Pos;
270 const bool IsBot = pInfo->m_aSixup[g_Config.m_ClDummy].m_BotTexture.IsValid();
271
272 // first pass we draw the outline
273 // second pass we draw the filling
274 for(int Pass = 0; Pass < 2; Pass++)
275 {
276 bool OutLine = Pass == 0;
277
278 for(int Filling = 0; Filling < 2; Filling++)
279 {
280 float AnimScale = pInfo->m_Size * 1.0f / 64.0f;
281 float BaseSize = pInfo->m_Size;
282 if(Filling == 1)
283 {
284 vec2 BodyPos = Position + vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale;
285 IGraphics::CQuadItem BodyItem(BodyPos.x, BodyPos.y, BaseSize, BaseSize);
286 IGraphics::CQuadItem Item;
287
288 if(IsBot && !OutLine)
289 {
290 IGraphics::CQuadItem BotItem(BodyPos.x + (2.f / 3.f) * AnimScale, BodyPos.y + (-16 + 2.f / 3.f) * AnimScale, BaseSize, BaseSize); // x+0.66, y+0.66 to correct some rendering bug
291
292 // draw bot visuals (background)
293 Graphics()->TextureSet(Texture: pInfo->m_aSixup[g_Config.m_ClDummy].m_BotTexture);
294 Graphics()->QuadsBegin();
295 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
296 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_BOT_BACKGROUND);
297 Item = BotItem;
298 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
299 Graphics()->QuadsEnd();
300
301 // draw bot visuals (foreground)
302 Graphics()->TextureSet(Texture: pInfo->m_aSixup[g_Config.m_ClDummy].m_BotTexture);
303 Graphics()->QuadsBegin();
304 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
305 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_BOT_FOREGROUND);
306 Item = BotItem;
307 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
308 Graphics()->SetColor(pInfo->m_aSixup[g_Config.m_ClDummy].m_BotColor.WithAlpha(alpha: Alpha));
309 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_BOT_GLOW);
310 Item = BotItem;
311 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
312 Graphics()->QuadsEnd();
313 }
314
315 // draw decoration
316 const IGraphics::CTextureHandle &DecorationTexture = pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_DECORATION);
317 if(DecorationTexture.IsValid())
318 {
319 Graphics()->TextureSet(Texture: DecorationTexture);
320 Graphics()->QuadsBegin();
321 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
322 Graphics()->SetColor(pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_DECORATION].WithAlpha(alpha: Alpha));
323 Graphics()->SelectSprite7(Id: OutLine ? client_data7::SPRITE_TEE_DECORATION_OUTLINE : client_data7::SPRITE_TEE_DECORATION);
324 Item = BodyItem;
325 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
326 Graphics()->QuadsEnd();
327 }
328
329 // draw body (behind marking)
330 const IGraphics::CTextureHandle &BodyTexture = pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_BODY);
331 Graphics()->TextureSet(Texture: BodyTexture);
332 Graphics()->QuadsBegin();
333 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
334 if(OutLine)
335 {
336 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
337 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_BODY_OUTLINE);
338 }
339 else
340 {
341 Graphics()->SetColor(pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_BODY].WithAlpha(alpha: Alpha));
342 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_BODY);
343 }
344 Item = BodyItem;
345 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
346 Graphics()->QuadsEnd();
347
348 // draw marking
349 const IGraphics::CTextureHandle &MarkingTexture = pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_MARKING);
350 if(MarkingTexture.IsValid() && !OutLine)
351 {
352 Graphics()->TextureSet(Texture: MarkingTexture);
353 Graphics()->QuadsBegin();
354 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
355 ColorRGBA MarkingColor = pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_MARKING];
356 Graphics()->SetColor(r: MarkingColor.r * MarkingColor.a, g: MarkingColor.g * MarkingColor.a, b: MarkingColor.b * MarkingColor.a, a: MarkingColor.a * Alpha);
357 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_MARKING);
358 Item = BodyItem;
359 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
360 Graphics()->QuadsEnd();
361 }
362
363 // draw body (in front of marking)
364 if(!OutLine)
365 {
366 Graphics()->TextureSet(Texture: BodyTexture);
367 Graphics()->QuadsBegin();
368 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
369 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
370 for(int t = 0; t < 2; t++)
371 {
372 Graphics()->SelectSprite7(Id: t == 0 ? client_data7::SPRITE_TEE_BODY_SHADOW : client_data7::SPRITE_TEE_BODY_UPPER_OUTLINE);
373 Item = BodyItem;
374 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
375 }
376 Graphics()->QuadsEnd();
377 }
378
379 // draw eyes
380 Graphics()->TextureSet(Texture: pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_EYES));
381 Graphics()->QuadsBegin();
382 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
383 if(IsBot)
384 {
385 Graphics()->SetColor(pInfo->m_aSixup[g_Config.m_ClDummy].m_BotColor.WithAlpha(alpha: Alpha));
386 Emote = EMOTE_SURPRISE;
387 }
388 else
389 {
390 Graphics()->SetColor(pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_EYES].WithAlpha(alpha: Alpha));
391 }
392 if(Pass == 1)
393 {
394 switch(Emote)
395 {
396 case EMOTE_PAIN:
397 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_EYES_PAIN);
398 break;
399 case EMOTE_HAPPY:
400 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_EYES_HAPPY);
401 break;
402 case EMOTE_SURPRISE:
403 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_EYES_SURPRISE);
404 break;
405 case EMOTE_ANGRY:
406 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_EYES_ANGRY);
407 break;
408 default:
409 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_EYES_NORMAL);
410 break;
411 }
412
413 float EyeScale = BaseSize * 0.60f;
414 float h = Emote == EMOTE_BLINK ? BaseSize * 0.15f / 2.0f : EyeScale / 2.0f;
415 vec2 Offset = vec2(Direction.x * 0.125f, -0.05f + Direction.y * 0.10f) * BaseSize;
416 IGraphics::CQuadItem QuadItem(BodyPos.x + Offset.x, BodyPos.y + Offset.y, EyeScale, h);
417 Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1);
418 }
419 Graphics()->QuadsEnd();
420
421 // draw xmas hat
422 if(!OutLine && pInfo->m_aSixup[g_Config.m_ClDummy].m_HatTexture.IsValid())
423 {
424 Graphics()->TextureSet(Texture: pInfo->m_aSixup[g_Config.m_ClDummy].m_HatTexture);
425 Graphics()->QuadsBegin();
426 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
427 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
428 int Flag = Direction.x < 0.0f ? IGraphics::SPRITE_FLAG_FLIP_X : 0;
429 switch(pInfo->m_aSixup[g_Config.m_ClDummy].m_HatSpriteIndex)
430 {
431 case 0:
432 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_HATS_TOP1, Flags: Flag);
433 break;
434 case 1:
435 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_HATS_TOP2, Flags: Flag);
436 break;
437 case 2:
438 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_HATS_SIDE1, Flags: Flag);
439 break;
440 case 3:
441 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_HATS_SIDE2, Flags: Flag);
442 }
443 Item = BodyItem;
444 Graphics()->QuadsDraw(pArray: &Item, Num: 1);
445 Graphics()->QuadsEnd();
446 }
447 }
448
449 // draw feet
450 Graphics()->TextureSet(Texture: pInfo->m_aSixup[g_Config.m_ClDummy].PartTexture(Part: protocol7::SKINPART_FEET));
451 Graphics()->QuadsBegin();
452 const CAnimKeyframe *pFoot = Filling ? pAnim->GetFrontFoot() : pAnim->GetBackFoot();
453
454 float w = BaseSize / 2.1f;
455 float h = w;
456
457 Graphics()->QuadsSetRotation(Angle: pFoot->m_Angle * pi * 2);
458
459 if(OutLine)
460 {
461 Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: Alpha);
462 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_FOOT_OUTLINE);
463 }
464 else
465 {
466 bool Indicate = !pInfo->m_GotAirJump && g_Config.m_ClAirjumpindicator;
467 float ColorScale = 1.0f;
468 if(Indicate)
469 ColorScale = 0.5f;
470 Graphics()->SetColor(
471 r: pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_FEET].r * ColorScale,
472 g: pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_FEET].g * ColorScale,
473 b: pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_FEET].b * ColorScale,
474 a: pInfo->m_aSixup[g_Config.m_ClDummy].m_aColors[protocol7::SKINPART_FEET].a * Alpha);
475 Graphics()->SelectSprite7(Id: client_data7::SPRITE_TEE_FOOT);
476 }
477
478 IGraphics::CQuadItem QuadItem(Position.x + pFoot->m_X * AnimScale, Position.y + pFoot->m_Y * AnimScale, w, h);
479 Graphics()->QuadsDraw(pArray: &QuadItem, Num: 1);
480 Graphics()->QuadsEnd();
481 }
482 }
483}
484
485void CRenderTools::RenderTee6(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha) const
486{
487 vec2 Direction = Dir;
488 vec2 Position = Pos;
489
490 const CSkin::CSkinTextures *pSkinTextures = pInfo->m_CustomColoredSkin ? &pInfo->m_ColorableRenderSkin : &pInfo->m_OriginalRenderSkin;
491
492 // first pass we draw the outline
493 // second pass we draw the filling
494 for(int Pass = 0; Pass < 2; Pass++)
495 {
496 int OutLine = Pass == 0 ? 1 : 0;
497
498 for(int Filling = 0; Filling < 2; Filling++)
499 {
500 float AnimScale, BaseSize;
501 GetRenderTeeAnimScaleAndBaseSize(pInfo, AnimScale, BaseSize);
502 if(Filling == 1)
503 {
504 Graphics()->QuadsSetRotation(Angle: pAnim->GetBody()->m_Angle * pi * 2);
505
506 // draw body
507 Graphics()->SetColor(r: pInfo->m_ColorBody.r, g: pInfo->m_ColorBody.g, b: pInfo->m_ColorBody.b, a: Alpha);
508 vec2 BodyPos = Position + vec2(pAnim->GetBody()->m_X, pAnim->GetBody()->m_Y) * AnimScale;
509 float BodyScale;
510 GetRenderTeeBodyScale(BaseSize, BodyScale);
511 Graphics()->TextureSet(Texture: OutLine == 1 ? pSkinTextures->m_BodyOutline : pSkinTextures->m_Body);
512 Graphics()->RenderQuadContainerAsSprite(ContainerIndex: m_TeeQuadContainerIndex, QuadOffset: OutLine, X: BodyPos.x, Y: BodyPos.y, ScaleX: BodyScale, ScaleY: BodyScale);
513
514 // draw eyes
515 if(Pass == 1)
516 {
517 int QuadOffset = 2;
518 int EyeQuadOffset = 0;
519 int TeeEye = 0;
520
521 switch(Emote)
522 {
523 case EMOTE_PAIN:
524 EyeQuadOffset = 0;
525 TeeEye = SPRITE_TEE_EYE_PAIN - SPRITE_TEE_EYE_NORMAL;
526 break;
527 case EMOTE_HAPPY:
528 EyeQuadOffset = 1;
529 TeeEye = SPRITE_TEE_EYE_HAPPY - SPRITE_TEE_EYE_NORMAL;
530 break;
531 case EMOTE_SURPRISE:
532 EyeQuadOffset = 2;
533 TeeEye = SPRITE_TEE_EYE_SURPRISE - SPRITE_TEE_EYE_NORMAL;
534 break;
535 case EMOTE_ANGRY:
536 EyeQuadOffset = 3;
537 TeeEye = SPRITE_TEE_EYE_ANGRY - SPRITE_TEE_EYE_NORMAL;
538 break;
539 default:
540 EyeQuadOffset = 4;
541 break;
542 }
543
544 float EyeScale = BaseSize * 0.40f;
545 float h = Emote == EMOTE_BLINK ? BaseSize * 0.15f : EyeScale;
546 float EyeSeparation = (0.075f - 0.010f * absolute(a: Direction.x)) * BaseSize;
547 vec2 Offset = vec2(Direction.x * 0.125f, -0.05f + Direction.y * 0.10f) * BaseSize;
548
549 Graphics()->TextureSet(Texture: pSkinTextures->m_aEyes[TeeEye]);
550 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));
551 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));
552 }
553 }
554
555 // draw feet
556 const CAnimKeyframe *pFoot = Filling ? pAnim->GetFrontFoot() : pAnim->GetBackFoot();
557
558 float w = BaseSize;
559 float h = BaseSize / 2;
560
561 int QuadOffset = 7;
562 if(Dir.x < 0 && pInfo->m_FeetFlipped)
563 {
564 QuadOffset += 2;
565 }
566
567 Graphics()->QuadsSetRotation(Angle: pFoot->m_Angle * pi * 2);
568
569 bool Indicate = !pInfo->m_GotAirJump && g_Config.m_ClAirjumpindicator;
570 float ColorScale = 1.0f;
571
572 if(!OutLine)
573 {
574 ++QuadOffset;
575 if(Indicate)
576 ColorScale = 0.5f;
577 }
578
579 Graphics()->SetColor(r: pInfo->m_ColorFeet.r * ColorScale, g: pInfo->m_ColorFeet.g * ColorScale, b: pInfo->m_ColorFeet.b * ColorScale, a: Alpha);
580
581 Graphics()->TextureSet(Texture: OutLine == 1 ? pSkinTextures->m_FeetOutline : pSkinTextures->m_Feet);
582 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);
583 }
584 }
585}
586