From 1bc1cf8cd2c66bbae615a5ddba883b7cd55bd67f Mon Sep 17 00:00:00 2001 From: skal Date: Sun, 8 Feb 2026 07:00:28 +0100 Subject: feat(3d): Implement Blender export and binary scene loading pipeline --- src/3d/scene_loader.cc | 108 +++++++++++++++++++++++++++++++++++++++++ src/3d/scene_loader.h | 14 ++++++ src/generated/assets_data.cc | 46 ++++++++++++++++++ src/tests/test_scene_loader.cc | 107 ++++++++++++++++++++++++++++++++++++++++ src/util/asset_manager.h | 3 ++ 5 files changed, 278 insertions(+) create mode 100644 src/3d/scene_loader.cc create mode 100644 src/3d/scene_loader.h create mode 100644 src/tests/test_scene_loader.cc (limited to 'src') 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 +#include +#include + +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(data + offset); offset += 4; + uint32_t num_cameras = *reinterpret_cast(data + offset); offset += 4; + uint32_t num_lights = *reinterpret_cast(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(data + offset); offset += 4; + ObjectType type = (ObjectType)type_val; + + if (offset + 12 + 16 + 12 + 16 > size) return false; // Transforms + Color + + float px = *reinterpret_cast(data + offset); offset += 4; + float py = *reinterpret_cast(data + offset); offset += 4; + float pz = *reinterpret_cast(data + offset); offset += 4; + vec3 pos(px, py, pz); + + float rx = *reinterpret_cast(data + offset); offset += 4; + float ry = *reinterpret_cast(data + offset); offset += 4; + float rz = *reinterpret_cast(data + offset); offset += 4; + float rw = *reinterpret_cast(data + offset); offset += 4; + quat rot(rx, ry, rz, rw); + + float sx = *reinterpret_cast(data + offset); offset += 4; + float sy = *reinterpret_cast(data + offset); offset += 4; + float sz = *reinterpret_cast(data + offset); offset += 4; + vec3 scale(sx, sy, sz); + + float cr = *reinterpret_cast(data + offset); offset += 4; + float cg = *reinterpret_cast(data + offset); offset += 4; + float cb = *reinterpret_cast(data + offset); offset += 4; + float ca = *reinterpret_cast(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(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(data + offset); offset += 4; + float restitution = *reinterpret_cast(data + offset); offset += 4; + uint32_t is_static_u32 = *reinterpret_cast(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 +#include + +// 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 #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 +#include +#include +#include + +int main() { + Scene scene; + std::vector 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