diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py
index 210e7cd..b74cca8 100644
--- a/korman/exporter/convert.py
+++ b/korman/exporter/convert.py
@@ -22,6 +22,7 @@ from . import explosions
from . import logger
from . import manager
from . import mesh
+from . import physics
from . import utils
class Exporter:
@@ -42,6 +43,7 @@ class Exporter:
self.mgr = manager.ExportManager(globals()[self._op.version])
self.mesh = mesh.MeshConverter(self)
self.report = logger.ExportAnalysis()
+ self.physics = physics.PhysicsConverter(self)
# Step 1: Gather a list of objects that we need to export
# We should do this first so we can sanity check
diff --git a/korman/exporter/physics.py b/korman/exporter/physics.py
new file mode 100644
index 0000000..102a26c
--- /dev/null
+++ b/korman/exporter/physics.py
@@ -0,0 +1,116 @@
+# This file is part of Korman.
+#
+# Korman is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Korman is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Korman. If not, see .
+
+import mathutils
+from PyHSPlasma import *
+import weakref
+
+from . import utils
+
+class PhysicsConverter:
+ def __init__(self, exporter):
+ self._exporter = weakref.ref(exporter)
+
+ def _convert_mesh_data(self, bo, physical, indices=True):
+ mesh = bo.data
+ mesh.update(calc_tessface=True)
+
+ mat = bo.matrix_world
+ if not self._mgr.has_coordiface(bo):
+ physical.rot = utils.quaternion(mat.to_quaternion())
+ physical.pos = utils.vector3(mat.to_translation())
+
+ # Physicals can't have scale...
+ scale = mat.to_scale()
+ if scale[0] == 1.0 and scale[1] == 1.0 and scale[2] == 1.0:
+ # Whew, don't need to do any math!
+ vertices = [hsVector3(i.co.x, i.co.y, i.co.z) for i in mesh.vertices]
+ else:
+ # Dagnabbit...
+ vertices = [hsVector3(i.co.x * scale.x, i.co.y * scale.y, i.co.z * scale.z) for i in mesh.vertices]
+
+ if indices:
+ indices = []
+ for face in mesh.tessfaces:
+ v = face.vertices
+ if len(v) == 3:
+ indices += v
+ elif len(v) == 4:
+ indices += (v[0], v[1], v[2],)
+ indices += (v[0], v[2], v[3],)
+ return (vertices, indices)
+ else:
+ return vertices
+
+ def generate_physical(self, bo, so, name=None):
+ """Generates a physical object for the given object pair"""
+ if so.sim is None:
+ if name is None:
+ name = bo.name
+
+ simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo)
+ physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo, name=name)
+
+ so.sim = simIface.key
+ simIface.physical = physical.key
+ physical.object = so.key
+ physical.sceneNode = self._mgr.get_scene_node(bl=bo)
+ else:
+ simIface = so.sim.object
+ physical = simIface.physical.object
+ if name is not None:
+ physical.key.name = name
+
+ return (simIface, physical)
+
+ def export(self, bo, physical, bounds):
+ getattr(self, "_export_{}".format(bounds))(bo, physical)
+
+ def _export_box(self, bo, physical):
+ """Exports box bounds based on the object"""
+ physical.boundsType = plSimDefs.kBoxBounds
+
+ vertices = self._convert_mesh_data(bo, physical, indices=False)
+ physical.calcBoxBounds(vertices)
+
+ def _export_hull(self, bo, physical):
+ """Exports convex hull bounds based on the object"""
+ physical.boundsType = plSimDefs.kHullBounds
+
+ vertices = self._convert_mesh_data(bo, physical, indices=False)
+ # --- TODO ---
+ # Until we have real convex hull processing, simply dump the verts into the physical
+ # Note that PyPRP has always done this... PhysX will optimize this for us. So, it's not
+ # the end of the world (but it is evil).
+ physical.verts = vertices
+
+ def _export_sphere(self, bo, physical):
+ """Exports sphere bounds based on the object"""
+ physical.boundsType = plSimDefs.kSphereBounds
+
+ vertices = self._convert_mesh_data(bo, physical, indices=False)
+ physical.calcSphereBounds(vertices)
+
+ def _export_trimesh(self, bo, physical):
+ """Exports an object's mesh as exact physical bounds"""
+ physical.boundsType = plSimDefs.kExplicitBounds
+
+ vertices, indices = self._convert_mesh_data(bo, physical)
+ physical.verts = vertices
+ physical.indices = indices
+
+ @property
+ def _mgr(self):
+ return self._exporter().mgr
diff --git a/korman/exporter/utils.py b/korman/exporter/utils.py
index 39a71dc..297b116 100644
--- a/korman/exporter/utils.py
+++ b/korman/exporter/utils.py
@@ -29,6 +29,10 @@ def matrix44(blmat):
hsmat[i, 3] = blmat[i][3]
return hsmat
+def quaternion(blquat):
+ """Converts a mathutils.Quaternion to an hsQuat"""
+ return hsQuat(blquat.x, blquat.y, blquat.z, blquat.w)
+
def vector3(blvec):
"""Converts a mathutils.Vector to an hsVector3"""
return hsVector3(blvec.x, blvec.y, blvec.z)
diff --git a/korman/properties/modifiers/__init__.py b/korman/properties/modifiers/__init__.py
index cd52643..c0cef55 100644
--- a/korman/properties/modifiers/__init__.py
+++ b/korman/properties/modifiers/__init__.py
@@ -18,6 +18,7 @@ import inspect
from .base import PlasmaModifierProperties
from .logic import *
+from .physics import *
class PlasmaModifiers(bpy.types.PropertyGroup):
def determine_next_id(self):
diff --git a/korman/properties/modifiers/physics.py b/korman/properties/modifiers/physics.py
new file mode 100644
index 0000000..cb004f9
--- /dev/null
+++ b/korman/properties/modifiers/physics.py
@@ -0,0 +1,92 @@
+# This file is part of Korman.
+#
+# Korman is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Korman is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Korman. If not, see .
+
+import bpy
+from bpy.props import *
+from PyHSPlasma import *
+
+from .base import PlasmaModifierProperties
+
+# These are the kinds of physical bounds Plasma can work with.
+# This sequence is acceptable in any EnumProperty
+bounds_types = (
+ ("box", "Bounding Box", "Use a perfect bounding box"),
+ ("sphere", "Bounding Sphere", "Use a perfect bounding sphere"),
+ ("hull", "Convex Hull", "Use a convex set encompasing all vertices"),
+ ("trimesh", "Triangle Mesh", "Use the exact triangle mesh (SLOW!)")
+)
+
+def _set_phys_prop(prop, sim, phys, value=True):
+ """Sets properties on plGenericPhysical and plSimulationInterface (seeing as how they are duped)"""
+ sim.setProperty(prop, value)
+ phys.setProperty(prop, value)
+
+
+class PlasmaCollider(PlasmaModifierProperties):
+ pl_id = "collision"
+
+ bl_category = "Physics"
+ bl_label = "Collision"
+ bl_icon = "MOD_PHYSICS"
+ bl_description = "Simple physical collider"
+
+ bounds = EnumProperty(name="Bounds Type", description="", items=bounds_types)
+
+ avatar_blocker = BoolProperty(name="Blocks Avatars", description="Object blocks avatars", default=True)
+ camera_blocker = BoolProperty(name="Blocks Camera", description="Object blocks the camera", default=True)
+
+ friction = FloatProperty(name="Friction", min=0.0, default=0.5)
+ restitution = FloatProperty(name="Restitution", description="Coefficient of collision elasticity", min=0.0, max=1.0)
+ terrain = BoolProperty(name="Terrain", description="Object represents the ground", default=False)
+
+ dynamic = BoolProperty(name="Dynamic", description="Object can be influenced by other objects (ie is kickable)", default=False)
+ mass = FloatProperty(name="Mass", description="Mass of object in pounds", min=0.0, default=1.0)
+ start_asleep = BoolProperty(name="Start Asleep", description="Object is not active until influenced by another object", default=False)
+
+ def created(self, obj):
+ self.display_name = "{}_Collision".format(obj.name)
+
+ def export(self, exporter, bo, so):
+ simIface, physical = exporter.physics.generate_physical(bo, so, self.display_name)
+
+ # Common props
+ physical.friction = self.friction
+ physical.restitution = self.restitution
+
+ # Collision groups and such
+ if not self.avatar_blocker:
+ physical.collideGroup = plSimDefs.kGroupLOSOnly
+
+ if self.dynamic:
+ physical.memberGroup = plSimDefs.kGroupDynamic
+ physical.mass = self.mass
+ _set_phys_prop(plSimulationInterface.kStartInactive, simIface, physical, value=self.start_asleep)
+ else:
+ physical.memberGroup = plSimDefs.kGroupStatic
+
+ # Line of Sight DB
+ if self.camera_blocker:
+ physical.LOSDBs |= plSimDefs.kLOSDBCameraBlockers
+ # This appears to be dead in CWE, but we'll set it anyway
+ _set_phys_prop(plSimulationInterface.kCameraAvoidObject, simIface, physical)
+ if self.terrain:
+ physical.LOSDBs |= plSimDefs.kLOSDBAvatarWalkable
+
+ # Pass this off to the PhysicsConverter to export the meat
+ exporter.physics.export(bo, physical, self.bounds)
+
+ @property
+ def requires_actor(self):
+ return self.dynamic
diff --git a/korman/ui/modifiers/__init__.py b/korman/ui/modifiers/__init__.py
index a8b2222..2facd58 100644
--- a/korman/ui/modifiers/__init__.py
+++ b/korman/ui/modifiers/__init__.py
@@ -14,3 +14,4 @@
# along with Korman. If not, see .
from .logic import *
+from .physics import *
diff --git a/korman/ui/modifiers/physics.py b/korman/ui/modifiers/physics.py
new file mode 100644
index 0000000..fc72225
--- /dev/null
+++ b/korman/ui/modifiers/physics.py
@@ -0,0 +1,40 @@
+# This file is part of Korman.
+#
+# Korman is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Korman is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Korman. If not, see .
+
+def collision(modifier, layout, context):
+ layout.prop(modifier, "bounds")
+ layout.separator()
+
+ split = layout.split()
+ col = split.column()
+ col.prop(modifier, "avatar_blocker")
+ col.prop(modifier, "camera_blocker")
+ col.prop(modifier, "terrain")
+
+ col = split.column()
+ col.prop(modifier, "friction")
+ col.prop(modifier, "restitution")
+ layout.separator()
+
+ split = layout.split()
+ col = split.column()
+ col.prop(modifier, "dynamic")
+ row = col.row()
+ row.active = modifier.dynamic
+ row.prop(modifier, "start_asleep")
+
+ col = split.column()
+ col.active = modifier.dynamic
+ col.prop(modifier, "mass")