update to 25.3.0 sdk, switch from scene to engine sdk
This commit is contained in:
parent
beeb9a709d
commit
86d621515a
41
README.md
41
README.md
@ -36,18 +36,11 @@ Load **bear.bsz** into [bella_gui](https://bellarender.com/builds) for rendering
|
||||
- [TODO] convert chunk camera for anim
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Build
|
||||
|
||||
Download SDK for your OS and move **bella_scene_sdk** into your **workdir**. On Windows rename unzipped folder by removing version ie bella_engine_sdk-24.6.0 -> bella_scene_sdk
|
||||
https://bellarender.com/builds/
|
||||
|
||||
|
||||
```
|
||||
workdir/
|
||||
├── bella_scene_sdk/
|
||||
├── bella_engine_sdk/
|
||||
├── libplist/
|
||||
├── lzfse/
|
||||
├── opengametools/
|
||||
@ -55,9 +48,16 @@ workdir/
|
||||
```
|
||||
|
||||
# MacOS
|
||||
Install Cmake to /Applications
|
||||
```
|
||||
curl -LO https://github.com/Kitware/CMake/releases/download/v3.31.6/cmake-3.31.6-macos-universal.dmg
|
||||
open cmake-3.31.6-macos-universal.dmg
|
||||
```
|
||||
|
||||
```
|
||||
mkdir workdir
|
||||
curl -LO https://downloads.bellarender.com/bella_scene_sdk-25.3.0-macos.zip
|
||||
unzip bella_scene_sdk-25.3.0-macos.zip
|
||||
git clone https://github.com/lzfse/lzfse
|
||||
mkdir -p lzfse/build
|
||||
cd lzfse/build
|
||||
@ -85,13 +85,17 @@ cd vmax2bella
|
||||
make all -j4
|
||||
install_name_tool -change ../lzfse/build/liblzfse.dylib @rpath/liblzfse.dylib bin/Darwin/release/vmax2bella
|
||||
install_name_tool -change /usr/local/lib/libplist-2.0.4.dylib @rpath/libplist-2.0.4.dylib bin/Darwin/release/vmax2bella
|
||||
|
||||
git clone https://github.com/oomer/vmax2bella.git
|
||||
cd vmax2bella
|
||||
make all -j4
|
||||
```
|
||||
|
||||
# Linux [NOT READY]
|
||||
## Linux
|
||||
|
||||
```
|
||||
mkdir workdir
|
||||
curl -LO https://downloads.bellarender.com/bella_engine_sdk-25.3.0-linux.tar.gz
|
||||
tar -xvf bella_engine_sdk-25.3.0-linux.tar.gz
|
||||
git clone https://github.com/lzfse/lzfse
|
||||
mkdir lzfse/build
|
||||
cd lzfse/build
|
||||
@ -107,15 +111,22 @@ git clone https://github.com/jpaver/opengametools.git
|
||||
git clone https://github.com/oomer/oom.git
|
||||
git clone https://github.com/oomer/vmax2bella.git
|
||||
cd vmax2bella
|
||||
make
|
||||
make all -j4
|
||||
```
|
||||
|
||||
# Windows
|
||||
- Install Visual Studio Community 2022
|
||||
- Add Desktop development with C++ workload
|
||||
- Launch x64 Native tools Command Prompt for VS2022
|
||||
## Windows (win10)
|
||||
- [optioanl] Install https://git-scm.com
|
||||
|
||||
- Download Visual Studio Community Edition 2022
|
||||
- Run VisualStudioSetup.exe
|
||||
- Workload = [x] Desktop development with C++
|
||||
- Individual components = [x] Git For Windows
|
||||
|
||||
#### x64 Developer console
|
||||
```
|
||||
mkdir workdir
|
||||
curl -LO https://downloads.bellarender.com/bella_engine_sdk-25.3.0-win32.zip
|
||||
tar -xf bella_engine_sdk-25.3.0-win32.zip
|
||||
git clone https://github.com/lzfse/lzfse
|
||||
mkdir -p lzfse/build
|
||||
cd lzfse/build
|
||||
|
||||
586
debug.cpp
586
debug.cpp
@ -1,586 +0,0 @@
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a voxel's layercolor and color from the ds data stream
|
||||
*
|
||||
* @param dsData The raw ds data stream containing layer-color pairs
|
||||
* @return A vector of Voxel structures with explicit coordinates and colors
|
||||
*/
|
||||
std::vector<newVoxel> decodeVoxels2(const std::vector<uint8_t>& dsData, int mortonOffset) {
|
||||
std::vector<newVoxel> voxels;
|
||||
for (int i = 0; i < dsData.size() - 1; i += 2) {
|
||||
dsVoxel _vxVoxel; // VoxelMax data
|
||||
_vxVoxel.layer = static_cast<int>(dsData[i]);
|
||||
_vxVoxel.color = static_cast<uint8_t>(dsData[i + 1]);
|
||||
uint32_t dx, dy, dz;
|
||||
decodeMorton3DOptimized(i/2 + mortonOffset, dx, dy, dz); // index IS the morton code
|
||||
if (_vxVoxel.color != 0) {
|
||||
newVoxel voxel = {dx, dy, dz, _vxVoxel.color};
|
||||
voxels.push_back(voxel);
|
||||
}
|
||||
|
||||
}
|
||||
return voxels;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print a plist node's contents recursively.
|
||||
* This function takes a plist node and prints its contents in a human-readable format.
|
||||
* It handles all types of plist nodes (dictionaries, arrays, strings, etc.) by using
|
||||
* recursion to traverse the entire plist structure.
|
||||
*
|
||||
* @param node The plist node to print (plist_t is a pointer to the internal plist structure)
|
||||
* @param indent The current indentation level (defaults to 0 for the root node)
|
||||
*/
|
||||
void printPlistNode(const plist_t& node, int indent ) {
|
||||
// Early return if node is null (safety check)
|
||||
if (!node) return;
|
||||
|
||||
// Create a string with 'indent * 2' spaces for proper indentation
|
||||
// This helps visualize the hierarchy of nested structures
|
||||
std::string indentStr(indent * 2, ' ');
|
||||
|
||||
// Get the type of the current node (dictionary, array, string, etc.)
|
||||
plist_type nodeType = plist_get_node_type(node);
|
||||
|
||||
// Handle each type of node differently
|
||||
switch (nodeType) {
|
||||
case PLIST_DICT: {
|
||||
std::cout << indentStr << "Dictionary:" << std::endl;
|
||||
|
||||
// Create an iterator for the dictionary
|
||||
// nullptr is passed as initial value; the iterator will be allocated by plist_dict_new_iter
|
||||
plist_dict_iter it = nullptr;
|
||||
plist_dict_new_iter(node, &it);
|
||||
|
||||
// Variables to store the current key-value pair
|
||||
char* key = nullptr; // Will hold the dictionary key (needs to be freed)
|
||||
plist_t value = nullptr; // Will hold the value node
|
||||
|
||||
// Iterate through all items in the dictionary
|
||||
while (true) {
|
||||
// Get the next key-value pair
|
||||
plist_dict_next_item(node, it, &key, &value);
|
||||
|
||||
// Break if we've reached the end of the dictionary
|
||||
if (!key || !value) break;
|
||||
|
||||
// Print the key and recursively print its value
|
||||
std::cout << indentStr << " " << key << ":" << std::endl;
|
||||
printPlistNode(value, indent + 2); // Increase indent for nested values
|
||||
|
||||
// Free the key string (allocated by plist_dict_next_item)
|
||||
free(key);
|
||||
key = nullptr; // Set to nullptr to avoid double-free
|
||||
}
|
||||
|
||||
// Free the iterator when done
|
||||
free(it);
|
||||
break;
|
||||
}
|
||||
case PLIST_ARRAY: {
|
||||
std::cout << indentStr << "Array:" << std::endl;
|
||||
uint32_t size = plist_array_get_size(node);
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
plist_t item = plist_array_get_item(node, i);
|
||||
std::cout << indentStr << " [" << i << "]:" << std::endl;
|
||||
printPlistNode(item, indent + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PLIST_STRING: {
|
||||
char* str = nullptr;
|
||||
plist_get_string_val(node, &str);
|
||||
std::cout << indentStr << "String: " << (str ? str : "(null)") << std::endl;
|
||||
free(str);
|
||||
break;
|
||||
}
|
||||
case PLIST_BOOLEAN: {
|
||||
uint8_t bval;
|
||||
plist_get_bool_val(node, &bval);
|
||||
std::cout << indentStr << "Boolean: " << (bval ? "true" : "false") << std::endl;
|
||||
break;
|
||||
}
|
||||
case PLIST_UINT: {
|
||||
uint64_t val;
|
||||
plist_get_uint_val(node, &val);
|
||||
std::cout << indentStr << "Integer: " << val << std::endl;
|
||||
break;
|
||||
}
|
||||
case PLIST_REAL: {
|
||||
double val;
|
||||
plist_get_real_val(node, &val);
|
||||
std::cout << indentStr << "Real: " << val << std::endl;
|
||||
break;
|
||||
}
|
||||
case PLIST_DATE: {
|
||||
int32_t sec = 0;
|
||||
int32_t usec = 0;
|
||||
plist_get_date_val(node, &sec, &usec);
|
||||
std::cout << indentStr << "Date: " << sec << "." << usec << std::endl;
|
||||
break;
|
||||
}
|
||||
case PLIST_DATA: {
|
||||
char* data = nullptr;
|
||||
uint64_t length = 0;
|
||||
plist_get_data_val(node, &data, &length);
|
||||
std::cout << indentStr << "Data: <" << length << " bytes>" << std::endl;
|
||||
free(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::cout << indentStr << "Unknown type" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New visualization function that definitely uses the correct z-plane
|
||||
*
|
||||
* @param voxels The vector of decoded voxels
|
||||
* @param zPlane The z-coordinate of the plane to visualize
|
||||
* @param size The size of the grid (default: 32x32)
|
||||
*/
|
||||
void visualizeZPlaneFixed(const std::vector<newVoxel>& voxels, int zPlane, int size ) {
|
||||
// Bounds checking
|
||||
const int MIN_Z = 0;
|
||||
const int MAX_Z = 31;
|
||||
if (zPlane < MIN_Z || zPlane > MAX_Z) {
|
||||
std::cout << "WARNING: z-plane value " << zPlane << " is out of bounds. Valid range is " << MIN_Z << "-" << MAX_Z << ". Using z=0 instead." << std::endl;
|
||||
zPlane = 0;
|
||||
}
|
||||
|
||||
std::cout << "Visualizing z-plane: " << zPlane << std::endl;
|
||||
|
||||
// Create a 2D grid for visualization
|
||||
std::vector<std::vector<char>> grid(size, std::vector<char>(size, ' '));
|
||||
|
||||
// Count voxels for statistics
|
||||
int totalVoxels = voxels.size();
|
||||
int voxelsAtRequestedZ = 0;
|
||||
int coloredVoxels = 0;
|
||||
int clearVoxels = 0;
|
||||
|
||||
// Loop 1: Debug output for the first few matching voxels
|
||||
int debugCount = 0;
|
||||
for (const auto& voxel : voxels) {
|
||||
if (voxel.z == zPlane) {
|
||||
voxelsAtRequestedZ++;
|
||||
|
||||
// Update the grid and count color types
|
||||
if (voxel.x >= 0 && voxel.x < size && voxel.y >= 0 && voxel.y < size) {
|
||||
if (voxel.color == 0x00) {
|
||||
grid[voxel.y][voxel.x] = '.'; // Clear voxel (0x00)
|
||||
clearVoxels++;
|
||||
} else if (voxel.color == 0x25) {
|
||||
grid[voxel.y][voxel.x] = '#'; // Colored voxel (0x25)
|
||||
coloredVoxels++;
|
||||
} else {
|
||||
grid[voxel.y][voxel.x] = 'X'; // Other color
|
||||
coloredVoxels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print statistics
|
||||
std::cout << "\nVisualization Statistics:" << std::endl;
|
||||
std::cout << "- Total voxels in data: " << totalVoxels << std::endl;
|
||||
std::cout << "- Voxels at z=" << zPlane << ": " << voxelsAtRequestedZ << std::endl;
|
||||
std::cout << "- Colored voxels: " << coloredVoxels << " (shown as '#' or 'X')" << std::endl;
|
||||
std::cout << "- Clear voxels: " << clearVoxels << " (shown as '.')" << std::endl;
|
||||
|
||||
// If no matching voxels were found, print a message and return
|
||||
if (voxelsAtRequestedZ == 0) {
|
||||
std::cout << "\n*** NO VOXELS FOUND AT Z=" << zPlane << " ***\n" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Print legend
|
||||
std::cout << "\nLegend:" << std::endl;
|
||||
std::cout << "- '#': Color 0x25" << std::endl;
|
||||
std::cout << "- '.': Clear (0x00)" << std::endl;
|
||||
std::cout << "- 'X': Other colors" << std::endl;
|
||||
std::cout << "- ' ': No voxel present" << std::endl;
|
||||
std::cout << "- Each 8x4 section represents one subchunk" << std::endl;
|
||||
|
||||
// Print x-axis header
|
||||
std::cout << "\n ";
|
||||
for (int x = 0; x < size; x++) {
|
||||
if (x % 8 == 0) {
|
||||
std::cout << "|"; // Mark subchunk boundaries
|
||||
} else {
|
||||
std::cout << x % 10; // Print digit for readability
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Print divider line
|
||||
std::cout << " ";
|
||||
for (int x = 0; x < size; x++) {
|
||||
if (x % 8 == 0) {
|
||||
std::cout << "+"; // Mark subchunk corners
|
||||
} else {
|
||||
std::cout << "-";
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Print grid with y-axis labels and subchunk markers
|
||||
for (int y = 0; y < size; y++) {
|
||||
std::cout << std::setw(2) << y << " ";
|
||||
|
||||
// Mark subchunk boundaries on y-axis
|
||||
if (y % 4 == 0) {
|
||||
std::cout << "+";
|
||||
} else {
|
||||
std::cout << "|";
|
||||
}
|
||||
|
||||
// Print the actual voxel data for this row
|
||||
for (int x = 0; x < size; x++) {
|
||||
std::cout << grid[y][x];
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n===============================================\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines a specific array element at the given index from a plist file.
|
||||
* This function allows inspection of individual chunks/snapshots in the data.
|
||||
*
|
||||
* @param plistFilePath Path to the plist file
|
||||
* @param index The index of the array element to examine
|
||||
* @param arrayPath The path to the array in the plist structure
|
||||
* @return true if successful, false if any errors occurred
|
||||
*/
|
||||
bool examinePlistNode(const plist_t& root_node, int snapshotIndex, int zIndex, const std::string& arrayPath) {
|
||||
std::cout << "Examining Plist array at snapshot " << snapshotIndex << " zIndex " << zIndex << std::endl;
|
||||
|
||||
if (!root_node) {
|
||||
std::cerr << "Failed to process Plist data" << std::endl;
|
||||
return false;
|
||||
}
|
||||
plist_t current_node = root_node;
|
||||
// if the array path contains slashes, we need to navigate through the structure
|
||||
std::string path = arrayPath;
|
||||
size_t pos = 0;
|
||||
std::string token;
|
||||
while ((pos = path.find('/')) != std::string::npos) {
|
||||
token = path.substr(0, pos);
|
||||
path.erase(0, pos + 1);
|
||||
|
||||
// current node must be a dictionary
|
||||
if (plist_get_node_type(current_node) != PLIST_DICT) {
|
||||
std::cerr << "error: expected dictionary at path component '" << token << "'" << std::endl;
|
||||
//plist_free(root_node);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the next node in the path
|
||||
current_node = plist_dict_get_item(current_node, token.c_str());
|
||||
if (!current_node) {
|
||||
std::cerr << "error: could not find key '" << token << "' in dictionary" << std::endl;
|
||||
//plist_free(root_node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now path contains the final key name
|
||||
if (!path.empty() && plist_get_node_type(current_node) == PLIST_DICT) {
|
||||
current_node = plist_dict_get_item(current_node, path.c_str());
|
||||
if (!current_node) {
|
||||
std::cerr << "Error: Could not find key '" << path << "' in dictionary" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we found an array
|
||||
if (plist_get_node_type(current_node) != PLIST_ARRAY) {
|
||||
std::cerr << "Error: '" << "arrayPath" << "' is not an array" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Plist node array size
|
||||
uint32_t arraySize = plist_array_get_size(current_node);
|
||||
if (snapshotIndex < 0 || snapshotIndex >= static_cast<int>(arraySize)) {
|
||||
std::cerr << "Error: Index " << snapshotIndex << " is out of range (array size: " << arraySize << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Plist node at the specified index
|
||||
plist_t element = plist_array_get_item(current_node, snapshotIndex);
|
||||
if (!element) {
|
||||
std::cerr << "Error: Could not get Plist node at snapshot " << snapshotIndex << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Array size: " << arraySize << std::endl;
|
||||
std::cout << "Plist node details at snapshot " << snapshotIndex << " zIndex " << zIndex << ":" << std::endl;
|
||||
printPlistNode(element);
|
||||
debugSnapshots(element, snapshotIndex, zIndex);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Handles 's' dictionary in a Plist node holding 32x32x32 chunks of voxel data.
|
||||
*
|
||||
* @param element The Plist node to examine
|
||||
* @return true if successful, false if any errors occurred
|
||||
*/
|
||||
bool debugSnapshots(plist_t element, int snapshotIndex, int zIndex) {
|
||||
std::cout << "Debugging snapshots" << std::endl;
|
||||
// Special handling for 's' dictionaries
|
||||
if (plist_get_node_type(element) == PLIST_DICT) {
|
||||
plist_t sNode = plist_dict_get_item(element, "s");
|
||||
if (sNode) {
|
||||
// Look for specific keys of interest in the 's' dictionary
|
||||
if (plist_get_node_type(sNode) == PLIST_DICT) {
|
||||
// Check for 'ds' (data stream) in the 's' dictionary
|
||||
plist_t dsNode = plist_dict_get_item(sNode, "ds");
|
||||
if (dsNode && plist_get_node_type(dsNode) == PLIST_DATA) {
|
||||
char* data = nullptr;
|
||||
uint64_t length = 0;
|
||||
plist_get_data_val(dsNode, &data, &length);
|
||||
|
||||
std::cout << "\nDetailed analysis of 'ds' data stream (size: " << length << " bytes):" << std::endl;
|
||||
|
||||
// Detailed analysis of the data stream
|
||||
if (length > 0 && data) {
|
||||
// Display as hex bytes - increased to 384 bytes
|
||||
std::cout << "First 384 bytes (hex):" << std::endl;
|
||||
size_t bytesToShow = std::min(static_cast<size_t>(384), static_cast<size_t>(length));
|
||||
for (size_t i = 0; i < bytesToShow; i++) {
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(static_cast<uint8_t>(data[i])) << " ";
|
||||
if ((i + 1) % 16 == 0) std::cout << std::endl;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
|
||||
// If data appears to be position-color pairs (as in voxel data)
|
||||
if (length % 2 == 0) {
|
||||
size_t numPairs = length / 2;
|
||||
std::cout << "Data appears to contain " << numPairs << " position-color pairs" << std::endl;
|
||||
|
||||
// Check if all positions are 0 (common for optimized voxel data)
|
||||
bool allPositionsZero = true;
|
||||
for (size_t i = 0; i < std::min(numPairs, static_cast<size_t>(100)); i++) {
|
||||
if (static_cast<uint8_t>(data[i * 2]) != 0) {
|
||||
allPositionsZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allPositionsZero) {
|
||||
// Show only color values for more compact analysis
|
||||
std::cout << "\nAll position values are 0. Showing only color values:" << std::endl;
|
||||
std::cout << "First 384 color values (hex):" << std::endl;
|
||||
size_t colorsToShow = std::min(static_cast<size_t>(384), numPairs);
|
||||
for (size_t i = 0; i < colorsToShow; i++) {
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(static_cast<uint8_t>(data[i * 2 + 1])) << " ";
|
||||
if ((i + 1) % 16 == 0) std::cout << std::endl;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
} else {
|
||||
// Show position-color pairs if positions vary
|
||||
std::cout << "\nFirst 10 position-color pairs:" << std::endl;
|
||||
std::cout << "Index | Position | Color" << std::endl;
|
||||
std::cout << "------|----------|------" << std::endl;
|
||||
|
||||
size_t pairsToShow = std::min(static_cast<size_t>(10), numPairs);
|
||||
for (size_t i = 0; i < pairsToShow; i++) {
|
||||
uint8_t position = static_cast<uint8_t>(data[i * 2]);
|
||||
uint8_t color = static_cast<uint8_t>(data[i * 2 + 1]);
|
||||
|
||||
std::cout << std::setw(5) << i << " | "
|
||||
<< std::setw(8) << std::hex << std::setfill('0')
|
||||
<< static_cast<int>(position) << std::dec << std::setfill(' ') << " | "
|
||||
<< std::setw(5) << std::hex << std::setfill('0')
|
||||
<< static_cast<int>(color) << std::dec << std::setfill(' ') << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze and print color runs
|
||||
std::cout << "\nAnalyzing color runs:" << std::endl;
|
||||
|
||||
if (numPairs > 0) {
|
||||
uint8_t currentColor = static_cast<uint8_t>(data[1]); // First color
|
||||
size_t runStart = 0;
|
||||
size_t runLength = 1;
|
||||
|
||||
// Find all runs
|
||||
std::vector<std::tuple<size_t, size_t, uint8_t>> colorRuns;
|
||||
|
||||
for (size_t i = 1; i < numPairs; i++) {
|
||||
uint8_t color = static_cast<uint8_t>(data[i * 2 + 1]);
|
||||
|
||||
if (color == currentColor) {
|
||||
// Continue the current run
|
||||
runLength++;
|
||||
} else {
|
||||
// End the current run and start a new one
|
||||
colorRuns.emplace_back(runStart, runStart + runLength - 1, currentColor);
|
||||
currentColor = color;
|
||||
runStart = i;
|
||||
runLength = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last run
|
||||
colorRuns.emplace_back(runStart, runStart + runLength - 1, currentColor);
|
||||
|
||||
// Print the runs in a condensed format
|
||||
std::cout << "Found " << colorRuns.size() << " color runs:" << std::endl;
|
||||
std::cout << "Color | Voxel Count | Range" << std::endl;
|
||||
std::cout << "------|-------------|------" << std::endl;
|
||||
|
||||
for (const auto& run : colorRuns) {
|
||||
size_t start = std::get<0>(run);
|
||||
size_t end = std::get<1>(run);
|
||||
uint8_t color = std::get<2>(run);
|
||||
size_t length = end - start + 1;
|
||||
|
||||
std::cout << " 0x" << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(color) << " | "
|
||||
<< std::dec << std::setfill(' ') << std::setw(11) << length << " | "
|
||||
<< std::setw(5) << start << "-" << std::setw(5) << end
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Add special notice for full-voxel-space runs
|
||||
if (colorRuns.size() == 1) {
|
||||
const auto& singleRun = colorRuns[0];
|
||||
size_t start = std::get<0>(singleRun);
|
||||
size_t end = std::get<1>(singleRun);
|
||||
size_t length = end - start + 1;
|
||||
uint8_t color = std::get<2>(singleRun);
|
||||
|
||||
if (start == 0 && length == 32768) {
|
||||
std::cout << "\nNOTICE: This chunk contains a single color (0x"
|
||||
<< std::hex << static_cast<int>(color) << std::dec
|
||||
<< ") for all 32,768 voxels, which would fill a complete 32x32x32 voxel space." << std::endl;
|
||||
std::cout << "This could indicate:";
|
||||
std::cout << "\n - A solid block of one color";
|
||||
std::cout << "\n - A special encoding for empty/default chunks";
|
||||
std::cout << "\n - A placeholder or initialization state" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode voxels for visualization
|
||||
// @param TODO: get morton offset from the 'lt' dictionary
|
||||
std::vector<newVoxel> voxels = decodeVoxels2(std::vector<uint8_t>(data, data + length), 0);
|
||||
|
||||
printVoxelTable(voxels, 100);
|
||||
|
||||
// Explicitly decode the voxels for visualization
|
||||
char* data = nullptr;
|
||||
uint64_t length = 0;
|
||||
plist_get_data_val(dsNode, &data, &length);
|
||||
|
||||
if (length > 0 && data) {
|
||||
std::vector<newVoxel> voxels = decodeVoxels2(std::vector<uint8_t>(data, data + length), 0);
|
||||
visualizeZPlaneFixed(voxels, zIndex);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
// Check for 'id' dictionary to get chunk information
|
||||
plist_t idNode = plist_dict_get_item(sNode, "id");
|
||||
if (idNode && plist_get_node_type(idNode) == PLIST_DICT) {
|
||||
plist_t chunkIdNode = plist_dict_get_item(idNode, "c");
|
||||
if (chunkIdNode && plist_get_node_type(chunkIdNode) == PLIST_UINT) {
|
||||
uint64_t chunkId;
|
||||
plist_get_uint_val(chunkIdNode, &chunkId);
|
||||
std::cout << "\nChunk ID: " << chunkId << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for 'lt' (location table)
|
||||
plist_t ltNode = plist_dict_get_item(sNode, "lt");
|
||||
if (ltNode && plist_get_node_type(ltNode) == PLIST_DATA) {
|
||||
char* data = nullptr;
|
||||
uint64_t length = 0;
|
||||
plist_get_data_val(ltNode, &data, &length);
|
||||
|
||||
std::cout << "\nLocation table size: " << length << " bytes" << std::endl;
|
||||
if (length > 0 && data) {
|
||||
std::cout << "First 16 bytes of location table:" << std::endl;
|
||||
size_t bytesToShow = std::min(static_cast<size_t>(16), static_cast<size_t>(length));
|
||||
for (size_t i = 0; i < bytesToShow; i++) {
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(static_cast<uint8_t>(data[i])) << " ";
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a table of voxel positions and colors
|
||||
*
|
||||
* @param voxels The vector of decoded voxels
|
||||
* @param limit Maximum number of voxels to display (0 for all)
|
||||
* @param filterZ Optional z-value to filter by
|
||||
*/
|
||||
void printVoxelTable(const std::vector<newVoxel>& voxels, size_t limit , int filterZ ) {
|
||||
int emptyVoxels = 32768 - voxels.size();
|
||||
std::cout << "Voxels: " << voxels.size() << " Empty: " << emptyVoxels << std::endl;
|
||||
|
||||
// Count voxels at the filtered z-level if filtering is active
|
||||
int filteredCount = 0;
|
||||
if (filterZ >= 0) {
|
||||
for (const auto& voxel : voxels) {
|
||||
if (voxel.z == filterZ) filteredCount++;
|
||||
}
|
||||
std::cout << "Voxels at z=" << filterZ << ": " << filteredCount << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Index | X | Y | Z | Color" << std::endl;
|
||||
std::cout << "------|----|----|----|---------" << std::endl;
|
||||
|
||||
int count = 0;
|
||||
int shownCount = 0;
|
||||
for (size_t i = 0; i < voxels.size(); i++) {
|
||||
const auto& voxel = voxels[i];
|
||||
|
||||
// Skip if we're filtering by z and this doesn't match
|
||||
if (filterZ >= 0 && voxel.z != filterZ) continue;
|
||||
|
||||
std::cout << std::setw(6) << i << " | ";
|
||||
std::cout << std::setw(2) << voxel.x << " | ";
|
||||
std::cout << std::setw(2) << voxel.y << " | ";
|
||||
std::cout << std::setw(2) << voxel.z << " | ";
|
||||
std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(voxel.color) << std::dec << std::setfill(' ') << std::endl;
|
||||
|
||||
// Count shown voxels
|
||||
shownCount++;
|
||||
|
||||
// Check if we've reached the limit
|
||||
if (limit > 0 && shownCount >= limit) {
|
||||
if (filterZ >= 0) {
|
||||
int remaining = filteredCount - shownCount;
|
||||
if (remaining > 0) {
|
||||
std::cout << "... (output truncated, " << remaining << " more voxels at z=" << filterZ << ")" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "... (output truncated, " << (voxels.size() - shownCount) << " more voxels)" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
debug.h
24
debug.h
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include "../libplist/include/plist/plist.h" // Library for handling Apple property list files
|
||||
#include "common.h" // Debugging functions
|
||||
// Include any necessary structures
|
||||
|
||||
// Optimized function to compact bits (From VoxelMax)
|
||||
uint32_t compactBits(uint32_t n);
|
||||
// Optimized function to decode Morton code using parallel bit manipulation
|
||||
void decodeMorton3DOptimized(uint32_t morton, uint32_t& x, uint32_t& y, uint32_t& z);
|
||||
|
||||
|
||||
|
||||
// Function declarations
|
||||
std::vector<newVoxel> decodeVoxels(const std::vector<uint8_t>& dsData, int mortonOffset);
|
||||
void printPlistNode(const plist_t& node, int indent = 0);
|
||||
bool examinePlistNode(const plist_t& root_node, int snapshotIndex, int zIndex, const std::string& arrayPath);
|
||||
bool debugSnapshots(plist_t element, int snapshotIndex, int zIndex);
|
||||
void printVoxelTable(const std::vector<newVoxel>& voxels, size_t limit = 100, int filterZ = -1);
|
||||
void visualizeZPlaneFixed(const std::vector<newVoxel>& voxels, int zPlane, int size = 32);
|
||||
70
extra.cpp
70
extra.cpp
@ -1,70 +0,0 @@
|
||||
#include "extra.h"
|
||||
// Function that returns the license text for this program
|
||||
std::string initializeGlobalLicense()
|
||||
{
|
||||
// R"(...)" is a C++ raw string literal - allows multi-line strings with preserved formatting
|
||||
return R"(
|
||||
vmax2bella
|
||||
|
||||
Copyright (c) 2025 Harvey Fong
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.)";
|
||||
}
|
||||
|
||||
// Function that returns third-party license text
|
||||
std::string initializeGlobalThirdPartyLicences()
|
||||
{
|
||||
return R"(
|
||||
Bella SDK (Software Development Kit)
|
||||
|
||||
Copyright Diffuse Logic SCP, all rights reserved.
|
||||
|
||||
Permission is hereby granted to any person obtaining a copy of this software
|
||||
(the "Software"), to use, copy, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ALL
|
||||
IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF MERCHANTABILITY
|
||||
ARE HEREBY DISCLAIMED.)
|
||||
|
||||
===
|
||||
|
||||
lzfse
|
||||
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
)";
|
||||
}
|
||||
|
||||
13
makefile
13
makefile
@ -1,11 +1,11 @@
|
||||
# Project configuration
|
||||
BELLA_SDK_NAME = bella_scene_sdk
|
||||
BELLA_SDK_NAME = bella_engine_sdk
|
||||
EXECUTABLE_NAME = vmax2bella
|
||||
PLATFORM = $(shell uname)
|
||||
BUILD_TYPE ?= release# Default to release build if not specified
|
||||
|
||||
# Common paths
|
||||
BELLA_SDK_PATH = ../bella_scene_sdk
|
||||
BELLA_SDK_PATH = ../bella_engine_sdk
|
||||
LZFSE_PATH = ../lzfse
|
||||
LIBPLIST_PATH = ../libplist
|
||||
OBJ_DIR = obj/$(PLATFORM)/$(BUILD_TYPE)
|
||||
@ -18,6 +18,7 @@ ifeq ($(PLATFORM), Darwin)
|
||||
SDK_LIB_EXT = dylib
|
||||
LZFSE_LIB_NAME = liblzfse.$(SDK_LIB_EXT)
|
||||
PLIST_LIB_NAME = libplist-2.0.4.$(SDK_LIB_EXT)
|
||||
USD_LIB_NAME = libdl_usd_ms.$(SDK_LIB_EXT)
|
||||
MACOS_SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
|
||||
|
||||
# Compiler settings
|
||||
@ -43,6 +44,7 @@ else
|
||||
SDK_LIB_EXT = so
|
||||
LZFSE_LIB_NAME = liblzfse.$(SDK_LIB_EXT)
|
||||
PLIST_LIB_NAME = libplist.$(SDK_LIB_EXT)
|
||||
USD_LIB_NAME = libdl_usd_ms.$(SDK_LIB_EXT)
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
@ -96,6 +98,7 @@ $(OUTPUT_FILE): $(OBJECT_FILES)
|
||||
$(CXX) -o $@ $(OBJECT_FILES) $(LINKER_FLAGS) $(LIB_PATHS) $(LIBRARIES)
|
||||
@echo "Copying libraries to $(BIN_DIR)..."
|
||||
@cp $(SDK_LIB_PATH)/$(SDK_LIB_FILE) $(BIN_DIR)/$(SDK_LIB_FILE)
|
||||
@cp $(SDK_LIB_PATH)/$(USD_LIB_NAME) $(BIN_DIR)/$(USD_LIB_NAME)
|
||||
@cp $(LZFSE_BUILD_DIR)/$(LZFSE_LIB_NAME) $(BIN_DIR)/$(LZFSE_LIB_NAME)
|
||||
@cp $(PLIST_LIB_DIR)/$(PLIST_LIB_NAME) $(BIN_DIR)/
|
||||
@echo "Build complete: $(OUTPUT_FILE)"
|
||||
@ -108,7 +111,7 @@ clean:
|
||||
rm -f $(OBJ_DIR)/vmax2bella.o
|
||||
rm -f $(OUTPUT_FILE)
|
||||
rm -f $(BIN_DIR)/$(SDK_LIB_FILE)
|
||||
rm -f $(BIN_DIR)/*.dylib
|
||||
rm -f $(BIN_DIR)/*.$(SDK_LIB_EXT)
|
||||
rmdir $(OBJ_DIR) 2>/dev/null || true
|
||||
rmdir $(BIN_DIR) 2>/dev/null || true
|
||||
|
||||
@ -119,8 +122,8 @@ cleanall:
|
||||
rm -f bin/*/debug/$(EXECUTABLE_NAME)
|
||||
rm -f bin/*/release/$(SDK_LIB_FILE)
|
||||
rm -f bin/*/debug/$(SDK_LIB_FILE)
|
||||
rm -f bin/*/release/*.dylib
|
||||
rm -f bin/*/debug/*.dylib
|
||||
rm -f bin/*/release/*.$(SDK_LIB_EXT)
|
||||
rm -f bin/*/debug/*.$(SDK_LIB_EXT)
|
||||
rmdir obj/*/release 2>/dev/null || true
|
||||
rmdir obj/*/debug 2>/dev/null || true
|
||||
rmdir bin/*/release 2>/dev/null || true
|
||||
|
||||
1595
oomer_misc.h
1595
oomer_misc.h
File diff suppressed because it is too large
Load Diff
@ -1,236 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
@ -1,967 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// This is a set of common oomer utilities for Vmax models
|
||||
// Will avoid using bella_sdk
|
||||
|
||||
|
||||
// Standard C++ library includes - these provide essential functionality
|
||||
#include <map> // For key-value pair data structures (maps)
|
||||
#include <set> // For set data structure
|
||||
#include <vector> // For dynamic arrays (vectors)
|
||||
#include <string> // For std::string
|
||||
#include <cstdint> // For fixed-size integer types (uint8_t, uint32_t, etc.)
|
||||
#include <fstream> // For file operations (reading/writing files)
|
||||
#include <iostream> // For input/output operations (cout, cin, etc.)
|
||||
#include <filesystem> // For file system operations (directory handling, path manipulation)
|
||||
|
||||
#include "../lzfse/src/lzfse.h"
|
||||
#include "../libplist/include/plist/plist.h" // Library for handling Apple property list files
|
||||
#include "thirdparty/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// Define STB_IMAGE_IMPLEMENTATION before including to create the implementation
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "thirdparty/stb_image.h" // STB Image library
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Structure to represent a 4x4 matrix for 3D transformations
|
||||
// The matrix is stored as a 2D array where m[i][j] represents row i, column j
|
||||
struct VmaxMatrix4x4 {
|
||||
double m[4][4];
|
||||
// Constructor: Creates an identity matrix (1's on diagonal, 0's elsewhere)
|
||||
VmaxMatrix4x4() {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int j = 0; j < 4; j++) {
|
||||
m[i][j] = (i == j) ? 1.0 : 0.0; // 1.0 on diagonal, 0.0 elsewhere
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matrix multiplication operator to combine transformations
|
||||
// Returns: A new matrix that represents the combined transformation
|
||||
VmaxMatrix4x4 operator*(const VmaxMatrix4x4& other) const {
|
||||
VmaxMatrix4x4 result;
|
||||
// Perform matrix multiplication
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int j = 0; j < 4; j++) {
|
||||
result.m[i][j] = 0.0;
|
||||
for(int k = 0; k < 4; k++) {
|
||||
result.m[i][j] += m[i][k] * other.m[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper function to create a translation matrix
|
||||
static VmaxMatrix4x4 createTranslation(double x, double y, double z) {
|
||||
VmaxMatrix4x4 result;
|
||||
result.m[3][0] = x; // Translation in X (bottom row)
|
||||
result.m[3][1] = y; // Translation in Y (bottom row)
|
||||
result.m[3][2] = z; // Translation in Z (bottom row)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper function to create a scale matrix
|
||||
static VmaxMatrix4x4 createScale(double x, double y, double z) {
|
||||
VmaxMatrix4x4 result;
|
||||
result.m[0][0] = x; // Scale in X
|
||||
result.m[1][1] = y; // Scale in Y
|
||||
result.m[2][2] = z; // Scale in Z
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Converts axis-angle rotation to a 4x4 rotation matrix
|
||||
// Parameters:
|
||||
// ax, ay, az: The axis vector to rotate around (doesn't need to be normalized)
|
||||
// angle: The angle to rotate by (in radians)
|
||||
// Returns: A 4x4 rotation matrix that can be used to transform vectors
|
||||
VmaxMatrix4x4 axisAngleToMatrix4x4(double ax, double ay, double az, double angle) {
|
||||
// Step 1: Normalize the axis vector to make it a unit vector
|
||||
// This is required for the rotation formula to work correctly
|
||||
double length = sqrt(ax*ax + ay*ay + az*az);
|
||||
if (length != 0) {
|
||||
ax /= length;
|
||||
ay /= length;
|
||||
az /= length;
|
||||
}
|
||||
|
||||
// Step 2: Calculate trigonometric values needed for the rotation
|
||||
double s = sin(angle); // sine of angle
|
||||
double c = cos(angle); // cosine of angle
|
||||
double t = 1.0 - c; // 1 - cos(angle), used in formula
|
||||
|
||||
// Step 3: Create rotation matrix using Rodrigues' rotation formula
|
||||
// This formula converts an axis-angle rotation into a 3x3 matrix
|
||||
// We'll embed it in the upper-left corner of our 4x4 matrix
|
||||
VmaxMatrix4x4 result;
|
||||
|
||||
// First row of rotation matrix (upper-left 3x3 portion)
|
||||
result.m[0][0] = t*ax*ax + c; // First column
|
||||
result.m[0][1] = t*ax*ay + s*az; // Second column (changed sign)
|
||||
result.m[0][2] = t*ax*az - s*ay; // Third column (changed sign)
|
||||
|
||||
// Second row of rotation matrix
|
||||
result.m[1][0] = t*ax*ay - s*az; // First column (changed sign)
|
||||
result.m[1][1] = t*ay*ay + c; // Second column
|
||||
result.m[1][2] = t*ay*az + s*ax; // Third column (changed sign)
|
||||
|
||||
// Third row of rotation matrix
|
||||
result.m[2][0] = t*ax*az + s*ay; // First column (changed sign)
|
||||
result.m[2][1] = t*ay*az - s*ax; // Second column (changed sign)
|
||||
result.m[2][2] = t*az*az + c; // Third column
|
||||
|
||||
// Fourth row and column remain unchanged (0,0,0,1)
|
||||
// This is already set by the constructor
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Combine a rotation, translation, and scale into a single 4x4 matrix
|
||||
// Parameters:
|
||||
// rotx, roty, rotz: The axis vector to rotate around (doesn't need to be normalized)
|
||||
// rota: The angle to rotate by (in radians)
|
||||
// posx, posy, posz: The position to translate to
|
||||
// scalex, scaley, scalez: The scale to apply to the object
|
||||
// Returns: A 4x4 matrix that represents the combined transformation
|
||||
VmaxMatrix4x4 combineVmaxTransforms(double rotx, double roty, double rotz, double rota, double posx, double posy, double posz, double scalex, double scaley, double scalez) {
|
||||
VmaxMatrix4x4 rotMat4 = axisAngleToMatrix4x4(rotx,
|
||||
roty,
|
||||
rotz,
|
||||
rota);
|
||||
VmaxMatrix4x4 transMat4 = VmaxMatrix4x4();
|
||||
transMat4 = transMat4.createTranslation(posx,
|
||||
posy,
|
||||
posz);
|
||||
VmaxMatrix4x4 scaleMat4 = VmaxMatrix4x4();
|
||||
scaleMat4 = scaleMat4.createScale(scalex,
|
||||
scaley,
|
||||
scalez);
|
||||
VmaxMatrix4x4 resultMat4 = scaleMat4 * rotMat4 * transMat4;
|
||||
return resultMat4;
|
||||
}
|
||||
|
||||
|
||||
struct VmaxRGBA {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
|
||||
// Read a 256x1 PNG file and return a vector of VmaxRGBA colors
|
||||
std::vector<VmaxRGBA> read256x1PaletteFromPNG(const std::string& filename) {
|
||||
int width, height, channels;
|
||||
// Load the image with 4 desired channels (RGBA)
|
||||
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, 4);
|
||||
|
||||
if (!data) {
|
||||
std::cerr << "Error loading PNG file: " << filename << std::endl;
|
||||
return {};
|
||||
}
|
||||
// Make sure the image is 256x1 as expected
|
||||
if (width != 256 || height != 1) {
|
||||
std::cerr << "Warning: Expected a 256x1 image, but got " << width << "x" << height << std::endl;
|
||||
}
|
||||
// Create our palette array
|
||||
std::vector<VmaxRGBA> palette;
|
||||
// Read each pixel (each pixel is 4 bytes - RGBA)
|
||||
for (int i = 0; i < width; i++) {
|
||||
VmaxRGBA color;
|
||||
color.r = data[i * 4];
|
||||
color.g = data[i * 4 + 1];
|
||||
color.b = data[i * 4 + 2];
|
||||
color.a = data[i * 4 + 3];
|
||||
palette.push_back(color);
|
||||
}
|
||||
stbi_image_free(data); // Free the image data
|
||||
return palette;
|
||||
}
|
||||
|
||||
// Standard useful voxel structure, maps easily to VoxelMax's voxel structure and probably MagicaVoxel's
|
||||
// We are using this to unpack a chunked voxel into a simple giant voxel
|
||||
// using a uint8_t saves memory over a uint32_t and both VM and MV models are 256x256x256
|
||||
// The world itself can be larger, see scene.json
|
||||
// Helper struct because in VmaxModel we use array of arrays to store material and color
|
||||
// Allowing us to group voxels by material and color
|
||||
struct VmaxVoxel {
|
||||
uint8_t x, y, z;
|
||||
uint8_t material; // material value 0-7
|
||||
uint8_t palette; // Color palette mapping index 0-255, search palette1.png
|
||||
uint16_t chunkID; // Chunk ID 0-511 8x8x8 is a morton code
|
||||
uint16_t minMorton; // morton encoded offset from chunk origin 0-32767 32x32x32, decoded value is added to voxel x y z
|
||||
// Constructor
|
||||
VmaxVoxel( uint8_t _x,
|
||||
uint8_t _y,
|
||||
uint8_t _z,
|
||||
uint8_t _material,
|
||||
uint8_t _palette,
|
||||
uint16_t _chunkID,
|
||||
uint16_t _minMorton)
|
||||
: x(_x), y(_y), z(_z), material(_material), palette(_palette), chunkID(_chunkID), minMorton(_minMorton) {
|
||||
}
|
||||
};
|
||||
|
||||
inline uint32_t compactBits(uint32_t n) {
|
||||
// For a 32-bit integer in C++
|
||||
n &= 0x49249249; // Keep only every 3rd bit
|
||||
n = (n ^ (n >> 2)) & 0xc30c30c3; // Merge groups
|
||||
n = (n ^ (n >> 4)) & 0x0f00f00f; // Continue merging
|
||||
n = (n ^ (n >> 8)) & 0x00ff00ff; // Merge larger groups
|
||||
n = (n ^ (n >> 16)) & 0x0000ffff; // Final merge
|
||||
return n;
|
||||
}
|
||||
|
||||
// Optimized function to decode Morton code using parallel bit manipulation
|
||||
inline void decodeMorton3DOptimized(uint32_t morton, uint32_t& x, uint32_t& y, uint32_t& z) {
|
||||
x = compactBits(morton);
|
||||
y = compactBits(morton >> 1);
|
||||
z = compactBits(morton >> 2);
|
||||
}
|
||||
|
||||
struct VmaxMaterial {
|
||||
std::string materialName;
|
||||
double transmission;
|
||||
double roughness;
|
||||
double metalness;
|
||||
double emission;
|
||||
bool enableShadows;
|
||||
bool dielectric; // 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
|
||||
// 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
|
||||
|
||||
// Voxels organized by material and color
|
||||
// First dimension: material (0-7)
|
||||
// Second dimension: color (1-255, index 0 unused since color 0 means no voxel)
|
||||
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
|
||||
std::array<VmaxMaterial, 8> materials;
|
||||
// Each model has local colors
|
||||
std::array<VmaxRGBA, 256> 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<uint32_t>(x) << 16) | (static_cast<uint32_t>(y) << 8) | static_cast<uint32_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
materials = newMaterials;
|
||||
}
|
||||
|
||||
// Add colors to this model
|
||||
void addColors(const std::array<VmaxRGBA, 256> newColors) {
|
||||
colors = newColors;
|
||||
}
|
||||
|
||||
// Get all voxels of a specific material and color
|
||||
const std::vector<VmaxVoxel>& getVoxels(int material, int color) const {
|
||||
if (material >= 0 && material < 8 && color > 0 && color < 256) {
|
||||
return voxels[material][color];
|
||||
}
|
||||
static std::vector<VmaxVoxel> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
// Get total voxel count for this model
|
||||
size_t getTotalVoxelCount() const {
|
||||
size_t count = 0;
|
||||
for (int m = 0; m < 8; m++) {
|
||||
for (int c = 1; c < 256; c++) { // Skip index 0
|
||||
count += voxels[m][c].size();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get a map of used materials and their associated colors
|
||||
std::map<int, std::set<int>> getUsedMaterialsAndColors() const {
|
||||
std::map<int, std::set<int>> result;
|
||||
|
||||
// Iterate through fixed size arrays - we know it's 8 materials and 256 colors
|
||||
for (int material = 0; material < 8; material++) {
|
||||
for (int color = 1; color < 256; color++) { // Skip index 0 as it means no voxel
|
||||
if (!voxels[material][color].empty()) {
|
||||
result[material].insert(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::array<VmaxMaterial, 8> getVmaxMaterials(plist_t pnodPalettePlist) {
|
||||
// Directly access the materials array
|
||||
std::array<VmaxMaterial, 8> vmaxMaterials;
|
||||
plist_t materialsNode = plist_dict_get_item(pnodPalettePlist, "materials");
|
||||
if (materialsNode && plist_get_node_type(materialsNode) == PLIST_ARRAY) {
|
||||
uint32_t materialsCount = plist_array_get_size(materialsNode);
|
||||
//std::cout << "Found materials array with " << materialsCount << " items" << std::endl;
|
||||
|
||||
// Process each material
|
||||
for (uint32_t i = 0; i < materialsCount; i++) {
|
||||
plist_t materialNode = plist_array_get_item(materialsNode, i);
|
||||
if (materialNode && plist_get_node_type(materialNode) == PLIST_DICT) {
|
||||
plist_t nameNode = plist_dict_get_item(materialNode, "mi");
|
||||
std::string vmaxMaterialName;
|
||||
double vmaxTransmission = 0.0; // Declare outside the if block
|
||||
double vmaxEmission = 0.0; // Declare outside the if block
|
||||
double vmaxRoughness = 0.0; // Declare outside the if block
|
||||
double vmaxMetalness = 0.0; // Declare outside the if block
|
||||
uint8_t vmaxEnableShadows = 1;
|
||||
|
||||
if (nameNode) {
|
||||
char* rawName = nullptr;
|
||||
plist_get_string_val(nameNode, &rawName);
|
||||
vmaxMaterialName = rawName ? rawName : "unnamed";
|
||||
free(rawName);
|
||||
}
|
||||
plist_t pnodTc = plist_dict_get_item(materialNode, "tc");
|
||||
if (pnodTc) { plist_get_real_val(pnodTc, &vmaxTransmission); }
|
||||
plist_t pnodEmission = plist_dict_get_item(materialNode, "sic");
|
||||
if (pnodEmission) { plist_get_real_val(pnodEmission, &vmaxEmission); }
|
||||
plist_t pnodRoughness = plist_dict_get_item(materialNode, "rc");
|
||||
if (pnodRoughness) { plist_get_real_val(pnodRoughness, &vmaxRoughness); }
|
||||
plist_t pnodMetalness = plist_dict_get_item(materialNode, "mc");
|
||||
if (pnodMetalness) { plist_get_real_val(pnodMetalness, &vmaxMetalness); }
|
||||
plist_t pnodEnableShadow = plist_dict_get_item(materialNode, "sh");
|
||||
if (pnodEnableShadow) { plist_get_bool_val(pnodEnableShadow, &vmaxEnableShadows); }
|
||||
|
||||
vmaxMaterials[i] = {
|
||||
vmaxMaterialName,
|
||||
vmaxTransmission,
|
||||
vmaxRoughness,
|
||||
vmaxMetalness,
|
||||
vmaxEmission,
|
||||
static_cast<bool>(vmaxEnableShadows),
|
||||
false, // dielectric
|
||||
false, // volumetric
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cout << "No materials array found or invalid type" << std::endl;
|
||||
}
|
||||
#ifdef _DEBUG23
|
||||
for (const auto& material : vmaxMaterials) {
|
||||
std::cout << "Material: " << material.materialName << std::endl;
|
||||
std::cout << " Transmission: " << material.transmission << std::endl;
|
||||
std::cout << " Emission: " << material.emission << std::endl;
|
||||
std::cout << " Roughness: " << material.roughness << std::endl;
|
||||
std::cout << " Metalness: " << material.metalness << std::endl;
|
||||
std::cout << " Enable Shadows: " << material.enableShadows << std::endl;
|
||||
std::cout << " Dielectric: " << material.dielectric << std::endl;
|
||||
std::cout << " Volumetric: " << material.volumetric << std::endl;
|
||||
}
|
||||
#endif
|
||||
return vmaxMaterials;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a voxel's material index and palette index from the ds data stream
|
||||
*
|
||||
* @param dsData The raw ds data stream containing material and palette index pairs
|
||||
* @param mortonOffset offset to apply to the morton code
|
||||
* @param chunkID chunk ID
|
||||
* @return vector of VmaxVoxel structures containing the voxels local to a snapshot
|
||||
*/
|
||||
inline std::vector<VmaxVoxel> decodeVoxels(const std::vector<uint8_t>& dsData, int mortonOffset, uint16_t chunkID) {
|
||||
std::vector<VmaxVoxel> voxels;
|
||||
uint8_t material;
|
||||
uint8_t color;
|
||||
for (int i = 0; i < dsData.size() - 1; i += 2) {
|
||||
material = dsData[i]; // also known as a layer color
|
||||
color = dsData[i + 1];
|
||||
uint32_t _tempx, _tempy, _tempz;
|
||||
decodeMorton3DOptimized(i/2 + mortonOffset,
|
||||
_tempx,
|
||||
_tempy,
|
||||
_tempz); // index IS the morton code
|
||||
if (color != 0) {
|
||||
VmaxVoxel voxel = {
|
||||
static_cast<uint8_t>(_tempx),
|
||||
static_cast<uint8_t>(_tempy),
|
||||
static_cast<uint8_t>(_tempz),
|
||||
material,
|
||||
color,
|
||||
chunkID, // todo is wasteful to pass chunkID?
|
||||
static_cast<uint16_t>(mortonOffset)
|
||||
};
|
||||
voxels.push_back(voxel);
|
||||
}
|
||||
}
|
||||
return voxels;
|
||||
}
|
||||
|
||||
//libplist reads in 64 bits
|
||||
struct VmaxChunkInfo {
|
||||
int64_t id; // was uint but will use -1 to indicate bad chunk
|
||||
uint64_t type;
|
||||
uint64_t mortoncode;
|
||||
uint32_t voxelOffsetX;
|
||||
uint32_t voxelOffsetY;
|
||||
uint32_t voxelOffsetZ;
|
||||
};
|
||||
|
||||
// Helper function to get a nested dictionary item
|
||||
// @param root: root dictionary
|
||||
// @param path: path to the item
|
||||
// @return: item
|
||||
// Using a vector of strings for dynamic path length
|
||||
plist_t getNestedPlistNode(plist_t plist_root, const std::vector<std::string>& path) {
|
||||
plist_t current = plist_root;
|
||||
for (const auto& key : path) {
|
||||
if (!current) return nullptr;
|
||||
current = plist_dict_get_item(current, key.c_str());
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Need morton code in snapshot before we can decode voxels
|
||||
// @param an individual chunk: plist_t of a snapshot dict->item->s
|
||||
// @return Chunk level info needed to decode voxels
|
||||
VmaxChunkInfo vmaxChunkInfo(const plist_t& plist_snapshot_dict_item) {
|
||||
uint64_t id;
|
||||
uint64_t type;
|
||||
uint64_t mortoncode;
|
||||
uint32_t voxelOffsetX, voxelOffsetY, voxelOffsetZ;
|
||||
try {
|
||||
plist_t plist_snapshot = getNestedPlistNode(plist_snapshot_dict_item, {"s"});
|
||||
|
||||
// vmax file format must guarantee the existence
|
||||
// s.st.min
|
||||
// s.id.t
|
||||
// s.id.c
|
||||
plist_t plist_min = getNestedPlistNode(plist_snapshot, {"st", "min"});
|
||||
plist_t plist_min_val = plist_array_get_item(plist_min, 3);
|
||||
plist_get_uint_val(plist_min_val, &mortoncode);
|
||||
|
||||
// convert to 32x32x32 chunk offset
|
||||
decodeMorton3DOptimized(mortoncode,
|
||||
voxelOffsetX,
|
||||
voxelOffsetY,
|
||||
voxelOffsetZ);
|
||||
|
||||
plist_t plist_type = getNestedPlistNode(plist_snapshot, {"id","t"});
|
||||
plist_get_uint_val(plist_type, &type);
|
||||
plist_t plist_chunk = getNestedPlistNode(plist_snapshot, {"id","c"});
|
||||
plist_get_uint_val(plist_chunk, &id);
|
||||
|
||||
return VmaxChunkInfo{static_cast<int64_t>(id),
|
||||
type,
|
||||
mortoncode,
|
||||
voxelOffsetX,
|
||||
voxelOffsetY,
|
||||
voxelOffsetZ};
|
||||
} catch (std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << std::endl;
|
||||
// Just continue to next snapshot
|
||||
// This bypass might mean we miss useful snapshots
|
||||
}
|
||||
return VmaxChunkInfo{-1, 0, 0, 0, 0, 0};
|
||||
}
|
||||
|
||||
|
||||
// Right after we get VmaxChunkInfo, we can get the voxels because we need morton chunk offset
|
||||
// @param pnodSnaphot: plist_t of a snapshot
|
||||
// @return vector of VmaxVoxel
|
||||
//std::vector<VmaxVoxel> getVmaxSnapshot(plist_t& pnod_each_snapshot) {
|
||||
std::vector<VmaxVoxel> vmaxVoxelInfo(plist_t& plist_datastream, uint64_t chunkID, uint64_t minMorton) {
|
||||
std::vector<VmaxVoxel> voxelsArray;
|
||||
try {
|
||||
|
||||
// Extract the binary data
|
||||
char* data = nullptr;
|
||||
uint64_t length = 0;
|
||||
plist_get_data_val(plist_datastream, &data, &length);
|
||||
auto foo = std::vector<uint8_t>(data, data + length) ;
|
||||
std::vector<VmaxVoxel> allModelVoxels = decodeVoxels(std::vector<uint8_t>(data, data + length), minMorton, chunkID);
|
||||
|
||||
//std::cout << "allModelVoxels: " << allModelVoxels.size() << std::endl;
|
||||
|
||||
uint32_t model_8x8x8_x, model_8x8x8_y, model_8x8x8_z;
|
||||
decodeMorton3DOptimized(chunkID,
|
||||
model_8x8x8_x,
|
||||
model_8x8x8_y,
|
||||
model_8x8x8_z); // index IS the morton code
|
||||
int model_256x256x256_x = model_8x8x8_x * 8; // convert to model space
|
||||
int model_256x256x256_y = model_8x8x8_y * 8;
|
||||
int model_256x256x256_z = model_8x8x8_z * 8;
|
||||
|
||||
for (const VmaxVoxel& eachVmaxVoxel : allModelVoxels) {
|
||||
auto [ chunk_32x32x32_x,
|
||||
chunk_32x32x32_y,
|
||||
chunk_32x32x32_z,
|
||||
materialMap,
|
||||
colorMap,
|
||||
chunkID,
|
||||
minMorton] = eachVmaxVoxel;
|
||||
|
||||
int voxel_256x256x256_x = model_256x256x256_x + chunk_32x32x32_x;
|
||||
int voxel_256x256x256_y = model_256x256x256_y + chunk_32x32x32_y;
|
||||
int voxel_256x256x256_z = model_256x256x256_z + chunk_32x32x32_z;
|
||||
|
||||
auto one_voxel = VmaxVoxel(voxel_256x256x256_x,
|
||||
voxel_256x256x256_y,
|
||||
voxel_256x256x256_z,
|
||||
materialMap,
|
||||
colorMap,
|
||||
chunkID,
|
||||
minMorton);
|
||||
voxelsArray.push_back(one_voxel);
|
||||
}
|
||||
return voxelsArray;
|
||||
} catch (std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << std::endl;
|
||||
// Just continue to next snapshot
|
||||
// This bypass might mean we miss useful snapshots
|
||||
}
|
||||
return voxelsArray; // empty return
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a binary plist file and return a plist node.
|
||||
* if the file is lzfse compressed, decompress it and parse the decompressed data
|
||||
*
|
||||
* Memory Management:
|
||||
* - Creates temporary buffers for decompression
|
||||
* - Handles buffer resizing if needed
|
||||
* - Returns a plist node that must be freed by the caller
|
||||
*
|
||||
* @param lzfseFullName Path to the LZFSE file
|
||||
* @param plistName Name of the plist file to write (optional)
|
||||
* @return plist_t A pointer to the root node of the parsed plist, or nullptr if failed
|
||||
*/
|
||||
// read binary lzfse compressed/uncompressed file
|
||||
inline plist_t readPlist(const std::string& inStrPlist, std::string outStrPlist, bool decompress) {
|
||||
// Get file size using std::filesystem
|
||||
size_t rawFileSize = std::filesystem::file_size(inStrPlist);
|
||||
std::vector<uint8_t> rawBytes(rawFileSize);
|
||||
std::vector<uint8_t> outBuffer;
|
||||
size_t decodedSize = 0;
|
||||
if (decompress) { // files are either lzfse compressed or uncompressed
|
||||
std::ifstream rawBytesFile(inStrPlist, std::ios::binary);
|
||||
if (!rawBytesFile.is_open()) {
|
||||
std::cerr << "Error: Could not open plist file: " << inStrPlist << std::endl;
|
||||
throw std::runtime_error("Error message"); // [learned] no need to return nullptr
|
||||
}
|
||||
|
||||
rawBytesFile.read(reinterpret_cast<char*>(rawBytes.data()), rawFileSize);
|
||||
rawBytesFile.close();
|
||||
// Start with output buffer 4x input size (compression ratio is usually < 4)
|
||||
size_t outAllocatedSize = rawFileSize * 8;
|
||||
// vector<uint8_t> automatically manages memory allocation/deallocation
|
||||
//std::vector<uint8_t> outBuffer(outAllocatedSize);
|
||||
outBuffer.resize(outAllocatedSize); // Resize preserves existing content
|
||||
|
||||
// LZFSE needs a scratch buffer for its internal operations
|
||||
// Get the required size and allocate it
|
||||
size_t scratchSize = lzfse_decode_scratch_size();
|
||||
std::vector<uint8_t> scratch(scratchSize);
|
||||
|
||||
// Decompress the data, growing the output buffer if needed
|
||||
//size_t decodedSize = 0;
|
||||
while (true) {
|
||||
// Try to decompress with current buffer size
|
||||
decodedSize = lzfse_decode_buffer(
|
||||
outBuffer.data(), // Where to store decompressed data
|
||||
outAllocatedSize, // Size of output buffer
|
||||
rawBytes.data(), // Source of compressed data
|
||||
rawBytes.size(), // Size of compressed data
|
||||
scratch.data()); // Scratch space for LZFSE
|
||||
|
||||
// Check if we need a larger buffer:
|
||||
// - decodedSize == 0 indicates failure
|
||||
// - decodedSize == outAllocatedSize might mean buffer was too small
|
||||
if (decodedSize == 0 || decodedSize == outAllocatedSize) {
|
||||
outAllocatedSize *= 2; // Double the buffer size
|
||||
outBuffer.resize(outAllocatedSize); // Resize preserves existing content
|
||||
continue; // Try again with larger buffer
|
||||
}
|
||||
break; // Successfully decompressed
|
||||
}
|
||||
|
||||
// Check if decompression failed
|
||||
if (decodedSize == 0) {
|
||||
std::cerr << "Failed to decompress data" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If requested, write the decompressed data to a file
|
||||
if (!outStrPlist.empty()) {
|
||||
std::ofstream outFile(outStrPlist, std::ios::binary);
|
||||
if (outFile) {
|
||||
outFile.write(reinterpret_cast<const char*>(outBuffer.data()), decodedSize);
|
||||
std::cout << "Wrote decompressed plist to: " << outStrPlist << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to write plist to file: " << outStrPlist << std::endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outBuffer.resize(rawFileSize);
|
||||
// if the file is not compressed, just read the raw bytes
|
||||
std::ifstream rawBytesFile(inStrPlist, std::ios::binary);
|
||||
if (!rawBytesFile.is_open()) {
|
||||
std::cerr << "Error: Could not open plist file: " << inStrPlist << std::endl;
|
||||
throw std::runtime_error("Error message"); // [learned] no need to return nullptr
|
||||
}
|
||||
rawBytesFile.read(reinterpret_cast<char*>(outBuffer.data()), rawFileSize);
|
||||
rawBytesFile.close();
|
||||
decodedSize = rawFileSize; // decodedSize is the same as rawFileSize when data is not compressed
|
||||
|
||||
|
||||
} // outBuffer now contains the raw bytes of the plist file
|
||||
|
||||
// Parse the decompressed data as a plist
|
||||
plist_t root_node = nullptr;
|
||||
plist_format_t format; // Will store the format of the plist (binary, xml, etc.)
|
||||
|
||||
// Convert the raw decompressed data into a plist structure
|
||||
plist_err_t err = plist_from_memory(
|
||||
reinterpret_cast<const char*>(outBuffer.data()), // Cast uint8_t* to char*
|
||||
static_cast<uint32_t>(decodedSize), // Cast size_t to uint32_t
|
||||
&root_node, // Where to store the parsed plist
|
||||
&format); // Where to store the format
|
||||
|
||||
// Check if parsing succeeded
|
||||
if (err != PLIST_ERR_SUCCESS) {
|
||||
std::cerr << "Failed to parse plist data" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return root_node; // Caller is responsible for calling plist_free()
|
||||
}
|
||||
|
||||
// Overload for when you only want to specify inStrPlist and decompress
|
||||
inline plist_t readPlist(const std::string& inStrPlist, bool decompress) {
|
||||
return readPlist(inStrPlist, "", decompress);
|
||||
}
|
||||
|
||||
// Structure to hold object/model information from VoxelMax's scene.json
|
||||
struct JsonModelInfo {
|
||||
std::string id;
|
||||
std::string parentId;
|
||||
std::string name;
|
||||
std::string dataFile; // The .vmaxb file
|
||||
std::string paletteFile; // The palette PNG
|
||||
std::string historyFile; // The history file, not sure what this is
|
||||
|
||||
// Transform information
|
||||
std::vector<double> position; // t_p
|
||||
std::vector<double> rotation; // t_r
|
||||
std::vector<double> scale; // t_s
|
||||
|
||||
// Extent information
|
||||
std::vector<double> extentCenter; // e_c
|
||||
std::vector<double> extentMin; // e_mi
|
||||
std::vector<double> extentMax; // e_ma
|
||||
};
|
||||
|
||||
// Structure to hold group information from VoxelMax's scene.json
|
||||
struct JsonGroupInfo {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::vector<double> position;
|
||||
std::vector<double> rotation;
|
||||
std::vector<double> scale;
|
||||
std::vector<double> extentCenter;
|
||||
std::vector<double> extentMin;
|
||||
std::vector<double> extentMax;
|
||||
bool selected = false;
|
||||
std::string parentId;
|
||||
};
|
||||
|
||||
// Class to parse VoxelMax's scene.json
|
||||
class JsonVmaxSceneParser {
|
||||
private:
|
||||
std::map<std::string, JsonModelInfo> models; // a vmax model can also be called a content
|
||||
std::map<std::string, JsonGroupInfo> groups;
|
||||
|
||||
public:
|
||||
bool parseScene(const std::string& jsonFilePath) {
|
||||
try {
|
||||
// Read the JSON file
|
||||
std::ifstream file(jsonFilePath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to open file: " << jsonFilePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the JSON
|
||||
json sceneData;
|
||||
file >> sceneData;
|
||||
file.close();
|
||||
|
||||
// Parse groups
|
||||
if (sceneData.contains("groups") && sceneData["groups"].is_array()) {
|
||||
for (const auto& group : sceneData["groups"]) {
|
||||
JsonGroupInfo groupInfo;
|
||||
|
||||
// Extract group information
|
||||
if (group.contains("id")) groupInfo.id = group["id"];
|
||||
if (group.contains("name")) groupInfo.name = group["name"];
|
||||
|
||||
// Extract transform data
|
||||
if (group.contains("t_p") && group["t_p"].is_array())
|
||||
groupInfo.position = group["t_p"].get<std::vector<double>>();
|
||||
|
||||
if (group.contains("t_r") && group["t_r"].is_array())
|
||||
groupInfo.rotation = group["t_r"].get<std::vector<double>>();
|
||||
|
||||
if (group.contains("t_s") && group["t_s"].is_array())
|
||||
groupInfo.scale = group["t_s"].get<std::vector<double>>();
|
||||
|
||||
// Extract extent data
|
||||
if (group.contains("e_c") && group["e_c"].is_array())
|
||||
groupInfo.extentCenter = group["e_c"].get<std::vector<double>>();
|
||||
|
||||
if (group.contains("e_mi") && group["e_mi"].is_array())
|
||||
groupInfo.extentMin = group["e_mi"].get<std::vector<double>>();
|
||||
|
||||
if (group.contains("e_ma") && group["e_ma"].is_array())
|
||||
groupInfo.extentMax = group["e_ma"].get<std::vector<double>>();
|
||||
|
||||
// Check if selected
|
||||
if (group.contains("s")) groupInfo.selected = group["s"].get<bool>();
|
||||
|
||||
if (group.contains("pid")) groupInfo.parentId = group["pid"];
|
||||
// Store the group
|
||||
groups[groupInfo.id] = groupInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse objects (models) , objects are instances of models
|
||||
// Maybe rename this objects, will leave for now
|
||||
if (sceneData.contains("objects") && sceneData["objects"].is_array()) {
|
||||
for (const auto& obj : sceneData["objects"]) {
|
||||
JsonModelInfo modelInfo;
|
||||
|
||||
// Extract model information
|
||||
if (obj.contains("id")) modelInfo.id = obj["id"];
|
||||
if (obj.contains("pid")) modelInfo.parentId = obj["pid"];
|
||||
if (obj.contains("n")) modelInfo.name = obj["n"];
|
||||
|
||||
// Extract file paths
|
||||
if (obj.contains("data")) modelInfo.dataFile = obj["data"];// This is the canonical model
|
||||
if (obj.contains("pal")) modelInfo.paletteFile = obj["pal"];
|
||||
if (obj.contains("hist")) modelInfo.historyFile = obj["hist"];
|
||||
|
||||
// Extract transform data
|
||||
if (obj.contains("t_p") && obj["t_p"].is_array())
|
||||
modelInfo.position = obj["t_p"].get<std::vector<double>>();
|
||||
|
||||
if (obj.contains("t_r") && obj["t_r"].is_array())
|
||||
modelInfo.rotation = obj["t_r"].get<std::vector<double>>();
|
||||
|
||||
if (obj.contains("t_s") && obj["t_s"].is_array())
|
||||
modelInfo.scale = obj["t_s"].get<std::vector<double>>();
|
||||
|
||||
// Extract extent data
|
||||
if (obj.contains("e_c") && obj["e_c"].is_array())
|
||||
modelInfo.extentCenter = obj["e_c"].get<std::vector<double>>();
|
||||
|
||||
if (obj.contains("e_mi") && obj["e_mi"].is_array())
|
||||
modelInfo.extentMin = obj["e_mi"].get<std::vector<double>>();
|
||||
|
||||
if (obj.contains("e_ma") && obj["e_ma"].is_array())
|
||||
modelInfo.extentMax = obj["e_ma"].get<std::vector<double>>();
|
||||
|
||||
// Store the model
|
||||
models[modelInfo.id] = modelInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error parsing JSON: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the parsed models
|
||||
const std::map<std::string, JsonModelInfo>& getModels() const {
|
||||
return models;
|
||||
}
|
||||
|
||||
// Get the parsed groups
|
||||
const std::map<std::string, JsonGroupInfo>& getGroups() const {
|
||||
return groups;
|
||||
}
|
||||
|
||||
/* Groups models by their data file names ie contentsN.vmaxb
|
||||
* Since we can instance models, we can grab the first one when translating it to Bella
|
||||
* @return Map where:
|
||||
* - Key: contentN.vmaxb (string)
|
||||
* - Value: vector of models using that data file
|
||||
* Example:
|
||||
* Input models: {"id1": model1, "id2": model2} where both use "data.file"
|
||||
* Output: {"data.file": [model1, model2]}
|
||||
*/
|
||||
std::map<std::string, std::vector<JsonModelInfo>> getModelContentVMaxbMap() const {
|
||||
std::map<std::string, std::vector<JsonModelInfo>> fileMap;
|
||||
|
||||
for (const auto& [id, model] : models) {
|
||||
fileMap[model.dataFile].push_back(model);
|
||||
}
|
||||
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
// Print summary of parsed data
|
||||
void printSummary() const {
|
||||
std::cout << "=========== Scene Summary ===========" << std::endl;
|
||||
std::cout << "Groups: " << groups.size() << std::endl;
|
||||
std::cout << "Models: " << models.size() << std::endl;
|
||||
|
||||
// Print unique model files
|
||||
std::map<std::string, int> modelFiles;
|
||||
for (const auto& [id, model] : models) {
|
||||
modelFiles[model.dataFile]++;
|
||||
}
|
||||
|
||||
std::cout << "\nModel Files:" << std::endl;
|
||||
for (const auto& [file, count] : modelFiles) {
|
||||
std::cout << " " << file << " (used " << count << " times)" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\nGroups:" << std::endl;
|
||||
for (const auto& [id, group] : groups) {
|
||||
std::cout << " " << group.name << " (ID: " << id << ")" << std::endl;
|
||||
if (!group.position.empty()) {
|
||||
std::cout << " Position: ["
|
||||
<< group.position[0] << ", "
|
||||
<< group.position[1] << ", "
|
||||
<< group.position[2] << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nModels:" << std::endl;
|
||||
for (const auto& [id, model] : models) {
|
||||
std::cout << " " << model.name << " (ID: " << id << ")" << std::endl;
|
||||
std::cout << " Data: " << model.dataFile << std::endl;
|
||||
std::cout << " Palette: " << model.paletteFile << std::endl;
|
||||
std::cout << " Parent: " << model.parentId << std::endl;
|
||||
|
||||
if (!model.position.empty()) {
|
||||
std::cout << " Position: ["
|
||||
<< model.position[0] << ", "
|
||||
<< model.position[1] << ", "
|
||||
<< model.position[2] << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* cnary.c
|
||||
*
|
||||
* Created on: Mar 9, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
puts("Creating root node");
|
||||
node_t root = node_create(NULL, NULL);
|
||||
|
||||
puts("Creating child 1 node");
|
||||
node_t one = node_create(root, NULL);
|
||||
puts("Creating child 2 node");
|
||||
node_t two = node_create(root, NULL);
|
||||
|
||||
puts("Creating child 3 node");
|
||||
node_t three = node_create(one, NULL);
|
||||
|
||||
puts("Debugging root node");
|
||||
node_debug(root);
|
||||
|
||||
puts("Destroying root node");
|
||||
node_destroy(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define PACKAGE_VERSION "2.4.0"
|
||||
#define PACKAGE "libplist"
|
||||
#define PACKAGE_BUGREPORT "https://github.com/libimobiledevice/libplist/issues"
|
||||
#define PACKAGE_NAME "libplist"
|
||||
#define PACKAGE_STRING "libplist 2.4.0"
|
||||
#define PACKAGE_TARNAME "libplist"
|
||||
#define PACKAGE_URL "https://libimobiledevice.org"
|
||||
|
||||
#endif /* CONFIG_H */
|
||||
|
||||
@ -1,119 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{A34F0D57-E086-45B3-9A7D-8A04CAD3AFEF}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>libplist</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_USRDLL;LIBPLIST_EXPORTS;_CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir)libcnary\include;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<DisableSpecificWarnings>4244;4267;4146</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\base64.c" />
|
||||
<ClCompile Include="src\bplist.c" />
|
||||
<ClCompile Include="src\bytearray.c" />
|
||||
<ClCompile Include="src\hashtable.c" />
|
||||
<ClCompile Include="src\jplist.c" />
|
||||
<ClCompile Include="src\jsmn.c" />
|
||||
<ClCompile Include="src\oplist.c" />
|
||||
<ClCompile Include="src\plist.c" />
|
||||
<ClCompile Include="src\ptrarray.c" />
|
||||
<ClCompile Include="src\time64.c" />
|
||||
<ClCompile Include="src\xplist.c" />
|
||||
<ClCompile Include="libcnary\node.c" />
|
||||
<ClCompile Include="libcnary\node_list.c" />
|
||||
<ClCompile Include="libcnary\cnary.c" />
|
||||
<ClCompile Include="src\out-default.c" />
|
||||
<ClCompile Include="src\out-limd.c" />
|
||||
<ClCompile Include="src\out-plutil.c" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\base64.h" />
|
||||
<ClInclude Include="src\bytearray.h" />
|
||||
<ClInclude Include="src\hashtable.h" />
|
||||
<ClInclude Include="src\jsmn.h" />
|
||||
<ClInclude Include="src\plist.h" />
|
||||
<ClInclude Include="src\ptrarray.h" />
|
||||
<ClInclude Include="src\strbuf.h" />
|
||||
<ClInclude Include="src\time64.h" />
|
||||
<ClInclude Include="src\time64_limits.h" />
|
||||
<ClInclude Include="include\plist\plist.h" />
|
||||
<ClInclude Include="src\config.h" />
|
||||
<ClInclude Include="libcnary\include\node.h" />
|
||||
<ClInclude Include="libcnary\include\node_list.h" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -173,8 +173,9 @@ The 't' field in the snapshot's 's.id' dictionary indicates the type of snapshot
|
||||
#include <iostream> // For input/output operations (cout, cin, etc.)
|
||||
|
||||
// Bella SDK includes - external libraries for 3D rendering
|
||||
#include "../bella_scene_sdk/src/bella_sdk/bella_scene.h" // For creating and manipulating 3D scenes in Bella
|
||||
#include "../bella_scene_sdk/src/dl_core/dl_main.inl" // Core functionality from the Diffuse Logic engine
|
||||
#include "../bella_engine_sdk/src/bella_sdk/bella_scene.h" // For creating and manipulating 3D scenes in Bella
|
||||
#include "../bella_engine_sdk/src/bella_sdk/bella_engine.h" // For Engine class and rendering functionality
|
||||
#include "../bella_engine_sdk/src/dl_core/dl_main.inl" // Core functionality from the Diffuse Logic engine
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // For ShellExecuteW
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user