From e427eab5ff0324b286e649dad3871d27cde8425f Mon Sep 17 00:00:00 2001 From: Harvey Fong Date: Wed, 9 Apr 2025 20:28:39 -0600 Subject: [PATCH] first working ogt mesh, added --bevel, --mode:mesh --- README.md | 7 +- oomer_voxel_ogt.h | 236 +++++++++++++++++++++++++++++++++++++++++++++ oomer_voxel_vmax.h | 106 +++++++++++++++++++- vmax2bella.cpp | 210 ++++++++++++++++++++++++++++------------ 4 files changed, 492 insertions(+), 67 deletions(-) create mode 100644 oomer_voxel_ogt.h diff --git a/README.md b/README.md index 6e0f268..73922ec 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ workdir/ ├── bella_scene_sdk/ ├── libplist/ ├── lzfse/ -└── vox2bella/ +├── opengametools/ +└── vmax2bella/ ``` # MacOS @@ -42,6 +43,7 @@ cd libplist make -j4 install_name_tool -id @rpath/libplist-2.0.4.dylib src/.libs/libplist-2.0.4.dylib cd .. +git clone https://github.com/jpaver/opengametools.git git clone https://github.com/oomer/vmax2bella.git cd vmax2bella make @@ -62,6 +64,7 @@ cd libplist ./autogen.sh --prefix=$PWD/install --without-cython make -j4 cd .. +git clone https://github.com/jpaver/opengametools.git git clone https://github.com/oomer/vmax2bella.git cd vmax2bella make @@ -74,7 +77,7 @@ make ``` mkdir workdir git clone https://github.com/lzfse/lzfse - +git clone https://github.com/jpaver/opengametools.git git clone https://github.com/oomer/vmax2bella.git cd vmax2bella msbuild vox2bella.vcxproj /p:Configuration=release /p:Platform=x64 /p:PlatformToolset=v143 diff --git a/oomer_voxel_ogt.h b/oomer_voxel_ogt.h new file mode 100644 index 0000000..4ce22ae --- /dev/null +++ b/oomer_voxel_ogt.h @@ -0,0 +1,236 @@ +// oomer wrapper code for opengametools voxel conversion + +#pragma once + +#include "oomer_voxel_vmax.h" + +#include +#include +#include +#include +#include +#include + +#include "../opengametools/src/ogt_vox.h" + +// Only define implementation once to avoid redefinition errors +#ifndef OGT_VOXEL_MESHIFY_IMPLEMENTATION +#define OGT_VOXEL_MESHIFY_IMPLEMENTATION +#endif +#include "../opengametools/src/ogt_voxel_meshify.h" + +// Convert a VmaxModel to an ogt_vox_model +// Note: The returned ogt_vox_model must be freed using ogt_vox_free when no longer needed +/*ogt_vox_model* convert_vmax_to_ogt_vox(const VmaxModel& vmaxModel) { + // Use max dimensions from vmaxModel + // Add 1 to get the actual size (since coordinates are 0-based) + uint32_t size_x = vmaxModel.maxx+1; + uint32_t size_y = vmaxModel.maxy+1; + uint32_t size_z = vmaxModel.maxz+1; + + // Add some safety checks + if (size_x > 256 || size_y > 256 || size_z > 256) { + std::cout << "Warning: Model dimensions exceed 256 limit. Clamping to 256." << std::endl; + size_x = std::min(size_x, 256u); + size_y = std::min(size_y, 256u); + size_z = std::min(size_z, 256u); + } + if (size_x == 0 || size_y == 0 || size_z == 0) { + std::cout << "Error: Model has zero dimensions. Setting minimum size of 1x1x1." << std::endl; + size_x = std::max(size_x, 1u); + size_y = std::max(size_y, 1u); + size_z = std::max(size_z, 1u); + } + // Create the voxel data array (initialized to 0, which means empty in ogt_vox) + size_t voxel_count = size_x * size_y * size_z; + uint8_t* voxel_data = (uint8_t*)ogt_vox_malloc(voxel_count); + if (!voxel_data) { + std::cout << "Error: Failed to allocate memory for voxel data" << std::endl; + return nullptr; + } + memset(voxel_data, 0, voxel_count); // Initialize all to 0 (empty) + + // Fill the voxel data array with color indices + int voxel_count_populated = 0; + for (uint32_t x = 0; x < size_x; x++) { + for (uint32_t y = 0; y < size_y; y++) { + for (uint32_t z = 0; z < size_z; z++) { + // Calculate the index in the 1D array + size_t index = x + (y * size_x) + (z * size_x * size_y); + + if (index < voxel_count) { + try { + // Use the new hasVoxelsAt and getVoxelsAt methods instead of directly accessing voxelsSpatial + if (vmaxModel.hasVoxelsAt(x, y, z)) { + const std::vector& voxels = vmaxModel.getVoxelsAt(x, y, z); + if (!voxels.empty()) { + uint8_t palette_index = voxels[0].palette; + if (palette_index == 0) palette_index = 1; // If palette is 0, use 1 instead to make it visible + voxel_data[index] = palette_index; + voxel_count_populated++; + } + } + // If no voxels at this position, it remains 0 (empty) + } + catch (const std::exception& e) { + std::cout << "ERROR: Exception accessing voxels at [" << x << "][" << y << "][" << z + << "]: " << e.what() << std::endl; + ogt_vox_free(voxel_data); + return nullptr; + } + } + } + } + } + + // Create and initialize the ogt_vox_model + ogt_vox_model* model = (ogt_vox_model*)ogt_vox_malloc(sizeof(ogt_vox_model)); + if (!model) { + std::cout << "Error: Failed to allocate memory for ogt_vox_model" << std::endl; + ogt_vox_free(voxel_data); + return nullptr; + } + + model->size_x = size_x; + model->size_y = size_y; + model->size_z = size_z; + model->voxel_data = voxel_data; + + // Calculate a simple hash for the voxel data + uint32_t hash = 0; + for (size_t i = 0; i < voxel_count; i++) { + hash = hash * 65599 + voxel_data[i]; + } + model->voxel_hash = hash; + + return model; +} +*/ + + +// Convert a vector of VmaxVoxel to an ogt_vox_model +// Note: The returned ogt_vox_model must be freed using ogt_vox_free when no longer needed +ogt_vox_model* convert_voxelsoftype_to_ogt_vox(const std::vector& voxelsOfType) { + // Find the maximum dimensions from the voxels + uint32_t size_x = 0; + uint32_t size_y = 0; + uint32_t size_z = 0; + + // WARNING must add 1 to each dimension + // because voxel coordinates are 0-based + for (const auto& voxel : voxelsOfType) { + size_x = std::max(size_x, static_cast(voxel.x)+1); // this seems wasteful + size_y = std::max(size_y, static_cast(voxel.y)+1); + size_z = std::max(size_z, static_cast(voxel.z)+1); + } + + // Add some safety checks + // This is a dense voxel model, so we need to make sure it's not too large + // todo use a sparse storage like morton + if (size_x > 256 || size_y > 256 || size_z > 256) { + std::cout << "Warning: Model dimensions exceed 256 limit. Clamping to 256." << std::endl; + size_x = std::min(size_x, 256u); + size_y = std::min(size_y, 256u); + size_z = std::min(size_z, 256u); + } + if (size_x == 0 || size_y == 0 || size_z == 0) { + std::cout << "Error: Model has zero dimensions. Setting minimum size of 1x1x1." << std::endl; + size_x = std::max(size_x, 1u); + size_y = std::max(size_y, 1u); + size_z = std::max(size_z, 1u); + } + + // Create the voxel data array (initialized to 0, which means empty in ogt_vox) + size_t voxel_count = size_x * size_y * size_z; + uint8_t* voxel_data = (uint8_t*)ogt_vox_malloc(voxel_count); + if (!voxel_data) { + std::cout << "Error: Failed to allocate memory for voxel data" << std::endl; + return nullptr; + } + memset(voxel_data, 0, voxel_count); // Initialize all to 0 (empty) + + // Fill the voxel data array with color indices + int voxel_count_populated = 0; + + // Loop through the vector of voxels directly + for (const auto& voxel : voxelsOfType) { + // Get the coordinates and palette + uint32_t x = voxel.x; + uint32_t y = voxel.y; + uint32_t z = voxel.z; + + // Skip voxels outside our valid range + if (x >= size_x || y >= size_y || z >= size_z) + continue; + + // Calculate the index in the 1D array + size_t index = x + (y * size_x) + (z * size_x * size_y); + + if (index < voxel_count) { + uint8_t palette_index = 0; // hardcoded for now + if (palette_index == 0) palette_index = 1; // If palette is 0, use 1 instead to make it visible + voxel_data[index] = palette_index; + voxel_count_populated++; + } + } + + // Create the model + ogt_vox_model* model = (ogt_vox_model*)ogt_vox_malloc(sizeof(ogt_vox_model)); + if (!model) { + std::cout << "Error: Failed to allocate memory for model" << std::endl; + ogt_vox_free(voxel_data); + return nullptr; + } + + model->size_x = size_x; + model->size_y = size_y; + model->size_z = size_z; + model->voxel_data = voxel_data; + + // Calculate a simple hash for the voxel data + uint32_t hash = 0; + for (size_t i = 0; i < voxel_count; i++) { + hash = hash * 65599 + voxel_data[i]; + } + model->voxel_hash = hash; + + return model; +} + +// Free resources allocated for an ogt_vox_model created by convert_vmax_to_ogt_vox +void free_ogt_vox_model(ogt_vox_model* model) { + if (model) { + if (model->voxel_data) { + ogt_vox_free((void*)model->voxel_data); + } + ogt_vox_free(model); + } +} + +// Free resources allocated for an ogt_vox_scene created by create_ogt_vox_scene_from_vmax +/*void free_ogt_vox_scene(ogt_vox_scene* scene) { + if (scene) { + // Free each model + for (uint32_t i = 0; i < scene->num_models; i++) { + free_ogt_vox_model((ogt_vox_model*)scene->models[i]); + } + + // Free pointers + if (scene->models) ogt_vox_free((void*)scene->models); + if (scene->instances) ogt_vox_free((void*)scene->instances); + if (scene->layers) ogt_vox_free((void*)scene->layers); + + // Free the scene itself + ogt_vox_free(scene); + } +} +*/ + +// Custom allocator functions for ogt_voxel_meshify +static void* voxel_meshify_malloc(size_t size, void* user_data) { + return malloc(size); +} + +static void voxel_meshify_free(void* ptr, void* user_data) { + free(ptr); +} diff --git a/oomer_voxel_vmax.h b/oomer_voxel_vmax.h index 72f3098..80f8d64 100644 --- a/oomer_voxel_vmax.h +++ b/oomer_voxel_vmax.h @@ -233,7 +233,17 @@ struct VmaxMaterial { bool volumetric; // future use }; +struct VmaxVoxelGrid { + // dimensions of the voxel grid + uint32_t size_x, size_y, size_z; + // voxel data + uint8_t* voxel_data; +}; + + // Create a structure to represent a model with its voxels with helper functions +// since the xyz coords are at the voxel level, we need an accessor to walk it sequentially +// maybe I create a new structure called VmaxVoxelGrid struct VmaxModel { // Model identifier or name std::string vmaxbFileName; // file name is used like a key @@ -243,32 +253,118 @@ struct VmaxModel { // Second dimension: color (1-255, index 0 unused since color 0 means no voxel) std::vector voxels[8][256]; + // EDUCATIONAL NOTES ON DUAL DATA STRUCTURES FOR VOXELS: + // ---------------------------------------------------- + // This class uses two different data structures to store the same voxel data, + // each optimized for different access patterns: + // + // 1. voxels[8][256] - Organizes voxels by material and color + // - Efficient for queries like "give me all voxels of material 2, color 37" + // - Poor for spatial queries like "what's at position (x,y,z)?" + // + // 2. voxelsSpatial - Organizes voxels by their spatial position + // - Efficient for spatial queries like "what's at position (x,y,z)?" + // - Uses a map for memory efficiency with sparse data + // + // Tradeoffs: + // - Memory: We use more memory by storing voxels twice + // - Performance: We get optimal performance for both types of queries + // - Complexity: We need to maintain both structures in sync + // + // For novice programmers: This is a common technique in game/graphics programming + // where performance is critical. We're trading some extra memory for faster access. + + // Using a map for sparse 3D data instead of a fixed-size 3D array + // Key format: (x << 16) | (y << 8) | z + // This approach is memory-efficient for sparse data (mostly empty space) + // A 3D array would need 256³ = 16.7 million elements even if most are empty! + + + // todo we need to do morton decoing using chunkid to get x,y,z + std::map> voxelsSpatial; + // Each model has local 0-7 materials std::array materials; // Each model has local colors std::array colors; + uint8_t maxx=0, maxy=0, maxz=0; // Constructor VmaxModel(const std::string& modelName) : vmaxbFileName(modelName) { } + // Helper function to create a key for the voxelsSpatial map + static uint32_t makeVoxelKey(uint8_t x, uint8_t y, uint8_t z) { + return (static_cast(x) << 16) | (static_cast(y) << 8) | static_cast(z); + } + // Add a voxel to this model + // EDUCATIONAL NOTE: + // Notice how we maintain BOTH data structures when adding a voxel. + // This is a key practice when using dual data structures - keep them in sync + // by updating both whenever data changes. void addVoxel(int x, int y, int z, int material, int color, int chunk, int chunkMin) { + + //todo add chunk offset to x,y,z + //chunkMin is offset withing each chunk used earlier + uint32_t _tempx, _tempy, _tempz; + decodeMorton3DOptimized(chunk, _tempx, _tempy, _tempz); // index IS the morton code + int worldOffsetX = _tempx * 24; // get world loc within 256x256x256 grid + int worldOffsetY = _tempy * 24; // Don't know why we need to multiply by 24 + int worldOffsetZ = _tempz * 24; // use to be 32 + x += worldOffsetX; + y += worldOffsetY; + z += worldOffsetZ; + + if (material >= 0 && material < 8 && color > 0 && color < 256) { voxels[material][color].emplace_back(x, y, z, material, color, chunk, chunkMin); + + // Add to voxelsSpatial using the map approach + uint32_t key = makeVoxelKey(x, y, z); + //todo add chunk offset to x,y,z + voxelsSpatial[key].emplace_back(x, y, z, material, color, chunk, chunkMin); + + if (x > maxx) maxx = x; + if (y > maxy) maxy = y; + if (z > maxz) maxz = z; } } - - // Add a materials to this model + + // Get voxels at a specific position + // EDUCATIONAL NOTE: + // This method demonstrates the power of our spatial index. + // Time complexity: O(log n) where n is the number of occupied positions. + // Without voxelsSpatial, we would need to scan through ALL voxels (potentially thousands) + // to find those at a specific position, which would be O(total_voxel_count). + const std::vector& getVoxelsAt(uint8_t x, uint8_t y, uint8_t z) const { + uint32_t key = makeVoxelKey(x, y, z); + auto it = voxelsSpatial.find(key); + if (it != voxelsSpatial.end()) { + return it->second; + } + static const std::vector empty; + return empty; + } + + // Check if there are voxels at a specific position + // Another spatial query that benefits from our map-based structure + bool hasVoxelsAt(uint8_t x, uint8_t y, uint8_t z) const { + uint32_t key = makeVoxelKey(x, y, z); + auto it = voxelsSpatial.find(key); + return (it != voxelsSpatial.end() && !it->second.empty()); + } + + // Add materials to this model void addMaterials(const std::array newMaterials) { materials = newMaterials; } - // Add a colors to this model + // Add colors to this model void addColors(const std::array newColors) { colors = newColors; } - + // Get all voxels of a specific material and color const std::vector& getVoxels(int material, int color) const { if (material >= 0 && material < 8 && color > 0 && color < 256) { @@ -288,7 +384,7 @@ struct VmaxModel { } return count; } - + // Get a map of used materials and their associated colors std::map> getUsedMaterialsAndColors() const { std::map> result; diff --git a/vmax2bella.cpp b/vmax2bella.cpp index 505545a..8dc039a 100644 --- a/vmax2bella.cpp +++ b/vmax2bella.cpp @@ -178,13 +178,22 @@ The 't' field in the snapshot's 's.id' dictionary indicates the type of snapshot #include "oomer_voxel_vmax.h" // common vmax voxel code and structures #include "oomer_misc.h" // common misc code +#include "oomer_voxel_ogt.h" // common opengametools voxel conversion wrappers + +#define OGT_VOX_IMPLEMENTATION +#include "../opengametools/src/ogt_vox.h" + dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene); -dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld, const VmaxModel& vmaxModel, const std::vector& vmaxPalette, const std::array& vmaxMaterial); +dl::bella_sdk::Node addModelToScene(dl::Args& args, dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld, const VmaxModel& vmaxModel, const std::vector& vmaxPalette, const std::array& vmaxMaterial); +dl::bella_sdk::Node add_ogt_mesh_to_scene(dl::String bellaName, ogt_mesh* ogtMesh, dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld ); int DL_main(dl::Args& args) { args.add("i", "input", "", "vmax directory or vmax.zip file"); args.add("o", "output", "", "set output bella file name"); + args.add("mo", "mode", "", "mode for output, mesh, voxel, or both"); + args.add("mt", "meshtype", "", "meshtype classic, greedy, other"); + args.add("be", "bevel", "", "add bevel to material"); args.add("tp", "thirdparty", "", "prints third party licenses"); args.add("li", "licenseinfo", "", "prints license info"); @@ -211,9 +220,11 @@ int DL_main(dl::Args& args) { if (args.have("--input")) { dl::String bszName; + dl::String objName; dl::String vmaxDirName; vmaxDirName = args.value("--input"); bszName = vmaxDirName.replace("vmax", "bsz"); + objName = vmaxDirName.replace("vmax", "obj"); // Create a new scene dl::bella_sdk::Scene belScene; @@ -294,8 +305,6 @@ int DL_main(dl::Args& args) { std::vector allModels; std::vector> vmaxPalettes; // one palette per model std::vector> vmaxMaterials; // one material per model - //std::vector> allMaterials; // one material per model - //std::vector> allPalettes; essentialsToScene(belScene); // create the basic scene elements in Bella @@ -311,19 +320,6 @@ int DL_main(dl::Args& args) { std::vector scale = jsonModelInfo.scale; std::vector extentCenter = jsonModelInfo.extentCenter; - - /*VmaxMatrix4x4 modelMat4 = combineVmaxTransforms(rotation[0], - rotation[1], - rotation[2], - rotation[3], - position[0], - position[1], - position[2], - scale[0], - scale[1], - scale[2]);*/ - //modelMatrix = scaleMatrix * modelMatrix * transMatrix; - // Get file names dl::String materialName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str(); materialName = materialName.replace(".png", ".settings.vmaxpsb"); @@ -381,8 +377,13 @@ int DL_main(dl::Args& args) { std::cout << modelIndex << "Model: " << eachModel.vmaxbFileName << std::endl; std::cout << "Voxel Count Model: " << eachModel.getTotalVoxelCount() << std::endl; - dl::bella_sdk::Node belModel = addModelToScene(belScene, belWorld, eachModel, vmaxPalettes[modelIndex], vmaxMaterials[modelIndex]); - // TODO add to a map of canonical models + dl::bella_sdk::Node belModel = addModelToScene( args, + belScene, + belWorld, + eachModel, + vmaxPalettes[modelIndex], + vmaxMaterials[modelIndex]); + // TODO add to a map00000 of canonical models dl::String lllmodelName = dl::String(eachModel.vmaxbFileName.c_str()); dl::String lllcanonicalName = lllmodelName.replace(".vmaxb", ""); std::cout << "========lllcanonicalName: " << lllcanonicalName.buf() << std::endl; @@ -402,12 +403,12 @@ int DL_main(dl::Args& args) { std::vector extentCenter = jsonModelInfo.extentCenter; auto jsonParentId = jsonModelInfo.parentId; auto belParentId = dl::String(jsonParentId.c_str()); - dl::String belParentGroupUUID = belParentId.replace("-", "_"); - belParentGroupUUID = "_" + belParentGroupUUID; + dl::String belParentGroupUUID = belParentId.replace("-", "_"); // Make sure the group name is valid for a Bella node name + belParentGroupUUID = "_" + belParentGroupUUID; // Make sure the group name is valid for a Bella node name auto belObjectId = dl::String(jsonModelInfo.id.c_str()); - belObjectId = belObjectId.replace("-", "_"); - belObjectId = "_" + belObjectId; + belObjectId = belObjectId.replace("-", "_"); // Make sure the object name is valid for a Bella node name + belObjectId = "_" + belObjectId; // Make sure the object name is valid for a Bella node name dl::String getCanonicalName = dl::String(jsonModelInfo.dataFile.c_str()); dl::String canonicalName = getCanonicalName.replace(".vmaxb", ""); @@ -415,7 +416,6 @@ int DL_main(dl::Args& args) { auto belCanonicalNode = belCanonicalNodes[canonicalName.buf()]; auto foofoo = belScene.findNode(canonicalName); - VmaxMatrix4x4 objectMat4 = combineVmaxTransforms(rotation[0], rotation[1], rotation[2], @@ -438,7 +438,8 @@ int DL_main(dl::Args& args) { if (jsonParentId == "") { belNodeObjectInstance.parentTo(belScene.world()); } else { - belNodeObjectInstance.parentTo(belGroupNodes[belParentGroupUUID]); + dl::bella_sdk::Node myParentGroup = belGroupNodes[belParentGroupUUID]; // Get bella obj + belNodeObjectInstance.parentTo(myParentGroup); // Group underneath a group } foofoo.parentTo(belNodeObjectInstance); } @@ -493,7 +494,7 @@ dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene) { belColorDome["horizon"] = dl::Rgba{.85f, 0.76f, 0.294f, 1.0f}; belColorDome["altitude"] = 14.0f; // Configure ground plane - belGroundPlane["elevation"] = -.5f; + //belGroundPlane["elevation"] = -.5f; belGroundPlane["material"] = belGroundMat; /* Commented out: Sun configuration @@ -522,6 +523,10 @@ dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene) { auto belLiqVoxelForm = belScene.createNode("xform","oomerLiqVoxelXform","oomerLiqVoxelXform"); auto belVoxelMat = belScene.createNode("orenNayar","oomerVoxelMat","oomerVoxelMat"); auto belMeshVoxel = belScene.createNode("mesh", "oomerMeshVoxel"); + auto belBevel = belScene.createNode("bevel", "oomerBevel"); + belBevel["radius"] = 90.0f; + belBevel["samples"] =dl::UInt(6); + #include "resources/smoothcube.h" // Configure voxel box dimensions belVoxel["radius"] = 0.33f; @@ -554,7 +559,12 @@ dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene) { // The datastream contains the voxels for the snapshot // The voxels are stored in chunks, each chunk is 8x8x8 voxels // The chunks are stored in a morton order -dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld, const VmaxModel& vmaxModel, const std::vector& vmaxPalette, const std::array& vmaxMaterial) { +dl::bella_sdk::Node addModelToScene(dl::Args& args, + dl::bella_sdk::Scene& belScene, + dl::bella_sdk::Node& belWorld, + const VmaxModel& vmaxModel, + const std::vector& vmaxPalette, + const std::array& vmaxMaterial) { // Create Bella scene nodes for each voxel int i = 0; dl::String modelName = dl::String(vmaxModel.vmaxbFileName.c_str()); @@ -568,30 +578,31 @@ dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sd auto belMeshVoxel = belScene.findNode("oomerMeshVoxel"); auto belVoxelForm = belScene.findNode("oomerVoxelXform"); auto belLiqVoxelForm = belScene.findNode("oomerLiqVoxelXform"); + auto belBevel = belScene.findNode("oomerBevel"); auto modelXform = belScene.createNode("xform", canonicalName, canonicalName); modelXform["steps"][0]["xform"] = dl::Mat4 {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1}; for (const auto& [material, colorID] : vmaxModel.getUsedMaterialsAndColors()) { for (int color : colorID) { - auto belInstancer = belScene.createNode("instancer", - canonicalName + dl::String("Material") + dl::String(material) + dl::String("Color") + dl::String(color)); - auto xformsArray = dl::ds::Vector(); - belInstancer["steps"][0]["xform"] = dl::Mat4 {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1}; - belInstancer.parentTo(modelXform); + + auto thisname = canonicalName + dl::String("Material") + dl::String(material) + dl::String("Color") + dl::String(color); auto belMaterial = belScene.createNode("quickMaterial", canonicalName + dl::String("vmaxMat") + dl::String(material) + dl::String("Color") + dl::String(color)); - + bool isMesh = false; + bool isBox = true; if(material==7) { belMaterial["type"] = "liquid"; //belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; - belMaterial["liquidDepth"] = 100.0f; - belMaterial["ior"] = 1.11f; + belMaterial["liquidDepth"] = 300.0f; + belMaterial["liquidIor"] = 1.33f; + isMesh = true; + isBox = false; } else if(material==6 || vmaxPalette[color-1].a < 255) { belMaterial["type"] = "glass"; belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; - belMaterial["glassDepth"] = 200.0f; + belMaterial["glassDepth"] = 500.0f; } else if(vmaxMaterial[material].metalness > 0.1f) { belMaterial["type"] = "metal"; belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; @@ -606,7 +617,15 @@ dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sd belMaterial["type"] = "plastic"; belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; } - belInstancer["material"] = belMaterial; + + if (args.have("bevel")) { + belMaterial["bevel"] = belBevel; + } + if (args.have("mode") && args.value("mode") == "mesh" || args.value("mode") == "both") { + isMesh = true; + isBox = false; + } + // Convert 0-255 to 0-1 , remember to -1 color index becuase voxelmax needs 0 to indicate no voxel double bellaR = static_cast(vmaxPalette[color-1].r)/255.0; double bellaG = static_cast(vmaxPalette[color-1].g)/255.0; @@ -623,32 +642,68 @@ dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sd const std::vector& voxelsOfType = vmaxModel.getVoxels(material, color); int showchunk =0; - // Right now we group voxels by MatCol ie Mat0Col2 - // But voxels are stored in chunks with many colors - // Since we aren't grouping voxels in chunks, we need to traverse the voxels - // and offset each voxel by the morton decode of chunk index - for (const auto& eachvoxel : voxelsOfType) { - // Get chunk coordinates and world origin - uint32_t _tempx, _tempy, _tempz; - decodeMorton3DOptimized(eachvoxel.chunkID, _tempx, _tempy, _tempz); // index IS the morton code - int worldOffsetX = _tempx * 24; // get world loc within 256x256x256 grid - int worldOffsetY = _tempy * 24; // Don't know why we need to multiply by 24 - int worldOffsetZ = _tempz * 24; // use to be 32 - xformsArray.push_back( dl::Mat4f{ 1, 0, 0, 0, + if (isMesh) { + auto belMeshXform = belScene.createNode("xform", + thisname+dl::String("Xform")); + belMeshXform.parentTo(modelXform); + + std::cout << "Converting voxels to mesh\n"; + // Convert voxels of a particular color to ogt_vox_model + ogt_vox_model* ogt_model = convert_voxelsoftype_to_ogt_vox(voxelsOfType); + ogt_mesh_rgba* palette = new ogt_mesh_rgba[256]; // Create a palette array + for (int i = 0; i < 256; i++) { // Copy palette from Vmax to OGT + palette[i] = ogt_mesh_rgba{vmaxPalette[i].r, vmaxPalette[i].g, vmaxPalette[i].b, vmaxPalette[i].a}; + } + ogt_voxel_meshify_context ctx = {}; // Create a context struct, not a pointer, todo , what is this for + + // Convert ogt voxels to mesh + ogt_mesh* mesh = ogt_mesh_from_paletted_voxels_simple( &ctx, + ogt_model->voxel_data, + ogt_model->size_x, + ogt_model->size_y, + ogt_model->size_z, + palette ); + + if (voxelsOfType.size() > 0) { + auto belMesh = add_ogt_mesh_to_scene( thisname, + mesh, + belScene, + belWorld + ); + belMesh.parentTo(belMeshXform); + belMeshXform["material"] = belMaterial; + } else { + std::cout << "skipping" << color << "\n"; + } + } + if (isBox) { + auto belInstancer = belScene.createNode("instancer", + thisname); + auto xformsArray = dl::ds::Vector(); + belInstancer["steps"][0]["xform"] = dl::Mat4 {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1}; + belInstancer.parentTo(modelXform); + + std::cout << "Converting voxels to bella boxes\n"; + //WARNING we use to do morton decoding above but now VoxModel does it when addVoxel is called + // So we can just use the x,y,z values + for (const auto& eachvoxel : voxelsOfType) { + xformsArray.push_back( dl::Mat4f{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, - static_cast(eachvoxel.x + worldOffsetX), - static_cast(eachvoxel.y + worldOffsetY), - static_cast(eachvoxel.z + worldOffsetZ), 1 }); - } - belInstancer["steps"][0]["instances"] = xformsArray; - if(material==7) { - belLiqVoxel.parentTo(belInstancer); - } else { - belMeshVoxel.parentTo(belInstancer); - } - if(vmaxMaterial[material].emission > 0.0f) { - belVoxelForm.parentTo(belInstancer); + (static_cast(eachvoxel.x))+0.5f, // offset center of voxel to match mesh + (static_cast(eachvoxel.y))+0.5f, + (static_cast(eachvoxel.z))+0.5f, 1 }); + }; + belInstancer["steps"][0]["instances"] = xformsArray; + belInstancer["material"] = belMaterial; + if(material==7) { + belLiqVoxel.parentTo(belInstancer); + } else { + belMeshVoxel.parentTo(belInstancer); + } + if(vmaxMaterial[material].emission > 0.0f) { + belVoxelForm.parentTo(belInstancer); + } } } } @@ -656,3 +711,38 @@ dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sd } return dl::bella_sdk::Node(); } + +dl::bella_sdk::Node add_ogt_mesh_to_scene(dl::String name, ogt_mesh* meshmesh, dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld ) { + + //auto ogtXform = belScene.createNode("xform", name+"ogtXform", name+"ogtXform"); + //ogtXform["steps"][0]["xform"] = dl::Mat4 {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1}; + //ogtXform.parentTo(belWorld); + auto ogtMesh = belScene.createNode("mesh", name+"ogtmesh", name+"ogtmesh"); + ogtMesh["normals"] = "flat"; + // Add vertices and faces to the mesh + dl::ds::Vector verticesArray; + for (uint32_t i = 0; i < meshmesh->vertex_count; i++) { + const auto& vertex = meshmesh->vertices[i]; + uint32_t xx = static_cast(vertex.pos.x); + uint32_t yy = static_cast(vertex.pos.y); + uint32_t zz = static_cast(vertex.pos.z); + verticesArray.push_back(dl::Pos3f{ static_cast(xx), + static_cast(yy), + static_cast(zz) }); + + } + + ogtMesh["steps"][0]["points"] = verticesArray; + + + dl::ds::Vector facesArray; + for (size_t i = 0; i < meshmesh->index_count; i+=3) { + facesArray.push_back(dl::Vec4u{ static_cast(meshmesh->indices[i]), + static_cast(meshmesh->indices[i+1]), + static_cast(meshmesh->indices[i+2]), + static_cast(meshmesh->indices[i+2]) }); + } + ogtMesh["polygons"] = facesArray; + + return ogtMesh; +} \ No newline at end of file