-1

I'm attempting to create a terrain, however the same error keeps on appearing. Where triangles form links between verices that are not close to each other, i.e. v1 to v100, causing malformed triangles to form when rendering.

Error Image: Triangles malformed

Wireframe: Triangles malformed in wireframe

The code to reproduce this:

    class Generator
    {
        TerrainMesh gen_emptyTerrainMesh  = TerrainMesh();

        /* Parameters for the class */

        int                 gen_size;

        /**
         \brief for loop from 0 to n, making x, y, z
         coordinates, n's default is 128
        
         \param arr Array to store the vertices in

        **/ 
        void gen(std::vector<vec3>* arr) const
        {
            float x = 0.0f, y = 0.0f, z = 0.0f;

            for (int j = 0; j < gen_size; j++)
            {
                for (int i = 0; i < gen_size; i++)
                {
                    

                    x = static_cast<float>(i);
                    // y += rand() % 10 - 1.0f;
                    z = static_cast<float>(j);

                    arr->push_back(vec3(x, y, z));

                    // DEBUG 
                    // printf("[BUILD INFO]: x: %F, y: %F, z: %F \n", x, y, z);
                }
            }
        }

        /**
         *\brief Using struct Triangle and vector convert x, y z
         * coordinates from vertices into quads made up of 2 triangles
         * stored sequentially. 
         *
         * 
         *  Triangle {
         *
         *      p:  n
         *
         *      p2: n + 1 \ n + 2 + size
         *
         *      p3: n + 2 + size \ n + 1 + size
         *
         *  }
         *
         * At the same time work out normals for each Triangle
         **/
        void toTriangle(const std::vector<vec3>* arr, std::vector<Triangle>* tri_arr) const
        {
            int crrPointer = 0;

            // Convert vector float to vector Triangle/Quad format
            for (int n = 0; n < arr->size(); n++)
            {
                if (n % gen_size == 0)
                    crrPointer += 1;

                if (crrPointer < (gen_size - 1) && n % (gen_size - 1) != 0) {
                    tri_arr->push_back(Triangle{arr->at(n), arr->at(n + 1), arr->at(n + 2 + gen_size)});
                    tri_arr->push_back(Triangle{arr->at(n), arr->at(n + 2 + gen_size), arr->at(n + 1 + gen_size), true});
                }
            }

            // Work out the normal for each triangle
            // needs to be done

        }

        static void toVectorArray(std::vector<float>* vertices, const std::vector<Triangle>* tri_arr)
        {
            for (auto &tri : *tri_arr)
            {
                vertices->push_back(tri.p.x);
                vertices->push_back(tri.p.y);
                vertices->push_back(tri.p.z);

                vertices->push_back(tri.normal.x);
                vertices->push_back(tri.normal.y);
                vertices->push_back(tri.normal.z);

                vertices->push_back(0.0);
                vertices->push_back(1.0);

                vertices->push_back(tri.p2.x);
                vertices->push_back(tri.p2.y);
                vertices->push_back(tri.p2.z);

                vertices->push_back(tri.normal.x);
                vertices->push_back(tri.normal.y);
                vertices->push_back(tri.normal.z);

                vertices->push_back(1.0);
                vertices->push_back(1.0);

                vertices->push_back(tri.p3.x);
                vertices->push_back(tri.p3.y);
                vertices->push_back(tri.p3.z);

                vertices->push_back(tri.normal.x);
                vertices->push_back(tri.normal.y);
                vertices->push_back(tri.normal.z);

                if (!tri.isP4)
                {
                    vertices->push_back(1.0);
                    vertices->push_back(0.0);
                } else
                {
                    vertices->push_back(0.0);
                    vertices->push_back(0.0);
                }

            }
        }
        // DEBUG

        //----------------------------------------------------------------------------
        static std::string getTime()
        {
            time_t result = time(nullptr);

            char str[26];
            ctime_s(str, sizeof str, &result);


            // Remove the "\n" source: https://stackoverflow.com/a/41705582
            if (str[strlen(str) - 1] == '\n') str[strlen(str) - 1] = '\0';

            return str;
        }
        //----------------------------------------------------------------------------
    public:
        Generator(std::vector<float>* p_vecArray, int p_size = 128): gen_size(p_size)
        {
            std::vector<vec3> tempVec;
            std::vector<Triangle> triangles;


            // Make the vertices, gen_size * gen_size
            gen(&tempVec);

            // Turn them into Triangles so its easier
            // to work out normals and texture coordinates
            toTriangle(&tempVec, &triangles);

            std::cout << p_vecArray->size() << "\n";

            // turn the triangles into
            // x, y, z, nx, ny, nz, texX, texY
            // for each vertex
            toVectorArray(p_vecArray, &triangles);

            std::cout << p_vecArray->size() << "\n";
            // printTriangles(&triangles);
            
        }
    };

Triangle struct

struct Triangle
{
    vec3 p;
    vec3 p2;
    vec3 p3;

    bool isP4 = false;

    vec3 normal = {0, 0, 0};
};

I've tried to use Quad instead of triangles to see if that helped but to no avail. I think the error has to do with how they are being converted into trianlges or how they are in order inside the vertices vector, checked online to see if that was the case but haven't come across anything meaningful to suggest that order is important inside opengl.

Nukestye
  • 19
  • 8
  • 5
    The order of vertices is very important! It must have come up, otherwise, you should burn whatever resource you're using. It has to do with [winding order](https://www.khronos.org/opengl/wiki/Face_Culling). Also, every three consecutive points is what's making up a single triangle. Your wireframe shows that the order is messed up. However, you should start small and not try to generate a huge wall of triangles. Start with one, then 2, then 4, and then it's easy to generalize up to n from there. Starting small makes it easier for you to verify that all vertices are correct – Ted Klein Bergman Feb 21 '22 at 09:39
  • @TedKleinBergman Thanks for the help! I will attempt to do as you say and hopefully patch up the problem. – Nukestye Feb 21 '22 at 11:16

1 Answers1

0

TL;DR: So by listening to @TedKleinBergman, I was able to deduce the problem, i.e. the vertices were in the wrong order and the way I was binding them previously wasn't accurate, thus worked tirelessly to figure out the correct order.


Long version:

So I went back to the code that generated the vertices and changed it a bit to accommodate the texture coordinates.

/**
\brief for loop from 0 to n, making x, y, z
coordinates, n's default is 128
        
\param arr Array to store the vertices in

**/
void gen(std::vector<vec4>* arr) const
{
    float x = 0.0f, y = 0.0f, z = 0.0f;

    int p = 1;

    for (int j = 0; j <= gen_size; j++)
    {
        for (int i = 0; i <= gen_size; i++)
        {
            x = static_cast<float>(i);
            // y += rand() % 10 - 1.0f;
            z = static_cast<float>(j);

            if (p < 4)
            {
                if (p == 3)
                {
                    arr->push_back(vec4({ x, y, z }, txc_p));
                    // printf("4, value x: %F z: %F\n", x, z);
                    p++;
                } else if (p == 2)
                {
                    arr->push_back(vec4({ x, y, z }, txc_p2));
                    // printf("3, value x: %F z: %F\n", x, z);
                    p++;
                } else if (p == 1)
                {
                    arr->push_back(vec4({ x, y, z }, txc_p3));
                    // printf("2, value x: %F z: %F\n", x, z);
                    p++;
                } else if (p == 4)
                {
                    arr->push_back(vec4({ x, y, z }, txc_p4));
                    // printf("1, value x: %F z: %F\n", x, z);
                    p++;
                }
            } else
            {
                p = 2;
                arr->push_back(vec4({ x, y, z }, txc_p));
                // printf("null, value x: %F z: %F\n", x, z);
            }
        }
    }
}

The main changes that helped were changing the functions toTriangle and ToArray into one function and looking at the triangles vertices as vectors (took me too long to figure that) and using a quad(square) labelling each vertex and seeing the difference. Which should result in +- 1 difference between v1 to v4 for i and j. The rest of the code should be self-explanatory, but the issues now I see are optimization & the lighting (which I will try to fix by myself, of course).

So beware this code isn't as optimized it can be, and I may have done something stupid that could be easily fixed, and if so do leave a comment!


void OrderPair(std::vector<vec4> v, std::vector<float>* tempV) const
{
    /*
     *
     *    Counter-Clockwise order; is default
     *
     *  Left Triangle
     *  v2 --- v1
     *   |     /
     *   |    /
     *   |   /      ORDER: v1 -> v2 -> v3
     *   |  /
     *   | /
     *   v3
     *
     *   Right Triangle
     *         v1
     *        / |
     *       /  |
     *      /   |   ORDER: v1 -> v3 -> v4
     *     /    |
     *    /     |
     *   v3 --- v4
     *
     *
     */

    std::vector<Triangle> triangles;


    for (int index = 1; index < v.size(); index += 1)
    {
        int YPointer = 0;
        if (index % gen_size == 0)
        {
            YPointer++;
            continue;
        }

        if (YPointer <= (gen_size - 1))
        {
            vec4 n = v.at(index);

            vec3 p = n.xyz;
            vec3 p2 = { n.xyz.x - 1, n.xyz.y,       n.xyz.z, };
            vec3 p3 = { n.xyz.x - 1, n.xyz.y, n.xyz.z + 1 };
            vec3 p4 = { n.xyz.x,        n.xyz.y, n.xyz.z + 1 };

            // Left Triangle
            auto tri1 = Triangle(p, p2, p3);

            vec3 u = p2 - p;
            vec3 v = p3 - p;

            tri1.normal.x = (u.x * v.z) - (u.z * v.y);
            tri1.normal.y = (u.z * v.x) - (u.x * v.z);
            tri1.normal.z = (u.x * v.y) - (u.y * v.x);

            // Right Triangle
            auto tri2 = Triangle(p, p3, p4, false, true);

            u = p3 - p;
            v = p4 - p;

            tri2.normal.x = (u.x * v.z) - (u.z * v.y);
            tri2.normal.y = (u.z * v.x) - (u.x * v.z);
            tri2.normal.z = (u.x * v.y) - (u.y * v.x);

            triangles.push_back(tri1);
            triangles.push_back(tri2);
        }

    }

    for (auto [p, p2, p3, isP2, isP4, normal] : triangles)
    {
        // Vertex 1;
        tempV->push_back(p.x);
        tempV->push_back(p.y);
        tempV->push_back(p.z);

        tempV->push_back(normal.x);
        tempV->push_back(normal.y);
        tempV->push_back(normal.z);

        tempV->push_back(txc_p.x);
        tempV->push_back(txc_p.y);

        // Vertex 2;
        tempV->push_back(p2.x);
        tempV->push_back(p2.y);
        tempV->push_back(p2.z);

        tempV->push_back(normal.x);
        tempV->push_back(normal.y);
        tempV->push_back(normal.z);
        if (isP2)
        {
            tempV->push_back(txc_p2.x);
            tempV->push_back(txc_p2.y);
        }
        else
        {
            tempV->push_back(txc_p3.x);
            tempV->push_back(txc_p3.y);
        }

        // Vertex 3;
        tempV->push_back(p3.x);
        tempV->push_back(p3.y);
        tempV->push_back(p3.z);

        tempV->push_back(normal.x);
        tempV->push_back(normal.y);
        tempV->push_back(normal.z);

        if (isP4)
        {
            tempV->push_back(txc_p4.x);
            tempV->push_back(txc_p4.y);
        }
        else
        {
            tempV->push_back(txc_p3.x);
            tempV->push_back(txc_p3.y);
        }

    }

}
Generator(std::vector<float>* p_vecArray, int p_size = 2): gen_size(p_size)
{
    std::vector<vec4> tempVec;
    
    // Make the vertices, gen_size * gen_size
    gen(&tempVec);
    // printArray(&tempVec);

    OrderPair(tempVec, p_vecArray);
    // printArray(p_vecArray);
    // printTriangles(&triangles);
            
}

Results:

using gen_size as 128 Triangles being rendered properly

wireframe: triangles being rendered properly wireframe

Nukestye
  • 19
  • 8