diff --git a/include/SSVOpenHexagon/Core/HexagonGame.hpp b/include/SSVOpenHexagon/Core/HexagonGame.hpp index 0645a8418..da61807cb 100644 --- a/include/SSVOpenHexagon/Core/HexagonGame.hpp +++ b/include/SSVOpenHexagon/Core/HexagonGame.hpp @@ -430,6 +430,9 @@ class HexagonGame Utils::FastVertexVectorTris pivotQuads; Utils::FastVertexVectorTris playerTris; Utils::FastVertexVectorTris capTris; + Utils::FastVertexVectorTris wallQuads3DTop; + Utils::FastVertexVectorTris pivotQuads3DTop; + Utils::FastVertexVectorTris playerTris3DTop; Utils::FastVertexVectorTris wallQuads3D; Utils::FastVertexVectorTris pivotQuads3D; Utils::FastVertexVectorTris playerTris3D; diff --git a/include/SSVOpenHexagon/Data/StyleData.hpp b/include/SSVOpenHexagon/Data/StyleData.hpp index 646d9aa68..2088ce3a6 100644 --- a/include/SSVOpenHexagon/Data/StyleData.hpp +++ b/include/SSVOpenHexagon/Data/StyleData.hpp @@ -77,6 +77,9 @@ class StyleData float maxSwapTime{}; float _3dDepth{}; + float _3dLayerOffset{}; + bool _3dAlphaMirror{}; + bool _3dMainOnTop{}; float _3dSkew{}; float _3dSpacing{}; float _3dDarkenMult{}; diff --git a/src/SSVOpenHexagon/Core/HGGraphics.cpp b/src/SSVOpenHexagon/Core/HGGraphics.cpp index f99404e05..53fb40cc5 100644 --- a/src/SSVOpenHexagon/Core/HGGraphics.cpp +++ b/src/SSVOpenHexagon/Core/HGGraphics.cpp @@ -120,6 +120,9 @@ void HexagonGame::draw() window->setView(backgroundCamera->apply()); + wallQuads3DTop.clear(); + pivotQuads3DTop.clear(); + playerTris3DTop.clear(); wallQuads3D.clear(); pivotQuads3D.clear(); playerTris3D.clear(); @@ -145,16 +148,42 @@ void HexagonGame::draw() Config::getShowSwapBlinkingEffect()); } + const bool mustRender3DAboveMain(Config::get3D() && + styleData._3dLayerOffset < -1 && + !styleData._3dMainOnTop); if(Config::get3D()) { - const float depth(styleData._3dDepth); + // subtract the layers above main from the normal depth if there are any + const float depth( + styleData._3dDepth + + mustRender3DAboveMain * (styleData._3dLayerOffset + 1)); + + // The amount of layers above the main layer is how much it is offset + // above until the amount of layers is used up. In that case the amount + // is just 3D depth (all layers are floating above the main layer) + const float layersAboveMain( + depth >= 0 ? -(styleData._3dLayerOffset + 1) : styleData._3dDepth); const std::size_t numWallQuads(wallQuads.size()); const std::size_t numPivotQuads(pivotQuads.size()); const std::size_t numPlayerTris(playerTris.size()); - wallQuads3D.reserve(numWallQuads * depth); - pivotQuads3D.reserve(numPivotQuads * depth); - playerTris3D.reserve(numPlayerTris * depth); + // only reserve space for upper layers if there are any + if(mustRender3DAboveMain) + { + wallQuads3DTop.reserve(numWallQuads * layersAboveMain); + pivotQuads3DTop.reserve(numPivotQuads * layersAboveMain); + playerTris3DTop.reserve(numPlayerTris * layersAboveMain); + } + + // only reserve space for lower layers if there are any, otherwise due + // to the above logic the depth value could be negative, which should be + // ignored here + if(depth > 0) + { + wallQuads3D.reserve(numWallQuads * depth); + pivotQuads3D.reserve(numPivotQuads * depth); + playerTris3D.reserve(numPlayerTris * depth); + } const float pulse3D{Config::getNoPulse() ? 1.f : status.pulse3D}; const float effect{ @@ -175,20 +204,104 @@ void HexagonGame::draw() playerTris3D.unsafe_emplace_other(playerTris); } + if(mustRender3DAboveMain) + { + for(std::size_t i = 0; i < layersAboveMain; ++i) + { + wallQuads3DTop.unsafe_emplace_other(wallQuads); + pivotQuads3DTop.unsafe_emplace_other(pivotQuads); + playerTris3DTop.unsafe_emplace_other(playerTris); + } + } + const auto adjustAlpha = [&](sf::Color& c, const float i) { SSVOH_ASSERT(styleData._3dAlphaMult != 0.f); + // modify the layer value here to mirror the alpha falloff if needed + // (-1 is the layer inside the main layer, so it should be 0, that's + // why 1 is added) + const float layer = styleData._3dAlphaMirror ? std::abs(i + 1) : i; const float newAlpha = (static_cast(c.a) / styleData._3dAlphaMult) - - i * styleData._3dAlphaFalloff; + layer * styleData._3dAlphaFalloff; c.a = Utils::componentClamp(newAlpha); }; - for(int j(0); j < static_cast(depth); ++j) + for(int j(0); j < layersAboveMain; ++j) + { + const int i(layersAboveMain - j - 1 + styleData._3dLayerOffset); + const float offset(styleData._3dSpacing * + (float(i + 1.f) * styleData._3dPerspectiveMult) * + (effect * 3.6f) * 1.4f); + + const sf::Vector2f newPos(offset * cosRot, offset * sinRot); + + sf::Color overrideColor; + + if(!Config::getBlackAndWhite()) + { + overrideColor = Utils::getColorDarkened( + styleData.get3DOverrideColor(), styleData._3dDarkenMult); + } + else + { + overrideColor = Utils::getColorDarkened( + sf::Color(255, 255, 255, styleData.getMainColor().a), + styleData._3dDarkenMult); + } + adjustAlpha(overrideColor, i); + + // Draw pivot layers + for(std::size_t k = j * numPivotQuads; k < (j + 1) * numPivotQuads; + ++k) + { + pivotQuads3DTop[k].position += newPos; + pivotQuads3DTop[k].color = overrideColor; + } + + if(styleData.get3DOverrideColor() == styleData.getMainColor()) + { + overrideColor = Utils::getColorDarkened( + getColorWall(), styleData._3dDarkenMult); + + adjustAlpha(overrideColor, i); + } + + // Draw wall layers + for(std::size_t k = j * numWallQuads; k < (j + 1) * numWallQuads; + ++k) + { + wallQuads3DTop[k].position += newPos; + wallQuads3DTop[k].color = overrideColor; + } + + // Apply player color if no 3D override is present. + if(styleData.get3DOverrideColor() == styleData.getMainColor()) + { + overrideColor = Utils::getColorDarkened( + getColorPlayer(), styleData._3dDarkenMult); + + adjustAlpha(overrideColor, i); + } + + // Draw player layers + for(std::size_t k = j * numPlayerTris; k < (j + 1) * numPlayerTris; + ++k) + { + playerTris3DTop[k].position += newPos; + playerTris3DTop[k].color = overrideColor; + } + } + // If layers are rendered above the lower layers, they should only be + // shifted by -1 instead of the actual offset since the other layers + // will be processed in the above loop instead of being shifted here + const float lowerLayerOffset( + mustRender3DAboveMain ? -1.f : styleData._3dLayerOffset); + for(int j(0); j < depth; ++j) { - const float i(depth - j - 1); + const float i(depth - j - 1 + lowerLayerOffset); const float offset(styleData._3dSpacing * (float(i + 1.f) * styleData._3dPerspectiveMult) * @@ -252,12 +365,14 @@ void HexagonGame::draw() playerTris3D[k].color = overrideColor; } } + if(depth > 0) + { + render(wallQuads3D, getRenderStates(RenderStage::WallQuads3D)); + render(pivotQuads3D, getRenderStates(RenderStage::PivotQuads3D)); + render(playerTris3D, getRenderStates(RenderStage::PlayerTris3D)); + } } - render(wallQuads3D, getRenderStates(RenderStage::WallQuads3D)); - render(pivotQuads3D, getRenderStates(RenderStage::PivotQuads3D)); - render(playerTris3D, getRenderStates(RenderStage::PlayerTris3D)); - if(Config::getShowPlayerTrail() && status.showPlayerTrail) { drawTrailParticles(); @@ -273,6 +388,13 @@ void HexagonGame::draw() render(pivotQuads, getRenderStates(RenderStage::PivotQuads)); render(playerTris, getRenderStates(RenderStage::PlayerTris)); + if(Config::get3D() && mustRender3DAboveMain) + { + render(wallQuads3DTop, getRenderStates(RenderStage::WallQuads3D)); + render(pivotQuads3DTop, getRenderStates(RenderStage::PivotQuads3D)); + render(playerTris3DTop, getRenderStates(RenderStage::PlayerTris3D)); + } + window->setView(overlayCamera->apply()); drawParticles(); diff --git a/src/SSVOpenHexagon/Core/LuaScripting.cpp b/src/SSVOpenHexagon/Core/LuaScripting.cpp index 6e233733b..e558ca923 100644 --- a/src/SSVOpenHexagon/Core/LuaScripting.cpp +++ b/src/SSVOpenHexagon/Core/LuaScripting.cpp @@ -1038,6 +1038,26 @@ static void initStyleControl(Lua::LuaContext& lua, StyleData& styleData) "Sets the amount of 3D layers in a style to `$0`."); + sdVar("3dLayerOffset", &StyleData::_3dLayerOffset, + "Gets the current offset of the 3D layers compared to where they " + "usually are in layers.", + + "Sets the current offset of the 3D layers to `$0`."); + + sdVar("3dAlphaMirror", &StyleData::_3dAlphaMirror, + "Gets if the alpha of the 3D layers above the main layer should be " + "mirrored (just makes alpha falloff start one layer earlier with " + "3dLayerOffset >= -1).", + + "Sets if the alpha of the 3D layers above the main layer should be " + "mirrored."); + + sdVar("3dMainOnTop", &StyleData::_3dMainOnTop, + "Gets if the main layer should be rendered on top of all 3D layers " + "(has no effect with 3dLayerOffset >= -1).", + + "Sets if the main layer should be rendered on top of all 3D layers."); + sdVar("3dSkew", &StyleData::_3dSkew, "Gets the current value of where the 3D skew is in the style. The Skew " "is what gives the 3D effect in the first " diff --git a/src/SSVOpenHexagon/Data/StyleData.cpp b/src/SSVOpenHexagon/Data/StyleData.cpp index 0db0a06e9..d439af4fd 100644 --- a/src/SSVOpenHexagon/Data/StyleData.cpp +++ b/src/SSVOpenHexagon/Data/StyleData.cpp @@ -44,6 +44,9 @@ StyleData::StyleData(const ssvuj::Obj& mRoot) maxSwapTime{ssvuj::getExtr(mRoot, "max_swap_time", 100.f)}, _3dDepth{ssvuj::getExtr(mRoot, "3D_depth", 15.f)}, + _3dLayerOffset{ssvuj::getExtr(mRoot, "3D_layer_offset", 0.f)}, + _3dAlphaMirror{ssvuj::getExtr(mRoot, "3D_alpha_mirror", false)}, + _3dMainOnTop{ssvuj::getExtr(mRoot, "3D_main_on_top", false)}, _3dSkew{ssvuj::getExtr(mRoot, "3D_skew", 0.18f)}, _3dSpacing{ssvuj::getExtr(mRoot, "3D_spacing", 1.f)}, _3dDarkenMult{ssvuj::getExtr(mRoot, "3D_darken_multiplier", 1.5f)},