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.
This commit is contained in:
parent
c4c28360eb
commit
a05e712319
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
90
source/renderer/ModelDefRData.cpp
Normal file
90
source/renderer/ModelDefRData.cpp
Normal file
@ -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;
|
||||
}
|
40
source/renderer/ModelDefRData.h
Normal file
40
source/renderer/ModelDefRData.h
Normal file
@ -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<CModelRData*> m_SubmissionModels;
|
||||
static CModelDefRData* m_Submissions;
|
||||
};
|
||||
|
||||
|
||||
#endif // __MODELDEFRDATA_H__
|
@ -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<CModel*> 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; j<mdef->GetNumFaces(); 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<float[]> UVit = m_UV.GetIterator<float[]>();
|
||||
|
||||
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<numVertices; j++) {
|
||||
m_Vertices[j].m_UVs[0]=vertices[j].m_U;
|
||||
m_Vertices[j].m_UVs[1]=1-vertices[j].m_V;
|
||||
}
|
||||
}
|
||||
|
||||
// build vertices
|
||||
VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
|
||||
VertexArrayIterator<CVector3D> Color = m_Color.GetIterator<CVector3D>();
|
||||
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; j<numVertices; j++) {
|
||||
SkinPoint(vertices[j],bonematrices,m_Vertices[j].m_Position);
|
||||
SkinPoint(vertices[j],bonematrices,Position[j]);
|
||||
SkinNormal(vertices[j],invbonematrices,m_Normals[j]);
|
||||
}
|
||||
} else {
|
||||
@ -176,7 +192,7 @@ void CModelRData::BuildVertices()
|
||||
const CMatrix3D& transform=m_Model->GetTransform();
|
||||
const CMatrix3D& invtransform=m_Model->GetInvTransform();
|
||||
for (uint j=0; j<numVertices; j++) {
|
||||
transform.Transform(vertices[j].m_Coords,m_Vertices[j].m_Position);
|
||||
transform.Transform(vertices[j].m_Coords,Position[j]);
|
||||
invtransform.RotateTransposed(vertices[j].m_Norm,m_Normals[j]);
|
||||
}
|
||||
}
|
||||
@ -185,16 +201,15 @@ void CModelRData::BuildVertices()
|
||||
// now fill in UV and vertex colour data
|
||||
for (uint j=0; j<numVertices; j++) {
|
||||
CColor sc = m_Model->GetShadingColor();
|
||||
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;i<numFaces;i++) {
|
||||
SModelFace& face=faces[IndexSorter[i].first];
|
||||
m_Indices[indices++]=(u16)(face.m_Verts[0]+m_VB->m_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;i<m_Models.size();++i) {
|
||||
if (!flags || (m_Models[i]->GetFlags()&flags)) {
|
||||
CModelRData* modeldata=(CModelRData*) m_Models[i]->GetRenderData();
|
||||
modeldata->SubmitBatches();
|
||||
}
|
||||
}
|
||||
|
||||
// step through all accumulated batches
|
||||
const std::list<CVertexBuffer*>& buffers=g_VBMan.GetBufferList();
|
||||
std::list<CVertexBuffer*>::const_iterator iter;
|
||||
for (iter=buffers.begin();iter!=buffers.end();++iter) {
|
||||
CVertexBuffer* buffer=*iter;
|
||||
|
||||
// any batches in this VB?
|
||||
const std::vector<CVertexBuffer::Batch*>& 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;i<batches.size();++i) {
|
||||
const CVertexBuffer::Batch* batch=batches[i];
|
||||
if (batch->m_IndexData.size()>0) {
|
||||
if (streamflags & STREAM_UV0)
|
||||
ogl_tex_bind(batch->m_Texture);
|
||||
|
||||
for (uint j=0;j<batch->m_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;i<m_Models.size();++i) {
|
||||
if (!flags || (m_Models[i]->GetFlags()&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;
|
||||
}
|
||||
|
@ -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<CModel*> m_Models;
|
||||
// linked list of submitted models per CModelDefRData
|
||||
CModelRData* m_SubmissionNext;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
205
source/renderer/VertexArray.cpp
Normal file
205
source/renderer/VertexArray.cpp
Normal file
@ -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<CVector3D> VertexArray::Attribute::GetIterator<CVector3D>() const
|
||||
{
|
||||
debug_assert(vertexArray);
|
||||
debug_assert(type == GL_FLOAT);
|
||||
debug_assert(elems >= 3);
|
||||
|
||||
return vertexArray->MakeIterator<CVector3D>(this);
|
||||
}
|
||||
|
||||
template<>
|
||||
VertexArrayIterator<CVector4D> VertexArray::Attribute::GetIterator<CVector4D>() const
|
||||
{
|
||||
debug_assert(vertexArray);
|
||||
debug_assert(type == GL_FLOAT);
|
||||
debug_assert(elems >= 4);
|
||||
|
||||
return vertexArray->MakeIterator<CVector4D>(this);
|
||||
}
|
||||
|
||||
template<>
|
||||
VertexArrayIterator<float[]> VertexArray::Attribute::GetIterator<float[]>() const
|
||||
{
|
||||
debug_assert(vertexArray);
|
||||
debug_assert(type == GL_FLOAT);
|
||||
|
||||
return vertexArray->MakeIterator<float[]>(this);
|
||||
}
|
||||
|
||||
template<>
|
||||
VertexArrayIterator<SColor3ub> VertexArray::Attribute::GetIterator<SColor3ub>() const
|
||||
{
|
||||
debug_assert(vertexArray);
|
||||
debug_assert(type == GL_UNSIGNED_BYTE);
|
||||
debug_assert(elems >= 3);
|
||||
|
||||
return vertexArray->MakeIterator<SColor3ub>(this);
|
||||
}
|
||||
|
||||
template<>
|
||||
VertexArrayIterator<SColor4ub> VertexArray::Attribute::GetIterator<SColor4ub>() const
|
||||
{
|
||||
debug_assert(vertexArray);
|
||||
debug_assert(type == GL_UNSIGNED_BYTE);
|
||||
debug_assert(elems >= 4);
|
||||
|
||||
return vertexArray->MakeIterator<SColor4ub>(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;
|
||||
}
|
||||
|
172
source/renderer/VertexArray.h
Normal file
172
source/renderer/VertexArray.h
Normal file
@ -0,0 +1,172 @@
|
||||
#ifndef __VERTEXARRAY_H__
|
||||
#define __VERTEXARRAY_H__
|
||||
|
||||
#include "renderer/VertexBuffer.h"
|
||||
|
||||
// Iterator
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
VertexArrayIterator<T> 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<typename T>
|
||||
VertexArrayIterator<T> MakeIterator(const Attribute* attr)
|
||||
{
|
||||
debug_assert(attr->type && attr->elems);
|
||||
return VertexArrayIterator<T>(m_BackingStore + attr->offset, m_Stride);
|
||||
}
|
||||
|
||||
bool m_Dynamic;
|
||||
size_t m_NumVertices;
|
||||
std::vector<Attribute*> m_Attributes;
|
||||
|
||||
CVertexBuffer::VBChunk* m_VB;
|
||||
size_t m_Stride;
|
||||
char* m_BackingStore;
|
||||
};
|
||||
|
||||
|
||||
#endif // __VERTEXARRAY_H__
|
@ -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;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <vector>
|
||||
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue
Block a user