diff --git a/profiles/common/scriptvars.lua b/profiles/common/scriptvars.lua index c0032c6cf..eaaab6b81 100644 --- a/profiles/common/scriptvars.lua +++ b/profiles/common/scriptvars.lua @@ -76,9 +76,11 @@ root.ScriptVars = { SW_MASK3ALPHA_OFS=2788, SW_BGEFF1_OFSX=2800, SW_BGEFF1_OFSY=2801, - SW_BGEFF1_ALPHA_OFS=2806, SW_BGEFF1_SIZE_OFS=2802, + SW_BGEFF1_ROTX_OFS=2803, + SW_BGEFF1_ROTY_OFS=2804, SW_BGEFF1_ROTZ_OFS=2805, + SW_BGEFF1_ALPHA_OFS=2806, SW_BGEFF1_MASK_VERTEX1_OFSX=2809, SW_BGEFF1_MASK_VERTEX1_OFSY=2810, SW_MAINTHDP=3304, @@ -347,6 +349,8 @@ root.ScriptVars = { SW_BGEFF1_MASK_TYPE=6612, SW_BGEFF1_FADECT=6613, SW_BGEFF1_MODE=6614, + SW_BGEFF1_ROTX=6615, + SW_BGEFF1_ROTY=6616, SW_BGEFF1_ROTZ=6617, SW_BGEFF1_ALPHA=6618, SW_BGEFF1_MASKNO=6619, diff --git a/src/background2d.cpp b/src/background2d.cpp index 609c40fea..8ae2c255e 100644 --- a/src/background2d.cpp +++ b/src/background2d.cpp @@ -169,9 +169,13 @@ void Background2D::RenderBgEff(int bgId, int layer) { FadeCount = ScrWork[SW_BGEFF1_FADECT + structOffset]; FadeRange = ScrWork[SW_BGEFF1_MASKFADERANGE + structOffset]; - Angle = (ScrWork[SW_BGEFF1_ROTZ + structOffset] + - ScrWork[SW_BGEFF1_ROTZ_OFS + structOfsOffset]) * - (float)(2.0f * M_PI / (float)(1 << 16)); + Rotation = ScrWorkAnglesToQuaternion( + ScrWork[SW_BGEFF1_ROTX + structOffset] + + ScrWork[SW_BGEFF1_ROTX_OFS + structOfsOffset], + ScrWork[SW_BGEFF1_ROTY + structOffset] + + ScrWork[SW_BGEFF1_ROTY_OFS + structOfsOffset], + ScrWork[SW_BGEFF1_ROTZ + structOffset] + + ScrWork[SW_BGEFF1_ROTZ_OFS + structOfsOffset]); Scale = glm::vec2((ScrWork[SW_BGEFF1_SIZE + structOffset] + ScrWork[SW_BGEFF1_SIZE_OFS + structOfsOffset]) / @@ -222,6 +226,14 @@ void Background2D::RenderBgEff(int bgId, int layer) { Origin = {0.0f, 0.0f}; for (const glm::vec2 vertex : vertices) Origin += vertex / (float)vertexCount; + // Transform vertices + const glm::mat4 transformation = TransformationMatrix( + Origin, Scale, {Origin, 0.0f}, Rotation, DisplayCoords); + std::transform(vertices.begin(), vertices.end(), vertices.begin(), + [transformation](const glm::vec2 vertex) { + return transformation * glm::vec4(vertex, 0.0f, 1.0f); + }); + // Draw if (maskType == 0) { // Rectangle const glm::vec2 maskDimensions = vertices[3] - vertices[0]; @@ -241,43 +253,43 @@ void Background2D::RenderBgEff(int bgId, int layer) { } void Background2D::RenderRegular() { - Renderer->DrawSpriteOffset(BgSprite, DisplayCoords, Origin, Tint, Scale, - Angle, false); + const glm::mat4 transformation = TransformationMatrix( + Origin, Scale, {Origin, 0.0f}, Rotation, DisplayCoords); + Renderer->DrawSprite(BgSprite, transformation, Tint, false); for (int i = 0; i < MaxLinkedBgBuffers; i++) { if (Links[i].Direction != LD_Off && Links[i].LinkedBuffer != NULL) { - Renderer->DrawSpriteOffset(Links[i].LinkedBuffer->BgSprite, - Links[i].DisplayCoords, Origin, Tint, Scale, - Angle, false); + const glm::mat4 linkTransformation = + transformation * + glm::translate(glm::mat4(1.0f), {Links[i].DisplayCoords, 0.0f}); + + Renderer->DrawSprite(Links[i].LinkedBuffer->BgSprite, transformation, + Tint, false); } } } void Background2D::RenderMasked() { - Renderer->DrawMaskedSpriteOffset(BgSprite, Masks2D[MaskNumber].MaskSprite, - DisplayCoords, Origin, FadeCount, FadeRange, - Tint, Scale, Angle, false, false, false); + const glm::mat4 transformation = TransformationMatrix( + Origin, Scale, {Origin, 0.0f}, Rotation, DisplayCoords); + + Renderer->DrawMaskedSprite(BgSprite, Masks2D[MaskNumber].MaskSprite, + FadeCount, FadeRange, transformation, Tint, false, + false); } void Background2D::RenderMaskedInverted() { - Renderer->DrawMaskedSpriteOffset(BgSprite, Masks2D[MaskNumber].MaskSprite, - DisplayCoords, Origin, FadeCount, FadeRange, - Tint, Scale, Angle, false, true, false); + const glm::mat4 transformation = TransformationMatrix( + Origin, Scale, {Origin, 0.0f}, Rotation, DisplayCoords); + Renderer->DrawMaskedSprite(BgSprite, Masks2D[MaskNumber].MaskSprite, + FadeCount, FadeRange, transformation, Tint, true, + false); } void Background2D::RenderFade() { Tint.a *= FadeCount / 256.0f; - Renderer->DrawSpriteOffset(BgSprite, DisplayCoords, Origin, Tint, Scale, - Angle, false); - - for (int i = 0; i < MaxLinkedBgBuffers; i++) { - if (Links[i].Direction != LD_Off && Links[i].LinkedBuffer != NULL) { - Renderer->DrawSpriteOffset(Links[i].LinkedBuffer->BgSprite, - Links[i].DisplayCoords, Origin, Tint, Scale, - Angle, false); - } - } + RenderRegular(); } } // namespace Impacto \ No newline at end of file diff --git a/src/background2d.h b/src/background2d.h index 36b91894a..4d595c2ef 100644 --- a/src/background2d.h +++ b/src/background2d.h @@ -39,7 +39,7 @@ class Background2D : public Loadable { glm::vec2 DisplayCoords; glm::vec2 Origin = {0.0f, 0.0f}; glm::vec2 Scale = {1.0f, 1.0f}; - float Angle = 0.0f; + glm::quat Rotation = glm::quat(); glm::vec4 Tint = glm::vec4(1.0f); int MaskNumber; diff --git a/src/character2d.cpp b/src/character2d.cpp index 826ce0a98..5acbcf621 100644 --- a/src/character2d.cpp +++ b/src/character2d.cpp @@ -37,7 +37,7 @@ bool Character2D::LoadSync(uint32_t charaId) { // Skip to state data stream->Seek(0x68, SEEK_CUR); - MvlVerticesCount = Io::ReadLE(stream); + int vertexCount = Io::ReadLE(stream); int vertexOffset = Io::ReadLE(stream); for (int i = 0; i < stateCount; i++) { @@ -79,8 +79,18 @@ bool Character2D::LoadSync(uint32_t charaId) { // They seem to use the whole vertex array in all states, so... read it once // and forget about it? stream->Seek(vertexOffset, SEEK_SET); - MvlVertices.resize(MvlVerticesCount * 5); - Io::ReadArrayLE(MvlVertices.data(), stream, MvlVerticesCount * 5); + std::vector mvlVertexInfo(vertexCount * 5); + Io::ReadArrayLE(mvlVertexInfo.data(), stream, vertexCount * 5); + + MvlVertices.resize(vertexCount); + for (size_t i = 0; i < vertexCount; i++) { + MvlVertices[i] = VertexBufferSprites{ + .Position = {mvlVertexInfo[i * 5], mvlVertexInfo[i * 5 + 1]}, + .UV = glm::vec2(mvlVertexInfo[i * 5 + 3], mvlVertexInfo[i * 5 + 4]), + .Tint = Tint, + }; + } + delete stream; } else { // LAY format @@ -146,7 +156,7 @@ void Character2D::UnloadSync() { Show = false; std::fill(Layers.begin(), Layers.end(), -1); MvlVertices.clear(); - MvlIndicesCount = 0; + MvlIndices.clear(); States.clear(); StatesToDraw.clear(); } @@ -165,7 +175,7 @@ void Character2D::MainThreadOnLoad() { void Character2D::Update(float dt) { if (Profile::CharaIsMvl) { - MvlIndicesCount = 0; + MvlIndices.clear(); StatesToDraw.clear(); StatesToDraw.push_back((Face & 0xFFFF0000) >> 16); // face StatesToDraw.push_back(0x40000000 | ((Face & 0xFFFF0000) >> 8) | @@ -178,9 +188,8 @@ void Character2D::Update(float dt) { Character2DState const& state = stateItr->second; auto& stateIndices = std::get_if(&state.Data)->Indices; - std::copy(stateIndices.get(), stateIndices.get() + state.Count, - MvlIndices.begin() + MvlIndicesCount); - MvlIndicesCount += state.Count; + MvlIndices.insert(MvlIndices.end(), stateIndices.get(), + stateIndices.get() + state.Count); } } } else { @@ -202,10 +211,10 @@ void Character2D::Render(int layer) { if (Status != LS_Loaded || !OnLayer(layer) || !Show) return; if (Profile::CharaIsMvl) { - Renderer->DrawCharacterMvl(CharaSprite, glm::vec2(OffsetX, OffsetY), - MvlVerticesCount, MvlVertices.data(), - MvlIndicesCount, MvlIndices.data(), false, Tint, - glm::vec2(ScaleX, ScaleY)); + const glm::mat4 transformation = TransformationMatrix( + {0.0f, 0.0f}, {ScaleX, ScaleY}, {0.0f, 0.0f}, 0.0f, {OffsetX, OffsetY}); + Renderer->DrawVertices(CharaSpriteSheet, MvlVertices, MvlIndices, + transformation); } else { for (auto id : StatesToDraw) { if (auto stateItr = States.find(id); stateItr != States.end()) { diff --git a/src/character2d.h b/src/character2d.h index 7b18fd599..42ebe3540 100644 --- a/src/character2d.h +++ b/src/character2d.h @@ -45,8 +45,6 @@ struct Character2DState { } }; -int constexpr MaxMvlIndices = 128 * 1024; - class Character2D : public Loadable { friend class Loadable; @@ -86,10 +84,8 @@ class Character2D : public Loadable { ankerl::unordered_dense::map States; std::vector StatesToDraw; - int MvlVerticesCount; - std::vector MvlVertices; - std::array MvlIndices; - int MvlIndicesCount; + std::vector MvlVertices; + std::vector MvlIndices; }; int constexpr MaxCharacters2D = 16; diff --git a/src/game.cpp b/src/game.cpp index 61b8f9b7f..26766b3f4 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -370,7 +370,7 @@ static void RenderMain() { } glm::vec4 col = ScrWorkGetColor(SW_MASK1COLOR); col.a = glm::min(maskAlpha / 255.0f, 1.0f); - Renderer->DrawRect(RectF(maskPosX, maskPosY, maskSizeX, maskSizeY), + Renderer->DrawQuad(RectF(maskPosX, maskPosY, maskSizeX, maskSizeY), col); } } @@ -522,7 +522,7 @@ void Render() { break; } case DrawComponentType::Mask: { - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), glm::vec4(0.0f, 0.0f, 0.0f, (ScrWork[SW_RESTARTMASK] / 256.0f))); break; diff --git a/src/games/cc/dialoguebox.cpp b/src/games/cc/dialoguebox.cpp index ada4931aa..1c3bac5c5 100644 --- a/src/games/cc/dialoguebox.cpp +++ b/src/games/cc/dialoguebox.cpp @@ -41,7 +41,7 @@ void DialogueBox::Render(DialoguePageMode mode, bool hasName, float nameWidth, } } else { glm::vec4 nvlBoxTint(0.0f, 0.0f, 0.0f, opacity * NVLBoxMaxOpacity); - Renderer->DrawRect(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), + Renderer->DrawQuad(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), nvlBoxTint); } } diff --git a/src/games/cc/titlemenu.cpp b/src/games/cc/titlemenu.cpp index 23d9b8c3c..c06edc590 100644 --- a/src/games/cc/titlemenu.cpp +++ b/src/games/cc/titlemenu.cpp @@ -252,7 +252,7 @@ void TitleMenu::Render() { OverlaySprite, RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight)); DrawSmoke(SmokeOpacityNormal); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f - ScrWork[SW_TITLEDISPCT] / 60.0f)); diff --git a/src/games/cclcc/delusiontrigger.cpp b/src/games/cclcc/delusiontrigger.cpp index 484d3416e..2bfcd522e 100644 --- a/src/games/cclcc/delusiontrigger.cpp +++ b/src/games/cclcc/delusiontrigger.cpp @@ -184,150 +184,172 @@ void DelusionTrigger::Update(float dt) { } void DelusionTrigger::RenderStartTransition(float spinAngle, int spinAlpha) { - float scale = (ScrWork[SW_DELUSION_SPIN_COUNTER] + 64) / 64.0f; - float transparency = - ((64 - ScrWork[SW_DELUSION_SPIN_COUNTER]) * spinAlpha >> 6) / 256.0f; + const glm::vec2 scale((ScrWork[SW_DELUSION_SPIN_COUNTER] + 64) / 64.0f); + const glm::vec4 tint = { + 1.0f, 1.0f, 1.0f, + ((64 - ScrWork[SW_DELUSION_SPIN_COUNTER]) * spinAlpha >> 6) / 256.0f}; + if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_PosOnly)) { - glm::vec2 topLeft = {-800.0f, -109.0f}; - glm::vec2 offset = {600.0f, 557.0f}; - Renderer->DrawSpriteOffset(PositiveDelusionSprite, topLeft, offset, - {1.0f, 1.0f, 1.0f, transparency}, {scale, scale}, - spinAngle); + const glm::vec2 offset = {-800, -109}; + const glm::vec2 origin = {600, 557}; + const glm::mat4 transformation = + TransformationMatrix(origin, scale, origin, spinAngle, offset); + Renderer->DrawSprite(PositiveDelusionSprite, transformation, tint); } + if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_NegOnly)) { - glm::vec2 topLeft = {1696.0f, -109.0f}; - glm::vec2 offset = {424.0f, 557.0f}; - Renderer->DrawSpriteOffset(NegativeDelusionSprite, topLeft, offset, - {1.0f, 1.0f, 1.0f, transparency}, {scale, scale}, - spinAngle); + const glm::vec2 offset = {1696, -109}; + const glm::vec2 origin = {424, 557}; + const glm::mat4 transformation = + TransformationMatrix(origin, scale, origin, spinAngle, offset); + Renderer->DrawSprite(NegativeDelusionSprite, transformation, tint); } } void DelusionTrigger::RenderStable(float spinAngle, int spinAlpha) { + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + if (DelusionState == DS_Neutral) { if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_PosOnly)) { - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, {-800, -109}, {600, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1.0, 1.0}, spinAngle); + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {600, 557}) + .Translate({-800, -109}); + Renderer->DrawSprite(PositiveDelusionSprite, dest, tint); } + if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_NegOnly)) { - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, {1696.0f, -109.0f}, {424, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1.0, 1.0}, spinAngle); + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {424, 557}) + .Translate({1696, -109}); + Renderer->DrawSprite(NegativeDelusionSprite, dest, tint); } + } else if (DelusionState == DS_Positive) { - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{448, -109.0f}, glm::vec2{600, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1.8, 1.8}, spinAngle); + const glm::mat4 transformation = TransformationMatrix( + {600, 557}, {1.8f, 1.8f}, {600, 557}, spinAngle, {448, -109}); + Renderer->DrawSprite(PositiveDelusionSprite, transformation, tint); + } else if (DelusionState == DS_Negative) { - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{448, -109.0f}, glm::vec2{424, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1.8, 1.8}, spinAngle); + const glm::mat4 transformation = TransformationMatrix( + {424, 557}, {1.8f, 1.8f}, {424, 557}, spinAngle, {448, -109}); + Renderer->DrawSprite(NegativeDelusionSprite, transformation, tint); } } void DelusionTrigger::RenderNeutralToPositiveTransition(float spinAngle, int spinAlpha) { - float topLeftX = - 448.0f - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6); - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{topLeftX, -109.0f}, {600, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, - {1.8 - ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8 / 64, - 1.8 - ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8 / 64}, - spinAngle); + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + + const glm::vec2 offset = { + 448 - ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6), -109}; + const glm::vec2 scale(1.8f - + ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8f / 64.0f); + const glm::mat4 transformation = + TransformationMatrix({600, 557}, scale, {600, 557}, spinAngle, offset); + Renderer->DrawSprite(PositiveDelusionSprite, transformation, tint); + if (ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) { - float centerX = - 2944.0f - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6); - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{centerX, -109.0f}, - glm::vec2{424.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 center = { + 2944 - ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6), -109}; + const CornersQuad dest = NegativeDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {424, 557}) + .Translate(center); + Renderer->DrawSprite(NegativeDelusionSprite, dest, tint); } } void DelusionTrigger::RenderNeutralToNegativeTransition(float spinAngle, int spinAlpha) { - float topLeftX = - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) + 448.0f; - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{topLeftX, -109.0f}, {424, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, - {1.8 - ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8 / 64, - 1.8 - ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8 / 64}, - spinAngle); + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + + const glm::vec2 offset = { + ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) + 448, -109}; + const glm::vec2 scale(1.8f - + ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8f / 64.0f); + const glm::mat4 transformation = + TransformationMatrix({424, 557}, scale, {424, 557}, spinAngle, offset); + Renderer->DrawSprite(NegativeDelusionSprite, transformation, tint); + if (ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) { - float topLeftX = - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) - 2048.0f; - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{topLeftX, -109.0f}, - glm::vec2{600.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 offsetPositive = { + ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) - 2048, -109}; + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {600, 557}) + .Translate(offsetPositive); + Renderer->DrawSprite(PositiveDelusionSprite, dest, tint); } } void DelusionTrigger::RenderPositiveToNeutralTransition(float spinAngle, int spinAlpha) { - float scale = 1.0f + ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8f / 64.0f; + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + + const glm::vec2 offset = { + ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) - 800, -109}; + const glm::vec2 scale(1.0f + + ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8f / 64.0f); + const glm::mat4 transformation = + TransformationMatrix({600, 557}, scale, {600, 557}, spinAngle, offset); + Renderer->DrawSprite(PositiveDelusionSprite, transformation, tint); - float topLeftX = - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) - 800.0f; - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{topLeftX, -109.0f}, {600, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {scale, scale}, spinAngle); if (ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) { - float centerX = - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) + 1696.0f; - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{centerX, -109.0f}, - glm::vec2{424.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 center = { + ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6) + 1696, -109}; + const CornersQuad dest = NegativeDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {424, 557}) + .Translate(center); + Renderer->DrawSprite(NegativeDelusionSprite, dest, tint); } } void DelusionTrigger::RenderNegativeToNeutralTransition(float spinAngle, int spinAlpha) { - float scale = - 1.0f + (float)(ScrWork[SW_DELUSION_SPIN_COUNTER]) * 0.8f / 64.0f; - - float topLeftX = - 1696.0f - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6); - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{topLeftX, -109.0f}, {424, 557}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {scale, scale}, spinAngle); + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + + const glm::vec2 offset = { + 1696 - ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6), -109}; + const glm::vec2 scale(1.0f + + ScrWork[SW_DELUSION_SPIN_COUNTER] * 0.8f / 64.0f); + const glm::mat4 transformation = + TransformationMatrix({424, 557}, scale, {424, 557}, spinAngle, offset); + Renderer->DrawSprite(NegativeDelusionSprite, transformation, tint); + if (ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) { - float centerX = - -800.0f - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6); - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{centerX, -109.0f}, - glm::vec2{600.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 center = { + -800 - ((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 6), -109}; + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {600, 557}) + .Translate(center); + Renderer->DrawSprite(PositiveDelusionSprite, dest, tint); } } void DelusionTrigger::RenderEndNeutralTransition(float spinAngle, int spinAlpha) { + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, spinAlpha / 256.0f}; + if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_PosOnly)) { - float topLeftX = - (float)((ScrWork[SW_DELUSION_BG_COUNTER] * 1248) >> 5) - 2048.0f; - Renderer->DrawSpriteOffset( - PositiveDelusionSprite, glm::vec2{topLeftX, -109.0f}, - glm::vec2{600.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 offset = { + ((ScrWork[SW_DELUSION_BG_COUNTER] * 1248) >> 5) - 2048, -109}; + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {600, 557}) + .Translate(offset); + Renderer->DrawSprite(PositiveDelusionSprite, dest, tint); } + if ((ScrWork[SW_DELUSION_LIMIT] == Delusion_Both) || (ScrWork[SW_DELUSION_LIMIT] == Delusion_NegOnly)) { - float topLeftX = - 2944.0f - (float)((ScrWork[SW_DELUSION_SPIN_COUNTER] * 1248) >> 5); - Renderer->DrawSpriteOffset( - NegativeDelusionSprite, glm::vec2{topLeftX, -109.0f}, - glm::vec2{424.0f, 557.0f}, - {1.0f, 1.0f, 1.0f, (float)spinAlpha / 256.0f}, {1, 1}, spinAngle); + const glm::vec2 offset = { + 2944 - ((ScrWork[SW_DELUSION_BG_COUNTER] * 1248) >> 5), -109}; + const CornersQuad dest = PositiveDelusionSprite.ScaledBounds() + .Rotate(spinAngle, {424, 557}) + .Translate(offset); + Renderer->DrawSprite(NegativeDelusionSprite, dest, tint); } } @@ -335,15 +357,16 @@ void DelusionTrigger::Render() { if (ScrWork[SW_DELUSION_BG_COUNTER] == 0) { return; } - float mtrgSelAlpha = - ((ScrWork[SW_DELUSION_BG_COUNTER] * 8) & 0xffffff) / 256.0f; + const glm::vec4 mtrgSelTint = { + 1.0f, 1.0f, 1.0f, + ((ScrWork[SW_DELUSION_BG_COUNTER] * 8) & 0xffffff) / 256.0f}; if (ScrWork[SW_DELUSION_BG_COUNTER] < 32) { - float scale = 2.0f - (ScrWork[SW_DELUSION_BG_COUNTER] / 32.0f); - Renderer->DrawSpriteOffset( - BgOverlaySprite, glm::vec2{0.0f, 0.0f}, glm::vec2{960.0f, 413.0f}, - glm::vec4{1.0f, 1.0f, 1.0f, mtrgSelAlpha}, {scale, scale}); + const glm::vec2 scale(2.0f - (ScrWork[SW_DELUSION_BG_COUNTER] / 32.0f)); + const CornersQuad dest = + BgOverlaySprite.ScaledBounds().Scale(scale, {960, 413}); + Renderer->DrawSprite(BgOverlaySprite, dest, mtrgSelTint); } else { - Renderer->DrawSprite(BgOverlaySprite, {0, 0, 1920, 1080}); + Renderer->DrawSprite(BgOverlaySprite, RectF{0, 0, 1920, 1080}); } float spinAngle = MtrgAng / 65535.0f * 2.0f * float(M_PI); diff --git a/src/games/cclcc/mapsystem.cpp b/src/games/cclcc/mapsystem.cpp index 508890f9b..dcc880023 100644 --- a/src/games/cclcc/mapsystem.cpp +++ b/src/games/cclcc/mapsystem.cpp @@ -1419,18 +1419,19 @@ void MapSystemCCLCC::MapDispPhoto(int id, int photoGroupId) { float scaledPosOffsetY = (yOffset + 10) * scaledFactor; // Shadow - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2((xOffset + 82 + 3) * scaledFactor - 82, - (yOffset + 10 + 3) * scaledFactor - 10), - glm::vec2{82, 10}, glm::vec4{0.0f, 0.0f, 0.0f, (alpha / 2) / 256.0f}, - glm::vec2{shadowZoom, shadowZoom}, angle); + const glm::vec2 shadowPos = {(xOffset + 82 + 3) * scaledFactor - 82, + (yOffset + 10 + 3) * scaledFactor - 10}; + const glm::mat4 shadowTransformation = TransformationMatrix( + {82, 10}, {shadowZoom, shadowZoom}, {82, 10}, angle, shadowPos); + Renderer->DrawSprite(displayedSprite, shadowTransformation, + {0.0f, 0.0f, 0.0f, (alpha / 2) / 256.0f}); + // Photo - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2(scaledPosOffsetX - 82, scaledPosOffsetY - 10), - glm::vec2{82, 10}, glm::vec4{1.0f, 1.0f, 1.0f, alpha / 256.0f}, - glm::vec2{zoomMulti, zoomMulti}, angle); + const glm::mat4 photoTransformation = + TransformationMatrix({82, 10}, {zoomMulti, zoomMulti}, {82, 10}, angle, + {scaledPosOffsetX - 82, scaledPosOffsetY - 10}); + Renderer->DrawSprite(displayedSprite, photoTransformation, + {1.0f, 1.0f, 1.0f, alpha / 256.0f}); } } @@ -1498,27 +1499,26 @@ void MapSystemCCLCC::MapPoolDispPhoto(int poolId) { (MapPoolCurCt[poolId] * 50.0f / 16.0f + 78.0f) * 2; // HoverTag Tag - Renderer->DrawSpriteOffset( - SelectedMapPoolTagSprite, - glm::vec2(scaledPosOffsetX - selectedTagXOffset, - scaledPosOffsetY - 18), - glm::vec2{selectedTagXOffset, 11}, - glm::vec4{1.0f, 1.0f, 1.0f, alpha / 256.0f}, - glm::vec2{zoomMulti / 2.0, zoomMulti / 2.0}, angle); + const glm::vec2 origin = {selectedTagXOffset, 11}; + const glm::mat4 transformation = TransformationMatrix( + origin, glm::vec2(zoomMulti / 2.0f), origin, angle, + {scaledPosOffsetX - selectedTagXOffset, scaledPosOffsetY - 18}); + Renderer->DrawSprite(SelectedMapPoolTagSprite, transformation, + {1.0f, 1.0f, 1.0f, alpha / 256.0f}); } // Shadow - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2((xOffset + 82 + 3) * scaledFactor - 82, - (yOffset + 10 + 3) * scaledFactor - 10), - glm::vec2{82, 10}, glm::vec4{0.0f, 0.0f, 0.0f, (alpha >> 1) / 256.0f}, - glm::vec2{shadowZoom, shadowZoom}, angle); + const glm::mat4 shadowTransformation = + TransformationMatrix({82, 10}, glm::vec2(shadowZoom), {82, 10}, angle, + {(xOffset + 82 + 3) * scaledFactor - 82, + (yOffset + 10 + 3) * scaledFactor - 10}); + Renderer->DrawSprite(displayedSprite, shadowTransformation, + {0.0f, 0.0f, 0.0f, (alpha >> 1) / 256.0f}); // Photo - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2(scaledPosOffsetX - 82, scaledPosOffsetY - 10), - glm::vec2{82, 10}, glm::vec4{1.0f, 1.0f, 1.0f, alpha / 256.0f}, - glm::vec2{zoomMulti, zoomMulti}, angle); + const glm::mat4 photoTransformation = + TransformationMatrix({82, 10}, glm::vec2(zoomMulti), {82, 10}, angle, + {scaledPosOffsetX - 82, scaledPosOffsetY - 10}); + Renderer->DrawSprite(displayedSprite, photoTransformation, + {1.0f, 1.0f, 1.0f, alpha / 256.0f}); if (!MapPool[poolId].button.Enabled) { MapPool[poolId].button.Enabled = true; MapPool[poolId].button.Id = poolId; @@ -1575,10 +1575,11 @@ void MapSystemCCLCC::MapPoolDispPin(int poolId) { glm::vec4 color = RgbIntToFloat(Tints[poolId]); color[3] = alpha / 256.0f; - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2(scaledPosOffsetX - 16.0f, scaledPosOffsetY - 29.0f), - glm::vec2{16.0f, 29.0f}, color, glm::vec2{zoomMulti, zoomMulti}, 0); + const RectF dest = + displayedSprite.ScaledBounds() + .Scale({zoomMulti, zoomMulti}, {16.0f, 29.0f}) + .Translate({scaledPosOffsetX - 16.0f, scaledPosOffsetY - 29.0f}); + Renderer->DrawSprite(displayedSprite, dest, color); } } @@ -1648,10 +1649,11 @@ void MapSystemCCLCC::MapDispPin(int id) { glm::vec4 color = RgbIntToFloat(Tints[mappedId + 27]); color[3] = alpha / 256.0f; - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2(scaledPosOffsetX - 16.0f, scaledPosOffsetY - 29.0f), - glm::vec2{16.0f, 29.0f}, color, glm::vec2{zoomMulti, zoomMulti}, 0); + const RectF dest = + displayedSprite.ScaledBounds() + .Scale({zoomMulti, zoomMulti}, {16, 29}) + .Translate({scaledPosOffsetX - 16.0f, scaledPosOffsetY - 29.0f}); + Renderer->DrawSprite(displayedSprite, dest, color); } } @@ -1696,20 +1698,16 @@ void MapSystemCCLCC::MapDispArticle(int id) { float displayPhotoY = scaledPosOffsetY - 16; // Shadow - Renderer->DrawSpriteOffset( - MapPartsArticleSprites[partId], - glm::vec2(displayShadowPhotoX, displayShadowPhotoY), glm::vec2{166, 16}, - glm::vec4{0.0f, 0.0f, 0.0f, (alpha >> 1) / 256.0f}, - glm::vec2{shadowZoom * 0.5 * scaledFactor, - shadowZoom * scaledFactor * 0.5}, - angle); + const glm::mat4 shadowTransformation = TransformationMatrix( + {166, 16}, glm::vec2(shadowZoom * 0.5 * scaledFactor), {166, 16}, angle, + {displayShadowPhotoX, displayShadowPhotoY}); + Renderer->DrawSprite(MapPartsArticleSprites[partId], shadowTransformation, + {0.0f, 0.0f, 0.0f, (alpha >> 1) / 256.0f}); // Photo - Renderer->DrawSpriteOffset(MapPartsArticleSprites[partId], - glm::vec2(displayPhotoX, displayPhotoY), - glm::vec2{166, 16}, glm::vec4{1.0f}, - glm::vec2{zoomMulti * 0.5 * scaledFactor, - zoomMulti * 0.5 * scaledFactor}, - angle); + const glm::mat4 photoTransformation = TransformationMatrix( + {166, 16}, glm::vec2(zoomMulti * 0.5 * scaledFactor), {166, 16}, angle, + {displayPhotoX, displayPhotoY}); + Renderer->DrawSprite(MapPartsArticleSprites[partId], photoTransformation); } } @@ -1753,21 +1751,20 @@ void MapSystemCCLCC::MapDispTag(int id) { float shadowScaledPosOffsetX = (xOffset + 2.0f + 46.0f) * scaledFactor; float shadowScaledPosOffsetY = (yOffset + 2.0f + 16.0f) * scaledFactor; // Shadow - Renderer->DrawSpriteOffset(displayedSprite, - glm::vec2(shadowScaledPosOffsetX - 46.0f, - shadowScaledPosOffsetY - 16.0f), - glm::vec2{46.0f, 16.0f}, - {0.0f, 0.0f, 0.0f, alpha / 2 / 256.0f}, - glm::vec2{shadowZoom, shadowZoom}, angle); - float scaledPosOffsetX = (xOffset + 46.0f) * scaledFactor; - float scaledPosOffsetY = (yOffset + 16.0f) * scaledFactor; + const glm::mat4 shadowTransformation = TransformationMatrix( + {46, 16}, glm::vec2(shadowZoom), {46, 16}, angle, + {shadowScaledPosOffsetX - 46.0f, shadowScaledPosOffsetY - 16.0f}); + Renderer->DrawSprite(displayedSprite, shadowTransformation, + {0.0f, 0.0f, 0.0f, (alpha >> 1) / 256.0f}); // Tag - Renderer->DrawSpriteOffset( - displayedSprite, - glm::vec2(scaledPosOffsetX - 46.0f, scaledPosOffsetY - 16.0f), - glm::vec2{46.0f, 16.0f}, {1.0f, 1.0f, 1.0f, alpha / 256.0f}, - glm::vec2{zoomMulti, zoomMulti}, angle); + const glm::vec2 scaledPosOffset = + glm::vec2(xOffset + 46.0f, yOffset + 16.0f) * scaledFactor; + const glm::mat4 tagTransformation = + TransformationMatrix({46, 16}, {zoomMulti, zoomMulti}, {46, 16}, angle, + scaledPosOffset - glm::vec2(46.0f, 16.0f)); + Renderer->DrawSprite(displayedSprite, tagTransformation, + {1.0f, 1.0f, 1.0f, alpha / 256.0f}); } } @@ -1832,10 +1829,12 @@ void MapSystemCCLCC::MapDispLine(int id, int partType) { RectF lineShadowRect(xPos * scaledFactor, yPosShadow * scaledFactor, lineWidth * scaledFactor, 10.0f * scaledFactor); - Renderer->DrawSprite(shortenedLine, lineShadowRect, shadowTint, -angle); + Renderer->DrawSprite(shortenedLine, + lineShadowRect.RotateAroundCenter(-angle), shadowTint); RectF lineRect(xPos * scaledFactor, yPos * scaledFactor, lineWidth * scaledFactor, 10.0f * scaledFactor); - Renderer->DrawSprite(shortenedLine, lineRect, color, -angle); + Renderer->DrawSprite(shortenedLine, lineRect.RotateAroundCenter(-angle), + color); } } @@ -2007,7 +2006,8 @@ void MapSystemCCLCC::RenderButtonGuide() { glm::vec2{guideXWidth + 60, 988.0f}, // top right glm::vec2{guideXWidth + 60, 1042.0f}, // bottom right }; - Renderer->DrawSprite(MapButtonGuideSprite2, dest, tints, 0, false); + Renderer->DrawSprite(MapButtonGuideSprite2, dest, glm::mat4(1.0f), + tints, false); } } } @@ -2021,8 +2021,7 @@ void MapSystemCCLCC::Render() { MapBgSprite.Bounds = Rect(static_cast(MapPosX), static_cast(MapPosY), static_cast(MapBGWidth), static_cast(MapBGHeight)); - Renderer->DrawSprite(MapBgSprite, Rect(0, 0, 1920, 1080), glm::vec4{1.0f}, - 0.0f); + Renderer->DrawSprite(MapBgSprite, Rect(0, 0, 1920, 1080)); // Render map parts for (int i = 0; i < MapPartsMax; ++i) { diff --git a/src/games/cclcc/systemmenu.cpp b/src/games/cclcc/systemmenu.cpp index f897345f1..5e749b17c 100644 --- a/src/games/cclcc/systemmenu.cpp +++ b/src/games/cclcc/systemmenu.cpp @@ -154,76 +154,6 @@ void SystemMenu::Update(float dt) { } } -auto GenerateMatrix(CornersQuad const& corners, glm::vec2 offset) { - auto get2DIndex = [](int x, int y) { return x + (GridColCount + 1) * y; }; - constexpr int xVerticesCount = GridColCount + 1; - constexpr int yVerticesCount = GridRowCount + 1; - std::array topRowX; - std::array bottomRowX; - const float topRowWidthDelta = - (corners.TopRight.x - corners.TopLeft.x) / GridColCount; - const float bottomRowWidthDelta = - (corners.BottomRight.x - corners.BottomLeft.x) / GridColCount; - for (int i = 0; i < xVerticesCount; i++) { - topRowX[i] = corners.TopLeft.x + topRowWidthDelta * i; - bottomRowX[i] = corners.BottomLeft.x + bottomRowWidthDelta * i; - } - std::array leftColY; - std::array rightColY; - const float leftColHeightDelta = - (corners.BottomLeft.y - corners.TopLeft.y) / GridRowCount; - const float rightColHeightDelta = - (corners.BottomRight.y - corners.TopRight.y) / GridRowCount; - for (int i = 0; i < yVerticesCount; i++) { - leftColY[i] = corners.TopLeft.y + leftColHeightDelta * i; - rightColY[i] = corners.TopRight.y + rightColHeightDelta * i; - } - - std::array xDeltas; - for (int col = 0; col < xDeltas.size(); col++) { - xDeltas[col] = (bottomRowX[col] - topRowX[col]) / GridRowCount; - } - - std::array yDeltas; - for (int row = 0; row < yDeltas.size(); row++) { - yDeltas[row] = (rightColY[row] - leftColY[row]) / GridColCount; - } - - std::array matrix; - for (int row = 0; row < yVerticesCount; row++) { - for (int col = 0; col < xVerticesCount; col++) { - glm::vec2 pos = {topRowX[col] + xDeltas[col] * row - offset.x, - leftColY[row] + yDeltas[row] * col - offset.y}; - matrix[get2DIndex(col, row)] = pos; - } - } - return matrix; -} - -void TransformImage(CornersQuad const& sprCorners, - CornersQuad const& destCorners, float angleX, float angleY, - float angleZ, float scale, glm::vec2 dispOffset, - SystemMenu::GridVertices& outSpriteVertices, - SystemMenu::GridVertices& outDisplayVertices) { - glm::mat4 modelMatrix{1.0f}; - - modelMatrix = glm::rotate(modelMatrix, angleZ, glm::vec3(0.0f, 0.0f, 1.0f)); - modelMatrix = glm::rotate(modelMatrix, angleX, glm::vec3(1.0f, 0.0f, 0.0f)); - modelMatrix = glm::rotate(modelMatrix, angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - - outSpriteVertices = GenerateMatrix(sprCorners, {0, 0}); - outDisplayVertices = GenerateMatrix(destCorners, dispOffset); - for (auto& dispVertex : outDisplayVertices) { - glm::vec4 destPos{dispVertex, 0, 1}; - destPos *= scale; - destPos = modelMatrix * destPos; - const float perspective = (destPos.z / 2000.0f) + 1.0f; - dispVertex = destPos; - dispVertex *= perspective; - dispVertex += dispOffset; - } -} - void SystemMenu::Render() { if (State != Hidden && !GetFlag(SF_TITLEMODE)) { if (MenuTransition.IsIn()) { @@ -234,66 +164,64 @@ void SystemMenu::Render() { // Renderer->DrawSprite(BackgroundFilter, RectF(0.0f, 0.0f, 1280.0f, // 720.0f), // glm::vec4(tint, alpha)); + float bgOffset = (ScrWork[SW_SYSSUBMENUCT] * 3000.0f * 0.03125f); - RectF bgSpriteBounds = SystemMenuBG.Bounds; - bgSpriteBounds.X += (BGPosition.x - 0.5f * bgOffset); - bgSpriteBounds.Y += BGPosition.y; + Sprite offsetSystemMenuBG(SystemMenuBG); + offsetSystemMenuBG.Bounds.Translate(BGPosition.x - 0.5f * bgOffset, + BGPosition.y); const float scale = (1000.0f - (ScrWork[SW_SYSMENUCT] * 400 / 32)) / 1000.0f; - const float angleX = glm::pi() * - (ScrWork[SW_SYSMENUCT] * AngleMultiplier.x) / - (2 << 14); - const float angleY = glm::pi() * - (ScrWork[SW_SYSMENUCT] * AngleMultiplier.y) / - (2 << 14); - const float angleZ = glm::pi() * - (ScrWork[SW_SYSMENUCT] * AngleMultiplier.z) / - (2 << 14); - - CornersQuad bgDisp = { - BGDispOffsetTopLeft, - BGDispOffsetBottomLeft, - BGDispOffsetTopRight, - BGDispOffsetBottomRight, + const glm::quat rotation = + ScrWorkAnglesToQuaternion((ScrWork[SW_SYSMENUCT] * AngleMultiplier.x), + (ScrWork[SW_SYSMENUCT] * AngleMultiplier.y), + (ScrWork[SW_SYSMENUCT] * AngleMultiplier.z)); + + const auto transformation = [rotation, scale](glm::vec2 vertex) { + glm::mat4 transformation = TransformationMatrix( + BGTranslationOffset, {scale, scale}, {BGTranslationOffset, 0.0f}, + rotation, -BGTranslationOffset); + + glm::vec4 transformedVertex = {vertex, 0.0f, 1.0f}; + transformedVertex = transformation * transformedVertex; + const float perspective = (transformedVertex.z / 2000.0f) + 1.0f; + transformedVertex *= perspective; + transformedVertex += glm::vec4(BGTranslationOffset, 0.0f, 0.0f); + + return glm::vec2(transformedVertex); }; - TransformImage(bgSpriteBounds, bgDisp, angleX, angleY, angleZ, scale, - BGTranslationOffset, SpriteGridVertices, - DisplayGridVertices); - Renderer->DrawVertices(SystemMenuBG.Sheet, SpriteGridVertices, - DisplayGridVertices, 21, 11); + const CornersQuad bgDisp = + CornersQuad{BGDispOffsetTopLeft, BGDispOffsetBottomLeft, + BGDispOffsetTopRight, BGDispOffsetBottomRight} + .Transform(transformation); + Renderer->DrawSprite(offsetSystemMenuBG, bgDisp, glm::mat4(1.0f), + std::array{glm::vec4(1.0f), glm::vec4(1.0f), + glm::vec4(1.0f), glm::vec4(1.0f)}, + false, false, true); if (!GetFlag(SF_SYSTEMMENUDIRECT)) { - CornersQuad frameDisp = { - glm::vec2{bgOffset, 0} + FrameOffsetTopLeft, - glm::vec2{bgOffset, 0} + FrameOffsetBottomLeft, - glm::vec2{bgOffset, 0} + FrameOffsetTopRight, - glm::vec2{bgOffset, 0} + FrameOffsetBottomRight, - }; - - TransformImage(SystemMenuFrame.Bounds, frameDisp, angleX, angleY, angleZ, - scale, BGTranslationOffset, SpriteGridVertices, - DisplayGridVertices); - Renderer->DrawVertices(SystemMenuFrame.Sheet, SpriteGridVertices, - DisplayGridVertices, 21, 11); - - CornersQuad screenCapDisp = { + const CornersQuad frameDisp = + CornersQuad{glm::vec2{bgOffset, 0} + FrameOffsetTopLeft, + glm::vec2{bgOffset, 0} + FrameOffsetBottomLeft, + glm::vec2{bgOffset, 0} + FrameOffsetTopRight, + glm::vec2{bgOffset, 0} + FrameOffsetBottomRight} + .Transform(transformation); + Renderer->DrawSprite(SystemMenuFrame, frameDisp); + + const CornersQuad screenCapDisp = CornersQuad{ glm::vec2{bgOffset + 0, 0}, glm::vec2{bgOffset + 0, Profile::DesignHeight}, glm::vec2{bgOffset + Profile::DesignWidth, 0}, - glm::vec2{bgOffset + Profile::DesignWidth, Profile::DesignHeight}, - }; - TransformImage(ScreenCap.Bounds, screenCapDisp, angleX, angleY, angleZ, - scale, BGTranslationOffset, SpriteGridVertices, - DisplayGridVertices); - Renderer->DrawVertices(ScreenCap.Sheet, SpriteGridVertices, - DisplayGridVertices, 21, 11, glm::vec4{1}, false, - true); + glm::vec2{bgOffset + Profile::DesignWidth, + Profile::DesignHeight}}.Transform(transformation); + Renderer->DrawSprite(ScreenCap, screenCapDisp, glm::vec4(1.0f), false, + true); } - Renderer->DrawSprite(SystemMenuMask, - {0, 0, Profile::DesignWidth, Profile::DesignHeight}, - glm::vec4{tint, alpha}); + Renderer->DrawSprite( + SystemMenuMask, + RectF{0, 0, Profile::DesignWidth, Profile::DesignHeight}, + glm::vec4{tint, alpha}); MainItems->Tint = glm::vec4(tint, glm::smoothstep(0.0f, 1.0f, ItemsFade.Progress)); diff --git a/src/games/cclcc/systemmenu.h b/src/games/cclcc/systemmenu.h index 91e71daa9..c318d6bdd 100644 --- a/src/games/cclcc/systemmenu.h +++ b/src/games/cclcc/systemmenu.h @@ -13,8 +13,9 @@ namespace CCLCC { class SystemMenu : public Menu { public: using GridVertices = - std::array; + std::array; SystemMenu(); @@ -35,8 +36,7 @@ class SystemMenu : public Menu { bool ItemsFadeComplete = false; glm::vec2 BGPosition{}; - GridVertices SpriteGridVertices; - GridVertices DisplayGridVertices; + GridVertices Vertices; }; } // namespace CCLCC diff --git a/src/games/cclcc/titlemenu.cpp b/src/games/cclcc/titlemenu.cpp index 3e655be9a..e1f09d73a 100644 --- a/src/games/cclcc/titlemenu.cpp +++ b/src/games/cclcc/titlemenu.cpp @@ -529,7 +529,7 @@ void TitleMenu::Render() { DrawSmoke(SmokeOpacityNormal); Renderer->DrawSprite(CopyrightTextSprite, glm::vec2(CopyrightTextX, CopyrightTextY)); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f - ScrWork[SW_TITLEDISPCT] / 60.0f)); @@ -560,7 +560,7 @@ void TitleMenu::Render() { MainItems->Render(); ContinueItems->Render(); ExtraItems->Render(); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), {0.0f, 0.0f, 0.0f, ScrWork[SW_TITLEDISPCT] / 32.0f}); } break; @@ -590,7 +590,7 @@ void TitleMenu::Render() { int maskAlpha = ScrWork[SW_TITLEMASKALPHA]; glm::vec4 col = ScrWorkGetColor(SW_TITLEMASKCOLOR); col.a = glm::min(maskAlpha / 255.0f, 1.0f); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), col); } } diff --git a/src/games/cclcc/yesnotrigger.cpp b/src/games/cclcc/yesnotrigger.cpp index cef20583a..980e85d2e 100644 --- a/src/games/cclcc/yesnotrigger.cpp +++ b/src/games/cclcc/yesnotrigger.cpp @@ -249,7 +249,7 @@ void YesNoTrigger::Render() { static_cast(BgSpritePos.y + bgYOffset), static_cast(bgWidth), static_cast(bgHeight)); - Renderer->DrawSprite(ActiveBackground, Rect(0, 0, 1920, 1080), bgtint, 0.0f); + Renderer->DrawSprite(ActiveBackground, Rect(0, 0, 1920, 1080), bgtint); if (DispSel) { float chipYesX = @@ -298,8 +298,10 @@ void YesNoTrigger::Render() { float noChipWidthX = 0.5f * BgSpriteScale * ActiveNoChip->Bounds.Width; float noChipWidthY = 0.5f * BgSpriteScale * ActiveNoChip->Bounds.Height; if (Selection != YesNoSelect::NONE) { - Renderer->DrawSprite(StarChip, starPos, chipTint, {1.0, 1.0}, - StarAngle / 65536.0f); + const CornersQuad dest = StarChip.ScaledBounds() + .RotateAroundCenter(StarAngle / 65536.0f) + .Translate(starPos); + Renderer->DrawSprite(StarChip, dest, chipTint); } Renderer->DrawSprite(*ActiveYesChip, Rect(chipYesX, chipYesY, yesChipWidthX, yesChipWidthY), @@ -309,7 +311,7 @@ void YesNoTrigger::Render() { chipTint); } glm::vec4 maskTint = glm::vec4(1.0f, 1.0f, 1.0f, 160 / 256.0f); - Renderer->DrawSprite(YesNoBgMask, Rect(0, 0, 1920, 1080), maskTint, 0.0f); + Renderer->DrawSprite(YesNoBgMask, Rect(0, 0, 1920, 1080), maskTint); } void YesNoTrigger::Start(int type, int bgBufId, int chipsBufId) { diff --git a/src/games/chlcc/albummenu.cpp b/src/games/chlcc/albummenu.cpp index fb59285cb..1e925f2c6 100644 --- a/src/games/chlcc/albummenu.cpp +++ b/src/games/chlcc/albummenu.cpp @@ -112,7 +112,7 @@ void AlbumMenu::Hide() { void AlbumMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -123,8 +123,11 @@ void AlbumMenu::Render() { if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(AlbumMenuTitle, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), AlbumMenuTitleAngle); + + const CornersQuad titleDest = AlbumMenuTitle.ScaledBounds() + .RotateAroundCenter(AlbumMenuTitleAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(AlbumMenuTitle, titleDest); } Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); diff --git a/src/games/chlcc/clearlistmenu.cpp b/src/games/chlcc/clearlistmenu.cpp index 755864815..1baaa23bc 100644 --- a/src/games/chlcc/clearlistmenu.cpp +++ b/src/games/chlcc/clearlistmenu.cpp @@ -64,7 +64,7 @@ void ClearListMenu::Hide() { void ClearListMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -75,8 +75,11 @@ void ClearListMenu::Render() { if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleText, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleText.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleText, titleDest); } Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); diff --git a/src/games/chlcc/delusiontrigger.cpp b/src/games/chlcc/delusiontrigger.cpp index 5ccfe5cff..7c870fe36 100644 --- a/src/games/chlcc/delusiontrigger.cpp +++ b/src/games/chlcc/delusiontrigger.cpp @@ -334,17 +334,19 @@ void DelusionTrigger::Render() { ScaledMask.Bounds.Y = BackgroundSpriteMask.Bounds.Y - deltaHeight / 2.0f; TriggerOnTint[3] = TriggerOnTintAlpha * backgroundAlpha / 65536.0f; - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), TriggerOnTint); + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), TriggerOnTint); ScreenMask.Bounds.X = UnderlayerXOffset / 1000.0f; Renderer->DrawSprite( ScreenMask, RectF(0.0f, 0.0f, 1280.0f, 720.0f), glm::vec4(1.0f, 1.0f, 1.0f, (backgroundAlpha * 160) / 65536.0)); - Renderer->DrawMaskedSpriteOverlay( - BackgroundSprite, ScaledMask, RectF(0.0f, 0.0f, 1280.0f, 720.0f), - glm::vec4{1.0f}, (backgroundAlpha * 160) >> 8, 20, true, - spinAngle * 2 * (float)M_PI / 65536.0f); + const RectF spriteDest = {0.0f, 0.0f, 1280.0f, 720.0f}; + const CornersQuad maskDest = + ScaledMask.Bounds.RotateAroundCenter(ScrWorkAngleToRad(spinAngle)); + Renderer->DrawMaskedSpriteOverlay(BackgroundSprite, ScaledMask, spriteDest, + maskDest, (backgroundAlpha * 160) >> 8, 20, + glm::mat4(1.0f), glm::vec4(1.0f), true); } } // namespace CHLCC diff --git a/src/games/chlcc/dialoguebox.cpp b/src/games/chlcc/dialoguebox.cpp index 3473e97c2..63334364e 100644 --- a/src/games/chlcc/dialoguebox.cpp +++ b/src/games/chlcc/dialoguebox.cpp @@ -93,7 +93,7 @@ void DialogueBox::Render(DialoguePageMode mode, bool hasName, float nameWidth, if (mode == DPM_NVL) { glm::vec4 nvlBoxTint(0.0f, 0.0f, 0.0f, opacity * NVLBoxMaxOpacity); - Renderer->DrawRect(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), + Renderer->DrawQuad(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), nvlBoxTint); } } diff --git a/src/games/chlcc/moviemenu.cpp b/src/games/chlcc/moviemenu.cpp index a9d2c0204..d1c9422f6 100644 --- a/src/games/chlcc/moviemenu.cpp +++ b/src/games/chlcc/moviemenu.cpp @@ -125,7 +125,7 @@ void MovieMenu::Hide() { void MovieMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -135,8 +135,12 @@ void MovieMenu::Render() { if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleText, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleText.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleText, titleDest); + Renderer->DrawSprite(MenuTitleText, LeftTitlePos); } diff --git a/src/games/chlcc/musicmenu.cpp b/src/games/chlcc/musicmenu.cpp index a1c42dea0..6b7e6c8ad 100644 --- a/src/games/chlcc/musicmenu.cpp +++ b/src/games/chlcc/musicmenu.cpp @@ -102,7 +102,7 @@ void MusicMenu::Hide() { void MusicMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -112,8 +112,12 @@ void MusicMenu::Render() { if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(SoundLibraryTitle, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), SoundLibraryTitleAngle); + + const CornersQuad titleDest = + SoundLibraryTitle.ScaledBounds() + .RotateAroundCenter(SoundLibraryTitleAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(SoundLibraryTitle, titleDest); } Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); diff --git a/src/games/chlcc/optionsmenu.cpp b/src/games/chlcc/optionsmenu.cpp index 46498178e..d629c2238 100644 --- a/src/games/chlcc/optionsmenu.cpp +++ b/src/games/chlcc/optionsmenu.cpp @@ -44,7 +44,7 @@ OptionsMenu::OptionsMenu() : UI::OptionsMenu() { void OptionsMenu::Render() { if (State != Hidden) { if (FadeAnimation.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -54,8 +54,11 @@ void OptionsMenu::Render() { } if (FadeAnimation.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleText, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleText.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleText, titleDest); } glm::vec3 tint = {1.0f, 1.0f, 1.0f}; diff --git a/src/games/chlcc/savemenu.cpp b/src/games/chlcc/savemenu.cpp index 30c535b0c..923c913cd 100644 --- a/src/games/chlcc/savemenu.cpp +++ b/src/games/chlcc/savemenu.cpp @@ -357,7 +357,7 @@ void SaveMenu::Update(float dt) { void SaveMenu::Render() { if (State != Hidden && ScrWork[SW_FILEALPHA] != 0) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -367,8 +367,11 @@ void SaveMenu::Render() { if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleTextSprite, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleTextSprite.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleTextSprite, titleDest); } Renderer->CaptureScreencap(ShaderScreencapture.BgSprite); diff --git a/src/games/chlcc/systemmenu.cpp b/src/games/chlcc/systemmenu.cpp index 149a31ae4..b11305cdb 100644 --- a/src/games/chlcc/systemmenu.cpp +++ b/src/games/chlcc/systemmenu.cpp @@ -118,7 +118,7 @@ void SystemMenu::Update(float dt) { void SystemMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); diff --git a/src/games/chlcc/tipsmenu.cpp b/src/games/chlcc/tipsmenu.cpp index c4b17e6a7..ff880ad74 100644 --- a/src/games/chlcc/tipsmenu.cpp +++ b/src/games/chlcc/tipsmenu.cpp @@ -70,7 +70,7 @@ void TipsMenu::Hide() { void TipsMenu::Render() { if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -80,8 +80,11 @@ void TipsMenu::Render() { } if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleText, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleText.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleText, titleDest); } glm::vec3 tint = {1.0f, 1.0f, 1.0f}; @@ -214,12 +217,12 @@ inline void TipsMenu::DrawTipsTree(float yOffset) { currentTipBackgroundPosition); glm::vec2 gradientPosition(GradientPosition.x, GradientPosition.y + yOffset); Renderer->DrawSprite(TipsGradient, gradientPosition); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(GradientPosition.x, TipsGradient.Bounds.Height + yOffset, TipsGradient.Bounds.Width - 10.0f, 720.0f - TipsGradient.Bounds.Height - 86.0f), RgbIntToFloat(EndOfGradientColor)); - Renderer->DrawRect(RectF(GradientPosition.x, 634.0f + yOffset, + Renderer->DrawQuad(RectF(GradientPosition.x, 634.0f + yOffset, TipsGradient.Bounds.Width, 86.0f), RgbIntToFloat(EndOfGradientColor)); glm::vec2 treePosition(TreePosition.x, TreePosition.y + yOffset); diff --git a/src/games/chlcc/titlemenu.cpp b/src/games/chlcc/titlemenu.cpp index 76473cda7..d22404f8a 100644 --- a/src/games/chlcc/titlemenu.cpp +++ b/src/games/chlcc/titlemenu.cpp @@ -439,17 +439,22 @@ void TitleMenu::Render() { int maskAlpha = ScrWork[SW_TITLEMASKALPHA]; glm::vec4 col = ScrWorkGetColor(SW_TITLEMASKCOLOR); col.a = glm::min(maskAlpha / 255.0f, 1.0f); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), col); } } inline void TitleMenu::DrawTitleMenuBackGraphics() { Renderer->DrawSprite(BackgroundSprite, glm::vec2(0.0f)); - Renderer->DrawSprite(SpinningCircleSprite, - glm::vec2(SpinningCircleX, SpinningCircleY), - glm::vec4(1.0f), glm::vec2(2.0f), - -SpinningCircleAnimation.Progress * 2.0f * (float)M_PI); + + const CornersQuad spinningCircleDest = + SpinningCircleSprite.ScaledBounds() + .Scale({2.0f, 2.0f}, {0.0f, 0.0f}) + .RotateAroundCenter(-SpinningCircleAnimation.Progress * 2.0f * + (float)M_PI) + .Translate({SpinningCircleX, SpinningCircleY}); + Renderer->DrawSprite(SpinningCircleSprite, spinningCircleDest); + Renderer->DrawSprite(DelusionADVUnderSprite, glm::vec2(DelusionADVUnderX, DelusionADVUnderY)); Renderer->DrawSprite(DelusionADVSprite, diff --git a/src/games/chlcc/trophymenu.cpp b/src/games/chlcc/trophymenu.cpp index cb055f54c..6d58cf7fc 100644 --- a/src/games/chlcc/trophymenu.cpp +++ b/src/games/chlcc/trophymenu.cpp @@ -92,7 +92,7 @@ void TrophyMenu::Render() { if (State == Hidden) return; if (State != Hidden) { if (MenuTransition.IsIn()) { - Renderer->DrawRect(RectF(0.0f, 0.0f, 1280.0f, 720.0f), + Renderer->DrawQuad(RectF(0.0f, 0.0f, 1280.0f, 720.0f), RgbIntToFloat(BackgroundColor)); } else { DrawCircles(); @@ -102,8 +102,12 @@ void TrophyMenu::Render() { } if (MenuTransition.Progress > 0.34f) { Renderer->DrawSprite(RedBarLabel, RedTitleLabelPos); - Renderer->DrawSprite(MenuTitleText, RightTitlePos, glm::vec4(1.0f), - glm::vec2(1.0f), MenuTitleTextAngle); + + const CornersQuad titleDest = MenuTitleText.ScaledBounds() + .RotateAroundCenter(MenuTitleTextAngle) + .Translate(RightTitlePos); + Renderer->DrawSprite(MenuTitleText, titleDest); + Renderer->DrawSprite(MenuTitleText, LeftTitlePos); } diff --git a/src/games/mo6tw/dialoguebox.cpp b/src/games/mo6tw/dialoguebox.cpp index 967577bfc..cd6327deb 100644 --- a/src/games/mo6tw/dialoguebox.cpp +++ b/src/games/mo6tw/dialoguebox.cpp @@ -25,7 +25,7 @@ void DialogueBox::Render(DialoguePageMode mode, bool hasName, float nameWidth, Renderer->DrawSprite(ADVBoxDecoration, ADVBoxDecorationPos, col); } else { glm::vec4 nvlBoxTint(0.0f, 0.0f, 0.0f, opacity * NVLBoxMaxOpacity); - Renderer->DrawRect(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), + Renderer->DrawQuad(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), nvlBoxTint); } diff --git a/src/games/mo6tw/titlemenu.cpp b/src/games/mo6tw/titlemenu.cpp index 54674e045..48b0e44df 100644 --- a/src/games/mo6tw/titlemenu.cpp +++ b/src/games/mo6tw/titlemenu.cpp @@ -368,14 +368,14 @@ void TitleMenu::Render() { BackgroundSprite, BackgroundSprite, RectF(0.0f, 0.0f, BackgroundSprite.ScaledWidth(), BackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), (ScrWork[SW_TITLECT] * 287) / 48, 32, false, - true); + (ScrWork[SW_TITLECT] * 287) / 48, 32, glm::mat4(1.0f), + glm::vec4(1.0f), false, true); if (ScrWork[SW_TITLECT] > 48) { Renderer->DrawMaskedSprite( LogoSprite, Masks2D[17].MaskSprite, RectF(LogoX, LogoY, LogoSprite.ScaledWidth(), LogoSprite.ScaledHeight()), - glm::vec4(1.0f), (ScrWork[SW_TITLECT] * 271 - 13008) / 32, 16); + (ScrWork[SW_TITLECT] * 271 - 13008) / 32, 16); } break; case 1: { // Press to start @@ -383,7 +383,7 @@ void TitleMenu::Render() { BackgroundSprite, BackgroundSprite, RectF(0.0f, 0.0f, BackgroundSprite.ScaledWidth(), BackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); Renderer->DrawSprite(LogoSprite, glm::vec2(LogoX, LogoY)); Renderer->DrawSprite(CopyrightSprite, glm::vec2(CopyrightX, CopyrightY)); @@ -397,7 +397,7 @@ void TitleMenu::Render() { BackgroundSprite, BackgroundSprite, RectF(0.0f, 0.0f, BackgroundSprite.ScaledWidth(), BackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); Renderer->DrawSprite(LogoSprite, glm::vec2(LogoX, LogoY)); Renderer->DrawSprite(CopyrightSprite, glm::vec2(CopyrightX, CopyrightY)); @@ -408,7 +408,7 @@ void TitleMenu::Render() { MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); MainItems->Render(); } break; case 5: // Secondary menu Extra story Fade In @@ -417,7 +417,7 @@ void TitleMenu::Render() { MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); ExtraStoryItems->Render(); MainItems->Render(); } break; @@ -427,7 +427,7 @@ void TitleMenu::Render() { MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); ContinueItems->Render(); MainItems->Render(); } break; @@ -437,7 +437,7 @@ void TitleMenu::Render() { MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); MemoriesItems->Render(); MainItems->Render(); } break; @@ -447,7 +447,7 @@ void TitleMenu::Render() { MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); SystemItems->Render(); MainItems->Render(); } break; @@ -457,20 +457,20 @@ void TitleMenu::Render() { BackgroundSprite, BackgroundSprite, RectF(0.0f, 0.0f, BackgroundSprite.ScaledWidth(), BackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 32, false, true); + 287, 32, glm::mat4(1.0f), glm::vec4(1.0f), false, true); Renderer->DrawMaskedSprite( LogoSprite, Masks2D[17].MaskSprite, RectF(LogoX, LogoY, LogoSprite.ScaledWidth(), LogoSprite.ScaledHeight()), - glm::vec4(1.0f), 287, 16); + 287, 16); Renderer->DrawSprite(CopyrightSprite, glm::vec2(CopyrightX, CopyrightY)); Renderer->DrawMaskedSprite( MenuBackgroundSprite, MenuBackgroundSprite, RectF(0.0f, 0.0f, MenuBackgroundSprite.ScaledWidth(), MenuBackgroundSprite.ScaledHeight()), - glm::vec4(1.0f), (ScrWork[SW_TITLECT] * 287) / 48, 32, false, - true); + (ScrWork[SW_TITLECT] * 287) / 48, 32, glm::mat4(1.0f), + glm::vec4(1.0f), false, true); } break; } } @@ -478,7 +478,7 @@ void TitleMenu::Render() { int maskAlpha = ScrWork[SW_TITLEMASKALPHA]; glm::vec4 col = ScrWorkGetColor(SW_TITLEMASKCOLOR); col.a = glm::min(maskAlpha / 255.0f, 1.0f); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), col); } } diff --git a/src/games/mo8/titlemenu.cpp b/src/games/mo8/titlemenu.cpp index c3cc3417b..2eeacf561 100644 --- a/src/games/mo8/titlemenu.cpp +++ b/src/games/mo8/titlemenu.cpp @@ -284,7 +284,7 @@ void TitleMenu::Render() { glm::vec2(PressToStartX, PressToStartY), col); glm::vec4 black = glm::vec4(0.0f); black.a = glm::smoothstep(0.0f, 1.0f, PrimaryFadeAnimation.Progress); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), black); } break; @@ -314,7 +314,7 @@ void TitleMenu::Render() { GalleryItems->Render(); glm::vec4 black = glm::vec4(0.0f); black.a = glm::smoothstep(0.0f, 1.0f, PrimaryFadeAnimation.Progress); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), black); } break; diff --git a/src/games/rne/sysmesbox.cpp b/src/games/rne/sysmesbox.cpp index 19a1c7cc3..850dc1359 100644 --- a/src/games/rne/sysmesbox.cpp +++ b/src/games/rne/sysmesbox.cpp @@ -221,7 +221,7 @@ void SysMesBox::Render() { glm::vec4 col(1.0f, 1.0f, 1.0f, 1.0f); if (BoxAnimCount > BoxDisplayStartCount) { - Renderer->DrawRect( + Renderer->DrawQuad( RectF(BoxDisplayX, BoxTopY - 1.0f, BoxWidth, BoxHeight + 2.0f), glm::vec4(1.0f, 1.0f, 1.0f, 0.75f)); Renderer->DrawSprite(BoxDecorationTop, diff --git a/src/games/rne/tilebackground.cpp b/src/games/rne/tilebackground.cpp index 1eb1c2b32..5421e9530 100644 --- a/src/games/rne/tilebackground.cpp +++ b/src/games/rne/tilebackground.cpp @@ -63,12 +63,11 @@ void TileBackground::Render() { tileSprite.Bounds.X = (float)x * tileSprite.Bounds.Width; tileSprite.Bounds.Y = (float)y * tileSprite.Bounds.Height; - Renderer->DrawSprite3DRotated( - tileSprite, - RectF((float)x * ColumnWidth, (float)y * RowHeight, ColumnWidth, - RowHeight), - 2.0f, vanishingPoint, false, quat, - glm::vec4(1.0f, 1.0f, 1.0f, smoothedTileProgress)); + CornersQuad dest = RectF((float)x * ColumnWidth, (float)y * RowHeight, + ColumnWidth, RowHeight); + dest.Rotate(quat, {dest.Center(), 0.0f}, 2.0f, vanishingPoint); + Renderer->DrawSprite(tileSprite, dest, + {1.0f, 1.0f, 1.0f, smoothedTileProgress}); } } } diff --git a/src/games/rne/titlemenu.cpp b/src/games/rne/titlemenu.cpp index 493e52256..c25d14a01 100644 --- a/src/games/rne/titlemenu.cpp +++ b/src/games/rne/titlemenu.cpp @@ -91,13 +91,8 @@ void TitleMenu::Render() { glm::vec4(1.0f)); Renderer->DrawSprite(EliteSprite, glm::vec2(EliteX, EliteY), glm::vec4(1.0f)); - if (ScrWork[SW_TITLECGNO] == 542) { - Renderer->DrawSprite(LogoSprite, glm::vec2(LogoX, LogoY), - glm::vec4(1.0f), glm::vec2(1.0f), 0.0f, true); - } else { - Renderer->DrawSprite(LogoSprite, glm::vec2(LogoX, LogoY), - glm::vec4(1.0f)); - } + Renderer->DrawSprite(LogoSprite, {LogoX, LogoY}, glm::vec4(1.0f), + ScrWork[SW_TITLECGNO] == 542); } } } diff --git a/src/hud/autoicondisplay.cpp b/src/hud/autoicondisplay.cpp index a0dfa6fdd..4938d68d8 100644 --- a/src/hud/autoicondisplay.cpp +++ b/src/hud/autoicondisplay.cpp @@ -86,12 +86,13 @@ void Render(glm::vec4 opacityTint) { case AutoIconType::CHLCC: if (MesSkipMode & SkipModeFlags::Auto) { - Renderer->DrawSprite( - AutoSkipArrowsSprite, glm::vec2(AutoIconOffset.x, AutoIconOffset.y), - opacityTint, glm::vec2(1.0f), Progress * 2 * (float)M_PI); - Renderer->DrawSprite(AutoIconSprite, - glm::vec2(AutoIconOffset.x, AutoIconOffset.y), - opacityTint); + const CornersQuad arrowsDest = + AutoSkipArrowsSprite.ScaledBounds() + .RotateAroundCenter(Progress * 2.0f * (float)M_PI) + .Translate(AutoIconOffset); + Renderer->DrawSprite(AutoSkipArrowsSprite, arrowsDest, opacityTint); + + Renderer->DrawSprite(AutoIconSprite, AutoIconOffset, opacityTint); } break; } diff --git a/src/hud/dialoguebox.cpp b/src/hud/dialoguebox.cpp index f139c46a0..e1460a657 100644 --- a/src/hud/dialoguebox.cpp +++ b/src/hud/dialoguebox.cpp @@ -59,7 +59,7 @@ void DialogueBox::Render(DialoguePageMode mode, bool hasName, float nameWidth, RenderControlButtons(col); } else { glm::vec4 nvlBoxTint(0.0f, 0.0f, 0.0f, opacity * NVLBoxMaxOpacity); - Renderer->DrawRect(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), + Renderer->DrawQuad(RectF(0, 0, Profile::DesignWidth, Profile::DesignHeight), nvlBoxTint); } diff --git a/src/hud/skipicondisplay.cpp b/src/hud/skipicondisplay.cpp index 7992bf271..5c79628de 100644 --- a/src/hud/skipicondisplay.cpp +++ b/src/hud/skipicondisplay.cpp @@ -88,9 +88,12 @@ void Render(glm::vec4 opacityTint) { case SkipIconType::CHLCC: if (MesSkipMode & (SkipModeFlags::SkipRead | SkipModeFlags::SkipAll)) { - Renderer->DrawSprite( - AutoSkipArrowsSprite, glm::vec2(SkipIconOffset.x, SkipIconOffset.y), - opacityTint, glm::vec2(1.0f), Progress * 2 * (float)M_PI); + const CornersQuad arrowsDest = + AutoSkipArrowsSprite.ScaledBounds() + .RotateAroundCenter(Progress * 2.0f * (float)M_PI) + .Translate(SkipIconOffset); + Renderer->DrawSprite(AutoSkipArrowsSprite, arrowsDest, opacityTint); + Renderer->DrawSprite(SkipIconSprite, glm::vec2(SkipIconOffset.x, SkipIconOffset.y), opacityTint); diff --git a/src/hud/waiticondisplay.cpp b/src/hud/waiticondisplay.cpp index ef21793cd..8ac810b19 100644 --- a/src/hud/waiticondisplay.cpp +++ b/src/hud/waiticondisplay.cpp @@ -109,14 +109,12 @@ static void RenderRotateZ(glm::vec2 pos, glm::vec4 opacityTint) { glm::quat quat; eulerZYXToQuat(&euler, &quat); - glm::vec2 vanishingPoint( - (pos.x + WaitIconOffset.x) + (WaitIconSprite.ScaledWidth() / 2.0f), - (pos.y + WaitIconOffset.y) + (WaitIconSprite.ScaledHeight() / 2.0f)); - - Renderer->DrawSprite3DRotated( - WaitIconSprite, - glm::vec2(pos.x + WaitIconOffset.x, pos.y + WaitIconOffset.y), 1.0f, - vanishingPoint, true, quat, opacityTint); + glm::vec2 vanishingPoint = pos + WaitIconOffset + WaitIconSprite.Center(); + + CornersQuad dest = + WaitIconSprite.ScaledBounds().Translate(pos + WaitIconOffset); + dest.Rotate(quat, {dest.Center(), 0.0f}, 1.0f, vanishingPoint, true); + Renderer->DrawSprite(WaitIconSprite, dest, opacityTint); } static void RenderFixed(glm::vec4 opacityTint) { @@ -146,15 +144,17 @@ void Render(glm::vec2 pos, glm::vec4 opacityTint, DialoguePageMode mode) { RenderFixed(opacityTint); return; - default: + default: { if (!GetFlag(Profile::ScriptVars::SF_SHOWWAITICON)) return; - Renderer->DrawSprite( - WaitIconSprite, - glm::vec2(pos.x + WaitIconOffset.x, pos.y + WaitIconOffset.y), - opacityTint, glm::vec2(1.0f), - SimpleAnim.Progress * 2.0f * (float)M_PI); + const CornersQuad dest = + WaitIconSprite.ScaledBounds() + .RotateAroundCenter(SimpleAnim.Progress * 2.0f * (float)M_PI) + .Translate(pos + WaitIconOffset); + Renderer->DrawSprite(WaitIconSprite, dest, opacityTint); + return; + } } } diff --git a/src/renderer/dx9/renderer.cpp b/src/renderer/dx9/renderer.cpp index d9f7f0d9b..813a00b51 100644 --- a/src/renderer/dx9/renderer.cpp +++ b/src/renderer/dx9/renderer.cpp @@ -263,119 +263,11 @@ YUVFrame* Renderer::CreateYUVFrame(float width, float height) { return (YUVFrame*)VideoFrameInternal; } -void Renderer::DrawRect(RectF const& dest, glm::vec4 color, float angle) { - BaseRenderer::DrawSprite(RectSprite, dest, color, angle); -} - -void Renderer::DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - glm::vec4 tint, bool inverted) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite3DRotated() called before BeginFrame()\n"); - return; - } - - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - // Are we in sprite mode? - if (inverted) - EnsureShader(ShaderSpriteInverted); - else - EnsureShader(ShaderSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition3DRotated(dest, depth, vanishingPoint, stayInScreen, rot, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) { - DrawSprite3DRotated(RectSprite, dest, depth, vanishingPoint, stayInScreen, - rot, color); -} - -void Renderer::DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, - int indicesCount, uint16_t* mvlIndices, - bool inverted, glm::vec4 tint, - glm::vec2 scale) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawCharacterMvl() called before BeginFrame()\n"); - return; - } - - // Draw just the character with this since we need to rebind the index buffer - // anyway... - Flush(); - - // Are we in sprite mode? - if (inverted) - EnsureShader(ShaderSpriteInverted); - else - EnsureShader(ShaderSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); - - IndexBufferFill += indicesCount; - - memcpy(IndexBuffer, mvlIndices, indicesCount * sizeof(mvlIndices[0])); - - for (int i = 0; i < verticesCount; i++) { - glm::vec2 pos = glm::vec2(mvlVertices[i * 5], mvlVertices[i * 5 + 1]); - pos *= scale; - pos += topLeft; - vertices[i].Position = DesignToNDC(pos); - vertices[i].UV = glm::vec2(mvlVertices[i * 5 + 3], mvlVertices[i * 5 + 4]); - vertices[i].Tint = tint; - } - - Flush(); - - // ReFill index buffer with quads - int index = 0; - int vertex = 0; - while (index + 6 <= IndexBufferCount) { - // bottom-left -> top-left -> top-right - IndexBuffer[index] = vertex + 0; - IndexBuffer[index + 1] = vertex + 1; - IndexBuffer[index + 2] = vertex + 2; - // bottom-left -> top-right -> bottom-right - IndexBuffer[index + 3] = vertex + 0; - IndexBuffer[index + 4] = vertex + 2; - IndexBuffer[index + 5] = vertex + 3; - index += 6; - vertex += 4; - } -} - -void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, - const std::array& tints, float angle, - bool inverted) { +void Renderer::DrawSprite(const Sprite& sprite, const CornersQuad& dest, + const glm::mat4 transformation, + const std::span tints, + const bool inverted, const bool disableBlend, + const bool textureWrapRepeat) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawSprite() called before BeginFrame()\n"); @@ -402,83 +294,48 @@ void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, IndexBufferFill += 6; - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetPosition(CornersQuad(dest).Transform(transformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, glm::vec4 tint, - glm::vec2 scale, float angle, bool inverted) { +void Renderer::DrawMaskedSprite( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + const glm::mat4 spriteTransformation, const glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool isSameTexture) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite() called before BeginFrame()\n"); - return; - } - - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - // Are we in sprite mode? - if (inverted) - EnsureShader(ShaderSpriteInverted); - else - EnsureShader(ShaderSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - - QuadSetPositionOffset(sprite.Bounds, topLeft, displayOffset, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, - bool isSameTexture) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); + "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); return; } - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; + if (sprite.Sheet.IsScreenCap) Flush(); - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; + alpha = std::clamp(alpha, 0, fadeRange + 256); + const float alphaRange = 256.0f / fadeRange; + const float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; // Do we have space for one more sprite quad? EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); + std::array alphaRes = {alphaRange, constAlpha, 0.0f, 0.0f}; + const BOOL isInvertedB = (BOOL)isInverted; + const BOOL isSameTextureB = (BOOL)isSameTexture; + Flush(); - EnsureShader(ShaderMaskedSprite); + EnsureShader(ShaderMaskedSprite); Device->SetTexture(0, Textures[sprite.Sheet.Texture]); Device->SetTexture(1, Textures[mask.Sheet.Texture]); - // This is cursed man, idk - float alphaRes[] = {alphaRange, constAlpha}; - BOOL isInvertedB = (BOOL)isInverted; - BOOL isSameTextureB = (BOOL)isSameTexture; - Device->SetPixelShaderConstantF(0, alphaRes, 1); + Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + Device->SetPixelShaderConstantF(0, alphaRes.data(), 1); Device->SetPixelShaderConstantB(0, &isInvertedB, 1); Device->SetPixelShaderConstantB(1, &isSameTextureB, 1); @@ -490,31 +347,32 @@ void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, IndexBufferFill += 6; - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(CornersQuad(maskDest).Transform(maskTransformation), + sprite.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(CornersQuad(spriteDest).Transform(spriteTransformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - const glm::vec2 pos, - const glm::vec2 origin, int alpha, - const int fadeRange, const glm::vec4 tint, - const glm::vec2 scale, const float angle, - const bool spriteInverted, - const bool maskInverted, - const bool isSameTexture) { +void Renderer::DrawMaskedSpriteOverlay( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + const glm::mat4 spriteTransformation, const glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool useMaskAlpha) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); + "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); return; } + if (sprite.Sheet.IsScreenCap) Flush(); + if (alpha < 0) alpha = 0; if (alpha > fadeRange + 256) alpha = fadeRange + 256; @@ -524,20 +382,25 @@ void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, // Do we have space for one more sprite quad? EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - Flush(); - // TODO: handle inverted sprites - EnsureShader(ShaderMaskedSprite); + std::array alphaRes = {alphaRange, constAlpha, 0.0f, 0.0f}; + BOOL isInvertedB = (BOOL)isInverted; + BOOL isSameTextureB = (BOOL) false; + BOOL useMaskAlphaB = (BOOL)useMaskAlpha; + + if (useMaskAlpha) { + EnsureShader(ShaderMaskedSprite); + Device->SetPixelShaderConstantB(1, &isSameTextureB, 1); + } else { + EnsureShader(ShaderMaskedSpriteNoAlpha); + } Device->SetTexture(0, Textures[sprite.Sheet.Texture]); Device->SetTexture(1, Textures[mask.Sheet.Texture]); - // This is cursed man, idk - float alphaRes[] = {alphaRange, constAlpha}; - BOOL isInvertedB = (BOOL)maskInverted; - BOOL isSameTextureB = (BOOL)isSameTexture; - Device->SetPixelShaderConstantF(0, alphaRes, 1); + Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + Device->SetPixelShaderConstantF(0, alphaRes.data(), 1); Device->SetPixelShaderConstantB(0, &isInvertedB, 1); - Device->SetPixelShaderConstantB(1, &isSameTextureB, 1); // OK, all good, make quad @@ -547,89 +410,76 @@ void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, IndexBufferFill += 6; - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(CornersQuad(maskDest).Transform(maskTransformation), + mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPositionOffset(sprite.Bounds, pos, origin, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - QuadSetPositionOffset({0.0f, 0.0f, 1.0f, 1.0f}, pos, origin, scale, angle, - (uintptr_t)&vertices[0].MaskUV, - sizeof(VertexBufferSprites)); + QuadSetPosition(CornersQuad(spriteDest).Transform(spriteTransformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, - int width, int height, glm::vec4 tint, - bool inverted, bool disableBlend) { +void Renderer::DrawVertices(const SpriteSheet& sheet, + const SpriteSheet* const mask, + const std::span vertices, + const std::span indices, + const glm::mat4 transformation, + const bool inverted) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite() called before BeginFrame()\n"); - return; - } - const int verticesCount = sheetPositions.size(); - if (verticesCount != displayPositions.size()) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawVertices() called with mismatched vertices count\n"); + "Renderer->DrawVertices() called before BeginFrame()\n"); return; } - EnsureSpaceAvailable(verticesCount, sizeof(VertexBufferSprites), - verticesCount * 3); - + // The index buffer needs to be flushed Flush(); - // Are we in sprite mode? - if (inverted) - EnsureShader(ShaderSpriteInverted); - else - EnsureShader(ShaderSprite); + if (mask != nullptr) { + std::array alphaRes = {1.0f, 0.0f, 0.0f, 0.0f}; + BOOL isInvertedB = (BOOL)inverted; - EnsureTextureBound(sheet.Texture); - Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); - Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); + EnsureShader(ShaderMaskedSpriteNoAlpha); - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); + Device->SetTexture(0, Textures[sheet.Texture]); + Device->SetTexture(1, Textures[mask->Texture]); - IndexBufferFill = 0; - // Generate indices for triangles - for (int y = 0; y < height - 1; y++) { - for (int x = 0; x < width - 1; x++) { - uint16_t v0 = y * width + x; - uint16_t v1 = y * width + (x + 1); - uint16_t v2 = (y + 1) * width + x; - uint16_t v3 = (y + 1) * width + (x + 1); - - // First triangle - for (auto v : {v1, v0, v2}) { - IndexBuffer[IndexBufferFill++] = v; - } - // Second triangle - for (auto v : {v3, v1, v2}) { - IndexBuffer[IndexBufferFill++] = v; - } - } - } - assert(IndexBufferFill == (height - 1) * (width - 1) * 6); - - for (int i = 0; i < verticesCount; i++) { - vertices[i].Position = DesignToNDC(displayPositions[i]); - vertices[i].Tint = tint; - glm::vec2 uv = - sheetPositions[i] / glm::vec2(sheet.DesignWidth, sheet.DesignHeight); - vertices[i].UV = uv; + Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + Device->SetPixelShaderConstantF(0, alphaRes.data(), 1); + Device->SetPixelShaderConstantB(0, &isInvertedB, 1); + + } else { + if (inverted) + EnsureShader(ShaderSpriteInverted); + else + EnsureShader(ShaderSprite); + + EnsureTextureBound(sheet.Texture); } + // Push vertices + VertexBufferSprites* vertexBuffer = + (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); + VertexBufferFill += vertices.size_bytes(); + + const auto vertexInfoToNDC = [this, + transformation](VertexBufferSprites info) { + info.Position = DesignToNDC( + glm::vec2(transformation * glm::vec4(info.Position, 0.0f, 1.0f))); + return info; + }; + std::transform(vertices.begin(), vertices.end(), vertexBuffer, + vertexInfoToNDC); + + // Push indices + IndexBufferFill += indices.size(); + std::copy(indices.begin(), indices.end(), IndexBuffer); + + // Flush again and bind back our buffer Flush(); - Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); // ReFill index buffer with quads int index = 0; @@ -684,74 +534,12 @@ void Renderer::DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, IndexBufferFill += 6; - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(mask.Bounds, mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, - int alpha, int fadeRange, - bool isInverted, float angle, - bool useMaskAlpha) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); - return; - } - - if (sprite.Sheet.IsScreenCap) Flush(); - - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - float alphaRes[] = {alphaRange, constAlpha}; - BOOL isInvertedB = (BOOL)isInverted; - BOOL isSameTextureB = (BOOL) false; - BOOL useMaskAlphaB = (BOOL)useMaskAlpha; - - if (useMaskAlpha) { - EnsureShader(ShaderMaskedSprite); - Device->SetPixelShaderConstantB(1, &isSameTextureB, 1); - } else { - EnsureShader(ShaderMaskedSpriteNoAlpha); - } - - Device->SetTexture(0, Textures[sprite.Sheet.Texture]); - Device->SetTexture(1, Textures[mask.Sheet.Texture]); - - Device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - Device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - Device->SetPixelShaderConstantF(0, alphaRes, 1); - Device->SetPixelShaderConstantB(0, &isInvertedB, 1); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites), angle); - - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tint; } @@ -790,223 +578,18 @@ void Renderer::DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, IndexBufferFill += 6; - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, + QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(mask.Bounds, mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); } -inline void Renderer::QuadSetUVFlipped(RectF const& spriteBounds, - float designWidth, float designHeight, - uintptr_t uvs, int stride) { - float topUV = (spriteBounds.Y / designHeight); - float leftUV = (spriteBounds.X / designWidth); - float bottomUV = ((spriteBounds.Y + spriteBounds.Height) / designHeight); - float rightUV = ((spriteBounds.X + spriteBounds.Width) / designWidth); - - // top-left - *(glm::vec2*)(uvs + 0 * stride) = glm::vec2(leftUV, topUV); - // bottom-left - *(glm::vec2*)(uvs + 1 * stride) = glm::vec2(leftUV, bottomUV); - // bottom-right - *(glm::vec2*)(uvs + 2 * stride) = glm::vec2(rightUV, bottomUV); - // top-right - *(glm::vec2*)(uvs + 3 * stride) = glm::vec2(rightUV, topUV); -} - -inline void Renderer::QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle) { - float topUV = (spriteBounds.Y / designHeight); - float leftUV = (spriteBounds.X / designWidth); - float bottomUV = ((spriteBounds.Y + spriteBounds.Height) / designHeight); - float rightUV = ((spriteBounds.X + spriteBounds.Width) / designWidth); - - glm::vec2 bottomLeft(leftUV, bottomUV); - glm::vec2 topLeft(leftUV, topUV); - glm::vec2 topRight(rightUV, topUV); - glm::vec2 bottomRight(rightUV, bottomUV); - - if (angle != 0.0f) { - glm::vec2 center = (bottomLeft + topRight) * 0.5f; // Center of the quad - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(uvs + 0 * stride) = bottomLeft; - // top-left - *(glm::vec2*)(uvs + 1 * stride) = topLeft; - // top-right - *(glm::vec2*)(uvs + 2 * stride) = topRight; - // bottom-right - *(glm::vec2*)(uvs + 3 * stride) = bottomRight; -} - -inline void Renderer::QuadSetPositionOffset( - RectF const& spriteBounds, glm::vec2 displayXY, glm::vec2 displayOffset, - glm::vec2 scale, float angle, uintptr_t positions, int stride, bool toNDC) { - glm::vec2 topLeft = {0.0f, 0.0f}; - glm::vec2 bottomLeft = {0.0f, spriteBounds.Height}; - glm::vec2 topRight = {spriteBounds.Width, 0.0f}; - glm::vec2 bottomRight = {spriteBounds.Width, spriteBounds.Height}; - - // Translate to origin - topLeft -= displayOffset; - bottomLeft -= displayOffset; - topRight -= displayOffset; - bottomRight -= displayOffset; - - // Scale - bottomLeft *= scale; - topLeft *= scale; - topRight *= scale; - bottomRight *= scale; - - // Rotate - if (angle != 0.0f) { - const glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * bottomLeft; - topLeft = rot * topLeft; - topRight = rot * topRight; - bottomRight = rot * bottomRight; - } - - // Translate to the desired screen position - bottomLeft += displayOffset + displayXY; - topLeft += displayOffset + displayXY; - topRight += displayOffset + displayXY; - bottomRight += displayOffset + displayXY; - - if (toNDC) { - bottomLeft = DesignToNDC(bottomLeft); - topLeft = DesignToNDC(topLeft); - topRight = DesignToNDC(topRight); - bottomRight = DesignToNDC(bottomRight); - } - - // Store the transformed positions - *(glm::vec2*)(positions + 0 * stride) = bottomLeft; - *(glm::vec2*)(positions + 1 * stride) = topLeft; - *(glm::vec2*)(positions + 2 * stride) = topRight; - *(glm::vec2*)(positions + 3 * stride) = bottomRight; -} - -inline void Renderer::QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride) { - glm::vec2 bottomLeft = - glm::vec2(transformedQuad.X, transformedQuad.Y + transformedQuad.Height); - glm::vec2 topLeft = glm::vec2(transformedQuad.X, transformedQuad.Y); - glm::vec2 topRight = - glm::vec2(transformedQuad.X + transformedQuad.Width, transformedQuad.Y); - glm::vec2 bottomRight = glm::vec2(transformedQuad.X + transformedQuad.Width, - transformedQuad.Y + transformedQuad.Height); - - if (angle != 0.0f) { - glm::vec2 center = transformedQuad.Center(); - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(positions + 0 * stride) = DesignToNDC(bottomLeft); - // top-left - *(glm::vec2*)(positions + 1 * stride) = DesignToNDC(topLeft); - // top-right - *(glm::vec2*)(positions + 2 * stride) = DesignToNDC(topRight); - // bottom-right - *(glm::vec2*)(positions + 3 * stride) = DesignToNDC(bottomRight); -} - -inline void Renderer::QuadSetPosition(CornersQuad destQuad, float angle, - uintptr_t positions, int stride) { - if (angle != 0.0f) { - glm::vec2 center = (destQuad.BottomLeft + destQuad.TopRight) * 0.5f; - glm::mat2 rot = Rotate2D(angle); - - destQuad.BottomLeft = rot * (destQuad.BottomLeft - center) + center; - destQuad.TopLeft = rot * (destQuad.TopLeft - center) + center; - destQuad.TopRight = rot * (destQuad.TopRight - center) + center; - destQuad.BottomRight = rot * (destQuad.BottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(positions + 0 * stride) = DesignToNDC(destQuad.BottomLeft); - // top-left - *(glm::vec2*)(positions + 1 * stride) = DesignToNDC(destQuad.TopLeft); - // top-right - *(glm::vec2*)(positions + 2 * stride) = DesignToNDC(destQuad.TopRight); - // bottom-right - *(glm::vec2*)(positions + 3 * stride) = DesignToNDC(destQuad.BottomRight); -} - -void Renderer::QuadSetPosition3DRotated(RectF const& transformedQuad, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - uintptr_t positions, int stride) { - float widthNormalized = transformedQuad.Width / (Profile::DesignWidth * 0.5f); - float heightNormalized = - transformedQuad.Height / (Profile::DesignHeight * 0.5f); - - glm::vec4 corners[4]{ - // bottom-left - {glm::vec2(-widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}, - // top-left - {glm::vec2(-widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // top-right - {glm::vec2(widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // bottom-right - {glm::vec2(widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}}; - - glm::mat4 transform = - glm::translate(glm::mat4(1.0f), - glm::vec3(DesignToNDC(transformedQuad.Center()), 0)) * - glm::mat4_cast(rot); - - glm::vec4 vanishingPointNDC(DesignToNDC(vanishingPoint), 0, 0); - - for (int i = 0; i < 4; i++) { - corners[i] = transform * corners[i]; - } - - if (stayInScreen) { - float maxZ = 0.0f; - for (int i = 0; i < 4; i++) { - if (corners[i].z > maxZ) maxZ = corners[i].z; - } - for (int i = 0; i < 4; i++) { - corners[i].z -= maxZ; - } - } - - for (int i = 0; i < 4; i++) { - // perspective - corners[i] -= vanishingPointNDC; - corners[i].x *= (depth / (depth - corners[i].z)); - corners[i].y *= (depth / (depth - corners[i].z)); - corners[i] += vanishingPointNDC; - - *(glm::vec2*)(positions + i * stride) = corners[i]; - } -} - void Renderer::EnsureSpaceAvailable(int vertices, int vertexSize, int indices) { if (VertexBufferFill + vertices * vertexSize > VertexBufferSize || IndexBufferFill + indices > IndexBufferCount) { @@ -1085,8 +668,8 @@ void Renderer::Flush() { CurrentTexture = -1; } -void Renderer::DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint, float angle, bool alphaVideo) { +void Renderer::DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + const glm::vec4 tint, const bool alphaVideo) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawVideoTexture() called before BeginFrame()\n"); @@ -1115,10 +698,10 @@ void Renderer::DrawVideoTexture(YUVFrame* tex, RectF const& dest, IndexBufferFill += 6; - QuadSetUV(RectF(0.0f, 0.0f, tex->Width, tex->Height), tex->Width, tex->Height, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetUV(RectF(0.0f, 0.0f, frame.Width, frame.Height), + {frame.Width, frame.Height}, &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tint; } @@ -1181,5 +764,12 @@ void Renderer::DisableScissor() { Device->SetRenderState(D3DRS_SCISSORTESTENABLE, false); } +glm::vec2 Renderer::DesignToNDC(glm::vec2 designCoord) const { + glm::vec2 result; + result.x = (designCoord.x / (Profile::DesignWidth * 0.5f)) - 1.0f; + result.y = 1.0f - (designCoord.y / (Profile::DesignHeight * 0.5f)); + return result; +} + } // namespace DirectX9 } // namespace Impacto diff --git a/src/renderer/dx9/renderer.h b/src/renderer/dx9/renderer.h index 9ca66074c..aec51b9bd 100644 --- a/src/renderer/dx9/renderer.h +++ b/src/renderer/dx9/renderer.h @@ -34,66 +34,41 @@ class Renderer : public BaseRenderer { void FreeTexture(uint32_t id) override; YUVFrame* CreateYUVFrame(float width, float height) override; - void DrawSprite(Sprite const& sprite, CornersQuad const& dest, - const std::array& tints, float angle = 0.0f, - bool inverted = false) override; - - void DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool inverted = false) override; - - void DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, int width, - int height, glm::vec4 tint = glm::vec4(1.0), - bool inverted = false, bool disableBlend = false) override; - - void DrawRect(RectF const& dest, glm::vec4 color, - float angle = 0.0f) override; - - void DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted = false, - bool isSameTexture = false) override; - - void DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - glm::vec2 pos, glm::vec2 origin, int alpha, - int fadeRange, glm::vec4 tint = glm::vec4(1.0f), - glm::vec2 scale = glm::vec2(1.0), - float angle = 0.0f, bool spriteInverted = false, - bool maskInverted = false, - bool isSameTexture = false) override; + void DrawSprite(const Sprite& sprite, const CornersQuad& dest, + glm::mat4 transformation, std::span tints, + bool inverted, bool disableBlend, + bool textureWrapRepeat) override; + + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, int fadeRange, + glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, bool isInverted, + bool isSameTexture); + + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, + bool isInverted, bool useMaskAlpha) override; + + void DrawVertices(const SpriteSheet& sheet, const SpriteSheet* mask, + std::span vertices, + std::span indices, glm::mat4 transformation, + bool inverted) override; void DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, RectF const& dest, glm::vec4 tint, int alpha, int fadeRange, float effectCt) override; - void DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, float angle, - bool useMaskAlpha) override; - void DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, const RectF& dest, float alpha) override; - void DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 tint = glm::vec4(1.0f), - bool inverted = false) override; - void DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) override; - - void DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, int indicesCount, - uint16_t* mvlIndices, bool inverted, glm::vec4 tint, - glm::vec2 scale) override; - - void DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint = glm::vec4(1.0), float angle = 0.0f, - bool alphaVideo = false) override; + void DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + glm::vec4 tint, bool alphaVideo) override; void CaptureScreencap(Sprite& sprite) override; @@ -110,24 +85,9 @@ class Renderer : public BaseRenderer { void EnsureSpaceAvailable(int vertices, int vertexSize, int indices); void EnsureTextureBound(uint32_t texture); void EnsureShader(Shader* shader, bool flush = true); - void Flush(); - - void QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle = 0.0f); - void QuadSetPositionOffset(RectF const& spriteBounds, glm::vec2 topLeftPos, - glm::vec2 displayOffset, glm::vec2 scale, - float angle, uintptr_t positions, int stride, - bool toNDC = true); - void QuadSetUVFlipped(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride); - void QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride); - void QuadSetPosition(CornersQuad destQuad, float angle, uintptr_t positions, - int stride); - void QuadSetPosition3DRotated(RectF const& transformedQuad, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, uintptr_t positions, int stride); + void Flush() override; + + glm::vec2 DesignToNDC(glm::vec2 designCoord) const override; DirectX9Window* DXWindow; @@ -162,8 +122,6 @@ class Renderer : public BaseRenderer { IDirect3DIndexBuffer9* IndexBufferDevice; IDirect3DIndexBuffer9* IndexBufferMvl; int IndexBufferFill = 0; - - Sprite RectSprite; }; } // namespace DirectX9 diff --git a/src/renderer/opengl/3d/renderable3d.cpp b/src/renderer/opengl/3d/renderable3d.cpp index 844acb9df..d86802588 100644 --- a/src/renderer/opengl/3d/renderable3d.cpp +++ b/src/renderer/opengl/3d/renderable3d.cpp @@ -61,7 +61,7 @@ static bool CurrentMaterialIsDepthWrite = false; static GLWindow* Window; -void Renderable3D::Init(GLWindow* window, ShaderCompiler* shaderCompiler) { +void Renderable3D::Init(GLWindow* window, ShaderCompiler& shaderCompiler) { assert(IsInit == false); ImpLog(LogLevel::Info, LogChannel::Renderable3D, "Initializing Renderable3D system\n"); @@ -75,13 +75,13 @@ void Renderable3D::Init(GLWindow* window, ShaderCompiler* shaderCompiler) { shaderParams["DASH"] = ShaderParameter(isDaSH, true); ShaderProgram = - shaderCompiler->Compile("Renderable3D_Character", shaderParams); + shaderCompiler.Compile("Renderable3D_Character", shaderParams); ShaderProgramOutline = - shaderCompiler->Compile("Renderable3D_Outline", shaderParams); - ShaderProgramEye = shaderCompiler->Compile("Renderable3D_Eye", shaderParams); + shaderCompiler.Compile("Renderable3D_Outline", shaderParams); + ShaderProgramEye = shaderCompiler.Compile("Renderable3D_Eye", shaderParams); ShaderProgramBackground = - shaderCompiler->Compile("Renderable3D_Background", shaderParams); + shaderCompiler.Compile("Renderable3D_Background", shaderParams); GLuint sceneUniformIndices[SU_Count]; glGetUniformIndices(ShaderProgram, SU_Count, SceneUniformNames, diff --git a/src/renderer/opengl/3d/renderable3d.h b/src/renderer/opengl/3d/renderable3d.h index 3a51f9a44..2fff6ff85 100644 --- a/src/renderer/opengl/3d/renderable3d.h +++ b/src/renderer/opengl/3d/renderable3d.h @@ -12,7 +12,7 @@ enum RenderPass { RP_Outline = 0, RP_First = 1, RP_Second = 2, RP_Count }; class Renderable3D : public IRenderable3D { public: - static void Init(GLWindow* window, ShaderCompiler* shaderCompiler); + static void Init(GLWindow* window, ShaderCompiler& shaderCompiler); static void BeginFrame(IScene3D* scene, Camera* camera); void MakePlane() override; diff --git a/src/renderer/opengl/3d/scene.cpp b/src/renderer/opengl/3d/scene.cpp index 67190dcd1..a8ecd42ca 100644 --- a/src/renderer/opengl/3d/scene.cpp +++ b/src/renderer/opengl/3d/scene.cpp @@ -11,10 +11,8 @@ namespace Impacto { namespace OpenGL { -Scene3D::Scene3D(GLWindow* window, ShaderCompiler* shaderCompiler) { - Window = window; - Shaders = shaderCompiler; -} +Scene3D::Scene3D(GLWindow* window, ShaderCompiler& shaderCompiler) + : Window(window), Shaders(shaderCompiler) {} void Scene3D::Init() { assert(IsInit == false); @@ -211,7 +209,7 @@ void Scene3D::SetupFramebufferState() { glm::vec2(viewport.Width, viewport.Height); shaderParams["RenderScale"] = Window->RenderScale; - ShaderProgram = Shaders->Compile("SceneToRT", shaderParams); + ShaderProgram = Shaders.Compile("SceneToRT", shaderParams); glUseProgram(ShaderProgram); glUniform1i(glGetUniformLocation(ShaderProgram, "Framebuffer3D"), 0); diff --git a/src/renderer/opengl/3d/scene.h b/src/renderer/opengl/3d/scene.h index d5fb2789a..cea8fe54e 100644 --- a/src/renderer/opengl/3d/scene.h +++ b/src/renderer/opengl/3d/scene.h @@ -24,7 +24,7 @@ enum MSResolveMode { class Scene3D : public IScene3D { public: - Scene3D(GLWindow* window, ShaderCompiler* shaderCompiler); + Scene3D(GLWindow* window, ShaderCompiler& shaderCompiler); void Init(); void Shutdown(); @@ -35,7 +35,7 @@ class Scene3D : public IScene3D { bool IsInit = false; GLWindow* Window; - ShaderCompiler* Shaders; + ShaderCompiler& Shaders; void SetupFramebufferState(); void CleanFramebufferState(); diff --git a/src/renderer/opengl/renderer.cpp b/src/renderer/opengl/renderer.cpp index 6f337489c..495ab6d23 100644 --- a/src/renderer/opengl/renderer.cpp +++ b/src/renderer/opengl/renderer.cpp @@ -1,6 +1,7 @@ #include "renderer.h" #include +#include #include "shader.h" #include "../../profile/game.h" #include "../../game.h" @@ -24,42 +25,16 @@ void Renderer::Init() { OpenGLWindow->Init(); Window = (BaseWindow*)OpenGLWindow; - Shaders = new ShaderCompiler(); - if (Profile::GameFeatures & GameFeature::Scene3D) { Scene = new Scene3D(OpenGLWindow, Shaders); Scene->Init(); } - // Fill index buffer with quads - int index = 0; - int vertex = 0; - while (index + 6 <= IndexBufferCount) { - // bottom-left -> top-left -> top-right - IndexBuffer[index] = vertex + 0; - IndexBuffer[index + 1] = vertex + 1; - IndexBuffer[index + 2] = vertex + 2; - // bottom-left -> top-right -> bottom-right - IndexBuffer[index + 3] = vertex + 0; - IndexBuffer[index + 4] = vertex + 2; - IndexBuffer[index + 5] = vertex + 3; - index += 6; - vertex += 4; - } - // Generate buffers glGenBuffers(1, &VBO); glGenBuffers(1, &IBO); glGenVertexArrays(1, &VAOSprites); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, VertexBufferSize, NULL, GL_STREAM_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - IndexBufferCount * sizeof(IndexBuffer[0]), IndexBuffer, - GL_STATIC_DRAW); - GLC::InitializeFramebuffers(); // Specify vertex layouts @@ -74,10 +49,7 @@ void Renderer::Init() { (void*)offsetof(VertexBufferSprites, Tint)); glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexBufferSprites), (void*)offsetof(VertexBufferSprites, MaskUV)); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); + for (size_t i = 0; i < 4; i++) glEnableVertexAttribArray(i); // Make 1x1 white pixel for colored rectangles Texture rectTexture; @@ -86,51 +58,32 @@ void Renderer::Init() { rectSheet.Texture = rectTexture.Submit(); RectSprite = Sprite(rectSheet, 0.0f, 0.0f, 1.0f, 1.0f); - // Set up sprite shader - ShaderProgramSprite = Shaders->Compile("Sprite"); - glUseProgram(ShaderProgramSprite); - glUniform1i(glGetUniformLocation(ShaderProgramSprite, "ColorMap"), 0); - ShaderProgramSpriteInverted = Shaders->Compile("Sprite_inverted"); - glUniform1i(glGetUniformLocation(ShaderProgramSpriteInverted, "ColorMap"), 0); - - ShaderProgramMaskedSprite = Shaders->Compile("MaskedSprite"); - glUseProgram(ShaderProgramMaskedSprite); - glUniform1i(glGetUniformLocation(ShaderProgramMaskedSprite, "ColorMap"), 0); - MaskedIsInvertedLocation = - glGetUniformLocation(ShaderProgramMaskedSprite, "IsInverted"); - MaskedIsSameTextureLocation = - glGetUniformLocation(ShaderProgramMaskedSprite, "IsSameTexture"); - - ShaderProgramMaskedSpriteNoAlpha = Shaders->Compile("MaskedSpriteNoAlpha"); - glUseProgram(ShaderProgramMaskedSpriteNoAlpha); - glUniform1i( - glGetUniformLocation(ShaderProgramMaskedSpriteNoAlpha, "ColorMap"), 0); - MaskedNoAlphaIsInvertedLocation = - glGetUniformLocation(ShaderProgramMaskedSpriteNoAlpha, "IsInverted"); - - ShaderProgramYUVFrame = Shaders->Compile("YUVFrame"); - glUseProgram(ShaderProgramYUVFrame); - glUniform1i(glGetUniformLocation(ShaderProgramYUVFrame, "Luma"), 0); - YUVFrameCbLocation = glGetUniformLocation(ShaderProgramYUVFrame, "Cb"); - YUVFrameCrLocation = glGetUniformLocation(ShaderProgramYUVFrame, "Cr"); - YUVFrameIsAlphaLocation = - glGetUniformLocation(ShaderProgramYUVFrame, "IsAlpha"); - - ShaderProgramCCMessageBox = Shaders->Compile("CCMessageBoxSprite"); - glUseProgram(ShaderProgramCCMessageBox); - glUniform1i(glGetUniformLocation(ShaderProgramCCMessageBox, "ColorMap"), 0); - ShaderProgramCHLCCMenuBackground = Shaders->Compile("CHLCCMenuBackground"); - - // No-mipmapping sampler - glGenSamplers(1, &Sampler); - - // Don't wrap textures - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glSamplerParameteri(Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(Sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); + // Set up shaders + SpriteShaderProgram.emplace(Shaders.Compile("Sprite")); + SpriteInvertedShaderProgram.emplace(Shaders.Compile("SpriteInverted")); + MaskedSpriteShaderProgram.emplace(Shaders.Compile("MaskedSprite")); + MaskedSpriteNoAlphaShaderProgram.emplace( + Shaders.Compile("MaskedSpriteNoAlpha")); + YUVFrameShaderProgram.emplace(Shaders.Compile("YUVFrame")); + CCMessageBoxShaderProgram.emplace(Shaders.Compile("CCMessageBoxSprite")); + CHLCCMenuBackgroundShaderProgram.emplace( + Shaders.Compile("CHLCCMenuBackground")); + + glGenSamplers(Samplers.size(), Samplers.data()); + for (size_t i = 0; i < TextureUnitCount; i++) { + glBindSampler(i, Samplers[i]); + + glSamplerParameteri(Samplers[i], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(Samplers[i], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glSamplerParameteri(Samplers[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(Samplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glSamplerParameteri(Samplers[i], GL_TEXTURE_MAX_ANISOTROPY, 16); + } + + glActiveTexture(GL_TEXTURE0); + glBindSampler(0, Samplers[0]); } void Renderer::Shutdown() { @@ -145,6 +98,8 @@ void Renderer::Shutdown() { glDeleteTextures(GLC::FramebufferTextures.size(), GLC::FramebufferTextures.data()); + glDeleteSamplers(Samplers.size(), Samplers.data()); + if (Profile::GameFeatures & GameFeature::Scene3D) { Scene->Shutdown(); } @@ -168,15 +123,14 @@ void Renderer::BeginFrame2D() { } Drawing = true; - CurrentTexture = 0; - CurrentMode = R2D_None; - VertexBufferFill = 0; - IndexBufferFill = 0; + + VertexBuffer.clear(); + IndexBuffer.clear(); + NextFreeIndex = 0; glDisable(GL_CULL_FACE); - // TODO should we really be making this global? - glBindSampler(0, Sampler); + glBindSampler(0, Samplers[0]); } void Renderer::EndFrame() { @@ -264,489 +218,318 @@ YUVFrame* Renderer::CreateYUVFrame(float width, float height) { return (YUVFrame*)frame; } -void Renderer::DrawRect(RectF const& dest, glm::vec4 color, float angle) { - BaseRenderer::DrawSprite(RectSprite, dest, color, angle); -} - -void Renderer::DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - glm::vec4 tint, bool inverted) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite3DRotated() called before BeginFrame()\n"); - return; - } - - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - // Are we in sprite mode? - EnsureModeSprite(inverted); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition3DRotated(dest, depth, vanishingPoint, stayInScreen, rot, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) { - DrawSprite3DRotated(RectSprite, dest, depth, vanishingPoint, stayInScreen, - rot, color); -} - -void Renderer::DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, - int indicesCount, uint16_t* mvlIndices, - bool inverted, glm::vec4 tint, - glm::vec2 scale) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawCharacterMvl() called before BeginFrame()\n"); - return; +void Renderer::InsertVertices( + const std::span vertices, + const std::span indices) { + if (vertices.empty() || indices.empty()) return; + assert(indices.size() <= MaxIndexCount); + + if (IndexBuffer.size() + indices.size() > MaxIndexCount) Flush(); + + const uint16_t maxIndex = *std::max_element(indices.begin(), indices.end()); + + VertexBuffer.insert(VertexBuffer.end(), vertices.begin(), vertices.end()); + + const size_t indexOffset = IndexBuffer.size(); + IndexBuffer.reserve(IndexBuffer.size() + indices.size()); + std::transform( + indices.begin(), indices.end(), std::back_inserter(IndexBuffer), + [this](uint16_t index) { return index + this->NextFreeIndex; }); + + NextFreeIndex += maxIndex + 1; +} + +void Renderer::InsertVerticesQuad(const CornersQuad pos, const CornersQuad uv, + const std::span tints, + const CornersQuad maskUV) { + const std::array vertices = { + VertexBufferSprites{ + .Position = pos.BottomLeft, + .UV = uv.BottomLeft, + .Tint = tints[0], + .MaskUV = maskUV.BottomLeft, + }, + VertexBufferSprites{ + .Position = pos.TopLeft, + .UV = uv.TopLeft, + .Tint = tints[1], + .MaskUV = maskUV.TopLeft, + }, + VertexBufferSprites{ + .Position = pos.TopRight, + .UV = uv.TopRight, + .Tint = tints[2], + .MaskUV = maskUV.TopRight, + }, + VertexBufferSprites{ + .Position = pos.BottomRight, + .UV = uv.BottomRight, + .Tint = tints[3], + .MaskUV = maskUV.BottomRight, + }, + }; + + const std::array indices = {0, 1, 2, 0, 2, 3}; + + InsertVertices(vertices, indices); +} + +void Renderer::UseTextures( + const std::span> textureUnitPairs) { + for (const auto [textureId, unitIndex] : textureUnitPairs) { + if (TextureUnits[unitIndex].TextureId != textureId && + TextureUnits[unitIndex].InUse) { + Flush(); + } } - // Draw just the character with this since we need to rebind the index buffer - // anyway... - Flush(); - - // Do we have space for the whole character? - EnsureSpaceAvailable(verticesCount, sizeof(VertexBufferSprites), - indicesCount); - - // Are we in sprite mode? - EnsureModeSprite(inverted); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); + for (const auto [textureId, unitIndex] : textureUnitPairs) { + TextureUnit& textureUnit = TextureUnits[unitIndex]; - IndexBufferFill += indicesCount; + glBindTextureUnit(unitIndex, textureId); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesCount * sizeof(mvlIndices[0]), - mvlIndices, GL_STATIC_DRAW); - - for (int i = 0; i < verticesCount; i++) { - glm::vec2 pos = glm::vec2(mvlVertices[i * 5], mvlVertices[i * 5 + 1]); - pos *= scale; - pos += topLeft; - vertices[i].Position = DesignToNDC(pos); - vertices[i].UV = glm::vec2(mvlVertices[i * 5 + 3], mvlVertices[i * 5 + 4]); - vertices[i].Tint = tint; + textureUnit.TextureId = textureId; + textureUnit.InUse = true; } - - // Flush again and bind back our buffer - Flush(); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - IndexBufferCount * sizeof(IndexBuffer[0]), IndexBuffer, - GL_STATIC_DRAW); } -void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, - const std::array& tints, float angle, - bool inverted) { +void Renderer::DrawSprite(const Sprite& sprite, const CornersQuad& dest, + const glm::mat4 transformation, + const std::span tints, + const bool inverted, const bool disableBlend, + const bool textureWrapRepeat) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawSprite() called before BeginFrame()\n"); return; } - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - // Are we in sprite mode? - EnsureModeSprite(inverted); + if (sprite.Sheet.IsScreenCap) Flush(); - if (sprite.Sheet.IsScreenCap) { + if (textureWrapRepeat) { Flush(); - } - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - if (sprite.Sheet.IsScreenCap) { - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } else { - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; -} - -void Renderer::DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, - int width, int height, glm::vec4 tint, - bool inverted, bool disableBlend) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawVertices() called before BeginFrame()\n"); - return; + glBindSampler(0, Samplers[0]); + glSamplerParameteri(Samplers[0], GL_TEXTURE_WRAP_S, GL_REPEAT); + glSamplerParameteri(Samplers[0], GL_TEXTURE_WRAP_T, GL_REPEAT); } if (disableBlend) { + Flush(); glDisable(GL_BLEND); } - const int verticesCount = sheetPositions.size(); + // Set uniform variables + if (inverted) { + SpriteInvertedUniforms uniforms{ + .Projection = Projection, + .Transformation = transformation, + .ColorMap = 0, + }; - if (verticesCount != displayPositions.size()) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawVertices() called with mismatched vertices count\n"); - return; - } - Flush(); - glBindSampler(2, Sampler); - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_S, GL_REPEAT); - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_T, GL_REPEAT); - - EnsureSpaceAvailable(verticesCount, sizeof(VertexBufferSprites), - verticesCount * 3); - EnsureModeSprite(inverted); - - EnsureTextureBound(sheet.Texture); - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); - std::vector indices; - indices.reserve((width - 1) * (height - 1) * 6); - // Generate indices for triangles - for (int y = 0; y < height - 1; y++) { - for (int x = 0; x < width - 1; x++) { - int v0 = y * width + x; - int v1 = y * width + (x + 1); - int v2 = (y + 1) * width + x; - int v3 = (y + 1) * width + (x + 1); - - // First triangle - for (auto v : {v1, v0, v2}) { - indices.push_back(v); - } - // Second triangle - for (auto v : {v3, v1, v2}) { - indices.push_back(v); - } - } - } - IndexBufferFill += indices.size(); - - glBufferData(GL_ELEMENT_ARRAY_BUFFER, IndexBufferFill * sizeof(uint16_t), - indices.data(), GL_STATIC_DRAW); - - for (int i = 0; i < verticesCount; i++) { - vertices[i].Position = DesignToNDC(displayPositions[i]); - vertices[i].Tint = tint; - glm::vec2 uv = - sheetPositions[i] / glm::vec2(sheet.DesignWidth, sheet.DesignHeight); - if (sheet.IsScreenCap) { - uv.y = 1.0f - uv.y; - } - vertices[i].UV = uv; - } + UseShader(*SpriteInvertedShaderProgram, uniforms); - // Flush again and bind back our buffer - Flush(); - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (disableBlend) { - glEnable(GL_BLEND); - } - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - IndexBufferCount * sizeof(IndexBuffer[0]), IndexBuffer, - GL_STATIC_DRAW); -} + } else { + SpriteUniforms uniforms{ + .Projection = Projection, + .Transformation = transformation, + .ColorMap = 0, + }; -void Renderer::DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, glm::vec4 tint, - glm::vec2 scale, float angle, bool inverted) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite() called before BeginFrame()\n"); - return; + UseShader(*SpriteShaderProgram, uniforms); } - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - // Are we in sprite mode? - EnsureModeSprite(inverted); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); + UseTextures(std::array, 1>{ + std::pair{sprite.Sheet.Texture, 0}}); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; + CornersQuad uvDest = sprite.NormalizedBounds(); + if (sprite.Sheet.IsScreenCap) uvDest.FlipVertical(); + InsertVerticesQuad(dest, uvDest, tints); - if (sprite.Sheet.IsScreenCap) { - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } else { - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); + if (disableBlend) { + Flush(); + glEnable(GL_BLEND); } - QuadSetPositionOffset(sprite.Bounds, topLeft, displayOffset, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + if (textureWrapRepeat) { + Flush(); + glBindSampler(0, Samplers[0]); + glSamplerParameteri(Samplers[0], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(Samplers[0], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } } -void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, - bool isSameTexture) { +void Renderer::DrawMaskedSprite( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + glm::mat4 spriteTransformation, glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool isSameTexture) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); return; } - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - if (CurrentMode != R2D_Masked) { - Flush(); - CurrentMode = R2D_Masked; - } - - EnsureTextureBound(sprite.Sheet.Texture); + alpha = std::clamp(alpha, 0, fadeRange + 256); + const float alphaRange = 256.0f / fadeRange; + const float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - glBindVertexArray(VAOSprites); - glUseProgram(ShaderProgramMaskedSprite); - glUniform1i(glGetUniformLocation(ShaderProgramMaskedSprite, "Mask"), 2); - glUniform2f(glGetUniformLocation(ShaderProgramMaskedSprite, "Alpha"), - alphaRange, constAlpha); - glUniform1i(MaskedIsInvertedLocation, isInverted); - glUniform1i(MaskedIsSameTextureLocation, isSameTexture); + // Set uniform variables + MaskedSpriteUniforms uniforms{ + .Projection = Projection, + .SpriteTransformation = spriteTransformation, + .MaskTransformation = maskTransformation, + .ColorMap = 0, + .Mask = 2, + .Alpha = {alphaRange, constAlpha}, + .IsInverted = isInverted, + .IsSameTexture = isSameTexture, + }; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sprite.Sheet.Texture); + UseShader(*MaskedSpriteShaderProgram, uniforms); - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mask.Sheet.Texture); - glBindSampler(2, Sampler); + UseTextures(std::array, 2>{ + std::pair{sprite.Sheet.Texture, 0}, + std::pair{mask.Sheet.Texture, 2}, + }); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - if (sprite.Sheet.IsScreenCap) { - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } else { - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); - - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + CornersQuad uvDest = sprite.NormalizedBounds(); + if (sprite.Sheet.IsScreenCap) uvDest.FlipVertical(); + CornersQuad maskUVDest = CornersQuad(maskDest).Scale( + {1.0f / sprite.Bounds.Width, 1.0f / sprite.Bounds.Height}, {0.0f, 0.0f}); + InsertVerticesQuad(spriteDest, uvDest, tints, maskUVDest); } -void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - const glm::vec2 pos, - const glm::vec2 origin, int alpha, - const int fadeRange, const glm::vec4 tint, - const glm::vec2 scale, const float angle, - const bool spriteInverted, - const bool maskInverted, - const bool isSameTexture) { +void Renderer::DrawMaskedSpriteOverlay( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + glm::mat4 spriteTransformation, glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool useMaskAlpha) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); + "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); return; } - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; + alpha = std::clamp(alpha, 0, fadeRange + 256); + const float alphaRange = 256.0f / fadeRange; + const float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); + if (useMaskAlpha) { + MaskedSpriteUniforms uniforms{ + .Projection = Projection, + .SpriteTransformation = spriteTransformation, + .MaskTransformation = maskTransformation, + .ColorMap = 0, + .Mask = 2, + .Alpha = {alphaRange, constAlpha}, + .IsInverted = isInverted, + .IsSameTexture = false, + }; + + UseShader(*MaskedSpriteShaderProgram, uniforms); - // Are we in sprite mode? - EnsureModeSprite(spriteInverted); + } else { + MaskedSpriteNoAlphaUniforms uniforms{ + .Projection = Projection, + .SpriteTransformation = spriteTransformation, + .MaskTransformation = maskTransformation, + .ColorMap = 0, + .Mask = 2, + .Alpha = {alphaRange, constAlpha}, + .IsInverted = isInverted, + }; - if (CurrentMode != R2D_Masked) { - Flush(); - CurrentMode = R2D_Masked; + UseShader(*MaskedSpriteNoAlphaShaderProgram, uniforms); } - EnsureTextureBound(sprite.Sheet.Texture); - - glBindVertexArray(VAOSprites); - glUseProgram(ShaderProgramMaskedSprite); - glUniform1i(glGetUniformLocation(ShaderProgramMaskedSprite, "Mask"), 2); - glUniform2f(glGetUniformLocation(ShaderProgramMaskedSprite, "Alpha"), - alphaRange, constAlpha); - glUniform1i(MaskedIsInvertedLocation, maskInverted); - glUniform1i(MaskedIsSameTextureLocation, isSameTexture); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sprite.Sheet.Texture); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mask.Sheet.Texture); - glBindSampler(2, Sampler); + UseTextures(std::array, 2>{ + std::pair{sprite.Sheet.Texture, 0}, + std::pair{mask.Sheet.Texture, 2}, + }); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - if (sprite.Sheet.IsScreenCap) { - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } else { - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); - - QuadSetPositionOffset(sprite.Bounds, pos, origin, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - QuadSetPositionOffset({0.0f, 0.0f, 1.0f, 1.0f}, pos, origin, scale, angle, - (uintptr_t)&vertices[0].MaskUV, - sizeof(VertexBufferSprites), false); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + CornersQuad normalizedMaskDest = CornersQuad(maskDest).Scale( + {1.0f / mask.Sheet.DesignWidth, 1.0f / mask.Sheet.DesignHeight}, + {0.0f, 0.0f}); + InsertVerticesQuad(spriteDest, sprite.NormalizedBounds(), tints, + normalizedMaskDest); } -void Renderer::DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, - int alpha, int fadeRange, - bool isInverted, float angle, - bool useMaskAlpha) { +void Renderer::DrawVertices(const SpriteSheet& sheet, + const SpriteSheet* const mask, + const std::span vertices, + const std::span indices, + glm::mat4 transformation, const bool inverted) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); + "Renderer->DrawVertices() called before BeginFrame()\n"); return; } - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; + // Set uniform variables + if (mask != nullptr) { + MaskedSpriteNoAlphaUniforms uniforms{ + .Projection = Projection, + .SpriteTransformation = transformation, + .MaskTransformation = glm::mat4(1.0f), + .ColorMap = 0, + .Mask = 2, + .Alpha = {1.0f, 0.0f}, + .IsInverted = inverted, + }; - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); + UseShader(*MaskedSpriteNoAlphaShaderProgram, uniforms); - glBindVertexArray(VAOSprites); - if (useMaskAlpha) { - if (CurrentMode != R2D_Masked || sprite.Sheet.IsScreenCap) { - Flush(); - CurrentMode = R2D_Masked; - } - glUseProgram(ShaderProgramMaskedSprite); - glUniform1i(glGetUniformLocation(ShaderProgramMaskedSprite, "Mask"), 2); - glUniform2f(glGetUniformLocation(ShaderProgramMaskedSprite, "Alpha"), - alphaRange, constAlpha); - glUniform1i(MaskedIsInvertedLocation, isInverted); - glUniform1i(MaskedIsSameTextureLocation, false); } else { - if (CurrentMode != R2D_MaskedNoAlpha || sprite.Sheet.IsScreenCap) { - Flush(); - CurrentMode = R2D_MaskedNoAlpha; + if (inverted) { + SpriteInvertedUniforms uniforms{ + .Projection = Projection, + .Transformation = transformation, + .ColorMap = 0, + }; + + UseShader(*SpriteInvertedShaderProgram, uniforms); + + } else { + SpriteUniforms uniforms{ + .Projection = Projection, + .Transformation = transformation, + .ColorMap = 0, + }; + + UseShader(*SpriteShaderProgram, uniforms); } - glUseProgram(ShaderProgramMaskedSpriteNoAlpha); - glUniform1i(glGetUniformLocation(ShaderProgramMaskedSpriteNoAlpha, "Mask"), - 2); - glUniform2f(glGetUniformLocation(ShaderProgramMaskedSpriteNoAlpha, "Alpha"), - alphaRange, constAlpha); - glUniform1i(MaskedNoAlphaIsInvertedLocation, isInverted); } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sprite.Sheet.Texture); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mask.Sheet.Texture); - glBindSampler(2, Sampler); - - // OK, all good, make quad - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); + if (mask == nullptr) { + UseTextures(std::array, 1>{ + std::pair{sheet.Texture, 0}, + }); + } else { + UseTextures(std::array, 2>{ + std::pair{sheet.Texture, 0}, + std::pair{mask->Texture, 2}, + }); + } - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites), angle); + std::vector transformedVertices; + transformedVertices.resize(vertices.size()); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + const auto transformVertex = [this, sheet](VertexBufferSprites info) { + if (sheet.IsScreenCap) info.UV.y = 1.0f - info.UV.y; + return info; + }; + std::transform(vertices.begin(), vertices.end(), transformedVertices.begin(), + transformVertex); - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + InsertVertices(transformedVertices, indices); } void Renderer::DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, @@ -764,50 +547,25 @@ void Renderer::DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, float alphaRange = 256.0f / fadeRange; float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); + CCMessageBoxUniforms uniforms{ + .Projection = Projection, + .ColorMap = 0, + .Mask = 2, + .Alpha = {alphaRange, constAlpha, effectCt, 0.0f}, + }; - if (CurrentMode != R2D_CCMessageBox) { - Flush(); - CurrentMode = R2D_CCMessageBox; - } - glBindVertexArray(VAOSprites); - glUseProgram(ShaderProgramCCMessageBox); - glUniform1i(glGetUniformLocation(ShaderProgramCCMessageBox, "Mask"), 2); - glUniform4f(glGetUniformLocation(ShaderProgramCCMessageBox, "Alpha"), - alphaRange, constAlpha, effectCt, 0.0f); + UseShader(*CCMessageBoxShaderProgram, uniforms); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sprite.Sheet.Texture); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mask.Sheet.Texture); - glBindSampler(2, Sampler); + UseTextures(std::array, 2>{ + std::pair{sprite.Sheet.Texture, 0}, + std::pair{mask.Sheet.Texture, 2}, + }); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - if (sprite.Sheet.IsScreenCap) { - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } else { - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - } - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); - - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + CornersQuad uvDest = sprite.NormalizedBounds(); + if (sprite.Sheet.IsScreenCap) uvDest.FlipVertical(); + InsertVerticesQuad(dest, uvDest, tint, mask.NormalizedBounds()); } void Renderer::DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, @@ -823,289 +581,25 @@ void Renderer::DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, else if (alpha > 1.0f) alpha = 1.0f; - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); + CHLCCMenuBackgroundUniforms uniforms{ + .Projection = Projection, + .ColorMap = 0, + .Mask = 2, + .Alpha = alpha, + }; - if (CurrentMode != R2D_CHLCCMenuBackground) { - Flush(); - CurrentMode = R2D_CHLCCMenuBackground; - } + UseShader(*CHLCCMenuBackgroundShaderProgram, uniforms); - glBindVertexArray(VAOSprites); - glUseProgram(ShaderProgramCHLCCMenuBackground); - glUniform1i(glGetUniformLocation(ShaderProgramCCMessageBox, "ColorMap"), 0); - glUniform1i(glGetUniformLocation(ShaderProgramCCMessageBox, "Mask"), 2); - glUniform1f(glGetUniformLocation(ShaderProgramCHLCCMenuBackground, "Alpha"), - alpha); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sprite.Sheet.Texture); - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mask.Sheet.Texture); - glBindSampler(2, Sampler); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); + UseTextures(std::array, 2>{ + std::pair{sprite.Sheet.Texture, 0}, + std::pair{mask.Sheet.Texture, 2}, + }); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUVFlipped(sprite.Bounds, sprite.Sheet.DesignWidth, - sprite.Sheet.DesignHeight, (uintptr_t)&vertices[0].UV, - sizeof(VertexBufferSprites)); - - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); - - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); -} - -inline void Renderer::QuadSetUVFlipped(RectF const& spriteBounds, - float designWidth, float designHeight, - uintptr_t uvs, int stride) { - float topUV = (spriteBounds.Y / designHeight); - float leftUV = (spriteBounds.X / designWidth); - float bottomUV = ((spriteBounds.Y + spriteBounds.Height) / designHeight); - float rightUV = ((spriteBounds.X + spriteBounds.Width) / designWidth); - - // top-left - *(glm::vec2*)(uvs + 0 * stride) = glm::vec2(leftUV, topUV); - // bottom-left - *(glm::vec2*)(uvs + 1 * stride) = glm::vec2(leftUV, bottomUV); - // bottom-right - *(glm::vec2*)(uvs + 2 * stride) = glm::vec2(rightUV, bottomUV); - // top-right - *(glm::vec2*)(uvs + 3 * stride) = glm::vec2(rightUV, topUV); -} - -inline void Renderer::QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle) { - float topUV = (spriteBounds.Y / designHeight); - float leftUV = (spriteBounds.X / designWidth); - float bottomUV = ((spriteBounds.Y + spriteBounds.Height) / designHeight); - float rightUV = ((spriteBounds.X + spriteBounds.Width) / designWidth); - - glm::vec2 bottomLeft(leftUV, bottomUV); - glm::vec2 topLeft(leftUV, topUV); - glm::vec2 topRight(rightUV, topUV); - glm::vec2 bottomRight(rightUV, bottomUV); - - if (angle != 0.0f) { - glm::vec2 center = (bottomLeft + topRight) * 0.5f; // Center of the quad - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(uvs + 0 * stride) = bottomLeft; - // top-left - *(glm::vec2*)(uvs + 1 * stride) = topLeft; - // top-right - *(glm::vec2*)(uvs + 2 * stride) = topRight; - // bottom-right - *(glm::vec2*)(uvs + 3 * stride) = bottomRight; -} - -inline void Renderer::QuadSetPositionOffset( - RectF const& spriteBounds, glm::vec2 displayXY, glm::vec2 displayOffset, - glm::vec2 scale, float angle, uintptr_t positions, int stride, bool toNDC) { - glm::vec2 topLeft = {0.0f, 0.0f}; - glm::vec2 bottomLeft = {0.0f, spriteBounds.Height}; - glm::vec2 topRight = {spriteBounds.Width, 0.0f}; - glm::vec2 bottomRight = {spriteBounds.Width, spriteBounds.Height}; - - // Translate to origin - topLeft -= displayOffset; - bottomLeft -= displayOffset; - topRight -= displayOffset; - bottomRight -= displayOffset; - - // Scale - bottomLeft *= scale; - topLeft *= scale; - topRight *= scale; - bottomRight *= scale; - - // Rotate - if (angle != 0.0f) { - const glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * bottomLeft; - topLeft = rot * topLeft; - topRight = rot * topRight; - bottomRight = rot * bottomRight; - } - - // Translate to the desired screen position - bottomLeft += displayOffset + displayXY; - topLeft += displayOffset + displayXY; - topRight += displayOffset + displayXY; - bottomRight += displayOffset + displayXY; - - if (toNDC) { - bottomLeft = DesignToNDC(bottomLeft); - topLeft = DesignToNDC(topLeft); - topRight = DesignToNDC(topRight); - bottomRight = DesignToNDC(bottomRight); - } - - // Store the transformed positions - *(glm::vec2*)(positions + 0 * stride) = bottomLeft; - *(glm::vec2*)(positions + 1 * stride) = topLeft; - *(glm::vec2*)(positions + 2 * stride) = topRight; - *(glm::vec2*)(positions + 3 * stride) = bottomRight; -} - -inline void Renderer::QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride) { - glm::vec2 bottomLeft = - glm::vec2(transformedQuad.X, transformedQuad.Y + transformedQuad.Height); - glm::vec2 topLeft = glm::vec2(transformedQuad.X, transformedQuad.Y); - glm::vec2 topRight = - glm::vec2(transformedQuad.X + transformedQuad.Width, transformedQuad.Y); - glm::vec2 bottomRight = glm::vec2(transformedQuad.X + transformedQuad.Width, - transformedQuad.Y + transformedQuad.Height); - - if (angle != 0.0f) { - glm::vec2 center = transformedQuad.Center(); - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(positions + 0 * stride) = DesignToNDC(bottomLeft); - // top-left - *(glm::vec2*)(positions + 1 * stride) = DesignToNDC(topLeft); - // top-right - *(glm::vec2*)(positions + 2 * stride) = DesignToNDC(topRight); - // bottom-right - *(glm::vec2*)(positions + 3 * stride) = DesignToNDC(bottomRight); -} - -inline void Renderer::QuadSetPosition(CornersQuad destQuad, float angle, - uintptr_t positions, int stride) { - if (angle != 0.0f) { - glm::vec2 center = (destQuad.BottomLeft + destQuad.TopRight) * 0.5f; - glm::mat2 rot = Rotate2D(angle); - - destQuad.BottomLeft = rot * (destQuad.BottomLeft - center) + center; - destQuad.TopLeft = rot * (destQuad.TopLeft - center) + center; - destQuad.TopRight = rot * (destQuad.TopRight - center) + center; - destQuad.BottomRight = rot * (destQuad.BottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(positions + 0 * stride) = DesignToNDC(destQuad.BottomLeft); - // top-left - *(glm::vec2*)(positions + 1 * stride) = DesignToNDC(destQuad.TopLeft); - // top-right - *(glm::vec2*)(positions + 2 * stride) = DesignToNDC(destQuad.TopRight); - // bottom-right - *(glm::vec2*)(positions + 3 * stride) = DesignToNDC(destQuad.BottomRight); -} - -void Renderer::QuadSetPosition3DRotated(RectF const& transformedQuad, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - uintptr_t positions, int stride) { - float widthNormalized = transformedQuad.Width / (Profile::DesignWidth * 0.5f); - float heightNormalized = - transformedQuad.Height / (Profile::DesignHeight * 0.5f); - - glm::vec4 corners[4]{ - // bottom-left - {glm::vec2(-widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}, - // top-left - {glm::vec2(-widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // top-right - {glm::vec2(widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // bottom-right - {glm::vec2(widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}}; - - glm::mat4 transform = - glm::translate(glm::mat4(1.0f), - glm::vec3(DesignToNDC(transformedQuad.Center()), 0)) * - glm::mat4_cast(rot); - - glm::vec4 vanishingPointNDC(DesignToNDC(vanishingPoint), 0, 0); - - for (int i = 0; i < 4; i++) { - corners[i] = transform * corners[i]; - } - - if (stayInScreen) { - float maxZ = 0.0f; - for (int i = 0; i < 4; i++) { - if (corners[i].z > maxZ) maxZ = corners[i].z; - } - for (int i = 0; i < 4; i++) { - corners[i].z -= maxZ; - } - } - - for (int i = 0; i < 4; i++) { - // perspective - corners[i] -= vanishingPointNDC; - corners[i].x *= (depth / (depth - corners[i].z)); - corners[i].y *= (depth / (depth - corners[i].z)); - corners[i] += vanishingPointNDC; - - *(glm::vec2*)(positions + i * stride) = corners[i]; - } -} - -void Renderer::EnsureSpaceAvailable(int vertices, int vertexSize, int indices) { - if (VertexBufferFill + vertices * vertexSize > VertexBufferSize || - IndexBufferFill + indices > IndexBufferCount) { - ImpLogSlow( - LogLevel::Trace, LogChannel::Render, - "Renderer->EnsureSpaceAvailable flushing because buffers full at " - "VertexBufferFill=0x{:08x},IndexBufferFill=0x{:08x}\n", - VertexBufferFill, IndexBufferFill); - Flush(); - } -} - -void Renderer::EnsureTextureBound(GLuint texture) { - if (CurrentTexture != texture) { - ImpLogSlow(LogLevel::Trace, LogChannel::Render, - "Renderer->EnsureTextureBound flushing because texture {:d} is " - "not {:d}\n", - CurrentTexture, texture); - Flush(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - CurrentTexture = texture; - } -} - -void Renderer::EnsureModeSprite(bool inverted) { - Renderer2DMode wantedMode = inverted ? R2D_SpriteInverted : R2D_Sprite; - if (CurrentMode != wantedMode) { - ImpLogSlow( - LogLevel::Trace, LogChannel::Render, - "Renderer2D flushing because mode {:d} is not R2D_Sprite/inverted\n", - to_underlying(CurrentMode)); - Flush(); - glBindVertexArray(VAOSprites); - glUseProgram(inverted ? ShaderProgramSpriteInverted : ShaderProgramSprite); - CurrentMode = wantedMode; - } + CornersQuad uvDest = sprite.NormalizedBounds(); + if (sprite.Sheet.IsScreenCap) uvDest.FlipVertical(); + InsertVerticesQuad(dest, uvDest, glm::vec4(1.0f), mask.NormalizedBounds()); } void Renderer::Flush() { @@ -1114,66 +608,53 @@ void Renderer::Flush() { "Renderer->Flush() called before BeginFrame()\n"); return; } - if (VertexBufferFill > 0 && IndexBufferFill > 0) { + if (!VertexBuffer.empty() && !IndexBuffer.empty()) { glBindBuffer(GL_ARRAY_BUFFER, VBO); - // TODO: better to specify the whole thing or just this? - glBufferSubData(GL_ARRAY_BUFFER, 0, VertexBufferFill, VertexBuffer); - glDrawElements(GL_TRIANGLES, IndexBufferFill, GL_UNSIGNED_SHORT, 0); + glBufferData(GL_ARRAY_BUFFER, + VertexBuffer.size() * sizeof(VertexBufferSprites), + VertexBuffer.data(), GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer.size() * sizeof(uint16_t), + IndexBuffer.data(), GL_DYNAMIC_DRAW); + + glDrawElements(GL_TRIANGLES, IndexBuffer.size(), GL_UNSIGNED_SHORT, 0); } - IndexBufferFill = 0; - VertexBufferFill = 0; - CurrentTexture = 0; + + VertexBuffer.clear(); + IndexBuffer.clear(); + NextFreeIndex = 0; + + FlushTextures(); } -void Renderer::DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint, float angle, bool alphaVideo) { +void Renderer::DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + const glm::vec4 tint, const bool alphaVideo) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawVideoTexture() called before BeginFrame()\n"); return; } - // Do we have space for one more sprite quad? - EnsureSpaceAvailable(4, sizeof(VertexBufferSprites), 6); - - if (CurrentMode != R2D_YUVFrame) { - Flush(); - CurrentMode = R2D_YUVFrame; - } - glBindVertexArray(VAOSprites); - glUseProgram(ShaderProgramYUVFrame); - glUniform1i(YUVFrameCbLocation, 2); - glUniform1i(YUVFrameCrLocation, 4); - glUniform1i(YUVFrameIsAlphaLocation, alphaVideo); + UseTextures(std::array, 3>{ + std::pair{frame.LumaId, 0}, + std::pair{frame.CbId, 1}, + std::pair{frame.CrId, 2}, + }); - // Luma - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex->LumaId); + YUVFrameUniforms uniforms{ + .Projection = Projection, + .Luma = 0, + .Cb = 1, + .Cr = 2, + .IsAlpha = alphaVideo, + }; - // Cb - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, tex->CbId); - glBindSampler(2, Sampler); - - // Cr - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, tex->CrId); - glBindSampler(4, Sampler); + UseShader(*YUVFrameShaderProgram, uniforms); // OK, all good, make quad - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - IndexBufferFill += 6; - - QuadSetUV(RectF(0.0f, 0.0f, tex->Width, tex->Height), tex->Width, tex->Height, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + InsertVerticesQuad(dest, RectF(0.0f, 0.0f, 1.0f, 1.0f), tint); } void Renderer::CaptureScreencap(Sprite& sprite) { @@ -1193,11 +674,16 @@ void Renderer::CaptureScreencap(Sprite& sprite) { } void Renderer::EnableScissor() { + if (ScissorEnabled) return; + Flush(); glEnable(GL_SCISSOR_TEST); + ScissorEnabled = true; } void Renderer::SetScissorRect(RectF const& rect) { + if (!ScissorEnabled) return; + Rect viewport = Window->GetViewport(); float scale = fmin((float)Window->WindowWidth / Profile::DesignWidth, (float)Window->WindowHeight / Profile::DesignHeight); @@ -1212,8 +698,11 @@ void Renderer::SetScissorRect(RectF const& rect) { } void Renderer::DisableScissor() { + if (!ScissorEnabled) return; + Flush(); glDisable(GL_SCISSOR_TEST); + ScissorEnabled = false; } } // namespace OpenGL diff --git a/src/renderer/opengl/renderer.h b/src/renderer/opengl/renderer.h index 24fa88415..3738be291 100644 --- a/src/renderer/opengl/renderer.h +++ b/src/renderer/opengl/renderer.h @@ -8,23 +8,14 @@ #include "shader.h" #include "glc.h" +#include "../../profile/game.h" + namespace Impacto { namespace OpenGL { int constexpr NkMaxVertexMemory = 256 * 1024; int constexpr NkMaxElementMemory = 128 * 1024; -enum Renderer2DMode { - R2D_None, - R2D_Sprite, - R2D_SpriteInverted, - R2D_YUVFrame, - R2D_Masked, - R2D_MaskedNoAlpha, - R2D_CCMessageBox, - R2D_CHLCCMenuBackground, -}; - class Renderer : public BaseRenderer { public: void Init() override; @@ -45,66 +36,41 @@ class Renderer : public BaseRenderer { void FreeTexture(uint32_t id) override; YUVFrame* CreateYUVFrame(float width, float height) override; - void DrawSprite(Sprite const& sprite, CornersQuad const& dest, - const std::array& tints, float angle = 0.0f, - bool inverted = false) override; - - void DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool inverted = false) override; - - void DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, int width, - int height, glm::vec4 tint = glm::vec4(1.0), - bool inverted = false, bool disableBlend = false) override; - - void DrawRect(RectF const& dest, glm::vec4 color, - float angle = 0.0f) override; - - void DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted = false, - bool isSameTexture = false) override; - - void DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - glm::vec2 pos, glm::vec2 origin, int alpha, - int fadeRange, glm::vec4 tint = glm::vec4(1.0f), - glm::vec2 scale = glm::vec2(1.0), - float angle = 0.0f, bool spriteInverted = false, - bool maskInverted = false, - bool isSameTexture = false) override; + void DrawSprite(const Sprite& sprite, const CornersQuad& dest, + glm::mat4 transformation, std::span tints, + bool inverted, bool disableBlend, + bool textureWrapRepeat) override; + + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, int fadeRange, + glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, bool isInverted, + bool isSameTexture) override; + + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, + bool isInverted, bool useMaskAlpha) override; + + void DrawVertices(const SpriteSheet& sheet, const SpriteSheet* mask, + std::span vertices, + std::span indices, glm::mat4 transformation, + bool inverted) override; void DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, RectF const& dest, glm::vec4 tint, int alpha, int fadeRange, float effectCt) override; - void DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, float angle, - bool useMaskAlpha); - void DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, const RectF& dest, float alpha) override; - void DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 tint = glm::vec4(1.0f), - bool inverted = false) override; - void DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) override; - - void DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, int indicesCount, - uint16_t* mvlIndices, bool inverted, glm::vec4 tint, - glm::vec2 scale) override; - - void DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint = glm::vec4(1.0), float angle = 0.0f, - bool alphaVideo = false) override; + void DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + glm::vec4 tint, bool alphaVideo) override; void CaptureScreencap(Sprite& sprite) override; @@ -125,68 +91,89 @@ class Renderer : public BaseRenderer { void DisableScissor() override; private: - void EnsureSpaceAvailable(int vertices, int vertexSize, int indices); - void EnsureTextureBound(GLuint texture); - void EnsureModeSprite(bool inverted); - void Flush(); - - void QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle = 0.0f); - void QuadSetPositionOffset(RectF const& spriteBounds, glm::vec2 displayXY, - glm::vec2 displayOffset, glm::vec2 scale, - float angle, uintptr_t positions, int stride, - bool toNDC = true); - void QuadSetUVFlipped(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride); - void QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride); - void QuadSetPosition(CornersQuad destQuad, float angle, uintptr_t positions, - int stride); - void QuadSetPosition3DRotated(RectF const& transformedQuad, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, uintptr_t positions, int stride); + std::optional SpriteShaderProgram; + std::optional SpriteInvertedShaderProgram; + std::optional MaskedSpriteShaderProgram; + std::optional MaskedSpriteNoAlphaShaderProgram; + std::optional YUVFrameShaderProgram; + std::optional CCMessageBoxShaderProgram; + std::optional CHLCCMenuBackgroundShaderProgram; + + const void* CurrentShaderProgram = nullptr; + + template + void UseShader(ShaderType& shader, UniformsStruct uniforms) { + static_assert(std::is_base_of, ShaderType>()); + + if (CurrentShaderProgram != &shader) { + Flush(); + CurrentShaderProgram = &shader; + shader.Bind(); + } + + if (shader.GetUniforms() != uniforms) { + Flush(); + shader.UploadUniforms(uniforms); + } + } - GLWindow* OpenGLWindow; + void Flush() override; + + void InsertVertices(std::span vertices, + std::span indices); + void InsertVerticesQuad(CornersQuad pos, CornersQuad uv, + std::span tints, + CornersQuad maskUV = RectF()); + void InsertVerticesQuad(CornersQuad pos, CornersQuad uv, + glm::vec4 tint = glm::vec4(1.0f), + CornersQuad maskUV = RectF()) { + InsertVerticesQuad(pos, uv, std::array{tint, tint, tint, tint}, maskUV); + } - GLuint ShaderProgramSprite; - GLuint ShaderProgramSpriteInverted; - GLuint ShaderProgramMaskedSprite; - GLuint ShaderProgramMaskedSpriteNoAlpha; - GLuint ShaderProgramYUVFrame; - GLuint ShaderProgramCCMessageBox; - GLuint ShaderProgramCHLCCMenuBackground; - - GLuint YUVFrameCbLocation; - GLuint YUVFrameCrLocation; - GLuint YUVFrameIsAlphaLocation; - GLuint MaskedIsInvertedLocation; - GLuint MaskedIsSameTextureLocation; - GLuint MaskedNoAlphaIsInvertedLocation; + GLWindow* OpenGLWindow; GLuint VBO; GLuint IBO; GLuint VAOSprites; - GLuint Sampler; - bool Drawing = false; - static int constexpr VertexBufferSize = 1024 * 1024; - static int constexpr IndexBufferCount = - VertexBufferSize / (4 * sizeof(VertexBufferSprites)) * 6; + struct TextureUnit { + uint32_t TextureId = 0; + bool InUse = false; + }; + static constexpr size_t TextureUnitCount = 15; + std::array TextureUnits; + + void UseTextures( + std::span> textureUnitPairs); + void FlushTextures() { + std::transform(TextureUnits.begin(), TextureUnits.end(), + TextureUnits.begin(), [](TextureUnit unit) { + unit.InUse = false; + return unit; + }); + } + + std::array Samplers; + + static constexpr size_t MaxVertexCount = + 1024 * 1024 / sizeof(VertexBufferSprites); + static constexpr uint16_t MaxIndexCount = + std::numeric_limits::max(); - GLuint CurrentTexture = 0; - Renderer2DMode CurrentMode = R2D_None; - uint8_t VertexBuffer[VertexBufferSize]; - int VertexBufferFill = 0; - uint16_t IndexBuffer[IndexBufferCount]; - int IndexBufferFill = 0; + std::vector VertexBuffer; + std::vector IndexBuffer; - Sprite RectSprite; + uint16_t NextFreeIndex = 0; + + const glm::mat4 Projection = + glm::ortho(0.0f, Profile::DesignWidth, Profile::DesignHeight, 0.0f); // ShaderCompiler compiler - ShaderCompiler* Shaders; + ShaderCompiler Shaders; + + bool ScissorEnabled = false; }; } // namespace OpenGL diff --git a/src/renderer/opengl/shader.cpp b/src/renderer/opengl/shader.cpp index f66bea211..bce28fe56 100644 --- a/src/renderer/opengl/shader.cpp +++ b/src/renderer/opengl/shader.cpp @@ -238,5 +238,174 @@ GLuint ShaderCompiler::Compile(char const* name, ShaderParamMap const& params) { return program; } +SpriteShader::SpriteShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + TransformationLocation(glGetUniformLocation(programId, "Transformation")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.Transformation, TransformationLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); +} + +void SpriteShader::UploadUniforms(SpriteUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + UpdateVar(newUniforms.Transformation, Uniforms.Transformation, + TransformationLocation); + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); +} + +SpriteInvertedShader::SpriteInvertedShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + TransformationLocation(glGetUniformLocation(programId, "Transformation")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.Transformation, TransformationLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); +} + +void SpriteInvertedShader::UploadUniforms(SpriteInvertedUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + UpdateVar(newUniforms.Transformation, Uniforms.Transformation, + TransformationLocation); + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); +} + +YUVFrameShader::YUVFrameShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + LumaLocation(glGetUniformLocation(programId, "Luma")), + CbLocation(glGetUniformLocation(programId, "Cb")), + CrLocation(glGetUniformLocation(programId, "Cr")), + IsAlphaLocation(glGetUniformLocation(programId, "IsAlpha")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.Luma, LumaLocation); + UploadVar(Uniforms.Cb, CbLocation); + UploadVar(Uniforms.Cr, CrLocation); + UploadVar(Uniforms.IsAlpha, IsAlphaLocation); +} + +void YUVFrameShader::UploadUniforms(YUVFrameUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + + UpdateVar(newUniforms.Luma, Uniforms.Luma, LumaLocation); + UpdateVar(newUniforms.Cb, Uniforms.Cb, CbLocation); + UpdateVar(newUniforms.Cr, Uniforms.Cr, CrLocation); + UpdateVar(newUniforms.IsAlpha, Uniforms.IsAlpha, IsAlphaLocation); +} + +MaskedSpriteNoAlphaShader::MaskedSpriteNoAlphaShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + SpriteTransformationLocation( + glGetUniformLocation(programId, "SpriteTransformation")), + MaskTransformationLocation( + glGetUniformLocation(programId, "MaskTransformation")), + AlphaLocation(glGetUniformLocation(programId, "Alpha")), + IsInvertedLocation(glGetUniformLocation(programId, "IsInverted")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")), + MaskLocation(glGetUniformLocation(programId, "Mask")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.SpriteTransformation, SpriteTransformationLocation); + UploadVar(Uniforms.MaskTransformation, MaskTransformationLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); + UploadVar(Uniforms.Mask, MaskLocation); + UploadVar(Uniforms.Alpha, AlphaLocation); + UploadVar(Uniforms.IsInverted, IsInvertedLocation); +} + +void MaskedSpriteNoAlphaShader::UploadUniforms( + MaskedSpriteNoAlphaUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + UpdateVar(newUniforms.SpriteTransformation, Uniforms.SpriteTransformation, + SpriteTransformationLocation); + UpdateVar(newUniforms.MaskTransformation, Uniforms.MaskTransformation, + MaskTransformationLocation); + + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); + UpdateVar(newUniforms.Mask, Uniforms.Mask, MaskLocation); + UpdateVar(newUniforms.Alpha, Uniforms.Alpha, AlphaLocation); + UpdateVar(newUniforms.IsInverted, Uniforms.IsInverted, IsInvertedLocation); +} + +MaskedSpriteShader::MaskedSpriteShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + SpriteTransformationLocation( + glGetUniformLocation(programId, "SpriteTransformation")), + MaskTransformationLocation( + glGetUniformLocation(programId, "MaskTransformation")), + AlphaLocation(glGetUniformLocation(programId, "Alpha")), + IsInvertedLocation(glGetUniformLocation(programId, "IsInverted")), + IsSameTextureLocation(glGetUniformLocation(programId, "IsSameTexture")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")), + MaskLocation(glGetUniformLocation(programId, "Mask")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.SpriteTransformation, SpriteTransformationLocation); + UploadVar(Uniforms.MaskTransformation, MaskTransformationLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); + UploadVar(Uniforms.Mask, MaskLocation); + UploadVar(Uniforms.Alpha, AlphaLocation); + UploadVar(Uniforms.IsInverted, IsInvertedLocation); + UploadVar(Uniforms.IsSameTexture, IsSameTextureLocation); +} + +void MaskedSpriteShader::UploadUniforms(MaskedSpriteUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + UpdateVar(newUniforms.SpriteTransformation, Uniforms.SpriteTransformation, + SpriteTransformationLocation); + UpdateVar(newUniforms.MaskTransformation, Uniforms.MaskTransformation, + MaskTransformationLocation); + + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); + UpdateVar(newUniforms.Mask, Uniforms.Mask, MaskLocation); + UpdateVar(newUniforms.Alpha, Uniforms.Alpha, AlphaLocation); + UpdateVar(newUniforms.IsInverted, Uniforms.IsInverted, IsInvertedLocation); + UpdateVar(newUniforms.IsSameTexture, Uniforms.IsSameTexture, + IsSameTextureLocation); +} + +CCMessageBoxShader::CCMessageBoxShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + AlphaLocation(glGetUniformLocation(programId, "Alpha")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")), + MaskLocation(glGetUniformLocation(programId, "Mask")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); + UploadVar(Uniforms.Mask, MaskLocation); + UploadVar(Uniforms.Alpha, AlphaLocation); +} + +void CCMessageBoxShader::UploadUniforms(CCMessageBoxUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); + UpdateVar(newUniforms.Mask, Uniforms.Mask, MaskLocation); + UpdateVar(newUniforms.Alpha, Uniforms.Alpha, AlphaLocation); +} + +CHLCCMenuBackgroundShader::CHLCCMenuBackgroundShader(GLint programId) + : Shader(programId), + ProjectionLocation(glGetUniformLocation(programId, "Projection")), + AlphaLocation(glGetUniformLocation(programId, "Alpha")), + ColorMapLocation(glGetUniformLocation(programId, "ColorMap")), + MaskLocation(glGetUniformLocation(programId, "Mask")) { + UploadVar(Uniforms.Projection, ProjectionLocation); + UploadVar(Uniforms.ColorMap, ColorMapLocation); + UploadVar(Uniforms.Mask, MaskLocation); + UploadVar(Uniforms.Alpha, AlphaLocation); +} + +void CHLCCMenuBackgroundShader::UploadUniforms( + CHLCCMenuBackgroundUniforms newUniforms) { + UpdateVar(newUniforms.Projection, Uniforms.Projection, ProjectionLocation); + + UpdateVar(newUniforms.ColorMap, Uniforms.ColorMap, ColorMapLocation); + UpdateVar(newUniforms.Mask, Uniforms.Mask, MaskLocation); + UpdateVar(newUniforms.Alpha, Uniforms.Alpha, AlphaLocation); +} + } // namespace OpenGL -} // namespace Impacto \ No newline at end of file +} // namespace Impacto diff --git a/src/renderer/opengl/shader.h b/src/renderer/opengl/shader.h index 360a99b76..8fffb16b3 100644 --- a/src/renderer/opengl/shader.h +++ b/src/renderer/opengl/shader.h @@ -74,5 +74,227 @@ class ShaderCompiler { char const* params); }; +template +class Shader { + public: + Shader(GLuint programId) : ProgramId(programId) { Bind(); } + + ~Shader() { glDeleteProgram(ProgramId); } + + void Bind() { glUseProgram(ProgramId); } + + UniformsStruct GetUniforms() { return Uniforms; } + virtual void UploadUniforms(UniformsStruct uniforms) = 0; + + protected: + const GLuint ProgramId; + + UniformsStruct Uniforms{}; + + void UploadVar(bool value, GLint location) { glUniform1i(location, value); } + void UploadVar(int value, GLint location) { glUniform1i(location, value); } + void UploadVar(GLuint value, GLint location) { + glUniform1ui(location, value); + } + void UploadVar(float value, GLint location) { glUniform1f(location, value); } + void UploadVar(glm::vec2 value, GLint location) { + glUniform2fv(location, 1, &value[0]); + } + void UploadVar(glm::vec4 value, GLint location) { + glUniform4fv(location, 1, &value[0]); + } + void UploadVar(glm::mat4 value, GLint location) { + glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]); + } + + template + void UpdateVar(T newValue, T& oldValue, GLint location) { + if (newValue != oldValue) { + oldValue = newValue; + UploadVar(newValue, location); + } + } +}; + +struct SpriteUniforms { + bool operator==(const SpriteUniforms& other) const = default; + + glm::mat4 Projection{}; + glm::mat4 Transformation{}; + + GLint ColorMap = 0; +}; + +class SpriteShader : public Shader { + public: + SpriteShader(GLint programId); + + void UploadUniforms(SpriteUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + const GLint TransformationLocation; + + const GLint ColorMapLocation; +}; + +struct SpriteInvertedUniforms { + bool operator==(const SpriteInvertedUniforms& other) const = default; + + glm::mat4 Projection{}; + glm::mat4 Transformation{}; + + GLint ColorMap = 0; +}; + +class SpriteInvertedShader : public Shader { + public: + SpriteInvertedShader(GLint programId); + + void UploadUniforms(SpriteInvertedUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + const GLint TransformationLocation; + + const GLint ColorMapLocation; +}; + +struct YUVFrameUniforms { + bool operator==(const YUVFrameUniforms& other) const = default; + + glm::mat4 Projection; + + GLint Luma = 0; + GLint Cb = 0; + GLint Cr = 0; + bool IsAlpha = false; +}; + +class YUVFrameShader : public Shader { + public: + YUVFrameShader(GLint programId); + + void UploadUniforms(YUVFrameUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + + const GLint LumaLocation; + const GLint CbLocation; + const GLint CrLocation; + const GLint IsAlphaLocation; +}; + +struct MaskedSpriteNoAlphaUniforms { + bool operator==(const MaskedSpriteNoAlphaUniforms& other) const = default; + + glm::mat4 Projection{}; + glm::mat4 SpriteTransformation{}; + glm::mat4 MaskTransformation{}; + + GLint ColorMap = 0; + GLint Mask = 0; + glm::vec2 Alpha{}; + bool IsInverted = false; +}; + +class MaskedSpriteNoAlphaShader : public Shader { + public: + MaskedSpriteNoAlphaShader(GLint programId); + + void UploadUniforms(MaskedSpriteNoAlphaUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + const GLint SpriteTransformationLocation; + const GLint MaskTransformationLocation; + + const GLint ColorMapLocation; + const GLint MaskLocation; + const GLint AlphaLocation; + const GLint IsInvertedLocation; +}; + +struct MaskedSpriteUniforms { + bool operator==(const MaskedSpriteUniforms& other) const = default; + + glm::mat4 Projection{}; + glm::mat4 SpriteTransformation{}; + glm::mat4 MaskTransformation{}; + + GLint ColorMap; + GLint Mask; + glm::vec2 Alpha{}; + bool IsInverted = false; + bool IsSameTexture = false; +}; + +class MaskedSpriteShader : public Shader { + public: + MaskedSpriteShader(GLint programId); + + void UploadUniforms(MaskedSpriteUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + const GLint SpriteTransformationLocation; + const GLint MaskTransformationLocation; + + const GLint ColorMapLocation; + const GLint MaskLocation; + const GLint AlphaLocation; + const GLint IsInvertedLocation; + const GLint IsSameTextureLocation; +}; + +struct CCMessageBoxUniforms { + bool operator==(const CCMessageBoxUniforms& other) const = default; + + glm::mat4 Projection{}; + + GLint ColorMap; + GLint Mask; + glm::vec4 Alpha{}; +}; + +class CCMessageBoxShader : public Shader { + public: + CCMessageBoxShader(GLint programId); + + void UploadUniforms(CCMessageBoxUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + + const GLint ColorMapLocation; + const GLint MaskLocation; + const GLint AlphaLocation; +}; + +struct CHLCCMenuBackgroundUniforms { + bool operator==(const CHLCCMenuBackgroundUniforms& other) const = default; + + glm::mat4 Projection{}; + + GLint ColorMap; + GLint Mask; + float Alpha = 0.0f; +}; + +class CHLCCMenuBackgroundShader : public Shader { + public: + CHLCCMenuBackgroundShader(GLint programId); + + void UploadUniforms(CHLCCMenuBackgroundUniforms uniforms) override; + + private: + const GLint ProjectionLocation; + + const GLint ColorMapLocation; + const GLint MaskLocation; + const GLint AlphaLocation; +}; + } // namespace OpenGL } // namespace Impacto \ No newline at end of file diff --git a/src/renderer/opengl/yuvframe.cpp b/src/renderer/opengl/yuvframe.cpp index 7c1f5efe3..564c4bdd1 100644 --- a/src/renderer/opengl/yuvframe.cpp +++ b/src/renderer/opengl/yuvframe.cpp @@ -21,12 +21,17 @@ void GLYUVFrame::Submit(const void* luma, const void* cb, const void* cr) { glBindTexture(GL_TEXTURE_2D, LumaId); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)Width, (GLsizei)Height, 0, GL_RED, GL_UNSIGNED_BYTE, luma); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, CbId); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)(Width / 2), (GLsizei)(Height / 2), 0, GL_RED, GL_UNSIGNED_BYTE, cb); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, CrId); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)(Width / 2), (GLsizei)(Height / 2), 0, GL_RED, GL_UNSIGNED_BYTE, cr); + glGenerateMipmap(GL_TEXTURE_2D); } void GLYUVFrame::Release() { diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index 03fbc939b..f3cb48bb5 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -40,41 +40,47 @@ void InitRenderer() { Renderer->Init(); } -void BaseRenderer::DrawSprite(Sprite const& sprite, RectF const& dest, - glm::vec4 tint, float angle, bool inverted) { - std::array tints = {tint, tint, tint, tint}; - DrawSprite(sprite, CornersQuad(dest), tints, angle, inverted); -} - -void BaseRenderer::DrawSprite(Sprite const& sprite, glm::vec2 topLeft, - glm::vec4 tint, glm::vec2 scale, float angle, - bool inverted) { - RectF scaledDest(topLeft.x, topLeft.y, - scale.x * sprite.Bounds.Width * sprite.BaseScale.x, - scale.y * sprite.Bounds.Height * sprite.BaseScale.y); - DrawSprite(sprite, scaledDest, tint, angle, inverted); +static void InsertQuad(const std::span vertices, + const CornersQuad position, const CornersQuad uv, + const glm::vec4 tint, + const CornersQuad maskUV = RectF()) { + vertices[0] = { + .Position = position.BottomLeft, + .UV = uv.BottomLeft, + .Tint = tint, + .MaskUV = maskUV.BottomLeft, + }; + vertices[1] = { + .Position = position.TopLeft, + .UV = uv.TopLeft, + .Tint = tint, + .MaskUV = maskUV.TopLeft, + }; + vertices[2] = { + .Position = position.TopRight, + .UV = uv.TopRight, + .Tint = tint, + .MaskUV = maskUV.TopRight, + }; + vertices[3] = { + .Position = position.BottomRight, + .UV = uv.BottomRight, + .Tint = tint, + .MaskUV = maskUV.BottomRight, + }; } void BaseRenderer::DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, glm::vec2 topLeft, glm::vec4 tint, int alpha, int fadeRange, float effectCt, glm::vec2 scale) { - RectF scaledDest(topLeft.x, topLeft.y, - scale.x * sprite.Bounds.Width * sprite.BaseScale.x, - scale.y * sprite.Bounds.Height * sprite.BaseScale.y); - DrawCCMessageBox(sprite, mask, scaledDest, tint, alpha, fadeRange, effectCt); + const RectF dest = + sprite.ScaledBounds().Scale(scale, {0.0f, 0.0f}).Translate(topLeft); + DrawCCMessageBox(sprite, mask, dest, tint, alpha, fadeRange, effectCt); } -void BaseRenderer::DrawSprite3DRotated(Sprite const& sprite, glm::vec2 topLeft, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - glm::vec4 tint, glm::vec2 scale, - bool inverted) { - RectF scaledDest(topLeft.x, topLeft.y, - scale.x * sprite.Bounds.Width * sprite.BaseScale.x, - scale.y * sprite.Bounds.Height * sprite.BaseScale.y); - DrawSprite3DRotated(sprite, scaledDest, depth, vanishingPoint, stayInScreen, - rot, tint, inverted); +void BaseRenderer::DrawQuad(const CornersQuad& dest, const glm::vec4 color) { + DrawSprite(RectSprite, dest, color); } void BaseRenderer::DrawProcessedText(std::span text, @@ -114,162 +120,244 @@ void BaseRenderer::DrawProcessedText(std::span text, } } -void BaseRenderer::DrawVideoTexture(YUVFrame* tex, glm::vec2 topLeft, - glm::vec4 tint, glm::vec2 scale, - float angle, bool alphaVideo) { - RectF scaledDest(topLeft.x, topLeft.y, scale.x * tex->Width, - scale.y * tex->Height); - DrawVideoTexture(tex, scaledDest, tint, angle, alphaVideo); -} - void BaseRenderer::DrawProcessedText_BasicFont( std::span text, BasicFont* font, float opacity, RendererOutlineMode outlineMode, bool smoothstepGlyphOpacity, float outlineOpacity, SpriteSheet* maskedSheet) { - // cruddy mages outline + const size_t vertexCount = text.size() * 4; + const size_t indexCount = text.size() * 6; + std::vector vertices; + std::vector indices; + vertices.resize(vertexCount); + indices.resize(indexCount); + + for (size_t i = 0; i < text.size(); i++) { + const uint16_t bl = i * 4; + const uint16_t tl = bl + 1; + const uint16_t tr = bl + 2; + const uint16_t br = bl + 3; + + indices[i * 6 + 0] = bl; + indices[i * 6 + 1] = tl; + indices[i * 6 + 2] = tr; + indices[i * 6 + 3] = bl; + indices[i * 6 + 4] = tr; + indices[i * 6 + 5] = br; + } + + for (size_t i = 0; i < text.size(); i++) { + const ProcessedTextGlyph glyph = text[i]; + + const CornersQuad dest = glyph.DestRect; + const CornersQuad destUV = font->Glyph(glyph.CharId).NormalizedBounds(); + const CornersQuad maskUV = CornersQuad(dest).Scale( + {1.0f / Window->WindowWidth, 1.0f / Window->WindowHeight}, + {0.0f, 0.0f}); + glm::vec4 color = RgbIntToFloat(glyph.Colors.TextColor); + color.a = opacity * (smoothstepGlyphOpacity + ? glm::smoothstep(0.0f, 1.0f, glyph.Opacity) + : glyph.Opacity); + + InsertQuad(std::span(vertices.begin() + i * 4, 4), + dest, destUV, color, maskUV); + } + uint16_t maxIndex = vertexCount; + if (outlineMode != RendererOutlineMode::None) { - for (int i = 0; i < text.size(); i++) { - glm::vec4 color = RgbIntToFloat(text[i].Colors.OutlineColor); - color.a = outlineOpacity; - if (smoothstepGlyphOpacity) { - color.a *= glm::smoothstep(0.0f, 1.0f, text[i].Opacity); - } else { - color.a *= text[i].Opacity; + // Add outline to the front of the buffers + vertices.insert(vertices.end(), vertices.begin(), vertices.end()); + + indices.reserve(indexCount * 2); + std::transform(indices.begin(), indices.end(), std::back_inserter(indices), + [maxIndex](uint16_t index) { return index + maxIndex; }); + maxIndex += vertexCount; + + // Set the color of the outline + for (size_t i = 0; i < text.size(); i++) { + const ProcessedTextGlyph glyph = text[i]; + glm::vec4 color = RgbIntToFloat(glyph.Colors.OutlineColor); + color.a = + outlineOpacity * (smoothstepGlyphOpacity + ? glm::smoothstep(0.0f, 1.0f, glyph.Opacity) + : glyph.Opacity); + const auto glyphStart = vertices.begin() + i * 4; + const auto glyphEnd = glyphStart + 4; + std::transform(glyphStart, glyphEnd, glyphStart, [color](auto vertex) { + vertex.Tint = color; + return vertex; + }); + } + + const auto translateVertex = [](VertexBufferSprites vertex, + const glm::vec2 offset) { + vertex.Position += offset; + return vertex; + }; + + switch (outlineMode) { + case RendererOutlineMode::Full: { + // Add bottom-right outline + vertices.insert(vertices.begin() + vertexCount, vertices.begin(), + vertices.begin() + vertexCount); + + indices.resize(indexCount * 3); + std::transform(indices.begin(), indices.begin() + indexCount, + indices.begin() + indexCount * 2, + [maxIndex](uint16_t index) { return index + maxIndex; }); + maxIndex += vertexCount; + + // Translate outlines + const auto tlOutlineStart = vertices.begin(); + const auto brOutlineStart = tlOutlineStart + vertexCount; + const auto foregroundStart = brOutlineStart + vertexCount; + std::transform(tlOutlineStart, brOutlineStart, tlOutlineStart, + [translateVertex](auto vertex) { + return translateVertex(vertex, {-1.0f, -1.0f}); + }); + std::transform(brOutlineStart, foregroundStart, brOutlineStart, + [translateVertex](auto vertex) { + return translateVertex(vertex, {1.0f, 1.0f}); + }); + + break; } - Sprite glyph = font->Glyph(text[i].CharId); - RectF dest = text[i].DestRect; - switch (outlineMode) { - case RendererOutlineMode::Full: - dest.X--; - dest.Y--; - if (maskedSheet) { - Sprite mask; - mask.Sheet = *maskedSheet; - mask.Bounds = dest; - DrawMaskedSpriteOverlay(glyph, mask, dest, color, color.a * 255, - 256, false, 0, i == 0); - } else { - DrawSprite(glyph, dest, color); - } - dest.X++; - dest.Y++; - [[fallthrough]]; - case RendererOutlineMode::BottomRight: - dest.X++; - dest.Y++; - if (maskedSheet) { - Sprite mask; - mask.Sheet = *maskedSheet; - mask.Bounds = dest; - DrawMaskedSpriteOverlay(glyph, mask, dest, color, color.a * 255, - 256, false, 0, i == 0); - } else { - DrawSprite(glyph, dest, color); - } - break; - default: - break; + + case RendererOutlineMode::BottomRight: { + const auto brOutlineStart = vertices.begin(); + const auto foregroundStart = vertices.begin() + vertexCount; + std::transform(brOutlineStart, foregroundStart, brOutlineStart, + [translateVertex](auto vertex) { + return translateVertex(vertex, {1.0f, 1.0f}); + }); + + break; } - } - } - for (int i = 0; i < text.size(); i++) { - glm::vec4 color = RgbIntToFloat(text[i].Colors.TextColor); - color.a = opacity; - if (smoothstepGlyphOpacity) { - color.a *= glm::smoothstep(0.0f, 1.0f, text[i].Opacity); - } else { - color.a *= text[i].Opacity; - } - if (maskedSheet) { - Sprite mask; - mask.Sheet = *maskedSheet; - mask.Bounds = text[i].DestRect; - DrawMaskedSpriteOverlay(font->Glyph(text[i].CharId), mask, - text[i].DestRect, color, color.a * 255, 256, - false, 0, false); - } else { - DrawSprite(font->Glyph(text[i].CharId), text[i].DestRect, color); + default: + ImpLogSlow(LogLevel::Warning, LogChannel::Render, + "Unexpected outline mode!"); + break; } } + + DrawVertices(font->Sheet, maskedSheet, vertices, indices); } void BaseRenderer::DrawProcessedText_LBFont( std::span text, LBFont* font, float opacity, RendererOutlineMode outlineMode, bool smoothstepGlyphOpacity, float outlineOpacity, SpriteSheet* maskedSheet) { + const size_t vertexCount = text.size() * 4; + const size_t indexCount = text.size() * 6; + std::vector vertices(vertexCount); + std::vector indices(indexCount); + + for (size_t i = 0; i < text.size(); i++) { + const uint16_t bl = i * 4; + const uint16_t tl = bl + 1; + const uint16_t tr = bl + 2; + const uint16_t br = bl + 3; + + indices[i * 6 + 0] = bl; + indices[i * 6 + 1] = tl; + indices[i * 6 + 2] = tr; + indices[i * 6 + 3] = bl; + indices[i * 6 + 4] = tr; + indices[i * 6 + 5] = br; + } + if (outlineMode != RendererOutlineMode::None) { - for (int i = 0; i < text.size(); i++) { - glm::vec4 color = RgbIntToFloat(text[i].Colors.OutlineColor); - color.a = outlineOpacity; - if (smoothstepGlyphOpacity) { - color.a *= glm::smoothstep(0.0f, 1.0f, text[i].Opacity); - } else { - color.a *= text[i].Opacity; - } + for (size_t i = 0; i < text.size(); i++) { + const ProcessedTextGlyph glyph = text[i]; - float scaleX = text[i].DestRect.Height / font->BitmapEmWidth; - float scaleY = text[i].DestRect.Height / font->BitmapEmHeight; - RectF outlineDest; + glm::vec4 color = RgbIntToFloat(glyph.Colors.OutlineColor); + color.a = + outlineOpacity * (smoothstepGlyphOpacity + ? glm::smoothstep(0.0f, 1.0f, glyph.Opacity) + : glyph.Opacity); + const CornersQuad destUV = + font->OutlineGlyph(glyph.CharId).NormalizedBounds(); + CornersQuad dest = RectF(); + const glm::vec2 scale = {glyph.DestRect.Height / font->BitmapEmWidth, + glyph.DestRect.Height / font->BitmapEmHeight}; switch (outlineMode) { - case RendererOutlineMode::Full: - outlineDest = - RectF(text[i].DestRect.X + scaleX * font->OutlineOffset.x, - text[i].DestRect.Y + scaleY * font->OutlineOffset.y, - scaleX * font->OutlineCellWidth, - scaleY * font->OutlineCellHeight); + case RendererOutlineMode::Full: { + dest = RectF(font->OutlineOffset.x, font->OutlineOffset.y, + font->OutlineCellWidth, font->OutlineCellHeight) + .Scale(scale, {0.0f, 0.0f}) + .Translate(glyph.DestRect.GetPos()); break; - case RendererOutlineMode::BottomRight: - outlineDest = RectF( - text[i].DestRect.X + scaleX * (font->OutlineOffset.x * 3 / 4), - text[i].DestRect.Y + scaleY * (font->OutlineOffset.y * 3 / 4), - scaleX * (font->OutlineCellWidth + font->OutlineOffset.x / 2), - scaleY * (font->OutlineCellHeight + font->OutlineOffset.y / 2)); + } + + case RendererOutlineMode::BottomRight: { + dest = RectF(font->OutlineOffset.x * 3 / 4, + font->OutlineOffset.y * 3 / 4, + font->OutlineCellWidth + font->OutlineOffset.x / 2, + font->OutlineCellHeight + font->OutlineOffset.y / 2) + .Scale(scale, {0.0f, 0.0f}) + .Translate(glyph.DestRect.GetPos()); break; + } + default: + ImpLogSlow(LogLevel::Warning, LogChannel::Render, + "Unexpected outline mode!"); break; } - if (maskedSheet) { - Sprite mask; - mask.Sheet = *maskedSheet; - mask.Bounds = outlineDest; - DrawMaskedSpriteOverlay(font->OutlineGlyph(text[i].CharId), mask, - outlineDest, color, color.a * 255, 256, false, - 0, i == 0); - } else { - DrawSprite(font->OutlineGlyph(text[i].CharId), outlineDest, color); - } + + const CornersQuad maskUV = CornersQuad(dest).Scale( + {1.0f / Window->WindowWidth, 1.0f / Window->WindowHeight}, + {0.0f, 0.0f}); + InsertQuad(std::span(vertices.begin() + i * 4, 4), + dest, destUV, color, maskUV); } + + DrawVertices(font->OutlineSheet, maskedSheet, vertices, indices); } - for (int i = 0; i < text.size(); i++) { - glm::vec4 color = RgbIntToFloat(text[i].Colors.TextColor); - color.a = opacity; - if (smoothstepGlyphOpacity) { - color.a *= glm::smoothstep(0.0f, 1.0f, text[i].Opacity); - } else { - color.a *= text[i].Opacity; - } + for (size_t i = 0; i < text.size(); i++) { + const ProcessedTextGlyph glyph = text[i]; - float scaleX = text[i].DestRect.Height / font->BitmapEmWidth; - float scaleY = text[i].DestRect.Height / font->BitmapEmHeight; - - RectF foregroundDest = - RectF(text[i].DestRect.X + scaleX * font->ForegroundOffset.x, - text[i].DestRect.Y + scaleY * font->ForegroundOffset.y, - scaleX * font->CellWidth, scaleY * font->CellHeight); - if (maskedSheet) { - Sprite mask; - mask.Sheet = *maskedSheet; - mask.Bounds = foregroundDest; - DrawMaskedSpriteOverlay(font->Glyph(text[i].CharId), mask, foregroundDest, - color, color.a * 255, 256, false, 0, false); - } else { - DrawSprite(font->Glyph(text[i].CharId), foregroundDest, color); - } + glm::vec2 scale = {glyph.DestRect.Height / font->BitmapEmWidth, + glyph.DestRect.Height / font->BitmapEmHeight}; + CornersQuad dest = RectF(font->ForegroundOffset.x, font->ForegroundOffset.y, + font->CellWidth, font->CellHeight) + .Scale(scale, {0.0f, 0.0f}) + .Translate(glyph.DestRect.GetPos()); + + const CornersQuad destUV = font->Glyph(glyph.CharId).NormalizedBounds(); + + glm::vec4 color = RgbIntToFloat(glyph.Colors.TextColor); + color.a = opacity * (smoothstepGlyphOpacity + ? glm::smoothstep(0.0f, 1.0f, glyph.Opacity) + : glyph.Opacity); + + const CornersQuad maskUV = CornersQuad(dest).Scale( + {1.0f / Window->WindowWidth, 1.0f / Window->WindowHeight}, + {0.0f, 0.0f}); + InsertQuad(std::span(vertices.begin() + i * 4, 4), + dest, destUV, color, maskUV); } - if (maskedSheet) Flush(); + DrawVertices(font->ForegroundSheet, maskedSheet, vertices, indices); +} + +void BaseRenderer::QuadSetPosition(CornersQuad quad, glm::vec2* const pos, + int stride) { + *(glm::vec2*)((uint8_t*)pos + 0 * stride) = DesignToNDC(quad.TopLeft); + *(glm::vec2*)((uint8_t*)pos + 1 * stride) = DesignToNDC(quad.BottomLeft); + *(glm::vec2*)((uint8_t*)pos + 2 * stride) = DesignToNDC(quad.BottomRight); + *(glm::vec2*)((uint8_t*)pos + 3 * stride) = DesignToNDC(quad.TopRight); } + +void BaseRenderer::QuadSetUV(const CornersQuad bounds, + const glm::vec2 dimensions, glm::vec2* const uvs, + const size_t stride) { + *(glm::vec2*)((uint8_t*)uvs + 0 * stride) = bounds.TopLeft / dimensions; + *(glm::vec2*)((uint8_t*)uvs + 1 * stride) = bounds.BottomLeft / dimensions; + *(glm::vec2*)((uint8_t*)uvs + 2 * stride) = bounds.BottomRight / dimensions; + *(glm::vec2*)((uint8_t*)uvs + 3 * stride) = bounds.TopRight / dimensions; +} + } // namespace Impacto diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index ffd52a734..5ff790643 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -17,6 +17,13 @@ enum class RendererOutlineMode { None, BottomRight, Full }; constexpr inline int MaxFramebuffers = 10; +struct VertexBufferSprites { + glm::vec2 Position = {0.0f, 0.0f}; + glm::vec2 UV = {0.0f, 0.0f}; + glm::vec4 Tint = glm::vec4(1.0f); + glm::vec2 MaskUV = {0.0f, 0.0f}; +}; + class BaseRenderer { public: virtual void Init() = 0; @@ -44,51 +51,204 @@ class BaseRenderer { virtual void FreeTexture(uint32_t id) = 0; virtual YUVFrame* CreateYUVFrame(float width, float height) = 0; - void DrawSprite(Sprite const& sprite, RectF const& dest, - glm::vec4 tint = glm::vec4(1.0), float angle = 0.0f, - bool inverted = false); - void DrawSprite(Sprite const& sprite, glm::vec2 topLeft, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool inverted = false); - virtual void DrawSprite(Sprite const& sprite, CornersQuad const& dest, - std::array const& tints, - float angle = 0.0f, bool inverted = false) = 0; - - virtual void DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), - float angle = 0.0f, bool inverted = false) = 0; - - virtual void DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, - int width, int height, - glm::vec4 tint = glm::vec4(1.0), - bool inverted = false, - bool disableBlend = false) = 0; - - virtual void DrawRect(RectF const& dest, glm::vec4 color, - float angle = 0.0f) = 0; - - virtual void DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted = false, + virtual void DrawSprite(const Sprite& sprite, const CornersQuad& dest, + glm::mat4 transformation, + std::span tints, + bool inverted = false, bool disableBlend = false, + bool textureWrapRepeat = false) = 0; + + void DrawSprite(const Sprite& sprite, const CornersQuad& dest, + glm::mat4 transformation = glm::mat4(1.0f), + glm::vec4 tint = glm::vec4(1.0f), bool inverted = false, + bool disableBlend = false) { + DrawSprite(sprite, dest, transformation, + std::array{tint, tint, tint, tint}, inverted, + disableBlend); + } + void DrawSprite(const Sprite& sprite, const CornersQuad& dest, glm::vec4 tint, + bool inverted = false, bool disableBlend = false) { + DrawSprite(sprite, dest, glm::mat4(1.0f), tint, inverted, disableBlend); + } + void DrawSprite(const Sprite& sprite, glm::mat4 transformation, + glm::vec4 tint = glm::vec4(1.0f), bool inverted = false, + bool disableBlend = false) { + DrawSprite(sprite, sprite.ScaledBounds(), transformation, tint, inverted, + disableBlend); + } + void DrawSprite(const Sprite& sprite, glm::vec2 topLeft, + glm::vec4 tint = glm::vec4(1.0f), bool inverted = false, + bool disableBlend = false) { + DrawSprite(sprite, sprite.ScaledBounds().Translate(topLeft), + glm::mat4(1.0f), tint, inverted, disableBlend); + } + + virtual void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, + bool isInverted = false, bool isSameTexture = false) = 0; - virtual void DrawMaskedSpriteOffset( - const Sprite& sprite, const Sprite& mask, glm::vec2 pos, glm::vec2 origin, - int alpha, int fadeRange, glm::vec4 tint = glm::vec4(1.0f), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool spriteInverted = false, bool maskInverted = false, - bool isSameTexture = false) { - DrawMaskedSprite( - sprite, mask, - {pos.x, pos.y, sprite.ScaledWidth(), sprite.ScaledHeight()}, tint, - alpha, fadeRange, maskInverted, isSameTexture); + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, int alpha, int fadeRange, + glm::mat4 spriteTransformation = glm::mat4(1.0f), + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, bool isSameTexture = false) { + DrawMaskedSprite(sprite, mask, spriteDest, sprite.ScaledBounds(), alpha, + fadeRange, spriteTransformation, glm::mat4(1.0f), + std::array{tint, tint, tint, tint}, + isInverted, isSameTexture); + } + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, bool isSameTexture = false) { + DrawMaskedSprite(sprite, mask, sprite.ScaledBounds(), sprite.ScaledBounds(), + alpha, fadeRange, spriteTransformation, maskTransformation, + std::array{tint, tint, tint, tint}, + isInverted, isSameTexture); + } + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, bool isSameTexture = false) { + DrawMaskedSprite(sprite, mask, alpha, fadeRange, spriteTransformation, + glm::mat4(1.0f), tint, isInverted, isSameTexture); + } + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, int alpha, + int fadeRange, glm::vec2 spriteTopLeft, + glm::vec2 maskTopLeft, glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, bool isSameTexture = false) { + DrawMaskedSprite(sprite, mask, + sprite.ScaledBounds().Translate(spriteTopLeft), + sprite.ScaledBounds().Translate(maskTopLeft), alpha, + fadeRange, glm::mat4(1.0f), glm::mat4(1.0f), + std::array{tint, tint, tint, tint}, + isInverted, isSameTexture); + } + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, int alpha, + int fadeRange, glm::vec2 spriteTopLeft, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, bool isSameTexture = false) { + DrawMaskedSprite(sprite, mask, alpha, fadeRange, spriteTopLeft, + {0.0f, 0.0f}, tint, isInverted, isSameTexture); } + virtual void DrawMaskedSpriteOverlay( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, int fadeRange, + glm::mat4 spriteTransformation, glm::mat4 maskTransformation, + std::span tints, bool isInverted = false, + bool useMaskAlpha = true) = 0; + + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, spriteDest, maskDest, alpha, + fadeRange, spriteTransformation, glm::mat4(1.0f), + std::array{tint, tint, tint, tint}, + isInverted, useMaskAlpha); + } + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, int alpha, + int fadeRange, + glm::mat4 spriteTransformation = glm::mat4(1.0f), + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, spriteDest, mask.ScaledBounds(), + alpha, fadeRange, spriteTransformation, + glm::mat4(1.0f), + std::array{tint, tint, tint, tint}, + isInverted, useMaskAlpha); + } + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + int alpha, int fadeRange, + glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, sprite.ScaledBounds(), + mask.ScaledBounds(), alpha, fadeRange, + spriteTransformation, maskTransformation, + std::array{tint, tint, tint, tint}, + isInverted, useMaskAlpha); + } + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + int alpha, int fadeRange, + glm::mat4 spriteTransformation, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, alpha, fadeRange, + spriteTransformation, glm::mat4(1.0f), tint, + isInverted, useMaskAlpha); + } + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + int alpha, int fadeRange, + glm::vec2 spriteTopLeft, glm::vec2 maskTopLeft, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, + sprite.ScaledBounds().Translate(spriteTopLeft), + mask.ScaledBounds().Translate(maskTopLeft), alpha, + fadeRange, glm::mat4(1.0f), glm::mat4(1.0f), + std::array{tint, tint, tint, tint}, + isInverted, useMaskAlpha); + } + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + int alpha, int fadeRange, + glm::vec2 spriteTopLeft, + glm::vec4 tint = glm::vec4(1.0f), + bool isInverted = false, + bool useMaskAlpha = true) { + DrawMaskedSpriteOverlay(sprite, mask, alpha, fadeRange, spriteTopLeft, + {0.0f, 0.0f}, tint, isInverted, useMaskAlpha); + } + + virtual void DrawVertices(const SpriteSheet& sheet, const SpriteSheet* mask, + std::span vertices, + std::span indices, + glm::mat4 transformation = glm::mat4(1.0f), + bool inverted = false) = 0; + + virtual void DrawVertices(const SpriteSheet& sheet, + std::span vertices, + std::span indices, + glm::mat4 transformation = glm::mat4(1.0f), + bool inverted = false) { + DrawVertices(sheet, nullptr, vertices, indices, transformation, inverted); + } + + void DrawVertices(const SpriteSheet& sheet, const SpriteSheet* mask, + std::span vertices, + std::span indices, glm::vec2 offset, + bool inverted = false) { + DrawVertices(sheet, mask, vertices, indices, + glm::translate(glm::mat4(1.0f), glm::vec3(offset, 0.0f)), + inverted); + } + + void DrawVertices(const SpriteSheet& sheet, + std::span vertices, + std::span indices, glm::vec2 offset, + bool inverted = false) { + DrawVertices(sheet, vertices, indices, + glm::translate(glm::mat4(1.0f), glm::vec3(offset, 0.0f)), + inverted); + } + + void DrawQuad(const CornersQuad& dest, glm::vec4 color); + void DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, glm::vec2 topLeft, glm::vec4 tint, int alpha, int fadeRange, float effectCt, @@ -100,27 +260,6 @@ class BaseRenderer { virtual void DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, const RectF& dest, float alpha) = 0; - virtual void DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, - int alpha, int fadeRange, - bool isInverted = false, - float angle = 0.0f, - bool useMaskAlpha = true) = 0; - - virtual void DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - glm::vec4 tint = glm::vec4(1.0f), - bool inverted = false) = 0; - void DrawSprite3DRotated(Sprite const& sprite, glm::vec2 topLeft, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 tint = glm::vec4(1.0f), - glm::vec2 scale = glm::vec2(1.0f), - bool inverted = false); - virtual void DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) = 0; - void DrawProcessedText_BasicFont(std::span text, BasicFont* font, float opacity, RendererOutlineMode outlineMode, @@ -137,29 +276,16 @@ class BaseRenderer { std::span text, Font* font, float opacity = 1.0f, RendererOutlineMode outlineMode = RendererOutlineMode::None, - bool smoothstepGlyphOpacity = true, SpriteSheet* maskedSheet = 0); + bool smoothstepGlyphOpacity = true, SpriteSheet* maskedSheet = nullptr); void DrawProcessedText( std::span text, Font* font, float opacity, float outlineOpacity, RendererOutlineMode outlineMode = RendererOutlineMode::None, - bool smoothstepGlyphOpacity = true, SpriteSheet* maskedSheet = 0); - - virtual void DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, - int indicesCount, uint16_t* mvlIndices, - bool inverted = false, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0f)) = 0; - - virtual void DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint = glm::vec4(1.0), - float angle = 0.0f, - bool alphaVideo = false) = 0; - void DrawVideoTexture(YUVFrame* tex, glm::vec2 topLeft, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool alphaVideo = false); + bool smoothstepGlyphOpacity = true, SpriteSheet* maskedSheet = nullptr); + + virtual void DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + glm::vec4 tint, bool alphaVideo = false) = 0; virtual void CaptureScreencap(Sprite& sprite) = 0; @@ -177,12 +303,24 @@ class BaseRenderer { protected: virtual void Flush() = 0; - struct VertexBufferSprites { - glm::vec2 Position; - glm::vec2 UV; - glm::vec4 Tint; - glm::vec2 MaskUV; - }; + static void QuadSetUV(CornersQuad spriteBounds, glm::vec2 designDimensions, + glm::vec2* uvs, size_t stride); + + static void QuadSetUVFlipped(CornersQuad spriteBounds, + glm::vec2 designDimensions, glm::vec2* uvs, + size_t stride) { + QuadSetUV({spriteBounds.BottomLeft, spriteBounds.TopLeft, + spriteBounds.BottomRight, spriteBounds.TopRight}, + designDimensions, uvs, stride); + } + + void QuadSetPosition(CornersQuad destQuad, glm::vec2* positions, int stride); + + virtual glm::vec2 DesignToNDC(glm::vec2 designCoord) const { + return designCoord; + } + + Sprite RectSprite; }; inline BaseRenderer* Renderer; diff --git a/src/renderer/vulkan/pipeline.cpp b/src/renderer/vulkan/pipeline.cpp index 9ad9f024f..696dcc68c 100644 --- a/src/renderer/vulkan/pipeline.cpp +++ b/src/renderer/vulkan/pipeline.cpp @@ -23,8 +23,7 @@ Pipeline::Pipeline(VkDevice device, VkRenderPass renderPass) { rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.cullMode = VK_CULL_MODE_NONE; rasterizer.depthBiasEnable = VK_FALSE; RasterizerInfo = rasterizer; diff --git a/src/renderer/vulkan/renderer.cpp b/src/renderer/vulkan/renderer.cpp index 32eb07f9b..7dd730b02 100644 --- a/src/renderer/vulkan/renderer.cpp +++ b/src/renderer/vulkan/renderer.cpp @@ -18,7 +18,7 @@ namespace Vulkan { VkVertexInputBindingDescription Renderer::GetBindingDescription() { VkVertexInputBindingDescription bindingDescription{}; bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Renderer::VertexBufferSprites); + bindingDescription.stride = sizeof(VertexBufferSprites); bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return bindingDescription; @@ -1132,146 +1132,11 @@ YUVFrame* Renderer::CreateYUVFrame(float width, float height) { return (YUVFrame*)VideoFrameInternal; } -void Renderer::DrawRect(RectF const& dest, glm::vec4 color, float angle) { - BaseRenderer::DrawSprite(RectSprite, dest, color, angle); -} - -void Renderer::DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - glm::vec4 tint, bool inverted) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite3DRotated() called before BeginFrame()\n"); - return; - } - - if (Textures.count(sprite.Sheet.Texture) == 0) return; - - // Are we in sprite mode? - if (inverted) - EnsureMode(PipelineSpriteInverted); - else - EnsureMode(PipelineSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - MakeQuad(); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferOffset + - VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition3DRotated(dest, depth, vanishingPoint, stayInScreen, rot, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) { - DrawSprite3DRotated(RectSprite, dest, depth, vanishingPoint, stayInScreen, - rot, color); -} - -void Renderer::DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, - int indicesCount, uint16_t* mvlIndices, - bool inverted, glm::vec4 tint, - glm::vec2 scale) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawCharacterMvl() called before BeginFrame()\n"); - return; - } - - if (Textures.count(sprite.Sheet.Texture) == 0) return; - - // Draw just the character with this since we need to rebind the index buffer - // anyway... - Flush(); - - // Are we in sprite mode? - if (inverted) - EnsureMode(PipelineSpriteInverted); - else - EnsureMode(PipelineSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferOffset + - VertexBufferFill); - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); - - IndexBufferFill += indicesCount; - - int indexBufferOffset = IndexBufferOffset / sizeof(uint16_t); - memcpy(IndexBuffer + indexBufferOffset, mvlIndices, - indicesCount * sizeof(mvlIndices[0])); - - for (int i = 0; i < verticesCount; i += 4) { - glm::vec2 pos = glm::vec2(mvlVertices[i * 5], mvlVertices[i * 5 + 1]); - (void)pos; // Currently unused - glm::vec2 bottomLeftV = - glm::vec2(mvlVertices[i * 5], mvlVertices[i * 5 + 1]); - glm::vec2 topLeftV = - glm::vec2(mvlVertices[(i + 1) * 5], mvlVertices[(i + 1) * 5 + 1]); - glm::vec2 topRightV = - glm::vec2(mvlVertices[(i + 2) * 5], mvlVertices[(i + 2) * 5 + 1]); - glm::vec2 bottomRightV = - glm::vec2(mvlVertices[(i + 3) * 5], mvlVertices[(i + 3) * 5 + 1]); - - glm::vec2 bottomLeftUV = - glm::vec2(mvlVertices[i * 5 + 3], mvlVertices[i * 5 + 4]); - glm::vec2 topLeftUV = - glm::vec2(mvlVertices[(i + 1) * 5 + 3], mvlVertices[(i + 1) * 5 + 4]); - glm::vec2 topRightUV = - glm::vec2(mvlVertices[(i + 2) * 5 + 3], mvlVertices[(i + 2) * 5 + 4]); - glm::vec2 bottomRightUV = - glm::vec2(mvlVertices[(i + 3) * 5 + 3], mvlVertices[(i + 3) * 5 + 4]); - - bottomLeftV *= scale; - bottomLeftV += topLeft; - topLeftV *= scale; - topLeftV += topLeft; - topRightV *= scale; - topRightV += topLeft; - bottomRightV *= scale; - bottomRightV += topLeft; - - // top-left - vertices[i].Position = DesignToNDCNonFlipped(topLeftV); - vertices[i].UV = topLeftUV; - vertices[i].Tint = tint; - // bottom-left - vertices[i + 1].Position = DesignToNDCNonFlipped(bottomLeftV); - vertices[i + 1].UV = bottomLeftUV; - vertices[i + 1].Tint = tint; - // bottom-right - vertices[i + 2].Position = DesignToNDCNonFlipped(bottomRightV); - vertices[i + 2].UV = bottomRightUV; - vertices[i + 2].Tint = tint; - // top-right - vertices[i + 3].Position = DesignToNDCNonFlipped(topRightV); - vertices[i + 3].UV = topRightUV; - vertices[i + 3].Tint = tint; - } - - Flush(); -} - -void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, - std::array const& tints, float angle, - bool inverted) { +void Renderer::DrawSprite(const Sprite& sprite, const CornersQuad& dest, + const glm::mat4 transformation, + const std::span tints, + const bool inverted, const bool disableBlend, + const bool textureWrapRepeat) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawSprite() called before BeginFrame()\n"); @@ -1284,6 +1149,8 @@ void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, // Are we in sprite mode? if (inverted) EnsureMode(PipelineSpriteInverted); + else if (disableBlend) + EnsureMode(PipelineSpriteNoBlending); else EnsureMode(PipelineSprite); @@ -1298,81 +1165,53 @@ void Renderer::DrawSprite(Sprite const& sprite, CornersQuad const& dest, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetPosition(CornersQuad(dest).Transform(transformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 centerSprite, glm::vec4 tint, - glm::vec2 scale, float angle, bool inverted) { +void Renderer::DrawMaskedSprite( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + const glm::mat4 spriteTransformation, const glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool isSameTexture) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawSprite() called before BeginFrame()\n"); + "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); return; } - if (!sprite.Sheet.Texture) return; - if (Textures.count(sprite.Sheet.Texture) == 0) return; - - // Are we in sprite mode? - if (inverted) - EnsureMode(PipelineSpriteInverted); - else - EnsureMode(PipelineSprite); - - // Do we have the texture assigned? - EnsureTextureBound(sprite.Sheet.Texture); - - // OK, all good, make quad - MakeQuad(); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferOffset + - VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - - QuadSetPositionOffset(sprite.Bounds, topLeft, centerSprite, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, - bool isSameTexture) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); - return; - } + Flush(); if (Textures.count(sprite.Sheet.Texture) == 0 || Textures.count(mask.Sheet.Texture) == 0) return; - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; + alpha = std::clamp(alpha, 0, fadeRange + 256); + const float alphaRange = 256.0f / fadeRange; + const float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - Flush(); - EnsureMode(PipelineMaskedSprite, false); + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.pNext = nullptr; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; VkDescriptorImageInfo imageBufferInfo[2]; imageBufferInfo[0].sampler = Sampler; imageBufferInfo[0].imageView = Textures[sprite.Sheet.Texture].ImageView; imageBufferInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageBufferInfo[1].sampler = Sampler; + + vkCreateSampler(Device, &samplerInfo, nullptr, &imageBufferInfo[1].sampler); imageBufferInfo[1].imageView = Textures[mask.Sheet.Texture].ImageView; imageBufferInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -1384,10 +1223,10 @@ void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSet.pImageInfo = imageBufferInfo; + EnsureMode(PipelineMaskedSprite, true); vkCmdPushDescriptorSetKHR( CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, - PipelineMaskedSprite->PipelineLayout, 0, 1, &writeDescriptorSet); - + CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); SpritePushConstants constants = {}; constants.Alpha = glm::vec2(alphaRange, constAlpha); constants.IsInverted = isInverted; @@ -1404,50 +1243,57 @@ void Renderer::DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(CornersQuad(maskDest).Transform(maskTransformation), + sprite.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(CornersQuad(spriteDest).Transform(spriteTransformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - const glm::vec2 pos, - const glm::vec2 origin, int alpha, - const int fadeRange, const glm::vec4 tint, - const glm::vec2 scale, const float angle, - const bool spriteInverted, - const bool maskInverted, - const bool isSameTexture) { +void Renderer::DrawMaskedSpriteOverlay( + const Sprite& sprite, const Sprite& mask, const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, const int fadeRange, + const glm::mat4 spriteTransformation, const glm::mat4 maskTransformation, + const std::span tints, const bool isInverted, + const bool useMaskAlpha) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSprite() called before BeginFrame()\n"); + "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); return; } + if (sprite.Sheet.IsScreenCap) Flush(); + if (Textures.count(sprite.Sheet.Texture) == 0 || Textures.count(mask.Sheet.Texture) == 0) return; - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; + alpha = std::clamp(alpha, 0, fadeRange + 256); + const float alphaRange = 256.0f / fadeRange; + const float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - Flush(); - // TODO: handle inverted sprites - EnsureMode(PipelineMaskedSprite, false); + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.pNext = nullptr; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; VkDescriptorImageInfo imageBufferInfo[2]; imageBufferInfo[0].sampler = Sampler; imageBufferInfo[0].imageView = Textures[sprite.Sheet.Texture].ImageView; imageBufferInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageBufferInfo[1].sampler = Sampler; + + vkCreateSampler(Device, &samplerInfo, nullptr, &imageBufferInfo[1].sampler); imageBufferInfo[1].imageView = Textures[mask.Sheet.Texture].ImageView; imageBufferInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -1459,17 +1305,33 @@ void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSet.pImageInfo = imageBufferInfo; - vkCmdPushDescriptorSetKHR( - CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, - PipelineMaskedSprite->PipelineLayout, 0, 1, &writeDescriptorSet); + if (useMaskAlpha) { + EnsureMode(PipelineMaskedSprite, true); + vkCmdPushDescriptorSetKHR( + CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, + CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); + SpritePushConstants constants = {}; + constants.Alpha = glm::vec2(alphaRange, constAlpha); + constants.IsInverted = isInverted; + constants.IsSameTexture = false; + vkCmdPushConstants(CommandBuffers[CurrentFrameIndex], + CurrentPipeline->PipelineLayout, + VK_SHADER_STAGE_FRAGMENT_BIT, 0, + sizeof(SpritePushConstants), &constants); - SpritePushConstants constants = {}; - constants.Alpha = glm::vec2(alphaRange, constAlpha); - constants.IsInverted = maskInverted; - constants.IsSameTexture = isSameTexture; - vkCmdPushConstants( - CommandBuffers[CurrentFrameIndex], CurrentPipeline->PipelineLayout, - VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(SpritePushConstants), &constants); + } else { + EnsureMode(PipelineMaskedSpriteNoAlpha, true); + vkCmdPushDescriptorSetKHR( + CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, + CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); + MaskedNoAlphaPushConstants constants = {}; + constants.Alpha = glm::vec2(alphaRange, constAlpha); + constants.IsInverted = isInverted; + vkCmdPushConstants(CommandBuffers[CurrentFrameIndex], + CurrentPipeline->PipelineLayout, + VK_SHADER_STAGE_FRAGMENT_BIT, 0, + sizeof(MaskedNoAlphaPushConstants), &constants); + } // OK, all good, make quad MakeQuad(); @@ -1479,26 +1341,24 @@ void Renderer::DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(sprite.Bounds, sprite.Bounds.Width, sprite.Bounds.Height, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(CornersQuad(maskDest).Transform(maskTransformation), + mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPositionOffset(sprite.Bounds, pos, origin, scale, angle, - (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - QuadSetPositionOffset({0.0f, 0.0f, 1.0f, 1.0f}, pos, origin, scale, angle, - (uintptr_t)&vertices[0].MaskUV, - sizeof(VertexBufferSprites), false); + QuadSetPosition(CornersQuad(spriteDest).Transform(spriteTransformation), + &vertices[0].Position, sizeof(VertexBufferSprites)); - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; + for (int i = 0; i < 4; i++) vertices[i].Tint = tints[i]; } -void Renderer::DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, - int width, int height, glm::vec4 tint, - bool inverted, bool disableBlend) { +void Renderer::DrawVertices(const SpriteSheet& sheet, + const SpriteSheet* const mask, + const std::span vertices, + const std::span indices, + const glm::mat4 transformation, + const bool inverted) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawVertices() called before BeginFrame()\n"); @@ -1506,91 +1366,81 @@ void Renderer::DrawVertices(SpriteSheet const& sheet, } if (Textures.count(sheet.Texture) == 0) return; - const int verticesCount = sheetPositions.size(); - - if (verticesCount != displayPositions.size()) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawVertices() called with mismatched vertices count\n"); - return; - } + // The index buffer needs to be flushed Flush(); - VkSamplerCreateInfo samplerInfo = {}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.pNext = nullptr; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = 16; - - VkDescriptorImageInfo imageBufferInfo[2]; - imageBufferInfo[0].sampler = Sampler; - imageBufferInfo[0].imageView = Textures[sheet.Texture].ImageView; - imageBufferInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + if (mask != nullptr) { + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.pNext = nullptr; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; + + VkDescriptorImageInfo imageBufferInfo[2]; + imageBufferInfo[0].sampler = Sampler; + imageBufferInfo[0].imageView = Textures[sheet.Texture].ImageView; + imageBufferInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + vkCreateSampler(Device, &samplerInfo, nullptr, &imageBufferInfo[1].sampler); + imageBufferInfo[1].imageView = Textures[mask->Texture].ImageView; + imageBufferInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCreateSampler(Device, &samplerInfo, nullptr, &imageBufferInfo[1].sampler); - imageBufferInfo[1].imageView = Textures[sheet.Texture].ImageView; - imageBufferInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.dstSet = 0; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.descriptorCount = 2; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.pImageInfo = imageBufferInfo; + VkWriteDescriptorSet writeDescriptorSet{}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.dstSet = 0; + writeDescriptorSet.dstBinding = 0; + writeDescriptorSet.descriptorCount = 2; + writeDescriptorSet.descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.pImageInfo = imageBufferInfo; - // Are we in sprite mode? - if (inverted) - EnsureMode(PipelineSpriteInverted); - else if (disableBlend) - EnsureMode(PipelineSpriteNoBlending); - else - EnsureMode(PipelineSprite); + EnsureMode(PipelineMaskedSpriteNoAlpha); + vkCmdPushDescriptorSetKHR( + CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, + CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); + MaskedNoAlphaPushConstants constants = {}; + constants.Alpha = glm::vec2(1.0f, 0.0f); + constants.IsInverted = inverted; + vkCmdPushConstants(CommandBuffers[CurrentFrameIndex], + CurrentPipeline->PipelineLayout, + VK_SHADER_STAGE_FRAGMENT_BIT, 0, + sizeof(MaskedNoAlphaPushConstants), &constants); + } else { + if (inverted) + EnsureMode(PipelineSpriteInverted); + else + EnsureMode(PipelineSprite); - vkCmdPushDescriptorSetKHR( - CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, - CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); + EnsureTextureBound(sheet.Texture); + } - VertexBufferSprites* vertices = + // Push vertices + VertexBufferSprites* vertexBuffer = (VertexBufferSprites*)(VertexBuffer + VertexBufferOffset + VertexBufferFill); - - VertexBufferFill += verticesCount * sizeof(VertexBufferSprites); - + VertexBufferFill += vertices.size_bytes(); + + const auto vertexInfoToNDC = [this, + transformation](VertexBufferSprites info) { + info.Position = DesignToNDC( + glm::vec2(transformation * glm::vec4(info.Position, 0.0f, 1.0f))); + return info; + }; + std::transform(vertices.begin(), vertices.end(), vertexBuffer, + vertexInfoToNDC); + + // Push indices + IndexBufferFill += indices.size(); int indexBufferOffset = IndexBufferOffset / sizeof(uint16_t); + std::copy(indices.begin(), indices.end(), IndexBuffer + indexBufferOffset); - // Generate indices for triangles - for (int y = 0; y < height - 1; y++) { - for (int x = 0; x < width - 1; x++) { - uint16_t v0 = y * width + x; - uint16_t v1 = y * width + (x + 1); - uint16_t v2 = (y + 1) * width + x; - uint16_t v3 = (y + 1) * width + (x + 1); - - // First triangle - for (auto v : {v1, v0, v2}) { - IndexBuffer[indexBufferOffset + IndexBufferFill++] = v; - } - // Second triangle - for (auto v : {v3, v1, v2}) { - IndexBuffer[indexBufferOffset + IndexBufferFill++] = v; - } - } - } - assert(IndexBufferFill == (width - 1) * (height - 1) * 6); - - for (int i = 0; i < verticesCount; i++) { - vertices[i].Position = DesignToNDCNonFlipped(displayPositions[i]); - vertices[i].Tint = tint; - glm::vec2 uv = - sheetPositions[i] / glm::vec2(sheet.DesignWidth, sheet.DesignHeight); - vertices[i].UV = uv; - } + // Flush again and bind back our buffer Flush(); } @@ -1649,111 +1499,12 @@ void Renderer::DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(mask.Bounds, mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); - - for (int i = 0; i < 4; i++) vertices[i].Tint = tint; -} - -void Renderer::DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, - int alpha, int fadeRange, - bool isInverted, float angle, - bool useMaskAlpha) { - if (!Drawing) { - ImpLog(LogLevel::Error, LogChannel::Render, - "Renderer->DrawMaskedSpriteOverlay() called before BeginFrame()\n"); - return; - } - - if (sprite.Sheet.IsScreenCap) Flush(); - - if (Textures.count(sprite.Sheet.Texture) == 0 || - Textures.count(mask.Sheet.Texture) == 0) - return; - - if (alpha < 0) alpha = 0; - if (alpha > fadeRange + 256) alpha = fadeRange + 256; - - float alphaRange = 256.0f / fadeRange; - float constAlpha = ((255.0f - alpha) * alphaRange) / 255.0f; - - VkSamplerCreateInfo samplerInfo = {}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.pNext = nullptr; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = 16; - - VkDescriptorImageInfo imageBufferInfo[2]; - imageBufferInfo[0].sampler = Sampler; - imageBufferInfo[0].imageView = Textures[sprite.Sheet.Texture].ImageView; - imageBufferInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - vkCreateSampler(Device, &samplerInfo, nullptr, &imageBufferInfo[1].sampler); - imageBufferInfo[1].imageView = Textures[mask.Sheet.Texture].ImageView; - imageBufferInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.dstSet = 0; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.descriptorCount = 2; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.pImageInfo = imageBufferInfo; - - if (useMaskAlpha) { - EnsureMode(PipelineMaskedSprite, true); - vkCmdPushDescriptorSetKHR( - CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, - CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); - SpritePushConstants constants = {}; - constants.Alpha = glm::vec2(alphaRange, constAlpha); - constants.IsInverted = isInverted; - constants.IsSameTexture = false; - vkCmdPushConstants(CommandBuffers[CurrentFrameIndex], - CurrentPipeline->PipelineLayout, - VK_SHADER_STAGE_FRAGMENT_BIT, 0, - sizeof(SpritePushConstants), &constants); - - } else { - EnsureMode(PipelineMaskedSpriteNoAlpha, true); - vkCmdPushDescriptorSetKHR( - CommandBuffers[CurrentFrameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, - CurrentPipeline->PipelineLayout, 0, 1, &writeDescriptorSet); - MaskedNoAlphaPushConstants constants = {}; - constants.Alpha = glm::vec2(alphaRange, constAlpha); - constants.IsInverted = isInverted; - vkCmdPushConstants(CommandBuffers[CurrentFrameIndex], - CurrentPipeline->PipelineLayout, - VK_SHADER_STAGE_FRAGMENT_BIT, 0, - sizeof(MaskedNoAlphaPushConstants), &constants); - } - - // OK, all good, make quad - MakeQuad(); - - VertexBufferSprites* vertices = - (VertexBufferSprites*)(VertexBuffer + VertexBufferOffset + - VertexBufferFill); - VertexBufferFill += 4 * sizeof(VertexBufferSprites); - - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites), angle); - - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tint; } @@ -1811,13 +1562,12 @@ void Renderer::DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(sprite.Bounds, sprite.Sheet.DesignWidth, sprite.Sheet.DesignHeight, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetUV(mask.Bounds, mask.Sheet.DesignWidth, mask.Sheet.DesignHeight, - (uintptr_t)&vertices[0].MaskUV, sizeof(VertexBufferSprites)); + QuadSetUV(sprite.Bounds, sprite.Sheet.GetDimensions(), &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetUV(mask.Bounds, mask.Sheet.GetDimensions(), &vertices[0].MaskUV, + sizeof(VertexBufferSprites)); - QuadSetPosition(dest, 0.0f, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); } inline void Renderer::MakeQuad() { @@ -1841,196 +1591,6 @@ inline void Renderer::MakeQuad() { } } -inline void Renderer::QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle) { - float topUV = (spriteBounds.Y / designHeight); - float leftUV = (spriteBounds.X / designWidth); - float bottomUV = ((spriteBounds.Y + spriteBounds.Height) / designHeight); - float rightUV = ((spriteBounds.X + spriteBounds.Width) / designWidth); - - glm::vec2 bottomLeft(leftUV, bottomUV); - glm::vec2 topLeft(leftUV, topUV); - glm::vec2 topRight(rightUV, topUV); - glm::vec2 bottomRight(rightUV, bottomUV); - - if (angle != 0.0f) { - glm::vec2 center = (bottomLeft + topRight) * 0.5f; // Center of the quad - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // top-left - *(glm::vec2*)(uvs + 0 * stride) = topLeft; - // bottom-left - *(glm::vec2*)(uvs + 1 * stride) = bottomLeft; - // bottom-right - *(glm::vec2*)(uvs + 2 * stride) = bottomRight; - // top-right - *(glm::vec2*)(uvs + 3 * stride) = topRight; -} - -inline void Renderer::QuadSetPositionOffset( - RectF const& spriteBounds, glm::vec2 displayXY, glm::vec2 displayOffset, - glm::vec2 scale, float angle, uintptr_t positions, int stride, bool toNDC) { - glm::vec2 topLeft = {0.0f, 0.0f}; - glm::vec2 bottomLeft = {0.0f, spriteBounds.Height}; - glm::vec2 topRight = {spriteBounds.Width, 0.0f}; - glm::vec2 bottomRight = {spriteBounds.Width, spriteBounds.Height}; - - // Translate to origin - topLeft -= displayOffset; - bottomLeft -= displayOffset; - topRight -= displayOffset; - bottomRight -= displayOffset; - - // Scale - bottomLeft *= scale; - topLeft *= scale; - topRight *= scale; - bottomRight *= scale; - - // Rotate - if (angle != 0.0f) { - const glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * bottomLeft; - topLeft = rot * topLeft; - topRight = rot * topRight; - bottomRight = rot * bottomRight; - } - - // Translate to the desired screen position - bottomLeft += displayOffset + displayXY; - topLeft += displayOffset + displayXY; - topRight += displayOffset + displayXY; - bottomRight += displayOffset + displayXY; - - if (toNDC) { - topLeft = DesignToNDCNonFlipped(topLeft); - bottomLeft = DesignToNDCNonFlipped(bottomLeft); - bottomRight = DesignToNDCNonFlipped(bottomRight); - topRight = DesignToNDCNonFlipped(topRight); - } - - // Store the transformed positions - *(glm::vec2*)(positions + 0 * stride) = topLeft; - *(glm::vec2*)(positions + 1 * stride) = bottomLeft; - *(glm::vec2*)(positions + 2 * stride) = bottomRight; - *(glm::vec2*)(positions + 3 * stride) = topRight; -} - -inline void Renderer::QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride) { - glm::vec2 bottomLeft = - glm::vec2(transformedQuad.X, transformedQuad.Y + transformedQuad.Height); - glm::vec2 topLeft = glm::vec2(transformedQuad.X, transformedQuad.Y); - glm::vec2 topRight = - glm::vec2(transformedQuad.X + transformedQuad.Width, transformedQuad.Y); - glm::vec2 bottomRight = glm::vec2(transformedQuad.X + transformedQuad.Width, - transformedQuad.Y + transformedQuad.Height); - - if (angle != 0.0f) { - glm::vec2 center = transformedQuad.Center(); - glm::mat2 rot = Rotate2D(angle); - - bottomLeft = rot * (bottomLeft - center) + center; - topLeft = rot * (topLeft - center) + center; - topRight = rot * (topRight - center) + center; - bottomRight = rot * (bottomRight - center) + center; - } - - // top-left - *(glm::vec2*)(positions + 0 * stride) = DesignToNDCNonFlipped(topLeft); - // bottom-left - *(glm::vec2*)(positions + 1 * stride) = DesignToNDCNonFlipped(bottomLeft); - // bottom-right - *(glm::vec2*)(positions + 2 * stride) = DesignToNDCNonFlipped(bottomRight); - // top-right - *(glm::vec2*)(positions + 3 * stride) = DesignToNDCNonFlipped(topRight); -} - -inline void Renderer::QuadSetPosition(CornersQuad destQuad, float angle, - uintptr_t positions, int stride) { - if (angle != 0.0f) { - glm::vec2 center = (destQuad.BottomLeft + destQuad.TopRight) * 0.5f; - glm::mat2 rot = Rotate2D(angle); - - destQuad.BottomLeft = rot * (destQuad.BottomLeft - center) + center; - destQuad.TopLeft = rot * (destQuad.TopLeft - center) + center; - destQuad.TopRight = rot * (destQuad.TopRight - center) + center; - destQuad.BottomRight = rot * (destQuad.BottomRight - center) + center; - } - - // bottom-left - *(glm::vec2*)(positions + 0 * stride) = - DesignToNDCNonFlipped(destQuad.TopLeft); - // top-left - *(glm::vec2*)(positions + 1 * stride) = - DesignToNDCNonFlipped(destQuad.BottomLeft); - // top-right - *(glm::vec2*)(positions + 2 * stride) = - DesignToNDCNonFlipped(destQuad.BottomRight); - // bottom-right - *(glm::vec2*)(positions + 3 * stride) = - DesignToNDCNonFlipped(destQuad.TopRight); -} - -void Renderer::QuadSetPosition3DRotated(RectF const& transformedQuad, - float depth, glm::vec2 vanishingPoint, - bool stayInScreen, glm::quat rot, - uintptr_t positions, int stride) { - float widthNormalized = transformedQuad.Width / (Profile::DesignWidth * 0.5f); - float heightNormalized = - transformedQuad.Height / (Profile::DesignHeight * 0.5f); - - glm::vec4 corners[4]{ - // top-left - {glm::vec2(-widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // top-right - {glm::vec2(widthNormalized / 2.0f, heightNormalized / 2.0f), 0, 1}, - // bottom-right - {glm::vec2(widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}, - // bottom-left - {glm::vec2(-widthNormalized / 2.0f, -heightNormalized / 2.0f), 0, 1}}; - - glm::mat4 transform = - glm::translate( - glm::mat4(1.0f), - glm::vec3(DesignToNDCNonFlipped(transformedQuad.Center()), 0)) * - glm::mat4_cast(rot); - - glm::vec4 vanishingPointNDC(DesignToNDCNonFlipped(vanishingPoint), 0, 0); - - for (int i = 0; i < 4; i++) { - corners[i] = transform * corners[i]; - } - - if (stayInScreen) { - float maxZ = 0.0f; - for (int i = 0; i < 4; i++) { - if (corners[i].z > maxZ) maxZ = corners[i].z; - } - for (int i = 0; i < 4; i++) { - corners[i].z -= maxZ; - } - } - - for (int i = 0; i < 4; i++) { - // perspective - corners[i] -= vanishingPointNDC; - corners[i].x *= (depth / (depth - corners[i].z)); - corners[i].y *= (depth / (depth - corners[i].z)); - corners[i] += vanishingPointNDC; - - *(glm::vec2*)(positions + i * stride) = corners[i]; - } -} - void Renderer::EnsureTextureBound(unsigned int texture) { if (CurrentTexture != texture) { ImpLogSlow(LogLevel::Trace, LogChannel::Render, @@ -2100,8 +1660,8 @@ void Renderer::Flush() { CurrentTexture = 0; } -void Renderer::DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint, float angle, bool alphaVideo) { +void Renderer::DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + const glm::vec4 tint, const bool alphaVideo) { if (!Drawing) { ImpLog(LogLevel::Error, LogChannel::Render, "Renderer->DrawVideoTexture() called before BeginFrame()\n"); @@ -2148,10 +1708,10 @@ void Renderer::DrawVideoTexture(YUVFrame* tex, RectF const& dest, VertexBufferFill); VertexBufferFill += 4 * sizeof(VertexBufferSprites); - QuadSetUV(RectF(0.0f, 0.0f, tex->Width, tex->Height), tex->Width, tex->Height, - (uintptr_t)&vertices[0].UV, sizeof(VertexBufferSprites)); - QuadSetPosition(dest, angle, (uintptr_t)&vertices[0].Position, - sizeof(VertexBufferSprites)); + QuadSetUV(RectF(0.0f, 0.0f, frame.Width, frame.Height), + {frame.Width, frame.Height}, &vertices[0].UV, + sizeof(VertexBufferSprites)); + QuadSetPosition(dest, &vertices[0].Position, sizeof(VertexBufferSprites)); for (int i = 0; i < 4; i++) vertices[i].Tint = tint; } @@ -2354,5 +1914,12 @@ void Renderer::DisableScissor() { } } +glm::vec2 Renderer::DesignToNDC(const glm::vec2 designCoord) const { + glm::vec2 result; + result.x = (designCoord.x / (Profile::DesignWidth * 0.5f)) - 1.0f; + result.y = (designCoord.y / (Profile::DesignHeight * 0.5f)) - 1.0f; + return result; +} + } // namespace Vulkan } // namespace Impacto diff --git a/src/renderer/vulkan/renderer.h b/src/renderer/vulkan/renderer.h index e6bcf0d8e..7599e24ff 100644 --- a/src/renderer/vulkan/renderer.h +++ b/src/renderer/vulkan/renderer.h @@ -62,66 +62,41 @@ class Renderer : public BaseRenderer { void FreeTexture(uint32_t id) override; YUVFrame* CreateYUVFrame(float width, float height) override; - void DrawSprite(Sprite const& sprite, CornersQuad const& dest, - const std::array& tints, float angle = 0.0f, - bool inverted = false) override; - - void DrawSpriteOffset(Sprite const& sprite, glm::vec2 topLeft, - glm::vec2 displayOffset, - glm::vec4 tint = glm::vec4(1.0), - glm::vec2 scale = glm::vec2(1.0), float angle = 0.0f, - bool inverted = false) override; - - void DrawVertices(SpriteSheet const& sheet, - std::span sheetPositions, - std::span displayPositions, int width, - int height, glm::vec4 tint = glm::vec4(1.0), - bool inverted = false, bool disableBlend = false) override; - - void DrawRect(RectF const& dest, glm::vec4 color, - float angle = 0.0f) override; - - void DrawMaskedSprite(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted = false, - bool isSameTexture = false) override; - - void DrawMaskedSpriteOffset(const Sprite& sprite, const Sprite& mask, - glm::vec2 pos, glm::vec2 origin, int alpha, - int fadeRange, glm::vec4 tint = glm::vec4(1.0f), - glm::vec2 scale = glm::vec2(1.0), - float angle = 0.0f, bool spriteInverted = false, - bool maskInverted = false, - bool isSameTexture = false) override; + void DrawSprite(const Sprite& sprite, const CornersQuad& dest, + glm::mat4 transformation, std::span tints, + bool inverted, bool disableBlend, + bool textureWrapRepeat) override; + + void DrawMaskedSprite(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, int fadeRange, + glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, bool isInverted, + bool isSameTexture) override; + + void DrawMaskedSpriteOverlay(const Sprite& sprite, const Sprite& mask, + const CornersQuad& spriteDest, + const CornersQuad& maskDest, int alpha, + int fadeRange, glm::mat4 spriteTransformation, + glm::mat4 maskTransformation, + std::span tints, + bool isInverted, bool useMaskAlpha) override; + + void DrawVertices(const SpriteSheet& sheet, const SpriteSheet* mask, + std::span vertices, + std::span indices, glm::mat4 transformation, + bool inverted) override; void DrawCCMessageBox(Sprite const& sprite, Sprite const& mask, RectF const& dest, glm::vec4 tint, int alpha, int fadeRange, float effectCt) override; - void DrawMaskedSpriteOverlay(Sprite const& sprite, Sprite const& mask, - RectF const& dest, glm::vec4 tint, int alpha, - int fadeRange, bool isInverted, float angle, - bool useMaskAlpha); - void DrawCHLCCMenuBackground(const Sprite& sprite, const Sprite& mask, const RectF& dest, float alpha) override; - void DrawSprite3DRotated(Sprite const& sprite, RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 tint = glm::vec4(1.0f), - bool inverted = false) override; - void DrawRect3DRotated(RectF const& dest, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, glm::vec4 color) override; - - void DrawCharacterMvl(Sprite const& sprite, glm::vec2 topLeft, - int verticesCount, float* mvlVertices, int indicesCount, - uint16_t* mvlIndices, bool inverted, glm::vec4 tint, - glm::vec2 scale) override; - - void DrawVideoTexture(YUVFrame* tex, RectF const& dest, - glm::vec4 tint = glm::vec4(1.0), float angle = 0.0f, - bool alphaVideo = false) override; + void DrawVideoTexture(const YUVFrame& frame, const RectF& dest, + glm::vec4 tint, bool alphaVideo) override; void CaptureScreencap(Sprite& sprite) override; @@ -158,23 +133,11 @@ class Renderer : public BaseRenderer { void EnsureTextureBound(unsigned int texture); void EnsureMode(Pipeline* pipeline, bool flush = true); - void Flush(); + void Flush() override; void MakeQuad(); - void QuadSetUV(RectF const& spriteBounds, float designWidth, - float designHeight, uintptr_t uvs, int stride, - float angle = 0.0f); - void QuadSetPositionOffset(RectF const& spriteBounds, glm::vec2 topLeftPos, - glm::vec2 displayOffset, glm::vec2 scale, - float angle, uintptr_t positions, int stride, - bool toNDC = true); - void QuadSetPosition(RectF const& transformedQuad, float angle, - uintptr_t positions, int stride); - void QuadSetPosition(CornersQuad destQuad, float angle, uintptr_t positions, - int stride); - void QuadSetPosition3DRotated(RectF const& transformedQuad, float depth, - glm::vec2 vanishingPoint, bool stayInScreen, - glm::quat rot, uintptr_t positions, int stride); + + glm::vec2 DesignToNDC(glm::vec2 designCoord) const override; bool Drawing = false; @@ -247,8 +210,6 @@ class Renderer : public BaseRenderer { int IndexBufferFill = 0; int IndexBufferOffset = 0; - Sprite RectSprite; - RectF PreviousScissorRect; static VkVertexInputBindingDescription GetBindingDescription(); diff --git a/src/scriptvars.h b/src/scriptvars.h index 76c8de13a..0e67451b1 100644 --- a/src/scriptvars.h +++ b/src/scriptvars.h @@ -80,9 +80,11 @@ V(SW_MASK2ALPHA_OFS) V(SW_MASK3ALPHA_OFS) V(SW_BGEFF1_OFSX) V(SW_BGEFF1_OFSY) -V(SW_BGEFF1_ALPHA_OFS) V(SW_BGEFF1_SIZE_OFS) +V(SW_BGEFF1_ROTX_OFS) +V(SW_BGEFF1_ROTY_OFS) V(SW_BGEFF1_ROTZ_OFS) +V(SW_BGEFF1_ALPHA_OFS) V(SW_BGEFF1_MASK_VERTEX1_OFSX) V(SW_BGEFF1_MASK_VERTEX1_OFSY) V(SW_MAINTHDP) @@ -346,6 +348,8 @@ V(SW_BGEFF1_PRI) V(SW_BGEFF1_MASK_TYPE) V(SW_BGEFF1_FADECT) V(SW_BGEFF1_MODE) +V(SW_BGEFF1_ROTX) +V(SW_BGEFF1_ROTY) V(SW_BGEFF1_ROTZ) V(SW_BGEFF1_ALPHA) V(SW_BGEFF1_MASKNO) diff --git a/src/shaders/opengl/CCMessageBoxSprite_frag.glsl b/src/shaders/opengl/CCMessageBoxSprite_frag.glsl index 3d51cbafd..516f5fbdb 100644 --- a/src/shaders/opengl/CCMessageBoxSprite_frag.glsl +++ b/src/shaders/opengl/CCMessageBoxSprite_frag.glsl @@ -17,7 +17,7 @@ void main() { float fa = 1.0 - alp.b; fa *= Alpha.r; fa -= Alpha.g; - fa = clamp(fa,0.0,1.0); + fa = clamp(fa, 0.0, 1.0); color.a *= fa; ea += Alpha.b; @@ -28,5 +28,5 @@ void main() { ga *= ea; ga = clamp(ga, 0.0, 1.0); color.a -= ga; - color.a = clamp(color.a,0.0,1.0); + color.a = clamp(color.a, 0.0, 1.0); } \ No newline at end of file diff --git a/src/shaders/opengl/CCMessageBoxSprite_vert.glsl b/src/shaders/opengl/CCMessageBoxSprite_vert.glsl index 60eff8035..30f694da7 100644 --- a/src/shaders/opengl/CCMessageBoxSprite_vert.glsl +++ b/src/shaders/opengl/CCMessageBoxSprite_vert.glsl @@ -7,8 +7,11 @@ out vec2 uv; out vec4 tint; out vec2 maskUV; +uniform mat4 Projection; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * vec4(Position, 0.0, 1.0); + uv = UV; tint = Tint; maskUV = MaskUV; diff --git a/src/shaders/opengl/CHLCCMenuBackground_frag.glsl b/src/shaders/opengl/CHLCCMenuBackground_frag.glsl index 2f218865e..a66108d1d 100644 --- a/src/shaders/opengl/CHLCCMenuBackground_frag.glsl +++ b/src/shaders/opengl/CHLCCMenuBackground_frag.glsl @@ -1,5 +1,3 @@ -uniform sampler2D ColorMap; -uniform sampler2D Mask; uniform float Alpha; in vec2 uv; @@ -7,6 +5,9 @@ in vec2 maskUV; out vec4 outColor; +uniform sampler2D ColorMap; +uniform sampler2D Mask; + float remapMiddle(float value, float newMiddle) { if (value < 0.5) { return mix(0.0, newMiddle, 2.0 * value); @@ -17,11 +18,11 @@ float remapMiddle(float value, float newMiddle) { void main() { vec4 color = texture(ColorMap, uv); - vec4 mask = texture(Mask, maskUV); + vec4 maskColor = texture(Mask, maskUV); outColor = vec4( - remapMiddle(color.r, mask.r), - remapMiddle(color.g, mask.g), - remapMiddle(color.b, mask.b), - mask.a * Alpha + remapMiddle(color.r, maskColor.r), + remapMiddle(color.g, maskColor.g), + remapMiddle(color.b, maskColor.b), + maskColor.a * Alpha ); } \ No newline at end of file diff --git a/src/shaders/opengl/CHLCCMenuBackground_vert.glsl b/src/shaders/opengl/CHLCCMenuBackground_vert.glsl index d8f3cc778..7ab5baa3a 100644 --- a/src/shaders/opengl/CHLCCMenuBackground_vert.glsl +++ b/src/shaders/opengl/CHLCCMenuBackground_vert.glsl @@ -5,8 +5,11 @@ layout (location = 3) in vec2 MaskUV; out vec2 uv; out vec2 maskUV; +uniform mat4 Projection; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * vec4(Position, 0.0, 1.0); + uv = UV; maskUV = MaskUV; } \ No newline at end of file diff --git a/src/shaders/opengl/MaskedSpriteNoAlpha_vert.glsl b/src/shaders/opengl/MaskedSpriteNoAlpha_vert.glsl index 60eff8035..f474ceefa 100644 --- a/src/shaders/opengl/MaskedSpriteNoAlpha_vert.glsl +++ b/src/shaders/opengl/MaskedSpriteNoAlpha_vert.glsl @@ -7,9 +7,14 @@ out vec2 uv; out vec4 tint; out vec2 maskUV; +uniform mat4 Projection; +uniform mat4 SpriteTransformation; +uniform mat4 MaskTransformation; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * SpriteTransformation * vec4(Position, 0.0, 1.0); + uv = UV; tint = Tint; - maskUV = MaskUV; + maskUV = vec2(MaskTransformation * vec4(MaskUV, 0.0, 1.0)); } \ No newline at end of file diff --git a/src/shaders/opengl/MaskedSprite_vert.glsl b/src/shaders/opengl/MaskedSprite_vert.glsl index 60eff8035..f474ceefa 100644 --- a/src/shaders/opengl/MaskedSprite_vert.glsl +++ b/src/shaders/opengl/MaskedSprite_vert.glsl @@ -7,9 +7,14 @@ out vec2 uv; out vec4 tint; out vec2 maskUV; +uniform mat4 Projection; +uniform mat4 SpriteTransformation; +uniform mat4 MaskTransformation; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * SpriteTransformation * vec4(Position, 0.0, 1.0); + uv = UV; tint = Tint; - maskUV = MaskUV; + maskUV = vec2(MaskTransformation * vec4(MaskUV, 0.0, 1.0)); } \ No newline at end of file diff --git a/src/shaders/opengl/Sprite_inverted_frag.glsl b/src/shaders/opengl/SpriteInverted_frag.glsl similarity index 100% rename from src/shaders/opengl/Sprite_inverted_frag.glsl rename to src/shaders/opengl/SpriteInverted_frag.glsl diff --git a/src/shaders/opengl/Sprite_inverted_vert.glsl b/src/shaders/opengl/SpriteInverted_vert.glsl similarity index 58% rename from src/shaders/opengl/Sprite_inverted_vert.glsl rename to src/shaders/opengl/SpriteInverted_vert.glsl index 3a11d26a8..4908c6f70 100644 --- a/src/shaders/opengl/Sprite_inverted_vert.glsl +++ b/src/shaders/opengl/SpriteInverted_vert.glsl @@ -5,8 +5,12 @@ layout(location = 2) in vec4 Tint; out vec2 uv; out vec4 tint; +uniform mat4 Projection; +uniform mat4 Transformation; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * Transformation * vec4(Position, 0.0, 1.0); + uv = UV; tint = Tint; } \ No newline at end of file diff --git a/src/shaders/opengl/Sprite_vert.glsl b/src/shaders/opengl/Sprite_vert.glsl index 3a11d26a8..4908c6f70 100644 --- a/src/shaders/opengl/Sprite_vert.glsl +++ b/src/shaders/opengl/Sprite_vert.glsl @@ -5,8 +5,12 @@ layout(location = 2) in vec4 Tint; out vec2 uv; out vec4 tint; +uniform mat4 Projection; +uniform mat4 Transformation; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * Transformation * vec4(Position, 0.0, 1.0); + uv = UV; tint = Tint; } \ No newline at end of file diff --git a/src/shaders/opengl/YUVFrame_vert.glsl b/src/shaders/opengl/YUVFrame_vert.glsl index 3a11d26a8..cdef1458d 100644 --- a/src/shaders/opengl/YUVFrame_vert.glsl +++ b/src/shaders/opengl/YUVFrame_vert.glsl @@ -5,8 +5,10 @@ layout(location = 2) in vec4 Tint; out vec2 uv; out vec4 tint; +uniform mat4 Projection; + void main() { - gl_Position = vec4(Position.x, Position.y, 0.0, 1.0); + gl_Position = Projection * vec4(Position, 0.0, 1.0); uv = UV; tint = Tint; } \ No newline at end of file diff --git a/src/spritesheet.h b/src/spritesheet.h index 925c85fd8..e7e976bf3 100644 --- a/src/spritesheet.h +++ b/src/spritesheet.h @@ -12,6 +12,8 @@ struct SpriteSheet { float DesignWidth = 0; float DesignHeight = 0; + glm::vec2 GetDimensions() const { return {DesignWidth, DesignHeight}; } + uint32_t Texture = 0; bool IsScreenCap = false; }; @@ -36,6 +38,17 @@ struct Sprite { void SetScaledHeight(float scaledHeight) { Bounds.Height = scaledHeight / BaseScale.y; } + RectF ScaledBounds() const { + return {0.0f, 0.0f, ScaledWidth(), ScaledHeight()}; + } + + glm::vec2 Center() const { return ScaledBounds().Center(); } + + RectF NormalizedBounds() const { + return RectF(Bounds).Scale( + glm::vec2(1.0f / Sheet.DesignWidth, 1.0f / Sheet.DesignHeight), + {0.0f, 0.0f}); + } }; } // namespace Impacto \ No newline at end of file diff --git a/src/ui/widgets/button.cpp b/src/ui/widgets/button.cpp index 5c5d2bbf9..0db60349e 100644 --- a/src/ui/widgets/button.cpp +++ b/src/ui/widgets/button.cpp @@ -47,10 +47,12 @@ void Button::UpdateInput() { void Button::Render() { if (HasFocus) { - Renderer->DrawSprite( - HighlightSprite, - glm::vec2(Bounds.X + HighlightOffset.x, Bounds.Y + HighlightOffset.y), - Tint, glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + const RectF dest = + HighlightSprite.ScaledBounds() + .Scale({Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f}, + {0.0f, 0.0f}) + .Translate(Bounds.GetPos() + HighlightOffset); + Renderer->DrawSprite(HighlightSprite, dest, Tint); } if (IsLocked) { diff --git a/src/ui/widgets/cc/backlogentry.cpp b/src/ui/widgets/cc/backlogentry.cpp index dbdff1dd6..512d9cf2d 100644 --- a/src/ui/widgets/cc/backlogentry.cpp +++ b/src/ui/widgets/cc/backlogentry.cpp @@ -21,8 +21,8 @@ void BacklogEntry::Render() { mask.Sheet = BacklogMaskSheet; mask.Bounds = bounds; - Renderer->DrawMaskedSpriteOverlay(VoiceIcon, mask, bounds, Tint, - Tint.a * 255, 256, false, 0, false); + Renderer->DrawMaskedSpriteOverlay(VoiceIcon, mask, bounds, Tint.a * 255, + 256, glm::mat4(1.0f), Tint, false, false); } if (BacklogPage->HasName) { diff --git a/src/ui/widgets/cclcc/optionsbinarybutton.cpp b/src/ui/widgets/cclcc/optionsbinarybutton.cpp index 7f35fa978..1d0e28233 100644 --- a/src/ui/widgets/cclcc/optionsbinarybutton.cpp +++ b/src/ui/widgets/cclcc/optionsbinarybutton.cpp @@ -46,13 +46,11 @@ void OptionsBinaryButton::Render() { highlightBounds.X = highlightPos.x; highlightBounds.Y = highlightPos.y; - Renderer->DrawRect(highlightBounds, HighlightTint); + Renderer->DrawQuad(highlightBounds, HighlightTint); Renderer->DrawSprite(BoxSprite, TrueButton.Bounds.GetPos(), Tint); - Renderer->DrawSprite(TrueSprite, TrueButton.Bounds.GetPos(), Tint, - glm::vec2(1.0f), 0.0f, !State); - Renderer->DrawSprite(FalseSprite, FalseButton.Bounds.GetPos(), Tint, - glm::vec2(1.0f), 0.0f, State); + Renderer->DrawSprite(TrueSprite, TrueButton.Bounds.GetPos(), Tint, !State); + Renderer->DrawSprite(FalseSprite, FalseButton.Bounds.GetPos(), Tint, State); } void OptionsBinaryButton::Update(float dt) { diff --git a/src/ui/widgets/cclcc/optionsentry.cpp b/src/ui/widgets/cclcc/optionsentry.cpp index 28c4f346a..3f6754fc1 100644 --- a/src/ui/widgets/cclcc/optionsentry.cpp +++ b/src/ui/widgets/cclcc/optionsentry.cpp @@ -35,8 +35,8 @@ void OptionsEntry::Render() { if (HasFocus) { RectF highlightBoundBox(Bounds.X, Bounds.Y, EntryDimensions.x, EntryDimensions.y); - Renderer->DrawRect(highlightBoundBox, HighlightTint); - Renderer->DrawRect( + Renderer->DrawQuad(highlightBoundBox, HighlightTint); + Renderer->DrawQuad( RectF(highlightBoundBox.X + 2.0f, highlightBoundBox.Y + 2.0f, highlightBoundBox.Width - 4.0f, highlightBoundBox.Height - 4.0f), glm::vec4(1.0f, 1.0f, 1.0f, Tint.a)); diff --git a/src/ui/widgets/cclcc/optionsslider.cpp b/src/ui/widgets/cclcc/optionsslider.cpp index 5276ac2f4..fa79d7230 100644 --- a/src/ui/widgets/cclcc/optionsslider.cpp +++ b/src/ui/widgets/cclcc/optionsslider.cpp @@ -46,7 +46,7 @@ void OptionsSlider::Render() { Bounds.Y + SliderTrackOffset.y, Slider.GetNormalizedValue() * BoxSprite.ScaledWidth(), BoxSprite.ScaledHeight()); - Renderer->DrawRect(highlightBounds, HighlightTint); + Renderer->DrawQuad(highlightBounds, HighlightTint); Renderer->DrawSprite(BoxSprite, Bounds.GetPos() + SliderTrackOffset, Tint); } diff --git a/src/ui/widgets/cclcc/optionsvoiceslider.cpp b/src/ui/widgets/cclcc/optionsvoiceslider.cpp index 3b426d170..a0e9ec52c 100644 --- a/src/ui/widgets/cclcc/optionsvoiceslider.cpp +++ b/src/ui/widgets/cclcc/optionsvoiceslider.cpp @@ -45,8 +45,8 @@ void OptionsVoiceSlider::Render() { if (HasFocus) { RectF highlightBoundBox(Bounds.X, Bounds.Y, VoiceEntryDimensions.x, VoiceEntryDimensions.y); - Renderer->DrawRect(highlightBoundBox, HighlightTint); - Renderer->DrawRect( + Renderer->DrawQuad(highlightBoundBox, HighlightTint); + Renderer->DrawQuad( RectF(highlightBoundBox.X + 2.0f, highlightBoundBox.Y + 2.0f, highlightBoundBox.Width - 4.0f, highlightBoundBox.Height - 4.0f), glm::vec4(1.0f, 1.0f, 1.0f, Tint.a)); @@ -61,7 +61,7 @@ void OptionsVoiceSlider::Render() { Bounds.Y + VoiceSliderOffset.y, Slider.GetNormalizedValue() * BoxSprite.ScaledWidth(), BoxSprite.ScaledHeight()); - Renderer->DrawRect(highlightBounds, HighlightTint); + Renderer->DrawQuad(highlightBounds, HighlightTint); Renderer->DrawSprite(BoxSprite, Bounds.GetPos() + VoiceSliderOffset, Tint); } diff --git a/src/ui/widgets/cclcc/saveentrybutton.cpp b/src/ui/widgets/cclcc/saveentrybutton.cpp index c4f31081d..5fc2b1c68 100644 --- a/src/ui/widgets/cclcc/saveentrybutton.cpp +++ b/src/ui/widgets/cclcc/saveentrybutton.cpp @@ -57,42 +57,58 @@ SaveEntryButton::SaveEntryButton(int id, int index, Sprite const& focusedBox, } void SaveEntryButton::Render() { + const glm::vec2 scale = {Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f}; + NormalSpriteLabel.Render(); if (HasFocus) { FocusedSpriteLabel.Tint = FocusedAlpha; FocusedSpriteLabel.Render(); - Renderer->DrawSprite( - HighlightSprite, - glm::vec2(Bounds.X + HighlightOffset.x, Bounds.Y + HighlightOffset.y), - Tint, glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + + const RectF highlightDest = + HighlightSprite.ScaledBounds() + .Scale(scale, {0.0f, 0.0f}) + .Translate(Bounds.GetPos() + HighlightOffset); + Renderer->DrawSprite(HighlightSprite, highlightDest, Tint); } + + const RectF numberDigitDest = NumberDigitSprite[0][0] + .ScaledBounds() + .Scale(scale, {0.0f, 0.0f}) + .Translate(Bounds.GetPos()); Renderer->DrawSprite( NumberDigitSprite[ScrWork[SW_SAVEMENUMODE]][(Index + 1) / 10], - glm::vec2(Bounds.X + 720, Bounds.Y + 120), Tint, - glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + RectF(numberDigitDest).Translate({720, 120}), Tint); Renderer->DrawSprite( NumberDigitSprite[ScrWork[SW_SAVEMENUMODE]][(Index + 1) % 10], - glm::vec2(Bounds.X + 752, Bounds.Y + 120), Tint, - glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + RectF(numberDigitDest).Translate({752, 120}), Tint); + if (SaveStatus == 1) { - Renderer->DrawSprite( - SeparationLineSprite[ScrWork[SW_SAVEMENUMODE]], - glm::vec2(Bounds.X + 308, Bounds.Y + 112), Tint, - glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + const RectF separationLineDest = + SeparationLineSprite[0] + .ScaledBounds() + .Scale(scale, {0.0f, 0.0f}) + .Translate(Bounds.GetPos() + glm::vec2(308, 112)); + Renderer->DrawSprite(SeparationLineSprite[ScrWork[SW_SAVEMENUMODE]], + separationLineDest, Tint); + if (IsLocked) { SceneTitleLabel.Render(); LockedSymbol.Render(); } + Renderer->DrawSprite( Thumbnail, - {Bounds.X + 20, Bounds.Y + 22, Thumbnail.Bounds.Width + 30, - Thumbnail.Bounds.Height + 17}, + RectF{Bounds.X + 20, Bounds.Y + 22, Thumbnail.Bounds.Width + 30, + Thumbnail.Bounds.Height + 17}, Tint); + CharacterRouteLabel.Render(); SceneTitleLabel.Render(); SaveDateLabel.Render(); + } else if (SaveStatus == 0) { NoDataSymbol.Render(); + } else { BrokenDataSymbol.Render(); } diff --git a/src/ui/widgets/cclcc/tipsentrybutton.cpp b/src/ui/widgets/cclcc/tipsentrybutton.cpp index a5486a6f5..b3b4b6939 100644 --- a/src/ui/widgets/cclcc/tipsentrybutton.cpp +++ b/src/ui/widgets/cclcc/tipsentrybutton.cpp @@ -79,10 +79,12 @@ void TipsEntryButton::UpdateInput() { void TipsEntryButton::Render() { if (HasFocus) { - Renderer->DrawSprite( - HighlightSprite, - glm::vec2(Bounds.X + HighlightOffset.x, Bounds.Y + HighlightOffset.y), - Tint, glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + const RectF dest = + HighlightSprite.ScaledBounds() + .Scale({Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f}, + {0.0f, 0.0f}) + .Translate(Bounds.GetPos() + HighlightOffset); + Renderer->DrawSprite(HighlightSprite, dest, Tint); } Renderer->DrawProcessedText(TipNumber, Profile::Dialogue::DialogueFont, diff --git a/src/ui/widgets/cgviewer.cpp b/src/ui/widgets/cgviewer.cpp index e1a5d71d5..4e0db85c8 100644 --- a/src/ui/widgets/cgviewer.cpp +++ b/src/ui/widgets/cgviewer.cpp @@ -111,7 +111,7 @@ void CgViewer::Update(float dt) { void CgViewer::Render() { glm::vec4 col(1.0f, 1.0f, 1.0f, glm::smoothstep(0.0f, 1.0f, FadeAnimation.Progress)); - Renderer->DrawRect( + Renderer->DrawQuad( RectF(0.0f, 0.0f, Profile::DesignWidth, Profile::DesignHeight), glm::vec4(0.0f, 0.0f, 0.0f, col.a)); glm::vec2 pos; diff --git a/src/ui/widgets/mo6tw/tipsentrybutton.cpp b/src/ui/widgets/mo6tw/tipsentrybutton.cpp index 9cc1d47e8..8f2c38c67 100644 --- a/src/ui/widgets/mo6tw/tipsentrybutton.cpp +++ b/src/ui/widgets/mo6tw/tipsentrybutton.cpp @@ -65,10 +65,12 @@ void TipsEntryButton::Update(float dt) { void TipsEntryButton::Render() { if (HasFocus) { - Renderer->DrawSprite( - HighlightSprite, - glm::vec2(Bounds.X + HighlightOffset.x, Bounds.Y + HighlightOffset.y), - Tint, glm::vec2(Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f)); + const RectF dest = + HighlightSprite.ScaledBounds() + .Scale({Bounds.Width / HighlightSprite.ScaledWidth(), 1.0f}, + {0.0f, 0.0f}) + .Translate(Bounds.GetPos() + HighlightOffset); + Renderer->DrawSprite(HighlightSprite, dest, Tint); } Renderer->DrawProcessedText(TipNumber, Profile::Dialogue::DialogueFont, diff --git a/src/ui/widgets/scrollbar.cpp b/src/ui/widgets/scrollbar.cpp index 05f5a1019..965624183 100644 --- a/src/ui/widgets/scrollbar.cpp +++ b/src/ui/widgets/scrollbar.cpp @@ -22,8 +22,7 @@ Scrollbar::Scrollbar(int id, glm::vec2 pos, float start, float end, Step((end - start) * 0.01f), Length(dir == SBDIR_VERTICAL ? trackBounds.y : trackBounds.x), ThumbBounds(0.0f, 0.0f, 0.0f, 0.0f), - ThumbLength(0.0f), - HasTrack(false) { + ThumbLength(0.0f) { UpdatePosition(); } @@ -64,11 +63,10 @@ Scrollbar::Scrollbar(int id, glm::vec2 pos, float start, float end, TrackBounds(pos.x, pos.y, track.ScaledWidth(), track.ScaledHeight()), ScrollWheelBounds(wheelBounds == RectF() ? TrackBounds : wheelBounds), WheelSpeedMultiplier(wheelSpeedMultiplier), - Step((EndValue - StartValue) * 0.01f), - HasTrack(true) { + Step((EndValue - StartValue) * 0.01f) { Enabled = true; - Length = Direction == SBDIR_VERTICAL ? TrackSprite.Bounds.Height - : TrackSprite.Bounds.Width; + Length = Direction == SBDIR_VERTICAL ? TrackSprite->Bounds.Height + : TrackSprite->Bounds.Width; ThumbBounds = RectF(0.0f, 0.0f, ThumbSprite.ScaledWidth(), ThumbSprite.ScaledHeight()); if (Direction == SBDIR_VERTICAL) { @@ -91,7 +89,6 @@ Scrollbar::Scrollbar(int id, glm::vec2 pos, float start, float end, : Scrollbar(id, pos, start, end, value, dir, track, thumb, thumbOffset, thumbLength, wheelBounds, wheelSpeedMultiplier) { FillSprite = fill; - HasFill = true; UpdatePosition(); } @@ -156,15 +153,15 @@ void Scrollbar::UpdateInput() { } void Scrollbar::Render() { - if (HasFill && FillBeforeTrack) { - Renderer->DrawSprite(FillSprite, glm::vec2(TrackBounds.X, TrackBounds.Y), + if (FillSprite.has_value() && FillBeforeTrack) { + Renderer->DrawSprite(*FillSprite, glm::vec2(TrackBounds.X, TrackBounds.Y), Tint); } - if (HasTrack) { - Renderer->DrawSprite(TrackSprite, TrackBounds, Tint); + if (TrackSprite.has_value()) { + Renderer->DrawSprite(*TrackSprite, TrackBounds, Tint); } - if (HasFill && !FillBeforeTrack) { - Renderer->DrawSprite(FillSprite, glm::vec2(TrackBounds.X, TrackBounds.Y), + if (FillSprite.has_value() && !FillBeforeTrack) { + Renderer->DrawSprite(*FillSprite, glm::vec2(TrackBounds.X, TrackBounds.Y), Tint); } Renderer->DrawSprite(ThumbSprite, glm::vec2(ThumbBounds.X, ThumbBounds.Y), @@ -209,14 +206,16 @@ void Scrollbar::UpdatePosition() { ThumbBounds.Y = (TrackBounds.Y + ThumbLength / 2.0f + thumbNormalizedProgress) - (ThumbSprite.ScaledHeight() / 2.0f); - FillSprite.Bounds.Height = TrackProgress; + + if (FillSprite) FillSprite->Bounds.Height = TrackProgress; } else if (Direction == SBDIR_HORIZONTAL) { ThumbBounds.X = (TrackBounds.X + ThumbLength / 2.0f + thumbNormalizedProgress) - (ThumbSprite.ScaledWidth() / 2.0f); ThumbBounds.Y = (TrackBounds.Y + (TrackBounds.Height / 2.0f)) - (ThumbSprite.ScaledHeight() / 2.0f); - FillSprite.Bounds.Width = TrackProgress; + + if (FillSprite) FillSprite->Bounds.Width = TrackProgress; } ThumbBounds.X += ThumbSpriteOffset.x; diff --git a/src/ui/widgets/scrollbar.h b/src/ui/widgets/scrollbar.h index 3ef457320..54dc95719 100644 --- a/src/ui/widgets/scrollbar.h +++ b/src/ui/widgets/scrollbar.h @@ -41,9 +41,9 @@ class Scrollbar : public Widget { int Id; ScrollbarDirection Direction; - Sprite TrackSprite; Sprite ThumbSprite; - Sprite FillSprite; + std::optional TrackSprite; + std::optional FillSprite; float Length; float StartValue; float EndValue; @@ -62,8 +62,6 @@ class Scrollbar : public Widget { RectF ScrollWheelBounds; float ThumbLength; bool ScrollHeld = false; - bool HasFill = false; - bool HasTrack = true; int LastScrollPos = 0; float WheelSpeedMultiplier = 1.0f; void UpdatePosition(); diff --git a/src/util.cpp b/src/util.cpp index 6b120f1d6..ae6831571 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -5,6 +5,174 @@ namespace Impacto { +CornersQuad& CornersQuad::Transform(const glm::mat4 transformation) { + TopLeft = transformation * glm::vec4(TopLeft, 0.0f, 1.0f); + TopRight = transformation * glm::vec4(TopRight, 0.0f, 1.0f); + BottomRight = transformation * glm::vec4(BottomRight, 0.0f, 1.0f); + BottomLeft = transformation * glm::vec4(BottomLeft, 0.0f, 1.0f); + + return *this; +} + +CornersQuad& CornersQuad::Transform( + const std::function& transformation) { + TopLeft = transformation(TopLeft); + TopRight = transformation(TopRight); + BottomLeft = transformation(BottomLeft); + BottomRight = transformation(BottomRight); + + return *this; +} + +CornersQuad& CornersQuad::Translate(const glm::vec2 offset) { + TopLeft += offset; + TopRight += offset; + BottomRight += offset; + BottomLeft += offset; + + return *this; +} + +CornersQuad& CornersQuad::Scale(const glm::vec2 scaling, + const glm::vec2 origin) { + Translate(-origin); + + TopLeft *= scaling; + TopRight *= scaling; + BottomRight *= scaling; + BottomLeft *= scaling; + + Translate(origin); + + return *this; +} + +CornersQuad& CornersQuad::Rotate(const float angle, const glm::vec2 origin) { + Translate(-origin); + + const glm::mat2 rotation = Rotate2D(angle); + TopLeft = rotation * TopLeft; + TopRight = rotation * TopRight; + BottomRight = rotation * BottomRight; + BottomLeft = rotation * BottomLeft; + + Translate(origin); + + return *this; +} + +CornersQuad& CornersQuad::Rotate(const glm::quat rotation, glm::vec3 origin) { + glm::vec3 topLeft = glm::vec3(TopLeft, 0.0f) - origin; + glm::vec3 topRight = glm::vec3(TopRight, 0.0f) - origin; + glm::vec3 bottomRight = glm::vec3(BottomRight, 0.0f) - origin; + glm::vec3 bottomLeft = glm::vec3(BottomLeft, 0.0f) - origin; + + const glm::mat3 rotationMatrix = glm::mat3_cast(rotation); + topLeft = rotationMatrix * topLeft; + topRight = rotationMatrix * topRight; + bottomRight = rotationMatrix * bottomRight; + topRight = rotationMatrix * topRight; + + TopLeft = topLeft + origin; + TopRight = topRight + origin; + BottomRight = bottomRight + origin; + BottomLeft = bottomLeft + origin; + + return *this; +} + +CornersQuad& CornersQuad::Rotate(const glm::quat rotation, + const glm::vec3 origin, const float depth, + const glm::vec2 vanishingPoint, + const bool stayInScreen) { + std::array vertices = { + glm::vec4(TopLeft, 0.0f, 1.0f), glm::vec4(BottomLeft, 0.0f, 1.0f), + glm::vec4(TopRight, 0.0f, 1.0f), glm::vec4(BottomRight, 0.0f, 1.0f)}; + + // Rotate + const glm::mat4 rotationMatrix = glm::mat4_cast(rotation); + for (glm::vec4& vertex : vertices) { + vertex -= glm::vec4(origin, 0.0f); + vertex = rotationMatrix * vertex; + vertex += glm::vec4(origin, 0.0f); + } + + // Move into screen + if (stayInScreen) { + float maxZ = + std::max_element(vertices.begin(), vertices.end(), + [](auto lhs, auto rhs) { return lhs.z < rhs.z; }) + ->z; + + for (glm::vec4& vertex : vertices) vertex.z -= maxZ; + } + + // Project + for (glm::vec4& vertex : vertices) { + vertex -= glm::vec4(vanishingPoint, 0.0f, 0.0f); + const float normalizedZ = vertex.z / glm::length(glm::vec3(vertex)); + vertex *= depth / (depth - normalizedZ); + vertex += glm::vec4(vanishingPoint, 0.0f, 0.0f); + } + + *this = {glm::vec2(vertices[0]), glm::vec2(vertices[1]), + glm::vec2(vertices[2]), glm::vec2(vertices[3])}; + return *this; +} + +CornersQuad RectF::Transform(glm::mat4 transformation) const { + return CornersQuad(*this).Transform(transformation); +} + +CornersQuad RectF::Transform( + const std::function& transformation) const { + return CornersQuad(*this).Transform(transformation); +} + +RectF& RectF::Scale(const glm::vec2 scaling, const glm::vec2 origin) { + Translate(-origin); + + X *= scaling.x; + Y *= scaling.y; + Width *= scaling.x; + Height *= scaling.y; + + Translate(origin); + + return *this; +} + +CornersQuad RectF::Rotate(float angle, glm::vec2 origin) const { + return CornersQuad(*this).Rotate(angle, origin); +} + +CornersQuad RectF::RotateAroundCenter(float angle) const { + return Rotate(angle, Center()); +} + +CornersQuad RectF::Rotate(glm::quat rotation, glm::vec3 origin) const { + return CornersQuad(*this).Rotate(rotation, origin); +} + +CornersQuad RectF::Rotate(const glm::quat rotation, const glm::vec3 origin, + const float depth, const glm::vec2 vanishingPoint, + const bool stayInScreen) const { + return CornersQuad(*this).Rotate(rotation, origin, depth, vanishingPoint, + stayInScreen); +} + +CornersQuad RectF::FlipVertical() const { + return CornersQuad(*this).FlipVertical(); +} + +CornersQuad RectF::FlipHorizontal() const { + return CornersQuad(*this).FlipHorizontal(); +} + +inline CornersQuad operator*(const glm::mat4 transformation, RectF rect) { + return rect.Transform(transformation); +} + glm::mat2 Rotate2D(float angle) { glm::mat2 result; float cosa = cosf(angle); @@ -16,28 +184,63 @@ glm::mat2 Rotate2D(float angle) { return result; } -glm::vec2 DesignToNDC(glm::vec2 xy) { - glm::vec2 result; - result.x = (xy.x / (Profile::DesignWidth * 0.5f)) - 1.0f; - result.y = 1.0f - (xy.y / (Profile::DesignHeight * 0.5f)); - return result; +glm::mat4 TransformationMatrix(const glm::vec2 scalingOrigin, + const glm::vec2 scaling, + const glm::vec2 rotationOrigin, + const float rotation, + const glm::vec2 translation) { + return TransformationMatrix( + scalingOrigin, scaling, {rotationOrigin, 0.0f}, + AxisAngleToQuaternion({0.0f, 0.0f, 1.0f}, rotation), translation); } -glm::vec2 DesignToNDCNonFlipped(glm::vec2 xy) { - glm::vec2 result; - result.x = (xy.x / (Profile::DesignWidth * 0.5f)) - 1.0f; - result.y = (xy.y / (Profile::DesignHeight * 0.5f)) - 1.0f; - return result; +glm::mat4 TransformationMatrix(const glm::vec3 scalingOrigin, + const glm::vec3 scaling, + const glm::vec3 rotationOrigin, + const glm::quat rotation, + const glm::vec3 translation) { + glm::mat4 matrix(1.0f); + + if (rotationOrigin + translation != glm::vec3(0.0f)) { + matrix = glm::translate(matrix, rotationOrigin + translation); + } + + if (rotation != glm::quat()) { + matrix *= glm::mat4_cast(normalize(rotation)); + } + + if (scalingOrigin - rotationOrigin != glm::vec3(0.0f)) { + matrix = glm::translate(matrix, scalingOrigin - rotationOrigin); + } + + if (scaling != glm::vec3(1.0f)) { + matrix = glm::scale(matrix, scaling); + } + + if (scalingOrigin != glm::vec3(0.0f)) { + matrix = glm::translate(matrix, -scalingOrigin); + } + + return matrix; } -RectF DesignToNDC(RectF const& rect) { - RectF result; - glm::vec2 xy = DesignToNDC(glm::vec2(rect.X, rect.Y)); - result.X = xy.x; - result.Y = xy.y; - result.Width = rect.Width / (Profile::DesignWidth * 0.5f); - result.Height = rect.Height / (Profile::DesignHeight * 0.5f); - return result; +glm::vec4 TransformVector(const glm::vec2 pos, const glm::vec2 scalingOrigin, + const glm::vec2 scaling, + const glm::vec2 rotationOrigin, const float rotation, + const glm::vec2 translation) { + return TransformationMatrix(scalingOrigin, scaling, rotationOrigin, rotation, + translation) * + glm::vec4(pos, 0.0f, 1.0f); +} + +glm::vec4 TransformVector(const glm::vec3 pos, const glm::vec3 scalingOrigin, + const glm::vec3 scaling, + const glm::vec3 rotationOrigin, + const glm::quat rotation, + const glm::vec3 translation) { + return TransformationMatrix(scalingOrigin, scaling, rotationOrigin, rotation, + translation) * + glm::vec4(pos, 1.0f); } uint32_t GetHashCode(uint8_t* data, int length) { diff --git a/src/util.h b/src/util.h index 833b78e7b..e485e1d02 100644 --- a/src/util.h +++ b/src/util.h @@ -51,7 +51,45 @@ concept is_any_of = std::disjunction_v...>; glm::mat2 Rotate2D(float angle); +glm::mat4 TransformationMatrix(glm::vec2 scalingOrigin, glm::vec2 scaling, + glm::vec2 rotationOrigin = glm::vec2(0.0f), + float rotation = 0.0f, + glm::vec2 translation = glm::vec2(0.0f)); +glm::mat4 TransformationMatrix(glm::vec3 scalingOrigin, glm::vec3 scaling, + glm::vec3 rotationOrigin = glm::vec3(0.0f), + glm::quat rotation = glm::quat(), + glm::vec3 translation = glm::vec3(0.0f)); +inline glm::mat4 TransformationMatrix(glm::vec2 scalingOrigin, + glm::vec2 scaling, + glm::vec3 rotationOrigin, + glm::quat rotation, + glm::vec2 translation = glm::vec2(0.0f)) { + return TransformationMatrix(glm::vec3(scalingOrigin, 0.0f), + glm::vec3(scaling, 1.0f), rotationOrigin, + rotation, glm::vec3(translation, 0.0f)); +} + +glm::vec4 TransformVector(glm::vec2 pos, glm::vec2 scalingOrigin, + glm::vec2 scaling, + glm::vec2 rotationOrigin = glm::vec2(0.0f), + float rotation = 0.0f, + glm::vec2 translation = glm::vec2(0.0f)); +glm::vec4 TransformVector(glm::vec3 pos, glm::vec3 scalingOrigin, + glm::vec3 scaling, + glm::vec3 rotationOrigin = glm::vec3(0.0f), + glm::quat rotation = glm::quat(), + glm::vec3 translation = glm::vec3(0.0f)); +inline glm::vec4 TransformVector(glm::vec2 pos, glm::vec2 scalingOrigin, + glm::vec2 scaling, glm::vec3 rotationOrigin, + glm::quat rotation, + glm::vec2 translation = glm::vec2(0.0f)) { + return TransformVector({pos, 0.0f}, glm::vec3(scalingOrigin, 0.0f), + glm::vec3(scaling, 1.0f), rotationOrigin, rotation, + {translation, 0.0f}); +} + struct Rect; +struct CornersQuad; struct RectF { float X = 0; @@ -96,14 +134,18 @@ struct RectF { return RectF(X - movementVector.x, Y - movementVector.y, Width, Height); } - constexpr void operator+=(const glm::vec2 movementVector) { + constexpr RectF& operator+=(const glm::vec2 movementVector) { X += movementVector.x; Y += movementVector.y; + + return *this; } - constexpr void operator-=(const glm::vec2 movementVector) { + constexpr RectF& operator-=(const glm::vec2 movementVector) { X -= movementVector.x; Y -= movementVector.y; + + return *this; } constexpr bool operator==(RectF const& other) const { @@ -119,13 +161,30 @@ struct RectF { static RectF Coalesce(const RectF& first, const RectF& second); - constexpr RectF Scale(const glm::vec2 scalar, const glm::vec2 origin) const { - const float scaledX = (X - origin.x) * scalar.x + origin.x; - const float scaledY = (Y - origin.y) * scalar.y + origin.y; - return RectF(scaledX, scaledY, Width * scalar.x, Height * scalar.y); + CornersQuad Transform(glm::mat4 transformation) const; + CornersQuad Transform( + const std::function& transformation) const; + + RectF& Translate(glm::vec2 offset) { return *this += offset; } + RectF& Translate(float dx, float dy) { return Translate({dx, dy}); } + + RectF& Scale(glm::vec2 scaling, glm::vec2 origin); + RectF& ScaleAroundCenter(glm::vec2 scaling, glm::vec2 origin) { + return Scale(scaling, Center()); } + + CornersQuad Rotate(float angle, glm::vec2 origin) const; + CornersQuad RotateAroundCenter(float angle) const; + CornersQuad Rotate(glm::quat rotation, glm::vec3 origin) const; + CornersQuad Rotate(glm::quat rotation, glm::vec3 origin, float depth, + glm::vec2 vanishingPoint, bool stayInScreen = false) const; + + CornersQuad FlipVertical() const; + CornersQuad FlipHorizontal() const; }; +inline CornersQuad operator*(const glm::mat4 transformation, RectF rect); + struct Rect { int X = 0; int Y = 0; @@ -186,16 +245,52 @@ struct CornersQuad { {(float)rect.X, (float)(rect.Y + rect.Height)}, {(float)(rect.X + rect.Width), (float)rect.Y}, {(float)(rect.X + rect.Width), (float)(rect.Y + rect.Height)}) {} + + constexpr glm::vec2 Center() const { + return (TopLeft + BottomLeft + TopRight + BottomRight) / 4.0f; + } + + CornersQuad& Transform(glm::mat4 transformation); + CornersQuad& Transform( + const std::function& transformation); + + CornersQuad& Translate(glm::vec2 offset); + CornersQuad& Translate(float dx, float dy) { return Translate({dx, dy}); } + + CornersQuad& Scale(glm::vec2 scaling, glm::vec2 origin); + CornersQuad& ScaleAroundCenter(glm::vec2 scaling, glm::vec2 origin) { + return Scale(scaling, Center()); + } + + CornersQuad& Rotate(float angle, glm::vec2 origin); + CornersQuad& RotateAroundCenter(float angle) { + return Rotate(angle, Center()); + } + CornersQuad& Rotate(glm::quat rotation, glm::vec3 origin); + CornersQuad& Rotate(glm::quat rotation, glm::vec3 origin, float depth, + glm::vec2 vanishingPoint, bool stayInScreen = false); + + CornersQuad& FlipVertical() { + std::swap(TopLeft, BottomLeft); + std::swap(TopRight, BottomRight); + return *this; + } + + CornersQuad& FlipHorizontal() { + std::swap(TopLeft, TopRight); + std::swap(BottomLeft, BottomRight); + return *this; + } }; +inline CornersQuad operator*(const glm::mat4 transformation, CornersQuad quad) { + return quad.Transform(transformation); +} + inline constexpr RectF::RectF(Rect const& rect) : RectF((float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height) {} -glm::vec2 DesignToNDC(glm::vec2 xy); -glm::vec2 DesignToNDCNonFlipped(glm::vec2 xy); -RectF DesignToNDC(RectF const& rect); - constexpr glm::vec4 RgbIntToFloat(uint32_t rgb) { return glm::vec4{(float)((rgb >> 16) & 0xFF) / 255.0f, (float)((rgb >> 8) & 0xFF) / 255.0f, @@ -286,6 +381,18 @@ inline float NormalizeRad(float rad) { if (rad < 0) rad += 2.0f * (float)M_PI; return rad - (float)M_PI; } +constexpr float ScrWorkAngleToRad(float angle) { + return angle * (float)(2.0f * M_PI / (float)(1 << 16)); +} + +inline glm::quat ScrWorkAnglesToQuaternion(int x, int y, int z) { + return glm::quat( + {ScrWorkAngleToRad(x), ScrWorkAngleToRad(y), ScrWorkAngleToRad(z)}); +} + +inline glm::quat AxisAngleToQuaternion(glm::vec3 axis, float angle) { + return glm::quat(cos(angle / 2.0f), sin(angle / 2.0f) * axis); +} inline bool StringEndsWith(std::string const& str, std::string const& ending) { if (str.length() < ending.length()) return false; diff --git a/src/video/ffmpegplayer.cpp b/src/video/ffmpegplayer.cpp index 7056091f6..c5f21d791 100644 --- a/src/video/ffmpegplayer.cpp +++ b/src/video/ffmpegplayer.cpp @@ -476,13 +476,10 @@ void FFmpegPlayer::Update(float dt) { void FFmpegPlayer::Render(float videoAlpha) { if (IsPlaying && PlaybackStarted) { - float widthScale = Profile::DesignWidth / VideoTexture->Width; - float heightScale = Profile::DesignHeight / VideoTexture->Height; - glm::vec4 tint = glm::vec4(1.0f); - tint.a = videoAlpha; - Renderer->DrawVideoTexture(VideoTexture, glm::vec2(0.0f, 0.0f), tint, - glm::vec2(widthScale, heightScale), 0.0f, - IsAlpha); + const RectF dest = {0.0f, 0.0f, Profile::DesignWidth, + Profile::DesignHeight}; + const glm::vec4 tint = {1.0f, 1.0f, 1.0f, videoAlpha}; + Renderer->DrawVideoTexture(*VideoTexture, dest, tint, IsAlpha); } }