From a05e7123193135f8d97884738cddc6c1364e2a1c Mon Sep 17 00:00:00 2001 From: prefect Date: Sun, 2 Oct 2005 21:11:11 +0000 Subject: [PATCH] Added VertexArray & co. classes to simplify a flexible, render path dependent vertex array layout. Add CModelDefRData, a per-CModelDef render data class. This was SVN commit r2826. --- source/graphics/Color.h | 8 + source/graphics/ModelDef.cpp | 12 +- source/graphics/ModelDef.h | 18 +++ source/renderer/ModelDefRData.cpp | 90 ++++++++++++ source/renderer/ModelDefRData.h | 40 +++++ source/renderer/ModelRData.cpp | 236 +++++++++++++++--------------- source/renderer/ModelRData.h | 38 +++-- source/renderer/Renderer.h | 17 +-- source/renderer/VertexArray.cpp | 205 ++++++++++++++++++++++++++ source/renderer/VertexArray.h | 172 ++++++++++++++++++++++ source/renderer/VertexBuffer.cpp | 10 +- source/renderer/VertexBuffer.h | 4 +- 12 files changed, 695 insertions(+), 155 deletions(-) create mode 100644 source/renderer/ModelDefRData.cpp create mode 100644 source/renderer/ModelDefRData.h create mode 100644 source/renderer/VertexArray.cpp create mode 100644 source/renderer/VertexArray.h diff --git a/source/graphics/Color.h b/source/graphics/Color.h index e7dae4d0c0..5d52e544df 100755 --- a/source/graphics/Color.h +++ b/source/graphics/Color.h @@ -18,6 +18,14 @@ typedef CVector3D RGBColor; typedef CVector4D RGBAColor; +// SColor3ub: structure for packed RGB colors +struct SColor3ub +{ + u8 R; + u8 G; + u8 B; +}; + // SColor4ub: structure for packed RGBA colors struct SColor4ub { diff --git a/source/graphics/ModelDef.cpp b/source/graphics/ModelDef.cpp index 78137a7487..4ceca16358 100755 --- a/source/graphics/ModelDef.cpp +++ b/source/graphics/ModelDef.cpp @@ -16,7 +16,7 @@ // CModelDef Constructor CModelDef::CModelDef() : m_pVertices(0), m_NumVertices(0), m_pFaces(0), m_NumFaces(0), m_Bones(0), m_NumBones(0), - m_NumPropPoints(0), m_PropPoints(0) + m_NumPropPoints(0), m_PropPoints(0), m_RenderData(0) { } @@ -24,6 +24,7 @@ CModelDef::CModelDef() // CModelDef Destructor CModelDef::~CModelDef() { + delete m_RenderData; delete[] m_pVertices; delete[] m_pFaces; delete[] m_Bones; @@ -132,3 +133,12 @@ void CModelDef::Save(const char* filename,const CModelDef* mdef) packer.Write(filename); } + +// Set render data. This can only be done once at the moment. +// TODO: Is there a need to re-create render data? Perhaps reacting to render path changes? +void CModelDef::SetRenderData(CSharedRenderData* data) +{ + debug_assert(m_RenderData == 0); + m_RenderData = data; +} + diff --git a/source/graphics/ModelDef.h b/source/graphics/ModelDef.h index 987e99b0ea..17f6d8362b 100755 --- a/source/graphics/ModelDef.h +++ b/source/graphics/ModelDef.h @@ -68,6 +68,17 @@ struct SModelFace }; +//////////////////////////////////////////////////////////////////////////////////////// +// CSharedRenderData: render data for a CModelDef, shared between all model render data +// instances that refer to the same CModelDef. +class CSharedRenderData +{ +public: + CSharedRenderData() { } + virtual ~CSharedRenderData() { } +}; + + //////////////////////////////////////////////////////////////////////////////////////// // CModelDef: a raw 3D model; describes the vertices, faces, skinning and skeletal // information of a model @@ -112,6 +123,10 @@ public: // null if no match (case insensitive search) SPropPoint* FindPropPoint(const char* name) const; + // accessor: render data + CSharedRenderData* GetRenderData() const { return m_RenderData; } + void SetRenderData(CSharedRenderData* data); + public: // vertex data u32 m_NumVertices; @@ -126,6 +141,9 @@ public: u32 m_NumPropPoints; SPropPoint* m_PropPoints; + // renderdata shared by models of the same modeldef + CSharedRenderData* m_RenderData; + protected: static CModelDef* Load(const char* filename); }; diff --git a/source/renderer/ModelDefRData.cpp b/source/renderer/ModelDefRData.cpp new file mode 100644 index 0000000000..c58ce2df39 --- /dev/null +++ b/source/renderer/ModelDefRData.cpp @@ -0,0 +1,90 @@ +#include "precompiled.h" + +#include "ogl.h" +#include "Vector3D.h" +#include "Vector4D.h" +#include "ps/CLogger.h" +#include "graphics/Color.h" +#include "graphics/Model.h" +#include "renderer/ModelRData.h" +#include "renderer/ModelDefRData.h" + + +#define LOG_CATEGORY "graphics" + + +// Shared list of all submitted models this frame, sorted by CModelDef +CModelDefRData* CModelDefRData::m_Submissions = 0; + +CModelDefRData::CModelDefRData(CModelDef* mdef) + : m_ModelDef(mdef) +{ + m_SubmissionNext = 0; + m_SubmissionSlots = 0; + + Build(); +} + +CModelDefRData::~CModelDefRData() +{ +} + + +// Create and upload shared vertex arrays +void CModelDefRData::Build() +{ +} + +// Submit one model. +// Models are sorted into a hash-table to avoid ping-ponging between +// different render states later on. +void CModelDefRData::Submit(CModelRData* data) +{ + debug_assert(data->GetModel()->GetModelDef()->GetRenderData() == this); + + if (!m_SubmissionSlots) + { + m_SubmissionNext = m_Submissions; + m_Submissions = this; + } + + Handle htex = data->GetModel()->GetTexture()->GetHandle(); + uint idx; + + for(idx = 0; idx < m_SubmissionSlots; ++idx) + { + CModelRData* in = m_SubmissionModels[idx]; + + if (in->GetModel()->GetTexture()->GetHandle() == htex) + break; + } + + if (idx >= m_SubmissionSlots) + { + ++m_SubmissionSlots; + if (m_SubmissionSlots > m_SubmissionModels.size()) + { + m_SubmissionModels.push_back(0); + debug_assert(m_SubmissionModels.size() == m_SubmissionSlots); + } + m_SubmissionModels[idx] = 0; + } + + data->m_SubmissionNext = m_SubmissionModels[idx]; + m_SubmissionModels[idx] = data; +} + + +// Clear all submissions for this CModelDef +// Do not shrink the submissions array immediately to avoid unnecessary +// allocate/free roundtrips through memory management. +void CModelDefRData::ClearSubmissions() +{ + static uint mostslots = 1; + if (m_SubmissionSlots > mostslots) + { + mostslots = m_SubmissionSlots; + debug_printf("CModelDefRData: SubmissionSlots maximum: %u\n", mostslots); + } + m_SubmissionSlots = 0; +} diff --git a/source/renderer/ModelDefRData.h b/source/renderer/ModelDefRData.h new file mode 100644 index 0000000000..969ed2c37b --- /dev/null +++ b/source/renderer/ModelDefRData.h @@ -0,0 +1,40 @@ +#ifndef __MODELDEFRDATA_H__ +#define __MODELDEFRDATA_H__ + +#include "graphics/ModelDef.h" +#include "renderer/ModelRData.h" +#include "renderer/VertexArray.h" + + +class CModelRData; + +// Maintain rendering data that can be shared across models +// that are based on the same model definition. +class CModelDefRData : public CSharedRenderData +{ + friend class CModelRData; + +public: + CModelDefRData(CModelDef* mdef); + virtual ~CModelDefRData(); + + // Submit one model + void Submit(CModelRData* data); + // Clear all submissions for this CModelDef + void ClearSubmissions(); + +private: + // Build and upload vertex arrays + void Build(); + +private: + CModelDef* m_ModelDef; + + CModelDefRData* m_SubmissionNext; + uint m_SubmissionSlots; + std::vector m_SubmissionModels; + static CModelDefRData* m_Submissions; +}; + + +#endif // __MODELDEFRDATA_H__ diff --git a/source/renderer/ModelRData.cpp b/source/renderer/ModelRData.cpp index 6e82a915fa..e99499ea48 100755 --- a/source/renderer/ModelRData.cpp +++ b/source/renderer/ModelRData.cpp @@ -12,15 +12,14 @@ #include "ModelDef.h" #include "MaterialManager.h" #include "Profile.h" +#include "renderer/ModelDefRData.h" + -/////////////////////////////////////////////////////////////////// -// shared list of all submitted models this frame -std::vector CModelRData::m_Models; /////////////////////////////////////////////////////////////////// // CModelRData constructor CModelRData::CModelRData(CModel* model) - : m_Model(model), m_Vertices(0), m_Normals(0), m_Indices(0), m_VB(0), m_Flags(0) + : m_Model(model), m_Normals(0), m_DynamicArray(true), m_Indices(0), m_Flags(0) { debug_assert(model); // build all data now @@ -33,17 +32,35 @@ CModelRData::~CModelRData() { // clean up system copies of data delete[] m_Indices; - delete[] m_Vertices; delete[] m_Normals; - if (m_VB) { - // release vertex buffer chunks - g_VBMan.Release(m_VB); - } } void CModelRData::Build() { + CModelDefPtr mdef = m_Model->GetModelDef(); + + if (!mdef->GetRenderData()) + { + mdef->SetRenderData(new CModelDefRData(&*mdef)); + } + + m_Position.type = GL_FLOAT; + m_Position.elems = 3; + m_DynamicArray.AddAttribute(&m_Position); + + m_UV.type = GL_FLOAT; + m_UV.elems = 2; + m_DynamicArray.AddAttribute(&m_UV); + + m_Color.type = GL_FLOAT; + m_Color.elems = 3; + m_DynamicArray.AddAttribute(&m_Color); + + m_DynamicArray.SetNumVertices(mdef->GetNumVertices()); + m_DynamicArray.Layout(); + // build data + BuildStaticVertices(); BuildVertices(); BuildIndices(); // force a texture load on model's texture @@ -56,9 +73,9 @@ void CModelRData::Build() { m_Flags |= MODELRDATA_FLAG_PLAYERCOLOR; } - else if(m_Model->GetMaterial().UsesAlpha()) + else if(m_Model->GetMaterial().UsesAlpha()) { - m_Flags |= MODELRDATA_FLAG_TRANSPARENT; + m_Flags |= MODELRDATA_FLAG_TRANSPARENT; } } @@ -67,40 +84,31 @@ void CModelRData::BuildIndices() CModelDefPtr mdef=m_Model->GetModelDef(); debug_assert(mdef); - // must have a valid vertex buffer by this point so we know where indices are supposed to start - debug_assert(m_VB); - // allocate indices if we haven't got any already if (!m_Indices) { m_Indices=new u16[mdef->GetNumFaces()*3]; } // build indices - u32 base=(u32)m_VB->m_Index; u32 indices=0; SModelFace* faces=mdef->GetFaces(); for (size_t j=0; jGetNumFaces(); j++) { SModelFace& face=faces[j]; - m_Indices[indices++]=face.m_Verts[0]+base; - m_Indices[indices++]=face.m_Verts[1]+base; - m_Indices[indices++]=face.m_Verts[2]+base; + m_Indices[indices++]=face.m_Verts[0]; + m_Indices[indices++]=face.m_Verts[1]; + m_Indices[indices++]=face.m_Verts[2]; } } -/* JW: function is apparently currently unused - -static SColor4ub ConvertColor(const RGBColor& src) +static SColor3ub ConvertColor(const RGBColor& src) { - SColor4ub result; + SColor3ub result; result.R=clamp(int(src.X*255),0,255); result.G=clamp(int(src.Y*255),0,255); result.B=clamp(int(src.Z*255),0,255); - result.A=0xff; return result; } -*/ - ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // SkinPoint: skin the vertex position using it's blend data and given bone matrices static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result) @@ -143,6 +151,19 @@ static void SkinNormal(const SModelVertex& vertex,const CMatrix3D* invmatrices,C } } +void CModelRData::BuildStaticVertices() +{ + CModelDefPtr mdef = m_Model->GetModelDef(); + size_t numVertices = mdef->GetNumVertices(); + SModelVertex* vertices = mdef->GetVertices(); + VertexArrayIterator UVit = m_UV.GetIterator(); + + for (uint j=0; j < numVertices; ++j, ++UVit) { + (*UVit)[0] = vertices[j].m_U; + (*UVit)[1] = 1.0-vertices[j].m_V; + } +} + void CModelRData::BuildVertices() { CModelDefPtr mdef=m_Model->GetModelDef(); @@ -151,24 +172,19 @@ void CModelRData::BuildVertices() // allocate vertices if we haven't got any already and // fill in data that never changes - if (!m_Vertices) { - m_Vertices=new SVertex[mdef->GetNumVertices()]; + if (!m_Normals) m_Normals=new CVector3D[mdef->GetNumVertices()]; - - for (uint j=0; j Position = m_Position.GetIterator(); + VertexArrayIterator Color = m_Color.GetIterator(); const CMatrix3D* bonematrices=m_Model->GetBoneMatrices(); if (bonematrices) { // boned model - calculate skinned vertex positions/normals PROFILE( "skinning bones" ); const CMatrix3D* invbonematrices=m_Model->GetInvBoneMatrices(); for (size_t j=0; jGetTransform(); const CMatrix3D& invtransform=m_Model->GetInvTransform(); for (uint j=0; jGetShadingColor(); - g_Renderer.m_SHCoeffsUnits.Evaluate(m_Normals[j], m_Vertices[j].m_Color, + RGBColor tempcolor; + g_Renderer.m_SHCoeffsUnits.Evaluate(m_Normals[j], tempcolor, RGBColor(sc.r, sc.g, sc.b)); + Color[j] = tempcolor;//ConvertColor(tempcolor); } PROFILE_END( "lighting vertices" ); - // upload everything to vertex buffer - create one if necessary - if (!m_VB) { - m_VB=g_VBMan.Allocate(sizeof(SVertex),mdef->GetNumVertices(),mdef->GetNumBones() ? true : false); - } - m_VB->m_Owner->UpdateChunkVertices(m_VB,m_Vertices); + // upload everything to vertex buffer + m_DynamicArray.Upload(); } @@ -203,29 +218,38 @@ void CModelRData::RenderStreams(u32 streamflags, bool isplayer) CModelDefPtr mdldef=m_Model->GetModelDef(); if (streamflags & STREAM_UV0) - { + { if(!isplayer) m_Model->GetMaterial().Bind(); else g_Renderer.SetTexture(1,m_Model->GetTexture()); - g_Renderer.SetTexture(0,m_Model->GetTexture()); - } + g_Renderer.SetTexture(0,m_Model->GetTexture()); + } - u8* base=m_VB->m_Owner->Bind(); - - // set vertex pointers - u32 stride=sizeof(SVertex); - glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SVertex,m_Position)); - if (streamflags & STREAM_COLOR) glColorPointer(3,GL_FLOAT,stride,base+offsetof(SVertex,m_Color)); - if (streamflags & STREAM_UV0) glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SVertex,m_UVs)); + u8* base = m_DynamicArray.Bind(); + size_t stride = m_DynamicArray.GetStride(); + + glVertexPointer(3, GL_FLOAT, stride, base + m_Position.offset); + if (streamflags & STREAM_COLOR) glColorPointer(3, m_Color.type, stride, base + m_Color.offset); +#if 0 + { + uint a = (uint)this; + uint b = (uint)m_Model->GetTexture(); + uint hash = ((a >> 16) ^ (b & 0xffff)) | ((a & 0xffff0000) ^ (b << 16)); + hash = (hash * 65537) + 17; + hash |= 0xff000000; + glColor4ubv((GLubyte*)&hash); + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + if (streamflags & STREAM_UV0) glTexCoordPointer(2, GL_FLOAT, stride, base + m_UV.offset); // render the lot size_t numFaces=mdldef->GetNumFaces(); -// glDrawRangeElements(GL_TRIANGLES,0,mdldef->GetNumVertices(),numFaces*3,GL_UNSIGNED_SHORT,m_Indices); - glDrawElements(GL_TRIANGLES,(GLsizei)numFaces*3,GL_UNSIGNED_SHORT,m_Indices); + glDrawRangeElements(GL_TRIANGLES,0,mdldef->GetNumVertices(),numFaces*3,GL_UNSIGNED_SHORT,m_Indices); - if(streamflags & STREAM_UV0 & !isplayer) + if(streamflags & STREAM_UV0 & !isplayer) m_Model->GetMaterial().Unbind(); // bump stats @@ -295,9 +319,9 @@ float CModelRData::BackToFrontIndexSort(CMatrix3D& objToCam) u32 indices=0; for (i=0;im_Index); - m_Indices[indices++]=(u16)(face.m_Verts[1]+m_VB->m_Index); - m_Indices[indices++]=(u16)(face.m_Verts[2]+m_VB->m_Index); + m_Indices[indices++]=(u16)(face.m_Verts[0]); + m_Indices[indices++]=(u16)(face.m_Verts[1]); + m_Indices[indices++]=(u16)(face.m_Verts[2]); } // clear list for next call @@ -306,74 +330,35 @@ float CModelRData::BackToFrontIndexSort(CMatrix3D& objToCam) return mindist; } -///////////////////////////////////////////////////////////////////////////////////////////////////// -// SubmitBatches: submit batches for this model to the vertex buffer -void CModelRData::SubmitBatches() -{ - debug_assert(m_VB); - m_VB->m_Owner->AppendBatch(m_VB,m_Model->GetTexture()->GetHandle(),m_Model->GetModelDef()->GetNumFaces()*3,m_Indices); -} ///////////////////////////////////////////////////////////////////////////////////////////////////// // RenderModels: render all submitted models; assumes necessary client states already enabled, // and texture environment already setup as required -void CModelRData::RenderModels(u32 streamflags,u32 flags) +void CModelRData::RenderModels(u32 streamflags, u32 flags) { - uint i; -#if 1 - // submit batches for each model to the vertex buffer - for (i=0;iGetFlags()&flags)) { - CModelRData* modeldata=(CModelRData*) m_Models[i]->GetRenderData(); - modeldata->SubmitBatches(); - } - } - - // step through all accumulated batches - const std::list& buffers=g_VBMan.GetBufferList(); - std::list::const_iterator iter; - for (iter=buffers.begin();iter!=buffers.end();++iter) { - CVertexBuffer* buffer=*iter; - - // any batches in this VB? - const std::vector& batches=buffer->GetBatches(); - if (batches.size()>0) { - u8* base=buffer->Bind(); - - // setup data pointers - u32 stride=sizeof(SVertex); - glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SVertex,m_Position)); - if (streamflags & STREAM_COLOR) glColorPointer(3,GL_FLOAT,stride,base+offsetof(SVertex,m_Color)); - if (streamflags & STREAM_UV0) glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SVertex,m_UVs[0])); - - // render each batch - for (i=0;im_IndexData.size()>0) { - if (streamflags & STREAM_UV0) - ogl_tex_bind(batch->m_Texture); - - for (uint j=0;jm_IndexData.size();j++) { - glDrawElements(GL_TRIANGLES,(GLsizei)batch->m_IndexData[j].first,GL_UNSIGNED_SHORT,batch->m_IndexData[j].second); - g_Renderer.m_Stats.m_DrawCalls++; - g_Renderer.m_Stats.m_ModelTris+=(u32)batch->m_IndexData[j].first/2; - } - } + for(CModelDefRData* mdefdata = CModelDefRData::m_Submissions; + mdefdata; + mdefdata = mdefdata->m_SubmissionNext) + { + for(uint idx = 0; idx < mdefdata->m_SubmissionSlots; ++idx) + { + if (!mdefdata->m_SubmissionModels[idx]) + break; + + for(CModelRData* modeldata = mdefdata->m_SubmissionModels[idx]; + modeldata; + modeldata = modeldata->m_SubmissionNext) + { + if (flags && !(modeldata->GetModel()->GetFlags()&flags)) + continue; + + modeldata->RenderStreams(streamflags, false); } } } - // everything rendered; empty out batch lists - g_VBMan.ClearBatchIndices(); -#else - for (i=0;iGetFlags()&flags)) { - CModelRData* modeldata=(CModelRData*) m_Models[i]->GetRenderData(); - modeldata->RenderStreams(streamflags); - } - } -#endif } + ///////////////////////////////////////////////////////////////////////////////////////////// // Submit: submit a model to render this frame void CModelRData::Submit(CModel* model) @@ -397,7 +382,24 @@ void CModelRData::Submit(CModel* model) // add this model to the player renderer g_PlayerRenderer.Add(model); } else { - // add to regular model list - m_Models.push_back(model); + CModelDefPtr mdldef = model->GetModelDef(); + CModelDefRData* mdefdata = (CModelDefRData*)mdldef->GetRenderData(); + + debug_assert(mdefdata != 0); + + mdefdata->Submit(data); } } + + +///////////////////////////////////////////////////////////////////////////////////////////// +// ClearSubmissions: Clear the submissions list +// TODO: This is asymmetrical: It only clears CModelRData lists, but no player/transparency renderer lists +void CModelRData::ClearSubmissions() +{ + for(CModelDefRData* mdefdata = CModelDefRData::m_Submissions; mdefdata; mdefdata = mdefdata->m_SubmissionNext) + { + mdefdata->ClearSubmissions(); + } + CModelDefRData::m_Submissions = 0; +} diff --git a/source/renderer/ModelRData.h b/source/renderer/ModelRData.h index fdb5163cfc..2faae32d98 100755 --- a/source/renderer/ModelRData.h +++ b/source/renderer/ModelRData.h @@ -5,14 +5,18 @@ #include "Vector3D.h" #include "Color.h" #include "RenderableObject.h" +#include "renderer/VertexArray.h" #define MODELRDATA_FLAG_TRANSPARENT (1<<0) #define MODELRDATA_FLAG_PLAYERCOLOR (1<<1) class CModel; +class CModelDefRData; class CModelRData : public CRenderData { + friend class CModelDefRData; + public: CModelRData(CModel* model); ~CModelRData(); @@ -22,15 +26,17 @@ public: // return render flags for this model u32 GetFlags() const { return m_Flags; } - + // accessor: model we're based on + CModel* GetModel() const { return m_Model; } + // sort indices of this object from back to front according to given // object to camera space transform; return sqrd distance to centre of nearest triangle float BackToFrontIndexSort(CMatrix3D& objToCam); // submit a model to render this frame static void Submit(CModel* model); - // clear per frame patch list - static void ClearSubmissions() { m_Models.clear(); } + // clear per frame model list + static void ClearSubmissions(); // render all submitted models static void RenderModels(u32 streamflags,u32 flags=0); @@ -39,35 +45,25 @@ private: // build this renderdata object void Build(); + void BuildStaticVertices(); void BuildVertices(); void BuildIndices(); - // submit batches for this model to the vertex buffer - void SubmitBatches(); - - struct SVertex { - // vertex position - CVector3D m_Position; - // vertex uvs for base texture - float m_UVs[2]; - // RGB vertex color - RGBColor m_Color; - }; - // owner model CModel* m_Model; - // vertex buffer object for this model - CVertexBuffer::VBChunk* m_VB; - // model render vertices - SVertex* m_Vertices; // transformed vertex normals - required for recalculating lighting on skinned models CVector3D* m_Normals; + // vertex array + VertexArray m_DynamicArray; + VertexArray::Attribute m_Position; + VertexArray::Attribute m_UV; + VertexArray::Attribute m_Color; // model render indices u16* m_Indices; // model render flags u32 m_Flags; - // list of all submitted models - static std::vector m_Models; + // linked list of submitted models per CModelDefRData + CModelRData* m_SubmissionNext; }; diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 23c06a8491..f07004229c 100755 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -40,13 +40,13 @@ class CTerrain; enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES }; // stream flags -#define STREAM_POS 0x01 -#define STREAM_NORMAL 0x02 -#define STREAM_COLOR 0x04 -#define STREAM_UV0 0x08 -#define STREAM_UV1 0x10 -#define STREAM_UV2 0x20 -#define STREAM_UV3 0x40 +#define STREAM_POS 0x01 +#define STREAM_NORMAL 0x02 +#define STREAM_COLOR 0x04 +#define STREAM_UV0 0x08 +#define STREAM_UV1 0x10 +#define STREAM_UV2 0x20 +#define STREAM_UV3 0x40 #define STREAM_POSTOUV0 0x80 ////////////////////////////////////////////////////////////////////////////////////////// @@ -247,9 +247,6 @@ protected: friend class CTransparencyRenderer; friend class CPlayerRenderer; - // update renderdata of everything submitted - void UpdateSubmittedObjectData(); - // patch rendering stuff void RenderPatchSubmissions(); void RenderPatches(); diff --git a/source/renderer/VertexArray.cpp b/source/renderer/VertexArray.cpp new file mode 100644 index 0000000000..173ead343f --- /dev/null +++ b/source/renderer/VertexArray.cpp @@ -0,0 +1,205 @@ +#include "precompiled.h" + +#include "ogl.h" +#include "Vector3D.h" +#include "Vector4D.h" +#include "graphics/Color.h" +#include "renderer/VertexArray.h" +#include "renderer/VertexBuffer.h" +#include "renderer/VertexBufferManager.h" + + +VertexArray::VertexArray(bool dynamic) +{ + m_Dynamic = dynamic; + m_NumVertices = 0; + + m_VB = 0; + m_BackingStore = 0; + m_Stride = 0; +} + + +VertexArray::~VertexArray() +{ + Free(); +} + +// Free all resources on destruction or when a layout parameter changes +void VertexArray::Free() +{ + delete[] m_BackingStore; + m_BackingStore = 0; + + if (m_VB) + { + g_VBMan.Release(m_VB); + m_VB = 0; + } +} + + +// Set the number of vertices stored in the array +void VertexArray::SetNumVertices(size_t num) +{ + if (num == m_NumVertices) + return; + + Free(); + m_NumVertices = num; +} + + +// Add vertex attributes like Position, Normal, UV +void VertexArray::AddAttribute(Attribute* attr) +{ + debug_assert((attr->type == GL_FLOAT || attr->type == GL_UNSIGNED_BYTE) && "Unsupported attribute type"); + debug_assert(attr->elems >= 1 && attr->elems <= 4); + + attr->vertexArray = this; + m_Attributes.push_back(attr); + + Free(); +} + + +// Template specialization for GetIterator(). +// We can put this into the source file because only a fixed set of types +// is supported for type safety. +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_FLOAT); + debug_assert(elems >= 3); + + return vertexArray->MakeIterator(this); +} + +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_FLOAT); + debug_assert(elems >= 4); + + return vertexArray->MakeIterator(this); +} + +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_FLOAT); + + return vertexArray->MakeIterator(this); +} + +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_UNSIGNED_BYTE); + debug_assert(elems >= 3); + + return vertexArray->MakeIterator(this); +} + +template<> +VertexArrayIterator VertexArray::Attribute::GetIterator() const +{ + debug_assert(vertexArray); + debug_assert(type == GL_UNSIGNED_BYTE); + debug_assert(elems >= 4); + + return vertexArray->MakeIterator(this); +} + + + +static size_t RoundStride(size_t stride) +{ + if (stride <= 0) + return 0; + if (stride <= 4) + return 4; + if (stride <= 8) + return 8; + if (stride <= 16) + return 16; + + return (stride + 31) & ~31; +} + +// Re-layout by assigning offsets on a first-come first-serve basis, +// then round up to a reasonable stride. +// Backing store is also created here, VBOs are created on upload. +void VertexArray::Layout() +{ + Free(); + + m_Stride = 0; + + //debug_printf("Layouting VertexArray\n"); + + for(int idx = m_Attributes.size()-1; idx >= 0; --idx) + { + Attribute* attr = m_Attributes[idx]; + + if (!attr->type || !attr->elems) + continue; + + size_t attrSize; + + switch(attr->type) { + case GL_UNSIGNED_BYTE: attrSize = sizeof(GLubyte); break; + case GL_FLOAT: attrSize = sizeof(GLfloat); break; + default: debug_warn("Bad AttributeInfo::Type"); break; + } + + attrSize *= attr->elems; + + attr->offset = m_Stride; + m_Stride = (m_Stride + attrSize + 3) & ~3; + + //debug_printf("%i: offset: %u\n", idx, attr->offset); + } + + m_Stride = RoundStride(m_Stride); + + //debug_printf("Stride: %u\n", m_Stride); + + if (m_Stride) + m_BackingStore = new char[m_Stride * m_NumVertices]; +} + + +// (Re-)Upload the attributes. +// Create the VBO if necessary. +void VertexArray::Upload() +{ + debug_assert(m_BackingStore); + + if (!m_VB) + m_VB = g_VBMan.Allocate(m_Stride, m_NumVertices, m_Dynamic); + + m_VB->m_Owner->UpdateChunkVertices(m_VB, m_BackingStore); +} + + +// Bind this array, returns the base address for calls to glVertexPointer etc. +u8* VertexArray::Bind() +{ + u8* base = m_VB->m_Owner->Bind(); + base += m_VB->m_Index*m_Stride; + return base; +} + + +// Free the backing store to save some memory +void VertexArray::FreeBackingStore() +{ + delete[] m_BackingStore; + m_BackingStore = 0; +} + diff --git a/source/renderer/VertexArray.h b/source/renderer/VertexArray.h new file mode 100644 index 0000000000..bc43af3949 --- /dev/null +++ b/source/renderer/VertexArray.h @@ -0,0 +1,172 @@ +#ifndef __VERTEXARRAY_H__ +#define __VERTEXARRAY_H__ + +#include "renderer/VertexBuffer.h" + +// Iterator +template +class VertexArrayIterator +{ +public: + typedef T Type; + +public: + VertexArrayIterator(char* data, size_t stride) : + m_Data(data), m_Stride(stride) + { + } + + VertexArrayIterator(const VertexArrayIterator& rhs) : + m_Data(rhs.m_Data), m_Stride(rhs.m_Stride) + { + } + + VertexArrayIterator& operator=(const VertexArrayIterator& rhs) + { + m_Data = rhs.m_Data; + m_Stride = rhs.m_Stride; + } + + // Accessors + T& operator*() const { return *(T*)m_Data; } + T* operator->() const { return (T*)m_Data; } + T& operator[](ssize_t idx) const { return *(T*)(m_Data + idx*m_Stride); } + + // Walking + VertexArrayIterator& operator++() + { + m_Data += m_Stride; + return *this; + } + VertexArrayIterator operator++(int) + { + VertexArrayIterator tmp = *this; + m_Data += m_Stride; + return tmp; + } + VertexArrayIterator& operator--() + { + m_Data -= m_Stride; + return *this; + } + VertexArrayIterator operator--(int) + { + VertexArrayIterator tmp = *this; + m_Data -= m_Stride; + return tmp; + } + + VertexArrayIterator& operator+=(ssize_t rhs) + { + m_Data += rhs*m_Stride; + return *this; + } + VertexArrayIterator& operator-=(ssize_t rhs) + { + m_Data -= rhs*m_Stride; + return *this; + } + + VertexArrayIterator operator+(ssize_t rhs) const + { + VertexArrayIterator tmp = *this; + tmp.m_Data += rhs*m_Stride; + return tmp; + } + VertexArrayIterator operator-(ssize_t rhs) const + { + VertexArrayIterator tmp = *this; + tmp.m_Data -= rhs*m_Stride; + return tmp; + } + +private: + char* m_Data; + size_t m_Stride; +}; + + +// Manage a vertex array with a runtime-determined set of attributes. +// +// Purpose: Different rendering paths sometimes require different sets of +// attributes (e.g. normal vector vs. color data), which is difficult to +// support with hardcoded vertex structures. +// This class chooses the vertex layout at runtime, based on the attributes +// that are actually needed. +// Furthermore, it stores dynamic and static attributes in different +// vertex buffers, so that needless re-uploads of static data is avoided. +// +// Note that this class will not allocate any OpenGL resources until one +// of the Upload functions is called. +class VertexArray +{ +public: + struct Attribute + { + // Data type. Currently supported: GL_FLOAT + GLenum type; + // How many elements per vertex (e.g. 3 for RGB, 2 for UV) + GLuint elems; + + // Offset (in bytes) into a vertex structure (filled in by Layout()) + size_t offset; + + VertexArray* vertexArray; + + Attribute() : type(0), elems(0), offset(0), vertexArray(0) { } + + // Get an iterator for the given attribute that initially points at the first vertex. + // Supported types T: CVector3D, CVector4D, float[], SColor3ub, SColor4ub + // This function verifies at runtime that the requested type T matches + // the attribute definition passed to AddAttribute(). + template + VertexArrayIterator GetIterator() const; + }; + +public: + VertexArray(bool dynamic); + ~VertexArray(); + + // Set the number of vertices stored in the array + void SetNumVertices(size_t num); + // Add vertex attributes + void AddAttribute(Attribute* attr); + + size_t GetNumVertices() const { return m_NumVertices; } + size_t GetStride() const { return m_Stride; } + + // Layout the vertex array format and create backing buffer in RAM. + // You must call Layout() after changing the number of vertices or + // attributes. + // All vertex data is lost when a vertex array is re-layouted. + void Layout(); + // (Re-)Upload the static/dynamic attributes of the vertex array. + void Upload(); + // Bind this array, returns the base address for calls to glVertexPointer etc. + u8* Bind(); + + // If you know for certain that you'll never have to change the data again, + // call this to free some memory. + void FreeBackingStore(); + +private: + void Free(); + + template + VertexArrayIterator MakeIterator(const Attribute* attr) + { + debug_assert(attr->type && attr->elems); + return VertexArrayIterator(m_BackingStore + attr->offset, m_Stride); + } + + bool m_Dynamic; + size_t m_NumVertices; + std::vector m_Attributes; + + CVertexBuffer::VBChunk* m_VB; + size_t m_Stride; + char* m_BackingStore; +}; + + +#endif // __VERTEXARRAY_H__ diff --git a/source/renderer/VertexBuffer.cpp b/source/renderer/VertexBuffer.cpp index eafd5e5afc..95fd0140e2 100755 --- a/source/renderer/VertexBuffer.cpp +++ b/source/renderer/VertexBuffer.cpp @@ -33,8 +33,7 @@ void CVertexBuffer::Shutdown() CVertexBuffer::CVertexBuffer(size_t vertexSize,bool dynamic) : m_VertexSize(vertexSize), m_Dynamic(dynamic), m_SysMem(0), m_Handle(0) { - // store max/free vertex counts - m_MaxVertices=m_FreeVertices=MAX_VB_SIZE_BYTES/vertexSize; + size_t size = MAX_VB_SIZE_BYTES; // allocate raw buffer if (g_Renderer.m_Caps.m_VBO) { @@ -49,13 +48,16 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize,bool dynamic) glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle); if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed(); - glBufferDataARB(GL_ARRAY_BUFFER_ARB,MAX_VB_SIZE_BYTES,0,m_Dynamic ? GL_STREAM_DRAW_ARB : GL_STATIC_DRAW_ARB); + glBufferDataARB(GL_ARRAY_BUFFER_ARB,size,0,m_Dynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB); if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed(); } else { - m_SysMem=new u8[MAX_VB_SIZE_BYTES]; + m_SysMem=new u8[size]; } + // store max/free vertex counts + m_MaxVertices=m_FreeVertices=size/vertexSize; + // create sole free chunk VBChunk* chunk=new VBChunk; chunk->m_Owner=this; diff --git a/source/renderer/VertexBuffer.h b/source/renderer/VertexBuffer.h index 3913b5e0b5..b146d8b2b2 100755 --- a/source/renderer/VertexBuffer.h +++ b/source/renderer/VertexBuffer.h @@ -16,7 +16,7 @@ #include // absolute maximum (bytewise) size of each GL vertex buffer object -#define MAX_VB_SIZE_BYTES (32*8192) +#define MAX_VB_SIZE_BYTES (512*1024) /////////////////////////////////////////////////////////////////////////////// // CVertexBuffer: encapsulation of ARB_vertex_buffer_object, also supplying @@ -47,7 +47,7 @@ public: public: // constructor, destructor - CVertexBuffer(size_t vertexSize,bool dynamic); + CVertexBuffer(size_t vertexSize, bool dynamic); ~CVertexBuffer(); // bind to this buffer; return pointer to address required as parameter