summaryrefslogtreecommitdiff
path: root/src/3d/scene_loader.cc
blob: d27cc1678fb9e7d025ad4a2532cbef2cf3307110 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include "3d/scene_loader.h"
#include "generated/assets.h"
#include "plane_data.h"
#include "util/asset_manager.h"
#include "util/mini_math.h"
#include <cstdio>
#include <cstring>
#include <memory> // For std::shared_ptr
#include <new>    // For std::nothrow
#include <vector>

namespace SceneLoader {

// Safe unaligned read helpers
static inline uint32_t read_u32(const uint8_t* p) {
  uint32_t v;
  std::memcpy(&v, p, sizeof(v));
  return v;
}
static inline float read_f32(const uint8_t* p) {
  float v;
  std::memcpy(&v, p, sizeof(v));
  return v;
}

bool 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 = read_u32(data + offset);
  offset += 4;
  uint32_t num_cameras = read_u32(data + offset);
  offset += 4;
  uint32_t num_lights = read_u32(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 = read_u32(data + offset);
    offset += 4;
    ObjectType type = (ObjectType)type_val;

    if (offset + 12 + 16 + 12 + 16 > size)
      return false; // Transforms + Color

    float px = read_f32(data + offset);
    offset += 4;
    float py = read_f32(data + offset);
    offset += 4;
    float pz = read_f32(data + offset);
    offset += 4;
    vec3 pos(px, py, pz);

    float rx = read_f32(data + offset);
    offset += 4;
    float ry = read_f32(data + offset);
    offset += 4;
    float rz = read_f32(data + offset);
    offset += 4;
    float rw = read_f32(data + offset);
    offset += 4;
    quat rot(rx, ry, rz, rw);

    float sx = read_f32(data + offset);
    offset += 4;
    float sy = read_f32(data + offset);
    offset += 4;
    float sz = read_f32(data + offset);
    offset += 4;
    vec3 scale(sx, sy, sz);

    float cr = read_f32(data + offset);
    offset += 4;
    float cg = read_f32(data + offset);
    offset += 4;
    float cb = read_f32(data + offset);
    offset += 4;
    float ca = read_f32(data + offset);
    offset += 4;
    vec4 color(cr, cg, cb, ca);

    // Plane Distance (if type == PLANE)
    float plane_distance = 0.0f;
    if (type == ObjectType::PLANE) {
      // Check bounds before reading plane_distance
      if (offset + 4 > size)
        return false;
      plane_distance = read_f32(data + offset);
      offset += 4; // Advance offset after reading plane_distance
    }

    // Mesh Asset Name Length
    // The offset is now correctly positioned for name_len,
    // either after ca (if not PLANE) or after plane_distance (if PLANE).
    if (offset + 4 > size)
      return false;
    uint32_t name_len = read_u32(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 = read_f32(data + offset);
    offset += 4;
    float restitution = read_f32(data + offset);
    offset += 4;
    uint32_t is_static_u32 = read_u32(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

    // Store plane distance in shared_user_data if it's a plane
    if (type == ObjectType::PLANE) {
      // Allocate PlaneData on the heap and manage with shared_ptr
      // Use std::make_shared for exception safety and efficiency
      obj.shared_user_data = std::make_shared<PlaneData>();
      // Assign the plane distance
      // Safely cast void* to PlaneData* using C-style cast on the shared_ptr's
      // get()
      ((PlaneData*)(obj.shared_user_data.get()))->distance = plane_distance;
    }

    // Add to scene
    scene.add_object(obj);
  }

  return true;
}
} // namespace SceneLoader