From fe3315376c00a085203eb9b18540c271bd82e678 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sun, 1 Jun 2014 18:05:46 +0000 Subject: [PATCH] Add BoundingSphere, SOverlaySphere. BoundingSphere is similar to a bounding box, but more spherical. SOverlaySphere is useful for visualising BoundingSphere (it's quite inefficient and should only be used for debug functionality). Normalise the camera frustum clipping planes, so that IsSphereVisible gives the correct result. This was SVN commit r15261. --- .../mods/public/shaders/arb/overlay_solid.fp | 7 + .../mods/public/shaders/arb/overlay_solid.vp | 17 +++ .../mods/public/shaders/arb/overlay_solid.xml | 13 ++ .../public/shaders/effects/overlay_solid.xml | 9 ++ source/graphics/Camera.cpp | 7 +- source/graphics/Frustum.cpp | 19 +-- source/graphics/Overlay.h | 9 ++ source/maths/BoundingBoxAligned.h | 9 ++ source/maths/BoundingSphere.h | 60 ++++++++ source/ps/CStrInternStatic.h | 1 + source/renderer/OverlayRenderer.cpp | 129 ++++++++++++++++++ source/renderer/OverlayRenderer.h | 13 ++ source/renderer/Renderer.cpp | 5 + source/renderer/Renderer.h | 1 + source/renderer/Scene.h | 6 + 15 files changed, 292 insertions(+), 13 deletions(-) create mode 100644 binaries/data/mods/public/shaders/arb/overlay_solid.fp create mode 100644 binaries/data/mods/public/shaders/arb/overlay_solid.vp create mode 100644 binaries/data/mods/public/shaders/arb/overlay_solid.xml create mode 100644 binaries/data/mods/public/shaders/effects/overlay_solid.xml create mode 100644 source/maths/BoundingSphere.h 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. */