From 4811defca2eeea69e40346be6b1647f276db8c76 Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Fri, 14 Dec 2012 21:30:35 +0100 Subject: Static objects using OpenGL VBOs and display lists --- src/app/app.cpp | 9 ++ src/app/app.h | 2 + src/graphics/core/device.h | 17 ++- src/graphics/core/vertex.h | 20 +-- src/graphics/engine/engine.cpp | 247 ++++++++++++++++++++++++++------------ src/graphics/engine/engine.h | 18 ++- src/graphics/engine/modelfile.cpp | 10 +- src/graphics/engine/pyro.cpp | 3 + src/graphics/engine/terrain.cpp | 6 + src/graphics/opengl/gldevice.cpp | 222 ++++++++++++++++++++++++++++++++++ src/graphics/opengl/gldevice.h | 36 +++++- 11 files changed, 491 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/app/app.cpp b/src/app/app.cpp index 823bc77..57a827d 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -142,6 +142,8 @@ CApplication::CApplication() m_lowCPU = true; + m_useVbo = false; + for (int i = 0; i < DIR_MAX; ++i) m_dataDirs[i] = nullptr; @@ -243,6 +245,10 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) { SetDebugMode(true); } + else if (arg == "-vbo") + { + m_useVbo = true; + } else if (arg == "-loglevel") { waitLogLevel = true; @@ -262,6 +268,7 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) GetLogger()->Message("\n"); GetLogger()->Message("List of available options:\n"); GetLogger()->Message(" -help this help\n"); + GetLogger()->Message(" -vbo enable OpenGL VBOs\n"); GetLogger()->Message(" -datadir path set custom data directory path\n"); GetLogger()->Message(" -debug enable debug mode (more info printed in logs)\n"); GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n"); @@ -425,6 +432,8 @@ bool CApplication::Create() return false; } + static_cast(m_device)->SetUseVbo(m_useVbo); + // Create the 3D engine m_engine = new Gfx::CEngine(m_iMan, this); diff --git a/src/app/app.h b/src/app/app.h index 84da0eb..c4288c1 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -449,5 +449,7 @@ protected: //! Low cpu mode bool m_lowCPU; + + int m_useVbo; // TODO: temporary }; diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index 8e7d446..a0e44e4 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -313,9 +313,24 @@ public: //! Renders primitive composed of vertices with multitexturing (2 textures) virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) = 0; - //! Renders primitive composed of vertices with color information + //! Renders primitive composed of vertices with solid color virtual void DrawPrimitive(PrimitiveType type, const VertexCol *vertices , int vertexCount) = 0; + //! Creates a static buffer composed of given primitives with single texture vertices + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) = 0; + + //! Creates a static buffer composed of given primitives with multitexturing (2 textures) + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) = 0; + + //! Creates a static buffer composed of given primitives with solid color + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) = 0; + + //! Draws a static buffer + virtual void DrawStaticObject(unsigned int objectId) = 0; + + //! Deletes a static buffer + virtual void DestroyStaticObject(unsigned int objectId) = 0; + //! Tests whether a sphere is (partially) within the frustum volume //! Returns a mask of frustum planes for which the test is positive virtual int ComputeSphereVisibility(const Math::Vector ¢er, float radius) = 0; diff --git a/src/graphics/core/vertex.h b/src/graphics/core/vertex.h index 2ee6be4..66e1503 100644 --- a/src/graphics/core/vertex.h +++ b/src/graphics/core/vertex.h @@ -44,23 +44,18 @@ namespace Gfx { * - vertex coordinates (x,y,z) as Math::Vector, * - normal coordinates (nx,ny,nz) as Math::Vector * - texture coordinates (u,v) as Math::Point. - * - * Additional padding is provided to align to even multiplies of 4 floats for faster access. */ struct Vertex { Math::Vector coord; - float pad1; Math::Vector normal; - float pad2; Math::Point texCoord; - float pad3, pad4; explicit Vertex(Math::Vector aCoord = Math::Vector(), Math::Vector aNormal = Math::Vector(), Math::Point aTexCoord = Math::Point()) - : coord(aCoord), pad1(0.0f), normal(aNormal), - pad2(0.0f),texCoord(aTexCoord), pad3(0.0f), pad4(0.0f) {} + : coord(aCoord), normal(aNormal), + texCoord(aTexCoord) {} //! Returns a string "(c: [...], n: [...], tc: [...])" @@ -81,18 +76,15 @@ struct Vertex * It contains: * - vertex coordinates (x,y,z) as Math::Vector, * - RGBA color as Color - * - * Additional padding is provided to align to even multiplies of 4 floats for faster access. */ struct VertexCol { Math::Vector coord; - float pad; Color color; explicit VertexCol(Math::Vector aCoord = Math::Vector(), Color aColor = Color()) - : coord(aCoord), pad(0.0f), color(aColor) {} + : coord(aCoord), color(aColor) {} //! Returns a string "(c: [...], col: [...])" inline std::string ToString() const @@ -111,15 +103,11 @@ struct VertexCol * * In addition to fields from Vector, it contains * secondary texture coordinates (u2, v2) as Math::Point - * - * Additional padding is provided to align to even multiplies of 4 floats for faster access. */ struct VertexTex2 { Math::Vector coord; - float pad1; Math::Vector normal; - float pad2; Math::Point texCoord; Math::Point texCoord2; @@ -127,7 +115,7 @@ struct VertexTex2 Math::Vector aNormal = Math::Vector(), Math::Point aTexCoord = Math::Point(), Math::Point aTexCoord2 = Math::Point()) - : coord(aCoord), pad1(0.0f), normal(aNormal), pad2(0.0f), + : coord(aCoord), normal(aNormal), texCoord(aTexCoord), texCoord2(aTexCoord2) {} //! Sets the fields from Vertex with texCoord2 = (0,0) diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 856c2d4..de5f2d4 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -92,6 +92,7 @@ EngineObjLevel4::EngineObjLevel4(bool used, EngineTriangleType type, const Mater this->type = type; this->material = material; this->state = state; + this->staticBufferId = 0; vertices.reserve(LEVEL4_VERTEX_PREALLOCATE_COUNT); } @@ -182,6 +183,7 @@ CEngine::CEngine(CInstanceManager *iMan, CApplication *app) m_alphaMode = 1; m_updateGeometry = false; + m_updateStaticObjects = false; m_interfaceMode = false; @@ -385,6 +387,7 @@ void CEngine::FrameUpdate() ComputeDistance(); UpdateGeometry(); + UpdateStaticObjects(); m_highlightTime = m_app->GetAbsTime(); @@ -570,11 +573,27 @@ bool CEngine::DeleteObject(int objRank) EngineObjLevel2& p2 = p1.next[l2]; if (! p2.used) continue; - if (p2.objRank == objRank) + if (p2.objRank != objRank) continue; + + if (m_objects[objRank].staticBuffer) { - p2.used = false; - p2.next.clear(); + for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) + { + EngineObjLevel3& p3 = p2.next[l3]; + if (! p3.used) continue; + + for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + { + EngineObjLevel4& p4 = p3.next[l4]; + + m_device->DestroyStaticObject(p4.staticBufferId); + } + } } + + + p2.used = false; + p2.next.clear(); } } @@ -623,6 +642,22 @@ bool CEngine::GetObjectTransform(int objRank, Math::Matrix& transform) return true; } +void CEngine::SetObjectStatic(int objRank, bool staticBuffer) +{ + if ( objRank < 0 || objRank >= static_cast( m_objects.size() ) ) + return; + + m_objects[objRank].staticBuffer = staticBuffer; +} + +bool CEngine::GetObjectStatic(int objRank) +{ + if ( objRank < 0 || objRank >= static_cast( m_objects.size() ) ) + return false; + + return m_objects[objRank].staticBuffer; +} + bool CEngine::SetObjectDrawWorld(int objRank, bool draw) { if ( objRank < 0 || objRank >= static_cast( m_objects.size() ) ) @@ -825,6 +860,17 @@ bool CEngine::AddTriangles(int objRank, const std::vector& vertices, p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end()); + if (m_objects[objRank].staticBuffer) + { + if (p4.staticBufferId != 0) + { + m_device->DestroyStaticObject(p4.staticBufferId); + p4.staticBufferId = 0; + } + + m_updateStaticObjects = true; + } + if (globalUpdate) { m_updateGeometry = true; @@ -872,6 +918,17 @@ bool CEngine::AddSurface(int objRank, const std::vector& vertices, p4.vertices.insert(p4.vertices.end(), vertices.begin(), vertices.end()); + if (m_objects[objRank].staticBuffer) + { + if (p4.staticBufferId != 0) + { + m_device->DestroyStaticObject(p4.staticBufferId); + p4.staticBufferId = 0; + } + + m_updateStaticObjects = true; + } + if (globalUpdate) { m_updateGeometry = true; @@ -898,8 +955,8 @@ bool CEngine::AddSurface(int objRank, const std::vector& vertices, } bool CEngine::AddQuick(int objRank, const EngineObjLevel4& buffer, - std::string tex1Name, std::string tex2Name, - float min, float max, bool globalUpdate) + std::string tex1Name, std::string tex2Name, + float min, float max, bool globalUpdate) { if ( objRank < 0 || objRank >= static_cast( m_objects.size() ) ) { @@ -912,7 +969,26 @@ bool CEngine::AddQuick(int objRank, const EngineObjLevel4& buffer, EngineObjLevel3& p3 = AddLevel3(p2, min, max); p3.next.push_back(buffer); - p3.next.back().used = true; // ensure that it is used + + EngineObjLevel4& p4 = p3.next.back(); + p4.used = true; // ensure that it is used + + if (m_objects[objRank].staticBuffer) + { + if (p4.staticBufferId != 0) + { + m_device->DestroyStaticObject(p4.staticBufferId); + p4.staticBufferId = 0; + } + + PrimitiveType type; + if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) + type = PRIMITIVE_TRIANGLES; + else + type = PRIMITIVE_TRIANGLE_STRIP; + + p4.staticBufferId = m_device->CreateStaticObject(type, &p4.vertices[0], p4.vertices.size()); + } if (globalUpdate) { @@ -920,24 +996,24 @@ bool CEngine::AddQuick(int objRank, const EngineObjLevel4& buffer, } else { - for (int i = 0; i < static_cast( buffer.vertices.size() ); i++) + for (int i = 0; i < static_cast( p4.vertices.size() ); i++) { - m_objects[objRank].bboxMin.x = Math::Min(buffer.vertices[i].coord.x, m_objects[objRank].bboxMin.x); - m_objects[objRank].bboxMin.y = Math::Min(buffer.vertices[i].coord.y, m_objects[objRank].bboxMin.y); - m_objects[objRank].bboxMin.z = Math::Min(buffer.vertices[i].coord.z, m_objects[objRank].bboxMin.z); - m_objects[objRank].bboxMax.x = Math::Max(buffer.vertices[i].coord.x, m_objects[objRank].bboxMax.x); - m_objects[objRank].bboxMax.y = Math::Max(buffer.vertices[i].coord.y, m_objects[objRank].bboxMax.y); - m_objects[objRank].bboxMax.z = Math::Max(buffer.vertices[i].coord.z, m_objects[objRank].bboxMax.z); + m_objects[objRank].bboxMin.x = Math::Min(p4.vertices[i].coord.x, m_objects[objRank].bboxMin.x); + m_objects[objRank].bboxMin.y = Math::Min(p4.vertices[i].coord.y, m_objects[objRank].bboxMin.y); + m_objects[objRank].bboxMin.z = Math::Min(p4.vertices[i].coord.z, m_objects[objRank].bboxMin.z); + m_objects[objRank].bboxMax.x = Math::Max(p4.vertices[i].coord.x, m_objects[objRank].bboxMax.x); + m_objects[objRank].bboxMax.y = Math::Max(p4.vertices[i].coord.y, m_objects[objRank].bboxMax.y); + m_objects[objRank].bboxMax.z = Math::Max(p4.vertices[i].coord.z, m_objects[objRank].bboxMax.z); } m_objects[objRank].radius = Math::Max(m_objects[objRank].bboxMin.Length(), m_objects[objRank].bboxMax.Length()); } - if (buffer.type == ENG_TRIANGLE_TYPE_TRIANGLES) - m_objects[objRank].totalTriangles += buffer.vertices.size() / 3; - else if (buffer.type == ENG_TRIANGLE_TYPE_SURFACE) - m_objects[objRank].totalTriangles += buffer.vertices.size() - 2; + if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) + m_objects[objRank].totalTriangles += p4.vertices.size() / 3; + else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) + m_objects[objRank].totalTriangles += p4.vertices.size() - 2; return true; } @@ -1735,10 +1811,57 @@ void CEngine::UpdateGeometry() m_updateGeometry = false; } +void CEngine::UpdateStaticObjects() +{ + if (!m_updateStaticObjects) + return; + + for (int l1 = 0; l1 < static_cast( m_objectTree.size() ); l1++) + { + EngineObjLevel1& p1 = m_objectTree[l1]; + if (! p1.used) continue; + + for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) + { + EngineObjLevel2& p2 = p1.next[l2]; + if (! p2.used) continue; + + int objRank = p2.objRank; + + if (!m_objects[objRank].staticBuffer) + continue; + + for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) + { + EngineObjLevel3& p3 = p2.next[l3]; + if (! p3.used) continue; + + for (int l4 = 0; l4 < static_cast( p3.next.size() ); l4++) + { + EngineObjLevel4& p4 = p3.next[l4]; + if (! p4.used) continue; + + if (p4.staticBufferId != 0) + continue; + + PrimitiveType type; + if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) + type = PRIMITIVE_TRIANGLES; + else + type = PRIMITIVE_TRIANGLE_STRIP; + + p4.staticBufferId = m_device->CreateStaticObject(type, &p4.vertices[0], p4.vertices.size()); + } + } + } + } +} + void CEngine::Update() { ComputeDistance(); UpdateGeometry(); + UpdateStaticObjects(); } bool CEngine::DetectBBox(int objRank, Math::Point mouse) @@ -3106,20 +3229,7 @@ void CEngine::Draw3DScene() SetMaterial(p4.material); SetState(p4.state); - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLES, - &p4.vertices[0], - p4.vertices.size() ); - m_statisticTriangle += p4.vertices.size() / 3; - } - if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLE_STRIP, - &p4.vertices[0], - p4.vertices.size() ); - m_statisticTriangle += p4.vertices.size() - 2; - } + DrawObject(p4, m_objects[objRank].staticBuffer); } } } @@ -3188,22 +3298,7 @@ void CEngine::Draw3DScene() SetMaterial(p4.material); SetState(p4.state); - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLES, - &p4.vertices[0], - p4.vertices.size() ); - - m_statisticTriangle += p4.vertices.size() / 3; - } - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLE_STRIP, - &p4.vertices[0], - p4.vertices.size() ); - - m_statisticTriangle += p4.vertices.size() - 2; - } + DrawObject(p4, m_objects[objRank].staticBuffer); } } } @@ -3264,21 +3359,7 @@ void CEngine::Draw3DScene() SetMaterial(p4.material); SetState(tState, tColor); - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLES, - &p4.vertices[0], - p4.vertices.size() ); - - m_statisticTriangle += p4.vertices.size() / 3; - } - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLE_STRIP, - &p4.vertices[0], - p4.vertices.size() ); - m_statisticTriangle += p4.vertices.size() - 2; - } + DrawObject(p4, m_objects[objRank].staticBuffer); } } } @@ -3307,6 +3388,32 @@ void CEngine::Draw3DScene() if (! m_overFront) DrawOverColor(); // draws the foreground color } +void CEngine::DrawObject(const EngineObjLevel4& obj, bool staticBuffer) +{ + if (staticBuffer) + { + m_device->DrawStaticObject(obj.staticBufferId); + + if (obj.type == ENG_TRIANGLE_TYPE_TRIANGLES) + m_statisticTriangle += obj.vertices.size() / 3; + else + m_statisticTriangle += obj.vertices.size() - 2; + } + else + { + if (obj.type == ENG_TRIANGLE_TYPE_TRIANGLES) + { + m_device->DrawPrimitive(PRIMITIVE_TRIANGLES, &obj.vertices[0], obj.vertices.size()); + m_statisticTriangle += obj.vertices.size() / 3; + } + else + { + m_device->DrawPrimitive(PRIMITIVE_TRIANGLE_STRIP, &obj.vertices[0], obj.vertices.size() ); + m_statisticTriangle += obj.vertices.size() - 2; + } + } +} + void CEngine::DrawInterface() { m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false); @@ -3397,21 +3504,7 @@ void CEngine::DrawInterface() SetMaterial(p4.material); SetState(p4.state); - if (p4.type == ENG_TRIANGLE_TYPE_TRIANGLES) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLES, - &p4.vertices[0], - p4.vertices.size() ); - - m_statisticTriangle += p4.vertices.size() / 3; - } - else if (p4.type == ENG_TRIANGLE_TYPE_SURFACE) - { - m_device->DrawPrimitive( PRIMITIVE_TRIANGLE_STRIP, - &p4.vertices[0], - p4.vertices.size() ); - m_statisticTriangle += p4.vertices.size() - 2; - } + DrawObject(p4, m_objects[objRank].staticBuffer); } } } diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index d127e74..de57e4d 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -195,7 +195,9 @@ struct EngineObject //! Number of triangles int totalTriangles; //! Type of object - EngineObjectType type; + EngineObjectType type; + //! Whether the object is stored and rendered as static buffer + bool staticBuffer; //! Transformation matrix Math::Matrix transform; //! Distance to object from eye point @@ -225,6 +227,7 @@ struct EngineObject drawWorld = false; drawFront = false; totalTriangles = 0; + staticBuffer = false; type = ENG_OBJTYPE_NULL; transform.LoadIdentity(); bboxMax.LoadZero(); @@ -252,6 +255,7 @@ struct EngineObjLevel4 Material material; int state; std::vector vertices; + unsigned int staticBufferId; EngineObjLevel4(bool used = false, EngineTriangleType type = ENG_TRIANGLE_TYPE_TRIANGLES, @@ -760,6 +764,12 @@ public: bool GetObjectTransform(int objRank, Math::Matrix& transform); //@} + //@{ + //! Management of object static drawing flag + void SetObjectStatic(int objRank, bool staticBuffer); + bool GetObjectStatic(int objRank); + //@} + //! Sets drawWorld for given object bool SetObjectDrawWorld(int objRank, bool draw); //! Sets drawFront for given object @@ -1151,6 +1161,8 @@ public: protected: //! Prepares the interface for 3D scene void Draw3DScene(); + //! Draw 3D object + void DrawObject(const EngineObjLevel4& obj, bool staticBuffer); //! Draws the user interface over the scene void DrawInterface(); @@ -1215,6 +1227,9 @@ protected: //! Updates geometric parameters of objects (bounding box and radius) void UpdateGeometry(); + //! Updates static buffers of changed objects + void UpdateStaticObjects(); + protected: CInstanceManager* m_iMan; CApplication* m_app; @@ -1293,6 +1308,7 @@ protected: Color m_waterAddColor; int m_statisticTriangle; bool m_updateGeometry; + bool m_updateStaticObjects; int m_alphaMode; bool m_groundSpotVisible; bool m_shadowVisible; diff --git a/src/graphics/engine/modelfile.cpp b/src/graphics/engine/modelfile.cpp index 3b0343a..a9972fe 100644 --- a/src/graphics/engine/modelfile.cpp +++ b/src/graphics/engine/modelfile.cpp @@ -1157,18 +1157,24 @@ bool CModelFile::WriteBinaryModel(std::ostream& stream) #ifndef MODELFILE_NO_ENGINE + +/** + * TODO: move the function to CEngine or new class (CModelManager?) + * and make models shared static objects. + */ + bool CModelFile::CreateEngineObject(int objRank) { std::vector vs(3, VertexTex2()); + m_engine->SetObjectStatic(objRank, true); // TODO: make optional in the future + float limit[2]; limit[0] = m_engine->GetLimitLOD(0); // frontier AB as config limit[1] = m_engine->GetLimitLOD(1); // frontier BC as config for (int i = 0; i < static_cast( m_triangles.size() ); i++) { - // TODO move this to CEngine - float min = m_triangles[i].min; float max = m_triangles[i].max; diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index 978471b..73c5cec 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -363,6 +363,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) m_type == PT_EXPLOW ) { CreateTriangle(obj, oType, 0); + m_engine->SetObjectStatic(m_object->GetObjectRank(0), false); m_engine->DeleteShadow(m_object->GetObjectRank(0)); ExploStart(); } @@ -1397,6 +1398,8 @@ void CPyro::CreateTriangle(CObject* obj, ObjectType oType, int part) int objRank = obj->GetObjectRank(part); if (objRank == -1) return; + m_engine->SetObjectStatic(objRank, false); + float min = 0.0f; float max = m_engine->GetLimitLOD(0); int total = m_engine->GetObjectTotalTriangles(objRank); diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp index 4c22a32..0e77ea2 100644 --- a/src/graphics/engine/terrain.cpp +++ b/src/graphics/engine/terrain.cpp @@ -478,6 +478,8 @@ VertexTex2 CTerrain::GetVertex(int x, int y, int step) v.texCoord.x = (o.x-oo.x)*m_textureScale*m_textureSubdivCount; v.texCoord.y = 1.0f - (o.z-oo.z)*m_textureScale*m_textureSubdivCount; + v.texCoord2 = v.texCoord; + return v; } @@ -1166,6 +1168,10 @@ bool CTerrain::CreateSquare(int x, int y) int objRank = m_engine->CreateObject(); m_engine->SetObjectType(objRank, ENG_OBJTYPE_TERRAIN); + // TODO: create a static object, but not split into squares, but a single object for all terrain + // Squares should be sub-objects accessing parts of triangle list + // m_engine->SetObjectStatic(objRank, true); + m_objRanks[x+y*m_mosaicCount] = objRank; float min = 0.0f; diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp index a6ba1eb..2d284d0 100644 --- a/src/graphics/opengl/gldevice.cpp +++ b/src/graphics/opengl/gldevice.cpp @@ -73,6 +73,8 @@ CGLDevice::CGLDevice(const GLDeviceConfig &config) { m_config = config; m_lighting = false; + m_lastVboId = 0; + m_useVbo = false; } @@ -109,6 +111,16 @@ bool CGLDevice::Create() GetLogger()->Error("GLEW reports required extensions not supported\n"); return false; } + + if (GLEW_ARB_vertex_buffer_object) + { + GetLogger()->Info("Detected ARB_vertex_buffer_object extension - using VBOs\n"); + m_useVbo = true; + } + else + { + GetLogger()->Info("No ARB_vertex_buffer_object extension present - using display lists\n"); + } } #endif @@ -174,6 +186,16 @@ void CGLDevice::ConfigChanged(const GLDeviceConfig& newConfig) Create(); } +void CGLDevice::SetUseVbo(bool useVbo) +{ + m_useVbo = useVbo; +} + +bool CGLDevice::GetUseVbo() +{ + return m_useVbo; +} + void CGLDevice::BeginScene() { Clear(); @@ -927,6 +949,206 @@ void CGLDevice::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int glDisableClientState(GL_COLOR_ARRAY); } +unsigned int CGLDevice::CreateStaticObject(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount) +{ + unsigned int id = 0; + if (m_useVbo) + { + id = ++m_lastVboId; + + VboObjectInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_NORMAL; + info.vertexCount = vertexCount; + info.bufferId = 0; + + glGenBuffers(1, &info.bufferId); + glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(Vertex), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_vboObjects[id] = info; + } + else + { + id = glGenLists(1); + + glNewList(id, GL_COMPILE); + + DrawPrimitive(primitiveType, vertices, vertexCount); + + glEndList(); + } + + return id; +} + +unsigned int CGLDevice::CreateStaticObject(PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount) +{ + unsigned int id = 0; + if (m_useVbo) + { + id = ++m_lastVboId; + + VboObjectInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_TEX2; + info.vertexCount = vertexCount; + info.bufferId = 0; + + glGenBuffers(1, &info.bufferId); + glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexTex2), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_vboObjects[id] = info; + } + else + { + id = glGenLists(1); + + glNewList(id, GL_COMPILE); + + DrawPrimitive(primitiveType, vertices, vertexCount); + + glEndList(); + } + + return id; +} + +unsigned int CGLDevice::CreateStaticObject(PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount) +{ + unsigned int id = 0; + if (m_useVbo) + { + id = ++m_lastVboId; + + VboObjectInfo info; + info.primitiveType = primitiveType; + info.vertexType = VERTEX_TYPE_COL; + info.vertexCount = vertexCount; + info.bufferId = 0; + + glGenBuffers(1, &info.bufferId); + glBindBuffer(GL_ARRAY_BUFFER, info.bufferId); + glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(VertexCol), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_vboObjects[id] = info; + } + else + { + id = glGenLists(1); + + glNewList(id, GL_COMPILE); + + DrawPrimitive(primitiveType, vertices, vertexCount); + + glEndList(); + } + + return id; +} + +void CGLDevice::DrawStaticObject(unsigned int objectId) +{ + if (m_useVbo) + { + auto it = m_vboObjects.find(objectId); + if (it == m_vboObjects.end()) + return; + + glEnable(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, (*it).second.bufferId); + + if ((*it).second.vertexType == VERTEX_TYPE_NORMAL) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(Vertex), static_cast(nullptr) + offsetof(Vertex, coord)); + + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, sizeof(Vertex), static_cast(nullptr) + offsetof(Vertex, normal)); + + glActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), static_cast(nullptr) + offsetof(Vertex, texCoord)); + } + else if ((*it).second.vertexType == VERTEX_TYPE_TEX2) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(VertexTex2), static_cast(nullptr) + offsetof(VertexTex2, coord)); + + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, sizeof(VertexTex2), static_cast(nullptr) + offsetof(VertexTex2, normal)); + + glClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTex2), static_cast(nullptr) + offsetof(VertexTex2, texCoord)); + + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTex2), static_cast(nullptr) + offsetof(VertexTex2, texCoord2)); + } + else if ((*it).second.vertexType == VERTEX_TYPE_COL) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(VertexCol), static_cast(nullptr) + offsetof(VertexCol, coord)); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_FLOAT, sizeof(VertexCol), static_cast(nullptr) + offsetof(VertexCol, color)); + } + + GLenum mode = TranslateGfxPrimitive((*it).second.primitiveType); + glDrawArrays(GL_TRIANGLES, 0, (*it).second.vertexCount); + + if ((*it).second.vertexType == VERTEX_TYPE_NORMAL) + { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // GL_TEXTURE0 + } + else if ((*it).second.vertexType == VERTEX_TYPE_TEX2) + { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // GL_TEXTURE1 + glClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + else if ((*it).second.vertexType == VERTEX_TYPE_COL) + { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDisable(GL_VERTEX_ARRAY); + } + else + { + glCallList(objectId); + } +} + +void CGLDevice::DestroyStaticObject(unsigned int objectId) +{ + if (m_useVbo) + { + auto it = m_vboObjects.find(objectId); + if (it == m_vboObjects.end()) + return; + + glDeleteBuffers(1, &(*it).second.bufferId); + + m_vboObjects.erase(it); + } + else + { + glDeleteLists(objectId, 1); + } +} + bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius) { float distance = originPlane + Math::DotProduct(normal, center); diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h index 87c1247..adae41b 100644 --- a/src/graphics/opengl/gldevice.h +++ b/src/graphics/opengl/gldevice.h @@ -27,6 +27,7 @@ #include #include #include +#include // Graphics module namespace @@ -84,6 +85,9 @@ public: void ConfigChanged(const GLDeviceConfig &newConfig); + void SetUseVbo(bool useVbo); + bool GetUseVbo(); + virtual void BeginScene(); virtual void EndScene(); @@ -119,14 +123,18 @@ public: virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT); - //! Renders primitive composed of vertices with single texture virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices , int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)); - //! Renders primitive composed of vertices with multitexturing (2 textures) virtual void DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)); virtual void DrawPrimitive(PrimitiveType type, const VertexCol *vertices , int vertexCount); + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount); + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const VertexTex2* vertices, int vertexCount); + virtual unsigned int CreateStaticObject(PrimitiveType primitiveType, const VertexCol* vertices, int vertexCount); + virtual void DrawStaticObject(unsigned int objectId); + virtual void DestroyStaticObject(unsigned int objectId); + virtual int ComputeSphereVisibility(const Math::Vector ¢er, float radius); virtual void SetRenderState(RenderState state, bool enabled); @@ -200,6 +208,30 @@ private: //! Set of all created textures std::set m_allTextures; + + //! Type of vertex structure + enum VertexType + { + VERTEX_TYPE_NORMAL, + VERTEX_TYPE_TEX2, + VERTEX_TYPE_COL, + }; + + //! Info about static VBO buffers + struct VboObjectInfo + { + PrimitiveType primitiveType; + unsigned int bufferId; + VertexType vertexType; + int vertexCount; + }; + + //! Whether to use VBOs or display lists + bool m_useVbo; + //! Map of saved VBO objects + std::map m_vboObjects; + //! Last ID of VBO object + unsigned int m_lastVboId; }; -- cgit v1.2.3-1-g7c22