Browse Source

Convert sound modifiers to newfangled ID Props

Unfortunately, sound indices do not match up directly with sound ID
blocks, therefore, those remain string properties.
pull/56/head
Adam Johnson 8 years ago
parent
commit
1b3afbe8d4
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 36
      korman/nodes/node_messages.py
  2. 5
      korman/operators/op_sound.py
  3. 104
      korman/properties/modifiers/sound.py
  4. 14
      korman/ui/modifiers/sound.py

36
korman/nodes/node_messages.py

@ -21,6 +21,7 @@ from PyHSPlasma import *
from .node_core import *
from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids
from ..exporter import ExportError
from .. import idprops
class PlasmaMessageSocketBase(PlasmaNodeSocketBase):
bl_color = (0.004, 0.282, 0.349, 1.0)
@ -484,14 +485,19 @@ class PlasmaSceneObjectMsgRcvrNode(PlasmaNodeBase, bpy.types.Node):
return ref_so_key
class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
class PlasmaSoundMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.types.Node):
bl_category = "MSG"
bl_idname = "PlasmaSoundMsgNode"
bl_label = "Sound"
bl_width_default = 190
object_name = StringProperty(name="Object",
description="Sound emitter object")
def _poll_sound_emitters(self, value):
return value.plasma_modifiers.soundemit.enabled
emitter_object = PointerProperty(name="Object",
description="Sound emitter object",
type=bpy.types.Object,
poll=_poll_sound_emitters)
sound_name = StringProperty(name="Sound",
description="Sound datablock")
@ -540,20 +546,19 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
msg.setCmd(plSoundMsg.kAddCallbacks)
def convert_message(self, exporter, so):
sound_bo = bpy.data.objects.get(self.object_name, None)
if sound_bo is None:
self.raise_error("'{}' is not a valid object".format(self.object_name))
soundemit = sound_bo.plasma_modifiers.soundemit
if self.emitter_object is None:
self.raise_error("Sound emitter must be set")
soundemit = self.emitter_object.plasma_modifiers.soundemit
if not soundemit.enabled:
self.raise_error("'{}' is not a valid Sound Emitter".format(self.object_name))
self.raise_error("'{}' is not a valid Sound Emitter".format(self.emitter_object.name))
# Always test the specified audible for validity
if self.sound_name and soundemit.sounds.get(self.sound_name, None) is None:
self.raise_error("Invalid Sound '{}' requested from Sound Emitter '{}'".format(self.sound_name, self.object_name))
self.raise_error("Invalid Sound '{}' requested from Sound Emitter '{}'".format(self.sound_name, self.emitter_object.name))
# Remember that 3D stereo sounds are exported as two emitters...
# But, if we only have one sound attached, who cares, we can just address the message to all
audible_key = exporter.mgr.find_create_key(plAudioInterface, bl=sound_bo)
audible_key = exporter.mgr.find_create_key(plAudioInterface, bl=self.emitter_object)
indices = (-1,) if not self.sound_name or len(soundemit.sounds) == 1 else soundemit.get_sound_indices(self.sound_name)
for idx in indices:
msg = plSoundMsg()
@ -586,10 +591,9 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
yield msg
def draw_buttons(self, context, layout):
layout.prop_search(self, "object_name", bpy.data, "objects")
bo = bpy.data.objects.get(self.object_name, None)
if bo is not None:
soundemit = bo.plasma_modifiers.soundemit
layout.prop(self, "emitter_object")
if self.emitter_object is not None:
soundemit = self.emitter_object.plasma_modifiers.soundemit
if soundemit.enabled:
layout.prop_search(self, "sound_name", soundemit, "sounds", icon="SOUND")
else:
@ -608,6 +612,10 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
def has_callbacks(self):
return True
@classmethod
def _idprop_mapping(cls):
return {"emitter_object": "object_name"}
class PlasmaTimerCallbackMsgNode(PlasmaMessageNode, bpy.types.Node):
bl_category = "MSG"

5
korman/operators/op_sound.py

@ -36,19 +36,16 @@ class PlasmaSoundOpenOperator(SoundOperator, bpy.types.Operator):
def execute(self, context):
# Check to see if the sound exists... Because the sneakily introduced bpy.data.sounds.load
# check_existing doesn't tell us if it already exists... dammit...
# We don't want to take ownership forcefully if we don't have to.
for i in bpy.data.sounds:
if self.filepath == i.filepath:
sound = i
break
else:
sound = bpy.data.sounds.load(self.filepath)
sound.plasma_owned = True
sound.use_fake_user = True
# Now do the stanky leg^H^H^H^H^H^H^H^H^H^H deed and put the sound on the mod
dest = eval(self.data_path)
setattr(dest, self.sound_property, sound.name)
setattr(dest, self.sound_property, sound)
return {"FINISHED"}
def invoke(self, context, event):

104
korman/properties/modifiers/sound.py

@ -23,6 +23,7 @@ from PyHSPlasma import *
from ... import korlib
from .base import PlasmaModifierProperties
from ...exporter import ExportError
from ... import idprops
class PlasmaSfxFade(bpy.types.PropertyGroup):
fade_type = EnumProperty(name="Type",
@ -38,9 +39,17 @@ class PlasmaSfxFade(bpy.types.PropertyGroup):
options=set(), subtype="TIME", unit="TIME")
class PlasmaSound(bpy.types.PropertyGroup):
def _sound_picked(self, context):
if not self.sound_data:
class PlasmaSound(idprops.IDPropMixin, bpy.types.PropertyGroup):
def _get_name_proxy(self):
if self.sound is not None:
return self.sound.name
return ""
def _set_name_proxy(self, value):
self.sound = bpy.data.sounds.get(value, None)
# This is the actual pointer update callback
if not self.sound:
self.name = "[Empty]"
return
@ -54,24 +63,33 @@ class PlasmaSound(bpy.types.PropertyGroup):
else:
self.is_valid = True
self.is_stereo = header.numChannels == 2
self._update_name(context)
self._update_name()
def _update_name(self, context):
def _update_name(self, context=None):
if self.is_stereo and self.channel != {"L", "R"}:
self.name = "{}:{}".format(self.sound_data, "L" if "L" in self.channel else "R")
self.name = "{}:{}".format(self._sound_name, "L" if "L" in self.channel else "R")
else:
self.name = self.sound_data
self.name = self._sound_name
enabled = BoolProperty(name="Enabled", default=True, options=set())
sound_data = StringProperty(name="Sound", description="Sound Datablock",
options=set(), update=_sound_picked)
sound = PointerProperty(name="Sound",
description="Sound Datablock",
type=bpy.types.Sound)
# This is needed because pointer properties do not seem to allow update CBs... Bug?
sound_data_proxy = StringProperty(name="Sound",
description="Name of sound datablock",
get=_get_name_proxy,
set=_set_name_proxy,
options=set())
is_stereo = BoolProperty(default=True, options={"HIDDEN"})
is_valid = BoolProperty(default=False, options={"HIDDEN"})
soft_region = StringProperty(name="Soft Volume",
sfx_region = PointerProperty(name="Soft Volume",
description="Soft region this sound can be heard in",
options=set())
type=bpy.types.Object,
poll=idprops.poll_softvolume_objects)
sfx_type = EnumProperty(name="Category",
description="Describes the purpose of this sound",
@ -170,9 +188,9 @@ class PlasmaSound(bpy.types.PropertyGroup):
def _convert_sound(self, exporter, so, pClass, wavHeader, dataSize, channel=None):
if channel is None:
name = "Sfx-{}_{}".format(so.key.name, self.sound_data)
name = "Sfx-{}_{}".format(so.key.name, self._sound_name)
else:
name = "Sfx-{}_{}:{}".format(so.key.name, self.sound_data, channel)
name = "Sfx-{}_{}:{}".format(so.key.name, self._sound_name, channel)
exporter.report.msg("[{}] {}", pClass.__name__[2:], name, indent=1)
sound = exporter.mgr.find_create_object(pClass, so=so, name=name)
@ -181,13 +199,10 @@ class PlasmaSound(bpy.types.PropertyGroup):
sv_mod, sv_key = self.id_data.plasma_modifiers.softvolume, None
if sv_mod.enabled:
sv_key = sv_mod.get_key(exporter, so)
elif self.soft_region:
sv_bo = bpy.data.objects.get(self.soft_region, None)
if sv_bo is None:
raise ExportError("'{}': Invalid object '{}' for SoundEmit '{}' soft volume".format(self.id_data.name, self.soft_region, self.sound_data))
sv_mod = sv_bo.plasma_modifiers.softvolume
elif self.sfx_region:
sv_mod = self.sfx_region.plasma_modifiers.softvolume
if not sv_mod.enabled:
raise ExportError("'{}': SoundEmit '{}', '{}' is not a SoftVolume".format(self.id_data.name, self.sound_data, self.soft_region))
raise ExportError("'{}': SoundEmit '{}', '{}' is not a SoftVolume".format(self.id_data.name, self._sound_name, self.sfx_region.name))
sv_key = sv_mod.get_key(exporter)
if sv_key is not None:
sv_key.object.listenState |= plSoftVolume.kListenCheck | plSoftVolume.kListenDirty | plSoftVolume.kListenRegistered
@ -305,21 +320,36 @@ class PlasmaSound(bpy.types.PropertyGroup):
key = sound.key
return key
@classmethod
def _idprop_mapping(cls):
return {"sound": "sound_data",
"sfx_region": "soft_region"}
def _idprop_sources(self):
return {"sound_data": bpy.data.sounds,
"soft_region": bpy.data.objects}
@property
def is_3d_stereo(self):
return self.sfx_type == "kSoundFX" and self.channel == {"L", "R"} and self.is_stereo
def _raise_error(self, msg):
raise ExportError("SoundEmitter '{}': Sound '{}' {}".format(self.id_data.name, self.sound_data, msg))
if self.sound:
raise ExportError("SoundEmitter '{}': Sound '{}' {}".format(self.id_data.name, self.sound.name, msg))
else:
raise ExportError("SoundEmitter '{}': {}".format(self.id_data.name, msg))
@property
def _sound(self):
try:
sound = bpy.data.sounds.get(self.sound_data)
except:
self._raise_error("is not loaded")
else:
return sound
if not self.sound:
self._raise_error("has an invalid sound specified")
return self.sound
@property
def _sound_name(self):
if self.sound:
return self.sound.name
return ""
class PlasmaSoundEmitter(PlasmaModifierProperties):
@ -341,7 +371,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
# Pass this off to each individual sound for conversion
for i in self.sounds:
if i.sound_data and i.enabled:
if i.enabled:
i.convert_sound(exporter, so, winaud)
def get_sound_indices(self, name=None, sound=None):
@ -374,26 +404,6 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
else:
raise ValueError(name)
@classmethod
def register(cls):
bpy.types.Sound.plasma_owned = BoolProperty(default=False, options={"HIDDEN"})
@property
def requires_actor(self):
return True
@persistent
def _toss_orphaned_sounds(scene):
used_sounds = set()
for i in bpy.data.objects:
soundemit = i.plasma_modifiers.soundemit
used_sounds.update((j.sound_data for j in soundemit.sounds))
dead_sounds = [i for i in bpy.data.sounds if i.plasma_owned and i.name not in used_sounds]
for i in dead_sounds:
i.use_fake_user = False
i.user_clear()
bpy.data.sounds.remove(i)
# collects orphaned Plasma owned sound datablocks
bpy.app.handlers.save_pre.append(_toss_orphaned_sounds)

14
korman/ui/modifiers/sound.py

@ -22,10 +22,8 @@ def _draw_fade_ui(modifier, layout, label):
class SoundListUI(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0):
if item.sound_data:
sound = bpy.data.sounds.get(item.sound_data)
icon = "SOUND" if sound is not None else "ERROR"
layout.prop(item, "name", emboss=False, icon=icon, text="")
if item.sound:
layout.prop(item, "name", emboss=False, icon="SOUND", text="")
layout.prop(item, "enabled", text="")
else:
layout.label("[Empty]")
@ -51,13 +49,13 @@ def soundemit(modifier, layout, context):
else:
# Sound datablock picker
row = layout.row(align=True)
row.prop_search(sound, "sound_data", bpy.data, "sounds", text="")
row.prop_search(sound, "sound_data_proxy", bpy.data, "sounds", text="")
open_op = row.operator("sound.plasma_open", icon="FILESEL", text="")
open_op.data_path = repr(sound)
open_op.sound_property = "sound_data"
# Pack/Unpack
data = bpy.data.sounds.get(sound.sound_data)
data = sound.sound
if data is not None:
if data.packed_file is None:
row.operator("sound.plasma_pack", icon="UGLYPACKAGE", text="")
@ -65,7 +63,7 @@ def soundemit(modifier, layout, context):
row.operator_menu_enum("sound.plasma_unpack", "method", icon="PACKAGE", text="")
# If an invalid sound data block is spec'd, let them know about it.
if sound.sound_data and not sound.is_valid:
if data and not sound.is_valid:
layout.label(text="Invalid sound specified", icon="ERROR")
# Core Props
@ -102,4 +100,4 @@ def soundemit(modifier, layout, context):
if not sv.enabled:
col.separator()
col.label("Soft Region:")
col.prop_search(sound, "soft_region", bpy.data, "objects", text="")
col.prop(sound, "sfx_region", text="")

Loading…
Cancel
Save