/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include #include #include "render.h" #include #include #include #include #include using namespace std::chrono_literals; void CRenderTools::RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, int64_t TimeNanos, float *pResult) { if(NumPoints == 0) { pResult[0] = 0; pResult[1] = 0; pResult[2] = 0; pResult[3] = 0; return; } if(NumPoints == 1) { pResult[0] = fx2f(pPoints[0].m_aValues[0]); pResult[1] = fx2f(pPoints[0].m_aValues[1]); pResult[2] = fx2f(pPoints[0].m_aValues[2]); pResult[3] = fx2f(pPoints[0].m_aValues[3]); return; } int64_t MaxPointTime = (int64_t)pPoints[NumPoints - 1].m_Time * std::chrono::nanoseconds(1ms).count(); if(MaxPointTime > 0) // TODO: remove this check when implementing a IO check for maps(in this case broken envelopes) TimeNanos = TimeNanos % MaxPointTime; else TimeNanos = 0; int TimeMillis = (int)(TimeNanos / std::chrono::nanoseconds(1ms).count()); for(int i = 0; i < NumPoints - 1; i++) { if(TimeMillis >= pPoints[i].m_Time && TimeMillis <= pPoints[i + 1].m_Time) { float Delta = pPoints[i + 1].m_Time - pPoints[i].m_Time; float a = (float)(((double)TimeNanos / (double)std::chrono::nanoseconds(1ms).count()) - pPoints[i].m_Time) / Delta; if(pPoints[i].m_Curvetype == CURVETYPE_SMOOTH) a = -2 * a * a * a + 3 * a * a; // second hermite basis else if(pPoints[i].m_Curvetype == CURVETYPE_SLOW) a = a * a * a; else if(pPoints[i].m_Curvetype == CURVETYPE_FAST) { a = 1 - a; a = 1 - a * a * a; } else if(pPoints[i].m_Curvetype == CURVETYPE_STEP) a = 0; else { // linear } for(int c = 0; c < Channels; c++) { float v0 = fx2f(pPoints[i].m_aValues[c]); float v1 = fx2f(pPoints[i + 1].m_aValues[c]); pResult[c] = v0 + (v1 - v0) * a; } return; } } pResult[0] = fx2f(pPoints[NumPoints - 1].m_aValues[0]); pResult[1] = fx2f(pPoints[NumPoints - 1].m_aValues[1]); pResult[2] = fx2f(pPoints[NumPoints - 1].m_aValues[2]); pResult[3] = fx2f(pPoints[NumPoints - 1].m_aValues[3]); } static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation) { int x = pPoint->x - pCenter->x; int y = pPoint->y - pCenter->y; pPoint->x = (int)(x * cosf(Rotation) - y * sinf(Rotation) + pCenter->x); pPoint->y = (int)(x * sinf(Rotation) + y * cosf(Rotation) + pCenter->y); } void CRenderTools::RenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser) { if(!g_Config.m_ClShowQuads || g_Config.m_ClOverlayEntities == 100) return; ForceRenderQuads(pQuads, NumQuads, RenderFlags, pfnEval, pUser, (100 - g_Config.m_ClOverlayEntities) / 100.0f); } void CRenderTools::ForceRenderQuads(CQuad *pQuads, int NumQuads, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha) { Graphics()->TrianglesBegin(); float Conv = 1 / 255.0f; for(int i = 0; i < NumQuads; i++) { CQuad *q = &pQuads[i]; float r = 1, g = 1, b = 1, a = 1; if(q->m_ColorEnv >= 0) { float aChannels[4]; pfnEval(q->m_ColorEnvOffset, q->m_ColorEnv, aChannels, pUser); r = aChannels[0]; g = aChannels[1]; b = aChannels[2]; a = aChannels[3]; } if(a <= 0) continue; bool Opaque = false; /* TODO: Analyze quadtexture 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)) Opaque = true; */ if(Opaque && !(RenderFlags & LAYERRENDERFLAG_OPAQUE)) continue; if(!Opaque && !(RenderFlags & LAYERRENDERFLAG_TRANSPARENT)) continue; Graphics()->QuadsSetSubsetFree( fx2f(q->m_aTexcoords[0].x), fx2f(q->m_aTexcoords[0].y), fx2f(q->m_aTexcoords[1].x), fx2f(q->m_aTexcoords[1].y), fx2f(q->m_aTexcoords[2].x), fx2f(q->m_aTexcoords[2].y), fx2f(q->m_aTexcoords[3].x), fx2f(q->m_aTexcoords[3].y)); float OffsetX = 0; float OffsetY = 0; float Rot = 0; // TODO: fix this if(q->m_PosEnv >= 0) { float aChannels[4]; pfnEval(q->m_PosEnvOffset, q->m_PosEnv, aChannels, pUser); OffsetX = aChannels[0]; OffsetY = aChannels[1]; Rot = aChannels[2] / 360.0f * pi * 2; } IGraphics::CColorVertex Array[4] = { IGraphics::CColorVertex(0, q->m_aColors[0].r * Conv * r, q->m_aColors[0].g * Conv * g, q->m_aColors[0].b * Conv * b, q->m_aColors[0].a * Conv * a * Alpha), IGraphics::CColorVertex(1, q->m_aColors[1].r * Conv * r, q->m_aColors[1].g * Conv * g, q->m_aColors[1].b * Conv * b, q->m_aColors[1].a * Conv * a * Alpha), IGraphics::CColorVertex(2, q->m_aColors[2].r * Conv * r, q->m_aColors[2].g * Conv * g, q->m_aColors[2].b * Conv * b, q->m_aColors[2].a * Conv * a * Alpha), IGraphics::CColorVertex(3, q->m_aColors[3].r * Conv * r, q->m_aColors[3].g * Conv * g, q->m_aColors[3].b * Conv * b, q->m_aColors[3].a * Conv * a * Alpha)}; Graphics()->SetColorVertex(Array, 4); CPoint *pPoints = q->m_aPoints; if(Rot != 0) { static CPoint aRotated[4]; aRotated[0] = q->m_aPoints[0]; aRotated[1] = q->m_aPoints[1]; aRotated[2] = q->m_aPoints[2]; aRotated[3] = q->m_aPoints[3]; pPoints = aRotated; Rotate(&q->m_aPoints[4], &aRotated[0], Rot); Rotate(&q->m_aPoints[4], &aRotated[1], Rot); Rotate(&q->m_aPoints[4], &aRotated[2], Rot); Rotate(&q->m_aPoints[4], &aRotated[3], Rot); } IGraphics::CFreeformItem Freeform( fx2f(pPoints[0].x) + OffsetX, fx2f(pPoints[0].y) + OffsetY, fx2f(pPoints[1].x) + OffsetX, fx2f(pPoints[1].y) + OffsetY, fx2f(pPoints[2].x) + OffsetX, fx2f(pPoints[2].y) + OffsetY, fx2f(pPoints[3].x) + OffsetX, fx2f(pPoints[3].y) + OffsetY); Graphics()->QuadsDrawFreeform(&Freeform, 1); } Graphics()->TrianglesEnd(); } void CRenderTools::RenderTileRectangle(int RectX, int RectY, int RectW, int RectH, unsigned char IndexIn, unsigned char IndexOut, float Scale, ColorRGBA Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; float r = 1, g = 1, b = 1, a = 1; if(ColorEnv >= 0) { float aChannels[4]; pfnEval(ColorEnvOffset, ColorEnv, aChannels, pUser); r = aChannels[0]; g = aChannels[1]; b = aChannels[2]; a = aChannels[3]; } Graphics()->QuadsBegin(); Graphics()->SetColor(Color.r * r, Color.g * g, Color.b * b, Color.a * a); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) { for(int x = StartX; x < EndX; x++) { unsigned char Index = (x >= RectX && x < RectX + RectW && y >= RectY && y < RectY + RectH) ? IndexIn : IndexOut; if(Index) { bool Render = false; if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, ColorRGBA Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; float r = 1, g = 1, b = 1, a = 1; if(ColorEnv >= 0) { float aChannels[4]; pfnEval(ColorEnvOffset, ColorEnv, aChannels, pUser); r = aChannels[0]; g = aChannels[1]; b = aChannels[2]; a = aChannels[3]; } if(Graphics()->IsTileBufferingEnabled()) Graphics()->QuadsTex3DBegin(); else Graphics()->QuadsBegin(); Graphics()->SetColor(Color.r * r, Color.g * g, Color.b * b, Color.a * a); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) { for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(RenderFlags & TILERENDERFLAG_EXTEND) { if(mx < 0) mx = 0; if(mx >= w) mx = w - 1; if(my < 0) my = 0; if(my >= h) my = h - 1; } else { if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; } int c = mx + my * w; unsigned char Index = pTiles[c].m_Index; if(Index) { unsigned char Flags = pTiles[c].m_Flags; bool Render = false; if(Flags & TILEFLAG_OPAQUE && Color.a * a > 254.0f / 255.0f) { if(RenderFlags & LAYERRENDERFLAG_OPAQUE) Render = true; } else { if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; } if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; if(Graphics()->IsTileBufferingEnabled()) { x0 = 0; y0 = 0; x1 = x0 + 1; y1 = y0; x2 = x0 + 1; y2 = y0 + 1; x3 = x0; y3 = y0 + 1; } if(Flags & TILEFLAG_VFLIP) { x0 = x2; x1 = x3; x2 = x3; x3 = x0; } if(Flags & TILEFLAG_HFLIP) { y0 = y3; y2 = y1; y3 = y1; y1 = y0; } if(Flags & TILEFLAG_ROTATE) { float Tmp = x0; x0 = x3; x3 = x2; x2 = x1; x1 = Tmp; Tmp = y0; y0 = y3; y3 = y2; y2 = y1; y1 = Tmp; } if(Graphics()->IsTileBufferingEnabled()) { Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsTex3DDrawTL(&QuadItem, 1); } else { Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } x += pTiles[c].m_Skip; } } if(Graphics()->IsTileBufferingEnabled()) Graphics()->QuadsTex3DEnd(); else Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha) { if(!g_Config.m_ClTextEntities) return; float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay) return; // its useless to render text at this distance float Size = g_Config.m_ClTextEntitiesSize / 100.f; float ToCenterOffset = (1 - Size) / 2.f; for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; int c = mx + my * w; unsigned char Index = pTele[c].m_Number; if(Index && pTele[c].m_Type != TILE_TELECHECKIN && pTele[c].m_Type != TILE_TELECHECKINEVIL) { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Index); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale - 3.f, (my + ToCenterOffset) * Scale, Size * Scale, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderSpeedupOverlay(CSpeedupTile *pSpeedup, int w, int h, float Scale, float Alpha) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay) return; // its useless to render text at this distance float Size = g_Config.m_ClTextEntitiesSize / 100.f; float ToCenterOffset = (1 - Size) / 2.f; for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; int c = mx + my * w; int Force = (int)pSpeedup[c].m_Force; int MaxSpeed = (int)pSpeedup[c].m_MaxSpeed; if(Force) { // draw arrow Graphics()->TextureSet(g_pData->m_aImages[IMAGE_SPEEDUP_ARROW].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(255.0f, 255.0f, 255.0f, Alpha); SelectSprite(SPRITE_SPEEDUP_ARROW); Graphics()->QuadsSetRotation(pSpeedup[c].m_Angle * (3.14159265f / 180.0f)); DrawSprite(mx * Scale + 16, my * Scale + 16, 35.0f); Graphics()->QuadsEnd(); if(g_Config.m_ClTextEntities) { // draw force char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Force); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale, (my + 0.5f + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); if(MaxSpeed) { str_format(aBuf, sizeof(aBuf), "%d", MaxSpeed); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale, (my + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } } } Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderSwitchOverlay(CSwitchTile *pSwitch, int w, int h, float Scale, float Alpha) { if(!g_Config.m_ClTextEntities) return; float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay) return; // its useless to render text at this distance float Size = g_Config.m_ClTextEntitiesSize / 100.f; float ToCenterOffset = (1 - Size) / 2.f; for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; int c = mx + my * w; unsigned char Index = pSwitch[c].m_Number; if(Index) { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Index); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale, (my + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } unsigned char Delay = pSwitch[c].m_Delay; if(Delay) { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Delay); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale, (my + 0.5f + ToCenterOffset / 2) * Scale, Size * Scale / 2.f, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderTuneOverlay(CTuneTile *pTune, int w, int h, float Scale, float Alpha) { if(!g_Config.m_ClTextEntities) return; float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; if(EndX - StartX > Graphics()->ScreenWidth() / g_Config.m_GfxTextOverlay || EndY - StartY > Graphics()->ScreenHeight() / g_Config.m_GfxTextOverlay) return; // its useless to render text at this distance float Size = g_Config.m_ClTextEntitiesSize / 100.f; for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; int c = mx + my * w; unsigned char Index = pTune[c].m_Number; if(Index) { char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d", Index); TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha); TextRender()->Text(0, mx * Scale + 11.f, my * Scale + 6.f, Size * Scale / 1.5f - 5.f, aBuf, -1.0f); // numbers shouldn't be too big and in the center of the tile TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderTelemap(CTeleTile *pTele, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; Graphics()->QuadsBegin(); Graphics()->SetColor(Color); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(RenderFlags & TILERENDERFLAG_EXTEND) { if(mx < 0) mx = 0; if(mx >= w) mx = w - 1; if(my < 0) my = 0; if(my >= h) my = h - 1; } else { if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; } int c = mx + my * w; unsigned char Index = pTele[c].m_Type; if(Index) { bool Render = false; if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderSpeedupmap(CSpeedupTile *pSpeedupTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) { //Graphics()->TextureSet(img_get(tmap->image)); float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); //Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; Graphics()->QuadsBegin(); Graphics()->SetColor(Color); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(RenderFlags & TILERENDERFLAG_EXTEND) { if(mx < 0) mx = 0; if(mx >= w) mx = w - 1; if(my < 0) my = 0; if(my >= h) my = h - 1; } else { if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; } int c = mx + my * w; unsigned char Index = pSpeedupTile[c].m_Type; if(Index) { bool Render = false; if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderSwitchmap(CSwitchTile *pSwitchTile, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) { //Graphics()->TextureSet(img_get(tmap->image)); float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); //Graphics()->MapScreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; Graphics()->QuadsBegin(); Graphics()->SetColor(Color); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(RenderFlags & TILERENDERFLAG_EXTEND) { if(mx < 0) mx = 0; if(mx >= w) mx = w - 1; if(my < 0) my = 0; if(my >= h) my = h - 1; } else { if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; } int c = mx + my * w; unsigned char Index = pSwitchTile[c].m_Type; if(Index) { if(Index == TILE_SWITCHTIMEDOPEN) Index = 8; unsigned char Flags = pSwitchTile[c].m_Flags; bool Render = false; if(Flags & TILEFLAG_OPAQUE) { if(RenderFlags & LAYERRENDERFLAG_OPAQUE) Render = true; } else { if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; } if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; if(Flags & TILEFLAG_VFLIP) { x0 = x2; x1 = x3; x2 = x3; x3 = x0; } if(Flags & TILEFLAG_HFLIP) { y0 = y3; y2 = y1; y3 = y1; y1 = y0; } if(Flags & TILEFLAG_ROTATE) { float Tmp = x0; x0 = x3; x3 = x2; x2 = x1; x1 = Tmp; Tmp = y0; y0 = y3; y3 = y2; y2 = y1; y1 = Tmp; } Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } void CRenderTools::RenderTunemap(CTuneTile *pTune, int w, int h, float Scale, ColorRGBA Color, int RenderFlags) { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); // calculate the final pixelsize for the tiles float TilePixelSize = 1024 / 32.0f; float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); float FinalTilesetScale = FinalTileSize / TilePixelSize; Graphics()->QuadsBegin(); Graphics()->SetColor(Color); int StartY = (int)(ScreenY0 / Scale) - 1; int StartX = (int)(ScreenX0 / Scale) - 1; int EndY = (int)(ScreenY1 / Scale) + 1; int EndX = (int)(ScreenX1 / Scale) + 1; // adjust the texture shift according to mipmap level float TexSize = 1024.0f; float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int mx = x; int my = y; if(RenderFlags & TILERENDERFLAG_EXTEND) { if(mx < 0) mx = 0; if(mx >= w) mx = w - 1; if(my < 0) my = 0; if(my >= h) my = h - 1; } else { if(mx < 0) continue; // mx = 0; if(mx >= w) continue; // mx = w-1; if(my < 0) continue; // my = 0; if(my >= h) continue; // my = h-1; } int c = mx + my * w; unsigned char Index = pTune[c].m_Type; if(Index) { bool Render = false; if(RenderFlags & LAYERRENDERFLAG_TRANSPARENT) Render = true; if(Render) { int tx = Index % 16; int ty = Index / 16; int Px0 = tx * (1024 / 16); int Py0 = ty * (1024 / 16); int Px1 = Px0 + (1024 / 16) - 1; int Py1 = Py0 + (1024 / 16) - 1; float x0 = Nudge + Px0 / TexSize + Frac; float y0 = Nudge + Py0 / TexSize + Frac; float x1 = Nudge + Px1 / TexSize - Frac; float y1 = Nudge + Py0 / TexSize + Frac; float x2 = Nudge + Px1 / TexSize - Frac; float y2 = Nudge + Py1 / TexSize - Frac; float x3 = Nudge + Px0 / TexSize + Frac; float y3 = Nudge + Py1 / TexSize - Frac; Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); IGraphics::CQuadItem QuadItem(x * Scale, y * Scale, Scale, Scale); Graphics()->QuadsDrawTL(&QuadItem, 1); } } } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); }