summaryrefslogtreecommitdiff
path: root/tools/blender_export.py
blob: da7b986e4b811ac26a849fab6aa4926d5c128417 (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
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
#   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 = [obj for obj in bpy.context.scene.objects if obj.visible_get() and obj.type == 'MESH']
    
    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))
            
            # 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