diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index 9eace31..6f43726 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -32,6 +32,7 @@ class Exporter: def __init__(self, op): self._op = op # Blender export operator self._objects = [] + self.actors = set() @property def age_name(self): @@ -58,6 +59,10 @@ class Exporter: # us to export (both in the Age and Object Properties)... fun self._collect_objects() + # Step 2.5: Run through all the objects we collected in Step 2 and see if any relationships + # that the artist made requires something to have a CoordinateInterface + self._harvest_actors() + # Step 3: Export all the things! self._export_scene_objects() @@ -135,8 +140,8 @@ class Exporter: def _export_actor(self, so, bo): """Exports a Coordinate Interface if we need one""" - if self.mgr.has_coordiface(bo): - self.export_coordinate_interface(so, bo) + if self.has_coordiface(bo): + self._export_coordinate_interface(so, bo) # If this object has a parent, then we will need to go upstream and add ourselves to the # parent's CoordinateInterface... Because life just has to be backwards. @@ -154,7 +159,7 @@ class Exporter: The object may not appear in the correct location or animate properly.".format( bo.name, parent.name)) - def export_coordinate_interface(self, so, bl, name=None): + def _export_coordinate_interface(self, so, bl, name=None): """Ensures that the SceneObject has a CoordinateInterface""" if not so.coord: ci = self.mgr.find_create_object(plCoordinateInterface, bl=bl, so=so, name=name) @@ -207,6 +212,35 @@ class Exporter: else: print(" No material(s) on the ObData, so no drawables") + def _harvest_actors(self): + for bl_obj in self._objects: + for mod in bl_obj.plasma_modifiers.modifiers: + if mod.enabled: + self.actors.update(mod.harvest_actors()) + + # This is a little hacky, but it's an edge case... I guess? + # We MUST have CoordinateInterfaces for EnvironmentMaps (DCMs, bah) + for texture in bpy.data.textures: + envmap = getattr(texture, "environment_map", None) + if envmap is not None: + viewpt = envmap.viewpoint_object + if viewpt is not None: + self.actors.add(viewpt.name) + + def has_coordiface(self, bo): + if bo.type in {"CAMERA", "EMPTY", "LAMP"}: + return True + if bo.parent is not None: + return True + if bo.name in self.actors: + return True + + for mod in bo.plasma_modifiers.modifiers: + if mod.enabled: + if mod.requires_actor: + return True + return False + def _post_process_scene_objects(self): print("\nPostprocessing SceneObjects...") for bl_obj in self._objects: diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py index ecbab28..d7d4d1e 100644 --- a/korman/exporter/manager.py +++ b/korman/exporter/manager.py @@ -220,18 +220,6 @@ class ExportManager: else: return key.location - def has_coordiface(self, bo): - if bo.type in {"CAMERA", "EMPTY", "LAMP"}: - return True - if bo.parent is not None: - return True - - for mod in bo.plasma_modifiers.modifiers: - if mod.enabled: - if mod.requires_actor: - return True - return False - def save_age(self, path): relpath, ageFile = os.path.split(path) ageName = os.path.splitext(ageFile)[0] diff --git a/korman/exporter/material.py b/korman/exporter/material.py index dabea3e..3775162 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -396,8 +396,9 @@ class MaterialConverter: # It matters not whether or not the viewpoint object is a Plasma Object, it is exported as at # least a SceneObject and CoordInterface so that we can touch it... + # NOTE: that harvest_actor makes sure everyone alread knows we're going to have a CI root = self._mgr.find_create_key(plSceneObject, bl=bo, name=viewpt.name) - self._exporter().export_coordinate_interface(root.object, bl=bo, name=viewpt.name) + self._exporter()._export_coordinate_interface(root.object, bl=bo, name=viewpt.name) # FIXME: DynamicCamMap Camera # Ensure POT diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 8cfe019..4301f9a 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -132,7 +132,7 @@ class MeshConverter: geospan.addPermaProjs(i) # If this object has a CI, we don't need xforms here... - if self._mgr.has_coordiface(bo): + if self._exporter().has_coordiface(bo): geospan.localToWorld = hsMatrix44() geospan.worldToLocal = hsMatrix44() else: diff --git a/korman/exporter/physics.py b/korman/exporter/physics.py index e0e52c6..4653865 100644 --- a/korman/exporter/physics.py +++ b/korman/exporter/physics.py @@ -31,7 +31,7 @@ class PhysicsConverter: with TemporaryObject(mesh, bpy.data.meshes.remove) as mesh: # We can only use the plPhysical xforms if there is a CI... - if self._mgr.has_coordiface(bo): + if self._exporter().has_coordiface(bo): mesh.update(calc_tessface=indices) physical.pos = utils.vector3(mat.to_translation()) quat = mat.to_quaternion() diff --git a/korman/nodes/node_conditions.py b/korman/nodes/node_conditions.py index b79c75b..9c6e9a5 100644 --- a/korman/nodes/node_conditions.py +++ b/korman/nodes/node_conditions.py @@ -52,8 +52,6 @@ class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): if clickable_bo is None: self.raise_error("invalid Clickable object: '{}'".format(self.clickable), tree) clickable_so = exporter.mgr.find_create_object(plSceneObject, bl=clickable_bo) - # We're deep inside a potentially unrelated node tree... - exporter.export_coordinate_interface(clickable_so, clickable_bo) else: clickable_bo = parent_bo clickable_so = parent_so @@ -101,10 +99,8 @@ class PlasmaClickableNode(PlasmaNodeBase, bpy.types.Node): face_target = self.find_input_socket("facing") face_target.convert_subcondition(exporter, tree, clickable_bo, clickable_so, logicmod) - @property - def requires_actor(self): - face_target = self.find_input_socket("facing") - return face_target.enable_condition + def harvest_actors(self): + return (self.clickable,) class PlasmaClickableRegionNode(PlasmaNodeBase, bpy.types.Node): @@ -189,10 +185,6 @@ class PlasmaFacingTargetNode(PlasmaNodeBase, bpy.types.Node): layout.prop(self, "directional") layout.prop(self, "tolerance") - @property - def requires_actor(self): - return True - class PlasmaFacingTargetSocket(PlasmaNodeSocketBase, bpy.types.NodeSocket): bl_color = (0.0, 0.267, 0.247, 1.0) diff --git a/korman/nodes/node_core.py b/korman/nodes/node_core.py index bdd282d..81099aa 100644 --- a/korman/nodes/node_core.py +++ b/korman/nodes/node_core.py @@ -95,6 +95,9 @@ class PlasmaNodeBase: return i raise KeyError(key) + def harvest_actors(self): + return set() + def link_input(self, tree, node, out_key, in_key): """Links a given Node's output socket to a given input socket on this Node""" if isinstance(in_key, str): @@ -166,13 +169,12 @@ class PlasmaNodeTree(bpy.types.NodeTree): for node in self.nodes: node.export(exporter, self, bo, so) + def harvest_actors(self): + actors = set() + for node in self.nodes: + actors.update(node.harvest_actors()) + return actors + @classmethod def poll(cls, context): return (context.scene.render.engine == "PLASMA_GAME") - - @property - def requires_actor(self): - for node in self.nodes: - if node.requires_actor: - return True - return False diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index 6583505..96c6e2a 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -274,6 +274,9 @@ class PlasmaOneShotMsgNode(PlasmaMessageNode, bpy.types.Node): else: return exporter.mgr.find_create_key(plOneShotMod, name=name, so=so) + def harvest_actors(self): + return (self.pos,) + @property def has_callbacks(self): return bool(self.marker) diff --git a/korman/properties/modifiers/avatar.py b/korman/properties/modifiers/avatar.py index 2131abb..cd9a7a3 100644 --- a/korman/properties/modifiers/avatar.py +++ b/korman/properties/modifiers/avatar.py @@ -66,6 +66,11 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): # Now, export the node tree self.node_tree.export(exporter, bo, so) + def harvest_actors(self): + if self.facing_enabled: + return (self.clickable_obj,) + return () + def logicwiz(self, bo): tree = self.node_tree nodes = tree.nodes @@ -105,4 +110,5 @@ class PlasmaSittingBehavior(PlasmaModifierProperties, PlasmaModifierLogicWiz): @property def requires_actor(self): - return self.facing_enabled + # This should be an empty, really... + return True diff --git a/korman/properties/modifiers/base.py b/korman/properties/modifiers/base.py index 7f15211..3aa81ed 100644 --- a/korman/properties/modifiers/base.py +++ b/korman/properties/modifiers/base.py @@ -29,6 +29,9 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup): def enabled(self): return self.display_order >= 0 + def harvest_actors(self): + return () + @property def requires_actor(self): """Indicates if this modifier requires the object to be a movable actor""" diff --git a/korman/properties/modifiers/logic.py b/korman/properties/modifiers/logic.py index 9ee7dd3..4d1e1a0 100644 --- a/korman/properties/modifiers/logic.py +++ b/korman/properties/modifiers/logic.py @@ -62,12 +62,11 @@ class PlasmaAdvancedLogic(PlasmaModifierProperties): if version in our_versions: i.node_tree.export(exporter, bo, so) - @property - def requires_actor(self): + def harvest_actors(self): + actors = set() for i in self.logic_groups: - if i.node_tree.requires_actor: - return True - return False + actors.update(i.node_tree.harvest_actors()) + return actors class PlasmaSpawnPoint(PlasmaModifierProperties):