diff --git a/binaries/data/mods/public/shaders/arb/overlay_solid.fp b/binaries/data/mods/public/shaders/arb/overlay_solid.fp
new file mode 100644
index 0000000000..8c80de1116
--- /dev/null
+++ b/binaries/data/mods/public/shaders/arb/overlay_solid.fp
@@ -0,0 +1,7 @@
+!!ARBfp1.0
+
+PARAM color = program.local[0];
+
+MOV result.color, color;
+
+END
diff --git a/binaries/data/mods/public/shaders/arb/overlay_solid.vp b/binaries/data/mods/public/shaders/arb/overlay_solid.vp
new file mode 100644
index 0000000000..4f72886cdc
--- /dev/null
+++ b/binaries/data/mods/public/shaders/arb/overlay_solid.vp
@@ -0,0 +1,17 @@
+!!ARBvp1.0
+
+PARAM transform[4] = { program.local[0..3] };
+
+TEMP position;
+
+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;
+
+DP4 result.position.x, state.matrix.mvp.row[0], position;
+DP4 result.position.y, state.matrix.mvp.row[1], position;
+DP4 result.position.z, state.matrix.mvp.row[2], position;
+DP4 result.position.w, state.matrix.mvp.row[3], position;
+
+END
diff --git a/binaries/data/mods/public/shaders/arb/overlay_solid.xml b/binaries/data/mods/public/shaders/arb/overlay_solid.xml
new file mode 100644
index 0000000000..e191dd5115
--- /dev/null
+++ b/binaries/data/mods/public/shaders/arb/overlay_solid.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/effects/overlay_solid.xml b/binaries/data/mods/public/shaders/effects/overlay_solid.xml
new file mode 100644
index 0000000000..777cbdc785
--- /dev/null
+++ b/binaries/data/mods/public/shaders/effects/overlay_solid.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/source/graphics/Camera.cpp b/source/graphics/Camera.cpp
index ea96ffc003..036849a49e 100644
--- a/source/graphics/Camera.cpp
+++ b/source/graphics/Camera.cpp
@@ -126,11 +126,16 @@ void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)
m_ViewFrustum.m_aPlanes[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;
m_ViewFrustum.m_aPlanes[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;
m_ViewFrustum.m_aPlanes[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34;
+
+ for (size_t i = 0; i < 6; ++i)
+ m_ViewFrustum.m_aPlanes[i].Normalize();
}
void CCamera::ClipFrustum(const CPlane& clipPlane)
{
- m_ViewFrustum.AddPlane(clipPlane);
+ CPlane normClipPlane = clipPlane;
+ normClipPlane.Normalize();
+ m_ViewFrustum.AddPlane(normClipPlane);
}
void CCamera::SetViewPort(const SViewPort& viewport)
diff --git a/source/graphics/Frustum.cpp b/source/graphics/Frustum.cpp
index fa0e426889..a73d1ddbed 100644
--- a/source/graphics/Frustum.cpp
+++ b/source/graphics/Frustum.cpp
@@ -93,26 +93,21 @@ bool CFrustum::DoesSegmentIntersect(const CVector3D& startRef, const CVector3D &
}
return false;
}
+
bool CFrustum::IsSphereVisible (const CVector3D ¢er, float radius) const
{
- for (size_t i=0; i radius)
- return false;
- }
+ float Dist = m_aPlanes[i].DistanceToPlane(center);
+ // If none of the sphere is in front of the plane, then
+ // it is outside the frustum
+ if (-Dist > radius)
+ return false;
}
return true;
}
-
bool CFrustum::IsBoxVisible (const CVector3D &position,const CBoundingBoxAligned &bounds) const
{
//basically for every plane we calculate the furthest point
diff --git a/source/graphics/Overlay.h b/source/graphics/Overlay.h
index f304bdce9c..127afe8b64 100644
--- a/source/graphics/Overlay.h
+++ b/source/graphics/Overlay.h
@@ -154,6 +154,15 @@ struct SOverlayQuad
CColor m_Color;
};
+struct SOverlaySphere
+{
+ SOverlaySphere() : m_Radius(0) { }
+
+ CVector3D m_Center;
+ float m_Radius;
+ CColor m_Color;
+};
+
// TODO: OverlayText
#endif // INCLUDED_GRAPHICS_OVERLAY
diff --git a/source/maths/BoundingBoxAligned.h b/source/maths/BoundingBoxAligned.h
index 9f45141e1e..c8d94f87b5 100644
--- a/source/maths/BoundingBoxAligned.h
+++ b/source/maths/BoundingBoxAligned.h
@@ -55,6 +55,15 @@ public:
*/
void Transform(const CMatrix3D& m, CBoundingBoxOriented& result) const;
+ /**
+ * Translates these bounds by @p v, and writes the result to @p result.
+ */
+ void Translate(const CVector3D& v, CBoundingBoxAligned& result) const
+ {
+ result.m_Data[0] = m_Data[0] + v;
+ result.m_Data[1] = m_Data[1] + v;
+ }
+
CVector3D& operator[](int index) { return m_Data[index]; }
const CVector3D& operator[](int index) const { return m_Data[index]; }
diff --git a/source/maths/BoundingSphere.h b/source/maths/BoundingSphere.h
new file mode 100644
index 0000000000..533b35031f
--- /dev/null
+++ b/source/maths/BoundingSphere.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2014 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 .
+ */
+
+#ifndef INCLUDED_BOUNDINGSPHERE
+#define INCLUDED_BOUNDINGSPHERE
+
+#include "maths/BoundingBoxAligned.h"
+#include "maths/Vector3D.h"
+
+class CBoundingSphere
+{
+private:
+ CVector3D m_Center;
+ float m_Radius;
+
+public:
+ CBoundingSphere() : m_Radius(0) { }
+
+ CBoundingSphere(const CVector3D& center, float radius) : m_Center(center), m_Radius(radius) { }
+
+ /**
+ * Construct a bounding sphere that encompasses a bounding box
+ * swept through all possible rotations around the origin.
+ */
+ static CBoundingSphere FromSweptBox(const CBoundingBoxAligned& bbox)
+ {
+ float maxX = std::max(fabsf(bbox[0].X), fabsf(bbox[1].X));
+ float maxY = std::max(fabsf(bbox[0].Y), fabsf(bbox[1].Y));
+ float maxZ = std::max(fabsf(bbox[0].Z), fabsf(bbox[1].Z));
+ float radius = sqrtf(maxX*maxX + maxY*maxY + maxZ*maxZ);
+
+ return CBoundingSphere(CVector3D(0.f, 0.f, 0.f), radius);
+ }
+
+ const CVector3D& GetCenter()
+ {
+ return m_Center;
+ }
+
+ float GetRadius() const
+ {
+ return m_Radius;
+ }
+};
+
+#endif // INCLUDED_BOUNDINGSPHERE
diff --git a/source/ps/CStrInternStatic.h b/source/ps/CStrInternStatic.h
index 6a6d292529..a7e8f2cedd 100644
--- a/source/ps/CStrInternStatic.h
+++ b/source/ps/CStrInternStatic.h
@@ -107,6 +107,7 @@ X(murkiness)
X(normalMap)
X(normalMap2)
X(objectColor)
+X(overlay_solid)
X(particle)
X(particle_solid)
X(playerColor)
diff --git a/source/renderer/OverlayRenderer.cpp b/source/renderer/OverlayRenderer.cpp
index 7d08a34257..7ab79a2ad9 100644
--- a/source/renderer/OverlayRenderer.cpp
+++ b/source/renderer/OverlayRenderer.cpp
@@ -88,6 +88,7 @@ struct OverlayRendererInternals
std::vector texlines;
std::vector sprites;
std::vector quads;
+ std::vector spheres;
QuadBatchMap quadBatchMap;
@@ -111,6 +112,11 @@ struct OverlayRendererInternals
CShaderDefines defsOverlayLineAlwaysVisible;
CShaderDefines defsQuadOverlay;
+ // Geometry for a unit sphere
+ std::vector sphereVertexes;
+ std::vector sphereIndexes;
+ void GenerateSphere();
+
/// Performs one-time setup. Called from CRenderer::Open, after graphics capabilities have
/// been detected. Note that no VBOs must be created before this is called, since the shader
/// path and graphics capabilities are not guaranteed to be stable before this point.
@@ -223,12 +229,19 @@ void OverlayRenderer::Submit(SOverlayQuad* overlay)
m->quads.push_back(overlay);
}
+void OverlayRenderer::Submit(SOverlaySphere* overlay)
+{
+ m->spheres.push_back(overlay);
+}
+
void OverlayRenderer::EndFrame()
{
m->lines.clear();
m->texlines.clear();
m->sprites.clear();
m->quads.clear();
+ m->spheres.clear();
+
// this should leave the capacity unchanged, which is okay since it
// won't be very large or very variable
@@ -384,6 +397,7 @@ void OverlayRenderer::RenderOverlaysAfterWater()
RenderTexturedOverlayLines();
RenderQuadOverlays();
+ RenderSphereOverlays();
}
void OverlayRenderer::RenderTexturedOverlayLines()
@@ -629,3 +643,118 @@ void OverlayRenderer::RenderForegroundOverlays(const CCamera& viewCamera)
glDisable(GL_TEXTURE_2D);
#endif
}
+
+static void TessellateSphereFace(const CVector3D& a, u16 ai,
+ const CVector3D& b, u16 bi,
+ const CVector3D& c, u16 ci,
+ std::vector& vertexes, std::vector& indexes, int level)
+{
+ if (level == 0)
+ {
+ indexes.push_back(ai);
+ indexes.push_back(bi);
+ indexes.push_back(ci);
+ }
+ else
+ {
+ CVector3D d = (a + b).Normalized();
+ CVector3D e = (b + c).Normalized();
+ CVector3D f = (c + a).Normalized();
+ int di = vertexes.size() / 3; vertexes.push_back(d.X); vertexes.push_back(d.Y); vertexes.push_back(d.Z);
+ int ei = vertexes.size() / 3; vertexes.push_back(e.X); vertexes.push_back(e.Y); vertexes.push_back(e.Z);
+ int fi = vertexes.size() / 3; vertexes.push_back(f.X); vertexes.push_back(f.Y); vertexes.push_back(f.Z);
+ TessellateSphereFace(a,ai, d,di, f,fi, vertexes, indexes, level-1);
+ TessellateSphereFace(d,di, b,bi, e,ei, vertexes, indexes, level-1);
+ TessellateSphereFace(f,fi, e,ei, c,ci, vertexes, indexes, level-1);
+ TessellateSphereFace(d,di, e,ei, f,fi, vertexes, indexes, level-1);
+ }
+}
+
+static void TessellateSphere(std::vector& vertexes, std::vector& indexes, int level)
+{
+ /* Start with a tetrahedron, then tessellate */
+ float s = sqrtf(0.5f);
+#define VERT(a,b,c) vertexes.push_back(a); vertexes.push_back(b); vertexes.push_back(c);
+ VERT(-s, 0, -s);
+ VERT( s, 0, -s);
+ VERT( s, 0, s);
+ VERT(-s, 0, s);
+ VERT( 0, -1, 0);
+ VERT( 0, 1, 0);
+#define FACE(a,b,c) \
+ TessellateSphereFace( \
+ CVector3D(vertexes[a*3], vertexes[a*3+1], vertexes[a*3+2]), a, \
+ CVector3D(vertexes[b*3], vertexes[b*3+1], vertexes[b*3+2]), b, \
+ CVector3D(vertexes[c*3], vertexes[c*3+1], vertexes[c*3+2]), c, \
+ vertexes, indexes, level);
+ FACE(0,4,1);
+ FACE(1,4,2);
+ FACE(2,4,3);
+ FACE(3,4,0);
+ FACE(1,5,0);
+ FACE(2,5,1);
+ FACE(3,5,2);
+ FACE(0,5,3);
+#undef FACE
+#undef VERT
+}
+
+void OverlayRendererInternals::GenerateSphere()
+{
+ if (sphereVertexes.empty())
+ TessellateSphere(sphereVertexes, sphereIndexes, 3);
+}
+
+void OverlayRenderer::RenderSphereOverlays()
+{
+ PROFILE3_GPU("overlays (spheres)");
+
+ if (g_Renderer.GetRenderPath() != CRenderer::RP_SHADER)
+ return;
+
+ if (m->spheres.empty())
+ return;
+
+ glDisable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glDepthMask(0);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ CShaderProgramPtr shader;
+ CShaderTechniquePtr tech;
+
+ tech = g_Renderer.GetShaderManager().LoadEffect(str_overlay_solid);
+ tech->BeginPass();
+ shader = tech->GetShader();
+
+ m->GenerateSphere();
+
+ shader->VertexPointer(3, GL_FLOAT, 0, &m->sphereVertexes[0]);
+
+ for (size_t i = 0; i < m->spheres.size(); ++i)
+ {
+ SOverlaySphere* sphere = m->spheres[i];
+
+ CMatrix3D transform;
+ transform.SetIdentity();
+ transform.Scale(sphere->m_Radius, sphere->m_Radius, sphere->m_Radius);
+ transform.Translate(sphere->m_Center);
+
+ shader->Uniform(str_transform, transform);
+
+ shader->Uniform(str_color, sphere->m_Color);
+
+ glDrawElements(GL_TRIANGLES, m->sphereIndexes.size(), GL_UNSIGNED_SHORT, &m->sphereIndexes[0]);
+
+ g_Renderer.GetStats().m_DrawCalls++;
+ g_Renderer.GetStats().m_OverlayTris = m->sphereIndexes.size()/3;
+ }
+
+ tech->EndPass();
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDepthMask(1);
+ glDisable(GL_BLEND);
+}
diff --git a/source/renderer/OverlayRenderer.h b/source/renderer/OverlayRenderer.h
index 63e97c405a..ebfd624210 100644
--- a/source/renderer/OverlayRenderer.h
+++ b/source/renderer/OverlayRenderer.h
@@ -24,6 +24,7 @@ struct SOverlayLine;
struct SOverlayTexturedLine;
struct SOverlaySprite;
struct SOverlayQuad;
+struct SOverlaySphere;
class CCamera;
struct OverlayRendererInternals;
@@ -74,6 +75,13 @@ public:
*/
void Submit(SOverlayQuad* overlay);
+ /**
+ * Add a sphere overlay for rendering in this frame.
+ * @param overlay Must be non-null. The pointed-to object must remain valid at least
+ * until the end of the frame.
+ */
+ void Submit(SOverlaySphere* overlay);
+
/**
* Prepare internal data structures for rendering.
* Must be called after all Submit calls for a frame, and before
@@ -132,6 +140,11 @@ private:
*/
void RenderQuadOverlays();
+ /**
+ * Helper method; batch-renders all sphere quad overlays.
+ */
+ void RenderSphereOverlays();
+
private:
OverlayRendererInternals* m;
};
diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
index 402ed6a760..42d895b7ab 100644
--- a/source/renderer/Renderer.cpp
+++ b/source/renderer/Renderer.cpp
@@ -1749,6 +1749,11 @@ void CRenderer::Submit(SOverlayQuad* overlay)
m->overlayRenderer.Submit(overlay);
}
+void CRenderer::Submit(SOverlaySphere* overlay)
+{
+ m->overlayRenderer.Submit(overlay);
+}
+
void CRenderer::Submit(CModelDecal* decal)
{
m->terrainRenderer.Submit(decal);
diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
index aa6513994f..918602be2f 100644
--- a/source/renderer/Renderer.h
+++ b/source/renderer/Renderer.h
@@ -354,6 +354,7 @@ protected:
void Submit(SOverlayQuad* overlay);
void Submit(CModelDecal* decal);
void Submit(CParticleEmitter* emitter);
+ void Submit(SOverlaySphere* overlay);
void SubmitNonRecursive(CModel* model);
//END: Implementation of SceneCollector
diff --git a/source/renderer/Scene.h b/source/renderer/Scene.h
index 99c99f7a07..b7ac24eb83 100644
--- a/source/renderer/Scene.h
+++ b/source/renderer/Scene.h
@@ -40,6 +40,7 @@ struct SOverlayLine;
struct SOverlayTexturedLine;
struct SOverlaySprite;
struct SOverlayQuad;
+struct SOverlaySphere;
class SceneCollector;
@@ -109,6 +110,11 @@ public:
*/
virtual void Submit(SOverlayQuad* overlay) = 0;
+ /**
+ * Submit a sphere overlay.
+ */
+ virtual void Submit(SOverlaySphere* overlay) = 0;
+
/**
* Submit a terrain decal.
*/