jpaver / opengametools

A set of open c++ game development tools that are lightweight, easy-to-integrate and free to use. Currently hosting a magicavoxel .vox full scene loader.
MIT License
375 stars 36 forks source link

ogt_vox: Hints about applying the transform #19

Closed mgerhardy closed 2 years ago

mgerhardy commented 2 years ago

Maybe it's obvious for everybody except me, but I thought that sharing my current implementation might help others and is a good opportunity to get some feedback on my solution:

const glm::vec4 pivot(
    floor(model->size_x / 2.0f),
    floor(model->size_y / 2.0f),
    floor(model->size_z / 2.0f), 
    0.0f
);
/**
 * @brief Calculate the scene graph object transformation. Used for the voxel and the AABB of the volume.
 *
 * @param mat The world space model matrix (rotation and translation) for the chunk
 * @param pos The position inside the untransformed chunk (local position)
 * @param pivot The pivot to do the rotation around. This is the @code chunk_size - 1 + 0.5 @endcode. Please
 * note that the @c w component must be @c 0.0
 * @return glm::vec4 The transformed world position
 */
static inline glm::vec4 transform(const glm::mat4x4 &mat, const glm::ivec3 &pos, const glm::vec4 &pivot) {
    return glm::floor(mat * (glm::vec4((float)pos.x + 0.5f, (float)pos.y + 0.5f, (float)pos.z + 0.5f, 1.0f) - pivot));
}
const uint8_t *ogtVoxels = model->voxel_data
for (uint32_t k = 0; k < model->size_z; ++k) {
    for (uint32_t j = 0; j < model->size_y; ++j) {
        for (uint32_t i = 0; i < model->size_x; ++i, ++ogtVoxel) {
            if (ogtVoxel[0] == 0) {
                continue;
            }
            const voxel::Voxel voxel = voxel::createVoxel(voxel::VoxelType::Generic, _palette[ogtVoxel[0]]);
            const glm::ivec3& pos = transform(ogtMat, glm::ivec3(i, j, k), pivot);
            const glm::ivec3& poszUp = transform(zUpMat, pos, glm::ivec4(0));
            v->setVoxel(poszUp, voxel);
        }
    }
}

the zUpMat is just to bring everything into my own coordinate system (y upwards)

const glm::mat4 zUpMat = glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));

If I am doing something stupid here, please let me know. If not it might help others.

Find the full source code at https://github.com/mgerhardy/vengi/blob/master/src/modules/voxelformat/VoxFormat.cpp

jpaver commented 2 years ago

Hi Martin, and apologies for the late reply. I think you're right - this should be better documented in ogt_vox. I'm re-opening this issue until I can address this documentation.

The pivot for magicavoxel is the halfway through each voxel grid, (ie. each ogt_vox_model), precisely as you have it:

   pivot.x = floor(model->size_x / 2);
   pivot.y = floor(model->size_y / 2);
   pivot.z = floor(model->size_z / 2);

My own use-case is to generate triangle mesh from voxel grid (using ogt_voxel_meshify), and I usually bias the output vertex positions of that meshify step (which are in mesh local space) like so:

for each vertex:
 vertex.pos -= pivot;

The net effect is it recenters the output mesh such that the pivot is at coordinate (0,0,0) in mesh local space. Then I simply use the instance transforms within the scene for ordinary rendering.

mgerhardy commented 2 years ago

Thanks for your response. I've only closed this because the linked pull request added a method to perform these steps - which is kind of a documentation, too.

jpaver commented 2 years ago

I added some documentation in this commit: https://github.com/jpaver/opengametools/commit/d39bf794b43fd5c13afccb11bea92f7fafe257c3

@mgerhardy -- could you take a look and see if this is helpful? Otherwise, I will close as resolved. Thanks!

mgerhardy commented 2 years ago

Thanks a lot - that should help ;)