mirror of https://github.com/H-uru/korman.git
Browse Source
So this is a fairly massive chunk. I tried to get our Plasma Modifiers to match the Blender Modifier UI fairly well. In the process, I discovered that Blender "helpfully" hides the modifier button on Empty objects. Bah. Significant Changes: - Hid all of the Blender Physics mess - The Physics context is now the Plasma Object context - Moved Plasma Object and Plasma Synchronization to the Context Formerly Known as Physics - Added a Plasma Modifier Panel Here's how you create Plasma Modifiers: - Add your PropertyGroup to properties.modifiers - Make sure you have a pl_id naming your modifier, a category, and a label - Implement the export() method to actually export something useful (not goat porn, please) - Implement your UI draw function in ui.modifiers. - Wasn't that easy?pull/6/head
Adam Johnson
11 years ago
14 changed files with 460 additions and 7 deletions
@ -0,0 +1,125 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
from bpy.props import * |
||||||
|
|
||||||
|
from ..properties import modifiers |
||||||
|
|
||||||
|
def _fetch_modifiers(): |
||||||
|
items = [] |
||||||
|
|
||||||
|
mapping = modifiers.modifier_mapping() |
||||||
|
for i in mapping.keys(): |
||||||
|
items.append(("", i, "")) |
||||||
|
items.extend(mapping[i]) |
||||||
|
#yield ("", i, "") |
||||||
|
#yield mapping[i] |
||||||
|
return items |
||||||
|
|
||||||
|
class ModifierOperator: |
||||||
|
@classmethod |
||||||
|
def poll(cls, context): |
||||||
|
return context.scene.render.engine == "PLASMA_GAME" |
||||||
|
|
||||||
|
|
||||||
|
class ModifierAddOperator(ModifierOperator, bpy.types.Operator): |
||||||
|
bl_idname = "object.plasma_modifier_add" |
||||||
|
bl_label = "Add Modifier" |
||||||
|
bl_description = "Adds a Plasma Modifier" |
||||||
|
|
||||||
|
types = EnumProperty(name="Modifier Type", |
||||||
|
description="The type of modifier we add to the list", |
||||||
|
items=_fetch_modifiers()) |
||||||
|
|
||||||
|
def execute(self, context): |
||||||
|
plmods = context.object.plasma_modifiers |
||||||
|
myType = self.types |
||||||
|
theMod = getattr(plmods, myType) |
||||||
|
|
||||||
|
theMod.display_order = plmods.determine_next_id() |
||||||
|
theMod.created(context.object) |
||||||
|
return {"FINISHED"} |
||||||
|
|
||||||
|
|
||||||
|
class ModifierRemoveOperator(ModifierOperator, bpy.types.Operator): |
||||||
|
bl_idname = "object.plasma_modifier_remove" |
||||||
|
bl_label = "Remove Modifier" |
||||||
|
bl_description = "Removes this Plasma Modifier" |
||||||
|
|
||||||
|
active_modifier = IntProperty(name="Modifier Display Order", |
||||||
|
default=-1, |
||||||
|
options={"HIDDEN"}) |
||||||
|
|
||||||
|
def execute(self, context): |
||||||
|
assert self.active_modifier >= 0 |
||||||
|
|
||||||
|
for mod in context.object.plasma_modifiers.modifiers: |
||||||
|
if mod.display_order == self.active_modifier: |
||||||
|
mod.display_order = -1 |
||||||
|
mod.destroyed() |
||||||
|
elif mod.display_order > self.active_modifier: |
||||||
|
mod.display_order -= 1 |
||||||
|
return {"FINISHED"} |
||||||
|
|
||||||
|
|
||||||
|
class ModifierMoveOperator(ModifierOperator): |
||||||
|
def swap_modifier_ids(self, mods, s1, s2): |
||||||
|
done = 0 |
||||||
|
for mod in mods.modifiers: |
||||||
|
if mod.display_order == s1: |
||||||
|
mod.display_order = s2 |
||||||
|
done += 1 |
||||||
|
elif mod.display_order == s2: |
||||||
|
mod.display_order = s1 |
||||||
|
done += 1 |
||||||
|
if done == 2: |
||||||
|
break |
||||||
|
|
||||||
|
|
||||||
|
class ModifierMoveUpOperator(ModifierMoveOperator, bpy.types.Operator): |
||||||
|
bl_idname = "object.plasma_modifier_move_up" |
||||||
|
bl_label = "Move Up" |
||||||
|
bl_description = "Move the modifier up in the stack" |
||||||
|
|
||||||
|
active_modifier = IntProperty(name="Modifier Display Order", |
||||||
|
default=-1, |
||||||
|
options={"HIDDEN"}) |
||||||
|
|
||||||
|
def execute(self, context): |
||||||
|
assert self.active_modifier >= 0 |
||||||
|
if self.active_modifier > 0: |
||||||
|
plmods = context.object.plasma_modifiers |
||||||
|
self.swap_modifier_ids(plmods, self.active_modifier, self.active_modifier-1) |
||||||
|
return {"FINISHED"} |
||||||
|
|
||||||
|
|
||||||
|
class ModifierMoveDownOperator(ModifierMoveOperator, bpy.types.Operator): |
||||||
|
bl_idname = "object.plasma_modifier_move_down" |
||||||
|
bl_label = "Move Down" |
||||||
|
bl_description = "Move the modifier down in the stack" |
||||||
|
|
||||||
|
active_modifier = IntProperty(name="Modifier Display Order", |
||||||
|
default=-1, |
||||||
|
options={"HIDDEN"}) |
||||||
|
|
||||||
|
def execute(self, context): |
||||||
|
assert self.active_modifier >= 0 |
||||||
|
|
||||||
|
plmods = context.object.plasma_modifiers |
||||||
|
last = max([mod.display_order for mod in plmods.modifiers]) |
||||||
|
if self.active_modifier < last: |
||||||
|
self.swap_modifier_ids(plmods, self.active_modifier, self.active_modifier+1) |
||||||
|
return {"FINISHED"} |
@ -0,0 +1,90 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
import inspect |
||||||
|
|
||||||
|
from .base import PlasmaModifierProperties |
||||||
|
from .logic import * |
||||||
|
|
||||||
|
class PlasmaModifiers(bpy.types.PropertyGroup): |
||||||
|
def determine_next_id(self): |
||||||
|
"""Gets the ID for the next modifier in the UI""" |
||||||
|
# This is NOT a property, otherwise the modifiers property would access this... |
||||||
|
# Which acesses the modifiers property... INFINITE RECURSION! :D |
||||||
|
ids = [mod.display_order for mod in self.modifiers] |
||||||
|
if ids: |
||||||
|
return max(ids) + 1 |
||||||
|
else: |
||||||
|
return 0 |
||||||
|
|
||||||
|
@property |
||||||
|
def modifiers(self): |
||||||
|
"""Generates all of the enabled modifiers. |
||||||
|
NOTE: We do not promise to return modifiers in their display_order! |
||||||
|
""" |
||||||
|
for i in dir(self): |
||||||
|
attr = getattr(self, i) |
||||||
|
# Assumes each modifier is a single pointer to PlasmaModifierProperties |
||||||
|
if isinstance(attr, PlasmaModifierProperties): |
||||||
|
if attr.enabled: |
||||||
|
yield attr |
||||||
|
|
||||||
|
|
||||||
|
def _is_plasma_modifier(hClass): |
||||||
|
if inspect.isclass(hClass): |
||||||
|
if issubclass(hClass, PlasmaModifierProperties) and hasattr(hClass, "pl_id"): |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def modifier_definitions(): |
||||||
|
"""This returns a sequence of all modifiers""" |
||||||
|
for i in globals().values(): |
||||||
|
if _is_plasma_modifier(i): |
||||||
|
yield i |
||||||
|
|
||||||
|
def modifier_mapping(): |
||||||
|
"""This returns a dict mapping Plasma Modifier categories to names""" |
||||||
|
|
||||||
|
# FIXME: a more pythonic way to do this??? |
||||||
|
d = {} |
||||||
|
for i, mod in enumerate(modifier_definitions()): |
||||||
|
if hasattr(mod, "bl_icon"): |
||||||
|
icon = mod.bl_icon |
||||||
|
else: |
||||||
|
icon = "" |
||||||
|
|
||||||
|
tup = (mod.pl_id, mod.bl_label, mod.bl_description, icon, i) |
||||||
|
if mod.bl_category not in d: |
||||||
|
d[mod.bl_category] = [tup] |
||||||
|
else: |
||||||
|
d[mod.bl_category].append(tup) |
||||||
|
return d |
||||||
|
|
||||||
|
def register(): |
||||||
|
# Okay, so we have N plasma modifer property groups... |
||||||
|
# Rather than have (dis)organized chaos on the Blender Object, we will collect all of the |
||||||
|
# property groups of type PlasmaModifierProperties and generate on-the-fly a PlasmaModifier |
||||||
|
# property group to rule them all. The class attribute 'pl_id' will determine the name of |
||||||
|
# the property group in PlasmaModifiers. |
||||||
|
# Also, just to spite us, Blender doesn't seem to handle PropertyGroup inheritance... at all. |
||||||
|
# So, we're going to have to create our base properties on all of the PropertyGroups. |
||||||
|
# It's times like these that make me wonder about life... |
||||||
|
# Enjoy! |
||||||
|
for i in modifier_definitions(): |
||||||
|
for name, (prop, kwargs) in PlasmaModifierProperties._subprops.items(): |
||||||
|
setattr(i, name, prop(**kwargs)) |
||||||
|
setattr(PlasmaModifiers, i.pl_id, bpy.props.PointerProperty(type=i)) |
||||||
|
bpy.types.Object.plasma_modifiers = bpy.props.PointerProperty(type=PlasmaModifiers) |
@ -0,0 +1,45 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
from bpy.props import * |
||||||
|
|
||||||
|
class PlasmaModifierProperties(bpy.types.PropertyGroup): |
||||||
|
def created(self, obj): |
||||||
|
# This is here just to prevent us from having unnamed modifiers |
||||||
|
self.display_name = "{}Modifier{}".format(obj.name, self.display_order) |
||||||
|
|
||||||
|
def destroyed(self): |
||||||
|
pass |
||||||
|
|
||||||
|
@property |
||||||
|
def enabled(self): |
||||||
|
return self.display_order >= 0 |
||||||
|
|
||||||
|
# Guess what? |
||||||
|
# You can't register properties on a base class--Blender isn't smart enough to do inheritance, |
||||||
|
# you see... So, we'll store our definitions in a dict and make those properties on each subclass |
||||||
|
# at runtime. What joy. Python FTW. See register() in __init__.py |
||||||
|
_subprops = { |
||||||
|
"display_name": (StringProperty, {"name": "Name", |
||||||
|
"description": "Modifier name"}), |
||||||
|
"display_order": (IntProperty, {"name": "INTERNAL: Display Ordering", |
||||||
|
"description": "Position in the list of buttons", |
||||||
|
"default": -1, |
||||||
|
"options": {"HIDDEN"}}), |
||||||
|
"show_expanded": (BoolProperty, {"name": "INTERNAL: Actually draw the modifier", |
||||||
|
"default": True, |
||||||
|
"options": {"HIDDEN"}}) |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
from PyHSPlasma import * |
||||||
|
|
||||||
|
from .base import PlasmaModifierProperties |
||||||
|
|
||||||
|
class PlasmaSpawnPoint(PlasmaModifierProperties): |
||||||
|
pl_id = "spawnpoint" |
||||||
|
|
||||||
|
bl_category = "Logic" |
||||||
|
bl_label = "Spawn Point" |
||||||
|
bl_description = "Point at which avatars link into the Age" |
||||||
|
|
||||||
|
def created(self, obj): |
||||||
|
self.display_name = obj.name |
||||||
|
|
||||||
|
def export(self, exporter, bo, so): |
||||||
|
# Not much to this modifier... It's basically a flag that tells the engine, "hey, this is a |
||||||
|
# place the avatar can show up." Nice to have a simple one to get started with. |
||||||
|
spawn = exporter.mgr.add_object(pl=plSpawnModifier, bl=bo, name=self.display_name) |
||||||
|
so.addModifier(spawn.key) |
@ -0,0 +1,16 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
from .logic import * |
@ -0,0 +1,18 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
def spawnpoint(modifier, layout, context): |
||||||
|
layout.label(text="The Y axis indicates the direction the avatar is facing.") |
||||||
|
|
@ -0,0 +1,89 @@ |
|||||||
|
# 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 <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
|
||||||
|
from . import modifiers as modifier_draw |
||||||
|
|
||||||
|
class ModifierButtonsPanel: |
||||||
|
bl_space_type = "PROPERTIES" |
||||||
|
bl_region_type = "WINDOW" |
||||||
|
|
||||||
|
# Let me take this opportunity to rant. |
||||||
|
# For some STUPID REASON, Blender decides which buttons to show in the C code. This is all well |
||||||
|
# and good, EXCEPT THEY DO NOT SHOW THE MODIFIERS BUTTON FOR EMPTIES. This totally breaks the |
||||||
|
# Plasma Modifier workflow. As a shim workaround, we're overtaking the physics panel as our |
||||||
|
# Plasma Modifier Panel. The Physics, Object, and Constraint Panels' visibility determined by |
||||||
|
# the same block of a switch statement in Blender 2.71 (buttons_context_path in buttons_context.c) |
||||||
|
bl_context = "physics" |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def poll(cls, context): |
||||||
|
return context.object and context.scene.render.engine == "PLASMA_GAME" |
||||||
|
|
||||||
|
|
||||||
|
class PlasmaModifiersPanel(ModifierButtonsPanel, bpy.types.Panel): |
||||||
|
bl_label = "Plasma Modifiers" |
||||||
|
|
||||||
|
def draw(self, context): |
||||||
|
layout = self.layout |
||||||
|
obj = context.object |
||||||
|
|
||||||
|
# So, I had to read the doggone Blender source code to figure out how to use this because the |
||||||
|
# "documentation" only gives this helpful information about this interesting feature: "operator_menu_enum" |
||||||
|
# Bah. For the record: first param is the operator, second is an EnumProperty on that operator. |
||||||
|
# You define categories by inserting an enum item with an empty key, empty description, and just a name. |
||||||
|
# Any items following that are members of that category, of course... |
||||||
|
# ... I hope that my rambling has helped somebody understand more about the undocumented mess |
||||||
|
# that is Blender Python. |
||||||
|
layout.operator_menu_enum("object.plasma_modifier_add", "types") |
||||||
|
|
||||||
|
# First, let's sort the list of modifiers based on their display order |
||||||
|
# We don't do this sort in the property itself because this is really just a UI hint. |
||||||
|
modifiers = sorted(obj.plasma_modifiers.modifiers, key=lambda x: x.display_order) |
||||||
|
|
||||||
|
# Inside the modifier_draw module, we have draw callbables for each modifier |
||||||
|
# We'll loop through the list of active modifiers and call the drawprocs for the enabled mods |
||||||
|
for i in modifiers: |
||||||
|
modLayout = self._draw_modifier_template(i) |
||||||
|
if i.show_expanded: |
||||||
|
getattr(modifier_draw, i.pl_id)(i, modLayout, context) |
||||||
|
|
||||||
|
def _draw_modifier_template(self, modifier): |
||||||
|
"""This draws our lookalike modifier template and returns a UILayout object for each modifier |
||||||
|
to consume in order to draw its specific properties""" |
||||||
|
layout = self.layout.box() |
||||||
|
|
||||||
|
# This is the main title row. It mimics the Blender template_modifier, which (unfortunately) |
||||||
|
# requires valid Blender Modifier data. It would be nice if the Blender UI code were consistently |
||||||
|
# C or Python and not a frankenstein mix. I would probably prefer working in C, just because |
||||||
|
# the compiler saves my neck 99.9% of the time...</rant> |
||||||
|
row = layout.row(align=True) |
||||||
|
exicon = "TRIA_DOWN" if modifier.show_expanded else "TRIA_RIGHT" |
||||||
|
row.prop(modifier, "show_expanded", text="", icon=exicon, emboss=False) |
||||||
|
if hasattr(modifier, "bl_icon"): |
||||||
|
row.label(icon=modifier.bl_icon) |
||||||
|
else: |
||||||
|
row.label(text=modifier.bl_label) |
||||||
|
row.prop(modifier, "display_name", text="") |
||||||
|
|
||||||
|
row.operator("object.plasma_modifier_move_up", text="", icon="TRIA_UP").active_modifier = modifier.display_order |
||||||
|
row.operator("object.plasma_modifier_move_down", text="", icon="TRIA_DOWN").active_modifier = modifier.display_order |
||||||
|
row.operator("object.plasma_modifier_remove", text="", icon="X").active_modifier = modifier.display_order |
||||||
|
|
||||||
|
# Now we return the modifier box, which is populated with the modifier specific properties |
||||||
|
# by whatever insanity is in the modifier module. modifier modifier modifier... |
||||||
|
# MODDDDDDDDIFIIIIEEEERRRRRRRR!!! |
||||||
|
return layout |
Loading…
Reference in new issue