241 lines
7.8 KiB
Python
241 lines
7.8 KiB
Python
|
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()
|
||
|
|