Projection problems with 3D rendering in C++ using SDL

  3d, 3d-engine, c++, sdl-2

I am new to 3D calculation and for the most part (except that I am using SDL2 instead of OCL), I followed the tutorial of "One Lone Coder" about programming graphics engine.
Here is the 1st part of the tutorial series.

I followed part 1 and everything worked quite well, then I followed part 2 and still everything worked as presented in the tutorial.

But in the beginning of the 3rd tutorial of the series, he made some code changes that, when I tried to keep up, caused me headaches. (my code looks a bit different from his, because I added a mat4x4 and a vec3d class instead of structs).

I must have something to do with the translation or projection, because rotation works well and the problem is, that when I do not rotate it, I just see the front lines of the cube, not the ones in the back, even though, all faces get drawn.

Here is my code for drawMesh and drawTriangle (I believe the error lies somewhere in here):

void RenderWindow::drawMesh(mesh pMesh, float fTimeElapsed)
{
    fTheta += 1.0f * fTimeElapsed;

    mat4x4 matWorld = mat4x4::identity();
    mat4x4 matRotX = mat4x4::rotX(fTheta);
    mat4x4 matRotY = mat4x4::rotY(fTheta);
    mat4x4 matRotZ = mat4x4::rotZ(fTheta);
    mat4x4 matTrans = mat4x4::trans(0.0f, 0.0f, 5.0f);

    mat4x4 matProj = mat4x4::proj(90.0f, (float)_height/ (float)_width, 0.1f, 1000.0f);

    std::vector<triangle> vecTrianglesToRaster;

    for(auto tri: pMesh.tris)
    {
        triangle triRotatedX, triRotatedY, triRotatedZ, triTransformed, triTranslated, triProjected;

    // #################################
    // ## Rotates the cube in X, Y, Z ##
    // #################################
    triRotatedX.p[0] = mat4x4::mul_vec3d(matRotX, tri.p[0]);
    triRotatedX.p[1] = mat4x4::mul_vec3d(matRotX, tri.p[1]);
    triRotatedX.p[2] = mat4x4::mul_vec3d(matRotX, tri.p[2]);

    triRotatedY.p[0] = mat4x4::mul_vec3d(matRotY, triRotatedX.p[0]);
    triRotatedY.p[1] = mat4x4::mul_vec3d(matRotY, triRotatedX.p[1]);
    triRotatedY.p[2] = mat4x4::mul_vec3d(matRotY, triRotatedX.p[2]);

    triRotatedZ.p[0] = mat4x4::mul_vec3d(matRotZ, triRotatedY.p[0]);
    triRotatedZ.p[1] = mat4x4::mul_vec3d(matRotZ, triRotatedY.p[1]);
    triRotatedZ.p[2] = mat4x4::mul_vec3d(matRotZ, triRotatedY.p[2]);

    // #########################
    // ## Translates the cube ##
    // #########################
    triTransformed.p[0] = mat4x4::mul_vec3d(matTrans, triRotatedZ.p[0]);
    triTransformed.p[1] = mat4x4::mul_vec3d(matTrans, triRotatedZ.p[1]);
    triTransformed.p[2] = mat4x4::mul_vec3d(matTrans, triRotatedZ.p[2]);

    // ######################
    // ## Projects the cube ##
    // ######################
    triProjected.p[0] = mat4x4::mul_vec3d(matProj, triTransformed.p[0]);
    triProjected.p[1] = mat4x4::mul_vec3d(matProj, triTransformed.p[1]);
    triProjected.p[2] = mat4x4::mul_vec3d(matProj, triTransformed.p[2]);

    /*
    vec3d normal, line1, line2;

    line1 = vec3d::sub(triTransformed.p[1], triTransformed.p[0]);
    line2 = vec3d::sub(triTransformed.p[2], triTransformed.p[0]);

    normal = vec3d::cross(line1, line2);
    normal = vec3d::norm(normal);

    vec3d cameraRay = vec3d::sub(triTransformed.p[0], _camera);
    */
    //if(vec3d::dot(normal, cameraRay) < 0.0f) {

            // Devides by the aspect ration of the screen to make cube look like cube and not like rect
            //triProjected.p[0] = mat4x4::mul_vec3d(matProj, triTransformed.p[0]);
            //triProjected.p[1] = mat4x4::mul_vec3d(matProj, triTransformed.p[1]);
            //triProjected.p[2] = mat4x4::mul_vec3d(matProj, triTransformed.p[2]);

            /*
            triProjected.p[0] = vec3d::div(triProjected.p[0], triTransformed.p[0]._w);
            triProjected.p[1] = vec3d::div(triProjected.p[1], triTransformed.p[1]._w);
            triProjected.p[2] = vec3d::div(triProjected.p[2], triTransformed.p[2]._w);
            */

            // Center the cube
            vec3d vOffsetView(1, 1, 0, 1);
            triProjected.p[0] = vec3d::add(triProjected.p[0], vOffsetView);
            triProjected.p[1] = vec3d::add(triProjected.p[1], vOffsetView);
            triProjected.p[2] = vec3d::add(triProjected.p[2], vOffsetView);

            // Make cube right size
            triProjected.p[0]._x *= 0.5f * (float)_width;
            triProjected.p[0]._y *= 0.5f * (float)_height;
            triProjected.p[1]._x *= 0.5f * (float)_width;
            triProjected.p[1]._y *= 0.5f * (float)_height;
            triProjected.p[2]._x *= 0.5f * (float)_width;
            triProjected.p[2]._y *= 0.5f * (float)_height;

            vecTrianglesToRaster.push_back(triProjected);
        //}
    }

    std::sort(vecTrianglesToRaster.begin(), vecTrianglesToRaster.end(), [](triangle &t1, triangle &t2)
    {
        float z1 = (t1.p[0]._z + t1.p[1]._z + t1.p[2]._z) / 3.0f;
        float z2 = (t2.p[0]._z + t2.p[1]._z + t2.p[2]._z) / 3.0f;
        return z1 > z2;
    });

    color paintColor = { 0, 0, 255, 255 };

    for(auto &triProjected: vecTrianglesToRaster)
    {
        drawTriangle(triProjected, paintColor);
    }
}

void RenderWindow::drawTriangle(triangle pTriangle, color pColor)
{
    SDL_SetRenderDrawColor(_renderer, pColor.r, pColor.g, pColor.b, pColor.a);

    SDL_RenderDrawLine(_renderer, pTriangle.p[0]._x, pTriangle.p[0]._y, pTriangle.p[1]._x, pTriangle.p[1]._y);
    SDL_RenderDrawLine(_renderer, pTriangle.p[1]._x, pTriangle.p[1]._y, pTriangle.p[2]._x, pTriangle.p[2]._y);
    SDL_RenderDrawLine(_renderer, pTriangle.p[2]._x, pTriangle.p[2]._y, pTriangle.p[0]._x, pTriangle.p[0]._y);
}

And here are my mat4x4 and vec3d classes (in case some of the calculations are wrong (but I used the same as "One Lone Coder", I compared it several times…

mat4x4:

mat4x4::mat4x4()
{
    for(int c = 0; c < 4; c++) {
        for(int r = 0; r < 4; r++) {
            _m[c][r] = 0;
        }
    }
}

mat4x4::mat4x4(float m[][4])
{
    for(int c = 0; c < 4; c++) {
        for(int r = 0; r < 4; r++) {
            _m[c][r] = m[c][r];
        }
    }
}

vec3d mat4x4::mul_vec3d(mat4x4 &m, vec3d &v)
{
    vec3d r;

    r._x = v._x * m._m[0][0] + v._y * m._m[1][0] + v._z * m._m[2][0] + v._w * m._m[3][0];
    r._y = v._x * m._m[0][1] + v._y * m._m[1][1] + v._z * m._m[2][1] + v._w * m._m[3][1];
    r._z = v._x * m._m[0][2] + v._y * m._m[1][2] + v._z * m._m[2][2] + v._w * m._m[3][2];
    r._w = v._x * m._m[0][3] + v._y * m._m[1][3] + v._z * m._m[2][3] + v._w * m._m[3][3];

    return r;
}

mat4x4 mat4x4::identity()
{
    mat4x4 m;

    m._m[0][0] = 1.0f;
    m._m[1][1] = 1.0f;
    m._m[2][2] = 1.0f;
    m._m[3][3] = 1.0f;

    return m;
}

mat4x4 mat4x4::rotX(float fAngleRad)
{
    mat4x4 m;

    m._m[0][0] = 1.0f;
    m._m[1][1] = cosf(fAngleRad);
    m._m[1][2] = sinf(fAngleRad);
    m._m[2][1] = -sinf(fAngleRad);
    m._m[2][2] = cosf(fAngleRad);
    m._m[3][3] = 1.0f;

    return m;
}

mat4x4 mat4x4::rotY(float fAngleRad)
{
    mat4x4 m;

    m._m[0][0] = cosf(fAngleRad);
    m._m[0][2] = sinf(fAngleRad);
    m._m[2][0] = -sinf(fAngleRad);
    m._m[1][1] = 1.0f;
    m._m[2][2] = cosf(fAngleRad);
    m._m[3][3] = 1.0f;

    return m;
}

mat4x4 mat4x4::rotZ(float fAngleRad)
{
    mat4x4 m;

    m._m[0][0] = cosf(fAngleRad);
    m._m[0][1] = sinf(fAngleRad);
    m._m[1][0] = -sinf(fAngleRad);
    m._m[1][1] = cosf(fAngleRad);
    m._m[2][2] = 1.0f;
    m._m[3][3] = 1.0f;

    return m;
}

mat4x4 mat4x4::trans(float x, float y, float z)
{
    mat4x4 m;

    m._m[0][0] = 1.0f;
    m._m[1][1] = 1.0f;
    m._m[2][2] = 1.0f;
    m._m[3][3] = 1.0f;
    m._m[3][0] = x;
    m._m[3][1] = y;
    m._m[3][2] = z;

    return m;
}

mat4x4 mat4x4::proj(float fFovDegrees, float fAspectRatio, float fNear, float fFar)
{
    float fFovRad = 1.0f / tanf(fFovDegrees * 0.5f / 180.0f * 3.14159f);
    mat4x4 m;

    m._m[0][0] = fAspectRatio * fFovRad;
    m._m[1][1] = fFovRad;
    m._m[2][2] = fFar / (fFar - fNear);
    m._m[3][2] = (-fFar * fNear) / (fFar - fNear);
    m._m[2][3] = 1.0f;
    m._m[3][3] = 0.0f;

    return m;
}

mat4x4 mat4x4::mul_mat4x4(mat4x4 &m1, mat4x4 &m2)
{
    mat4x4 m;
    for (int c = 0; c < 4; c++) {
        for (int r = 0; r < 4; r++) {
            m._m[r][c] = m1._m[r][0] * m2._m[0][c] +
                         m1._m[r][1] * m2._m[1][c] +
                         m1._m[r][2] * m2._m[2][c] +
                         m1._m[r][3] * m2._m[3][c];
        }
    }

    return m;
}

void mat4x4::print()
{
    std::cout << _m[0][0] << " " << _m[0][1] << " " << _m[0][2] << " " << _m[0][3] << std::endl;
    std::cout << _m[1][0] << " " << _m[1][1] << " " << _m[1][2] << " " << _m[1][3] << std::endl;
    std::cout << _m[2][0] << " " << _m[2][1] << " " << _m[2][2] << " " << _m[2][3] << std::endl;
    std::cout << _m[3][0] << " " << _m[3][1] << " " << _m[3][2] << " " << _m[3][3] << std::endl << std::endl << std::endl;
}

vec3d:

vec3d::vec3d()
: _x(0), _y(0), _z(0), _w(1)
{}

vec3d::vec3d(float x, float y, float z, float w)
    : _x(x), _y(y), _z(z), _w(w)
{}

/*
 * Adds a vector to another vector
 */
vec3d vec3d::add(vec3d &v1, vec3d &v2)
{
    return vec3d(
        v1._x + v2._x,
        v1._y + v2._y,
        v1._z + v2._z,
        1.0f
    );
}

/*
 * Subtracts a vector from another vector
 */
vec3d vec3d::sub(vec3d &v1, vec3d &v2)
{
    return vec3d(
        v1._x - v2._x,
        v1._y - v2._y,
        v1._z - v2._z,
        1.0f
    );
}

/*
 * Multiplies a vector by a scalar
 */
vec3d vec3d::mul(vec3d &v, float k)
{
    return vec3d(
        v._x * k,
        v._y * k,
        v._z * k,
        1.0f
    );
}

/*
 * Divides a vector by a scalar
 */
vec3d vec3d::div(vec3d &v, float k)
{
    return vec3d(
        v._x / k,
        v._y / k,
        v._z / k,
        1.0f
    );
}

/*
 * Calculates the dot product of two given vectors
 */
float vec3d::dot(vec3d &v1, vec3d &v2)
{
    return v1._x * v2._x + v1._y * v2._y + v1._z * v2._z;
}

/*
 * Calculates the length of a given vector
 */
float vec3d::len(vec3d &v)
{
    return sqrtf(vec3d::dot(v, v));
}

/*
 * Normalizes a given vector
 */
vec3d vec3d::norm(vec3d &v)
{
    float len = vec3d::len(v);

    return vec3d(
        v._x / len,
        v._y / len,
        v._z / len,
        1.0f
    );
}

/*
 * Calculates the cross product of two given vectors
 */
vec3d vec3d::cross(vec3d &v1, vec3d &v2)
{
    return vec3d(
        v1._y * v2._z - v1._z * v2._y,
        v1._z * v2._x - v1._x * v2._z,
        v1._x * v2._y - v1._y * v2._x,
        1.0f
    );
}

void vec3d::print()
{
    std::cout << _x << " " << _y << " " << _z << " " << _w << std::endl;
}

Source: Windows Questions C++

LEAVE A COMMENT