|
|
@ -30,6 +30,22 @@ def _set_phys_prop(prop, sim, phys, value=True): |
|
|
|
class PhysicsConverter: |
|
|
|
class PhysicsConverter: |
|
|
|
def __init__(self, exporter): |
|
|
|
def __init__(self, exporter): |
|
|
|
self._exporter = weakref.ref(exporter) |
|
|
|
self._exporter = weakref.ref(exporter) |
|
|
|
|
|
|
|
self._bounds_converters = { |
|
|
|
|
|
|
|
"box": self._export_box, |
|
|
|
|
|
|
|
"sphere": self._export_sphere, |
|
|
|
|
|
|
|
"hull": self._export_hull, |
|
|
|
|
|
|
|
"trimesh": self._export_trimesh, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _apply_props(self, simIface, physical, props): |
|
|
|
|
|
|
|
for i in props.get("properties", []): |
|
|
|
|
|
|
|
_set_phys_prop(getattr(plSimulationInterface, i), simIface, physical) |
|
|
|
|
|
|
|
for i in props.get("losdbs", []): |
|
|
|
|
|
|
|
physical.LOSDBs |= getattr(plSimDefs, i) |
|
|
|
|
|
|
|
for i in props.get("report_groups", []): |
|
|
|
|
|
|
|
physical.reportGroup |= 1 << getattr(plSimDefs, i) |
|
|
|
|
|
|
|
for i in props.get("collide_groups", []): |
|
|
|
|
|
|
|
physical.collideGroup |= 1 << getattr(plSimDefs, i) |
|
|
|
|
|
|
|
|
|
|
|
def _convert_indices(self, mesh): |
|
|
|
def _convert_indices(self, mesh): |
|
|
|
indices = [] |
|
|
|
indices = [] |
|
|
@ -42,7 +58,7 @@ class PhysicsConverter: |
|
|
|
indices += (v[0], v[2], v[3],) |
|
|
|
indices += (v[0], v[2], v[3],) |
|
|
|
return indices |
|
|
|
return indices |
|
|
|
|
|
|
|
|
|
|
|
def _convert_mesh_data(self, bo, physical, indices=True, mesh_func=None): |
|
|
|
def _convert_mesh_data(self, bo, physical, local_space, indices=True, mesh_func=None): |
|
|
|
try: |
|
|
|
try: |
|
|
|
mesh = bo.to_mesh(bpy.context.scene, True, "RENDER", calc_tessface=False) |
|
|
|
mesh = bo.to_mesh(bpy.context.scene, True, "RENDER", calc_tessface=False) |
|
|
|
except: |
|
|
|
except: |
|
|
@ -54,8 +70,7 @@ class PhysicsConverter: |
|
|
|
if mesh_func is not None: |
|
|
|
if mesh_func is not None: |
|
|
|
mesh_func(mesh) |
|
|
|
mesh_func(mesh) |
|
|
|
|
|
|
|
|
|
|
|
# We can only use the plPhysical xforms if there is a CI... |
|
|
|
if local_space: |
|
|
|
if self._exporter().has_coordiface(bo): |
|
|
|
|
|
|
|
mesh.update(calc_tessface=indices) |
|
|
|
mesh.update(calc_tessface=indices) |
|
|
|
physical.pos = hsVector3(*mat.to_translation()) |
|
|
|
physical.pos = hsVector3(*mat.to_translation()) |
|
|
|
physical.rot = utils.quaternion(mat.to_quaternion()) |
|
|
|
physical.rot = utils.quaternion(mat.to_quaternion()) |
|
|
@ -79,12 +94,11 @@ class PhysicsConverter: |
|
|
|
else: |
|
|
|
else: |
|
|
|
return vertices |
|
|
|
return vertices |
|
|
|
|
|
|
|
|
|
|
|
def generate_flat_proxy(self, bo, so, z_coord=None, name=None): |
|
|
|
def generate_flat_proxy(self, bo, so, **kwargs): |
|
|
|
"""Generates a flat physical object""" |
|
|
|
"""Generates a flat physical object""" |
|
|
|
if so.sim is None: |
|
|
|
z_coord = kwargs.pop("z_coord", None) |
|
|
|
if name is None: |
|
|
|
|
|
|
|
name = bo.name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if so.sim is None: |
|
|
|
simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo) |
|
|
|
simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo) |
|
|
|
physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo, name=name) |
|
|
|
physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo, name=name) |
|
|
|
|
|
|
|
|
|
|
@ -94,7 +108,7 @@ class PhysicsConverter: |
|
|
|
|
|
|
|
|
|
|
|
mesh = bo.to_mesh(bpy.context.scene, True, "RENDER", calc_tessface=False) |
|
|
|
mesh = bo.to_mesh(bpy.context.scene, True, "RENDER", calc_tessface=False) |
|
|
|
with TemporaryObject(mesh, bpy.data.meshes.remove): |
|
|
|
with TemporaryObject(mesh, bpy.data.meshes.remove): |
|
|
|
# We will apply all xform, seeing as how this is a special case... |
|
|
|
# No mass and no emedded xform, so we force worldspace collision. |
|
|
|
mesh.transform(bo.matrix_world) |
|
|
|
mesh.transform(bo.matrix_world) |
|
|
|
mesh.update(calc_tessface=True) |
|
|
|
mesh.update(calc_tessface=True) |
|
|
|
|
|
|
|
|
|
|
@ -111,22 +125,36 @@ class PhysicsConverter: |
|
|
|
physical.verts = vertices |
|
|
|
physical.verts = vertices |
|
|
|
physical.indices = self._convert_indices(mesh) |
|
|
|
physical.indices = self._convert_indices(mesh) |
|
|
|
physical.boundsType = plSimDefs.kProxyBounds |
|
|
|
physical.boundsType = plSimDefs.kProxyBounds |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
group_name = kwargs.get("member_group") |
|
|
|
|
|
|
|
if group_name: |
|
|
|
|
|
|
|
physical.memberGroup = getattr(plSimDefs, group_name) |
|
|
|
else: |
|
|
|
else: |
|
|
|
simIface = so.sim.object |
|
|
|
simIface = so.sim.object |
|
|
|
physical = simIface.physical.object |
|
|
|
physical = simIface.physical.object |
|
|
|
if name is not None: |
|
|
|
|
|
|
|
physical.key.name = name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (simIface, physical) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_physical(self, bo, so, bounds, name=None): |
|
|
|
member_group = getattr(plSimDefs, kwargs.get("member_group", "kGroupLOSOnly")) |
|
|
|
"""Generates a physical object for the given object pair""" |
|
|
|
if physical.memberGroup != member_group and member_group != plSimDefs.kGroupLOSOnly: |
|
|
|
|
|
|
|
self._report.warn("{}: Physical memberGroup overwritten!", bo.name) |
|
|
|
|
|
|
|
physical.memberGroup = member_group |
|
|
|
|
|
|
|
self._apply_props(simIface, physical, kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_physical(self, bo, so, **kwargs): |
|
|
|
|
|
|
|
"""Generates a physical object for the given object pair. |
|
|
|
|
|
|
|
The following optional arguments are allowed: |
|
|
|
|
|
|
|
- bounds: (defaults to collision modifier setting) |
|
|
|
|
|
|
|
- member_group: str attribute of plSimDefs, defaults to kGroupStatic |
|
|
|
|
|
|
|
NOTE that kGroupLOSOnly generation will only succeed if no one else |
|
|
|
|
|
|
|
has generated this physical in another group |
|
|
|
|
|
|
|
- properties: sequence of str bit names from plSimulationInterface |
|
|
|
|
|
|
|
- losdbs: sequence of str bit names from plSimDefs |
|
|
|
|
|
|
|
- report_groups: sequence of str bit names from plSimDefs |
|
|
|
|
|
|
|
- collide_groups: sequence of str bit names from plSimDefs |
|
|
|
|
|
|
|
""" |
|
|
|
if so.sim is None: |
|
|
|
if so.sim is None: |
|
|
|
if name is None: |
|
|
|
|
|
|
|
name = bo.name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo) |
|
|
|
simIface = self._mgr.add_object(pl=plSimulationInterface, bl=bo) |
|
|
|
physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo, name=name) |
|
|
|
physical = self._mgr.add_object(pl=plGenericPhysical, bl=bo) |
|
|
|
|
|
|
|
ver = self._mgr.getVer() |
|
|
|
|
|
|
|
|
|
|
|
simIface.physical = physical.key |
|
|
|
simIface.physical = physical.key |
|
|
|
physical.object = so.key |
|
|
|
physical.object = so.key |
|
|
@ -137,6 +165,37 @@ class PhysicsConverter: |
|
|
|
if self.is_dedicated_subworld(subworld, sanity_check=False): |
|
|
|
if self.is_dedicated_subworld(subworld, sanity_check=False): |
|
|
|
physical.subWorld = self._mgr.find_create_key(plHKSubWorld, bl=subworld) |
|
|
|
physical.subWorld = self._mgr.find_create_key(plHKSubWorld, bl=subworld) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Export the collision modifier here since we like stealing from it anyway. |
|
|
|
|
|
|
|
mod = bo.plasma_modifiers.collision |
|
|
|
|
|
|
|
bounds = kwargs.get("bounds", mod.bounds) |
|
|
|
|
|
|
|
if mod.enabled: |
|
|
|
|
|
|
|
physical.friction = mod.friction |
|
|
|
|
|
|
|
physical.restitution = mod.restitution |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if mod.dynamic: |
|
|
|
|
|
|
|
if ver <= pvPots: |
|
|
|
|
|
|
|
physical.collideGroup = (1 << plSimDefs.kGroupDynamic) | \ |
|
|
|
|
|
|
|
(1 << plSimDefs.kGroupStatic) |
|
|
|
|
|
|
|
physical.memberGroup = plSimDefs.kGroupDynamic |
|
|
|
|
|
|
|
physical.mass = mod.mass |
|
|
|
|
|
|
|
_set_phys_prop(plSimulationInterface.kStartInactive, simIface, physical, |
|
|
|
|
|
|
|
value=mod.start_asleep) |
|
|
|
|
|
|
|
elif not mod.avatar_blocker: |
|
|
|
|
|
|
|
physical.memberGroup = plSimDefs.kGroupLOSOnly |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
physical.memberGroup = plSimDefs.kGroupStatic |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Line of Sight DB |
|
|
|
|
|
|
|
if mod.camera_blocker: |
|
|
|
|
|
|
|
physical.LOSDBs |= plSimDefs.kLOSDBCameraBlockers |
|
|
|
|
|
|
|
_set_phys_prop(plSimulationInterface.kCameraAvoidObject, simIface, physical) |
|
|
|
|
|
|
|
if mod.terrain: |
|
|
|
|
|
|
|
physical.LOSDBs |= plSimDefs.kLOSDBAvatarWalkable |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
group_name = kwargs.get("member_group") |
|
|
|
|
|
|
|
if group_name: |
|
|
|
|
|
|
|
physical.memberGroup = getattr(plSimDefs, group_name) |
|
|
|
|
|
|
|
|
|
|
|
# Ensure this thing is set up properly for animations. |
|
|
|
# Ensure this thing is set up properly for animations. |
|
|
|
# This was previously the collision modifier's postexport method, but that |
|
|
|
# This was previously the collision modifier's postexport method, but that |
|
|
|
# would miss cases where we have animated detectors (subworlds!!!) |
|
|
|
# would miss cases where we have animated detectors (subworlds!!!) |
|
|
@ -147,7 +206,6 @@ class PhysicsConverter: |
|
|
|
yield bo |
|
|
|
yield bo |
|
|
|
bo = bo.parent |
|
|
|
bo = bo.parent |
|
|
|
|
|
|
|
|
|
|
|
ver = self._mgr.getVer() |
|
|
|
|
|
|
|
for i in _iter_object_tree(bo, ver == pvMoul): |
|
|
|
for i in _iter_object_tree(bo, ver == pvMoul): |
|
|
|
if i.plasma_object.has_transform_animation: |
|
|
|
if i.plasma_object.has_transform_animation: |
|
|
|
tree_xformed = True |
|
|
|
tree_xformed = True |
|
|
@ -176,23 +234,30 @@ class PhysicsConverter: |
|
|
|
if physical.mass == 0.0: |
|
|
|
if physical.mass == 0.0: |
|
|
|
physical.mass = 1.0 |
|
|
|
physical.mass = 1.0 |
|
|
|
|
|
|
|
|
|
|
|
getattr(self, "_export_{}".format(bounds))(bo, physical) |
|
|
|
if ver <= pvPots: |
|
|
|
|
|
|
|
local_space = physical.mass > 0.0 |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
local_space = self._exporter().has_coordiface(bo) |
|
|
|
|
|
|
|
self._bounds_converters[bounds](bo, physical, local_space) |
|
|
|
else: |
|
|
|
else: |
|
|
|
simIface = so.sim.object |
|
|
|
simIface = so.sim.object |
|
|
|
physical = simIface.physical.object |
|
|
|
physical = simIface.physical.object |
|
|
|
if name is not None: |
|
|
|
|
|
|
|
physical.key.name = name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (simIface, physical) |
|
|
|
member_group = getattr(plSimDefs, kwargs.get("member_group", "kGroupLOSOnly")) |
|
|
|
|
|
|
|
if physical.memberGroup != member_group and member_group != plSimDefs.kGroupLOSOnly: |
|
|
|
|
|
|
|
self._report.warn("{}: Physical memberGroup overwritten!", bo.name, indent=2) |
|
|
|
|
|
|
|
physical.memberGroup = member_group |
|
|
|
|
|
|
|
|
|
|
|
def _export_box(self, bo, physical): |
|
|
|
self._apply_props(simIface, physical, kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _export_box(self, bo, physical, local_space): |
|
|
|
"""Exports box bounds based on the object""" |
|
|
|
"""Exports box bounds based on the object""" |
|
|
|
physical.boundsType = plSimDefs.kBoxBounds |
|
|
|
physical.boundsType = plSimDefs.kBoxBounds |
|
|
|
|
|
|
|
|
|
|
|
vertices = self._convert_mesh_data(bo, physical, indices=False) |
|
|
|
vertices = self._convert_mesh_data(bo, physical, local_space, indices=False) |
|
|
|
physical.calcBoxBounds(vertices) |
|
|
|
physical.calcBoxBounds(vertices) |
|
|
|
|
|
|
|
|
|
|
|
def _export_hull(self, bo, physical): |
|
|
|
def _export_hull(self, bo, physical, local_space): |
|
|
|
"""Exports convex hull bounds based on the object""" |
|
|
|
"""Exports convex hull bounds based on the object""" |
|
|
|
physical.boundsType = plSimDefs.kHullBounds |
|
|
|
physical.boundsType = plSimDefs.kHullBounds |
|
|
|
|
|
|
|
|
|
|
@ -212,20 +277,21 @@ class PhysicsConverter: |
|
|
|
bpy.ops.mesh.convex_hull(use_existing_faces=False) |
|
|
|
bpy.ops.mesh.convex_hull(use_existing_faces=False) |
|
|
|
bpy.ops.object.mode_set(mode="OBJECT") |
|
|
|
bpy.ops.object.mode_set(mode="OBJECT") |
|
|
|
|
|
|
|
|
|
|
|
physical.verts = self._convert_mesh_data(bo, physical, indices=False, mesh_func=_bake_hull) |
|
|
|
physical.verts = self._convert_mesh_data(bo, physical, local_space, indices=False, |
|
|
|
|
|
|
|
mesh_func=_bake_hull) |
|
|
|
|
|
|
|
|
|
|
|
def _export_sphere(self, bo, physical): |
|
|
|
def _export_sphere(self, bo, physical, local_space): |
|
|
|
"""Exports sphere bounds based on the object""" |
|
|
|
"""Exports sphere bounds based on the object""" |
|
|
|
physical.boundsType = plSimDefs.kSphereBounds |
|
|
|
physical.boundsType = plSimDefs.kSphereBounds |
|
|
|
|
|
|
|
|
|
|
|
vertices = self._convert_mesh_data(bo, physical, indices=False) |
|
|
|
vertices = self._convert_mesh_data(bo, physical, local_space, indices=False) |
|
|
|
physical.calcSphereBounds(vertices) |
|
|
|
physical.calcSphereBounds(vertices) |
|
|
|
|
|
|
|
|
|
|
|
def _export_trimesh(self, bo, physical): |
|
|
|
def _export_trimesh(self, bo, physical, local_space): |
|
|
|
"""Exports an object's mesh as exact physical bounds""" |
|
|
|
"""Exports an object's mesh as exact physical bounds""" |
|
|
|
physical.boundsType = plSimDefs.kExplicitBounds |
|
|
|
physical.boundsType = plSimDefs.kExplicitBounds |
|
|
|
|
|
|
|
|
|
|
|
vertices, indices = self._convert_mesh_data(bo, physical) |
|
|
|
vertices, indices = self._convert_mesh_data(bo, physical, local_space) |
|
|
|
physical.verts = vertices |
|
|
|
physical.verts = vertices |
|
|
|
physical.indices = indices |
|
|
|
physical.indices = indices |
|
|
|
|
|
|
|
|
|
|
@ -244,3 +310,7 @@ class PhysicsConverter: |
|
|
|
@property |
|
|
|
@property |
|
|
|
def _mgr(self): |
|
|
|
def _mgr(self): |
|
|
|
return self._exporter().mgr |
|
|
|
return self._exporter().mgr |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def _report(self): |
|
|
|
|
|
|
|
return self._exporter().report |
|
|
|