summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-08 07:00:28 +0100
committerskal <pascal.massimino@gmail.com>2026-02-08 07:00:28 +0100
commit1bc1cf8cd2c66bbae615a5ddba883b7cd55bd67f (patch)
tree15d989e9ff31c1a691bfaa840f661f3eed92a6b2 /src
parentef3839ac767057d80feb55aaaf3f4ededfe69e91 (diff)
feat(3d): Implement Blender export and binary scene loading pipeline
Diffstat (limited to 'src')
-rw-r--r--src/3d/scene_loader.cc108
-rw-r--r--src/3d/scene_loader.h14
-rw-r--r--src/generated/assets_data.cc46
-rw-r--r--src/tests/test_scene_loader.cc107
-rw-r--r--src/util/asset_manager.h3
5 files changed, 278 insertions, 0 deletions
diff --git a/src/3d/scene_loader.cc b/src/3d/scene_loader.cc
new file mode 100644
index 0000000..69079ef
--- /dev/null
+++ b/src/3d/scene_loader.cc
@@ -0,0 +1,108 @@
+#include "3d/scene_loader.h"
+#include "util/asset_manager.h"
+#include "generated/assets.h"
+#include "util/mini_math.h"
+#include <cstring>
+#include <cstdio>
+#include <vector>
+
+bool SceneLoader::LoadScene(Scene& scene, const uint8_t* data, size_t size) {
+ if (!data || size < 16) { // Header size check
+ printf("SceneLoader: Data too small\n");
+ return false;
+ }
+
+ // Check Magic
+ if (std::memcmp(data, "SCN1", 4) != 0) {
+ printf("SceneLoader: Invalid magic (expected SCN1)\n");
+ return false;
+ }
+
+ size_t offset = 4;
+
+ uint32_t num_objects = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+ uint32_t num_cameras = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+ uint32_t num_lights = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+
+ // printf("SceneLoader: Loading %d objects, %d cameras, %d lights\n", num_objects, num_cameras, num_lights);
+
+ for (uint32_t i = 0; i < num_objects; ++i) {
+ if (offset + 64 > size) return false; // Name check
+
+ char name[65] = {0};
+ std::memcpy(name, data + offset, 64); offset += 64;
+
+ if (offset + 4 > size) return false;
+ uint32_t type_val = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+ ObjectType type = (ObjectType)type_val;
+
+ if (offset + 12 + 16 + 12 + 16 > size) return false; // Transforms + Color
+
+ float px = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float py = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float pz = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ vec3 pos(px, py, pz);
+
+ float rx = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float ry = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float rz = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float rw = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ quat rot(rx, ry, rz, rw);
+
+ float sx = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float sy = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float sz = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ vec3 scale(sx, sy, sz);
+
+ float cr = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float cg = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float cb = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float ca = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ vec4 color(cr, cg, cb, ca);
+
+ // Mesh Asset Name Length
+ if (offset + 4 > size) return false;
+ uint32_t name_len = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+
+ AssetId mesh_id = (AssetId)0; // Default or INVALID (if 0 is invalid)
+
+ if (name_len > 0) {
+ if (offset + name_len > size) return false;
+ char mesh_name[128] = {0};
+ if (name_len < 128) {
+ std::memcpy(mesh_name, data + offset, name_len);
+ }
+ offset += name_len;
+
+ // Resolve Asset ID
+ mesh_id = GetAssetIdByName(mesh_name);
+ if (mesh_id == AssetId::ASSET_LAST_ID) {
+ printf("SceneLoader: Warning: Mesh asset '%s' not found for object '%s'\n", mesh_name, name);
+ }
+ }
+
+ // Physics properties
+ if (offset + 4 + 4 + 4 > size) return false;
+ float mass = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ float restitution = *reinterpret_cast<const float*>(data + offset); offset += 4;
+ uint32_t is_static_u32 = *reinterpret_cast<const uint32_t*>(data + offset); offset += 4;
+ bool is_static = (is_static_u32 != 0);
+
+ // Create Object3D
+ Object3D obj(type);
+ obj.position = pos;
+ obj.rotation = rot;
+ obj.scale = scale;
+ obj.color = color;
+ obj.mesh_asset_id = mesh_id;
+ obj.mass = mass;
+ obj.restitution = restitution;
+ obj.is_static = is_static;
+ // user_data is nullptr by default
+
+ // Add to scene
+ scene.add_object(obj);
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/src/3d/scene_loader.h b/src/3d/scene_loader.h
new file mode 100644
index 0000000..15f08c7
--- /dev/null
+++ b/src/3d/scene_loader.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "3d/scene.h"
+#include <cstdint>
+#include <cstddef>
+
+// SceneLoader handles parsing of binary scene files (.bin) exported from Blender.
+// It populates a Scene object with objects, lights, and cameras.
+class SceneLoader {
+ public:
+ // Loads a scene from a binary buffer.
+ // Returns true on success, false on failure (e.g., invalid magic, version mismatch).
+ static bool LoadScene(Scene& scene, const uint8_t* data, size_t size);
+};
diff --git a/src/generated/assets_data.cc b/src/generated/assets_data.cc
index 49d7368..b9a6a8a 100644
--- a/src/generated/assets_data.cc
+++ b/src/generated/assets_data.cc
@@ -1,5 +1,6 @@
// This file is auto-generated by asset_packer.cc. Do not edit.
+#include <cstring>
#include "util/asset_manager.h"
#include "assets.h"
namespace procedural { void gen_noise(uint8_t*, int, int, const float*, int); }
@@ -369390,3 +369391,48 @@ size_t GetAssetCount() {
return 41;
}
+AssetId GetAssetIdByName(const char* name) {
+ if (std::strcmp(name, "KICK_1") == 0) return AssetId::ASSET_KICK_1;
+ if (std::strcmp(name, "KICK_2") == 0) return AssetId::ASSET_KICK_2;
+ if (std::strcmp(name, "SNARE_1") == 0) return AssetId::ASSET_SNARE_1;
+ if (std::strcmp(name, "SNARE_2") == 0) return AssetId::ASSET_SNARE_2;
+ if (std::strcmp(name, "SNARE_3") == 0) return AssetId::ASSET_SNARE_3;
+ if (std::strcmp(name, "HIHAT_1") == 0) return AssetId::ASSET_HIHAT_1;
+ if (std::strcmp(name, "HIHAT_2") == 0) return AssetId::ASSET_HIHAT_2;
+ if (std::strcmp(name, "HIHAT_3") == 0) return AssetId::ASSET_HIHAT_3;
+ if (std::strcmp(name, "CRASH_1") == 0) return AssetId::ASSET_CRASH_1;
+ if (std::strcmp(name, "RIDE_1") == 0) return AssetId::ASSET_RIDE_1;
+ if (std::strcmp(name, "SPLASH_1") == 0) return AssetId::ASSET_SPLASH_1;
+ if (std::strcmp(name, "BASS_1") == 0) return AssetId::ASSET_BASS_1;
+ if (std::strcmp(name, "BASS_2") == 0) return AssetId::ASSET_BASS_2;
+ if (std::strcmp(name, "BASS_3") == 0) return AssetId::ASSET_BASS_3;
+ if (std::strcmp(name, "NOISE_TEX") == 0) return AssetId::ASSET_NOISE_TEX;
+ if (std::strcmp(name, "SHADER_RENDERER_3D") == 0) return AssetId::ASSET_SHADER_RENDERER_3D;
+ if (std::strcmp(name, "SHADER_COMMON_UNIFORMS") == 0) return AssetId::ASSET_SHADER_COMMON_UNIFORMS;
+ if (std::strcmp(name, "SHADER_SDF_PRIMITIVES") == 0) return AssetId::ASSET_SHADER_SDF_PRIMITIVES;
+ if (std::strcmp(name, "SHADER_LIGHTING") == 0) return AssetId::ASSET_SHADER_LIGHTING;
+ if (std::strcmp(name, "SHADER_RAY_BOX") == 0) return AssetId::ASSET_SHADER_RAY_BOX;
+ if (std::strcmp(name, "SHADER_MAIN") == 0) return AssetId::ASSET_SHADER_MAIN;
+ if (std::strcmp(name, "SHADER_PARTICLE_COMPUTE") == 0) return AssetId::ASSET_SHADER_PARTICLE_COMPUTE;
+ if (std::strcmp(name, "SHADER_PARTICLE_RENDER") == 0) return AssetId::ASSET_SHADER_PARTICLE_RENDER;
+ if (std::strcmp(name, "SHADER_PASSTHROUGH") == 0) return AssetId::ASSET_SHADER_PASSTHROUGH;
+ if (std::strcmp(name, "SHADER_ELLIPSE") == 0) return AssetId::ASSET_SHADER_ELLIPSE;
+ if (std::strcmp(name, "SHADER_PARTICLE_SPRAY_COMPUTE") == 0) return AssetId::ASSET_SHADER_PARTICLE_SPRAY_COMPUTE;
+ if (std::strcmp(name, "SHADER_GAUSSIAN_BLUR") == 0) return AssetId::ASSET_SHADER_GAUSSIAN_BLUR;
+ if (std::strcmp(name, "SHADER_SOLARIZE") == 0) return AssetId::ASSET_SHADER_SOLARIZE;
+ if (std::strcmp(name, "SHADER_DISTORT") == 0) return AssetId::ASSET_SHADER_DISTORT;
+ if (std::strcmp(name, "SHADER_CHROMA_ABERRATION") == 0) return AssetId::ASSET_SHADER_CHROMA_ABERRATION;
+ if (std::strcmp(name, "SHADER_VISUAL_DEBUG") == 0) return AssetId::ASSET_SHADER_VISUAL_DEBUG;
+ if (std::strcmp(name, "SHADER_SKYBOX") == 0) return AssetId::ASSET_SHADER_SKYBOX;
+ if (std::strcmp(name, "SHADER_MATH_SDF_SHAPES") == 0) return AssetId::ASSET_SHADER_MATH_SDF_SHAPES;
+ if (std::strcmp(name, "SHADER_MATH_SDF_UTILS") == 0) return AssetId::ASSET_SHADER_MATH_SDF_UTILS;
+ if (std::strcmp(name, "SHADER_RENDER_SHADOWS") == 0) return AssetId::ASSET_SHADER_RENDER_SHADOWS;
+ if (std::strcmp(name, "SHADER_RENDER_SCENE_QUERY_BVH") == 0) return AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH;
+ if (std::strcmp(name, "SHADER_RENDER_SCENE_QUERY_LINEAR") == 0) return AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR;
+ if (std::strcmp(name, "SHADER_RENDER_LIGHTING_UTILS") == 0) return AssetId::ASSET_SHADER_RENDER_LIGHTING_UTILS;
+ if (std::strcmp(name, "SHADER_MESH") == 0) return AssetId::ASSET_SHADER_MESH;
+ if (std::strcmp(name, "MESH_CUBE") == 0) return AssetId::ASSET_MESH_CUBE;
+ if (std::strcmp(name, "DODECAHEDRON") == 0) return AssetId::ASSET_DODECAHEDRON;
+ return AssetId::ASSET_LAST_ID;
+}
+
diff --git a/src/tests/test_scene_loader.cc b/src/tests/test_scene_loader.cc
new file mode 100644
index 0000000..e14054b
--- /dev/null
+++ b/src/tests/test_scene_loader.cc
@@ -0,0 +1,107 @@
+#include "3d/scene_loader.h"
+#include "util/mini_math.h"
+#include "util/asset_manager.h"
+#include "generated/assets.h"
+#include <cstdio>
+#include <cstring>
+#include <vector>
+#include <cassert>
+
+int main() {
+ Scene scene;
+ std::vector<uint8_t> buffer;
+
+ // Header
+ const char* magic = "SCN1";
+ for(int i=0; i<4; ++i) buffer.push_back(magic[i]);
+
+ uint32_t num_obj = 2; // Increased to 2
+ uint32_t num_cam = 0;
+ uint32_t num_light = 0;
+
+ auto push_u32 = [&](uint32_t v) {
+ uint8_t* p = (uint8_t*)&v;
+ for(int i=0; i<4; ++i) buffer.push_back(p[i]);
+ };
+ auto push_f = [&](float v) {
+ uint8_t* p = (uint8_t*)&v;
+ for(int i=0; i<4; ++i) buffer.push_back(p[i]);
+ };
+
+ push_u32(num_obj);
+ push_u32(num_cam);
+ push_u32(num_light);
+
+ // --- Object 1: Basic Cube ---
+ char name1[64] = {0};
+ std::strcpy(name1, "TestObject");
+ for(int i=0; i<64; ++i) buffer.push_back(name1[i]);
+
+ push_u32(0); // CUBE
+
+ // Pos
+ push_f(1.0f); push_f(2.0f); push_f(3.0f);
+ // Rot (0,0,0,1)
+ push_f(0.0f); push_f(0.0f); push_f(0.0f); push_f(1.0f);
+ // Scale
+ push_f(1.0f); push_f(1.0f); push_f(1.0f);
+ // Color
+ push_f(1.0f); push_f(0.0f); push_f(0.0f); push_f(1.0f);
+
+ // Mesh Name length 0
+ push_u32(0);
+
+ // Physics
+ push_f(10.0f); // mass
+ push_f(0.8f); // restitution
+ push_u32(1); // static
+
+ // --- Object 2: Mesh with Asset Ref ---
+ char name2[64] = {0};
+ std::strcpy(name2, "MeshObject");
+ for(int i=0; i<64; ++i) buffer.push_back(name2[i]);
+
+ push_u32(6); // MESH
+
+ // Pos
+ push_f(0.0f); push_f(0.0f); push_f(0.0f);
+ // Rot
+ push_f(0.0f); push_f(0.0f); push_f(0.0f); push_f(1.0f);
+ // Scale
+ push_f(1.0f); push_f(1.0f); push_f(1.0f);
+ // Color
+ push_f(0.0f); push_f(1.0f); push_f(0.0f); push_f(1.0f);
+
+ // Mesh Name "MESH_CUBE"
+ const char* mesh_name = "MESH_CUBE";
+ uint32_t mesh_name_len = std::strlen(mesh_name);
+ push_u32(mesh_name_len);
+ for(size_t i=0; i<mesh_name_len; ++i) buffer.push_back(mesh_name[i]);
+
+ // Physics
+ push_f(1.0f);
+ push_f(0.5f);
+ push_u32(0); // dynamic
+
+ // --- Load ---
+ if (SceneLoader::LoadScene(scene, buffer.data(), buffer.size())) {
+ printf("Scene loaded successfully.\n");
+ assert(scene.objects.size() == 2);
+
+ // Check Obj 1
+ assert(scene.objects[0].type == ObjectType::CUBE);
+ assert(scene.objects[0].position.x == 1.0f);
+ assert(scene.objects[0].is_static == true);
+
+ // Check Obj 2
+ assert(scene.objects[1].type == ObjectType::MESH);
+ assert(scene.objects[1].mesh_asset_id == AssetId::ASSET_MESH_CUBE);
+ printf("Mesh Asset ID resolved to: %d (Expected %d)\n", (int)scene.objects[1].mesh_asset_id, (int)AssetId::ASSET_MESH_CUBE);
+
+ } else {
+ printf("Scene load failed.\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/util/asset_manager.h b/src/util/asset_manager.h
index ed7f1aa..59bf7a0 100644
--- a/src/util/asset_manager.h
+++ b/src/util/asset_manager.h
@@ -24,3 +24,6 @@ struct AssetRecord {
// - 'out_size' returns the original asset size (excluding the null terminator).
const uint8_t* GetAsset(AssetId asset_id, size_t* out_size = nullptr);
void DropAsset(AssetId asset_id, const uint8_t* asset);
+
+// Returns the AssetId for a given asset name, or AssetId::ASSET_LAST_ID if not found.
+AssetId GetAssetIdByName(const char* name);