import bpy import os import numpy as np from .types import * @dataclass class Scene: transforms: list[Transform] @classmethod def collect_transforms(cls, parent, objs, out): if len(objs) == 0: return for obj in objs: if parent == "": name = obj.name else: name = parent + "+" + obj.name position = np.array([obj.location.x, obj.location.y, obj.location.z], dtype="float32") rotation = np.array([obj.rotation_euler.x, obj.rotation_euler.y, obj.rotation_euler.z], dtype="float32") scale = np.array([obj.scale.x, obj.scale.y, obj.scale.z], dtype="float32") if obj.instance_type == "COLLECTION": prefab = obj.instance_collection # Collections flatten the hierarchy; unflatten it components = {} for o in prefab.objects: components[o.name] = o to_remove = [] for (k,v) in components.items(): for child in v.children: if child.name in components: to_remove.append(child.name) for r in to_remove: components.pop(r) # Now we have the direct children of the prefab itself children = [] for child in components: child_name = name + "+" + child children.append(child_name) transform = Transform(name, position, rotation, scale, parent, children, Handle(0), [], obj) out.append(transform) cls.collect_transforms(name, components.values(), out) else: children = [] for child in obj.children: child_name = name + "+" + child.name children.append(child_name) transform = Transform(name, position, rotation, scale, parent, children, Handle(0), [], obj) out.append(transform) cls.collect_transforms(name, obj.children, out) def __init__(self): # Load transforms in hierarchical order transforms = [Transform()] self.collect_transforms("Root", bpy.data.objects["Root"].children, transforms) # Set up parent-child relationships for transform in transforms: if transform.parent_name != "": for i, parent_transform in enumerate(transforms): if transform.parent_name == parent_transform.name: transform.parent = Handle(np.uint32(i)) break if len(transform.children_names) > 0: for child_name in transform.children_names: for i, child_transform in enumerate(transforms): if child_name == child_transform.name: transform.children.append(Handle(np.uint32(i))) break self.transforms = transforms @dataclass class Data1: cameras: list[Camera] materials: list[Material] meshes: list[Mesh] renderables: list[Renderable] box_colliders: list[BoxCollider] point_lights: list[PointLight] def __init__(self, scene): materials = [Material()] for material in bpy.data.materials: if material.name == "Dots Stroke": continue materials.append(Material.load(material)) meshes = [Mesh()] for mesh in bpy.data.meshes: meshes.append(Mesh.load(mesh)) renderables = [Renderable()] box_colliders = [BoxCollider()] point_lights = [PointLight()] cameras = [Camera()] for (i,transform) in enumerate(scene.transforms): if transform.name == "NULL": continue; obj = transform.obj if obj.ajm_type.box_collider: box_colliders.append(BoxCollider.load(obj, i)) if obj.ajm_type.camera: cameras.append(Camera.load(obj, i)) if obj.ajm_type.point_light: point_lights.append(PointLight.load(obj, i)) if obj.ajm_type.renderable: renderables.append(Renderable.load(obj, meshes, materials, i)) self.cameras = cameras self.materials = materials self.meshes = meshes self.transforms = scene.transforms self.renderables = renderables self.box_colliders = box_colliders self.point_lights = point_lights def serialize(self, path): fp = open(path, "wb") fp.write("AJM\0".encode('UTF-8')) # Transforms print("Transforms: " + str(len(self.transforms)-1)) transform_count = np.uint16(len(self.transforms)) fp.write(transform_count.tobytes()) for transform in self.transforms: transform.serialize(fp) # Meshes print("Meshes: " + str(len(self.meshes)-1)) mesh_count = np.uint16(len(self.meshes)) fp.write(mesh_count.tobytes()) for mesh in self.meshes: mesh.serialize(fp) # Materials print("Materials: " + str(len(self.materials)-1)) material_count = np.uint16(len(self.materials)) fp.write(material_count.tobytes()) for material in self.materials: material.serialize(fp) # Cameras print("Cameras: " + str(len(self.cameras)-1)) camera_count = np.uint16(len(self.cameras)) fp.write(camera_count.tobytes()) for camera in self.cameras: camera.serialize(fp) # Renderables print("Renderables: " + str(len(self.renderables)-1)) renderable_count = np.uint16(len(self.renderables)) fp.write(renderable_count.tobytes()) for renderable in self.renderables: renderable.serialize(fp) # Box Colliders print("Box Colliders: " + str(len(self.box_colliders)-1)) box_collider_count = np.uint16(len(self.box_colliders)) fp.write(box_collider_count.tobytes()) for box_collider in self.box_colliders: box_collider.serialize(fp) # Point Lights print("Point Lights: " + str(len(self.point_lights)-1)) point_light_count = np.uint16(len(self.point_lights)) fp.write(point_light_count.tobytes()) for point_light in self.point_lights: point_light.serialize(fp) fp.close() @dataclass class Data2: sounds: list[Sound] audio_sources: list[AudioSource] def __init__(self, scene): sounds = [Sound()] audio_sources = [AudioSource()] for (i,transform) in enumerate(scene.transforms): if transform.name == "NULL": continue; obj = transform.obj if obj.ajm_type.audio_source: sound = Sound.load(obj.ajm_audio_source.file_path) unique = True for s in sounds: if sound.path == s.path: unique = False if unique: sounds.append(sound) audio_sources.append(AudioSource.load(obj, sounds, i)) self.sounds = sounds self.audio_sources = audio_sources def serialize(self, path): fp = open(path, "wb") fp.write("AJM\0".encode('UTF-8')) print("Sounds: " + str(len(self.sounds)-1)) sound_count = np.uint16(len(self.sounds)) fp.write(sound_count.tobytes()) for sound in self.sounds: sound.serialize(fp) print("Audio Sources: " + str(len(self.audio_sources)-1)) audio_source_count = np.uint16(len(self.audio_sources)) fp.write(audio_source_count.tobytes()) for audio_source in self.audio_sources: audio_source.serialize(fp) fp.close()