Positioning Policy for new nodes

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
primem0ver
Posts: 57
Joined: Sat Oct 11, 2014 11:07 pm

Positioning Policy for new nodes

Post by primem0ver »

I have written some code that I am willing to offer to the engine when an update is in the works. It allows the use of a positioning policy relative to the parent node to calculate the final coordinates of a new node. I haven't debugged it yet but it is fairly straightforward. Here is the basic idea. (I will change the code if necessary later).

Each axis gets assigned a positioning policy using an enum:

Code: Select all

enum PositioningPolicy3D { none, min, max, centered, weighted };
none = relative to 0 (no actual delta)
min = (minimum is used as delta)
max = (maximum used as delta)
centered = (maximum + minimum) / 2 as delta
weighted = sum / count as delta (coordinate values are summed).


The structure used in determining the offset is as follows:

Code: Select all

struct PositioningFlags3D
{
    int xMin : 1;
    int xMax : 1;
    int xWeighted : 1;
    int yMin : 1;
    int yMax : 1;
    int yWeighted : 1;
    int zMin : 1;
    int zMax;
    int zWeighted : 1;
 
    inline PositioningFlags3D(PositioningPolicy3D xPolicy, PositioningPolicy3D yPolicy, PositioningPolicy3D zPolicy);
};
the inline constructor basically does this for each axis (using the x axis as an example):

Code: Select all

switch (xPolicy)
    {
    case PositioningPolicy3D::min:
        xMin = 1;
        xMax = 0;
        xWeighted = 0;
        break;
    case PositioningPolicy3D::max:
        xMin = 0;
        xMax = 1;
        xWeighted = 0;
        break;
    case PositioningPolicy3D::centered:
        xMin = 1;
        xMax = 1;
        xWeighted = 0;
        break;
    case PositioningPolicy3D::weighted:
        xMin = 0;
        xMax = 0;
        xWeighted = 1;
        break;
    default:
        xMin = 0;
        xMax = 0;
        xWeighted = 0;
    }

In my own code I use the centering policy as default.

Code: Select all

PositioningFlags3D alignmentPolicy = PositioningFlags3D(PositioningPolicy3D::centered, PositioningPolicy3D::centered, PositioningPolicy3D::centered)
finally... the method that repositions the object based on the policy:

Code: Select all

resposition(vector3df* vertexArray, const int vertexCount, PositioningFlags3D alignmentPolicy)
{
    vector3df* vertex = vertexArray;
 
    float xMin = FLT_MAX;
    float yMin = FLT_MAX;
    float zMin = FLT_MAX;
    float xMax = FLT_MIN;
    float yMax = FLT_MIN;
    float zMax = FLT_MIN;
 
    int count;
    float xSum = 0.0f;
    float ySum = 0.0f;
    float zSum = 0.0f;
 
    // get positioning data
    for (int i = 0; i < vertexCount; ++i)
    {
        if (vertex->X > xMax)
            xMax = vertex->X;
        if (vertex->X < xMin)
            xMin = vertex->X;
        if (vertex->Y > yMax)
            yMax = vertex->Y;
        if (vertex->Y < yMin)
            yMin = vertex->Y;
        if (vertex->Z > zMax)
            zMax = vertex->Z;
        if (vertex->Z < zMin)
            zMin = vertex->Z;
 
        xSum += vertex->X;
        ySum += vertex->Y;
        zSum += vertex->Z;
 
        vertex += sizeof(vector3df);
    }
 
    // determine offset based on the alignment policy and positioning data
    vector3df offset;
    if (alignmentPolicy.xWeighted)
        offset.X = xSum / (float)vertexCount;
    else
    {
        if (alignmentPolicy.xMax & alignmentPolicy.xMin)
            offset.X = (xMax + xMin) / 2.0f;
        else if (alignmentPolicy.xMax)
            offset.X = xMax;
        else if (alignmentPolicy.xMin)
            offset.X = xMin;
        else
            offset.X = 0.0f;
    }
 
    if (alignmentPolicy.yWeighted)
        offset.Y = ySum / (float)vertexCount;
    else
    {
        if (alignmentPolicy.yMax & alignmentPolicy.yMin)
            offset.Y = (yMax + yMin) / 2.0f;
        else if (alignmentPolicy.xMax)
            offset.Y = yMax;
        else if (alignmentPolicy.xMin)
            offset.Y = yMin;
        else
            offset.Y = 0.0f;
    }
 
    if (alignmentPolicy.zWeighted)
        offset.Z = zSum / (float)vertexCount;
    else
    {
        if (alignmentPolicy.zMax & alignmentPolicy.zMin)
            offset.Z = (zMax + zMin) / 2.0f;
        else if (alignmentPolicy.xMax)
            offset.Z = zMax;
        else if (alignmentPolicy.xMin)
            offset.Z = zMin;
        else
            offset.Z = 0.0f;
    }
 
    // reposition vertices
    for (int i = 0; i < vertexCount; ++i)
        vertexArray[i] -= offset;
}
 

EDIT/NOTE: In my code, the vertex array and the way those vertices are used depends on the object being created. I basically wrote this code as part of a generic set of 3D shapes. My algorithm first calculates one of each vertex in the mesh before creating the mesh vertices since points will be duplicated in order to allow for multiple textures to be used on each face of certain meshes. That is why I am sending an array of coordinates rather than an array of vertices.
Last edited by primem0ver on Sat Nov 22, 2014 5:54 am, edited 1 time in total.
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: Positioning Policy for new nodes

Post by Seven »

can you give an example of when this code would be helpful?
trying to understand.......
primem0ver
Posts: 57
Joined: Sat Oct 11, 2014 11:07 pm

Re: Positioning Policy for new nodes

Post by primem0ver »

Sure I can do that. Currently I am building my own engine on top of the Irrlicht engine which requires more basic shape 3D meshes and more versatility with current ones (cube and sphere) and it may be a bit of time before I can go more in depth. However... I CAN provide a basic example using the current code I am working on.

First though: The whole point of this positioning policy is to use it in the creation of new nodes in a scene to dictate how the node should be positioned relative to the parent. Currently as I understand it there is no functionality for this and nodes are simply positioned relative to 0,0,0 when their position relative to the parent node is calculated. (For example, if I want my node to be placed at 5,5,5 relative to its parent, the coordinates 0,0,0 is assigned a relative position of 5,5,5). But what if I want the center of the object placed at 5,5,5 instead? Or the top of the object (y maximum) placed at 5,5,5? These structures allow for the calculation of the required offset during the creation of the new node. This can be done with any mesh. I will provide an example of how I achieve this using code from my own project.

Some background regarding my project so that you understand the code:
The engine I am designing requires the ability to create a large variety of meshes including basic shapes, files, and eventually proprietary coded methods (such as an ISEA grid). It will also offer the ability to dynamically assign different materials and textures to multiple faces on these objects depending on needs. As a result, the native sphere and cube meshes are insufficient. Hence the classes from which I am pulling this example.

Here is my basic "cube" class definition for cubes that require more than one texture coordinate per vertex. It ultimately inherits from ISceneNode and is formatted for easy reading. Please pardon any "beginner" mistakes in using the Irrlicht library as I am new to Irrlicht.

Code: Select all

class MultiTexturedCube : public MultiTexturedMesh // inherits from _3DMesh which Inherits from VRMesh which inherits from ISceneNode
{
public:
    MultiTexturedCube(
        ObjectInfo* info, 
        ISceneManager* manager, 
        ISceneNode* parent = NULL, 
        int id = -1, 
        const vector3df& position = vector3df(0.0f, 0.0f, 0.0f),  
        PositioningFlags3D alignmentPolicy = PositioningFlags3D(PositioningPolicy3D::centered, PositioningPolicy3D::centered, PositioningPolicy3D::centered));
    virtual ~MultiTexturedCube();
 
private /*methods*/:
    void createCubeVertices(float sidelength, PositioningFlags3D alignmentPolicy);
private:
};
Now for the implementations that we are concerned with for this example:

Code: Select all

MultiTexturedCube::MultiTexturedCube(ObjectInfo* info, ISceneManager* manager, ISceneNode* parent, int id, const vector3df& position, PositioningFlags3D alignmentPolicy) :
    MultiTexturedMesh(manager, parent, id)
{
    mVertexArray = new video::S3DVertex*[6];
    for (int i = 0; i < 6; ++i)
        mVertexArray[i] = new video::S3DVertex[4];
 
    // the ObjectInfo and Dynamic Mesh are proprietary classes containing information read from XML
    DynamicMesh* meshInfo = dynamic_cast<DynamicMesh*>(info->getMesh());
    float sidelength;
 
    if (meshInfo)
    {
        sidelength = meshInfo->getFloatParameter("size");
        if (sidelength == FLT_MAX)
            sidelength = meshInfo->getFloatParameter("length");
        if (sidelength == FLT_MAX)
            sidelength = 1.0f;
    }
    else // create cube of standard 1f size per side
        sidelength = 1.0f;
 
    createCubeVertices(sidelength, alignmentPolicy);
}
 
void MultiTexturedCube::createCubeVertices(float sideLength, PositioningFlags3D alignmentPolicy)
{
    // number of vertices
    const int n = 8;
    
    // first create the vertices
    vector3df v[n] = 
    { 
        vector3df(0.0f, sideLength, 0.0f),
        vector3df(sideLength, sideLength, 0.0f),
        vector3df(sideLength, sideLength, sideLength),
        vector3df(0.0f, sideLength, sideLength),
        vector3df(0.0f, 0.0f, 0.0f),                // this point would traditionally be assigned the "position" relative to the parent
        vector3df(sideLength, 0.0f, 0.0f),
        vector3df(sideLength, 0.0f, sideLength),
        vector3df(0.0f, 0.0f, sideLength)
    };
 
    // [b]apply the alignment policy using the resposition(...) method in my first post[/b]
    this->resposition(v, n, alignmentPolicy);
 
    // using the repositioned vectors as points for each face on the cube...
    // create the Irrlicht mesh vertices.
    ... (applicable code)
}
 
NOTES:
  1. the above implementation is MY choice based on my needs. The repositioning method COULD be altered to take the actual Irrlicht mesh vertex array as an argument and directly alter those. However if vertices are duplicated for the purpose of putting different texture coordinates on adjacent faces then this could create problems when using the PositioningPolicy3D::weighted policy as this policy would sum duplicate points.
  2. If future versions of Irrlicht come out, please feel free to incorporate this concept/code into the native Scene Node creation methods.
Post Reply