0
votes

When constructing my mesh I keep all the possible arrays of normals in another const static GLfloat[6][12] array. The mesh will only consist of square faces of a cube, so there is only six possible normals. When I add a face to the mesh I copy the data from the appropriate sub-array in my static array to an std::vector<GLfloat> and when finished I pass the data from the vector to the vertex buffer using glBufferData.

The static array:

static constexpr GLfloat facenormals[6][12] = {
    {
        0.0f,  1.0f,  0.0f, // TOP
        // Each sub-array has four identical 3-part vectors, I only included one of each.
    },
    {
        0.0f, -1.0f,  0.0f, // BOTTOM
    },
    {
        0.0f,  0.0f,  1.0f, // FRONT
    },
    {
        0.0f,  0.0f, -1.0f, // BACK
    },
    {
        1.0f,  0.0f,  0.0f, // RIGHT
    },
    {
        -1.0f,  0.0f,  0.0f, // LEFT
    }
};

Copied into the vector using:

normals.insert(normals.end(), CubeData::facenormals[direction], CubeData::facenormals[direction] + 12);

Passed to vertex buffer:

glBufferData(GL_ARRAY_BUFFER, chunkmesh.normals.size() * sizeof(GLfloat), chunkmesh.getnormals(), GL_STATIC_DRAW);

Like this the vector is just a continous block of memory containing the normals in the order specified by the indexbuffer.

Is it possible and/or sane to instead fill the vector with pointers to the static data in the array and pass the pointers to the vertex buffer, and by doing so shaving the time it takes to copy the data from the mesh building time.

I measured the time it takes to build the mesh, including random generation, using Visual Studios debugging tools, and the copying of data used up over 40% of CPU time, positions, normals, and texturecoordinates.

2
Why is the dimesion of the array [6][12]? Shouldn't it be facenormals[3][6]? Then it would be glBufferData(GL_ARRAY_BUFFER, 3*6*sizeof(GLfloat), &facenormals[0][0], GL_STATIC_DRAW); - Rabbid76
@Rabbid76, There is actually four identical sets of three floats in each sub-array, I removed them here to save space. I cannot pass the address of the first element and specify the whole size because I fill the vector with thousands of vertices before passing it to the vertex buffer. The data in the vector could look like: 12 floats(four normals pointing in the same direction, one for each vertex of a square) followed by four normals pointing in another direction, not necessarily the same that follows the previous in the static array. the final size of the vector is usually around 5000. - Viktor Axén

2 Answers

0
votes

so of course the following code is more for 2D drawing but this is my method.

-1. create an object that you can call Vertex.

typedef struct{
        float x, y;
        float r, g, b;
        float tex_coordx, tex_coordy;
}Vertex;

-2. define a static array of Vertex object

Vertex square[6] = {{0, 0, 1, 1, 1, 0, 1},
                            {1, 0, 1, 1, 1, 1, 1},
                            {0, 1, 1, 1, 1, 0, 0},
                            {0, 1, 1, 1, 1, 0, 0},
                            {1, 1, 1, 1, 1, 1, 0},
                            {1, 0, 1, 1, 1, 1, 1}};

as I said earlier, this code is for 2D drawing, your free to add a new float (or if you prefer GLfloat) variable in vertex call y.

-3. pass the pointer to the glBufferData without needed to copy the array in another unneeded std::vector object.

glBufferData(GL_ARRAY_BUFFER, sizeof(square), square, GL_STATIC_DRAW);

I hope that it would be useful for you.

0
votes

It sounds as though you want to know how to reuse 6 normals over the entire cube as a space saving measure? i.e. Provide separate indexing for normals & vertices? If that's the case, then there are two space saving measures you can employ.

The easiest is to simply convert the buffers to be half precision (16bit floats). Then later specify the data using GL_HALF_FLOAT.

#include <emmintrin.h>

typedef uint16_t half;
inline half toHalf(float f) { return _cvtss_sh(f, _MM_FROUND_CUR_DIRECTION); }

If you want to use separate indexing for vertices and normals, then you can upload the normal indices as an array of uint16 or uint32 (specify the data using glVertexAttribIFormat or glVertexArrayAttribIFormat!!!). The normals will need to be uploaded as a shader storage buffer, and the normal indices then become per-vertex attributes. A minimal shader to make that work would look like:

#version 450

in uint vs_normalIndex;

layout(std430, binding = 0) buffer Normals
{
  float normals[];
};

void main()
{
  uint index = vs_normalIndex * 3;
  vec3 normal = vec3(normals[index], normals[index + 1], normals[index + 2]);
}