Page 2 of 10

Posted: Sun May 09, 2010 7:04 pm
by randomMesh
Sudi wrote:well but still the accessAttribute function shouldn't be const.
Why not? It doesn't change the state of the object. Data is already allocated.

But you're right, a cleaner solution would be to have a separate readAttribute() and writeAttribute().

Posted: Sun May 09, 2010 10:27 pm
by Nadro
In my first post I put the latest code with support types and Your suggestions about separate read and write operations. What do You think about current code? I also wait for dev team comment.

We can also add next constructor, when we will put pointer do data created before simple vertex (this avoid situation with 3x 'new' per vertex, by 3x 'new[]' per mesh buffer; for it we will destroy vertex data inside mesh buffer destructor).

Posted: Mon May 10, 2010 12:50 am
by BlindSide
I like this, vertex attributes are the way to go.

I wonder if we can provide a cleaner interface using something like:

Code: Select all


IVertexAttributeBuffer
{
public:
    virtual void* getData();
    virtual E_DATE_TYPE getDataType();
    virtual u32 getDataWidth();
}

SMeshBuffer
{
private:
    core::array<IVertexAttributeBuffer*> Buffers;
};

template <class T>
SVertexAttributeBuffer
{
    core::array<T> Data;
}
Something like that to have less void*? I don't like having too much void* in interfaces...

The actual implementation looks nice though.

Posted: Mon May 10, 2010 1:14 pm
by 3DModelerMan
Is this flexible vertex format for adding vertex data into shaders? Like adding another position for hardware animation morphing?

Posted: Mon May 10, 2010 2:39 pm
by shadowslair
3DModelerMan wrote:Is this flexible vertex format for adding vertex data into shaders? Like adding another position for hardware animation morphing?
It should be used for whatever you may need. That`s why it`s called "flexible"... :P

Posted: Tue May 11, 2010 3:47 am
by Nadro
OK, I prepared code more similar to BlindSide idea.

Code: Select all

#include <iostream>
#include "irrlicht.h"

// Attribute Types
enum E_ATTRIBURE_TYPE
{
    EAT_POSITION = 0,
    EAT_BLEND_WEIGHT,
    EAT_BLEND_INDICES,
    EAT_NORMAL,
    EAT_TEXCOORD0,
    EAT_TEXCOORD1,
    EAT_TEXCOORD2,
    EAT_TEXCOORD3,
    EAT_TEXCOORD4,
    EAT_TEXCOORD5,
    EAT_TEXCOORD6,
    EAT_TEXCOORD7,
    EAT_TANGENT,
    EAT_BINORMAL,
    EAT_COLOR,
    EAT_XYZW,
    EAT_XYZ,
    EAT_XY,
    EAT_X
};

class IVertexAttributeBuffer
{
public:
    virtual void addAttribute(const void* vAttribute) = 0;
    virtual bool getAttribute(void* vAttribute, const irr::u32 vID) const = 0;
    virtual bool removeAttribute(const irr::u32 vID) = 0;
    virtual bool replaceAttribute(const void* vAttribute, const irr::u32 vID) = 0;
    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)
    {
        AttributeSize = sizeof(T);
    }

    ~SVertexAttributeBuffer()
    {
        Data.clear();
    }

    void addAttribute(const void* vAttribute)
    {
        T _Attribute;
        memcpy(&_Attribute, vAttribute, AttributeSize);

        Data.push_back(_Attribute);
    }

    bool getAttribute(void* vAttribute, const irr::u32 vID) const
    {
        // Check for avaiable index.
        if(vID >= Data.size())
            return false;

        memcpy(vAttribute, &Data[vID], AttributeSize);

        return true;
    }

    bool removeAttribute(const irr::u32 vID)
    {
        // Check for avaiable index.
        if(vID >= Data.size())
            return false;

        Data.erase(vID);

        return true;
    }

    bool replaceAttribute(const void* vAttribute, const irr::u32 vID)
    {
        // Check for avaiable index.
        if(!vAttribute || vID >= Data.size())
            return false;

        T _Attribute;
        memcpy(&_Attribute, vAttribute, AttributeSize);

        Data[vID] = _Attribute;

        return true;
    }

    irr::u32 getAttributesCount() const
    {
        return Data.size();
    }

    irr::u32 getAttributeSize() const
    {
        return AttributeSize;
    }

    E_ATTRIBURE_TYPE getAttributeType() const
    {
        return AttributeType;
    }

    const void* getData() const
    {
        return Data.const_pointer();
    }

private:
    irr::u32 AttributeSize;
    E_ATTRIBURE_TYPE AttributeType;

    irr::core::array<T> Data;
};

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.
    IVertexAttributeBuffer* VAB_P = new SVertexAttributeBuffer<irr::core::vector3df>(EAT_POSITION);
    IVertexAttributeBuffer* VAB_N = new SVertexAttributeBuffer<irr::core::vector3df>(EAT_NORMAL);
    IVertexAttributeBuffer* VAB_C = new SVertexAttributeBuffer<irr::video::SColor>(EAT_COLOR);

    // 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)
    {
        VAB_P->getAttribute(&PositionR[i], i);
        VAB_N->getAttribute(&NormalR[i], i);
        VAB_C->getAttribute(&ColorR[i], 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;
}
As You see in this solution we haven't good vertex structure, it's replaced by IVertexAttributeBuffer packs (we'll apply these packs to SMeshBuffer). Which system is better (with vertex structure or with ivertexattributebuffer) in Your opinion?

BTW. Code is not fully tested, but I'm tired, so I can optionally improved it tomorrow.

Posted: Tue May 11, 2010 8:20 am
by hybrid
AFAIK, the combined vertex structures are the preferred ones for efficient GPU handling. And at least a few common vertex structures are supported by all drivers and versions currently out there. I don't know if the separated stuctures are necessary for the new DX10/GL4 driver versions. But I'd say we need at least the combined versions, if not both.
I'd also prefer reinterpret_cast over memcpy. That would also avoid the additional member in each method.
Also, dynamic allocation per vertex seems overkill, we need a template based static version to get a really good vertex format. I'm not yet sure how to achieve this (otherwise Irrlicht might already have the flexible vertex format). But I also don't know, yet, which features should be implemented in this technique. Which types should be supported, are positions of the elements arbitrary, how many types could be part of a vertex, and how many elements, are multiple types and semantics of the same type allowed, ...

Posted: Tue May 11, 2010 1:19 pm
by 3DModelerMan
I don't know if the separated stuctures are necessary for the new DX10/GL4 driver versions. But I'd say we need at least the combined versions, if not both.
We're getting DX10 drivers?

Posted: Tue May 11, 2010 1:20 pm
by Nadro
Here is improved code:

Code: Select all

#include <iostream>
#include "irrlicht.h"

// Attribute Types
enum E_ATTRIBURE_TYPE
{
    EAT_POSITION = 0,
    EAT_BLEND_WEIGHT,
    EAT_BLEND_INDICES,
    EAT_NORMAL,
    EAT_TEXCOORD0,
    EAT_TEXCOORD1,
    EAT_TEXCOORD2,
    EAT_TEXCOORD3,
    EAT_TEXCOORD4,
    EAT_TEXCOORD5,
    EAT_TEXCOORD6,
    EAT_TEXCOORD7,
    EAT_TANGENT,
    EAT_BINORMAL,
    EAT_COLOR,
    EAT_XYZW,
    EAT_XYZ,
    EAT_XY,
    EAT_X
};

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;
}
I remove unnecessary stuff from IVertexAttributeBuffer, so I can replace void* by template type in SVertexAttributeBuffer. For OpenGL (current irr, 3.x, 4.x) we can use the same structure, I think that for D3D9,10,11 is similar, but I'm not 100% sure. This code have 1 known issue. Vertices data is stored on different arrays, so for each attribute we need different VBO and this is of course unacceptable solution. But if we move all data on one array adding new attributes will be unsafe, we will must add data in the same scheme eg:
add position, add normal, add other... // OK
add position, add normal, add other... // OK
add normal, add position, add other... // BAD
add position, add position... // BAD
So this isn't also the best solution... Some ideas? It looks like UnifiedVertex is better solution than IVertexAttributeBuffer.

Posted: Tue May 11, 2010 4:08 pm
by hybrid
Which one is IFlexibleVertex? My vote in the previous thread was for UnifiedVertex, at least from the basic concept. However, the attribute thing might come in handy for feedback buffers and a generalized buffer concept. So, as said, we might need both in the end, but this is pure speculation.

Posted: Tue May 11, 2010 5:10 pm
by Nadro
Yep, should be UnifiedVertex instead of IFlexibleVertex :P

Posted: Wed May 12, 2010 2:00 am
by BlindSide
so for each attribute we need different VBO and this is of course unacceptable solution
Why is this unacceptable? As I understand this way the user can choose to upload only certain attributes to the GPU (E.g. keep static X, Z position but update only Y position VBO).

Posted: Wed May 12, 2010 9:17 am
by hybrid
Yes, that's why I mentioned the possibility for both approaches. However, the separate VBO construction would be needed for every combination of data, so even for the usual position/normal/texcoord setup you'd need multiple VBOs. Or at least non-unified access, which makes this approach definitely slower than the combined vertex version.

Posted: Wed May 12, 2010 1:05 pm
by Nadro
Maybe unacceptable is too hard word, but many too small VBO's isn't good for performance. User can set flag per attribute so he can upload all position components or none, but this is nice when we need set only position as stream, but others as static.

Posted: Wed May 12, 2010 9:17 pm
by Nadro
Here in another solution:

Code: Select all

#include <iostream>
#include "irrlicht.h"

// Attribute Types.
enum E_ATTRIBURE_TYPE
{
    EAT_POSITION = 0, //vector3df
    EAT_BLEND_WEIGHT, // f32[4]
    EAT_BLEND_INDICES, // f32[4]
    EAT_NORMAL, //vector3df
    EAT_TEXCOORD0, //vector2df
    EAT_TEXCOORD1, //vector2df
    EAT_TEXCOORD2, //vector2df
    EAT_TEXCOORD3, //vector2df
    EAT_TEXCOORD4, //vector2df
    EAT_TEXCOORD5, //vector2df
    EAT_TEXCOORD6, //vector2df
    EAT_TEXCOORD7, //vector2df
    EAT_TANGENT, //vector3df
    EAT_BINORMAL, //vector3df
    EAT_COLOR, //SColor
    EAT_XYZWI, // u32[4]
    EAT_XYZI, // u32[3]
    EAT_XYI, // u32[2]
    EAT_XI, // u32
    EAT_XYZWF, // f32[4]
    EAT_XYZF, // f32[3]
    EAT_XYF, // f32[2]
    EAT_XF, // f32
    EAT_UNDECLARED // s32, 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_XYZWI = 0, // int, 4 x 4 bytes
    EAOT_XYZI, // int, 3 x 4 bytes
    EAOT_XYI, // int, 2 x 4 bytes
    EAOT_XI, // int, 1 x 4 bytes
    EAOT_XYZWF, // float, 4 x 4 bytes
    EAOT_XYZF, // float, 3 x 4 bytes
    EAOT_XYF, // float, 2 x 4 bytes
    EAOT_XF, // float, 1 x 4 bytes
    EAOT_XU, // unsigned, 1 x 4 bytes for SColor
    EAOT_UNDECLARED // s32, it tell our that with vertex attribute it's something wrong.
};

// Flexible Vertex class. We store all vertices inside the mesh buffer in this way.
class IFlexibleVertex
{
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 irr::u32 vID) const = 0;
};

// Our standard vertex type. Other types we will create in this way.
class SVertex : public IFlexibleVertex
{
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_UNDECLARED;

        switch(vID)
        {
        case 0:
            _Type = EAOT_XYZF;
            break;
        case 1:
            _Type = EAOT_XYZF;
            break;
        case 2:
            _Type = EAOT_XU;
            break;
        default:
            _Type = EAOT_UNDECLARED;
            break;
        }

        return _Type;
    }

    irr::u32 getAttributesCount(const irr::u32 vID) 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;
}
In this solution create new vertex type need some more programmer work (see SVertex class), but memory mangament is very similar to current Irrlicht system. Inside engine we will use IFlexibleMeshBuffer and IFlexibleVertex and at loader stage predefined S3DVertex or others. I think that this solution is very similar to our current vertex, but programmer can add Your own vertex types in easy way.

I showed implementation for 3 different solution about flexible vertex problem. Now we must compare each and select the best.

We need talk about E_ATTRIBURE_TYPE and E_ATTRIBURE_OFFSET_TYPE members (these enums will be usefull also for previous systems).