Skip to content

Commit

Permalink
Allow MVR reimport
Browse files Browse the repository at this point in the history
  • Loading branch information
vanous committed Dec 16, 2023
1 parent 6cc1fa8 commit 0b51b31
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 36 deletions.
21 changes: 16 additions & 5 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from dmx.osc import DMX_OSC

from dmx.util import rgb_to_cmy, xyY2rgbaa
from dmx.mvr_objects import DMX_MVR_Object

from bpy.props import (BoolProperty,
StringProperty,
Expand Down Expand Up @@ -93,6 +94,7 @@ class DMX(PropertyGroup):
DMX_Emitter_Material,
DMX_Fixture_Channel,
DMX_Fixture,
DMX_MVR_Object,
DMX_Group,
DMX_Universe,
DMX_Value,
Expand Down Expand Up @@ -216,6 +218,10 @@ def unregister():
name = "DMX Groups",
type = DMX_Universe)

mvr_objects: CollectionProperty(
name = "MVR Objects",
type = DMX_MVR_Object)

def prepare_empty_buffer(self, context):
# Clear the buffer on change of every protocol
DMX_Data.prepare_empty_buffer()
Expand Down Expand Up @@ -876,8 +882,8 @@ def syncProgrammer(self):
)
# Kernel Methods
# # Fixtures

def addFixture(self, name, profile, universe, address, mode, gel_color, display_beams, add_target, position=None, focus_point=None, uuid = None, fixture_id="", custom_id=0, fixture_id_numeric=0, unit_number=0):
# TODO: fix order of attributes to match fixture.build()
bpy.app.handlers.depsgraph_update_post.clear()
dmx = bpy.context.scene.dmx
dmx.fixtures.add()
Expand Down Expand Up @@ -930,8 +936,13 @@ def addMVR(self, file_name):
extract_mvr_textures(mvr_scene, media_folder_path)

for layer_index, layer in enumerate(mvr_scene.layers):
layer_collection = bpy.data.collections.new(layer.name or f"Layer {layer_index}")
bpy.context.scene.collection.children.link(layer_collection)

layer_collection_name = layer.name or f"Layer {layer_index}"
if layer_collection_name in bpy.context.scene.collection.children:
layer_collection = bpy.context.scene.collection.children[layer_collection_name]
else:
layer_collection = bpy.data.collections.new(layer.name or f"Layer {layer_index}")
bpy.context.scene.collection.children.link(layer_collection)

g_name = layer.name or "Layer"
g_name = f"{g_name} {layer_index}"
Expand All @@ -948,7 +959,7 @@ def addMVR(self, file_name):
fixture_group
)
self.clean_up_empty_mvr_collections(layer_collection)
if len(layer_collection.children) == 0:
if len(layer_collection.all_objects) == 0:
bpy.context.scene.collection.children.unlink(layer_collection)

bpy.context.window_manager.dmx.pause_render = False # re-enable render loop
Expand All @@ -957,7 +968,7 @@ def addMVR(self, file_name):

def clean_up_empty_mvr_collections(self,collections):
for collection in collections.children:
if len(collection.children) == 0:
if len(collection.all_objects) == 0:
collections.children.unlink(collection)

def ensureUniverseExists(self, universe):
Expand Down
1 change: 1 addition & 0 deletions group.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def update(self):
# and repopulate it
if (len(sel_fixtures)):
#self.runtime[self.name] = sel_fixtures;
# TODO: it would be better to use fixture UUID
self.dump = json.dumps([fixture.name for fixture in sel_fixtures])
else:
self.dump = ''
Expand Down
110 changes: 83 additions & 27 deletions mvr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import hashlib
import json
from dmx.group import FixtureGroup
from dmx.mvr_objects import DMX_MVR_Object


# importing from dmx didn't work, had to duplicate this function
Expand All @@ -33,8 +34,8 @@ def onDepsgraph(scene):


def process_mvr_child_list(dmx, child_list, layer_index, extract_to_folder_path, mvr_scene, already_extracted_files, layer_collection, fixture_group=None):
if "MVR Trusses" in layer_collection:
truss_collection = layer_collection["MVR Trusses"]
if "MVR Trusses" in layer_collection.children:
truss_collection = layer_collection.children["MVR Trusses"]
else:
truss_collection = bpy.data.collections.new("MVR Trusses")
layer_collection.children.link(truss_collection)
Expand Down Expand Up @@ -65,8 +66,8 @@ def process_mvr_child_list(dmx, child_list, layer_index, extract_to_folder_path,
fixture_group,
)

if "MVR Scene objects" in layer_collection:
scene_collection_top = layer_collection["MVR Scene objects"]
if "MVR Scene objects" in layer_collection.children:
scene_collection_top = layer_collection.children["MVR Scene objects"]
else:
scene_collection_top = bpy.data.collections.new("MVR Scene objects")
layer_collection.children.link(scene_collection_top)
Expand All @@ -90,6 +91,7 @@ def process_mvr_child_list(dmx, child_list, layer_index, extract_to_folder_path,
scene_collection = bpy.data.collections.new(scene_name)
scene_collection_top.children.link(scene_collection)
collection = scene_collection
print("creating extra collection", scene_name)

process_mvr_object(
mvr_scene,
Expand Down Expand Up @@ -163,12 +165,31 @@ def process_mvr_object(
folder = os.path.join(current_path, "assets", "models", "mvr")
name = mvr_object.name

dmx = bpy.context.scene.dmx
previous_mvr_object = None
for existing_mvr_object in dmx.mvr_objects:
if existing_mvr_object.uuid == mvr_object.uuid:
previous_mvr_object = existing_mvr_object
print("Updating existing mvr object")
for child in existing_mvr_object.collection.children:
for obj in child.objects:
bpy.data.objects.remove(obj)
break
if previous_mvr_object:
dmx_mvr_object = previous_mvr_object
else:
dmx_mvr_object = dmx.mvr_objects.add()
dmx_mvr_object.name = name
dmx_mvr_object.object_type = mvr_object.__class__.__name__
dmx_mvr_object.uuid = mvr_object.uuid
dmx_mvr_object.collection = bpy.data.collections.new(mvr_object.uuid)

for geometry in geometry3ds:
if geometry.file_name:
file = geometry.file_name
local_transform = geometry.matrix.matrix
extract_mvr_object(file, mvr_scene, folder, already_extracted_files)
add_mvr_object(
coll = add_mvr_object(
name,
file,
folder,
Expand All @@ -179,6 +200,8 @@ def process_mvr_object(
group_collection,
mvr_object,
)
if coll:
dmx_mvr_object.collection.children.link(coll)

for symbol in symbols:
symdefs = [sd for sd in mvr_scene.aux_data.symdefs if sd.uuid == symbol.symdef]
Expand All @@ -189,7 +212,7 @@ def process_mvr_object(
local_transform = geometry.matrix.matrix

extract_mvr_object(file, mvr_scene, folder, already_extracted_files)
add_mvr_object(
coll = add_mvr_object(
name,
file,
folder,
Expand All @@ -200,6 +223,8 @@ def process_mvr_object(
group_collection,
symbol,
)
if coll:
dmx_mvr_object.collection.children.link(coll)


def extract_mvr_object(file, mvr_scene, folder, already_extracted_files):
Expand Down Expand Up @@ -280,7 +305,6 @@ def add_mvr_object(
ob.rotation_mode = "XYZ"
ob.rotation_euler = Matrix(local_transform).to_euler("XYZ")
ob["file name"] = file
ob["uuid"] = mvr_object.uuid

ob.matrix_world = global_transform
# ob.location = Matrix(global_transform).to_translation()
Expand All @@ -298,9 +322,13 @@ def add_mvr_object(
ob.scale[2] *= 0.001

object_collection.objects.link(ob)
group_collection.children.link(object_collection)
# bpy.app.handlers.depsgraph_update_post.append(onDepsgraph)
print("MVR object loaded in %.4f sec." % (time.time() - start_time))

if len(object_collection.children) + len(object_collection.objects):
group_collection.children.link(object_collection)
print("MVR object loaded in %.4f sec." % (time.time() - start_time))
return object_collection

return None


def add_mvr_fixture(
Expand All @@ -315,6 +343,14 @@ def add_mvr_fixture(
fixture_group=None,
):
"""Add fixture to the scene"""

existing_fixture = None
for _fixture in dmx.fixtures:
if _fixture.uuid == fixture.uuid:
existing_fixture = _fixture
print(f"Update existing fixture {fixture.uuid}")
break

if f"{fixture.gdtf_spec}" in mvr_scene._package.namelist():
if fixture.gdtf_spec not in already_extracted_files.keys():
mvr_scene._package.extract(fixture.gdtf_spec, extract_to_folder_path)
Expand All @@ -326,23 +362,43 @@ def add_mvr_fixture(
fixture.gdtf_spec = "BlenderDMX@LED_PAR_64_RGBW@v0.3.gdtf"

dmx.ensureUniverseExists(fixture.addresses[0].universe)
dmx.addFixture(
f"{fixture.name} {layer_index}-{fixture_index}",
fixture.gdtf_spec,
fixture.addresses[0].universe,
fixture.addresses[0].address,
fixture.gdtf_mode,
xyY2rgbaa(fixture.color),
True,
True,
position=fixture.matrix.matrix,
focus_point=focus_point,
uuid=fixture.uuid,
fixture_id=fixture.fixture_id,
custom_id=fixture.custom_id,
fixture_id_numeric=fixture.fixture_id_numeric,
unit_number=fixture.unit_number,
)

if existing_fixture is not None:
existing_fixture.build(
f"{fixture.name} {layer_index}-{fixture_index}",
fixture.gdtf_spec,
fixture.gdtf_mode,
fixture.addresses[0].universe,
fixture.addresses[0].address,
xyY2rgbaa(fixture.color),
True,
True,
mvr_position=fixture.matrix.matrix,
focus_point=focus_point,
uuid=fixture.uuid,
fixture_id=fixture.fixture_id,
custom_id=fixture.custom_id,
fixture_id_numeric=fixture.fixture_id_numeric,
unit_number=fixture.unit_number,
)
else:
dmx.addFixture(
f"{fixture.name} {layer_index}-{fixture_index}",
fixture.gdtf_spec,
fixture.addresses[0].universe,
fixture.addresses[0].address,
fixture.gdtf_mode,
xyY2rgbaa(fixture.color),
True,
True,
position=fixture.matrix.matrix,
focus_point=focus_point,
uuid=fixture.uuid,
fixture_id=fixture.fixture_id,
custom_id=fixture.custom_id,
fixture_id_numeric=fixture.fixture_id_numeric,
unit_number=fixture.unit_number,
)

if fixture_group is not None:
fixture_name = f"{fixture.name} {layer_index}-{fixture_index}"
Expand Down
56 changes: 56 additions & 0 deletions mvr_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#
# BlendexDMX > MVR Objects
#
# http://www.github.com/open-stage/BlenderDMX
#

import bpy
import math
import mathutils
import uuid

import json
from bpy.props import (IntProperty,
FloatProperty,
BoolProperty,
FloatVectorProperty,
PointerProperty,
StringProperty,
CollectionProperty)

from bpy.types import (PropertyGroup,
Collection,
Object,
Material)

class DMX_MVR_Object(PropertyGroup):
"""Universal MVR object... in the future, make this specific
SceneObject, Truss, Layer..."""

name: StringProperty(
name = "Name",
description = "Name",
default = ""
)

collection: PointerProperty(
name = "Collection of objects",
type = Collection)

uuid: StringProperty(
name = "UUID",
description = "UUID",
default = str(uuid.uuid4())
)

object_type: StringProperty(
name = "Object type",
description = "Simple object classification",
default = "SceneObject" #Layer, Truss,
)
classing: StringProperty(
name = "Classing",
description = "Grouping/Layering",
default = ""
)

1 change: 1 addition & 0 deletions panels/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ def execute(self, context):
dmx = scene.dmx
selected = scene.dmx.selectedFixtures()
context.window_manager.dmx.pause_render = True # pause renderer as partially imported fixture can cause issues during updates
# TODO: handle re-grouping if fixture name changed
# Single fixture
if (len(selected) == 1):
fixture = selected[0]
Expand Down
22 changes: 18 additions & 4 deletions pymvr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,15 +560,17 @@ def _read_xml(self, xml_node: "Element"):
self.uuid = xml_node.attrib.get("uuid")

self.symbol = [Symbol(xml_node=i) for i in xml_node.findall("Symbol")]
self.geometry3d = [Geometry3D(xml_node=i) for i in xml_node.findall("Geometry3D")]
_geometry3d = [Geometry3D(xml_node=i) for i in xml_node.findall("Geometry3D")]
if xml_node.find("ChildList"):
child_list = xml_node.find("ChildList")

symbols = [Symbol(xml_node=i) for i in child_list.findall("Symbol")]
geometry3ds = [Geometry3D(xml_node=i) for i in child_list.findall("Geometry3D")]
self.symbol += symbols # TODO remove this over time, children should only be in the child_list
self.geometry3d += geometry3ds
self.symbol += symbols
_geometry3d += geometry3ds

# sometimes the list of geometry3d is full of duplicates, eliminate them here
self.geometry3d = list(set(_geometry3d))

class Geometry3D(BaseNode):
def __init__(
Expand All @@ -588,7 +590,19 @@ def _read_xml(self, xml_node: "Element"):
self.matrix = Matrix(str_repr=xml_node.find("Matrix").text)

def __str__(self):
return f"{self.file_name}"
return f"{self.file_name} {self.matrix}"

def __repr__(self):
return f"{self.file_name} {self.matrix}"

def __eq__(self, other):
return self.file_name == other.file_name and self.matrix == other.matrix

def __ne__(self, other):
return self.file_name != other.file_name or self.matrix != other.matrix

def __hash__(self):
return hash((self.file_name, str(self.matrix)))


class Symbol(BaseNode):
Expand Down
Loading

0 comments on commit 0b51b31

Please sign in to comment.