Browse Source

Implement the Random Sound modifier.

pull/251/head
Adam Johnson 4 years ago
parent
commit
0ac6be879e
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 1
      korman/exporter/convert.py
  2. 8
      korman/exporter/physics.py
  3. 22
      korman/properties/modifiers/physics.py
  4. 124
      korman/properties/modifiers/sound.py
  5. 1
      korman/ui/modifiers/physics.py
  6. 33
      korman/ui/modifiers/sound.py

1
korman/exporter/convert.py

@ -376,6 +376,7 @@ class Exporter:
for mod in bl_obj.plasma_modifiers.modifiers: for mod in bl_obj.plasma_modifiers.modifiers:
proc = getattr(mod, "post_export", None) proc = getattr(mod, "post_export", None)
if proc is not None: if proc is not None:
self.report.msg("Post processing '{}' modifier '{}'", bl_obj.name, mod.bl_label, indent=1)
proc(self, bl_obj, sceneobject) proc(self, bl_obj, sceneobject)
inc_progress() inc_progress()

8
korman/exporter/physics.py

@ -184,6 +184,14 @@ class PhysicsConverter:
_set_phys_prop(plSimulationInterface.kCameraAvoidObject, simIface, physical) _set_phys_prop(plSimulationInterface.kCameraAvoidObject, simIface, physical)
if mod.terrain: if mod.terrain:
physical.LOSDBs |= plSimDefs.kLOSDBAvatarWalkable physical.LOSDBs |= plSimDefs.kLOSDBAvatarWalkable
# Hacky? We'd like to share the simple surface descriptors(TM) as much as possible...
# This could result in a few orphaned PhysicalSndGroups, but I think that's preferable
# to having a bunch of empty objects...?
if mod.surface != "kNone":
sndgroup = self._mgr.find_create_object(plPhysicalSndGroup, so=so, name="SURFACEGEN_{}".format(mod.surface))
sndgroup.group = getattr(plPhysicalSndGroup, mod.surface)
physical.soundGroup = sndgroup.key
else: else:
group_name = kwargs.get("member_group") group_name = kwargs.get("member_group")
if group_name: if group_name:

22
korman/properties/modifiers/physics.py

@ -30,6 +30,22 @@ bounds_types = (
("trimesh", "Triangle Mesh", "Use the exact triangle mesh (SLOW!)") ("trimesh", "Triangle Mesh", "Use the exact triangle mesh (SLOW!)")
) )
# These are the collision sound surface types
surface_types = (
# Danger: do not reorder this one.
("kNone", "[None]", ""),
# Reorder away down here...
("kBone", "Bone", ""),
("kDirt", "Dirt", ""),
("kGrass", "Grass", ""),
("kMetal", "Metal", ""),
("kCone", "Plastic", ""),
("kRug", "Rug", ""),
("kStone", "Stone", ""),
("kWater", "Water", ""),
("kWood", "Wood", ""),
)
def bounds_type_index(key): def bounds_type_index(key):
return list(zip(*bounds_types))[0].index(key) return list(zip(*bounds_types))[0].index(key)
@ -68,6 +84,12 @@ class PlasmaCollider(PlasmaModifierProperties):
type=bpy.types.Object, type=bpy.types.Object,
poll=idprops.poll_mesh_objects) poll=idprops.poll_mesh_objects)
surface = EnumProperty(name="Surface Type",
description="Type of surface sound effect to play on collision",
items=surface_types,
default="kNone",
options=set())
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
# All modifier properties are examined by this little stinker... # All modifier properties are examined by this little stinker...
exporter.physics.generate_physical(bo, so) exporter.physics.generate_physical(bo, so)

124
korman/properties/modifiers/sound.py

@ -22,9 +22,133 @@ from PyHSPlasma import *
from ... import korlib from ... import korlib
from .base import PlasmaModifierProperties from .base import PlasmaModifierProperties
from .physics import surface_types
from ...exporter import ExportError from ...exporter import ExportError
from ... import idprops from ... import idprops
_randomsound_modes = {
"normal": plRandomSoundMod.kNormal,
"norepeat": plRandomSoundMod.kNoRepeats,
"coverall": plRandomSoundMod.kCoverall | plRandomSoundMod.kNoRepeats,
"sequential": plRandomSoundMod.kSequential
}
class PlasmaRandomSound(PlasmaModifierProperties):
pl_id = "random_sound"
pl_depends = {"soundemit"}
bl_category = "Logic"
bl_label = "Random Sound"
bl_description = ""
mode = EnumProperty(name="Mode",
description="Playback Type",
items=[("random", "Random Time", "Plays a random sound from the emitter at a random time"),
("collision", "Collision Surface", "Plays a random sound when the object's parent collides")],
default="random",
options=set())
# Physical (read: collision) sounds
play_on = EnumProperty(name="Play On",
description="Play sounds on this collision event",
items=[("slide", "Slide", "Plays a random sound on object slide"),
("impact", "Impact", "Plays a random sound on object slide")],
options=set())
surfaces = EnumProperty(name="Play Against",
description="Sounds are played on collision against these surfaces",
items=surface_types[1:],
options={"ENUM_FLAG"})
# Timed random sounds
auto_start = BoolProperty(name="Auto Start",
description="Start playing when the Age loads",
default=True,
options=set())
play_mode = EnumProperty(name="Play Mode",
description="",
items=[("normal", "Any", "Plays any attached sound"),
("norepeat", "No Repeats", "Do not replay a sound immediately after itself"),
("coverall", "Full Set", "Once a sound is played, do not replay it until after all sounds are played"),
("sequential", "Sequential", "Play sounds in the order they appear in the emitter")],
default="norepeat",
options=set())
stop_after_set = BoolProperty(name="Stop After Set",
description="Stop playing after all sounds are played",
default=False,
options=set())
stop_after_play = BoolProperty(name="Stop After Play",
description="Stop playing after one sound is played",
default=False,
options=set())
min_delay = FloatProperty(name="Min Delay",
description="Minimum delay length",
min=0.0,
subtype="TIME", unit="TIME",
options=set())
max_delay = FloatProperty(name="Max Delay",
description="Maximum delay length",
min=0.0,
subtype="TIME", unit="TIME",
options=set())
def export(self, exporter, bo, so):
rndmod = exporter.mgr.find_create_object(plRandomSoundMod, bl=bo, so=so)
if self.mode == "random":
if not self.auto_start:
rndmod.state = plRandomSoundMod.kStopped
if self.stop_after_play:
rndmod.mode |= plRandomSoundMod.kOneCmd
else:
rndmod.minDelay = min(self.min_delay, self.max_delay)
rndmod.maxDelay = max(self.min_delay, self.max_delay)
# Delaying from the start makes ZERO sense. Screw that.
rndmod.mode |= plRandomSoundMod.kDelayFromEnd
rndmod.mode |= _randomsound_modes[self.play_mode]
if self.stop_after_set:
rndmod.mode |= plRandomSoundMod.kOneCycle
elif self.mode == "collision":
rndmod.mode = plRandomSoundMod.kNoRepeats | plRandomSoundMod.kOneCmd
rndmod.state = plRandomSoundMod.kStopped
else:
raise RuntimeError()
def post_export(self, exporter, bo, so):
if self.mode == "collision":
parent_bo = bo.parent
if parent_bo is None:
raise ExportError("[{}]: Collision sound objects MUST be parented directly to the collider object.", bo.name)
phys = exporter.mgr.find_object(plGenericPhysical, bl=parent_bo)
if phys is None:
raise ExportError("[{}]: Collision sound objects MUST be parented directly to the collider object.", bo.name)
# The soundGroup on the physical may or may not be the generic "this is my surface type"
# soundGroup with no actual sounds attached. So, we need to lookup the actual one.
sndgroup = exporter.mgr.find_create_object(plPhysicalSndGroup, bl=parent_bo)
sndgroup.group = getattr(plPhysicalSndGroup, parent_bo.plasma_modifiers.collision.surface)
phys.soundGroup = sndgroup.key
rndmod = exporter.mgr.find_key(plRandomSoundMod, bl=bo, so=so)
if self.play_on == "slide":
groupattr = "slideSounds"
elif self.play_on == "impact":
groupattr = "impactSounds"
else:
raise RuntimeError()
sounds = { i: sound for i, sound in enumerate(getattr(sndgroup, groupattr)) }
for surface_name in self.surfaces:
surface_id = getattr(plPhysicalSndGroup, surface_name)
if surface_id in sounds:
exporter.report.warn("Overwriting physical {} surface '{}' ID:{}",
groupattr, surface_name, surface_id, indent=2)
else:
exporter.report.msg("Got physical {} surface '{}' ID:{}",
groupattr, surface_name, surface_id, indent=2)
sounds[surface_id] = rndmod
# Keeps the LUT (or should that be lookup vector?) as small as possible
setattr(sndgroup, groupattr, [sounds.get(i) for i in range(max(sounds.keys()) + 1)])
class PlasmaSfxFade(bpy.types.PropertyGroup): class PlasmaSfxFade(bpy.types.PropertyGroup):
fade_type = EnumProperty(name="Type", fade_type = EnumProperty(name="Type",
description="Fade Type", description="Fade Type",

1
korman/ui/modifiers/physics.py

@ -15,6 +15,7 @@
def collision(modifier, layout, context): def collision(modifier, layout, context):
layout.prop(modifier, "bounds") layout.prop(modifier, "bounds")
layout.prop(modifier, "surface")
layout.separator() layout.separator()
split = layout.split() split = layout.split()

33
korman/ui/modifiers/sound.py

@ -17,6 +17,39 @@ import bpy
from .. import ui_list from .. import ui_list
def random_sound(modifier, layout, context):
parent_bo = modifier.id_data.parent
collision_bad = (modifier.mode == "collision" and (parent_bo is None or
not parent_bo.plasma_modifiers.collision.enabled))
layout.alert = collision_bad
layout.prop(modifier, "mode")
if collision_bad:
layout.label(icon="ERROR", text="Sound emitter must be parented to a collider.")
layout.alert = False
layout.separator()
if modifier.mode == "random":
split = layout.split()
col = split.column()
col.prop(modifier, "play_mode", text="")
col.prop(modifier, "auto_start")
col = col.column()
col.active = not modifier.stop_after_play
col.prop(modifier, "stop_after_set")
col = split.column()
col.prop(modifier, "stop_after_play")
col = col.column(align=True)
col.active = not modifier.stop_after_play
col.alert = modifier.min_delay > modifier.max_delay
col.prop(modifier, "min_delay")
col.prop(modifier, "max_delay")
elif modifier.mode == "collision":
layout.prop(modifier, "play_on")
# Ugh, Blender...
layout.alert = len(modifier.surfaces) == 0
layout.prop_menu_enum(modifier, "surfaces")
def _draw_fade_ui(modifier, layout, label): def _draw_fade_ui(modifier, layout, label):
layout.label(label) layout.label(label)
layout.prop(modifier, "fade_type", text="") layout.prop(modifier, "fade_type", text="")

Loading…
Cancel
Save