summaryrefslogtreecommitdiff
path: root/tools/blender_export.py
blob: c4a45ffcf9c23228dfba7bb57bccd4294fa1eff8 (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
import bpy
import struct
import os

# Output format:
# Header:
#   char[4] magic = "SCN1"
#   uint32_t num_objects
#   uint32_t num_cameras (reserved)
#   uint32_t num_lights (reserved)
#
# Object Block:
#   char[64] name
#   uint32_t type (0=CUBE, 1=SPHERE, 2=PLANE, 3=TORUS, 4=BOX, 5=SKYBOX, 6=MESH)
#   vec3 position
#   quat rotation (x, y, z, w)
#   vec3 scale
#   vec4 color
#   float plane_distance (if type == PLANE)
#   uint32_t mesh_name_len
#   char[] mesh_name (if type == MESH)
#   float mass
#   float restitution
#   uint32_t is_static (bool)

def export_scene(filepath):
    print(f"Exporting scene to {filepath}...")
    
    objects = []
    for obj in bpy.context.scene.objects:
        if obj.visible_get():
            if obj.type == 'MESH':
                objects.append(obj)
            elif obj.type == 'EMPTY' and obj.name.lower().startswith('plane_'):
                objects.append(obj)
    
    with open(filepath, 'wb') as f:
        # Header
        f.write(b'SCN1')
        f.write(struct.pack('<III', len(objects), 0, 0))
        
        for obj in objects:
            print(f"  Exporting {obj.name}...")
            
            # Name (64 bytes, null-padded)
            name_bytes = obj.name.encode('utf-8')[:63]
            f.write(struct.pack('<64s', name_bytes))
            
            # Type detection
            # Default to MESH (6)
            obj_type = 6 
            
            # Simple heuristic for primitives based on name
            # In a real pipeline, we might use custom properties
            name_lower = obj.name.lower()
            if 'cube' in name_lower and 'mesh' not in name_lower: obj_type = 0
            elif 'sphere' in name_lower: obj_type = 1
            elif 'plane' in name_lower: obj_type = 2
            elif 'torus' in name_lower: obj_type = 3
            elif 'box' in name_lower: obj_type = 4
            
            f.write(struct.pack('<I', obj_type))
            
            # Transform
            # Blender uses Z-up. We typically use Y-up.
            # Conversion:
            #   Pos: (x, z, -y)
            #   Rot: Convert quaternion
            
            pos = obj.location
            rot = obj.rotation_quaternion
            scale = obj.scale
            
            # Position
            # For now, exporting raw Blender coordinates. 
            # We can fix coordinate system in C++ or here if decided.
            # Keeping raw for now to avoid confusion until verified.
            f.write(struct.pack('<3f', pos.x, pos.y, pos.z))
            
            # Rotation (x, y, z, w)
            # Blender provides (w, x, y, z)
            f.write(struct.pack('<4f', rot.x, rot.y, rot.z, rot.w))
            
            # Scale
            f.write(struct.pack('<3f', scale.x, scale.y, scale.z))
            
            # Color (RGBA)
            # Try to get from material
            color = (1.0, 1.0, 1.0, 1.0)
            if obj.active_material:
                c = obj.active_material.diffuse_color
                color = (c[0], c[1], c[2], c[3])
            f.write(struct.pack('<4f', *color))

            # Plane Distance (if type == PLANE)
            plane_distance = 0.0
            if obj_type == 2: # PLANE
                plane_distance = obj.get('plane_distance', 0.0)
            f.write(struct.pack('<f', plane_distance))
            
            # Mesh Asset Name
            mesh_asset_name = ""
            if obj_type == 6: # MESH
                # Ensure the mesh name is sanitized for AssetId (e.g. MESH_CUBE)
                # Convention: MESH_<ObjectName_Upper>
                mesh_asset_name = "MESH_" + obj.name.upper().replace('.', '_')
            
            name_len = len(mesh_asset_name)
            f.write(struct.pack('<I', name_len))
            if name_len > 0:
                f.write(mesh_asset_name.encode('utf-8'))
                
            # Physics properties (from custom properties or defaults)
            mass = obj.get('mass', 1.0)
            restitution = obj.get('restitution', 0.5)
            is_static = obj.get('is_static', 0)
            
            f.write(struct.pack('<ffI', float(mass), float(restitution), int(is_static)))

    print("Export complete.")

# To run in Blender:
# import sys
# sys.path.append('/path/to/demo/tools')
# import blender_export
# blender_export.export_scene('/path/to/demo/assets/final/scene.bin')

if __name__ == "__main__":
    # Standalone test (won't work outside Blender environment usually)
    pass