Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.vp (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.vp (revision 25582) @@ -0,0 +1,22 @@ +!!ARBvp1.0 +#ifdef USE_INSTANCING + PARAM instancingTransform[4] = { program.local[0..3] }; + TEMP position; + DP4 position.x, instancingTransform[0], vertex.position; + DP4 position.y, instancingTransform[1], vertex.position; + DP4 position.z, instancingTransform[2], vertex.position; + MOV position.w, 1.0; +#else + ATTRIB position = vertex.position; +#endif + +PARAM transform[4] = { program.local[4..7] }; + +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; + +MOV result.color, vertex.color; + +END Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.vp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml (revision 25581) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid.xml (revision 25582) @@ -1,12 +1,12 @@ - + Index: ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml (revision 25581) +++ ps/trunk/binaries/data/mods/public/shaders/arb/model_solid_player.xml (revision 25582) @@ -1,14 +1,14 @@ - + Index: ps/trunk/binaries/data/mods/public/shaders/arb/solid.fp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/solid.fp (revision 25581) +++ ps/trunk/binaries/data/mods/public/shaders/arb/solid.fp (revision 25582) @@ -1,3 +1,7 @@ !!ARBfp1.0 -MOV result.color, fragment.color; + +PARAM color = program.local[0]; + +MOV result.color, color; + END Index: ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp (revision 25581) +++ ps/trunk/binaries/data/mods/public/shaders/arb/solid.vp (revision 25582) @@ -1,22 +1,14 @@ !!ARBvp1.0 -#ifdef USE_INSTANCING - PARAM instancingTransform[4] = { program.local[0..3] }; - TEMP position; - DP4 position.x, instancingTransform[0], vertex.position; - DP4 position.y, instancingTransform[1], vertex.position; - DP4 position.z, instancingTransform[2], vertex.position; - MOV position.w, 1.0; -#else - ATTRIB position = vertex.position; -#endif -PARAM transform[4] = { program.local[4..7] }; +PARAM transform[4] = { program.local[0..3] }; -DP4 result.position.x, transform[0], position; -DP4 result.position.y, transform[1], position; -DP4 result.position.z, transform[2], position; -DP4 result.position.w, transform[3], position; +TEMP position; -MOV result.color, vertex.color; +DP4 position.x, transform[0], vertex.position; +DP4 position.y, transform[1], vertex.position; +DP4 position.z, transform[2], vertex.position; +MOV position.w, 1.0; + +MOV result.position, position; END Index: ps/trunk/binaries/data/mods/public/shaders/arb/solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/solid.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/solid.xml (revision 25582) @@ -0,0 +1,13 @@ + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/solid.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/effects/solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/effects/solid.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/effects/solid.xml (revision 25582) @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/effects/solid.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/solid.vs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/solid.vs (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/solid.vs (revision 25582) @@ -0,0 +1,10 @@ +#version 110 + +uniform mat4 transform; + +attribute vec3 a_vertex; + +void main() +{ + gl_Position = transform * vec4(a_vertex, 1.0); +} Index: ps/trunk/binaries/data/mods/public/shaders/glsl/solid.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/solid.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/solid.xml (revision 25582) @@ -0,0 +1,11 @@ + + + + + + + + + + + Index: ps/trunk/source/ps/CStrInternStatic.h =================================================================== --- ps/trunk/source/ps/CStrInternStatic.h (revision 25581) +++ ps/trunk/source/ps/CStrInternStatic.h (revision 25582) @@ -1,176 +1,177 @@ /* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ // This file defines global CStrIntern variables, to avoid the cost of // constructing CStrInterns frequently at runtime. // // A line like // X(foo) // defines a variable str_foo with value "foo". // // A line like // X2(foo_0, "foo[0]") // defines a variable str_foo_0 with value "foo[0]". // For direct inclusion, we presumably just want the extern definitions. #ifndef X #include "CStrIntern.h" #define X(id) extern CStrIntern str_##id; #define X2(id, str) extern CStrIntern str_##id; #endif X(0) X(1) X(2) X(ALPHABLEND_PASS_BLEND) X(ALPHABLEND_PASS_OPAQUE) X(BLEND) X(BLOOM_NOP) X(BLOOM_PASS_H) X(BLOOM_PASS_V) X(DECAL) X(DISABLE_RECEIVE_SHADOWS) X(IGNORE_LOS) X(MINIMAP_BASE) X(MINIMAP_LINE) X(MINIMAP_LOS) X(MINIMAP_MASK) X(MINIMAP_POINT) X(MODE_SHADOWCAST) X(MODE_SILHOUETTEDISPLAY) X(MODE_SILHOUETTEOCCLUDER) X(MODE_WIREFRAME) X(SYS_HAS_ARB) X(SYS_HAS_GLSL) X(SYS_PREFER_GLSL) X(USE_FANCY_EFFECTS) X(USE_FP_SHADOW) X(USE_GPU_SKINNING) X(USE_INSTANCING) X(USE_NORMALS) X(USE_OBJECTCOLOR) X(USE_REAL_DEPTH) X(USE_REFLECTION) X(USE_REFRACTION) X(USE_SHADOW) X(USE_SHADOW_PCF) X(USE_SHADOW_SAMPLER) X(USE_FOG) X(WATERTYPE_CLAP) X(WATERTYPE_LAKE) X2(_emptystring, "") X(a_apexPosition) X(a_otherPosition) X(a_retreatPosition) X(a_skinJoints) X(a_skinWeights) X(a_splashPosition) X(a_tangent) X(a_waterInfo) X(ambient) X(baseTex) X(blendTex) X(bloom) X(blurTex2) X(blurTex4) X(blurTex8) X(brightness) X(cameraPos) X(color) X(colorAdd) X(colorMul) X(debug_line) X(debug_overlay) X(delta) X(depthTex) X(foamTex) X(fogColor) X(fogParams) X(foreground_overlay) X(gui_add) X(gui_basic) X(gui_grayscale) X(gui_solid) X(gui_solid_mask) X(gui_text) X(hdr) X(height) X(instancingTransform) X(losTex) X(losTex1) X(losTex2) X(losTransform) X(los_interp) X(mapSize) X(maskTex) X(maskTextureTransform) X(minimap) X(modelViewMatrix) X(murkiness) X(normalMap) X(normalMap2) X(objectColor) X(overlay_solid) X(particle) X(particle_solid) X(playerColor) X(pointSize) X(projInvTransform) X(qualityLevel) X(reflectionMap) X(reflectionMatrix) X(refractionMap) X(refractionMatrix) X(renderedTex) X(repeatScale) X2(sans_10, "sans-10"); X(saturation) X(screenSize) X(shadingColor) X(shadowScale) X(shadowTex) X(shadowTransform) X(sharpness) X(skinBlendMatrices) X2(skinBlendMatrices_0, "skinBlendMatrices[0]") X(skyBoxRot) X(skyCube) X(sky_simple) +X(solid) X(sunColor) X(sunDir) X(tex) X(texSize) X(textureTransform) X(time) X(tint) X(transform) X(translation) X(viewInvTransform) X(water_simple) X(waterEffectsTex) X(waterTex) X(waveTex) X(waviness) X(waveParams1) X(waveParams2) X(width) X(windAngle) X(zFar) X(zNear) #undef X #undef X2 Index: ps/trunk/source/renderer/DebugRenderer.cpp =================================================================== --- ps/trunk/source/renderer/DebugRenderer.cpp (revision 25581) +++ ps/trunk/source/renderer/DebugRenderer.cpp (revision 25582) @@ -1,406 +1,406 @@ /* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "renderer/DebugRenderer.h" #include "graphics/Camera.h" #include "graphics/Color.h" #include "graphics/ShaderManager.h" #include "graphics/ShaderProgram.h" #include "lib/ogl.h" #include "maths/BoundingBoxAligned.h" #include "maths/Brush.h" #include "maths/Matrix3D.h" #include "maths/Vector3D.h" #include "ps/CStrInternStatic.h" #include "renderer/Renderer.h" #include void CDebugRenderer::DrawLine(const CVector3D& from, const CVector3D& to, const CColor& color, const float width) { if (from == to) return; DrawLine({from, to}, color, width); } void CDebugRenderer::DrawLine(const std::vector& line, const CColor& color, const float width) { #if CONFIG2_GLES #warning TODO: implement drawing line for GLES #else CShaderTechniquePtr debugLineTech = g_Renderer.GetShaderManager().LoadEffect(str_debug_line); debugLineTech->BeginPass(); CShaderProgramPtr debugLineShader = debugLineTech->GetShader(); debugLineShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); debugLineShader->Uniform(str_color, color); const CVector3D cameraIn = g_Renderer.GetViewCamera().GetOrientation().GetIn(); std::vector vertices; vertices.reserve(line.size() * 6 * 3); #define ADD(position) \ vertices.emplace_back((position).X); \ vertices.emplace_back((position).Y); \ vertices.emplace_back((position).Z); for (size_t idx = 1; idx < line.size(); ++idx) { const CVector3D from = line[idx - 1]; const CVector3D to = line[idx]; const CVector3D direction = (to - from).Normalized(); const CVector3D view = direction.Dot(cameraIn) > 0.9f ? CVector3D(0.0f, 1.0f, 0.0f) : cameraIn; const CVector3D offset = view.Cross(direction).Normalized() * width; ADD(from + offset) ADD(to - offset) ADD(to + offset) ADD(from + offset) ADD(from - offset) ADD(to - offset) } #undef ADD debugLineShader->VertexPointer(3, GL_FLOAT, 0, vertices.data()); debugLineShader->AssertPointersBound(); glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3); debugLineTech->EndPass(); #endif } void CDebugRenderer::DrawCircle(const CVector3D& origin, const float radius, const CColor& color) { #if CONFIG2_GLES #warning TODO: implement drawing circle for GLES #else CShaderTechniquePtr debugCircleTech = g_Renderer.GetShaderManager().LoadEffect(str_debug_line); debugCircleTech->BeginPass(); const CCamera& camera = g_Renderer.GetViewCamera(); CShaderProgramPtr debugCircleShader = debugCircleTech->GetShader(); debugCircleShader->Uniform(str_transform, camera.GetViewProjection()); debugCircleShader->Uniform(str_color, color); const CVector3D cameraUp = camera.GetOrientation().GetUp(); const CVector3D cameraLeft = camera.GetOrientation().GetLeft(); std::vector vertices; #define ADD(position) \ vertices.emplace_back((position).X); \ vertices.emplace_back((position).Y); \ vertices.emplace_back((position).Z); ADD(origin) constexpr size_t segments = 16; for (size_t idx = 0; idx <= segments; ++idx) { const float angle = M_PI * 2.0f * idx / segments; const CVector3D offset = cameraUp * sin(angle) - cameraLeft * cos(angle); ADD(origin + offset * radius) } #undef ADD debugCircleShader->VertexPointer(3, GL_FLOAT, 0, vertices.data()); debugCircleShader->AssertPointersBound(); glDrawArrays(GL_TRIANGLE_FAN, 0, vertices.size() / 3); debugCircleTech->EndPass(); #endif } void CDebugRenderer::DrawCameraFrustum(const CCamera& camera, const CColor& color, int intermediates) { #if CONFIG2_GLES #warning TODO: implement camera frustum for GLES #else CCamera::Quad nearPoints; CCamera::Quad farPoints; camera.GetViewQuad(camera.GetNearPlane(), nearPoints); camera.GetViewQuad(camera.GetFarPlane(), farPoints); for(int i = 0; i < 4; i++) { nearPoints[i] = camera.m_Orientation.Transform(nearPoints[i]); farPoints[i] = camera.m_Orientation.Transform(farPoints[i]); } CShaderTechniquePtr overlayTech = g_Renderer.GetShaderManager().LoadEffect(str_debug_line); overlayTech->BeginPass(); CShaderProgramPtr overlayShader = overlayTech->GetShader(); overlayShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); overlayShader->Uniform(str_color, color); std::vector vertices; #define ADD(position) \ vertices.emplace_back((position).X); \ vertices.emplace_back((position).Y); \ vertices.emplace_back((position).Z); // Near plane. ADD(nearPoints[0]); ADD(nearPoints[1]); ADD(nearPoints[2]); ADD(nearPoints[3]); // Far plane. ADD(farPoints[0]); ADD(farPoints[1]); ADD(farPoints[2]); ADD(farPoints[3]); // Intermediate planes. CVector3D intermediatePoints[4]; for(int i = 0; i < intermediates; ++i) { const float t = (i + 1.0f) / (intermediates + 1.0f); for(int j = 0; j < 4; ++j) intermediatePoints[j] = nearPoints[j] * t + farPoints[j] * (1.0f - t); ADD(intermediatePoints[0]); ADD(intermediatePoints[1]); ADD(intermediatePoints[2]); ADD(intermediatePoints[3]); } overlayShader->VertexPointer(3, GL_FLOAT, 0, vertices.data()); overlayShader->AssertPointersBound(); glDrawArrays(GL_QUADS, 0, vertices.size() / 3); vertices.clear(); // Connection lines. ADD(nearPoints[0]); ADD(farPoints[0]); ADD(nearPoints[1]); ADD(farPoints[1]); ADD(nearPoints[2]); ADD(farPoints[2]); ADD(nearPoints[3]); ADD(farPoints[3]); ADD(nearPoints[0]); ADD(farPoints[0]); overlayShader->VertexPointer(3, GL_FLOAT, 0, vertices.data()); overlayShader->AssertPointersBound(); glDrawArrays(GL_QUAD_STRIP, 0, vertices.size() / 3); #undef ADD overlayTech->EndPass(); #endif } void CDebugRenderer::DrawBoundingBox(const CBoundingBoxAligned& boundingBox, const CColor& color) { DrawBoundingBox(boundingBox, color, g_Renderer.GetViewCamera().GetViewProjection()); } void CDebugRenderer::DrawBoundingBox(const CBoundingBoxAligned& boundingBox, const CColor& color, const CMatrix3D& transform) { - CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_solid); shaderTech->BeginPass(); CShaderProgramPtr shader = shaderTech->GetShader(); shader->Uniform(str_color, color); shader->Uniform(str_transform, transform); std::vector data; #define ADD_FACE(x, y, z) \ ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \ ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z); #define ADD_PT(u_, v_, x, y, z) \ STMT(int u = u_; int v = v_; \ data.push_back(u); \ data.push_back(v); \ data.push_back(boundingBox[x].X); \ data.push_back(boundingBox[y].Y); \ data.push_back(boundingBox[z].Z); \ ) ADD_FACE(u, v, 0); ADD_FACE(0, u, v); ADD_FACE(u, 0, 1-v); ADD_FACE(u, 1-v, 1); ADD_FACE(1, u, 1-v); ADD_FACE(u, 1, v); #undef ADD_FACE shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_TRIANGLES, 0, 6*6); shaderTech->EndPass(); } void CDebugRenderer::DrawBoundingBoxOutline(const CBoundingBoxAligned& boundingBox, const CColor& color) { DrawBoundingBoxOutline(boundingBox, color, g_Renderer.GetViewCamera().GetViewProjection()); } void CDebugRenderer::DrawBoundingBoxOutline(const CBoundingBoxAligned& boundingBox, const CColor& color, const CMatrix3D& transform) { - CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_solid); shaderTech->BeginPass(); CShaderProgramPtr shader = shaderTech->GetShader(); shader->Uniform(str_color, color); shader->Uniform(str_transform, transform); std::vector data; #define ADD_FACE(x, y, z) \ ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); \ ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \ ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); \ ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z); #define ADD_PT(u_, v_, x, y, z) \ STMT(int u = u_; int v = v_; \ data.push_back(u); \ data.push_back(v); \ data.push_back(boundingBox[x].X); \ data.push_back(boundingBox[y].Y); \ data.push_back(boundingBox[z].Z); \ ) ADD_FACE(u, v, 0); ADD_FACE(0, u, v); ADD_FACE(u, 0, 1-v); ADD_FACE(u, 1-v, 1); ADD_FACE(1, u, 1-v); ADD_FACE(u, 1, v); #undef ADD_FACE shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_LINES, 0, 6*8); shaderTech->EndPass(); } void CDebugRenderer::DrawBrush(const CBrush& brush, const CColor& color) { - CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_solid); shaderTech->BeginPass(); CShaderProgramPtr shader = shaderTech->GetShader(); shader->Uniform(str_color, color); shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); std::vector data; std::vector> faces; brush.GetFaces(faces); #define ADD_VERT(a) \ STMT( \ data.push_back(u); \ data.push_back(v); \ data.push_back(brush.GetVertices()[faces[i][a]].X); \ data.push_back(brush.GetVertices()[faces[i][a]].Y); \ data.push_back(brush.GetVertices()[faces[i][a]].Z); \ ) for (size_t i = 0; i < faces.size(); ++i) { // Triangulate into (0,1,2), (0,2,3), ... for (size_t j = 1; j < faces[i].size() - 2; ++j) { float u = 0; float v = 0; ADD_VERT(0); ADD_VERT(j); ADD_VERT(j+1); } } #undef ADD_VERT shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_TRIANGLES, 0, data.size() / 5); shaderTech->EndPass(); } void CDebugRenderer::DrawBrushOutline(const CBrush& brush, const CColor& color) { - CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_solid); shaderTech->BeginPass(); CShaderProgramPtr shader = shaderTech->GetShader(); shader->Uniform(str_color, color); shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); std::vector data; std::vector> faces; brush.GetFaces(faces); #define ADD_VERT(a) \ STMT( \ data.push_back(u); \ data.push_back(v); \ data.push_back(brush.GetVertices()[faces[i][a]].X); \ data.push_back(brush.GetVertices()[faces[i][a]].Y); \ data.push_back(brush.GetVertices()[faces[i][a]].Z); \ ) for (size_t i = 0; i < faces.size(); ++i) { for (size_t j = 0; j < faces[i].size() - 1; ++j) { float u = 0; float v = 0; ADD_VERT(j); ADD_VERT(j+1); } } #undef ADD_VERT shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 5*sizeof(float), &data[0]); shader->VertexPointer(3, GL_FLOAT, 5*sizeof(float), &data[2]); shader->AssertPointersBound(); glDrawArrays(GL_LINES, 0, data.size() / 5); shaderTech->EndPass(); } Index: ps/trunk/source/renderer/SilhouetteRenderer.cpp =================================================================== --- ps/trunk/source/renderer/SilhouetteRenderer.cpp (revision 25581) +++ ps/trunk/source/renderer/SilhouetteRenderer.cpp (revision 25582) @@ -1,499 +1,499 @@ /* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "SilhouetteRenderer.h" #include "graphics/Camera.h" #include "graphics/HFTracer.h" #include "graphics/Model.h" #include "graphics/Patch.h" #include "graphics/ShaderManager.h" #include "maths/MathUtil.h" #include "ps/CStrInternStatic.h" #include "ps/Profile.h" #include "renderer/DebugRenderer.h" #include "renderer/Renderer.h" #include "renderer/Scene.h" #include extern int g_xres, g_yres; // For debugging static const bool g_DisablePreciseIntersections = false; SilhouetteRenderer::SilhouetteRenderer() { m_DebugEnabled = false; } void SilhouetteRenderer::AddOccluder(CPatch* patch) { m_SubmittedPatchOccluders.push_back(patch); } void SilhouetteRenderer::AddOccluder(CModel* model) { m_SubmittedModelOccluders.push_back(model); } void SilhouetteRenderer::AddCaster(CModel* model) { m_SubmittedModelCasters.push_back(model); } /* * Silhouettes are the solid-colored versions of units that are rendered when * standing behind a building or terrain, so the player won't lose them. * * The rendering is done in CRenderer::RenderSilhouettes, by rendering the * units (silhouette casters) and buildings/terrain (silhouette occluders) * in an extra pass using depth and stencil buffers. It's very inefficient to * render those objects when they're not actually going to contribute to a * silhouette. * * This class is responsible for finding the subset of casters/occluders * that might contribute to a silhouette and will need to be rendered. * * The algorithm is largely based on sweep-and-prune for detecting intersection * along a single axis: * * First we compute the 2D screen-space bounding box of every occluder, and * their minimum distance from the camera. We also compute the screen-space * position of each caster (approximating them as points, which is not perfect * but almost always good enough). * * We split each occluder's screen-space bounds into a left ('in') edge and * right ('out') edge. We put those edges plus the caster points into a list, * and sort by x coordinate. * * Then we walk through the list, maintaining an active set of occluders. * An 'in' edge will add an occluder to the set, an 'out' edge will remove it. * When we reach a caster point, the active set contains all the occluders that * intersect it in x. We do a quick test of y and depth coordinates against * each occluder in the set. If they pass that test, we do a more precise ray * vs bounding box test (for model occluders) or ray vs patch (for terrain * occluders) to see if we really need to render that caster and occluder. * * Performance relies on the active set being quite small. Given the game's * typical occluder sizes and camera angles, this works out okay. * * We have to do precise ray/patch intersection tests for terrain, because * if we just used the patch's bounding box, pretty much every unit would * be seen as intersecting the patch it's standing on. * * We store screen-space coordinates as 14-bit integers (0..16383) because * that lets us pack and sort the edge/point list efficiently. */ static const u16 g_MaxCoord = 1 << 14; static const u16 g_HalfMaxCoord = g_MaxCoord / 2; struct Occluder { CRenderableObject* renderable; bool isPatch; u16 x0, y0, x1, y1; float z; bool rendered; }; struct Caster { CModel* model; u16 x, y; float z; bool rendered; }; enum { EDGE_IN, EDGE_OUT, POINT }; // Entry is essentially: // struct Entry { // u16 id; // index into occluders array // u16 type : 2; // u16 x : 14; // }; // where x is in the most significant bits, so that sorting as a uint32_t // is the same as sorting by x. To avoid worrying about endianness and the // compiler's ability to handle bitfields efficiently, we use uint32_t instead // of the actual struct. typedef uint32_t Entry; static Entry EntryCreate(int type, u16 id, u16 x) { return (x << 18) | (type << 16) | id; } static int EntryGetId(Entry e) { return e & 0xffff; } static int EntryGetType(Entry e) { return (e >> 16) & 3; } struct ActiveList { std::vector m_Ids; void Add(u16 id) { m_Ids.push_back(id); } void Remove(u16 id) { ssize_t sz = m_Ids.size(); for (ssize_t i = sz-1; i >= 0; --i) { if (m_Ids[i] == id) { m_Ids[i] = m_Ids[sz-1]; m_Ids.pop_back(); return; } } debug_warn(L"Failed to find id"); } }; static void ComputeScreenBounds(Occluder& occluder, const CBoundingBoxAligned& bounds, CMatrix3D& proj) { u16 x0 = std::numeric_limits::max(); u16 y0 = std::numeric_limits::max(); u16 x1 = std::numeric_limits::min(); u16 y1 = std::numeric_limits::min(); float z0 = std::numeric_limits::max(); for (size_t ix = 0; ix <= 1; ++ix) { for (size_t iy = 0; iy <= 1; ++iy) { for (size_t iz = 0; iz <= 1; ++iz) { CVector4D svec = proj.Transform(CVector4D(bounds[ix].X, bounds[iy].Y, bounds[iz].Z, 1.0f)); x0 = std::min(x0, static_cast(g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.X / svec.W))); y0 = std::min(y0, static_cast(g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.Y / svec.W))); x1 = std::max(x1, static_cast(g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.X / svec.W))); y1 = std::max(y1, static_cast(g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.Y / svec.W))); z0 = std::min(z0, svec.Z / svec.W); } } } // TODO: there must be a quicker way to do this than to test every vertex, // given the symmetry of the bounding box occluder.x0 = Clamp(x0, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); occluder.y0 = Clamp(y0, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); occluder.x1 = Clamp(x1, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); occluder.y1 = Clamp(y1, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); occluder.z = z0; } static void ComputeScreenPos(Caster& caster, const CVector3D& pos, CMatrix3D& proj) { CVector4D svec = proj.Transform(CVector4D(pos.X, pos.Y, pos.Z, 1.0f)); u16 x = g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.X / svec.W); u16 y = g_HalfMaxCoord + static_cast(g_HalfMaxCoord * svec.Y / svec.W); caster.x = Clamp(x, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); caster.y = Clamp(y, std::numeric_limits::min(), static_cast(g_MaxCoord - 1)); caster.z = svec.Z / svec.W; } void SilhouetteRenderer::ComputeSubmissions(const CCamera& camera) { PROFILE3("compute silhouettes"); m_DebugBounds.clear(); m_DebugRects.clear(); m_DebugSpheres.clear(); m_VisiblePatchOccluders.clear(); m_VisibleModelOccluders.clear(); m_VisibleModelCasters.clear(); std::vector occluders; std::vector casters; std::vector entries; occluders.reserve(m_SubmittedModelOccluders.size() + m_SubmittedPatchOccluders.size()); casters.reserve(m_SubmittedModelCasters.size()); entries.reserve((m_SubmittedModelOccluders.size() + m_SubmittedPatchOccluders.size()) * 2 + m_SubmittedModelCasters.size()); CMatrix3D proj = camera.GetViewProjection(); // Bump the positions of unit casters upwards a bit, so they're not always // detected as intersecting the terrain they're standing on CVector3D posOffset(0.0f, 0.1f, 0.0f); #if 0 // For debugging ray-patch intersections - casts a ton of rays and draws // a sphere where they intersect for (int y = 0; y < g_yres; y += 8) { for (int x = 0; x < g_xres; x += 8) { SOverlaySphere sphere; sphere.m_Color = CColor(1, 0, 0, 1); sphere.m_Radius = 0.25f; sphere.m_Center = camera.GetWorldCoordinates(x, y, false); CVector3D origin, dir; camera.BuildCameraRay(x, y, origin, dir); for (size_t i = 0; i < m_SubmittedPatchOccluders.size(); ++i) { CPatch* occluder = m_SubmittedPatchOccluders[i]; if (CHFTracer::PatchRayIntersect(occluder, origin, dir, &sphere.m_Center)) sphere.m_Color = CColor(0, 0, 1, 1); } m_DebugSpheres.push_back(sphere); } } #endif { PROFILE("compute bounds"); for (size_t i = 0; i < m_SubmittedModelOccluders.size(); ++i) { CModel* occluder = m_SubmittedModelOccluders[i]; Occluder d; d.renderable = occluder; d.isPatch = false; d.rendered = false; ComputeScreenBounds(d, occluder->GetWorldBounds(), proj); // Skip zero-sized occluders, so we don't need to worry about EDGE_OUT // getting sorted before EDGE_IN if (d.x0 == d.x1 || d.y0 == d.y1) continue; u16 id = static_cast(occluders.size()); occluders.push_back(d); entries.push_back(EntryCreate(EDGE_IN, id, d.x0)); entries.push_back(EntryCreate(EDGE_OUT, id, d.x1)); } for (size_t i = 0; i < m_SubmittedPatchOccluders.size(); ++i) { CPatch* occluder = m_SubmittedPatchOccluders[i]; Occluder d; d.renderable = occluder; d.isPatch = true; d.rendered = false; ComputeScreenBounds(d, occluder->GetWorldBounds(), proj); // Skip zero-sized occluders if (d.x0 == d.x1 || d.y0 == d.y1) continue; u16 id = static_cast(occluders.size()); occluders.push_back(d); entries.push_back(EntryCreate(EDGE_IN, id, d.x0)); entries.push_back(EntryCreate(EDGE_OUT, id, d.x1)); } for (size_t i = 0; i < m_SubmittedModelCasters.size(); ++i) { CModel* model = m_SubmittedModelCasters[i]; CVector3D pos = model->GetTransform().GetTranslation() + posOffset; Caster d; d.model = model; d.rendered = false; ComputeScreenPos(d, pos, proj); u16 id = static_cast(casters.size()); casters.push_back(d); entries.push_back(EntryCreate(POINT, id, d.x)); } } // Make sure the u16 id didn't overflow ENSURE(occluders.size() < 65536 && casters.size() < 65536); { PROFILE("sorting"); std::sort(entries.begin(), entries.end()); } { PROFILE("sweeping"); ActiveList active; CVector3D cameraPos = camera.GetOrientation().GetTranslation(); for (size_t i = 0; i < entries.size(); ++i) { Entry e = entries[i]; int type = EntryGetType(e); u16 id = EntryGetId(e); if (type == EDGE_IN) active.Add(id); else if (type == EDGE_OUT) active.Remove(id); else { Caster& caster = casters[id]; for (size_t j = 0; j < active.m_Ids.size(); ++j) { Occluder& occluder = occluders[active.m_Ids[j]]; if (caster.y < occluder.y0 || caster.y > occluder.y1) continue; if (caster.z < occluder.z) continue; // No point checking further if both are already being rendered if (caster.rendered && occluder.rendered) continue; if (!g_DisablePreciseIntersections) { CVector3D pos = caster.model->GetTransform().GetTranslation() + posOffset; if (occluder.isPatch) { CPatch* patch = static_cast(occluder.renderable); if (!CHFTracer::PatchRayIntersect(patch, pos, cameraPos - pos, NULL)) continue; } else { float tmin, tmax; if (!occluder.renderable->GetWorldBounds().RayIntersect(pos, cameraPos - pos, tmin, tmax)) continue; } } caster.rendered = true; occluder.rendered = true; } } } } if (m_DebugEnabled) { for (size_t i = 0; i < occluders.size(); ++i) { DebugRect r; r.color = occluders[i].rendered ? CColor(1.0f, 1.0f, 0.0f, 1.0f) : CColor(0.2f, 0.2f, 0.0f, 1.0f); r.x0 = occluders[i].x0; r.y0 = occluders[i].y0; r.x1 = occluders[i].x1; r.y1 = occluders[i].y1; m_DebugRects.push_back(r); DebugBounds b; b.color = r.color; b.bounds = occluders[i].renderable->GetWorldBounds(); m_DebugBounds.push_back(b); } } for (size_t i = 0; i < occluders.size(); ++i) { if (occluders[i].rendered) { if (occluders[i].isPatch) m_VisiblePatchOccluders.push_back(static_cast(occluders[i].renderable)); else m_VisibleModelOccluders.push_back(static_cast(occluders[i].renderable)); } } for (size_t i = 0; i < casters.size(); ++i) if (casters[i].rendered) m_VisibleModelCasters.push_back(casters[i].model); } void SilhouetteRenderer::RenderSubmitOverlays(SceneCollector& collector) { for (size_t i = 0; i < m_DebugSpheres.size(); i++) collector.Submit(&m_DebugSpheres[i]); } void SilhouetteRenderer::RenderSubmitOccluders(SceneCollector& collector) { for (size_t i = 0; i < m_VisiblePatchOccluders.size(); ++i) collector.Submit(m_VisiblePatchOccluders[i]); for (size_t i = 0; i < m_VisibleModelOccluders.size(); ++i) collector.SubmitNonRecursive(m_VisibleModelOccluders[i]); } void SilhouetteRenderer::RenderSubmitCasters(SceneCollector& collector) { for (size_t i = 0; i < m_VisibleModelCasters.size(); ++i) collector.SubmitNonRecursive(m_VisibleModelCasters[i]); } void SilhouetteRenderer::RenderDebugOverlays(const CCamera& UNUSED(camera)) { if (m_DebugBounds.empty() && m_DebugRects.empty()) return; glDepthMask(0); glDisable(GL_CULL_FACE); for (size_t i = 0; i < m_DebugBounds.size(); ++i) g_Renderer.GetDebugRenderer().DrawBoundingBoxOutline(m_DebugBounds[i].bounds, m_DebugBounds[i].color); CMatrix3D m; m.SetIdentity(); m.Scale(1.0f, -1.f, 1.0f); m.Translate(0.0f, (float)g_yres, -1000.0f); CMatrix3D proj; proj.SetOrtho(0.f, g_MaxCoord, 0.f, g_MaxCoord, -1.f, 1000.f); m = proj * m; - CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_solid); shaderTech->BeginPass(); CShaderProgramPtr shader = shaderTech->GetShader(); shader->Uniform(str_transform, proj); for (size_t i = 0; i < m_DebugRects.size(); ++i) { const DebugRect& r = m_DebugRects[i]; shader->Uniform(str_color, r.color); u16 verts[] = { r.x0, r.y0, r.x1, r.y0, r.x1, r.y1, r.x0, r.y1, r.x0, r.y0, }; shader->VertexPointer(2, GL_SHORT, 0, verts); glDrawArrays(GL_LINE_STRIP, 0, 5); } shaderTech->EndPass(); glEnable(GL_CULL_FACE); glDepthMask(1); } void SilhouetteRenderer::EndFrame() { m_SubmittedPatchOccluders.clear(); m_SubmittedModelOccluders.clear(); m_SubmittedModelCasters.clear(); } Index: ps/trunk/source/renderer/TerrainRenderer.cpp =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 25581) +++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 25582) @@ -1,630 +1,630 @@ /* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * Terrain rendering (everything related to patches and water) is * encapsulated in TerrainRenderer */ #include "precompiled.h" #include "renderer/TerrainRenderer.h" #include "graphics/Camera.h" #include "graphics/Decal.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/LOSTexture.h" #include "graphics/Patch.h" #include "graphics/Model.h" #include "graphics/ShaderManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" #include "ps/Filesystem.h" #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" #include "renderer/DecalRData.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" #include "renderer/VertexArray.h" #include "renderer/WaterManager.h" namespace { CShaderProgramPtr GetDummyShader() { const char* shaderName; if (g_RenderingOptions.GetPreferGLSL()) shaderName = "glsl/dummy"; else shaderName = "arb/dummy"; return g_Renderer.GetShaderManager().LoadProgram(shaderName, CShaderDefines()); } } // anonymous namespace /** * TerrainRenderer keeps track of which phase it is in, to detect * when Submit, PrepareForRendering etc. are called in the wrong order. */ enum Phase { Phase_Submit, Phase_Render }; /** * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class. */ struct TerrainRendererInternals { /// Which phase (submitting or rendering patches) are we in right now? Phase phase; /// Patches that were submitted for this frame std::vector visiblePatches[CRenderer::CULL_MAX]; /// Decals that were submitted for this frame std::vector visibleDecals[CRenderer::CULL_MAX]; /// Fancy water shader CShaderProgramPtr fancyWaterShader; CSimulation2* simulation; }; /////////////////////////////////////////////////////////////////// // Construction/Destruction TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; } TerrainRenderer::~TerrainRenderer() { delete m; } void TerrainRenderer::SetSimulation(CSimulation2* simulation) { m->simulation = simulation; } /////////////////////////////////////////////////////////////////// // Submit a patch for rendering void TerrainRenderer::Submit(int cullGroup, CPatch* patch) { ENSURE(m->phase == Phase_Submit); CPatchRData* data = (CPatchRData*)patch->GetRenderData(); if (data == 0) { // no renderdata for patch, create it now data = new CPatchRData(patch, m->simulation); patch->SetRenderData(data); } data->Update(m->simulation); m->visiblePatches[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Submit a decal for rendering void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal) { ENSURE(m->phase == Phase_Submit); CDecalRData* data = (CDecalRData*)decal->GetRenderData(); if (data == 0) { // no renderdata for decal, create it now data = new CDecalRData(decal, m->simulation); decal->SetRenderData(data); } data->Update(m->simulation); m->visibleDecals[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Prepare for rendering void TerrainRenderer::PrepareForRendering() { ENSURE(m->phase == Phase_Submit); m->phase = Phase_Render; } /////////////////////////////////////////////////////////////////// // Clear submissions lists void TerrainRenderer::EndFrame() { ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit); for (int i = 0; i < CRenderer::CULL_MAX; ++i) { m->visiblePatches[i].clear(); m->visibleDecals[i].clear(); } m->phase = Phase_Submit; } void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix, GLuint texture) { #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES UNUSED2(cullGroup); UNUSED2(textureMatrix); UNUSED2(texture); #else ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(0); glDisable(GL_DEPTH_TEST); CShaderTechniquePtr debugOverlayTech = g_Renderer.GetShaderManager().LoadEffect(str_debug_overlay); debugOverlayTech->BeginPass(); CShaderProgramPtr debugOverlayShader = debugOverlayTech->GetShader(); debugOverlayShader->Bind(); debugOverlayShader->BindTexture(str_baseTex, texture); debugOverlayShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); debugOverlayShader->Uniform(str_textureTransform, textureMatrix); CPatchRData::RenderStreams(visiblePatches, debugOverlayShader, STREAM_POS | STREAM_POSTOUV0); glEnable(GL_DEPTH_TEST); // To make the overlay visible over water, render an additional map-sized // water-height patch. CBoundingBoxAligned waterBounds; for (CPatchRData* data : visiblePatches) waterBounds += data->GetWaterBounds(); if (!waterBounds.IsEmpty()) { // Add a delta to avoid z-fighting. const float height = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; const float waterPos[] = { waterBounds[0].X, height, waterBounds[0].Z, waterBounds[1].X, height, waterBounds[0].Z, waterBounds[0].X, height, waterBounds[1].Z, waterBounds[1].X, height, waterBounds[1].Z }; const GLsizei stride = sizeof(float) * 3; debugOverlayShader->VertexPointer(3, GL_FLOAT, stride, waterPos); debugOverlayShader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, waterPos); debugOverlayShader->AssertPointersBound(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } debugOverlayShader->Unbind(); debugOverlayTech->EndPass(); glDepthMask(1); glDisable(GL_BLEND); #endif } /////////////////////////////////////////////////////////////////// /** * Set up all the uniforms for a shader pass. */ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow) { shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shader->Uniform(str_cameraPos, g_Renderer.GetViewCamera().GetOrientation().GetTranslation()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); if (shadow) shadow->BindTo(shader); CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); shader->BindTexture(str_losTex, los.GetTextureSmooth()); shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shader->Uniform(str_ambient, lightEnv.m_AmbientColor); shader->Uniform(str_sunColor, lightEnv.m_SunColor); shader->Uniform(str_sunDir, lightEnv.GetSunDir()); shader->Uniform(str_fogColor, lightEnv.m_FogColor); shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); } void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; std::vector& visibleDecals = m->visibleDecals[cullGroup]; if (visiblePatches.empty() && visibleDecals.empty()) return; // render the solid black sides of the map first - CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); + CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_solid); techSolid->BeginPass(); CShaderProgramPtr shaderSolid = techSolid->GetShader(); shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); CPatchRData::RenderSides(visiblePatches, shaderSolid); techSolid->EndPass(); CPatchRData::RenderBases(visiblePatches, context, shadow); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch CPatchRData::RenderBlends(visiblePatches, context, shadow); CDecalRData::RenderDecals(visibleDecals, context, shadow); // restore OpenGL state g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(2, 0); g_Renderer.BindTexture(3, 0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } /////////////////////////////////////////////////////////////////// // Render un-textured patches as polygons void TerrainRenderer::RenderPatches(int cullGroup, const CColor& color) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderPatches for GLES #else CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, color); CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS); dummyShader->Unbind(); #endif } /////////////////////////////////////////////////////////////////// // Render outlines of submitted patches as lines void TerrainRenderer::RenderOutlines(int cullGroup) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderOutline(); } /////////////////////////////////////////////////////////////////// // Scissor rectangle of water patches CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CCamera& camera) { CBoundingBoxAligned scissor; for (const CPatchRData* data : m->visiblePatches[cullGroup]) { const CBoundingBoxAligned& waterBounds = data->GetWaterBounds(); if (waterBounds.IsEmpty()) continue; const CBoundingBoxAligned waterBoundsInViewPort = camera.GetBoundsInViewPort(waterBounds); if (!waterBoundsInViewPort.IsEmpty()) scissor += waterBoundsInViewPort; } return CBoundingBoxAligned( CVector3D(Clamp(scissor[0].X, -1.0f, 1.0f), Clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f), CVector3D(Clamp(scissor[1].X, -1.0f, 1.0f), Clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f)); } // Render fancy water bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { PROFILE3_GPU("fancy water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CShaderDefines defines = context; // If we're using fancy water, make sure its shader is loaded if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading) { if (WaterMgr->m_WaterRealDepth) defines.Add(str_USE_REAL_DEPTH, str_1); if (WaterMgr->m_WaterFancyEffects) defines.Add(str_USE_FANCY_EFFECTS, str_1); if (WaterMgr->m_WaterRefraction) defines.Add(str_USE_REFRACTION, str_1); if (WaterMgr->m_WaterReflection) defines.Add(str_USE_REFLECTION, str_1); // haven't updated the ARB shader yet so I'll always load the GLSL /*if (!g_RenderingOptions.GetPreferGLSL() && !superFancy) m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines); else*/ m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines); if (!m->fancyWaterShader) { LOGERROR("Failed to load water shader. Falling back to fixed pipeline water.\n"); WaterMgr->m_RenderWater = false; return false; } WaterMgr->m_NeedsReloading = false; } CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); // Calculating the advanced informations about Foam and all if the quality calls for it. /*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)) { WaterMgr->m_NeedInfoUpdate = false; WaterMgr->CreateSuperfancyInfo(); }*/ double time = WaterMgr->m_WaterTexTimer; double period = 8; int curTex = (int)(time*60/period) % 60; int nexTex = (curTex + 1) % 60; float repeatPeriod = WaterMgr->m_RepeatPeriod; // Render normals and foam to a framebuffer if we're in fancy effects if (WaterMgr->m_WaterFancyEffects) { // Save the post-processing framebuffer. GLint fbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, WaterMgr->m_FancyEffectsFBO); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glDisable(GL_CULL_FACE); // Overwrite waves that would be behind the ground. CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("glsl/gui_solid", CShaderDefines()); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(dummyShader, true, true); } dummyShader->Unbind(); glEnable(GL_CULL_FACE); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); m->fancyWaterShader->Bind(); const CCamera& camera = g_Renderer.GetViewCamera(); m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]); m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]); if (WaterMgr->m_WaterFancyEffects) { m->fancyWaterShader->BindTexture(str_waterEffectsTex, WaterMgr->m_FancyTexture); } if (WaterMgr->m_WaterRefraction && WaterMgr->m_WaterRealDepth) { m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_RefrFboDepthTexture); m->fancyWaterShader->Uniform(str_projInvTransform, WaterMgr->m_RefractionProjInvMatrix); m->fancyWaterShader->Uniform(str_viewInvTransform, WaterMgr->m_RefractionViewInvMatrix); } if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture); m->fancyWaterShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); m->fancyWaterShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube()); // TODO: check that this rotates in the right direction. CMatrix3D skyBoxRotation; skyBoxRotation.SetIdentity(); skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation()); m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation); if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix); m->fancyWaterShader->Uniform(str_ambient, lightEnv.m_AmbientColor); m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir()); m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor); m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor); m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint); m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness); m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness); m->fancyWaterShader->Uniform(str_windAngle, WaterMgr->m_WindAngle); m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod); m->fancyWaterShader->Uniform(str_losTransform, losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12], 0.f, 0.f); m->fancyWaterShader->Uniform(str_cameraPos, camera.GetOrientation().GetTranslation()); m->fancyWaterShader->Uniform(str_fogColor, lightEnv.m_FogColor); m->fancyWaterShader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); m->fancyWaterShader->Uniform(str_time, (float)time); m->fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f); if (WaterMgr->m_WaterType == L"clap") { m->fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f); } else if (WaterMgr->m_WaterType == L"lake") { m->fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f); } else { m->fancyWaterShader->Uniform(str_waveParams1, 15.0f,0.8f,10.0f,0.1f); m->fancyWaterShader->Uniform(str_waveParams2, 0.3f,0.0f,0.1f,0.3f); } if (shadow) shadow->BindTo(m->fancyWaterShader); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(m->fancyWaterShader); } m->fancyWaterShader->Unbind(); glDepthFunc(GL_LEQUAL); glDisable(GL_BLEND); return true; } void TerrainRenderer::RenderSimpleWater(int cullGroup) { #if CONFIG2_GLES UNUSED2(cullGroup); #else PROFILE3_GPU("simple water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); double time = WaterMgr->m_WaterTexTimer; double period = 1.6f; int curTex = (int)(time*60/period) % 60; CShaderTechniquePtr waterSimpleTech = g_Renderer.GetShaderManager().LoadEffect(str_water_simple); waterSimpleTech->BeginPass(); CShaderProgramPtr waterSimpleShader = waterSimpleTech->GetShader(); waterSimpleShader->Bind(); waterSimpleShader->BindTexture(str_baseTex, WaterMgr->m_WaterTexture[curTex]); waterSimpleShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); waterSimpleShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); waterSimpleShader->Uniform(str_losTransform, losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12], 0.f, 0.f); waterSimpleShader->Uniform(str_time, static_cast(time)); waterSimpleShader->Uniform(str_color, WaterMgr->m_WaterColor); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(waterSimpleShader, false, true); } waterSimpleShader->Unbind(); g_Renderer.BindTexture(1, 0); pglActiveTextureARB(GL_TEXTURE0_ARB); glDisable(GL_TEXTURE_2D); waterSimpleTech->EndPass(); #endif } /////////////////////////////////////////////////////////////////// // Render water that is part of the terrain void TerrainRenderer::RenderWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { WaterManager* WaterMgr = g_Renderer.GetWaterManager(); WaterMgr->UpdateQuality(); if (!WaterMgr->WillRenderFancyWater()) RenderSimpleWater(cullGroup); else RenderFancyWater(context, cullGroup, shadow); } void TerrainRenderer::RenderPriorities(int cullGroup) { PROFILE("priorities"); ENSURE(m->phase == Phase_Render); CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); tech->BeginPass(); CTextRenderer textRenderer(tech->GetShader()); textRenderer.Font(CStrIntern("mono-stroke-10")); textRenderer.Color(1.0f, 1.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderPriorities(textRenderer); textRenderer.Render(); tech->EndPass(); }