diff --git a/cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl b/cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl index 1d17c79544..1e01f1f9f9 100644 --- a/cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl +++ b/cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl @@ -36,19 +36,17 @@ uniform mat4 shadowMatrix; uniform float curAdjustedFrame; -flat in vec3 vPosTL; -flat in vec3 vPosTR; -flat in vec3 vPosBL; -flat in vec3 vPosBR; +flat in vec4 vPosTL; +flat in vec4 vPosTR; +flat in vec4 vPosBL; +flat in vec4 vPosBR; flat in vec4 vuvMain; flat in vec4 vuvNorm; flat in vec4 midPoint; in vec4 misc; //misc.x - alpha & glow, misc.y - height, misc.z - uvWrapDistance, misc.w - distance from left // can't be flat because of misc.x -flat in vec4 misc2; // groundNormal.xyz, decalTypeAsFloat -flat in vec3 xDir; - -#define groundNormal misc2.xyz +flat in vec4 misc2; // {3xempty}, decalTypeAsFloat +flat in mat3 rotMat; out vec4 fragColor; @@ -65,7 +63,7 @@ out vec4 fragColor; #define NORM2SNORM(value) (value * 2.0 - 1.0) #define SNORM2NORM(value) (value * 0.5 + 0.5) -#line 200068 +#line 200066 vec3 GetTriangleBarycentric(vec3 p, vec3 p0, vec3 p1, vec3 p2) { vec3 v0 = p2 - p0; @@ -262,6 +260,28 @@ vec3 RotateByNormalVector(vec3 p, vec3 newUpDir, vec3 rotAxis) { #undef nz } +/* +bool IsInEllipsoid(vec3 xyz, vec3 abc) { +#if 1 + float A = xyz.x * abc.y * abc.z; + float B = xyz.y * abc.x * abc.z; + float C = xyz.z * abc.x * abc.y; + float D = abc.x * abc.y * abc.z; + + return A * A + B * B + C * C <= D * D; +#else + return ((xyz.x*xyz.x) / (abc.x*abc.x) + (xyz.y*xyz.y) / (abc.y*abc.y) + (xyz.z*xyz.z) / (abc.z*abc.z) <= 1.0); +#endif +} +*/ +float EllipsoidRedunction(vec3 xyz, vec3 abc) { + float s = (xyz.x*xyz.x) / (abc.x*abc.x) + (xyz.y*xyz.y) / (abc.y*abc.y) + (xyz.z*xyz.z) / (abc.z*abc.z); + return clamp(2.0 - max(s, 1.0), 0.0, 1.0); +} + + +const float DECAL_EXPLOSION = 2.0; + const float SMF_INTENSITY_MULT = 210.0 / 255.0; const float SMF_SHALLOW_WATER_DEPTH = 10.0; const float SMF_SHALLOW_WATER_DEPTH_INV = 1.0 / SMF_SHALLOW_WATER_DEPTH; @@ -278,7 +298,11 @@ void main() { vec3 worldPos = GetWorldPos(gl_FragCoord.xy * screenSizeInverse, depthZO); - vec3 worldPosProj = worldPos - dot(worldPos - midPoint.xyz, groundNormal) * groundNormal; + //vec3 rotWorldPos = transpose(rotMat) * (worldPos - midPoint.xyz); + //if (misc2.w == DECAL_EXPLOSION && !IsInEllipsoid(rotWorldPos, vec3(vPosTL.w, misc.y, vPosTR.w))) + // discard; + + vec3 worldPosProj = worldPos - dot(worldPos - midPoint.xyz, rotMat[1]) * rotMat[1]; vec4 uvBL = vec4(uvMainBL, uvNormBL); vec4 uvTL = vec4(uvMainTL, uvNormTL); @@ -287,14 +311,13 @@ void main() { float u = 1.0; if (misc.z > 0.0) { - u = distance((vPosTL + vPosBL) * 0.5, (vPosBR + vPosTR) * 0.5) / misc.z; - //u = distance(vPosTL, vPosTR) / misc.z; + u = distance((vPosTL.xyz + vPosBL.xyz) * 0.5, (vPosBR.xyz + vPosTR.xyz) * 0.5) / misc.z; } vec4 relUV; bool disc = true; { - vec3 bc = GetTriangleBarycentric(worldPosProj, vPosBL, vPosTL, vPosTR); + vec3 bc = GetTriangleBarycentric(worldPosProj, vPosBL.xyz, vPosTL.xyz, vPosTR.xyz); if (all(greaterThanEqual(bc, all0)) && all(lessThanEqual(bc, all1))) { disc = false; //uv = bc.x * uvBL + bc.y * uvTL + bc.z * uvTR; @@ -303,7 +326,7 @@ void main() { } if (disc) { - vec3 bc = GetTriangleBarycentric(worldPosProj, vPosTR, vPosBR, vPosBL); + vec3 bc = GetTriangleBarycentric(worldPosProj, vPosTR.xyz, vPosBR.xyz, vPosBL.xyz); if (all(greaterThanEqual(bc, all0)) && all(lessThanEqual(bc, all1))) { disc = false; //uv = bc.x * uvTR + bc.y * uvBR + bc.z * uvBL; @@ -361,13 +384,13 @@ void main() { #else vec3 mapDecalMix = 2.0 * mainCol.rgb * mapDiffuse.rgb; #endif - mainCol.rgb = mix(mainCol.rgb, mapDecalMix, float(misc2.w == 2.0/*DECAL_EXPLOSION*/)); //only apply mapDecalMix for explosions + mainCol.rgb = mix(mainCol.rgb, mapDecalMix, float(misc2.w == DECAL_EXPLOSION)); //only apply mapDecalMix for explosions vec3 N = GetFragmentNormal(worldPos.xz); #if 0 // Expensive processing - vec3 T = xDir; //tangent if N was (0,1,0) + vec3 T = rotMat[0]; //tangent if N was (0,1,0) if (1.0 - N.y > 0.01) { // rotAxis is cross(Upvector, N), but Upvector is known to be (0, 1, 0), so simplify @@ -376,7 +399,7 @@ void main() { } #else // Cheaper Gramm-Schmidt - vec3 T = normalize(xDir - N * dot(xDir, N)); + vec3 T = normalize(rotMat[0] - N * dot(rotMat[0], N)); #endif vec3 B = normalize(cross(N, T)); @@ -420,9 +443,14 @@ void main() { #endif fragColor.a = mainCol.a * alpha; + + vec3 rotWorldPos = transpose(rotMat) * (worldPos - midPoint.xyz); + //fragColor.xyz=vec3(1); + fragColor.a *= mix(1.0, EllipsoidRedunction(rotWorldPos, vec3(vPosTL.w, misc.y, vPosTR.w)), float(misc2.w == DECAL_EXPLOSION)); // artistic adjustments - //fragColor *= pow(max(dot(groundNormal, N), 0.0), 1.5); // MdotL^1.5 is arbitrary - fragColor.a *= - smoothstep(0.0, EPS, relUV.x) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.x)) * - smoothstep(0.0, EPS, relUV.y) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.y)); + //fragColor *= pow(max(dot(rotMat[1], N), 0.0), 1.5); // MdotL^1.5 is arbitrary + //fragColor = vec4(1); + //fragColor.a *= + // smoothstep(0.0, EPS, relUV.x) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.x)) * + // smoothstep(0.0, EPS, relUV.y) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.y)); } diff --git a/cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl b/cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl index 13f15b5bab..880b15d357 100644 --- a/cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl +++ b/cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl @@ -14,19 +14,17 @@ uniform sampler2D groundNormalTex; uniform vec4 mapDims; //mapxy; 1.0 / mapxy uniform float curAdjustedFrame; -flat out vec3 vPosTL; -flat out vec3 vPosTR; -flat out vec3 vPosBL; -flat out vec3 vPosBR; +flat out vec4 vPosTL; +flat out vec4 vPosTR; +flat out vec4 vPosBL; +flat out vec4 vPosBR; flat out vec4 vuvMain; flat out vec4 vuvNorm; flat out vec4 midPoint; out vec4 misc; //misc.x - alpha & glow, misc.y - height, misc.z - uvWrapDistance, misc.w - uvOffset // can't be flat because of misc.x -flat out vec4 misc2; // groundNormal.xyz, decalTypeAsFloat -flat out vec3 xDir; - -#define groundNormal misc2.xyz +flat out vec4 misc2; // {3xempty}, decalTypeAsFloat +flat out mat3 rotMat; #define NORM2SNORM(value) (value * 2.0 - 1.0) #define SNORM2NORM(value) (value * 0.5 + 0.5) @@ -135,6 +133,8 @@ vec3 RotateByNormalVector(vec3 p, vec3 newUpDir, vec3 rotAxis) { #undef nz } +const float DECAL_EXPLOSION = 2.0; + // uv ==> L, T, R, B void main() { vec3 relPos = CUBE_VERT[gl_VertexID]; @@ -148,14 +148,14 @@ void main() { misc2.w = float(typeAndId & 0xFu); //copy type only, don't care about ID // emulate explosion fade in for the first 6 frames, asjusted by the initial alpha (less fadein for already weak scars) - misc.x *= mix(1.0, smoothstep(0.0, 6.0 * info.x, curAdjustedFrame - thisVertexCreateFrame), float(misc2.w == 2.0/*DECAL_EXPLOSION*/)); + misc.x *= mix(1.0, smoothstep(0.0, 6.0 * info.x, curAdjustedFrame - thisVertexCreateFrame), float(misc2.w == DECAL_EXPLOSION)); #if 1 if (alphaMax <= 0.0 || misc2.w <= 0.0) { - vPosTL = vec3(0); - vPosTR = vec3(0); - vPosBL = vec3(0); - vPosBR = vec3(0); + vPosTL = vec4(0); + vPosTR = vec4(0); + vPosBL = vec4(0); + vPosBR = vec4(0); gl_Position = vec4(2.0, 2.0, 2.0, 1.0); //place outside of [-1;1]^3 NDC, basically cull out from the further rendering return; } @@ -173,6 +173,7 @@ void main() { midPoint.w *= 1.22474; // groundNormal + vec3 groundNormal = vec3(0); if (dot(forcedNormalAndAlphaMult.xyz, forcedNormalAndAlphaMult.xyz) == 0.0) { groundNormal = vec3(0.0); groundNormal += 2.0 * GetFragmentNormal(midPoint.xz); @@ -186,26 +187,29 @@ void main() { } // get 2D orthonormal system - xDir = vec3( ca, 0.0, sa); - vec3 zDir = vec3(-sa, 0.0, ca); + vec3 xDir = vec3( ca, 0.0, sa); // don't rotate almost vertical cubes if (1.0 - groundNormal.y > 0.05) { // rotAxis is cross(Upvector, N), but Upvector is known to be (0, 1, 0), so simplify vec3 rotAxis = normalize(vec3(groundNormal.z, 0.0, -groundNormal.x)); xDir = RotateByNormalVector(xDir, groundNormal, rotAxis); - zDir = normalize(cross(xDir, groundNormal)); } + vec3 zDir = normalize(cross(xDir, groundNormal)); // orthonormal system - mat3 ONS = mat3(xDir, groundNormal, zDir); - //ONS = mat3(1); + rotMat = mat3(xDir, groundNormal, zDir); // absolute world coords, already rotated in all possible ways - vPosTL = ONS * (vec3(posT.x, 0.0, posT.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; - vPosTR = ONS * (vec3(posT.z, 0.0, posT.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; - vPosBR = ONS * (vec3(posB.x, 0.0, posB.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; - vPosBL = ONS * (vec3(posB.z, 0.0, posB.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; + vPosTL.xyz = rotMat * (vec3(posT.x, 0.0, posT.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; + vPosTR.xyz = rotMat * (vec3(posT.z, 0.0, posT.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; + vPosBR.xyz = rotMat * (vec3(posB.x, 0.0, posB.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; + vPosBL.xyz = rotMat * (vec3(posB.z, 0.0, posB.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz; + + vPosTL.w = distance(posT.zw, posT.xy) * 0.5; + vPosTR.w = distance(posB.xy, posT.zw) * 0.5; + vPosBR.w = distance(posB.zw, posB.xy) * 0.5; + vPosBL.w = distance(posT.xy, posB.zw) * 0.5; vuvMain = uvMain; vuvNorm = uvNorm; @@ -218,10 +222,10 @@ void main() { ); vec3 worldPos = vec3(0); - worldPos.xyz += testResults.x * vPosTL; - worldPos.xyz += testResults.y * vPosTR; - worldPos.xyz += testResults.z * vPosBR; - worldPos.xyz += testResults.w * vPosBL; + worldPos.xyz += testResults.x * vPosTL.xyz; + worldPos.xyz += testResults.y * vPosTR.xyz; + worldPos.xyz += testResults.z * vPosBR.xyz; + worldPos.xyz += testResults.w * vPosBL.xyz; // effect's height misc.y = info.w; diff --git a/rts/Rendering/Env/Decals/GroundDecal.h b/rts/Rendering/Env/Decals/GroundDecal.h index 6579ca0bc1..800a88926b 100644 --- a/rts/Rendering/Env/Decals/GroundDecal.h +++ b/rts/Rendering/Env/Decals/GroundDecal.h @@ -17,7 +17,7 @@ struct GroundDecal { DECAL_LUA = 4 }; public: - bool IsValid() const { return info.type > Type::DECAL_NONE || alpha > 0.0f; } + bool IsValid() const { return info.type > Type::DECAL_NONE; } void MarkInvalid() { info.type = Type::DECAL_NONE; } public: float2 posTL; diff --git a/rts/Rendering/Env/Decals/GroundDecalHandler.cpp b/rts/Rendering/Env/Decals/GroundDecalHandler.cpp index 78124c7ba0..22a0f22c0e 100644 --- a/rts/Rendering/Env/Decals/GroundDecalHandler.cpp +++ b/rts/Rendering/Env/Decals/GroundDecalHandler.cpp @@ -501,6 +501,9 @@ void CGroundDecalHandler::AddExplosion(float3 pos, float3 explNormalVec, float d if (radius < 5.0f) return; + if (damage < 5.0f) + return; + damage = std::min(damage, radius * 30.0f); damage *= (radius / (radius + altitude)); radius = std::min(radius, damage * 0.25f); @@ -524,7 +527,7 @@ void CGroundDecalHandler::AddExplosion(float3 pos, float3 explNormalVec, float d const auto normName = IntToString(scarIdx, "normscar_%i"); const auto createFrame = static_cast(std::max(gs->frameNum, 0)); - const auto height = argmax(size, maxHeightDiff) + 50.0f; // 50.0f is a leeway here + const auto height = argmax(size, maxHeightDiff); const auto& decal = decals.emplace_back(GroundDecal{ .posTL = posTL, @@ -1156,8 +1159,8 @@ void CGroundDecalHandler::CompactDecalsVector(int frameNum) numToDelete++; continue; } - const auto targetExpirationFrame = static_cast(decal.alpha / decal.alphaFalloff); - if (frameNum - decal.createFrameMax > targetExpirationFrame) { + const auto targetExpirationFrame = static_cast(decal.alpha / std::max(decal.alphaFalloff, 1e-6f)); + if (decal.info.type != GroundDecal::Type::DECAL_LUA && frameNum - decal.createFrameMax > targetExpirationFrame) { decal.MarkInvalid(); numToDelete++; } @@ -1176,18 +1179,22 @@ void CGroundDecalHandler::CompactDecalsVector(int frameNum) // after the compaction is complete spring::unordered_map tmpOwnerToId; - // Remove owners of expired items - for (auto doIt = decalOwners.begin(); doIt != decalOwners.end(); /*NOOP*/) { - if (const auto& decal = decals[doIt->second]; decal.IsValid()) { - const uint32_t id = decals.at(doIt->second).info.id; //can't use bitfield directly below - tmpOwnerToId.emplace(id, doIt->first); + // avoid erasing while iterating + std::vector eraseList; - doIt++; + // Remove owners of expired items + for (const auto& [owner, pos] : decalOwners) { + if (const auto& decal = decals.at(pos); decal.IsValid()) { + const uint32_t id = decal.info.id; //can't use bitfield directly below + tmpOwnerToId.emplace(id, owner); } else { - doIt = decalOwners.erase(doIt); + eraseList.emplace_back(owner); } } + for (const auto& owner : eraseList) { + decalOwners.erase(owner); + } // group all expired items towards the end of the vector // Lua items are not considered expired