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 .node_core import *
from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids from ..properties.modifiers.region import footstep_surfaces, footstep_surface_ids
from ..exporter import ExportError from ..exporter import ExportError
from .. import idprops
class PlasmaMessageSocketBase(PlasmaNodeSocketBase): class PlasmaMessageSocketBase(PlasmaNodeSocketBase):
bl_color = (0.004, 0.282, 0.349, 1.0) bl_color = (0.004, 0.282, 0.349, 1.0)
@ -484,14 +485,19 @@ class PlasmaSceneObjectMsgRcvrNode(PlasmaNodeBase, bpy.types.Node):
return ref_so_key return ref_so_key
class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node): class PlasmaSoundMsgNode(idprops.IDPropObjectMixin, PlasmaMessageNode, bpy.types.Node):
bl_category = "MSG" bl_category = "MSG"
bl_idname = "PlasmaSoundMsgNode" bl_idname = "PlasmaSoundMsgNode"
bl_label = "Sound" bl_label = "Sound"
bl_width_default = 190 bl_width_default = 190
object_name = StringProperty(name="Object", def _poll_sound_emitters(self, value):
description="Sound emitter object") 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", sound_name = StringProperty(name="Sound",
description="Sound datablock") description="Sound datablock")
@ -540,20 +546,19 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
msg.setCmd(plSoundMsg.kAddCallbacks) msg.setCmd(plSoundMsg.kAddCallbacks)
def convert_message(self, exporter, so): def convert_message(self, exporter, so):
sound_bo = bpy.data.objects.get(self.object_name, None) if self.emitter_object is None:
if sound_bo is None: self.raise_error("Sound emitter must be set")
self.raise_error("'{}' is not a valid object".format(self.object_name)) soundemit = self.emitter_object.plasma_modifiers.soundemit
soundemit = sound_bo.plasma_modifiers.soundemit
if not soundemit.enabled: 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 # Always test the specified audible for validity
if self.sound_name and soundemit.sounds.get(self.sound_name, None) is None: 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... # 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 # 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) indices = (-1,) if not self.sound_name or len(soundemit.sounds) == 1 else soundemit.get_sound_indices(self.sound_name)
for idx in indices: for idx in indices:
msg = plSoundMsg() msg = plSoundMsg()
@ -586,10 +591,9 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
yield msg yield msg
def draw_buttons(self, context, layout): def draw_buttons(self, context, layout):
layout.prop_search(self, "object_name", bpy.data, "objects") layout.prop(self, "emitter_object")
bo = bpy.data.objects.get(self.object_name, None) if self.emitter_object is not None:
if bo is not None: soundemit = self.emitter_object.plasma_modifiers.soundemit
soundemit = bo.plasma_modifiers.soundemit
if soundemit.enabled: if soundemit.enabled:
layout.prop_search(self, "sound_name", soundemit, "sounds", icon="SOUND") layout.prop_search(self, "sound_name", soundemit, "sounds", icon="SOUND")
else: else:
@ -608,6 +612,10 @@ class PlasmaSoundMsgNode(PlasmaMessageNode, bpy.types.Node):
def has_callbacks(self): def has_callbacks(self):
return True return True
@classmethod
def _idprop_mapping(cls):
return {"emitter_object": "object_name"}
class PlasmaTimerCallbackMsgNode(PlasmaMessageNode, bpy.types.Node): class PlasmaTimerCallbackMsgNode(PlasmaMessageNode, bpy.types.Node):
bl_category = "MSG" bl_category = "MSG"

5
korman/operators/op_sound.py

@ -36,19 +36,16 @@ class PlasmaSoundOpenOperator(SoundOperator, bpy.types.Operator):
def execute(self, context): def execute(self, context):
# Check to see if the sound exists... Because the sneakily introduced bpy.data.sounds.load # 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... # 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: for i in bpy.data.sounds:
if self.filepath == i.filepath: if self.filepath == i.filepath:
sound = i sound = i
break break
else: else:
sound = bpy.data.sounds.load(self.filepath) 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 # 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) dest = eval(self.data_path)
setattr(dest, self.sound_property, sound.name) setattr(dest, self.sound_property, sound)
return {"FINISHED"} return {"FINISHED"}
def invoke(self, context, event): def invoke(self, context, event):

104
korman/properties/modifiers/sound.py

@ -23,6 +23,7 @@ from PyHSPlasma import *
from ... import korlib from ... import korlib
from .base import PlasmaModifierProperties from .base import PlasmaModifierProperties
from ...exporter import ExportError from ...exporter import ExportError
from ... import idprops
class PlasmaSfxFade(bpy.types.PropertyGroup): class PlasmaSfxFade(bpy.types.PropertyGroup):
fade_type = EnumProperty(name="Type", fade_type = EnumProperty(name="Type",
@ -38,9 +39,17 @@ class PlasmaSfxFade(bpy.types.PropertyGroup):
options=set(), subtype="TIME", unit="TIME") options=set(), subtype="TIME", unit="TIME")
class PlasmaSound(bpy.types.PropertyGroup): class PlasmaSound(idprops.IDPropMixin, bpy.types.PropertyGroup):
def _sound_picked(self, context): def _get_name_proxy(self):
if not self.sound_data: 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]" self.name = "[Empty]"
return return
@ -54,24 +63,33 @@ class PlasmaSound(bpy.types.PropertyGroup):
else: else:
self.is_valid = True self.is_valid = True
self.is_stereo = header.numChannels == 2 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"}: 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: else:
self.name = self.sound_data self.name = self._sound_name
enabled = BoolProperty(name="Enabled", default=True, options=set()) enabled = BoolProperty(name="Enabled", default=True, options=set())
sound_data = StringProperty(name="Sound", description="Sound Datablock", sound = PointerProperty(name="Sound",
options=set(), update=_sound_picked) 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_stereo = BoolProperty(default=True, options={"HIDDEN"})
is_valid = BoolProperty(default=False, 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", 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", sfx_type = EnumProperty(name="Category",
description="Describes the purpose of this sound", 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): def _convert_sound(self, exporter, so, pClass, wavHeader, dataSize, channel=None):
if channel is None: if channel is None:
name = "Sfx-{}_{}".format(so.key.name, self.sound_data) name = "Sfx-{}_{}".format(so.key.name, self._sound_name)
else: 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) exporter.report.msg("[{}] {}", pClass.__name__[2:], name, indent=1)
sound = exporter.mgr.find_create_object(pClass, so=so, name=name) 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 sv_mod, sv_key = self.id_data.plasma_modifiers.softvolume, None
if sv_mod.enabled: if sv_mod.enabled:
sv_key = sv_mod.get_key(exporter, so) sv_key = sv_mod.get_key(exporter, so)
elif self.soft_region: elif self.sfx_region:
sv_bo = bpy.data.objects.get(self.soft_region, None) sv_mod = self.sfx_region.plasma_modifiers.softvolume
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
if not sv_mod.enabled: 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) sv_key = sv_mod.get_key(exporter)
if sv_key is not None: if sv_key is not None:
sv_key.object.listenState |= plSoftVolume.kListenCheck | plSoftVolume.kListenDirty | plSoftVolume.kListenRegistered sv_key.object.listenState |= plSoftVolume.kListenCheck | plSoftVolume.kListenDirty | plSoftVolume.kListenRegistered
@ -305,21 +320,36 @@ class PlasmaSound(bpy.types.PropertyGroup):
key = sound.key key = sound.key
return 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 @property
def is_3d_stereo(self): def is_3d_stereo(self):
return self.sfx_type == "kSoundFX" and self.channel == {"L", "R"} and self.is_stereo return self.sfx_type == "kSoundFX" and self.channel == {"L", "R"} and self.is_stereo
def _raise_error(self, msg): 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 @property
def _sound(self): def _sound(self):
try: if not self.sound:
sound = bpy.data.sounds.get(self.sound_data) self._raise_error("has an invalid sound specified")
except: return self.sound
self._raise_error("is not loaded")
else: @property
return sound def _sound_name(self):
if self.sound:
return self.sound.name
return ""
class PlasmaSoundEmitter(PlasmaModifierProperties): class PlasmaSoundEmitter(PlasmaModifierProperties):
@ -341,7 +371,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
# Pass this off to each individual sound for conversion # Pass this off to each individual sound for conversion
for i in self.sounds: for i in self.sounds:
if i.sound_data and i.enabled: if i.enabled:
i.convert_sound(exporter, so, winaud) i.convert_sound(exporter, so, winaud)
def get_sound_indices(self, name=None, sound=None): def get_sound_indices(self, name=None, sound=None):
@ -374,26 +404,6 @@ class PlasmaSoundEmitter(PlasmaModifierProperties):
else: else:
raise ValueError(name) raise ValueError(name)
@classmethod
def register(cls):
bpy.types.Sound.plasma_owned = BoolProperty(default=False, options={"HIDDEN"})
@property @property
def requires_actor(self): def requires_actor(self):
return True 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): class SoundListUI(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0):
if item.sound_data: if item.sound:
sound = bpy.data.sounds.get(item.sound_data) layout.prop(item, "name", emboss=False, icon="SOUND", text="")
icon = "SOUND" if sound is not None else "ERROR"
layout.prop(item, "name", emboss=False, icon=icon, text="")
layout.prop(item, "enabled", text="") layout.prop(item, "enabled", text="")
else: else:
layout.label("[Empty]") layout.label("[Empty]")
@ -51,13 +49,13 @@ def soundemit(modifier, layout, context):
else: else:
# Sound datablock picker # Sound datablock picker
row = layout.row(align=True) 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 = row.operator("sound.plasma_open", icon="FILESEL", text="")
open_op.data_path = repr(sound) open_op.data_path = repr(sound)
open_op.sound_property = "sound_data" open_op.sound_property = "sound_data"
# Pack/Unpack # Pack/Unpack
data = bpy.data.sounds.get(sound.sound_data) data = sound.sound
if data is not None: if data is not None:
if data.packed_file is None: if data.packed_file is None:
row.operator("sound.plasma_pack", icon="UGLYPACKAGE", text="") 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="") 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 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") layout.label(text="Invalid sound specified", icon="ERROR")
# Core Props # Core Props
@ -102,4 +100,4 @@ def soundemit(modifier, layout, context):
if not sv.enabled: if not sv.enabled:
col.separator() col.separator()
col.label("Soft Region:") col.label("Soft Region:")
col.prop_search(sound, "soft_region", bpy.data, "objects", text="") col.prop(sound, "sfx_region", text="")

Loading…
Cancel
Save