Nadro wrote:In this topic we talk about Flexible Vertex Format for Irrlicht. Currently we have a following solutions for this problem:
------------------------------------------------------------------------------------
Unified Vertex solution
-require many 'new' calls
Code: Select all
#include <iostream>
#include "irrlicht.h"
// Unified Vertex class.
class UnifiedVertex
{
public:
// Attribute Types.
enum E_ATTRIBURE_TYPE
{
EAT_POSITION = 0,
EAT_NORMAL,
EAT_TEXCOORD,
EAT_COLOR,
EAT_TANGENT,
EAT_BINORMAL,
EAT_BLEND_WEIGHT,
EAT_BLEND_INDICES,
EAT_XYZW,
EAT_XYZ,
EAT_XY,
EAT_X,
EAT_UNDECLARED // it tell our that with vertex attribute it's something wrong.
};
// Constructor. (Safely method)
UnifiedVertex(const irr::core::array<irr::u32>& vOffset, const irr::core::array<E_ATTRIBURE_TYPE>& vType) :
AttributesCount(vOffset.size())
{
irr::u32 _Size = 0;
// Allocate offsets and indices array.
Offset = new irr::u32[AttributesCount];
Type = new irr::u8[AttributesCount];
for(irr::u32 i = 0; i < AttributesCount; ++i)
{
Offset[i] = vOffset[i];
_Size += Offset[i];
if(i < vType.size())
Type[i] = (irr::u8)vType[i];
else
Type[i] = (irr::u8)EAT_UNDECLARED;
}
// Allocate memory for data.
Data = new irr::u8[_Size];
}
// Constructor.
UnifiedVertex(const irr::u32* vOffset, const E_ATTRIBURE_TYPE* vType, const irr::u32 AttributeCount) :
AttributesCount(AttributeCount)
{
irr::u32 _Size = 0;
// Allocate offsets and indices array.
Offset = new irr::u32[AttributesCount];
Type = new irr::u8[AttributesCount];
for(irr::u32 i = 0; i < AttributesCount; ++i)
{
Offset[i] = vOffset[i];
_Size += Offset[i];
Type[i] = (irr::u8)vType[i];
}
// Allocate memory for data.
Data = new irr::u8[_Size];
}
// Destructor.
~UnifiedVertex()
{
// Release allocated memory.
delete[] Offset;
delete[] Type;
delete[] Data;
}
// Read attribute data.
bool readAttribute(void* vData, const irr::u32 vPosition) const
{
// Check avaiable offsets.
if(vPosition >= AttributesCount || !vData)
return false;
irr::u32 _Size = 0;
for(irr::u32 i = 0; i < vPosition; ++i)
_Size += Offset[i];
// Read data.
memcpy(vData, Data + _Size, Offset[vPosition]);
return true;
}
// Write attribute data.
bool writeAttribute(const void* vData, const irr::u32 vPosition)
{
// Check avaiable offsets.
if(vPosition >= AttributesCount || !vData)
return false;
irr::u32 _Size = 0;
for(irr::u32 i = 0; i < vPosition; ++i)
_Size += Offset[i];
// Write data.
memcpy(Data + _Size, vData, Offset[vPosition]);
return true;
}
// Get selected attribute offset.
irr::u32 getAttributeOffset(const irr::u32 vPosition) const
{
irr::u32 _Offset = 0;
if(vPosition < AttributesCount)
_Offset = Offset[vPosition];
return _Offset;
}
// Get offset index by type.
irr::s32 getOffsetIndex(const E_ATTRIBURE_TYPE vType) const
{
irr::s32 _OffsetIndex = -1;
// get index.
if(vType != EAT_UNDECLARED)
for(irr::u32 i = 0; i < AttributesCount; ++i)
if((irr::u32)Type[i] == (irr::u32)vType)
_OffsetIndex = i;
return _OffsetIndex;
}
// Get attributes count.
irr::u32 getAttributesCount() const { return AttributesCount; }
// Get data.
irr::u8* getData() const { return Data; }
private:
// Array with offsets sizes.
irr::u32* Offset;
// Array with type index.
irr::u8* Type;
// Attributes count.
irr::u32 AttributesCount;
// Data.
irr::u8* Data;
};
int main()
{
// Example attributes.
irr::core::vector3df Position0(10.1,11.1,12.1);
irr::core::vector3df Normal0(20.1,21.1,22.1);
irr::video::SColor Color0(10,11,12,13);
// Prepare vertex structure.
irr::core::array<irr::u32> AttributeOffset;
AttributeOffset.push_back(sizeof(irr::core::vector3df));
AttributeOffset.push_back(sizeof(irr::core::vector3df));
AttributeOffset.push_back(sizeof(irr::video::SColor));
irr::core::array<UnifiedVertex::E_ATTRIBURE_TYPE> AttributeType;
AttributeType.push_back(UnifiedVertex::EAT_POSITION);
AttributeType.push_back(UnifiedVertex::EAT_NORMAL);
AttributeType.push_back(UnifiedVertex::EAT_COLOR);
UnifiedVertex* UV = new UnifiedVertex(AttributeOffset, AttributeType);
// Alternative method.
/*
irr::u32 AttributeOffset[3] =
{
sizeof(irr::core::vector3df),
sizeof(irr::core::vector3df),
sizeof(irr::video::SColor)
};
UnifiedVertex::E_ATTRIBURE_TYPE AttributeType[3] =
{
UnifiedVertex::EAT_POSITION,
UnifiedVertex::EAT_NORMAL,
UnifiedVertex::EAT_COLOR
};
UnifiedVertex* UV = new UnifiedVertex(AttributeOffset, AttributeType, 3);
*/
// Attributes indices.
irr::u32 PositionID = 0;
irr::u32 NormalID = 1;
irr::u32 ColorID = 2;
// When we don't know offsets indices.
/*
PositionID = UV->getOffsetIndex(UnifiedVertex::EAT_POSITION);
NormalID = UV->getOffsetIndex(UnifiedVertex::EAT_NORMAL);
ColorID = UV->getOffsetIndex(UnifiedVertex::EAT_COLOR);
*/
// Write attibutes values.
UV->writeAttribute(&Position0, PositionID);
UV->writeAttribute(&Normal0, NormalID);
UV->writeAttribute(&Color0, ColorID);
// Read attributes values.
irr::core::vector3df PositionR(0,0,0);
irr::core::vector3df NormalR(0,0,0);
irr::video::SColor ColorR(0,0,0,0);
UV->readAttribute(&PositionR, PositionID);
UV->readAttribute(&NormalR, NormalID);
UV->readAttribute(&ColorR, ColorID);
// Display attributes values.
std::cout <<
"Position: " << PositionR.X << ", " << PositionR.Y << ", " << PositionR.Z << "\n" <<
"Normal: " << NormalR.X << ", " << NormalR.Y << ", " << NormalR.Z << "\n" <<
"Color: " << ColorR.getAlpha() << ", " << ColorR.getRed() << ", " << ColorR.getGreen() << ", " << ColorR.getBlue() <<
std::endl;
delete UV;
system("Pause");
return 0;
}
------------------------------------------------------------------------------------
Vertex Attribute Buffer solution
-require independent VBO for each attribute
Code: Select all
#include <iostream>
#include "irrlicht.h"
// Attribute Types.
enum E_ATTRIBURE_TYPE
{
EAT_POSITION = 0,
EAT_NORMAL,
EAT_TEXCOORD,
EAT_COLOR,
EAT_TANGENT,
EAT_BINORMAL,
EAT_BLEND_WEIGHT,
EAT_BLEND_INDICES,
EAT_XYZW,
EAT_XYZ,
EAT_XY,
EAT_X,
EAT_UNDECLARED // it tell our that with vertex attribute it's something wrong.
};
class IVertexAttributeBuffer
{
public:
virtual irr::u32 getAttributesCount() const = 0;
virtual irr::u32 getAttributeSize() const = 0;
virtual E_ATTRIBURE_TYPE getAttributeType() const = 0;
virtual const void* getData() const = 0;
};
template <class T>
class SVertexAttributeBuffer : public IVertexAttributeBuffer
{
public:
SVertexAttributeBuffer(E_ATTRIBURE_TYPE vAttributeType) : AttributeType(vAttributeType)
{
}
~SVertexAttributeBuffer()
{
Data.clear();
}
void addAttribute(T vAttribute)
{
Data.push_back(vAttribute);
}
T getAttribute(const irr::u32 vID) const
{
// Check for avaiable index.
if(vID >= Data.size())
return (T)0;
return Data[vID];
}
bool removeAttribute(const irr::u32 vID)
{
// Check for avaiable index.
if(vID >= Data.size())
return false;
Data.erase(vID);
return true;
}
bool replaceAttribute(T vAttribute, const irr::u32 vID)
{
// Check for avaiable index.
if(!vAttribute || vID >= Data.size())
return false;
Data[vID] = _Attribute;
return true;
}
irr::u32 getAttributesCount() const
{
return Data.size();
}
irr::u32 getAttributeSize() const
{
return sizeof(T);
}
E_ATTRIBURE_TYPE getAttributeType() const
{
return AttributeType;
}
const void* getData() const
{
return Data.const_pointer();
}
private:
E_ATTRIBURE_TYPE AttributeType;
irr::core::array<T> Data;
};
class SFlexibleMesh
{
public:
const void* getVertexAttributeData() const
{
return VertexAttributeBuffer.const_pointer();
}
irr::core::array<IVertexAttributeBuffer*> VertexAttributeBuffer;
};
int main()
{
// Example attributes.
irr::core::vector3df Position[3] =
{
irr::core::vector3df(10.1f,11.1f,12.1f),
irr::core::vector3df(100.1f,110.1f,120.1f),
irr::core::vector3df(1000.1f,1100.1f,1200.1f)
};
irr::core::vector3df Normal[3] =
{
irr::core::vector3df(20.1f,21.1f,22.1f),
irr::core::vector3df(200.1f,210.1f,220.1f),
irr::core::vector3df(2000.1f,2100.1f,2200.1f)
};
irr::video::SColor Color[3] =
{
irr::video::SColor(10,11,12,13),
irr::video::SColor(100,110,120,130),
irr::video::SColor(200,210,220,230)
};
// Prepare vertex attribute buffers.
SVertexAttributeBuffer<irr::core::vector3df>* VAB_P = new SVertexAttributeBuffer<irr::core::vector3df>(EAT_POSITION);
SVertexAttributeBuffer<irr::core::vector3df>* VAB_N = new SVertexAttributeBuffer<irr::core::vector3df>(EAT_NORMAL);
SVertexAttributeBuffer<irr::video::SColor>* VAB_C = new SVertexAttributeBuffer<irr::video::SColor>(EAT_COLOR);
SFlexibleMesh* FlexibleMesh = new SFlexibleMesh();
FlexibleMesh->VertexAttributeBuffer.push_back(VAB_P);
FlexibleMesh->VertexAttributeBuffer.push_back(VAB_N);
FlexibleMesh->VertexAttributeBuffer.push_back(VAB_C);
// Write attributes
for(irr::u32 i = 0; i < 3; ++i)
{
VAB_P->addAttribute(Position[i]);
VAB_N->addAttribute(Normal[i]);
VAB_C->addAttribute(Color[i]);
}
// Read attributes
irr::core::vector3df PositionR[3] =
{
irr::core::vector3df(0.0f,0.0f,0.0f),
irr::core::vector3df(0.0f,0.0f,0.0f),
irr::core::vector3df(0.0f,0.0f,0.0f)
};
irr::core::vector3df NormalR[3] =
{
irr::core::vector3df(0.0f,0.0f,0.0f),
irr::core::vector3df(0.0f,0.0f,0.0f),
irr::core::vector3df(0.0f,0.0f,0.0f)
};
irr::video::SColor ColorR[3] =
{
irr::video::SColor(0,0,0,0),
irr::video::SColor(0,0,0,0),
irr::video::SColor(0,0,0,0)
};
for(irr::u32 i = 0; i < 3; ++i)
{
PositionR[i] = VAB_P->getAttribute(i);
NormalR[i] = VAB_N->getAttribute(i);
ColorR[i] = VAB_C->getAttribute(i);
}
// Display attributes values.
for(irr::u32 i = 0; i < 3; ++i)
{
std::cout << "Position" << i << ": " << PositionR[i].X << ", " << PositionR[i].Y << ", " << PositionR[i].Z << std::endl;
std::cout << "Normal" << i << ": " << PositionR[i].X << ", " << PositionR[i].Y << ", " << PositionR[i].Z << std::endl;
std::cout << "Color" << i << ": " << ColorR[i].getAlpha() << ", " << ColorR[i].getRed() << ", " << ColorR[i].getGreen() << ", " << ColorR[i].getBlue() << std::endl << std::endl;
}
delete VAB_P;
delete VAB_N;
delete VAB_C;
system("Pause");
return 0;
}
------------------------------------------------------------------------------------
Flexible Vertex Descriptor and Mesh Buffer solution
-require more programmer work to define new vertex type
-mesh buffer is integrated with vertex type
Code: Select all
#include <iostream>
#include "irrlicht.h"
// Attribute Types.
enum E_ATTRIBURE_TYPE
{
EAT_POSITION = 0,
EAT_NORMAL,
EAT_TEXCOORD,
EAT_COLOR,
EAT_TANGENT,
EAT_BINORMAL,
EAT_BLEND_WEIGHT,
EAT_BLEND_INDICES,
EAT_XYZW,
EAT_XYZ,
EAT_XY,
EAT_X,
EAT_UNDECLARED // it tell our that with vertex attribute it's something wrong.
};
// Attribute Offset Types (more friendly for switch-case selecting, which we will use eg. for OpenGL VBO's).
enum E_ATTRIBURE_OFFSET_TYPE
{
EAOT_BYTE,
EAOT_UNSIGNED_BYTE,
EAOT_SHORT,
EAOT_UNSIGNED_SHORT,
EAOT_INT,
EAOT_UNSIGNED_INT,
EAOT_FLOAT,
EAOT_DOUBLE
};
// Flexible Vertex Descriptor class.
class IFlexibleVertexDescriptor
{
public:
virtual irr::u32 getAttributeOffset(const irr::u32 vID) const = 0;
virtual E_ATTRIBURE_TYPE getAttributeType(const irr::u32 vID) const = 0;
virtual E_ATTRIBURE_OFFSET_TYPE getAttributeOffsetType(const irr::u32 vID) const = 0;
virtual irr::u32 getAttributesCount() const = 0;
};
// Our standard vertex type. Other types we will create in this way.
class SVertex : public IFlexibleVertexDescriptor
{
public:
SVertex() :
Position(irr::core::vector3df(0.0f, 0.0f, 0.0f)),
Normal(irr::core::vector3df(0.0f, 0.0f, 0.0f)),
Color(irr::video::SColor(0, 0 ,0 ,0))
{
}
SVertex(const irr::core::vector3df& vPosition, const irr::core::vector3df& vNormal, const irr::video::SColor& vColor) :
Position(vPosition), Normal(vNormal), Color(vColor)
{
}
irr::u32 getAttributeOffset(const irr::u32 vID) const
{
irr::u32 _Offset = 0;
switch(vID)
{
case 0:
_Offset = sizeof(irr::core::vector3df);
break;
case 1:
_Offset = sizeof(irr::core::vector3df);
break;
case 2:
_Offset = sizeof(irr::video::SColor);
break;
default:
_Offset = 0;
break;
}
return _Offset;
}
E_ATTRIBURE_TYPE getAttributeType(const irr::u32 vID) const
{
E_ATTRIBURE_TYPE _Type = EAT_UNDECLARED;
switch(vID)
{
case 0:
_Type = EAT_POSITION;
break;
case 1:
_Type = EAT_NORMAL;
break;
case 2:
_Type = EAT_COLOR;
break;
default:
_Type = EAT_UNDECLARED;
break;
}
return _Type;
}
E_ATTRIBURE_OFFSET_TYPE getAttributeOffsetType(const irr::u32 vID) const
{
E_ATTRIBURE_OFFSET_TYPE _Type = EAOT_FLOAT;
switch(vID)
{
/*case 0:
_Type = EAOT_FLOAT;
break;
case 1:
_Type = EAOT_FLOAT;
break;*/
case 2:
_Type = EAOT_UNSIGNED_BYTE;
break;
default:
_Type = EAOT_FLOAT;
break;
}
return _Type;
}
irr::u32 getAttributesCount() const
{
return 3;
}
irr::core::vector3df Position;
irr::core::vector3df Normal;
irr::video::SColor Color;
};
class IFlexibleMeshBuffer
{
public:
virtual void* getArray() = 0;
virtual void* getData() = 0;
virtual irr::u32 getVerticesCount() const = 0;
};
template <class T>
class SFlexibleMeshBuffer : public IFlexibleMeshBuffer
{
public:
~SFlexibleMeshBuffer()
{
Vertices.clear();
}
void* getArray()
{
return &Vertices;
};
void* getData()
{
return Vertices.pointer();
};
irr::u32 getVerticesCount() const
{
return Vertices.size();
}
private:
irr::core::array<T> Vertices;
};
// Write attributes.
void writeAttributes(IFlexibleMeshBuffer* MeshBuffer)
{
irr::core::vector3df Position[3] =
{
irr::core::vector3df(10.1f,11.1f,12.1f),
irr::core::vector3df(100.1f,110.1f,120.1f),
irr::core::vector3df(1000.1f,1100.1f,1200.1f)
};
irr::core::vector3df Normal[3] =
{
irr::core::vector3df(20.1f,21.1f,22.1f),
irr::core::vector3df(200.1f,210.1f,220.1f),
irr::core::vector3df(2000.1f,2100.1f,2200.1f)
};
irr::video::SColor Color[3] =
{
irr::video::SColor(10,11,12,13),
irr::video::SColor(100,110,120,130),
irr::video::SColor(200,210,220,230)
};
// Get array.
irr::core::array<SVertex>* Vertices = (irr::core::array<SVertex>*)MeshBuffer->getArray();
// Add vertex to our array.
for(irr::u32 i = 0; i < 3; ++i)
{
SVertex Vertex(Position[i], Normal[i], Color[i]);
Vertices->push_back(Vertex);
}
}
// Read and display attributes.
void readAndDisplayAttributes(IFlexibleMeshBuffer* MeshBuffer)
{
// Get data.
void* Data = MeshBuffer->getData();
for(irr::u32 i = 0; i < MeshBuffer->getVerticesCount(); ++i)
{
// We can also select properly types thanks to IFlexibleVertex functions, but it will be more costly (switch-case).
irr::core::vector3df Position(0.0f,0.0f,0.0f);
irr::core::vector3df Normal(0.0f,0.0f,0.0f);
irr::video::SColor Color(0,0,0,0);
// Mostly time we needn't acces to simple vertex attribute (for send data to GPU we will use pointer to vertices array with offsets), but we can do it in this way:
SVertex* Vertex = ((SVertex*)Data +i);
Position = Vertex->Position;
Normal = Vertex->Normal;
Color = Vertex->Color;
// Display
std::cout << "Position" << i << ": " << Position.X << ", " << Position.Y << ", " << Position.Z << std::endl;
std::cout << "Normal" << i << ": " << Normal.X << ", " << Normal.Y << ", " << Normal.Z << std::endl;
std::cout << "Color" << i << ": " << Color.getAlpha() << ", " << Color.getRed() << ", " << Color.getGreen() << ", " << Color.getBlue() << std::endl << std::endl;
}
}
int main()
{
// Create Mesh Buffer.
IFlexibleMeshBuffer* MeshBuffer = new SFlexibleMeshBuffer<SVertex>();
// Write attributes.
writeAttributes(MeshBuffer);
// Read and display attributes.
readAndDisplayAttributes(MeshBuffer);
// Delete Mesh Buffer;
delete MeshBuffer;
system("Pause");
return 0;
}
------------------------------------------------------------------------------------
Please comment each solution, we need the best option. Remember that existing codes are in preview version and need some clean up.