Efficient way of computing faces of axially-aligned box

Started by
1 comment, last by x6herbius 10 years, 3 months ago

Just found these forums and they look like they'd be pretty helpful for me. I'm semi-experienced in programming but fairly new to graphics specifically, so questions might seem basic while I'm getting to grips with some things. tongue.png

I'm attempting to implement a primitive axially-aligned box renderable for a level editor written in Qt (C++). I'm using the Qt3D library and am intending to ensure that each renderable implements a render interface, which at the moment consists just of:


// Export to QGeometryData, in world co-ordinates.
virtual QGeometryData toGeomData() const = 0;

The QGeometryData is a Qt3D manager for vertex data. My box renderable's function looks like this:


QGeometryData RenderBox::toGeomData() const
{
    // Calculate the min and max co-ordinates in world space.
    QVector3D min = m_vecPosition + minimum();
    QVector3D max = m_vecPosition + maximum();
    QColor col = color();
    
    // Order of vertices:
    // 0. Minimum
    // 1. Minimum with modified X co-ord
    // 2. Minimum with modified X and Z co-ords
    // 3. Minimum with modified Z co-ord
    // 4. Maximum with modified Z co-ord
    // 5. Maximum with modified Z and X co-ords
    // 6. Maximum with modified X co-ord
    // 7. Maximum
    // This gives us an easy counter-clockwise winding for the top and bottom faces.
    
    const QVector3D verts[8] =               // Taking Z as "north" and looking down on the box (Y pointing at us), we have:
    {
        min,                                    // Lower SW 0
        QVector3D(max.x(), min.y(), min.z()),   // Lower NW 1
        QVector3D(max.x(), min.y(), max.z()),   // Lower NE 2
        QVector3D(min.x(), min.y(), max.z()),   // Lower SE 3
        QVector3D(max.x(), max.y(), min.z()),   // Upper NW 4
        QVector3D(min.x(), max.y(), min.z()),   // Upper SW 5
        QVector3D(min.x(), max.y(), max.z()),   // Upper SE 6
        max                                     // Upper NE 7
    };
    
    // Calculate normals for faces.
    QVector3D nTop = ccwNormal(verts[5], verts[6], verts[7]);       // SW, SE, NE
    QVector3D nBottom = ccwNormal(verts[0], verts[1], verts[2]);    // SW, NW, NE
    QVector3D nNorth = ccwNormal(verts[7], verts[2], verts[1]);     // UNE, LNE, LNW
    QVector3D nSouth = ccwNormal(verts[0], verts[3], verts[6]);     // LSW, LSE, USE
    QVector3D nEast = ccwNormal(verts[7], verts[6], verts[3]);      // UNE, USE, LSE
    QVector3D nWest = ccwNormal(verts[0], verts[5], verts[4]);      // LSW, USW, UNW
    
    // Create a GeomData and add each face of the box.
    QGeometryData geom;
    
    // Top face
    geom.appendVertex(verts[4], verts[5], verts[6], verts[7]);
    geom.appendNormal(nTop, nTop, nTop, nTop);
    
    // Bottom face
    geom.appendVertex(verts[0], verts[1], verts[2], verts[3]);
    geom.appendNormal(nBottom, nBottom, nBottom, nBottom);
    
    // North face
    geom.appendVertex(verts[7], verts[2], verts[1], verts[4]);
    geom.appendNormal(nNorth, nNorth, nNorth, nNorth);
    
    // South face
    geom.appendVertex(verts[0], verts[3], verts[6], verts[5]);
    geom.appendNormal(nSouth, nSouth, nSouth, nSouth);
    
    // East face
    geom.appendVertex(verts[7], verts[6], verts[3], verts[2]);
    geom.appendNormal(nEast, nEast, nEast, nEast);
    
    // West face
    geom.appendVertex(verts[0], verts[5], verts[4], verts[1]);
    geom.appendNormal(nWest, nWest, nWest, nWest);
    
    // Append colour attributes.
    // These will all be the same.
    for ( int i = 0; i < 24; i++ )
    {
        geom.appendColor(col);
    }
    
    // Return the GeomData.
    return geom;
}

ccwNormal() is a simple helper function that returns the normal (pointing towards the viewer) of three vectors assumed to be supplied in an anticlockwise winding order.

My question here is whether there is a more efficient/elegant way of calculating the faces of the box. Theoretically (if I've done the calculations right) this way should provide the correct vertex information, but it seems inflexible from a distance and looks like it'd be a pain to modify later on if I needed to. How acceptable is this method?

(Also, an implementation-specific question for those familiar with Qt/OpenGL: is returning a QGeometryData object the best way to implement rendering? My concerns with it are twofold: firstly, copying data from geom on the stack to the returned object might cause unnecessary overhead, especially considering that this would have to be done for every single renderable, and secondly returning a fresh object every time toGeomData() is called would be a waste of time if the render data was exactly the same as the previous frame. According to the documentation for QGeometryData the graphics data is not re-uploaded to the GPU if it hasn't changed, but if the object is being replaced by a new one each frame then it might prevent this optimisation from happening.)

Advertisement

If the box is axis aligned, why calc normals at all? The face normal will be the axis it points down. Skip the normal calc calls, which will probably save 6 sqrt()'s.

That's a very good point, actually. I guess I'm a bit blind to my own code. :P

This topic is closed to new replies.

Advertisement