Commit 2ca8282e authored by lukeph's avatar lukeph

-- added VBO support --

-so far only the openGL driver is supported, will add dx tomorrow or the next day

-right now to use, go: MeshBuffer->setHardwareMappingHint(EHM_STATIC); but this will be automatic when complete

-code needs a clean up, and the design isn't fixed, I just wanted to get stuff happening :)

-works well with hardware skinning :)

(design: http://img220.imageshack.us/img220/1931/vbo8xy8.png)

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@1095 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 51f7a358
......@@ -18,7 +18,7 @@ namespace scene
{
public:
//! constructor
CMeshBuffer() // everything's default constructed
CMeshBuffer():ChangedID(1),MappingHint(EHM_NEVER) // everything's default constructed
{
#ifdef _DEBUG
setDebugName("SMeshBuffer");
......@@ -170,6 +170,29 @@ namespace scene
BoundingBox.addInternalBox(other->getBoundingBox());
}
//! get the current hardware mapping hint
virtual const E_HARDWARE_MAPPING getHardwareMappingHint() const
{
return MappingHint;
}
//! set the hardware mapping hint, for driver
virtual void setHardwareMappingHint( E_HARDWARE_MAPPING NewMappingHint )
{
MappingHint=NewMappingHint;
}
//! flags the mesh as changed, reloads hardware buffers
virtual void setDirty() {ChangedID++;}
virtual const u32 getChangedID() const {return ChangedID;}
u32 ChangedID;
//! hardware mapping hint
E_HARDWARE_MAPPING MappingHint;
//! Material for this meshbuffer.
video::SMaterial Material;
//! Vertices of this buffer
......
......@@ -55,6 +55,20 @@ namespace scene
};
enum E_HARDWARE_MAPPING
{
//! Don't load in hardware
EHM_NEVER=0,
//! Rarely changed
EHM_STATIC,
//! Sometimes changed
EHM_DYNAMIC,
//! Always changed
EHM_STREAM
};
//! Struct for holding a mesh with a single material
/** SMeshBuffer is a simple implementation of a MeshBuffer. */
......@@ -120,6 +134,21 @@ namespace scene
//! append the meshbuffer to the current buffer
virtual void append(const IMeshBuffer* const other) = 0;
//! get the current hardware mapping hint
virtual const E_HARDWARE_MAPPING getHardwareMappingHint() const = 0;
//! set the hardware mapping hint, for driver
virtual void setHardwareMappingHint( E_HARDWARE_MAPPING NewMappingHint ) = 0;
//! flags the mesh as changed, reloads hardware buffers
virtual void setDirty() = 0;
virtual const u32 getChangedID() const = 0;
u32 HardwareHint;
};
} // end namespace scene
......
......@@ -16,7 +16,7 @@ namespace scene
struct SSharedMeshBuffer : public IMeshBuffer
{
//! constructor
SSharedMeshBuffer() : IMeshBuffer(), Vertices(0)
SSharedMeshBuffer() : IMeshBuffer(), ChangedID(1),MappingHint(Never), Vertices(0)
{
#ifdef _DEBUG
setDebugName("SSharedMeshBuffer");
......@@ -127,6 +127,31 @@ namespace scene
//! append the meshbuffer to the current buffer
virtual void append(const IMeshBuffer* const other) {}
//! get the current hardware mapping hint
virtual const E_HARDWARE_MAPPING getHardwareMappingHint() const
{
return MappingHint;
}
//! set the hardware mapping hint, for driver
virtual void setHardwareMappingHint( E_HARDWARE_MAPPING NewMappingHint )
{
MappingHint=NewMappingHint;
}
//! flags the mesh as changed, reloads hardware buffers
virtual void setDirty() {ChangedID++;}
virtual const u32 getChangedID() const {return ChangedID;}
u32 ChangedID;
// hardware mapping hint
E_HARDWARE_MAPPING MappingHint;
video::SMaterial Material; //! material of this meshBuffer
core::array<video::S3DVertex> *Vertices;//! Shared Array of vertices
core::array<u16> Indices; //! Array of Indices
......
......@@ -19,7 +19,7 @@ namespace scene
//! S3DVertex2TCoords, S3DVertex and S3DVertexTangents at runtime
struct SSkinMeshBuffer : public IMeshBuffer
{
SSkinMeshBuffer(video::E_VERTEX_TYPE vt=video::EVT_STANDARD) : VertexType(vt)
SSkinMeshBuffer(video::E_VERTEX_TYPE vt=video::EVT_STANDARD) : ChangedID(1),MappingHint(EHM_NEVER),VertexType(vt)
{
#ifdef _DEBUG
setDebugName("SSkinMeshBuffer");
......@@ -257,6 +257,9 @@ struct SSkinMeshBuffer : public IMeshBuffer
}
}
//! append the vertices and indices to the current buffer
virtual void append(const void* const vertices, u32 numVertices, const u16* const indices, u32 numIndices) {}
......@@ -264,6 +267,31 @@ struct SSkinMeshBuffer : public IMeshBuffer
virtual void append(const IMeshBuffer* const other) {}
//! get the current hardware mapping hint
virtual const E_HARDWARE_MAPPING getHardwareMappingHint() const
{
return MappingHint;
}
//! set the hardware mapping hint, for driver
virtual void setHardwareMappingHint( E_HARDWARE_MAPPING NewMappingHint )
{
MappingHint=NewMappingHint;
}
//! flags the mesh as changed, reloads hardware buffers
virtual void setDirty() {ChangedID++;}
virtual const u32 getChangedID() const {return ChangedID;}
u32 ChangedID;
// hardware mapping hint
E_HARDWARE_MAPPING MappingHint;
//ISkinnedMesh::SJoint *AttachedJoint;
core::matrix4 Transformation;
......
......@@ -214,6 +214,7 @@ bool CNullDriver::beginScene(bool backBuffer, bool zBuffer, SColor color)
bool CNullDriver::endScene( s32 windowId, core::rect<s32>* sourceRect )
{
FPSCounter.registerFrame(os::Timer::getRealTime(), PrimitivesDrawn);
updateAllHardwareBuffers();
return true;
}
......@@ -1246,10 +1247,84 @@ void CNullDriver::drawMeshBuffer(const scene::IMeshBuffer* mb)
if (!mb)
return;
//IVertexBuffer and IIndexBuffer later
SHWBufferLink *HWBuffer=getBufferLink(mb);
if (HWBuffer)
drawHardwareBuffer(HWBuffer);
else
drawVertexPrimitiveList(mb->getVertices(), mb->getVertexCount(), mb->getIndices(), mb->getIndexCount()/3, mb->getVertexType(), scene::EPT_TRIANGLES);
}
CNullDriver::SHWBufferLink *CNullDriver::getBufferLink(const scene::IMeshBuffer* mb)
{
if (!isHardwareBufferRecommend(mb))
return 0;
/*
u32 startIndex=0;
for (u32 n=0;n<HWBufferLinks.size();n+=30)
{
if (HWBufferLinks[n]->MeshBuffer < mb)
startIndex=n;
}
*/
//search for hardware links
for (u32 n=0;n<HWBufferLinks.size();++n)
{
SHWBufferLink *Link=HWBufferLinks[n];
if (Link->MeshBuffer==mb)
{
((scene::IMeshBuffer*)mb)->HardwareHint=n;
return Link;
}
}
return createHardwareBuffer(mb); //no hardware links, and mesh wants one, create it
}
//! Update all hardware buffers, remove unused ones
void CNullDriver::updateAllHardwareBuffers()
{
for (u32 n=0;n<HWBufferLinks.size();++n)
{
SHWBufferLink *Link=HWBufferLinks[n];
Link->LastUsed++;
if (Link->LastUsed>1000)
{
deleteHardwareBuffer(Link);
delete Link;
HWBufferLinks.erase(n);
}
}
}
//! Remove all hardware buffers
void CNullDriver::removeAllHardwareBuffers()
{
for (u32 n=0;n<HWBufferLinks.size();++n)
{
deleteHardwareBuffer(HWBufferLinks[n]);
delete HWBufferLinks[n];
}
HWBufferLinks.clear();
}
bool CNullDriver::isHardwareBufferRecommend(const scene::IMeshBuffer* mb)
{
if (mb->getHardwareMappingHint()==scene::EHM_NEVER)
return false;
if (mb->getVertexCount()<500) //todo: tweak and make user definable
return false;
return true;
}
//! Only used by the internal engine. Used to notify the driver that
//! the window was resized.
......
......@@ -30,6 +30,8 @@ namespace video
class IImageLoader;
class IImageWriter;
class CNullDriver : public IVideoDriver, public IGPUProgrammingServices
{
public:
......@@ -288,6 +290,52 @@ namespace video
//! Draws a mesh buffer
virtual void drawMeshBuffer(const scene::IMeshBuffer* mb);
struct SHWBufferLink
{
SHWBufferLink(const scene::IMeshBuffer *_MeshBuffer):MeshBuffer(_MeshBuffer),ChangedID(0),LastUsed(0),Mapped(scene::EHM_NEVER)
{
if (MeshBuffer)
MeshBuffer->grab();
}
virtual ~SHWBufferLink()
{
if (MeshBuffer)
MeshBuffer->drop();
}
const scene::IMeshBuffer *MeshBuffer;
u32 ChangedID;
u32 LastUsed;
scene::E_HARDWARE_MAPPING Mapped;
};
//! Gets hardware buffer link from a meshbuffer (may create or update buffer)
virtual SHWBufferLink *getBufferLink(const scene::IMeshBuffer* mb);
//! updates hardware buffer if needed (only some drivers can)
virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer) {}
//! Create hardware buffer from mesh (only some drivers can)
virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer* mb) {return 0;}
//! Draw hardware buffer (only some drivers can)
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer) {}
//! Delete hardware buffer (only some drivers can)
virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer) {}
//! Update all hardware buffers, remove unused ones
virtual void updateAllHardwareBuffers();
//! Remove all hardware buffers
virtual void removeAllHardwareBuffers();
//! is vbo recommended on this mesh?
virtual bool isHardwareBufferRecommend(const scene::IMeshBuffer* mb);
//! Only used by the internal engine. Used to notify the driver that
//! the window was resized.
virtual void OnResize(const core::dimension2d<s32>& size);
......@@ -495,12 +543,17 @@ namespace video
core::dimension2d<s32> size;
};
core::array<SSurface> Textures;
core::array<video::IImageLoader*> SurfaceLoader;
core::array<video::IImageWriter*> SurfaceWriter;
core::array<SLight> Lights;
core::array<SMaterialRenderer> MaterialRenderers;
core::array<SHWBufferLink*> HWBufferLinks;
io::IFileSystem* FileSystem;
core::rect<s32> ViewPort;
......
......@@ -506,6 +506,225 @@ void COpenGLDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matri
}
}
bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer)
{
if (!HWBuffer)
return false;
if (!VertexBufferObjectExtension)
return false;
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void* vertices=mb->getVertices();
u32 vertexCount=mb->getVertexCount();
E_VERTEX_TYPE vType=mb->getVertexType();
u32 vertexSize = getVertexPitchFromType(vType);
//buffer vertex data, and convert colours...
core::array<c8> buffer(vertexSize * vertexCount);
memcpy(buffer.pointer(), vertices, vertexSize * vertexCount);
// in order to convert the colours into opengl format (RGBA)
switch (vType)
{
case EVT_STANDARD:
{
const S3DVertex* pb = reinterpret_cast<const S3DVertex*>(buffer.pointer());
const S3DVertex* po = reinterpret_cast<const S3DVertex*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
case EVT_2TCOORDS:
{
const S3DVertex2TCoords* pb = reinterpret_cast<const S3DVertex2TCoords*>(buffer.pointer());
const S3DVertex2TCoords* po = reinterpret_cast<const S3DVertex2TCoords*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
case EVT_TANGENTS:
{
const S3DVertexTangents* pb = reinterpret_cast<const S3DVertexTangents*>(buffer.pointer());
const S3DVertexTangents* po = reinterpret_cast<const S3DVertexTangents*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
default:
{
return false;
}
}
//get or create buffer
bool newBuffer=false;
if (!HWBuffer->vbo_verticesID)
{
extGlGenBuffers(1, &HWBuffer->vbo_verticesID);
newBuffer=true;
}
extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID );
//copy data to graphics card
if (!newBuffer)
extGlBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, buffer.const_pointer());
else
{
if (HWBuffer->Mapped==scene::EHM_STATIC)
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STATIC_DRAW);
else if (HWBuffer->Mapped==scene::EHM_DYNAMIC)
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_DYNAMIC_DRAW);
else //scene::EHM_STREAM
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STREAM_DRAW);
}
extGlBindBuffer(GL_ARRAY_BUFFER, 0);
return true;
}
bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer)
{
if (!HWBuffer)
return false;
if(!VertexBufferObjectExtension)
return false;
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const u16* indices=mb->getIndices();
u32 indexCount= mb->getIndexCount();
u32 indexSize = 2;
//get or create buffer
bool newBuffer=false;
if (!HWBuffer->vbo_indicesID)
{
extGlGenBuffers(1, &HWBuffer->vbo_indicesID);
newBuffer=true;
}
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);
//copy data to graphics card
if (!newBuffer)
extGlBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices);
else
{
if (HWBuffer->Mapped==scene::EHM_STATIC)
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW);
else if (HWBuffer->Mapped==scene::EHM_DYNAMIC)
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW);
else //scene::EHM_STREAM
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STREAM_DRAW);
}
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return true;
}
//! updates hardware buffer if needed
bool COpenGLDriver::updateHardwareBuffer(SHWBufferLink *HWBuffer)
{
if (!HWBuffer) return false;
if (HWBuffer->ChangedID != HWBuffer->MeshBuffer->getChangedID()
|| !((SHWBufferLink_opengl*)HWBuffer)->vbo_indicesID
|| !((SHWBufferLink_opengl*)HWBuffer)->vbo_verticesID)
{
HWBuffer->ChangedID = HWBuffer->MeshBuffer->getChangedID();
if (!updateVertexHardwareBuffer((SHWBufferLink_opengl*)HWBuffer)) return false;
if (!updateIndexHardwareBuffer((SHWBufferLink_opengl*)HWBuffer)) return false;
}
}
//! Create hardware buffer from mesh
COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IMeshBuffer* mb)
{
if (!mb) return 0;
if (mb->getHardwareMappingHint()==scene::EHM_NEVER) return 0;
SHWBufferLink_opengl *HWBuffer=new SHWBufferLink_opengl(mb);
//add to list, in order of their meshbuffer pointer
u32 n;
for (n=0;n<HWBufferLinks.size();++n)
if (HWBufferLinks[n]->MeshBuffer > HWBuffer->MeshBuffer)
break;
if (n<HWBufferLinks.size())
HWBufferLinks.insert(HWBuffer,n);
else
HWBufferLinks.push_back(HWBuffer);
HWBuffer->ChangedID=HWBuffer->MeshBuffer->getChangedID();
HWBuffer->Mapped=mb->getHardwareMappingHint();
HWBuffer->LastUsed=0;
HWBuffer->vbo_verticesID=0;
HWBuffer->vbo_indicesID=0;
if (!updateHardwareBuffer(HWBuffer))
{
deleteHardwareBuffer(HWBuffer);
HWBufferLinks.erase(n);
delete HWBuffer;
return 0;
}
return HWBuffer;
}
void COpenGLDriver::deleteHardwareBuffer(SHWBufferLink *_HWBuffer)
{
SHWBufferLink_opengl *HWBuffer=(SHWBufferLink_opengl*)_HWBuffer;
if (HWBuffer->vbo_verticesID)
{
extGlDeleteBuffers(1, &HWBuffer->vbo_verticesID);
HWBuffer->vbo_verticesID=0;
}
if (HWBuffer->vbo_indicesID)
{
extGlDeleteBuffers(1, &HWBuffer->vbo_indicesID);
HWBuffer->vbo_indicesID=0;
}
}
//! Draw hardware buffer
void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer)
{
if (!_HWBuffer) return;
SHWBufferLink_opengl *HWBuffer=(SHWBufferLink_opengl*)_HWBuffer;
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
updateHardwareBuffer(HWBuffer); //check if update is needed
HWBuffer->LastUsed=0;//reset count
extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID);
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);
drawVertexPrimitiveList(0, mb->getVertexCount(), 0, mb->getIndexCount()/3, mb->getVertexType(), scene::EPT_TRIANGLES);
extGlBindBuffer(GL_ARRAY_BUFFER, 0);
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
//! draws a vertex primitive list
......@@ -519,6 +738,8 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType);
if (vertices)
{
// convert colors to gl color format.
vertexCount *= 4; //reused as color component count
ColorBuffer.set_used(vertexCount);
......@@ -557,9 +778,9 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
}
break;
}
}
// draw everything
setRenderStates3DMode();
if (MultiTextureExtension)
......@@ -572,13 +793,17 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
if ((pType!=scene::EPT_POINTS) && (pType!=scene::EPT_POINT_SPRITES))
glEnableClientState(GL_NORMAL_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]);
if (vertices) glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]);
switch (vType)
{
case EVT_STANDARD:
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), &(reinterpret_cast<const S3DVertex*>(vertices))[0].Pos);
glNormalPointer(GL_FLOAT, sizeof(S3DVertex), &(reinterpret_cast<const S3DVertex*>(vertices))[0].Normal);
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(reinterpret_cast<const S3DVertex*>(vertices))[0].TCoords);
if (!vertices) glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(reinterpret_cast<const S3DVertex*>(vertices))[0].Color);
if (MultiTextureExtension && CurrentTexture[1])
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
......@@ -591,6 +816,9 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), &(reinterpret_cast<const S3DVertex2TCoords*>(vertices))[0].Normal);
// texture coordinates
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(reinterpret_cast<const S3DVertex2TCoords*>(vertices))[0].TCoords);
if (!vertices) glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(reinterpret_cast<const S3DVertex2TCoords*>(vertices))[0].Color);
if (MultiTextureExtension)
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
......@@ -603,6 +831,9 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), &(reinterpret_cast<const S3DVertexTangents*>(vertices))[0].Normal);
// texture coordinates
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), &(reinterpret_cast<const S3DVertexTangents*>(vertices))[0].TCoords);
if (!vertices) glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(reinterpret_cast<const S3DVertexTangents*>(vertices))[0].Color);
if (MultiTextureExtension)
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
......@@ -695,6 +926,7 @@ void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCoun
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
......
......@@ -106,6 +106,30 @@ namespace video
//! sets transformation
virtual void setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat);
struct SHWBufferLink_opengl : public SHWBufferLink
{
SHWBufferLink_opengl(const scene::IMeshBuffer *_MeshBuffer): SHWBufferLink(_MeshBuffer), vbo_verticesID(0),vbo_indicesID(0){}
GLuint vbo_verticesID; //tmp
GLuint vbo_indicesID; //tmp
};
bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer);
bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer);
//! updates hardware buffer if needed
virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer);
//! Create hardware buffer from mesh
virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer* mb);
//! Delete hardware buffer (only some drivers can)
virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer);
//! Draw hardware buffer
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer);
//! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount, const u16* indexList, u32 primitiveCount, E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType);
......@@ -324,6 +348,10 @@ namespace video
void createMaterialRenderers();
core::stringw Name;
core::matrix4 Matrices[ETS_COUNT];
core::array<u8> ColorBuffer;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment