first working
This commit is contained in:
parent
efb1354d80
commit
9041081b15
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 oomer
|
||||
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
|
||||
|
||||
28
README.md
28
README.md
@ -1,2 +1,26 @@
|
||||
# poomer-ogt-bella
|
||||
prototype convertor from MagicaVoxel .vox to Bella's .bsz
|
||||
prototype [MagicaVoxel](https://ephtracy.github.io) .vox to DiffuseLogic's [Bella](https://bellarender.com) .bsz
|
||||
|
||||
|
||||
# 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/
|
||||
├── opengametools/
|
||||
└── poomer-ogt-bella/
|
||||
```
|
||||
|
||||
|
||||
# MacOS
|
||||
|
||||
```
|
||||
mkdir workdir
|
||||
git clone https://github.com/jpaver/opengametools.git
|
||||
git clone https://github.com/oomer/poomer-ogt-bella.git
|
||||
cd poomer-ogt-bella
|
||||
make all -j4
|
||||
```
|
||||
|
||||
|
||||
111
makefile
Normal file
111
makefile
Normal file
@ -0,0 +1,111 @@
|
||||
# Project configuration
|
||||
BELLA_SDK_NAME = bella_scene_sdk
|
||||
EXECUTABLE_NAME = poomer-ogt-bella
|
||||
PLATFORM = $(shell uname)
|
||||
BUILD_TYPE ?= release# Default to release build if not specified
|
||||
|
||||
# Common paths
|
||||
BELLA_SDK_PATH = ../bella_scene_sdk
|
||||
OBJ_DIR = obj/$(PLATFORM)/$(BUILD_TYPE)
|
||||
BIN_DIR = bin/$(PLATFORM)/$(BUILD_TYPE)
|
||||
OUTPUT_FILE = $(BIN_DIR)/$(EXECUTABLE_NAME)
|
||||
|
||||
# Platform-specific configuration
|
||||
ifeq ($(PLATFORM), Darwin)
|
||||
# macOS configuration
|
||||
SDK_LIB_EXT = dylib
|
||||
MACOS_SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
|
||||
|
||||
# Compiler settings
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
|
||||
# Architecture flags
|
||||
ARCH_FLAGS = -arch arm64 -arch x86_64 -mmacosx-version-min=11.0 -isysroot $(MACOS_SDK_PATH)
|
||||
|
||||
# Linking flags - Use multiple rpath entries to look in executable directory
|
||||
LINKER_FLAGS = $(ARCH_FLAGS) -framework Cocoa -framework IOKit -fvisibility=hidden -O5 \
|
||||
-rpath @executable_path \
|
||||
-rpath .
|
||||
|
||||
else
|
||||
# Linux configuration
|
||||
SDK_LIB_EXT = so
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
|
||||
# Architecture flags
|
||||
ARCH_FLAGS = -m64 -D_FILE_OFFSET_BITS=64
|
||||
|
||||
# Linking flags
|
||||
LINKER_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 -Wl,-rpath,'$$ORIGIN' -Wl,-rpath,'$$ORIGIN/lib'
|
||||
|
||||
endif
|
||||
|
||||
# Common include and library paths
|
||||
INCLUDE_PATHS = -I$(BELLA_SDK_PATH)/src
|
||||
|
||||
# Library flags
|
||||
LIB_PATHS = -L$(BELLA_SDK_PATH)/lib
|
||||
LIBRARIES = -l$(BELLA_SDK_NAME) -lm -ldl
|
||||
|
||||
# Build type specific flags
|
||||
ifeq ($(BUILD_TYPE), debug)
|
||||
CPP_DEFINES = -D_DEBUG -DDL_USE_SHARED
|
||||
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -g -O0 $(INCLUDE_PATHS)
|
||||
else
|
||||
CPP_DEFINES = -DNDEBUG=1 -DDL_USE_SHARED
|
||||
COMMON_FLAGS = $(ARCH_FLAGS) -fvisibility=hidden -O3 $(INCLUDE_PATHS)
|
||||
endif
|
||||
|
||||
# Language-specific flags
|
||||
C_FLAGS = $(COMMON_FLAGS) -std=c17
|
||||
CXX_FLAGS = $(COMMON_FLAGS) -std=c++17 -Wno-deprecated-declarations
|
||||
|
||||
# Objects
|
||||
OBJECTS = $(EXECUTABLE_NAME).o
|
||||
OBJECT_FILES = $(patsubst %,$(OBJ_DIR)/%,$(OBJECTS))
|
||||
|
||||
# Build rules
|
||||
$(OBJ_DIR)/$(EXECUTABLE_NAME).o: $(EXECUTABLE_NAME).cpp
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -c -o $@ $< $(CXX_FLAGS) $(CPP_DEFINES)
|
||||
|
||||
$(OUTPUT_FILE): $(OBJECT_FILES)
|
||||
@mkdir -p $(@D)
|
||||
$(CXX) -o $@ $(OBJECT_FILES) $(LINKER_FLAGS) $(LIB_PATHS) $(LIBRARIES)
|
||||
@echo "Copying libraries to $(BIN_DIR)..."
|
||||
@cp $(BELLA_SDK_PATH)/lib/lib$(BELLA_SDK_NAME).$(SDK_LIB_EXT) $(BIN_DIR)/lib$(BELLA_SDK_NAME).$(SDK_LIB_EXT)
|
||||
@echo "Build complete: $(OUTPUT_FILE)"
|
||||
|
||||
# Add default target
|
||||
all: $(OUTPUT_FILE)
|
||||
|
||||
.PHONY: clean cleanall all
|
||||
clean:
|
||||
rm -f $(OBJ_DIR)/$(EXECUTABLE_NAME).o
|
||||
rm -f $(OUTPUT_FILE)
|
||||
rm -f $(BIN_DIR)/$(SDK_LIB_FILE)
|
||||
rm -f $(BIN_DIR)/*.dylib
|
||||
rmdir $(OBJ_DIR) 2>/dev/null || true
|
||||
rmdir $(BIN_DIR) 2>/dev/null || true
|
||||
|
||||
cleanall:
|
||||
rm -f obj/*/release/*.o
|
||||
rm -f obj/*/debug/*.o
|
||||
rm -f bin/*/release/$(EXECUTABLE_NAME)
|
||||
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
|
||||
rmdir obj/*/release 2>/dev/null || true
|
||||
rmdir obj/*/debug 2>/dev/null || true
|
||||
rmdir bin/*/release 2>/dev/null || true
|
||||
rmdir bin/*/debug 2>/dev/null || true
|
||||
rmdir obj/* 2>/dev/null || true
|
||||
rmdir bin/* 2>/dev/null || true
|
||||
rmdir obj 2>/dev/null || true
|
||||
rmdir bin 2>/dev/null || true
|
||||
2074
oomer_bella_scene.h
Normal file
2074
oomer_bella_scene.h
Normal file
File diff suppressed because it is too large
Load Diff
1597
oomer_misc.h
Normal file
1597
oomer_misc.h
Normal file
File diff suppressed because it is too large
Load Diff
236
oomer_voxel_ogt.h
Normal file
236
oomer_voxel_ogt.h
Normal file
@ -0,0 +1,236 @@
|
||||
// oomer wrapper code for opengametools voxel conversion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oomer_voxel_vmax.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "../opengametools/src/ogt_vox.h"
|
||||
|
||||
// Only define implementation once to avoid redefinition errors
|
||||
#ifndef OGT_VOXEL_MESHIFY_IMPLEMENTATION
|
||||
#define OGT_VOXEL_MESHIFY_IMPLEMENTATION
|
||||
#endif
|
||||
#include "../opengametools/src/ogt_voxel_meshify.h"
|
||||
|
||||
// Convert a VmaxModel to an ogt_vox_model
|
||||
// Note: The returned ogt_vox_model must be freed using ogt_vox_free when no longer needed
|
||||
/*ogt_vox_model* convert_vmax_to_ogt_vox(const VmaxModel& vmaxModel) {
|
||||
// Use max dimensions from vmaxModel
|
||||
// Add 1 to get the actual size (since coordinates are 0-based)
|
||||
uint32_t size_x = vmaxModel.maxx+1;
|
||||
uint32_t size_y = vmaxModel.maxy+1;
|
||||
uint32_t size_z = vmaxModel.maxz+1;
|
||||
|
||||
// Add some safety checks
|
||||
if (size_x > 256 || size_y > 256 || size_z > 256) {
|
||||
std::cout << "Warning: Model dimensions exceed 256 limit. Clamping to 256." << std::endl;
|
||||
size_x = std::min(size_x, 256u);
|
||||
size_y = std::min(size_y, 256u);
|
||||
size_z = std::min(size_z, 256u);
|
||||
}
|
||||
if (size_x == 0 || size_y == 0 || size_z == 0) {
|
||||
std::cout << "Error: Model has zero dimensions. Setting minimum size of 1x1x1." << std::endl;
|
||||
size_x = std::max(size_x, 1u);
|
||||
size_y = std::max(size_y, 1u);
|
||||
size_z = std::max(size_z, 1u);
|
||||
}
|
||||
// Create the voxel data array (initialized to 0, which means empty in ogt_vox)
|
||||
size_t voxel_count = size_x * size_y * size_z;
|
||||
uint8_t* voxel_data = (uint8_t*)ogt_vox_malloc(voxel_count);
|
||||
if (!voxel_data) {
|
||||
std::cout << "Error: Failed to allocate memory for voxel data" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
memset(voxel_data, 0, voxel_count); // Initialize all to 0 (empty)
|
||||
|
||||
// Fill the voxel data array with color indices
|
||||
int voxel_count_populated = 0;
|
||||
for (uint32_t x = 0; x < size_x; x++) {
|
||||
for (uint32_t y = 0; y < size_y; y++) {
|
||||
for (uint32_t z = 0; z < size_z; z++) {
|
||||
// Calculate the index in the 1D array
|
||||
size_t index = x + (y * size_x) + (z * size_x * size_y);
|
||||
|
||||
if (index < voxel_count) {
|
||||
try {
|
||||
// Use the new hasVoxelsAt and getVoxelsAt methods instead of directly accessing voxelsSpatial
|
||||
if (vmaxModel.hasVoxelsAt(x, y, z)) {
|
||||
const std::vector<VmaxVoxel>& voxels = vmaxModel.getVoxelsAt(x, y, z);
|
||||
if (!voxels.empty()) {
|
||||
uint8_t palette_index = voxels[0].palette;
|
||||
if (palette_index == 0) palette_index = 1; // If palette is 0, use 1 instead to make it visible
|
||||
voxel_data[index] = palette_index;
|
||||
voxel_count_populated++;
|
||||
}
|
||||
}
|
||||
// If no voxels at this position, it remains 0 (empty)
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cout << "ERROR: Exception accessing voxels at [" << x << "][" << y << "][" << z
|
||||
<< "]: " << e.what() << std::endl;
|
||||
ogt_vox_free(voxel_data);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and initialize the ogt_vox_model
|
||||
ogt_vox_model* model = (ogt_vox_model*)ogt_vox_malloc(sizeof(ogt_vox_model));
|
||||
if (!model) {
|
||||
std::cout << "Error: Failed to allocate memory for ogt_vox_model" << std::endl;
|
||||
ogt_vox_free(voxel_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
model->size_x = size_x;
|
||||
model->size_y = size_y;
|
||||
model->size_z = size_z;
|
||||
model->voxel_data = voxel_data;
|
||||
|
||||
// Calculate a simple hash for the voxel data
|
||||
uint32_t hash = 0;
|
||||
for (size_t i = 0; i < voxel_count; i++) {
|
||||
hash = hash * 65599 + voxel_data[i];
|
||||
}
|
||||
model->voxel_hash = hash;
|
||||
|
||||
return model;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Convert a vector of VmaxVoxel to an ogt_vox_model
|
||||
// Note: The returned ogt_vox_model must be freed using ogt_vox_free when no longer needed
|
||||
ogt_vox_model* convert_voxelsoftype_to_ogt_vox(const std::vector<VmaxVoxel>& voxelsOfType) {
|
||||
// Find the maximum dimensions from the voxels
|
||||
uint32_t size_x = 0;
|
||||
uint32_t size_y = 0;
|
||||
uint32_t size_z = 0;
|
||||
|
||||
// WARNING must add 1 to each dimension
|
||||
// because voxel coordinates are 0-based
|
||||
for (const auto& voxel : voxelsOfType) {
|
||||
size_x = std::max(size_x, static_cast<uint32_t>(voxel.x)+1); // this seems wasteful
|
||||
size_y = std::max(size_y, static_cast<uint32_t>(voxel.y)+1);
|
||||
size_z = std::max(size_z, static_cast<uint32_t>(voxel.z)+1);
|
||||
}
|
||||
|
||||
// Add some safety checks
|
||||
// This is a dense voxel model, so we need to make sure it's not too large
|
||||
// todo use a sparse storage like morton
|
||||
if (size_x > 256 || size_y > 256 || size_z > 256) {
|
||||
std::cout << "Warning: Model dimensions exceed 256 limit. Clamping to 256." << std::endl;
|
||||
size_x = std::min(size_x, 256u);
|
||||
size_y = std::min(size_y, 256u);
|
||||
size_z = std::min(size_z, 256u);
|
||||
}
|
||||
if (size_x == 0 || size_y == 0 || size_z == 0) {
|
||||
std::cout << "Error: Model has zero dimensions. Setting minimum size of 1x1x1." << std::endl;
|
||||
size_x = std::max(size_x, 1u);
|
||||
size_y = std::max(size_y, 1u);
|
||||
size_z = std::max(size_z, 1u);
|
||||
}
|
||||
|
||||
// Create the voxel data array (initialized to 0, which means empty in ogt_vox)
|
||||
size_t voxel_count = size_x * size_y * size_z;
|
||||
uint8_t* voxel_data = (uint8_t*)ogt_vox_malloc(voxel_count);
|
||||
if (!voxel_data) {
|
||||
std::cout << "Error: Failed to allocate memory for voxel data" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
memset(voxel_data, 0, voxel_count); // Initialize all to 0 (empty)
|
||||
|
||||
// Fill the voxel data array with color indices
|
||||
int voxel_count_populated = 0;
|
||||
|
||||
// Loop through the vector of voxels directly
|
||||
for (const auto& voxel : voxelsOfType) {
|
||||
// Get the coordinates and palette
|
||||
uint32_t x = voxel.x;
|
||||
uint32_t y = voxel.y;
|
||||
uint32_t z = voxel.z;
|
||||
|
||||
// Skip voxels outside our valid range
|
||||
if (x >= size_x || y >= size_y || z >= size_z)
|
||||
continue;
|
||||
|
||||
// Calculate the index in the 1D array
|
||||
size_t index = x + (y * size_x) + (z * size_x * size_y);
|
||||
|
||||
if (index < voxel_count) {
|
||||
uint8_t palette_index = 0; // hardcoded for now
|
||||
if (palette_index == 0) palette_index = 1; // If palette is 0, use 1 instead to make it visible
|
||||
voxel_data[index] = palette_index;
|
||||
voxel_count_populated++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the model
|
||||
ogt_vox_model* model = (ogt_vox_model*)ogt_vox_malloc(sizeof(ogt_vox_model));
|
||||
if (!model) {
|
||||
std::cout << "Error: Failed to allocate memory for model" << std::endl;
|
||||
ogt_vox_free(voxel_data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
model->size_x = size_x;
|
||||
model->size_y = size_y;
|
||||
model->size_z = size_z;
|
||||
model->voxel_data = voxel_data;
|
||||
|
||||
// Calculate a simple hash for the voxel data
|
||||
uint32_t hash = 0;
|
||||
for (size_t i = 0; i < voxel_count; i++) {
|
||||
hash = hash * 65599 + voxel_data[i];
|
||||
}
|
||||
model->voxel_hash = hash;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
// Free resources allocated for an ogt_vox_model created by convert_vmax_to_ogt_vox
|
||||
void free_ogt_vox_model(ogt_vox_model* model) {
|
||||
if (model) {
|
||||
if (model->voxel_data) {
|
||||
ogt_vox_free((void*)model->voxel_data);
|
||||
}
|
||||
ogt_vox_free(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Free resources allocated for an ogt_vox_scene created by create_ogt_vox_scene_from_vmax
|
||||
/*void free_ogt_vox_scene(ogt_vox_scene* scene) {
|
||||
if (scene) {
|
||||
// Free each model
|
||||
for (uint32_t i = 0; i < scene->num_models; i++) {
|
||||
free_ogt_vox_model((ogt_vox_model*)scene->models[i]);
|
||||
}
|
||||
|
||||
// Free pointers
|
||||
if (scene->models) ogt_vox_free((void*)scene->models);
|
||||
if (scene->instances) ogt_vox_free((void*)scene->instances);
|
||||
if (scene->layers) ogt_vox_free((void*)scene->layers);
|
||||
|
||||
// Free the scene itself
|
||||
ogt_vox_free(scene);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Custom allocator functions for ogt_voxel_meshify
|
||||
static void* voxel_meshify_malloc(size_t size, void* user_data) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void voxel_meshify_free(void* ptr, void* user_data) {
|
||||
free(ptr);
|
||||
}
|
||||
77
poomer-ogt-bella.cpp
Normal file
77
poomer-ogt-bella.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
#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
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // For ShellExecuteW
|
||||
#include <shellapi.h> // For ShellExecuteW
|
||||
#include <codecvt> // For wstring_convert
|
||||
#endif
|
||||
|
||||
#include "oomer_bella_scene.h" // common bella scene code
|
||||
#include "oomer_misc.h" // common misc code
|
||||
|
||||
#define OGT_VOX_IMPLEMENTATION
|
||||
#include "../opengametools/src/ogt_vox.h"
|
||||
|
||||
dl::bella_sdk::Node essentialsToScene(dl::bella_sdk::Scene& belScene);
|
||||
|
||||
|
||||
int DL_main(dl::Args& args) {
|
||||
args.add("i", "input", "", "vmax directory or vmax.zip file");
|
||||
args.add("mo", "mode", "", "mode for output, mesh, voxel, or both");
|
||||
args.add("mt", "meshtype", "", "meshtype classic, greedy, other");
|
||||
args.add("be", "bevel", "", "add bevel to material");
|
||||
args.add("tp", "thirdparty", "", "prints third party licenses");
|
||||
args.add("li", "licenseinfo", "", "prints license info");
|
||||
|
||||
// If --help was requested, print help and exit
|
||||
if (args.helpRequested()) {
|
||||
std::cout << args.help("poomer-ogt-bella © 2025 Harvey Fong","poomer-ogt-bella", "1.0") << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If --licenseinfo was requested, print license info and exit
|
||||
if (args.have("--licenseinfo"))
|
||||
{
|
||||
std::cout << "poomer-ogt-bella\n\nCopyright 2025 Harvey Fong" << std::endl;
|
||||
std::cout << printLicense() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If --thirdparty was requested, print third-party licenses and exit
|
||||
if (args.have("--thirdparty"))
|
||||
{
|
||||
std::cout << printBellaSDKThirdPartyLicence() << "\n===\n" << std::endl;
|
||||
std::cout << printOpenGameToolsThirdPartyLicence() << "\n===\n" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.have("--input"))
|
||||
{
|
||||
dl::String bszName;
|
||||
dl::String objName;
|
||||
dl::String vmaxDirName;
|
||||
vmaxDirName = args.value("--input");
|
||||
bszName = vmaxDirName.replace("vmax", "bsz");
|
||||
objName = vmaxDirName.replace("vmax", "obj");
|
||||
|
||||
// Create a new scene
|
||||
dl::bella_sdk::Scene belScene;
|
||||
belScene.loadDefs();
|
||||
auto belWorld = essentialsToScene(belScene);
|
||||
auto belMeshXform = belScene.createNode("xform", "oomerMeshXform");
|
||||
auto belMeshVoxel = belScene.createNode("mesh", "oomerMeshVoxel");
|
||||
belMeshVoxel.parentTo(belMeshXform);
|
||||
belMeshXform.parentTo(belWorld);
|
||||
addMeshCube(belMeshVoxel);
|
||||
|
||||
// Save the scene to a Bella scene file
|
||||
belScene.write("foo.bsz");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user