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