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();
}