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