first working groups and instances
This commit is contained in:
parent
7772f4e30b
commit
e1c2743bf8
2
makefile
2
makefile
@ -77,7 +77,7 @@ CXX_FLAGS = $(COMMON_FLAGS) -std=c++17 -Wno-deprecated-declarations
|
|||||||
CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED
|
CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED
|
||||||
|
|
||||||
# Objects
|
# Objects
|
||||||
OBJECTS = vmax2bella.o extra.o debug.o
|
OBJECTS = vmax2bella.o
|
||||||
OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS))
|
OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS))
|
||||||
|
|
||||||
# Build rules
|
# Build rules
|
||||||
|
|||||||
11
oomer_misc.h
11
oomer_misc.h
@ -153,6 +153,17 @@ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
(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)
|
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.
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
DayEnvironmentHDRI019_1K-TONEMAPPED.jpg from ambientCG.com,
|
||||||
|
licensed under the Creative Commons CC0 1.0 Universal License.
|
||||||
|
|
||||||
|
===
|
||||||
|
https://github.com/libimobiledevice/libplist
|
||||||
|
|
||||||
|
This software uses libraries from the libplist project under the LGPL version 2.1.
|
||||||
|
|
||||||
)";
|
)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map> // For std::map
|
// Standard C++ library includes - these provide essential functionality
|
||||||
#include <set> // For std::set
|
#include <map> // For key-value pair data structures (maps)
|
||||||
#include <vector> // For std::vector (you're already using this)
|
#include <set> // For set data structure
|
||||||
#include <string> // For std::string (you're already using this)
|
#include <vector> // For dynamic arrays (vectors)
|
||||||
#include <cstdint> // For uint8_t
|
#include <string> // For std::string
|
||||||
#include <fstream> // For std::ifstream and std::ofstream
|
#include <cstdint> // For fixed-size integer types (uint8_t, uint32_t, etc.)
|
||||||
#include <iostream> // For std::cerr, std::cout
|
#include <fstream> // For file operations (reading/writing files)
|
||||||
#include <filesystem> // For std::filesystem functions
|
#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 "../lzfse/src/lzfse.h"
|
||||||
#include "../libplist/include/plist/plist.h" // Library for handling Apple property list files
|
#include "../libplist/include/plist/plist.h" // Library for handling Apple property list files
|
||||||
@ -19,12 +20,172 @@ using json = nlohmann::json;
|
|||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "thirdparty/stb_image.h" // STB Image library
|
#include "thirdparty/stb_image.h" // STB Image library
|
||||||
|
|
||||||
struct VoxelRGBA {
|
|
||||||
|
|
||||||
|
struct VmaxVector3 {
|
||||||
|
double x, y, z;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts axis-angle rotation to Euler angles (in XYZ order)
|
||||||
|
// 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: Vector3 containing Euler angles (x=pitch, y=yaw, z=roll) in radians
|
||||||
|
VmaxVector3 axisAngleToEulerXYZ(double ax, double ay, double az, double angle) {
|
||||||
|
// First normalize the axis
|
||||||
|
double length = sqrt(ax*ax + ay*ay + az*az);
|
||||||
|
if (length != 0) {
|
||||||
|
ax /= length;
|
||||||
|
ay /= length;
|
||||||
|
az /= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute sin and cos of angle
|
||||||
|
double s = sin(angle);
|
||||||
|
double c = cos(angle);
|
||||||
|
double t = 1.0 - c;
|
||||||
|
|
||||||
|
// Build rotation matrix from axis-angle
|
||||||
|
// This is the Rodrigues' rotation formula
|
||||||
|
double m11 = t*ax*ax + c;
|
||||||
|
double m12 = t*ax*ay - s*az;
|
||||||
|
double m13 = t*ax*az + s*ay;
|
||||||
|
|
||||||
|
double m21 = t*ax*ay + s*az;
|
||||||
|
double m22 = t*ay*ay + c;
|
||||||
|
double m23 = t*ay*az - s*ax;
|
||||||
|
|
||||||
|
double m31 = t*ax*az - s*ay;
|
||||||
|
double m32 = t*ay*az + s*ax;
|
||||||
|
double m33 = t*az*az + c;
|
||||||
|
|
||||||
|
// Convert rotation matrix to euler angles
|
||||||
|
VmaxVector3 euler;
|
||||||
|
|
||||||
|
// Handle gimbal lock cases
|
||||||
|
if (m13 > 0.998) { // singularity at north pole
|
||||||
|
euler.y = atan2(m12, m22);
|
||||||
|
euler.x = M_PI/2;
|
||||||
|
euler.z = 0;
|
||||||
|
}
|
||||||
|
else if (m13 < -0.998) { // singularity at south pole
|
||||||
|
euler.y = atan2(m12, m22);
|
||||||
|
euler.x = -M_PI/2;
|
||||||
|
euler.z = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
euler.x = asin(-m13);
|
||||||
|
euler.y = atan2(m23, m33);
|
||||||
|
euler.z = atan2(m12, m11);
|
||||||
|
}
|
||||||
|
|
||||||
|
return euler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
// This is the default starting point for any transformation
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VmaxRGBA {
|
||||||
uint8_t r, g, b, a;
|
uint8_t r, g, b, a;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read a 256x1 PNG file and return a vector of VoxelRGBA colors
|
// Read a 256x1 PNG file and return a vector of VmaxRGBA colors
|
||||||
std::vector<VoxelRGBA> read256x1PaletteFromPNG(const std::string& filename) {
|
std::vector<VmaxRGBA> read256x1PaletteFromPNG(const std::string& filename) {
|
||||||
int width, height, channels;
|
int width, height, channels;
|
||||||
// Load the image with 4 desired channels (RGBA)
|
// Load the image with 4 desired channels (RGBA)
|
||||||
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, 4);
|
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &channels, 4);
|
||||||
@ -38,10 +199,10 @@ std::vector<VoxelRGBA> read256x1PaletteFromPNG(const std::string& filename) {
|
|||||||
std::cerr << "Warning: Expected a 256x1 image, but got " << width << "x" << height << std::endl;
|
std::cerr << "Warning: Expected a 256x1 image, but got " << width << "x" << height << std::endl;
|
||||||
}
|
}
|
||||||
// Create our palette array
|
// Create our palette array
|
||||||
std::vector<VoxelRGBA> palette;
|
std::vector<VmaxRGBA> palette;
|
||||||
// Read each pixel (each pixel is 4 bytes - RGBA)
|
// Read each pixel (each pixel is 4 bytes - RGBA)
|
||||||
for (int i = 0; i < width; i++) {
|
for (int i = 0; i < width; i++) {
|
||||||
VoxelRGBA color;
|
VmaxRGBA color;
|
||||||
color.r = data[i * 4];
|
color.r = data[i * 4];
|
||||||
color.g = data[i * 4 + 1];
|
color.g = data[i * 4 + 1];
|
||||||
color.b = data[i * 4 + 2];
|
color.b = data[i * 4 + 2];
|
||||||
@ -117,7 +278,7 @@ struct VmaxModel {
|
|||||||
// 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<VoxelRGBA, 256> colors;
|
std::array<VmaxRGBA, 256> colors;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
VmaxModel(const std::string& modelName) : vmaxbFileName(modelName) {
|
VmaxModel(const std::string& modelName) : vmaxbFileName(modelName) {
|
||||||
@ -136,7 +297,7 @@ struct VmaxModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a colors to this model
|
// Add a colors to this model
|
||||||
void addColors(const std::array<VoxelRGBA, 256> newColors) {
|
void addColors(const std::array<VmaxRGBA, 256> newColors) {
|
||||||
colors = newColors;
|
colors = newColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +344,7 @@ inline std::array<VmaxMaterial, 8> getVmaxMaterials(plist_t pnodPalettePlist) {
|
|||||||
plist_t materialsNode = plist_dict_get_item(pnodPalettePlist, "materials");
|
plist_t materialsNode = plist_dict_get_item(pnodPalettePlist, "materials");
|
||||||
if (materialsNode && plist_get_node_type(materialsNode) == PLIST_ARRAY) {
|
if (materialsNode && plist_get_node_type(materialsNode) == PLIST_ARRAY) {
|
||||||
uint32_t materialsCount = plist_array_get_size(materialsNode);
|
uint32_t materialsCount = plist_array_get_size(materialsNode);
|
||||||
std::cout << "Found materials array with " << materialsCount << " items" << std::endl;
|
//std::cout << "Found materials array with " << materialsCount << " items" << std::endl;
|
||||||
|
|
||||||
// Process each material
|
// Process each material
|
||||||
for (uint32_t i = 0; i < materialsCount; i++) {
|
for (uint32_t i = 0; i < materialsCount; i++) {
|
||||||
@ -229,7 +390,7 @@ inline std::array<VmaxMaterial, 8> getVmaxMaterials(plist_t pnodPalettePlist) {
|
|||||||
} else {
|
} else {
|
||||||
std::cout << "No materials array found or invalid type" << std::endl;
|
std::cout << "No materials array found or invalid type" << std::endl;
|
||||||
}
|
}
|
||||||
#ifdef _DEBUG2
|
#ifdef _DEBUG23
|
||||||
for (const auto& material : vmaxMaterials) {
|
for (const auto& material : vmaxMaterials) {
|
||||||
std::cout << "Material: " << material.materialName << std::endl;
|
std::cout << "Material: " << material.materialName << std::endl;
|
||||||
std::cout << " Transmission: " << material.transmission << std::endl;
|
std::cout << " Transmission: " << material.transmission << std::endl;
|
||||||
@ -561,6 +722,7 @@ struct JsonGroupInfo {
|
|||||||
std::vector<double> extentMin;
|
std::vector<double> extentMin;
|
||||||
std::vector<double> extentMax;
|
std::vector<double> extentMax;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
std::string parentId;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class to parse VoxelMax's scene.json
|
// Class to parse VoxelMax's scene.json
|
||||||
@ -616,12 +778,14 @@ public:
|
|||||||
// Check if selected
|
// Check if selected
|
||||||
if (group.contains("s")) groupInfo.selected = group["s"].get<bool>();
|
if (group.contains("s")) groupInfo.selected = group["s"].get<bool>();
|
||||||
|
|
||||||
|
if (group.contains("pid")) groupInfo.parentId = group["pid"];
|
||||||
// Store the group
|
// Store the group
|
||||||
groups[groupInfo.id] = groupInfo;
|
groups[groupInfo.id] = groupInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse objects (models)
|
// Parse objects (models) , objects are instances of models
|
||||||
|
// Maybe rename this objects, will leave for now
|
||||||
if (sceneData.contains("objects") && sceneData["objects"].is_array()) {
|
if (sceneData.contains("objects") && sceneData["objects"].is_array()) {
|
||||||
for (const auto& obj : sceneData["objects"]) {
|
for (const auto& obj : sceneData["objects"]) {
|
||||||
JsonModelInfo modelInfo;
|
JsonModelInfo modelInfo;
|
||||||
@ -632,7 +796,7 @@ public:
|
|||||||
if (obj.contains("n")) modelInfo.name = obj["n"];
|
if (obj.contains("n")) modelInfo.name = obj["n"];
|
||||||
|
|
||||||
// Extract file paths
|
// Extract file paths
|
||||||
if (obj.contains("data")) modelInfo.dataFile = obj["data"];
|
if (obj.contains("data")) modelInfo.dataFile = obj["data"];// This is the canonical model
|
||||||
if (obj.contains("pal")) modelInfo.paletteFile = obj["pal"];
|
if (obj.contains("pal")) modelInfo.paletteFile = obj["pal"];
|
||||||
if (obj.contains("hist")) modelInfo.historyFile = obj["hist"];
|
if (obj.contains("hist")) modelInfo.historyFile = obj["hist"];
|
||||||
|
|
||||||
@ -714,7 +878,7 @@ public:
|
|||||||
for (const auto& [file, count] : modelFiles) {
|
for (const auto& [file, count] : modelFiles) {
|
||||||
std::cout << " " << file << " (used " << count << " times)" << std::endl;
|
std::cout << " " << file << " (used " << count << " times)" << std::endl;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
std::cout << "\nGroups:" << std::endl;
|
std::cout << "\nGroups:" << std::endl;
|
||||||
for (const auto& [id, group] : groups) {
|
for (const auto& [id, group] : groups) {
|
||||||
std::cout << " " << group.name << " (ID: " << id << ")" << std::endl;
|
std::cout << " " << group.name << " (ID: " << id << ")" << std::endl;
|
||||||
@ -739,6 +903,15 @@ public:
|
|||||||
<< model.position[1] << ", "
|
<< model.position[1] << ", "
|
||||||
<< model.position[2] << "]" << std::endl;
|
<< model.position[2] << "]" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!model.rotation.empty()) {
|
||||||
|
VmaxVector3 VmaxEuler = axisAngleToEulerXYZ(model.rotation[0], model.rotation[1], model.rotation[2], model.rotation[3]);
|
||||||
|
std::cout << " Rotation: ["
|
||||||
|
<< VmaxEuler.x << ", "
|
||||||
|
<< VmaxEuler.y << ", "
|
||||||
|
<< VmaxEuler.z << "]" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
780
vmax2bella.cpp
780
vmax2bella.cpp
@ -1,26 +1,274 @@
|
|||||||
#include <iostream>
|
// vmax2bella.cpp - A program to convert VoxelMax (.vmax) files to Bella 3D scene (.bsz) files
|
||||||
#include "../bella_scene_sdk/src/bella_sdk/bella_scene.h"
|
//
|
||||||
#include "../bella_scene_sdk/src/dl_core/dl_main.inl"
|
// This program reads VoxelMax files (which store voxel-based 3D models) and
|
||||||
|
// converts them to Bella (a 3D rendering engine) scene files.
|
||||||
|
|
||||||
#include "oomer_voxel_vmax.h" // common voxel code and structures
|
/*
|
||||||
#include "oomer_misc.h"
|
# Technical Specification: VoxelMax Format
|
||||||
|
|
||||||
int addModelToScene(dl::bella_sdk::Scene& belScene, const VmaxModel& vmaxModel, const std::vector<VoxelRGBA>& vmaxPalette, const std::array<VmaxMaterial, 8>& vmaxMaterial);
|
## Overview
|
||||||
|
- This document specifies a chunked voxel storage format embedded in property list (plist) files. The format provides an efficient representation of 3D voxel data through a combination of Morton-encoded spatial indexing and a sparse representation approach.
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
- Format: Property List (plist)
|
||||||
|
- Structure: Hierarchical key-value structure with nested dictionaries and arrays
|
||||||
|
- plist is compressed using the LZFSE, an open source reference c implementation is [here](https://github.com/lzfse/lzfse)
|
||||||
|
|
||||||
|
```
|
||||||
|
root
|
||||||
|
└── snapshots (array)
|
||||||
|
└── Each snapshot (dictionary)
|
||||||
|
├── s (dictionary) - Snapshot data
|
||||||
|
│ ├── id (dictionary) - Identifiers
|
||||||
|
│ │ ├── c (int64) - Chunk ID
|
||||||
|
│ │ ├── s (int64) - Session ID
|
||||||
|
│ │ └── t (int64) - Type ID
|
||||||
|
│ ├── lc (binary data) - Layer Color Usage
|
||||||
|
│ ├── ds (binary data) - Voxel data stream
|
||||||
|
│ ├── dlc (binary data) - Deselected Layer Color Usage
|
||||||
|
│ └── st (dictionary) - Statistics/metadata
|
||||||
|
│ ├── c (int64) - Count of voxels in the chunk
|
||||||
|
│ ├── sc (int64) - Selected Count (number of selected voxels)
|
||||||
|
│ ├── smin (array) - Selected Minimum coordinates [x,y,z,w]
|
||||||
|
│ ├── smax (array) - Selected Maximum coordinates [x,y,z,w]
|
||||||
|
│ ├── min (array) - Minimum coordinates of all voxels [x,y,z]
|
||||||
|
│ ├── max (array) - Maximum coordinates of all voxels [x,y,z]
|
||||||
|
│ └── e (dictionary) - Extent
|
||||||
|
│ ├── o (array) - Origin/reference point [x,y,z]
|
||||||
|
│ └── s (array) - Size/dimensions [width,height,depth]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Chunking System
|
||||||
|
### Volume Organization
|
||||||
|
- The total volume is divided into chunks for efficient storage and manipulation
|
||||||
|
- Standard chunk size: 32×32×32 voxels
|
||||||
|
- Total addressable space: 256×256×256 voxels (8×8×8 chunks)
|
||||||
|
|
||||||
|
### Morton Encoding for Chunks
|
||||||
|
- Chunk IDs are encoded using 24 bits (8 bits per dimension)
|
||||||
|
- This provides full addressability for the 8×8×8 chunks without requiring sequential traversal
|
||||||
|
- The decodeMortonChunkID function extracts x, y, z coordinates from a Morton-encoded chunk ID stored in s.id.c
|
||||||
|
- The resulting chunk coordinates are then multiplied by 32 to get the world position of the chunk
|
||||||
|
|
||||||
|
### Voxel-Level Hybrid Encoding
|
||||||
|
- Within each 32×32×32 chunk, voxels use a hybrid addressing system
|
||||||
|
- The format uses a hybrid encoding approach that combines sequential traversal and Morton encoding:
|
||||||
|
- st.min store and offset from origin of 32x32x32 chunk
|
||||||
|
- iterate through all voxels in chunk x=0 to 31, y=0 to 31, z=0 to 31 it that order
|
||||||
|
- start at origin (0,0,0) with a counter= 0
|
||||||
|
- do counter + st.min and decode this morton value to get x,y,z
|
||||||
|
|
||||||
|
### Chunk Addressing
|
||||||
|
- Chunks are only stored if they contain at least one non-empty voxel
|
||||||
|
- Each snapshot contains data for a specific chunk, identified by the 'c' value in the 's.id' dictionary
|
||||||
|
|
||||||
|
## Data Fields
|
||||||
|
### Voxel Data Stream (ds)
|
||||||
|
- Variable-length binary data
|
||||||
|
- Contains pairs of bytes for each voxel: [layer_byte, color_byte]
|
||||||
|
- Each chunk can contain up to 32,768 voxels (32×32×32)
|
||||||
|
- *Position Byte:*
|
||||||
|
- The format uses a hybrid encoding approach that combines sequential traversal and Morton encoding:
|
||||||
|
- Data stream can terminate at any point, avoiding the need to store all 32,768 voxel pairs
|
||||||
|
|
||||||
|
### Morton Encoding Process
|
||||||
|
- A space-filling curve that interleaves the bits of the x, y, and z coordinates
|
||||||
|
- Used to convert 3D coordinates to a 1D index and vice versa
|
||||||
|
- Creates a coherent ordering of voxels that preserves spatial locality
|
||||||
|
1. Take the binary representation of x, y, and z coordinates
|
||||||
|
2. Interleave the bits in the order: z₀, y₀, x₀, z₁, y₁, x₁, z₂, y₂, x₂, ...
|
||||||
|
3. The resulting binary number is the Morton code
|
||||||
|
|
||||||
|
- *Color Byte:*
|
||||||
|
- Stores the color value + 1 (offset of +1 from actual color)
|
||||||
|
- Value 0 indicates no voxel at this position
|
||||||
|
- A fully populated chunk will have 32,768 voxel pairs (65,536 bytes total in ds)
|
||||||
|
|
||||||
|
### Snapshot Accumulation
|
||||||
|
- Each snapshot contains data for a specific chunk (identified by the chunk ID)
|
||||||
|
- Multiple snapshots together build up the complete voxel model
|
||||||
|
- Later snapshots for the same chunk ID overwrite earlier ones, allowing for edits over time
|
||||||
|
|
||||||
|
### Layer Color Usage (lc)
|
||||||
|
- s.lc is a summary table (256 bytes) that tracks which colors are used anywhere in the chunk
|
||||||
|
- Each byte position (0-255) corresponds to a color palette ID
|
||||||
|
- [TODO] understand why the word layer color is used, what is a layer color
|
||||||
|
|
||||||
|
### Deselected Layer Color Usage (dlc)
|
||||||
|
- Optional 256-byte array
|
||||||
|
- Used during editing to track which color layers the user has deselected
|
||||||
|
- Primarily for UI state preservation rather than 3D model representation
|
||||||
|
|
||||||
|
### Statistics Data (st)
|
||||||
|
- Dictionary containing metadata about the voxels in a chunk:
|
||||||
|
- c (count): Total number of voxels in the chunk
|
||||||
|
- sc (selectedCount): Number of currently selected voxels
|
||||||
|
- sMin (selectedMin): Array defining minimum coordinates of current selection [x,y,z,w]
|
||||||
|
- sMax (selectedMax): Array defining maximum coordinates of current selection [x,y,z,w]
|
||||||
|
- min: Array defining minimum coordinates of all voxels [x,y,z]
|
||||||
|
- max: Array defining maximum coordinates of all voxels [x,y,z]
|
||||||
|
- e (extent): Array defining the bounding box [min_x, min_y, min_z, max_x, max_y, max_z]
|
||||||
|
- e.o (extent.origin): Reference point or offset for extent calculations
|
||||||
|
|
||||||
|
## Coordinate Systems
|
||||||
|
### Primary Coordinate System
|
||||||
|
- Y-up coordinate system: Y is the vertical axis
|
||||||
|
- Origin (0,0,0) is at the bottom-left-front corner
|
||||||
|
- Coordinates increase toward right (X+), up (Y+), and backward (Z+)
|
||||||
|
|
||||||
|
### Addressing Scheme
|
||||||
|
1. World Space: Absolute coordinates in the full volume
|
||||||
|
2. Chunk Space: Which chunk contains a voxel (chunk_x, chunk_y, chunk_z)
|
||||||
|
3. Local Space: Coordinates within a chunk (local_x, local_y, local_z)
|
||||||
|
|
||||||
|
## Coordinate Conversion
|
||||||
|
- *World to Chunk:*
|
||||||
|
- chunk_x = floor(world_x / 32)
|
||||||
|
- chunk_y = floor(world_y / 32)
|
||||||
|
- chunk_z = floor(world_z / 32)
|
||||||
|
- *World to Local:*
|
||||||
|
- local_x = world_x % 32
|
||||||
|
- local_y = world_y % 32
|
||||||
|
- local_z = world_z % 32
|
||||||
|
- *Chunk+Local to World:*
|
||||||
|
- world_x = chunk_x * 32 + local_x
|
||||||
|
- world_y = chunk_y * 32 + local_y
|
||||||
|
- world_z = chunk_z * 32 + local_z
|
||||||
|
|
||||||
|
## Implementation Guidance
|
||||||
|
### Reading Algorithm
|
||||||
|
1. Parse the plist file to access the snapshot array
|
||||||
|
2. For each snapshot:
|
||||||
|
a. Extract the chunk ID from s > id > c
|
||||||
|
b. Extract the lc and ds data
|
||||||
|
c. Process the ds data in pairs of bytes (position, color)
|
||||||
|
d. Calculate the world origin by decoding the Morton chunk ID and multiplying by 32
|
||||||
|
e. Store the voxels for this chunk ID
|
||||||
|
3. Combine all snapshots to build the complete voxel model, using the chunk IDs as keys
|
||||||
|
|
||||||
|
### Writing Algorithm
|
||||||
|
1. Organize voxels by chunk (32×32×32 voxels per chunk)
|
||||||
|
2. For each non-empty chunk:
|
||||||
|
a. Create a snapshot entry
|
||||||
|
b. Set up the id dictionary with the appropriate chunk ID
|
||||||
|
c. Set up a 256-byte lc array (all zeros)
|
||||||
|
d. Create the ds data by encoding each voxel as a (position, color+1) pair
|
||||||
|
e. Set the appropriate byte in lc to 1 if the color is used in ds
|
||||||
|
3. Add all snapshots to the array
|
||||||
|
4. Write the complete structure to a plist file
|
||||||
|
|
||||||
|
- [?] Models typically use SessionIDs to group related edits (observed values include 10 and 18)
|
||||||
|
|
||||||
|
## Snapshot Types
|
||||||
|
The 't' field in the snapshot's 's.id' dictionary indicates the type of snapshot:
|
||||||
|
- 0: underRestore - Snapshot being restored from a previous state
|
||||||
|
- 1: redoRestore - Snapshot being restored during a redo operation
|
||||||
|
- 2: undo - Snapshot created for an undo operation
|
||||||
|
- 3: redo - Snapshot created for a redo operation
|
||||||
|
- 4: checkpoint - Snapshot created as a regular checkpoint during editing (most common)
|
||||||
|
- 5: selection - Snapshot representing a selection operation
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 "oomer_voxel_vmax.h" // common vmax voxel code and structures
|
||||||
|
#include "oomer_misc.h" // common misc code
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
int DL_main(dl::Args& args) {
|
int DL_main(dl::Args& args) {
|
||||||
args.add("he", "hello", "world", "Print a hello message with custom text");
|
args.add("i", "input", "", "vmax directory or vmax.zip file");
|
||||||
args.add("vi", "voxin", "voxin", "voxin");
|
args.add("o", "output", "", "set output bella file name");
|
||||||
|
args.add("tp", "thirdparty", "", "prints third party licenses");
|
||||||
|
args.add("li", "licenseinfo", "", "prints license info");
|
||||||
|
|
||||||
dl::String bszName;
|
// If --help was requested, print help and exit
|
||||||
dl::String vmaxDirName;
|
if (args.helpRequested()) {
|
||||||
if (args.have("--voxin"))
|
std::cout << args.help("vmax2bella © 2025 Harvey Fong","vmax2bella", "1.0") << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If --licenseinfo was requested, print license info and exit
|
||||||
|
if (args.have("--licenseinfo"))
|
||||||
{
|
{
|
||||||
vmaxDirName = args.value("--voxin");
|
std::cout << initializeGlobalLicense() << std::endl;
|
||||||
bszName = vmaxDirName.replace("vmax", "bsa");
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If --thirdparty was requested, print third-party licenses and exit
|
||||||
|
if (args.have("--thirdparty"))
|
||||||
|
{
|
||||||
|
std::cout << initializeGlobalThirdPartyLicences() << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.have("--input"))
|
||||||
|
{
|
||||||
|
dl::String bszName;
|
||||||
|
dl::String vmaxDirName;
|
||||||
|
vmaxDirName = args.value("--input");
|
||||||
|
bszName = vmaxDirName.replace("vmax", "bsz");
|
||||||
|
|
||||||
|
// Create a new scene
|
||||||
|
dl::bella_sdk::Scene belScene;
|
||||||
|
belScene.loadDefs();
|
||||||
|
auto belWorld = belScene.world(true);
|
||||||
|
|
||||||
|
// Parse the scene.json file to get the models
|
||||||
JsonVmaxSceneParser vmaxSceneParser;
|
JsonVmaxSceneParser vmaxSceneParser;
|
||||||
vmaxSceneParser.parseScene((vmaxDirName+"/scene.json").buf());
|
vmaxSceneParser.parseScene((vmaxDirName+"/scene.json").buf());
|
||||||
auto models = vmaxSceneParser.getModels();
|
//auto models = vmaxSceneParser.getModels();
|
||||||
|
vmaxSceneParser.printSummary();
|
||||||
|
std::map<std::string, JsonGroupInfo> jsonGroups = vmaxSceneParser.getGroups();
|
||||||
|
std::map<dl::String, dl::bella_sdk::Node> belGroupNodes; // Map of UUID to bella node
|
||||||
|
std::map<dl::String, dl::bella_sdk::Node> belCanonicalNodes; // Map of UUID to bella node
|
||||||
|
|
||||||
|
// First pass to create all the Bella nodes for the groups
|
||||||
|
for (const auto& [groupName, groupInfo] : jsonGroups) {
|
||||||
|
dl::String belGroupUUID = dl::String(groupName.c_str());
|
||||||
|
belGroupUUID = belGroupUUID.replace("-", "_"); // Make sure the group name is valid for a Bella node name
|
||||||
|
belGroupUUID = "_" + belGroupUUID; // Make sure the group name is valid for a Bella node name
|
||||||
|
belGroupNodes[belGroupUUID] = belScene.createNode("xform", belGroupUUID, belGroupUUID); // Create a Bella node for the group
|
||||||
|
VmaxMatrix4x4 objectMat4 = axisAngleToMatrix4x4( groupInfo.rotation[0],
|
||||||
|
groupInfo.rotation[1],
|
||||||
|
groupInfo.rotation[2],
|
||||||
|
groupInfo.rotation[3]);
|
||||||
|
VmaxMatrix4x4 objectTransMat4 = VmaxMatrix4x4();
|
||||||
|
//std::vector<double> translation = extentCenter-position;
|
||||||
|
objectTransMat4 = objectTransMat4.createTranslation(groupInfo.position[0],
|
||||||
|
groupInfo.position[1],
|
||||||
|
groupInfo.position[2]);
|
||||||
|
objectMat4 = objectMat4 * objectTransMat4;
|
||||||
|
|
||||||
|
belGroupNodes[belGroupUUID]["steps"][0]["xform"] = dl::Mat4({
|
||||||
|
objectMat4.m[0][0], objectMat4.m[0][1], objectMat4.m[0][2], objectMat4.m[0][3],
|
||||||
|
objectMat4.m[1][0], objectMat4.m[1][1], objectMat4.m[1][2], objectMat4.m[1][3],
|
||||||
|
objectMat4.m[2][0], objectMat4.m[2][1], objectMat4.m[2][2], objectMat4.m[2][3],
|
||||||
|
objectMat4.m[3][0], objectMat4.m[3][1], objectMat4.m[3][2], objectMat4.m[3][3]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// json file is allowed the parent to be defined after the child, requiring us to create all the bella nodes before we can parent them
|
||||||
|
for (const auto& [groupName, groupInfo] : jsonGroups) {
|
||||||
|
dl::String belGroupUUID = dl::String(groupName.c_str());
|
||||||
|
belGroupUUID = belGroupUUID.replace("-", "_");
|
||||||
|
belGroupUUID = "_" + belGroupUUID;
|
||||||
|
if (groupInfo.parentId == "") {
|
||||||
|
belGroupNodes[belGroupUUID].parentTo(belWorld); // Group without a parent is a child of the world
|
||||||
|
} else {
|
||||||
|
dl::String belPPPGroupUUID = dl::String(groupInfo.parentId.c_str());
|
||||||
|
belPPPGroupUUID = belPPPGroupUUID.replace("-", "_");
|
||||||
|
belPPPGroupUUID = "_" + belPPPGroupUUID;
|
||||||
|
dl::bella_sdk::Node myParentGroup = belGroupNodes[belPPPGroupUUID]; // Get bella obj
|
||||||
|
belGroupNodes[belGroupUUID].parentTo(myParentGroup); // Group underneath a group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Efficiently process unique models by examining only the first instance of each model type.
|
// Efficiently process unique models by examining only the first instance of each model type.
|
||||||
// Example: If we have 100 instances of 3 different models:
|
// Example: If we have 100 instances of 3 different models:
|
||||||
@ -28,67 +276,51 @@ int DL_main(dl::Args& args) {
|
|||||||
// "model2.vmaxb": [instance1, ..., instance30],
|
// "model2.vmaxb": [instance1, ..., instance30],
|
||||||
// "model3.vmaxb": [instance1, ..., instance20]
|
// "model3.vmaxb": [instance1, ..., instance20]
|
||||||
// This loop runs only 3 times (once per unique model), not 100 times (once per instance)
|
// This loop runs only 3 times (once per unique model), not 100 times (once per instance)
|
||||||
//std::map<std::string, std::vector<JsonModelInfo>> modelVmaxbMap = vmaxSceneParser.getModelContentVMaxbMap();
|
|
||||||
auto modelVmaxbMap = vmaxSceneParser.getModelContentVMaxbMap();
|
auto modelVmaxbMap = vmaxSceneParser.getModelContentVMaxbMap();
|
||||||
for (auto& model : modelVmaxbMap) {
|
std::vector<VmaxModel> allModels;
|
||||||
for (const auto& [vmaxContentName, vmaxModelList] : modelVmaxbMap) {
|
std::vector<std::vector<VmaxRGBA>> vmaxPalettes; // one palette per model
|
||||||
std::cout << "model: " << vmaxContentName << std::endl;
|
std::vector<std::array<VmaxMaterial, 8>> vmaxMaterials; // one material per model
|
||||||
const auto& jsonModelInfo = vmaxModelList.front(); // get the first model, others are instances at the scene level
|
//std::vector<std::array<VmaxMaterial, 8>> allMaterials; // one material per model
|
||||||
std::cout << "name: " << jsonModelInfo.name << std::endl;
|
//std::vector<std::vector<VmaxRGBA>> allPalettes;
|
||||||
std::cout << "content: " << jsonModelInfo.dataFile << std::endl;
|
|
||||||
std::cout << "palette: " << jsonModelInfo.paletteFile << std::endl;
|
|
||||||
dl::String materialName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str();
|
|
||||||
materialName = materialName.replace(".png", ".settings.vmaxpsb");
|
|
||||||
std::cout << "material: " << materialName << std::endl;
|
|
||||||
plist_t pnod_material = readPlist(materialName.buf(),false);
|
|
||||||
std::cout << "pnod_material: " << pnod_material << std::endl;
|
|
||||||
std::array<VmaxMaterial, 8> vmaxMaterials = getVmaxMaterials(pnod_material);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.helpRequested()) {
|
essentialsToScene(belScene); // create the basic scene elements in Bella
|
||||||
std::cout << args.help("Hello App", "hello", "1.0") << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.have("--hello")) {
|
// Loop over each model defined in scene.json and process the first instance
|
||||||
dl::String helloText = args.value("--hello", "world");
|
// This will be out canonical models, not instances
|
||||||
}
|
// todo rename model to objects as per vmax
|
||||||
|
|
||||||
// Create a new scene
|
|
||||||
dl::bella_sdk::Scene belScene;
|
|
||||||
belScene.loadDefs();
|
|
||||||
|
|
||||||
// Root node
|
|
||||||
auto belWorld = belScene.world(true);
|
|
||||||
|
|
||||||
JsonVmaxSceneParser vmaxSceneParser;
|
|
||||||
vmaxSceneParser.parseScene((vmaxDirName+"/scene.json").buf());
|
|
||||||
auto models = vmaxSceneParser.getModels();
|
|
||||||
|
|
||||||
// Efficiently process unique models by examining only the first instance of each model type.
|
|
||||||
// Example: If we have 100 instances of 3 different models:
|
|
||||||
// "model1.vmaxb": [instance1, instance2, ..., instance50],
|
|
||||||
// "model2.vmaxb": [instance1, ..., instance30],
|
|
||||||
// "model3.vmaxb": [instance1, ..., instance20]
|
|
||||||
// This loop runs only 3 times (once per unique model), not 100 times (once per instance)
|
|
||||||
//std::map<std::string, std::vector<JsonModelInfo>> modelVmaxbMap = vmaxSceneParser.getModelContentVMaxbMap();
|
|
||||||
|
|
||||||
auto modelVmaxbMap = vmaxSceneParser.getModelContentVMaxbMap();
|
|
||||||
|
|
||||||
std::vector<std::vector<VoxelRGBA>> vmaxPalettes;
|
|
||||||
std::vector<std::array<VmaxMaterial, 8>> vmaxMaterials;
|
|
||||||
std::vector<VmaxModel> allModels;
|
|
||||||
std::vector<std::array<VmaxMaterial, 8>> allMaterials; // dynamic vector of fixed arrays
|
|
||||||
std::vector<std::vector<VoxelRGBA>> allPalettes;
|
|
||||||
for (auto& model : modelVmaxbMap) {
|
|
||||||
for (const auto& [vmaxContentName, vmaxModelList] : modelVmaxbMap) {
|
for (const auto& [vmaxContentName, vmaxModelList] : modelVmaxbMap) {
|
||||||
std::cout << "model: " << vmaxContentName << std::endl;
|
std::cout << "vmaxContentName: " << vmaxContentName << std::endl;
|
||||||
|
VmaxModel currentVmaxModel(vmaxContentName);
|
||||||
const auto& jsonModelInfo = vmaxModelList.front(); // get the first model, others are instances at the scene level
|
const auto& jsonModelInfo = vmaxModelList.front(); // get the first model, others are instances at the scene level
|
||||||
std::cout << "name: " << jsonModelInfo.name << std::endl;
|
std::vector<double> position = jsonModelInfo.position;
|
||||||
std::cout << "content: " << jsonModelInfo.dataFile << std::endl;
|
std::vector<double> rotation = jsonModelInfo.rotation;
|
||||||
std::cout << "palette: " << jsonModelInfo.paletteFile << std::endl;
|
std::vector<double> scale = jsonModelInfo.scale;
|
||||||
|
std::vector<double> extentCenter = jsonModelInfo.extentCenter;
|
||||||
|
|
||||||
|
//what is this for?
|
||||||
|
/*
|
||||||
|
auto jsonParentId = jsonModelInfo.parentId;
|
||||||
|
auto belParentId = dl::String(jsonParentId.c_str());
|
||||||
|
dl::String belParentGroupUUID = belParentId.replace("-", "_");
|
||||||
|
if (jsonParentId != "") {
|
||||||
|
belGroupNodes[belParentGroupUUID].parentTo(belWorld);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
VmaxMatrix4x4 modelMatrix = axisAngleToMatrix4x4(rotation[0], rotation[1], rotation[2], rotation[3]);
|
||||||
|
VmaxMatrix4x4 transMatrix = VmaxMatrix4x4();
|
||||||
|
//std::vector<double> translation = extentCenter-position;
|
||||||
|
//transMatrix = transMatrix.createTranslation(extentCenter[0]-position[0], extentCenter[1]-position[1], extentCenter[2]-position[2]);
|
||||||
|
transMatrix = transMatrix.createTranslation(position[0],
|
||||||
|
position[1],
|
||||||
|
position[2]);
|
||||||
|
modelMatrix = transMatrix*modelMatrix;
|
||||||
|
//std::cout << modelMatrix.m[0][0] << "," << modelMatrix.m[0][1] << "," << modelMatrix.m[0][2] << "," << modelMatrix.m[0][3] << std::endl;
|
||||||
|
//std::cout << modelMatrix.m[1][0] << "," << modelMatrix.m[1][1] << "," << modelMatrix.m[1][2] << "," << modelMatrix.m[1][3] << std::endl;
|
||||||
|
//std::cout << modelMatrix.m[2][0] << "," << modelMatrix.m[2][1] << "," << modelMatrix.m[2][2] << "," << modelMatrix.m[2][3] << std::endl;
|
||||||
|
//std::cout << modelMatrix.m[3][0] << "," << modelMatrix.m[3][1] << "," << modelMatrix.m[3][2] << "," << modelMatrix.m[3][3] << std::endl;
|
||||||
|
|
||||||
// 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");
|
||||||
@ -96,35 +328,19 @@ int DL_main(dl::Args& args) {
|
|||||||
// Get this models colors from the paletteN.png
|
// Get this models colors from the paletteN.png
|
||||||
dl::String pngName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str();
|
dl::String pngName = vmaxDirName + "/" + jsonModelInfo.paletteFile.c_str();
|
||||||
vmaxPalettes.push_back(read256x1PaletteFromPNG(pngName.buf())); // gather all models palettes
|
vmaxPalettes.push_back(read256x1PaletteFromPNG(pngName.buf())); // gather all models palettes
|
||||||
allPalettes.push_back(read256x1PaletteFromPNG(pngName.buf())); // gather all models palettes
|
//allPalettes.push_back(read256x1PaletteFromPNG(pngName.buf())); // gather all models palettes
|
||||||
if (vmaxPalettes.empty()) { throw std::runtime_error("Failed to read palette from: png " ); }
|
if (vmaxPalettes.empty()) { throw std::runtime_error("Failed to read palette from: png " ); }
|
||||||
|
|
||||||
// lstSnapshots contains ALL snapshots
|
|
||||||
// if a chunkID is present more than once it holds the past history of a snapshot's state
|
|
||||||
// The playback of each of these snapshots is the entire creation history of a chunk
|
|
||||||
// Therefore a chunkID's last snapshot is the latest user edit
|
|
||||||
// Therefore we need to process each chunkdID's snapshot in reverse order and ignore
|
|
||||||
// all previous snapshots for that chunkID unless we need animation
|
|
||||||
// [todo] implement animation
|
|
||||||
|
|
||||||
// Read contentsN.vmaxb plist file, lzfse compressed
|
// Read contentsN.vmaxb plist file, lzfse compressed
|
||||||
dl::String modelName2 = vmaxDirName + "/" + jsonModelInfo.dataFile.c_str();
|
dl::String modelFileName = vmaxDirName + "/" + jsonModelInfo.dataFile.c_str();
|
||||||
plist_t plist_model_root = readPlist(modelName2.buf(), true); // decompress=true
|
plist_t plist_model_root = readPlist(modelFileName.buf(), true); // decompress=true
|
||||||
|
|
||||||
plist_t plist_snapshots_array = plist_dict_get_item(plist_model_root, "snapshots");
|
plist_t plist_snapshots_array = plist_dict_get_item(plist_model_root, "snapshots");
|
||||||
uint32_t snapshots_array_size = plist_array_get_size(plist_snapshots_array);
|
uint32_t snapshots_array_size = plist_array_get_size(plist_snapshots_array);
|
||||||
std::cout << "snapshots_array_size: " << snapshots_array_size << std::endl;
|
//std::cout << "snapshots_array_size: " << snapshots_array_size << std::endl;
|
||||||
|
|
||||||
// Create a VmaxModel object
|
// Create a VmaxModel object
|
||||||
VmaxModel currentVmaxModel(vmaxContentName);
|
//VmaxModel currentVmaxModel(vmaxContentName);
|
||||||
|
|
||||||
// Add a voxel to this model
|
|
||||||
//void addVoxel(int x, int y, int z, int material, int color, int chunk, int chunkMin) {
|
|
||||||
// if (material >= 0 && material < 8 && color > 0 && color < 256) {
|
|
||||||
// voxels[material][color].emplace_back(x, y, z, material, color, chunk, chunkMin);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < snapshots_array_size; i++) {
|
for (uint32_t i = 0; i < snapshots_array_size; i++) {
|
||||||
plist_t plist_snapshot = plist_array_get_item(plist_snapshots_array, i);
|
plist_t plist_snapshot = plist_array_get_item(plist_snapshots_array, i);
|
||||||
plist_t plist_chunk = getNestedPlistNode(plist_snapshot, {"s", "id", "c"});
|
plist_t plist_chunk = getNestedPlistNode(plist_snapshot, {"s", "id", "c"});
|
||||||
@ -132,13 +348,13 @@ int DL_main(dl::Args& args) {
|
|||||||
uint64_t chunkID;
|
uint64_t chunkID;
|
||||||
plist_get_uint_val(plist_chunk, &chunkID);
|
plist_get_uint_val(plist_chunk, &chunkID);
|
||||||
VmaxChunkInfo chunkInfo = vmaxChunkInfo(plist_snapshot);
|
VmaxChunkInfo chunkInfo = vmaxChunkInfo(plist_snapshot);
|
||||||
std::cout << "\nChunkID: " << chunkInfo.id << std::endl;
|
//std::cout << "\nChunkID: " << chunkInfo.id << std::endl;
|
||||||
std::cout << "TypeID: " << chunkInfo.type << std::endl;
|
//std::cout << "TypeID: " << chunkInfo.type << std::endl;
|
||||||
std::cout << "MortonCode: " << chunkInfo.mortoncode << "\n" <<std::endl;
|
//std::cout << "MortonCode: " << chunkInfo.mortoncode << "\n" <<std::endl;
|
||||||
|
|
||||||
|
|
||||||
std::vector<VmaxVoxel> xvoxels = vmaxVoxelInfo(plist_datastream, chunkInfo.id, chunkInfo.mortoncode);
|
std::vector<VmaxVoxel> xvoxels = vmaxVoxelInfo(plist_datastream, chunkInfo.id, chunkInfo.mortoncode);
|
||||||
std::cout << "xxxvoxels: " << xvoxels.size() << std::endl;
|
//std::cout << "xxxvoxels: " << xvoxels.size() << std::endl;
|
||||||
|
|
||||||
for (const auto& voxel : xvoxels) {
|
for (const auto& voxel : xvoxels) {
|
||||||
currentVmaxModel.addVoxel(voxel.x, voxel.y, voxel.z, voxel.material, voxel.palette ,chunkInfo.id, chunkInfo.mortoncode);
|
currentVmaxModel.addVoxel(voxel.x, voxel.y, voxel.z, voxel.material, voxel.palette ,chunkInfo.id, chunkInfo.mortoncode);
|
||||||
@ -149,75 +365,114 @@ int DL_main(dl::Args& args) {
|
|||||||
plist_t plist_material = readPlist(materialName.buf(),false); // decompress=false
|
plist_t plist_material = readPlist(materialName.buf(),false); // decompress=false
|
||||||
std::array<VmaxMaterial, 8> currentMaterials = getVmaxMaterials(plist_material);
|
std::array<VmaxMaterial, 8> currentMaterials = getVmaxMaterials(plist_material);
|
||||||
vmaxMaterials.push_back(currentMaterials);
|
vmaxMaterials.push_back(currentMaterials);
|
||||||
allMaterials.push_back(currentMaterials);
|
}
|
||||||
//allPalettes.push_back(vmaxPalettes);
|
//}
|
||||||
int modelIndex=0;
|
int modelIndex=0;
|
||||||
// Need to access voxles by material and color groupings
|
// Need to access voxles by material and color groupings
|
||||||
for (const auto& eachModel : allModels) {
|
// Models are canonical models, not instances
|
||||||
if (modelIndex == 0) {
|
// Vmax objects are instances of models
|
||||||
std::cout << "Model: " << eachModel.vmaxbFileName << std::endl;
|
|
||||||
std::cout << "Voxel Count Model: " << eachModel.getTotalVoxelCount() << std::endl;
|
|
||||||
|
|
||||||
addModelToScene(belScene, eachModel, allPalettes[modelIndex], allMaterials[modelIndex]);
|
// First create canonical models and they are NOT attached to belWorld
|
||||||
std::map<int, std::set<int>> materialColorMap = eachModel.getUsedMaterialsAndColors();
|
for (const auto& eachModel : allModels) {
|
||||||
//auto materialColorMap = eachModel.getUsedMaterialsAndColors();
|
//if (modelIndex == 0) { // only process the first model
|
||||||
for (const auto& [eachMaterial, eachColorPalette] : materialColorMap) {
|
std::cout << modelIndex << "Model: " << eachModel.vmaxbFileName << std::endl;
|
||||||
// Create Bella material for this material type
|
std::cout << "Voxel Count Model: " << eachModel.getTotalVoxelCount() << std::endl;
|
||||||
// ... your Bella material creation code here ...
|
|
||||||
std::cout << eachMaterial << std::endl;
|
dl::bella_sdk::Node belModel = addModelToScene(belScene, belWorld, eachModel, vmaxPalettes[modelIndex], vmaxMaterials[modelIndex]);
|
||||||
// Iterate through each color used by this material
|
// TODO add to a map of canonical models
|
||||||
for (int eachColor : eachColorPalette) {
|
dl::String lllmodelName = dl::String(eachModel.vmaxbFileName.c_str());
|
||||||
// Get all voxels for this material/color combination
|
dl::String lllcanonicalName = lllmodelName.replace(".vmaxb", "");
|
||||||
const std::vector<VmaxVoxel>& voxelsOfType = eachModel.getVoxels(eachMaterial, eachColor);
|
std::cout << "========lllcanonicalName: " << lllcanonicalName.buf() << std::endl;
|
||||||
std::cout << "voxelsOfType: " << voxelsOfType.size() << std::endl;
|
belCanonicalNodes[lllcanonicalName.buf()] = belModel;
|
||||||
// Now voxelsOfType contains all voxels sharing this material/color
|
modelIndex++;
|
||||||
// Create a single Bella object for all these voxels
|
}
|
||||||
// ... your Bella object creation code here ...
|
|
||||||
}
|
// Second Loop through each vmax object and create an instance of the canonical model
|
||||||
}
|
// This is the instances of the models, we did a pass to create the canonical models earlier
|
||||||
|
for (const auto& [vmaxContentName, vmaxModelList] : modelVmaxbMap) {
|
||||||
|
//std::cout << "model: " << vmaxContentName << std::endl;
|
||||||
|
VmaxModel currentVmaxModel(vmaxContentName);
|
||||||
|
for(const auto& jsonModelInfo : vmaxModelList) {
|
||||||
|
std::vector<double> position = jsonModelInfo.position;
|
||||||
|
std::vector<double> rotation = jsonModelInfo.rotation;
|
||||||
|
std::vector<double> scale = jsonModelInfo.scale;
|
||||||
|
std::vector<double> extentCenter = jsonModelInfo.extentCenter;
|
||||||
|
auto jsonParentId = jsonModelInfo.parentId;
|
||||||
|
auto belParentId = dl::String(jsonParentId.c_str());
|
||||||
|
dl::String belParentGroupUUID = belParentId.replace("-", "_");
|
||||||
|
belParentGroupUUID = "_" + belParentGroupUUID;
|
||||||
|
|
||||||
|
auto belObjectId = dl::String(jsonModelInfo.id.c_str());
|
||||||
|
belObjectId = belObjectId.replace("-", "_");
|
||||||
|
belObjectId = "_" + belObjectId;
|
||||||
|
|
||||||
|
dl::String getCanonicalName = dl::String(jsonModelInfo.dataFile.c_str());
|
||||||
|
dl::String canonicalName = getCanonicalName.replace(".vmaxb", "");
|
||||||
|
//get bel node from canonical name
|
||||||
|
auto belCanonicalNode = belCanonicalNodes[canonicalName.buf()];
|
||||||
|
// create a new xform
|
||||||
|
auto foofoo = belScene.findNode(canonicalName);
|
||||||
|
|
||||||
|
VmaxMatrix4x4 objectMat4 = axisAngleToMatrix4x4( rotation[0],
|
||||||
|
rotation[1],
|
||||||
|
rotation[2],
|
||||||
|
rotation[3]);
|
||||||
|
VmaxMatrix4x4 objectTransMat4 = VmaxMatrix4x4();
|
||||||
|
//std::vector<double> translation = extentCenter-position;
|
||||||
|
/*objectTransMat4 = objectTransMat4.createTranslation(extentCenter[0]-position[0],
|
||||||
|
extentCenter[1]-position[1],
|
||||||
|
extentCenter[2]-position[2]);*/
|
||||||
|
objectTransMat4 = objectTransMat4.createTranslation(position[0],
|
||||||
|
position[1],
|
||||||
|
position[2]);
|
||||||
|
objectMat4 = objectMat4 * objectTransMat4;
|
||||||
|
|
||||||
|
auto belNodeObjectInstance = belScene.createNode("xform", belObjectId, belObjectId);
|
||||||
|
belNodeObjectInstance["steps"][0]["xform"] = dl::Mat4({
|
||||||
|
objectMat4.m[0][0], objectMat4.m[0][1], objectMat4.m[0][2], objectMat4.m[0][3],
|
||||||
|
objectMat4.m[1][0], objectMat4.m[1][1], objectMat4.m[1][2], objectMat4.m[1][3],
|
||||||
|
objectMat4.m[2][0], objectMat4.m[2][1], objectMat4.m[2][2], objectMat4.m[2][3],
|
||||||
|
objectMat4.m[3][0], objectMat4.m[3][1], objectMat4.m[3][2], objectMat4.m[3][3]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (jsonParentId == "") {
|
||||||
|
belNodeObjectInstance.parentTo(belScene.world());
|
||||||
|
} else {
|
||||||
|
belNodeObjectInstance.parentTo(belGroupNodes[belParentGroupUUID]);
|
||||||
}
|
}
|
||||||
modelIndex++;
|
foofoo.parentTo(belNodeObjectInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write Bella File .bsz=compressed .bsa=ascii .bsx=binary
|
||||||
|
belScene.write(bszName.buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Write Bella File .bsz=compressed .bsa=ascii .bsx=binary
|
|
||||||
std::cout << "writing to: " << bszName.buf() << std::endl;
|
|
||||||
belScene.write(bszName.buf());
|
|
||||||
//writeBszScene(bszName.buf(), AllModels[0], vmaxPalettes);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int addModelToScene(dl::bella_sdk::Scene& belScene, const VmaxModel& vmaxModel, const std::vector<VoxelRGBA>& vmaxPalette, const std::array<VmaxMaterial, 8>& vmaxMaterial) {
|
|
||||||
// Create a new Bella scene
|
|
||||||
|
|
||||||
|
// @param belScene - the scene to create the essentials in
|
||||||
|
// @return - the world node
|
||||||
|
dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene) {
|
||||||
// Create the basic scene elements in Bella
|
// Create the basic scene elements in Bella
|
||||||
// Each line creates a different type of node in the scene
|
// Each line creates a different type of node in the scene auto belBeautyPass = belScene.createNode("beautyPass","oomerBeautyPass","oomerBeautyPass");
|
||||||
auto belBeautyPass = belScene.createNode("beautyPass","beautyPass1","beautyPass1");
|
auto belWorld = belScene.world(); // Get scene world root
|
||||||
auto belCamForm = belScene.createNode("xform","cameraXform1","cameraXform1");
|
|
||||||
auto belCam = belScene.createNode("camera","camera1","camera1");
|
|
||||||
auto belSensor = belScene.createNode("sensor","sensor1","sensor1");
|
|
||||||
auto belLens = belScene.createNode("thinLens","thinLens1","thinLens1");
|
|
||||||
auto belImageDome = belScene.createNode("imageDome","imageDome1","imageDome1");
|
|
||||||
auto belGroundPlane = belScene.createNode("groundPlane","groundPlane1","groundPlane1");
|
|
||||||
auto belVoxel = belScene.createNode("box","box1","box1");
|
|
||||||
auto belVoxelForm = belScene.createNode("xform","voxelXform1","voxelXform1");
|
|
||||||
auto belVoxelMat = belScene.createNode("orenNayar","voxelMat1","voxelMat1");
|
|
||||||
auto belGroundMat = belScene.createNode("quickMaterial","groundMat1","groundMat1");
|
|
||||||
auto belSun = belScene.createNode("sun","sun1","sun1");
|
|
||||||
auto belColorDome = belScene.createNode("colorDome","colorDome1","colorDome1");
|
|
||||||
auto oomerSmoothCube = belScene.createNode("mesh", "oomerSmoothCube");
|
|
||||||
|
|
||||||
// Set up the scene with an EventScope
|
|
||||||
// EventScope groups multiple changes together for efficiency
|
|
||||||
{
|
{
|
||||||
#include "smoothcube.h"
|
|
||||||
dl::bella_sdk::Scene::EventScope es(belScene);
|
dl::bella_sdk::Scene::EventScope es(belScene);
|
||||||
auto belSettings = belScene.settings(); // Get scene settings
|
|
||||||
auto belWorld = belScene.world(); // Get scene world root
|
|
||||||
|
|
||||||
|
auto belCamForm = belScene.createNode("xform","oomerCameraXform","oomerCameraXform");
|
||||||
|
auto belCam = belScene.createNode("camera","oomerCamera","oomerCamera");
|
||||||
|
auto belSensor = belScene.createNode("sensor","oomerSensor","oomerSensor");
|
||||||
|
auto belLens = belScene.createNode("thinLens","oomerThinLens","oomerThinLens");
|
||||||
|
auto belImageDome = belScene.createNode("imageDome","oomerImageDome","oomerImageDome");
|
||||||
|
auto belGroundPlane = belScene.createNode("groundPlane","oomerGroundPlane","oomerGroundPlane");
|
||||||
|
|
||||||
|
auto belBeautyPass = belScene.createNode("beautyPass","oomerBeautyPass","oomerBeautyPass");
|
||||||
|
auto belGroundMat = belScene.createNode("quickMaterial","oomerGroundMat","oomerGroundMat");
|
||||||
|
auto belSun = belScene.createNode("sun","oomerSun","oomerSun");
|
||||||
|
auto belColorDome = belScene.createNode("colorDome","oomerColorDome","oomerColorDome");
|
||||||
|
auto belSettings = belScene.settings(); // Get scene settings
|
||||||
// Configure camera
|
// Configure camera
|
||||||
belCam["resolution"] = dl::Vec2 {1920, 1080}; // Set resolution to 1080p
|
belCam["resolution"] = dl::Vec2 {1920, 1080}; // Set resolution to 1080p
|
||||||
belCam["lens"] = belLens; // Connect camera to lens
|
belCam["lens"] = belLens; // Connect camera to lens
|
||||||
@ -237,7 +492,6 @@ int addModelToScene(dl::bella_sdk::Scene& belScene, const VmaxModel& vmaxModel,
|
|||||||
belColorDome["zenith"] = dl::Rgba{1.0f, 1.0f, 1.0f, 1.0f};
|
belColorDome["zenith"] = dl::Rgba{1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
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;
|
||||||
@ -252,16 +506,6 @@ int addModelToScene(dl::bella_sdk::Scene& belScene, const VmaxModel& vmaxModel,
|
|||||||
belGroundMat["roughness"] = 22.0f;
|
belGroundMat["roughness"] = 22.0f;
|
||||||
belGroundMat["color"] = dl::Rgba{0.138431623578, 0.5, 0.3, 1.0};
|
belGroundMat["color"] = dl::Rgba{0.138431623578, 0.5, 0.3, 1.0};
|
||||||
|
|
||||||
// Configure voxel box dimensions
|
|
||||||
belVoxel["radius"] = 0.33f;
|
|
||||||
belVoxel["sizeX"] = 0.99f;
|
|
||||||
belVoxel["sizeY"] = 0.99f;
|
|
||||||
belVoxel["sizeZ"] = 0.99f;
|
|
||||||
belVoxel.parentTo(belVoxelForm);
|
|
||||||
belVoxelForm["steps"][0]["xform"] = dl::Mat4 {0.999,0,0,0,0,0.999,0,0,0,0,0.999,0,0,0,0,1};
|
|
||||||
belVoxelMat["reflectance"] = dl::Rgba{0.0, 0.0, 0.0, 1.0};
|
|
||||||
belVoxelForm["material"] = belVoxelMat;
|
|
||||||
|
|
||||||
// Set up scene settings
|
// Set up scene settings
|
||||||
belSettings["beautyPass"] = belBeautyPass;
|
belSettings["beautyPass"] = belBeautyPass;
|
||||||
belSettings["camera"] = belCam;
|
belSettings["camera"] = belCam;
|
||||||
@ -271,102 +515,144 @@ int addModelToScene(dl::bella_sdk::Scene& belScene, const VmaxModel& vmaxModel,
|
|||||||
belSettings["groundPlane"] = belGroundPlane;
|
belSettings["groundPlane"] = belGroundPlane;
|
||||||
belSettings["iprNavigation"] = "maya"; // Use Maya-like navigation in viewer
|
belSettings["iprNavigation"] = "maya"; // Use Maya-like navigation in viewer
|
||||||
//settings["sun"] = sun;
|
//settings["sun"] = sun;
|
||||||
|
|
||||||
|
auto belVoxel = belScene.createNode("box","oomerVoxel","oomerVoxel");
|
||||||
|
auto belLiqVoxel = belScene.createNode("box","oomerLiqVoxel","oomerLiqVoxel");
|
||||||
|
auto belVoxelForm = belScene.createNode("xform","oomerVoxelXform","oomerVoxelXform");
|
||||||
|
auto belLiqVoxelForm = belScene.createNode("xform","oomerLiqVoxelXform","oomerLiqVoxelXform");
|
||||||
|
auto belVoxelMat = belScene.createNode("orenNayar","oomerVoxelMat","oomerVoxelMat");
|
||||||
|
auto belMeshVoxel = belScene.createNode("mesh", "oomerMeshVoxel");
|
||||||
|
#include "resources/smoothcube.h"
|
||||||
|
// Configure voxel box dimensions
|
||||||
|
belVoxel["radius"] = 0.33f;
|
||||||
|
belVoxel["sizeX"] = 0.99f;
|
||||||
|
belVoxel["sizeY"] = 0.99f;
|
||||||
|
belVoxel["sizeZ"] = 0.99f;
|
||||||
|
|
||||||
|
// Less gap to make liquid look better, allows more light to pass through
|
||||||
|
belLiqVoxel["sizeX"] = 0.99945f;
|
||||||
|
belLiqVoxel["sizeY"] = 0.99945f;
|
||||||
|
belLiqVoxel["sizeZ"] = 0.99945f;
|
||||||
|
|
||||||
|
belVoxel.parentTo(belVoxelForm);
|
||||||
|
belVoxelForm["steps"][0]["xform"] = dl::Mat4 {0.999,0,0,0,0,0.999,0,0,0,0,0.999,0,0,0,0,1};
|
||||||
|
belVoxelMat["reflectance"] = dl::Rgba{0.0, 0.0, 0.0, 1.0};
|
||||||
|
belVoxelForm["material"] = belVoxelMat;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return belWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Only add the canonical model to the scene
|
||||||
|
// We'll use xforms to instance the model
|
||||||
|
// Each model is stores in contentsN.vmaxb as a lzfe compressed plist
|
||||||
|
// Each model has a paletteN.png that maps 0-255 to colors
|
||||||
|
// The model stored in contentsN.vmaxb can have mulitple snapshots
|
||||||
|
// Each snapshot contains a chunkID, and a datastream
|
||||||
|
// The datastream contains the voxels for the snapshot
|
||||||
|
// The voxels are stored in chunks, each chunk is 8x8x8 voxels
|
||||||
|
// The chunks are stored in a morton order
|
||||||
|
dl::bella_sdk::Node addModelToScene(dl::bella_sdk::Scene& belScene, dl::bella_sdk::Node& belWorld, const VmaxModel& vmaxModel, const std::vector<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());
|
||||||
auto modelXform = belScene.createNode("xform", modelName, modelName);
|
dl::String canonicalName = modelName.replace(".vmaxb", "");
|
||||||
modelXform.parentTo(belScene.world());
|
dl::bella_sdk::Node belCanonicalNode;
|
||||||
|
{
|
||||||
|
dl::bella_sdk::Scene::EventScope es(belScene);
|
||||||
|
|
||||||
for (const auto& [material, colorID] : vmaxModel.getUsedMaterialsAndColors()) {
|
auto belVoxel = belScene.findNode("oomerVoxel");
|
||||||
for (int color : colorID) {
|
auto belLiqVoxel = belScene.findNode("oomerLiqVoxel");
|
||||||
auto belInstancer = belScene.createNode("instancer",
|
auto belMeshVoxel = belScene.findNode("oomerMeshVoxel");
|
||||||
dl::String("instMat") + dl::String(material) + dl::String("Color") + dl::String(color));
|
auto belVoxelForm = belScene.findNode("oomerVoxelXform");
|
||||||
auto xformsArray = dl::ds::Vector<dl::Mat4f>();
|
auto belLiqVoxelForm = belScene.findNode("oomerLiqVoxelXform");
|
||||||
belInstancer.parentTo(modelXform);
|
|
||||||
|
auto modelXform = belScene.createNode("xform", canonicalName, canonicalName);
|
||||||
|
modelXform["steps"][0]["xform"] = dl::Mat4 {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
|
||||||
|
for (const auto& [material, colorID] : vmaxModel.getUsedMaterialsAndColors()) {
|
||||||
|
for (int color : colorID) {
|
||||||
|
auto belInstancer = belScene.createNode("instancer",
|
||||||
|
canonicalName + dl::String("Material") + dl::String(material) + dl::String("Color") + dl::String(color));
|
||||||
|
auto xformsArray = dl::ds::Vector<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",
|
||||||
|
canonicalName + dl::String("vmaxMat") + dl::String(material) + dl::String("Color") + dl::String(color));
|
||||||
|
|
||||||
|
|
||||||
auto belMaterial = belScene.createNode("quickMaterial",
|
if(material==7) {
|
||||||
dl::String("vmaxMat") + dl::String(material) + dl::String("Color") + dl::String(color));
|
belMaterial["type"] = "liquid";
|
||||||
|
//belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
||||||
|
belMaterial["liquidDepth"] = 100.0f;
|
||||||
|
belMaterial["ior"] = 1.11f;
|
||||||
|
} else if(material==6 || vmaxPalette[color-1].a < 255) {
|
||||||
|
belMaterial["type"] = "glass";
|
||||||
|
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
||||||
|
belMaterial["glassDepth"] = 200.0f;
|
||||||
|
} else if(vmaxMaterial[material].metalness > 0.1f) {
|
||||||
|
belMaterial["type"] = "metal";
|
||||||
|
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
||||||
|
} else if(vmaxMaterial[material].transmission > 0.0f) {
|
||||||
|
belMaterial["type"] = "dielectric";
|
||||||
|
belMaterial["transmission"] = vmaxMaterial[material].transmission;
|
||||||
|
} else if(vmaxMaterial[material].emission > 0.0f) {
|
||||||
|
belMaterial["type"] = "emitter";
|
||||||
|
belMaterial["emitterUnit"] = "radiance";
|
||||||
|
belMaterial["energy"] = vmaxMaterial[material].emission*1.0f;
|
||||||
|
} else {
|
||||||
|
belMaterial["type"] = "plastic";
|
||||||
|
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
||||||
|
}
|
||||||
|
belInstancer["material"] = belMaterial;
|
||||||
|
// 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 bellaG = static_cast<double>(vmaxPalette[color-1].g)/255.0;
|
||||||
|
double bellaB = static_cast<double>(vmaxPalette[color-1].b)/255.0;
|
||||||
|
double bellaA = static_cast<double>(vmaxPalette[color-1].a)/255.0;
|
||||||
|
belMaterial["color"] = dl::Rgba{ // convert sRGB to linear
|
||||||
|
srgbToLinear(bellaR),
|
||||||
|
srgbToLinear(bellaG),
|
||||||
|
srgbToLinear(bellaB),
|
||||||
|
bellaA // alpha is already linear
|
||||||
|
}; // colors ready to use in Bella
|
||||||
|
|
||||||
std::cout << "vmaxMaterial: " << vmaxMaterial[material].materialName << std::endl;
|
// Get all voxels for this material/color combination
|
||||||
std::cout << "metalness: " << vmaxMaterial[material].metalness << std::endl;
|
const std::vector<VmaxVoxel>& voxelsOfType = vmaxModel.getVoxels(material, color);
|
||||||
std::cout << "roughness: " << vmaxMaterial[material].roughness << std::endl;
|
int showchunk =0;
|
||||||
std::cout << "transmission: " << vmaxMaterial[material].transmission << std::endl;
|
|
||||||
std::cout << "emission: " << vmaxMaterial[material].emission << std::endl;
|
|
||||||
|
|
||||||
if(material==7) {
|
// Right now we group voxels by MatCol ie Mat0Col2
|
||||||
belMaterial["type"] = "liquid";
|
// But voxels are stored in chunks with many colors
|
||||||
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
// Since we aren't grouping voxels in chunks, we need to traverse the voxels
|
||||||
} else if(material==6) {
|
// and offset each voxel by the morton decode of chunk index
|
||||||
belMaterial["type"] = "glass";
|
for (const auto& eachvoxel : voxelsOfType) {
|
||||||
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
// Get chunk coordinates and world origin
|
||||||
belMaterial["glassDepth"] = 200.0f;
|
uint32_t _tempx, _tempy, _tempz;
|
||||||
} else if(vmaxMaterial[material].metalness > 0.1f) {
|
decodeMorton3DOptimized(eachvoxel.chunkID, _tempx, _tempy, _tempz); // index IS the morton code
|
||||||
belMaterial["type"] = "metal";
|
int worldOffsetX = _tempx * 24; // get world loc within 256x256x256 grid
|
||||||
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
int worldOffsetY = _tempy * 24; // Don't know why we need to multiply by 24
|
||||||
} else if(vmaxMaterial[material].transmission > 0.0f) {
|
int worldOffsetZ = _tempz * 24; // use to be 32
|
||||||
belMaterial["type"] = "dielectric";
|
xformsArray.push_back( dl::Mat4f{ 1, 0, 0, 0,
|
||||||
belMaterial["transmission"] = vmaxMaterial[material].transmission;
|
0, 1, 0, 0,
|
||||||
} else if(vmaxMaterial[material].emission > 0.0f) {
|
0, 0, 1, 0,
|
||||||
belMaterial["type"] = "emitter";
|
static_cast<float>(eachvoxel.x + worldOffsetX),
|
||||||
belMaterial["emitterUnit"] = "radiance";
|
static_cast<float>(eachvoxel.y + worldOffsetY),
|
||||||
belMaterial["energy"] = vmaxMaterial[material].emission*1.0f;
|
static_cast<float>(eachvoxel.z + worldOffsetZ), 1 });
|
||||||
} else {
|
}
|
||||||
belMaterial["type"] = "plastic";
|
belInstancer["steps"][0]["instances"] = xformsArray;
|
||||||
belMaterial["roughness"] = vmaxMaterial[material].roughness * 100.0f;
|
if(material==7) {
|
||||||
}
|
belLiqVoxel.parentTo(belInstancer);
|
||||||
belInstancer["material"] = belMaterial;
|
} else {
|
||||||
std::cout << "material: " << material << std::endl;
|
belMeshVoxel.parentTo(belInstancer);
|
||||||
// Convert 0-255 to 0-1
|
}
|
||||||
double bellaR = static_cast<double>(vmaxPalette[color-1].r)/255.0;
|
if(vmaxMaterial[material].emission > 0.0f) {
|
||||||
double bellaG = static_cast<double>(vmaxPalette[color-1].g)/255.0;
|
belVoxelForm.parentTo(belInstancer);
|
||||||
double bellaB = static_cast<double>(vmaxPalette[color-1].b)/255.0;
|
}
|
||||||
double bellaA = static_cast<double>(vmaxPalette[color-1].a)/255.0;
|
|
||||||
/*
|
|
||||||
belMaterial["color"] = dl::Rgba{ // convert sRGB to linear
|
|
||||||
bellaR,
|
|
||||||
bellaG,
|
|
||||||
bellaB,
|
|
||||||
bellaA // alpha is already linear
|
|
||||||
}; // colors ready to use in Bella
|
|
||||||
*/
|
|
||||||
belMaterial["color"] = dl::Rgba{ // convert sRGB to linear
|
|
||||||
srgbToLinear(bellaR),
|
|
||||||
srgbToLinear(bellaG),
|
|
||||||
srgbToLinear(bellaB),
|
|
||||||
bellaA // alpha is already linear
|
|
||||||
}; // colors ready to use in Bella
|
|
||||||
|
|
||||||
// Get all voxels for this material/color combination
|
|
||||||
const std::vector<VmaxVoxel>& voxelsOfType = vmaxModel.getVoxels(material, color);
|
|
||||||
int showchunk =0;
|
|
||||||
|
|
||||||
// Right now we group voxels by MatCol ie Mat0Col2
|
|
||||||
// But voxels are stored in chunks with many colors
|
|
||||||
// Since we aren't grouping voxels in chunks, we need to traverse the voxels
|
|
||||||
// and offset each voxel by the morton decode of chunk index
|
|
||||||
for (const auto& eachvoxel : voxelsOfType) {
|
|
||||||
// Get chunk coordinates and world origin
|
|
||||||
uint32_t _tempx, _tempy, _tempz;
|
|
||||||
decodeMorton3DOptimized(eachvoxel.chunkID, _tempx, _tempy, _tempz); // index IS the morton code
|
|
||||||
int worldOffsetX = _tempx * 24; // get world loc within 256x256x256 grid
|
|
||||||
int worldOffsetY = _tempy * 24; // Don't know why we need to multiply by 24
|
|
||||||
int worldOffsetZ = _tempz * 24; // use to be 32
|
|
||||||
xformsArray.push_back( dl::Mat4f{ 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
static_cast<float>(eachvoxel.x + worldOffsetX),
|
|
||||||
static_cast<float>(eachvoxel.y + worldOffsetY),
|
|
||||||
static_cast<float>(eachvoxel.z + worldOffsetZ), 1 });
|
|
||||||
}
|
|
||||||
belInstancer["steps"][0]["instances"] = xformsArray;
|
|
||||||
oomerSmoothCube.parentTo(belInstancer);
|
|
||||||
if(vmaxMaterial[material].emission > 0.0f) {
|
|
||||||
belVoxelForm.parentTo(belInstancer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i++;
|
return modelXform;
|
||||||
}
|
}
|
||||||
return 0;
|
return dl::bella_sdk::Node();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user