Browse Source

Fix CoordinateInterface race conditions

To ensure that there are really, really, REALLY no race conditions related
to coordinate interfaces, we now run through all modifiers before we
export and ask them if they need to make Coordinate Interfaces. I was
hearing some comments about clickables warping around. This sounds like
physical coordinate issues to me...
pull/10/head
Adam Johnson 9 years ago
parent
commit
a3716a8c35
  1. 40
      korman/exporter/convert.py
  2. 12
      korman/exporter/manager.py
  3. 3
      korman/exporter/material.py
  4. 2
      korman/exporter/mesh.py
  5. 2
      korman/exporter/physics.py
  6. 12
      korman/nodes/node_conditions.py
  7. 16
      korman/nodes/node_core.py
  8. 3
      korman/nodes/node_messages.py
  9. 8
      korman/properties/modifiers/avatar.py
  10. 3
      korman/properties/modifiers/base.py
  11. 9
      korman/properties/modifiers/logic.py

40
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:

12
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]

3
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

2
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:

2
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()

12
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)

16
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

3
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)

8
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

3
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"""

9
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):

Loading…
Cancel
Save