first working ogt mesh, added --bevel, --mode:mesh

This commit is contained in:
Harvey Fong 2025-04-09 20:28:39 -06:00
parent c7da7c0a6f
commit e427eab5ff
4 changed files with 492 additions and 67 deletions

View File

@ -18,7 +18,8 @@ workdir/
├── bella_scene_sdk/ ├── bella_scene_sdk/
├── libplist/ ├── libplist/
├── lzfse/ ├── lzfse/
└── vox2bella/ ├── opengametools/
└── vmax2bella/
``` ```
# MacOS # MacOS
@ -42,6 +43,7 @@ cd libplist
make -j4 make -j4
install_name_tool -id @rpath/libplist-2.0.4.dylib src/.libs/libplist-2.0.4.dylib install_name_tool -id @rpath/libplist-2.0.4.dylib src/.libs/libplist-2.0.4.dylib
cd .. cd ..
git clone https://github.com/jpaver/opengametools.git
git clone https://github.com/oomer/vmax2bella.git git clone https://github.com/oomer/vmax2bella.git
cd vmax2bella cd vmax2bella
make make
@ -62,6 +64,7 @@ cd libplist
./autogen.sh --prefix=$PWD/install --without-cython ./autogen.sh --prefix=$PWD/install --without-cython
make -j4 make -j4
cd .. cd ..
git clone https://github.com/jpaver/opengametools.git
git clone https://github.com/oomer/vmax2bella.git git clone https://github.com/oomer/vmax2bella.git
cd vmax2bella cd vmax2bella
make make
@ -74,7 +77,7 @@ make
``` ```
mkdir workdir mkdir workdir
git clone https://github.com/lzfse/lzfse git clone https://github.com/lzfse/lzfse
git clone https://github.com/jpaver/opengametools.git
git clone https://github.com/oomer/vmax2bella.git git clone https://github.com/oomer/vmax2bella.git
cd vmax2bella cd vmax2bella
msbuild vox2bella.vcxproj /p:Configuration=release /p:Platform=x64 /p:PlatformToolset=v143 msbuild vox2bella.vcxproj /p:Configuration=release /p:Platform=x64 /p:PlatformToolset=v143

236
oomer_voxel_ogt.h Normal file
View File

@ -0,0 +1,236 @@
// oomer wrapper code for opengametools voxel conversion
#pragma once
#include "oomer_voxel_vmax.h"
#include <vector>
#include <string>
#include <stdio.h>
#include <stdint.h>
#include <cstdlib>
#include <cstring>
#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<VmaxVoxel>& 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<VmaxVoxel>& 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<uint32_t>(voxel.x)+1); // this seems wasteful
size_y = std::max(size_y, static_cast<uint32_t>(voxel.y)+1);
size_z = std::max(size_z, static_cast<uint32_t>(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);
}

View File

@ -233,7 +233,17 @@ struct VmaxMaterial {
bool volumetric; // future use 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 // 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 { struct VmaxModel {
// Model identifier or name // Model identifier or name
std::string vmaxbFileName; // file name is used like a key std::string vmaxbFileName; // file name is used like a key
@ -243,28 +253,114 @@ struct VmaxModel {
// Second dimension: color (1-255, index 0 unused since color 0 means no voxel) // Second dimension: color (1-255, index 0 unused since color 0 means no voxel)
std::vector<VmaxVoxel> voxels[8][256]; std::vector<VmaxVoxel> 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<uint32_t, std::vector<VmaxVoxel>> voxelsSpatial;
// Each model has local 0-7 materials // Each model has local 0-7 materials
std::array<VmaxMaterial, 8> materials; std::array<VmaxMaterial, 8> materials;
// Each model has local colors // Each model has local colors
std::array<VmaxRGBA, 256> colors; std::array<VmaxRGBA, 256> colors;
uint8_t maxx=0, maxy=0, maxz=0;
// Constructor // Constructor
VmaxModel(const std::string& modelName) : vmaxbFileName(modelName) { 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<uint32_t>(x) << 16) | (static_cast<uint32_t>(y) << 8) | static_cast<uint32_t>(z);
}
// Add a voxel to this model // 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) { 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) { if (material >= 0 && material < 8 && color > 0 && color < 256) {
voxels[material][color].emplace_back(x, y, z, material, color, chunk, chunkMin); 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<VmaxVoxel>& 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<VmaxVoxel> 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<VmaxMaterial, 8> newMaterials) { void addMaterials(const std::array<VmaxMaterial, 8> newMaterials) {
materials = newMaterials; materials = newMaterials;
} }
// Add a colors to this model // Add colors to this model
void addColors(const std::array<VmaxRGBA, 256> newColors) { void addColors(const std::array<VmaxRGBA, 256> newColors) {
colors = newColors; colors = newColors;
} }

View File

@ -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_voxel_vmax.h" // common vmax voxel code and structures
#include "oomer_misc.h" // common misc code #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 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<VmaxRGBA>& vmaxPalette, const std::array<VmaxMaterial, 8>& 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<VmaxRGBA>& vmaxPalette, const std::array<VmaxMaterial, 8>& 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) { int DL_main(dl::Args& args) {
args.add("i", "input", "", "vmax directory or vmax.zip file"); args.add("i", "input", "", "vmax directory or vmax.zip file");
args.add("o", "output", "", "set output bella file name"); 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("tp", "thirdparty", "", "prints third party licenses");
args.add("li", "licenseinfo", "", "prints license info"); args.add("li", "licenseinfo", "", "prints license info");
@ -211,9 +220,11 @@ int DL_main(dl::Args& args) {
if (args.have("--input")) if (args.have("--input"))
{ {
dl::String bszName; dl::String bszName;
dl::String objName;
dl::String vmaxDirName; dl::String vmaxDirName;
vmaxDirName = args.value("--input"); vmaxDirName = args.value("--input");
bszName = vmaxDirName.replace("vmax", "bsz"); bszName = vmaxDirName.replace("vmax", "bsz");
objName = vmaxDirName.replace("vmax", "obj");
// Create a new scene // Create a new scene
dl::bella_sdk::Scene belScene; dl::bella_sdk::Scene belScene;
@ -294,8 +305,6 @@ int DL_main(dl::Args& args) {
std::vector<VmaxModel> allModels; std::vector<VmaxModel> allModels;
std::vector<std::vector<VmaxRGBA>> vmaxPalettes; // one palette per model std::vector<std::vector<VmaxRGBA>> vmaxPalettes; // one palette per model
std::vector<std::array<VmaxMaterial, 8>> vmaxMaterials; // one material per model std::vector<std::array<VmaxMaterial, 8>> vmaxMaterials; // one material per model
//std::vector<std::array<VmaxMaterial, 8>> allMaterials; // one material per model
//std::vector<std::vector<VmaxRGBA>> allPalettes;
essentialsToScene(belScene); // create the basic scene elements in Bella essentialsToScene(belScene); // create the basic scene elements in Bella
@ -311,19 +320,6 @@ int DL_main(dl::Args& args) {
std::vector<double> scale = jsonModelInfo.scale; std::vector<double> scale = jsonModelInfo.scale;
std::vector<double> extentCenter = jsonModelInfo.extentCenter; std::vector<double> 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 // Get file names
dl::String materialName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str(); dl::String materialName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str();
materialName = materialName.replace(".png", ".settings.vmaxpsb"); 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 << modelIndex << "Model: " << eachModel.vmaxbFileName << std::endl;
std::cout << "Voxel Count Model: " << eachModel.getTotalVoxelCount() << std::endl; std::cout << "Voxel Count Model: " << eachModel.getTotalVoxelCount() << std::endl;
dl::bella_sdk::Node belModel = addModelToScene(belScene, belWorld, eachModel, vmaxPalettes[modelIndex], vmaxMaterials[modelIndex]); dl::bella_sdk::Node belModel = addModelToScene( args,
// TODO add to a map of canonical models 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 lllmodelName = dl::String(eachModel.vmaxbFileName.c_str());
dl::String lllcanonicalName = lllmodelName.replace(".vmaxb", ""); dl::String lllcanonicalName = lllmodelName.replace(".vmaxb", "");
std::cout << "========lllcanonicalName: " << lllcanonicalName.buf() << std::endl; std::cout << "========lllcanonicalName: " << lllcanonicalName.buf() << std::endl;
@ -402,12 +403,12 @@ int DL_main(dl::Args& args) {
std::vector<double> extentCenter = jsonModelInfo.extentCenter; std::vector<double> extentCenter = jsonModelInfo.extentCenter;
auto jsonParentId = jsonModelInfo.parentId; auto jsonParentId = jsonModelInfo.parentId;
auto belParentId = dl::String(jsonParentId.c_str()); auto belParentId = dl::String(jsonParentId.c_str());
dl::String belParentGroupUUID = belParentId.replace("-", "_"); dl::String belParentGroupUUID = belParentId.replace("-", "_"); // Make sure the group name is valid for a Bella node name
belParentGroupUUID = "_" + belParentGroupUUID; belParentGroupUUID = "_" + belParentGroupUUID; // Make sure the group name is valid for a Bella node name
auto belObjectId = dl::String(jsonModelInfo.id.c_str()); auto belObjectId = dl::String(jsonModelInfo.id.c_str());
belObjectId = belObjectId.replace("-", "_"); belObjectId = belObjectId.replace("-", "_"); // Make sure the object name is valid for a Bella node name
belObjectId = "_" + belObjectId; 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 getCanonicalName = dl::String(jsonModelInfo.dataFile.c_str());
dl::String canonicalName = getCanonicalName.replace(".vmaxb", ""); dl::String canonicalName = getCanonicalName.replace(".vmaxb", "");
@ -415,7 +416,6 @@ int DL_main(dl::Args& args) {
auto belCanonicalNode = belCanonicalNodes[canonicalName.buf()]; auto belCanonicalNode = belCanonicalNodes[canonicalName.buf()];
auto foofoo = belScene.findNode(canonicalName); auto foofoo = belScene.findNode(canonicalName);
VmaxMatrix4x4 objectMat4 = combineVmaxTransforms(rotation[0], VmaxMatrix4x4 objectMat4 = combineVmaxTransforms(rotation[0],
rotation[1], rotation[1],
rotation[2], rotation[2],
@ -438,7 +438,8 @@ int DL_main(dl::Args& args) {
if (jsonParentId == "") { if (jsonParentId == "") {
belNodeObjectInstance.parentTo(belScene.world()); belNodeObjectInstance.parentTo(belScene.world());
} else { } 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); 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["horizon"] = dl::Rgba{.85f, 0.76f, 0.294f, 1.0f};
belColorDome["altitude"] = 14.0f; belColorDome["altitude"] = 14.0f;
// Configure ground plane // Configure ground plane
belGroundPlane["elevation"] = -.5f; //belGroundPlane["elevation"] = -.5f;
belGroundPlane["material"] = belGroundMat; belGroundPlane["material"] = belGroundMat;
/* Commented out: Sun configuration /* 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 belLiqVoxelForm = belScene.createNode("xform","oomerLiqVoxelXform","oomerLiqVoxelXform");
auto belVoxelMat = belScene.createNode("orenNayar","oomerVoxelMat","oomerVoxelMat"); auto belVoxelMat = belScene.createNode("orenNayar","oomerVoxelMat","oomerVoxelMat");
auto belMeshVoxel = belScene.createNode("mesh", "oomerMeshVoxel"); 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" #include "resources/smoothcube.h"
// Configure voxel box dimensions // Configure voxel box dimensions
belVoxel["radius"] = 0.33f; 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 datastream contains the voxels for the snapshot
// The voxels are stored in chunks, each chunk is 8x8x8 voxels // The voxels are stored in chunks, each chunk is 8x8x8 voxels
// The chunks are stored in a morton order // 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<VmaxRGBA>& vmaxPalette, const std::array<VmaxMaterial, 8>& 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<VmaxRGBA>& vmaxPalette,
const std::array<VmaxMaterial, 8>& vmaxMaterial) {
// Create Bella scene nodes for each voxel // Create Bella scene nodes for each voxel
int i = 0; int i = 0;
dl::String modelName = dl::String(vmaxModel.vmaxbFileName.c_str()); 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 belMeshVoxel = belScene.findNode("oomerMeshVoxel");
auto belVoxelForm = belScene.findNode("oomerVoxelXform"); auto belVoxelForm = belScene.findNode("oomerVoxelXform");
auto belLiqVoxelForm = belScene.findNode("oomerLiqVoxelXform"); auto belLiqVoxelForm = belScene.findNode("oomerLiqVoxelXform");
auto belBevel = belScene.findNode("oomerBevel");
auto modelXform = belScene.createNode("xform", canonicalName, canonicalName); 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}; 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 (const auto& [material, colorID] : vmaxModel.getUsedMaterialsAndColors()) {
for (int color : colorID) { for (int color : colorID) {
auto belInstancer = belScene.createNode("instancer",
canonicalName + dl::String("Material") + dl::String(material) + dl::String("Color") + dl::String(color)); auto thisname = canonicalName + dl::String("Material") + dl::String(material) + dl::String("Color") + dl::String(color);
auto xformsArray = dl::ds::Vector<dl::Mat4f>();
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 belMaterial = belScene.createNode("quickMaterial", auto belMaterial = belScene.createNode("quickMaterial",
canonicalName + dl::String("vmaxMat") + dl::String(material) + dl::String("Color") + dl::String(color)); canonicalName + dl::String("vmaxMat") + dl::String(material) + dl::String("Color") + dl::String(color));
bool isMesh = false;
bool isBox = true;
if(material==7) { if(material==7) {
belMaterial["type"] = "liquid"; belMaterial["type"] = "liquid";
//belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; //belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
belMaterial["liquidDepth"] = 100.0f; belMaterial["liquidDepth"] = 300.0f;
belMaterial["ior"] = 1.11f; belMaterial["liquidIor"] = 1.33f;
isMesh = true;
isBox = false;
} else if(material==6 || vmaxPalette[color-1].a < 255) { } else if(material==6 || vmaxPalette[color-1].a < 255) {
belMaterial["type"] = "glass"; belMaterial["type"] = "glass";
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
belMaterial["glassDepth"] = 200.0f; belMaterial["glassDepth"] = 500.0f;
} else if(vmaxMaterial[material].metalness > 0.1f) { } else if(vmaxMaterial[material].metalness > 0.1f) {
belMaterial["type"] = "metal"; belMaterial["type"] = "metal";
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; 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["type"] = "plastic";
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f; 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 // Convert 0-255 to 0-1 , remember to -1 color index becuase voxelmax needs 0 to indicate no voxel
double bellaR = static_cast<double>(vmaxPalette[color-1].r)/255.0; double bellaR = static_cast<double>(vmaxPalette[color-1].r)/255.0;
double bellaG = static_cast<double>(vmaxPalette[color-1].g)/255.0; double bellaG = static_cast<double>(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<VmaxVoxel>& voxelsOfType = vmaxModel.getVoxels(material, color); const std::vector<VmaxVoxel>& voxelsOfType = vmaxModel.getVoxels(material, color);
int showchunk =0; int showchunk =0;
// Right now we group voxels by MatCol ie Mat0Col2 if (isMesh) {
// But voxels are stored in chunks with many colors auto belMeshXform = belScene.createNode("xform",
// Since we aren't grouping voxels in chunks, we need to traverse the voxels thisname+dl::String("Xform"));
// and offset each voxel by the morton decode of chunk index belMeshXform.parentTo(modelXform);
for (const auto& eachvoxel : voxelsOfType) {
// Get chunk coordinates and world origin std::cout << "Converting voxels to mesh\n";
uint32_t _tempx, _tempy, _tempz; // Convert voxels of a particular color to ogt_vox_model
decodeMorton3DOptimized(eachvoxel.chunkID, _tempx, _tempy, _tempz); // index IS the morton code ogt_vox_model* ogt_model = convert_voxelsoftype_to_ogt_vox(voxelsOfType);
int worldOffsetX = _tempx * 24; // get world loc within 256x256x256 grid ogt_mesh_rgba* palette = new ogt_mesh_rgba[256]; // Create a palette array
int worldOffsetY = _tempy * 24; // Don't know why we need to multiply by 24 for (int i = 0; i < 256; i++) { // Copy palette from Vmax to OGT
int worldOffsetZ = _tempz * 24; // use to be 32 palette[i] = ogt_mesh_rgba{vmaxPalette[i].r, vmaxPalette[i].g, vmaxPalette[i].b, vmaxPalette[i].a};
xformsArray.push_back( dl::Mat4f{ 1, 0, 0, 0, }
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<dl::Mat4f>();
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, 1, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
static_cast<float>(eachvoxel.x + worldOffsetX), (static_cast<float>(eachvoxel.x))+0.5f, // offset center of voxel to match mesh
static_cast<float>(eachvoxel.y + worldOffsetY), (static_cast<float>(eachvoxel.y))+0.5f,
static_cast<float>(eachvoxel.z + worldOffsetZ), 1 }); (static_cast<float>(eachvoxel.z))+0.5f, 1 });
} };
belInstancer["steps"][0]["instances"] = xformsArray; belInstancer["steps"][0]["instances"] = xformsArray;
if(material==7) { belInstancer["material"] = belMaterial;
belLiqVoxel.parentTo(belInstancer); if(material==7) {
} else { belLiqVoxel.parentTo(belInstancer);
belMeshVoxel.parentTo(belInstancer); } else {
} belMeshVoxel.parentTo(belInstancer);
if(vmaxMaterial[material].emission > 0.0f) { }
belVoxelForm.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(); 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<dl::Pos3f> verticesArray;
for (uint32_t i = 0; i < meshmesh->vertex_count; i++) {
const auto& vertex = meshmesh->vertices[i];
uint32_t xx = static_cast<uint32_t>(vertex.pos.x);
uint32_t yy = static_cast<uint32_t>(vertex.pos.y);
uint32_t zz = static_cast<uint32_t>(vertex.pos.z);
verticesArray.push_back(dl::Pos3f{ static_cast<float>(xx),
static_cast<float>(yy),
static_cast<float>(zz) });
}
ogtMesh["steps"][0]["points"] = verticesArray;
dl::ds::Vector<dl::Vec4u> facesArray;
for (size_t i = 0; i < meshmesh->index_count; i+=3) {
facesArray.push_back(dl::Vec4u{ static_cast<unsigned int>(meshmesh->indices[i]),
static_cast<unsigned int>(meshmesh->indices[i+1]),
static_cast<unsigned int>(meshmesh->indices[i+2]),
static_cast<unsigned int>(meshmesh->indices[i+2]) });
}
ogtMesh["polygons"] = facesArray;
return ogtMesh;
}