0
\$\begingroup\$

I'm trying to implement a voxel engine in C++ using OpenGL, and I've been working on the rendering of the world. In order to render, I have a 3D array of uint16's that hold that id of the block at the point. I also have a 3D array of uint8's that I am using to store the visibility data for that point, where each bit represents if a face is visible. I have it so the blocks render and all of the proper faces are hidden if needed, but all of the blocks are offset by a power of 2 from where they are stored in the array. So the block at [0][0][0] is rendered at (0, 0, 0), and the block at 11 is rendered at (1, 1, 1), but the block at [2][2][2] is rendered at (4, 4, 4) and the block at [3][3][3] is rendered at (8, 8, 8), and so on and so forth.

This is the result of drawing the above situation: enter image description here

I'm still a little new to the more advanced concepts of C++, like triple pointers, which I'm using for the 3D array, so I think the error is somewhere in there. This is the code for creating the arrays:

uint16*** _blockData; //Contains a 3D array of uint16s that are the ids of the blocks in the region
uint8*** _visibilityData; //Contains a 3D array of bytes that hold the visibility data for the faces

   //Allocate memory for the world data
_blockData = new uint16**[REGION_DIM];
for (int i = 0; i < REGION_DIM; i++)
{
    _blockData[i] = new uint16*[REGION_DIM];

    for (int j = 0; j < REGION_DIM; j++)
        _blockData[i][j] = new uint16[REGION_DIM];
}

//Allocate memory for the visibility
_visibilityData = new uint8**[REGION_DIM];
for (int i = 0; i < REGION_DIM; i++)
{
    _visibilityData[i] = new uint8*[REGION_DIM];

    for (int j = 0; j < REGION_DIM; j++)
        _visibilityData[i][j] = new uint8[REGION_DIM];
}

Here is the code used to create the block mesh for the region:

//Check if the positive x face is visible, this happens for every face
//Block::VERT_X_POS is just an array of non-transformed cube verts for one face 
//These checks are in a triple loop, which goes over every place in the array
if (_visibilityData[x][y][z] & 0x01 > 0)
{
    _vertexData->AddData(&(translateVertices(Block::VERT_X_POS, x, y, z)[0]), sizeof(Block::VERT_X_POS));
}

//This is a seperate method, not in the loop
glm::vec3* translateVertices(const glm::vec3 data[], uint16 x, uint16 y, uint16 z)
{
    glm::vec3* copy = new glm::vec3[6];
    memcpy(&copy, &data, sizeof(data));

    for(int i = 0; i < 6; i++)
        copy[i] += glm::vec3(x, -y, z); //Make +y go down instead

    return copy;
}

I cannot see where the blocks may be getting offset by more than they should be, and certainly not why the offsets are a power of 2. Any help is greatly appreciated. Thanks.

\$\endgroup\$
  • 3
    \$\begingroup\$ Things like int*** are completely the wrong thing to use here. A "triple pointer" is not logically the same thing as a 3-dimensional array. You can just use a plain std::vector and claculate offsets manually with something like z * width * height + y * width + x (with the total size being width * height * depth of course). \$\endgroup\$ – Sean Middleditch Oct 25 '13 at 17:38
  • \$\begingroup\$ @SeanMiddleditch, I changed the storage to a 1D array using the z * width * height + y * width + x code provided, initialized by uint16* _blockData = new uint16[REGION_DIM * REGION_DIM * REGION_DIM];. Now I am getting an error if the array doesn't contain at least one value that isn't 0 at the end of the Region constructor, where the array is being initialized. More specifically, this error. \$\endgroup\$ – sm81095 Oct 25 '13 at 19:14
  • \$\begingroup\$ @SeanMiddleditch You should really make that an answer. \$\endgroup\$ – Engineer Oct 25 '13 at 19:20
  • \$\begingroup\$ I found that memcpy() is not the way to go when copying arrays of glm::vec3's, so I switched it to just copying the values over one index at a time. This seemed to fix the power-of-two rendering errors. \$\endgroup\$ – sm81095 Oct 25 '13 at 19:51
  • 1
    \$\begingroup\$ @NickWiggill: I didn't realize that was the answer; I stopped reading as soon as I got that far and was just giving what I thought was a tangential tip. :) \$\endgroup\$ – Sean Middleditch Oct 25 '13 at 20:13
3
\$\begingroup\$

Things like int*** are completely the wrong thing to use here. A "triple pointer" is not logically the same thing as a 3-dimensional array. You can just use a plain std::vector and claculate offsets manually with something like z * width * height + y * width + x (with the total size being width * height * depth of course).

Create a vector, resize it, then copy in the right data. If you don't resize first, you must be sure to use push_back or back_inserter:

// or just pass the size to the constructor; same diff
std::vector<int> blocks;
blocks.resize(width * height * depth);

// not idiomatic C++, but it should work
std::memcpy(data.data(), source_array, blocks.size() * sizeof(blocks.at(0));

// better option if you resize first; may or may not be equivalent to memcpy depending on implementation and the PODness of the vector's contained type
std::copy(source_begin, source_end, begin(data));

// and the better option if you don't resize first, though this may resize multiple times internally, and is not the most efficient choice
std::copy(source_begin, source_end, std::back_inserter(blocks));
\$\endgroup\$
  • \$\begingroup\$ Many thanks. I managed to put a stop-gap measure on the drawing being off, and I'll try this later as a way to store the blocks. I got a "triple pointer" working for now, but I'll see if this way is more efficient later on. \$\endgroup\$ – sm81095 Oct 25 '13 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.