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. */