From 5fececf4e17b78ed61ca491ea49dfe2de7d58146 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 8 Jul 2012 01:39:38 -0400 Subject: [PATCH] Basic age/page UI controls We are attempting to encourage the pages=layers paradigm; however, this is not strictly enforced. This commit has lots of bits and bobs but little actual substance. More to come when I begin work on the exporter. --- .gitignore | 1 + korman/__init__.py | 6 +- korman/exporter.py | 6 +- korman/operators/__init__.py | 16 +++++ korman/operators/op_world.py | 72 +++++++++++++++++++ korman/properties/__init__.py | 24 +++++++ korman/properties/prop_object.py | 51 ++++++++++++++ korman/properties/prop_world.py | 114 +++++++++++++++++++++++++++++++ korman/ui/__init__.py | 17 +++++ korman/ui/ui_object.py | 40 +++++++++++ korman/ui/ui_world.py | 69 +++++++++++++++++++ 11 files changed, 413 insertions(+), 3 deletions(-) create mode 100644 korman/operators/__init__.py create mode 100644 korman/operators/op_world.py create mode 100644 korman/properties/__init__.py create mode 100644 korman/properties/prop_object.py create mode 100644 korman/properties/prop_world.py create mode 100644 korman/ui/__init__.py create mode 100644 korman/ui/ui_object.py create mode 100644 korman/ui/ui_world.py diff --git a/.gitignore b/.gitignore index f24cd99..45c9785 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.py[co] +__pycache__ # Packages *.egg diff --git a/korman/__init__.py b/korman/__init__.py index 8476085..170c467 100644 --- a/korman/__init__.py +++ b/korman/__init__.py @@ -15,6 +15,7 @@ import bpy from . import exporter, render +from . import operators, properties, ui bl_info = { "name": "Korman", @@ -31,9 +32,12 @@ def register(): """Registers all Blender operators and GUI items in Korman""" # This will auto-magically register all blender classes for us - # We will define a lot of them, methinks... bpy.utils.register_module(__name__) + # We have to setup pointer props to our custom property groups ourselves, + # so let's go ahead and do that now. + properties.register() + def unregister(): """Unregisters all Blender operators and GUI items""" bpy.utils.unregister_module(__name__) diff --git a/korman/exporter.py b/korman/exporter.py index dfdc21f..0203a7b 100644 --- a/korman/exporter.py +++ b/korman/exporter.py @@ -37,7 +37,8 @@ class PlasmaExporter(bpy.types.Operator): @classmethod def poll(cls, context): - return context.object is not None + if context.object is not None: + return context.scene.render.engine == "PLASMA_GAME" def execute(self, context): # TODO @@ -54,4 +55,5 @@ class PlasmaExporter(bpy.types.Operator): def menu_cb(self, context): self.layout.operator_context = "INVOKE_DEFAULT" self.layout.operator(PlasmaExporter.bl_idname, text="Plasma Age (.age)") -bpy.types.INFO_MT_file_export.append(menu_cb) +def register(): + bpy.types.INFO_MT_file_export.append(menu_cb) diff --git a/korman/operators/__init__.py b/korman/operators/__init__.py new file mode 100644 index 0000000..d44002a --- /dev/null +++ b/korman/operators/__init__.py @@ -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 . + +from .op_world import * diff --git a/korman/operators/op_world.py b/korman/operators/op_world.py new file mode 100644 index 0000000..19415a6 --- /dev/null +++ b/korman/operators/op_world.py @@ -0,0 +1,72 @@ +# 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 . + +import bpy + +class AgeOperator: + @classmethod + def poll(cls, context): + return context.scene.render.engine == "PLASMA_GAME" + +class PlasmaPageAddOperator(AgeOperator, bpy.types.Operator): + bl_idname = "world.plasma_page_add" + bl_label = "Add Page" + bl_description = "Adds a new Plasma Registry Page" + + def execute(self, context): + w = context.world + if w: + age = w.plasma_age + page = age.pages.add() + + # Find the first non-zero ID and suggest that. + suffixes = [] + for p in age.pages: + # Filter out pages with no-id or + if p.seq_suffix: + suffixes.append(p.seq_suffix) + if len(suffixes): + suffixes.sort() + test = set(range(suffixes[0], suffixes[-1])) + missing = test - set(suffixes) + try: + suffix = missing.pop() + except KeyError: + suffix = suffixes[-1] + 1 + page.make_default_name(suffix) + else: + # Page 0 is a magic "catch-all" page. The user *may* define it + # if he wants. If he doesn't, we'll defer it until export time + page.make_default_name(1) + + # Finally, select the new page + age.active_page_index = len(age.pages) - 1 + return {"FINISHED"} + else: + return {"CANCELLED"} + + +class PlasmaPageRemoveOperator(AgeOperator, bpy.types.Operator): + bl_idname = "world.plasma_page_remove" + bl_label = "Remove Page" + bl_description = "Removes the selected Plasma Registry Page" + + def execute(self, context): + w = context.world + if w: + # ... + return {"FINISHED"} + else: + return {"CANCELLED"} diff --git a/korman/properties/__init__.py b/korman/properties/__init__.py new file mode 100644 index 0000000..6164589 --- /dev/null +++ b/korman/properties/__init__.py @@ -0,0 +1,24 @@ +# 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 . + +import bpy + +from .prop_object import * +from .prop_world import * + +def register(): + bpy.types.Object.plasma_object = PointerProperty(type=PlasmaObject) + bpy.types.World.plasma_age = PointerProperty(type=PlasmaAge) + bpy.types.World.plasma_fni = PointerProperty(type=PlasmaFni) diff --git a/korman/properties/prop_object.py b/korman/properties/prop_object.py new file mode 100644 index 0000000..17b1caf --- /dev/null +++ b/korman/properties/prop_object.py @@ -0,0 +1,51 @@ +# 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 . + +import bpy +from bpy.props import * + +class PlasmaObject(bpy.types.PropertyGroup): + def _export_changed(self, context): + # This makes me sad + if not self.is_inited: + self._init(context) + self.is_inited = True + + def _init(self, context): + o = context.object + age = context.scene.world.plasma_age + + # We want to encourage the pages = layers paradigm. + # So, let's see which layers we're on and check for a page whose + # suffix matches our layers. We'll take the first match. + num_layers = len(o.layers) + for page in age.pages: + if page.seq_suffix > num_layers: + continue + if o.layers[page.seq_suffix-1]: + o.plasma_object.page = page.name + break + + export = BoolProperty(name="Export", + description="Export this as a discrete object", + default=False, + update=_export_changed) + page = StringProperty(name="Page", + description="Page this object will be exported to") + + # Implementation Details + is_inited = BoolProperty(description="INTERNAL: Init proc complete", + default=False, + options={"HIDDEN"}) diff --git a/korman/properties/prop_world.py b/korman/properties/prop_world.py new file mode 100644 index 0000000..011cecd --- /dev/null +++ b/korman/properties/prop_world.py @@ -0,0 +1,114 @@ +# 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 . + +import bpy +from bpy.props import * + +class PlasmaFni(bpy.types.PropertyGroup): + bl_idname = "world.plasma_fni" + + fog_color = FloatVectorProperty(name="Fog Color", + description="The default fog color used in your age", + default=(0.3, 0.2, 0.05), + subtype="COLOR") + clear_color = FloatVectorProperty(name="Clear Color", + description="The default background color rendered in your age", + subtype="COLOR") + yon = IntProperty(name="Draw Distance", + description="The distance (in feet) Plasma will draw", + default=100000, + soft_min=100, + min=1) + # TODO: density + +class PlasmaPage(bpy.types.PropertyGroup): + def _check_suffix(self, context): + """Verifies that a suffix change does not conflict""" + old = self.last_seq_suffix + self.last_seq_suffix = self.seq_suffix + if not self.check_suffixes: + return None + + for page in context.world.plasma_age.pages: + if page.seq_suffix == self.seq_suffix: + # Need to supress checking while we override the suffix + page.check_suffixes = False + page.seq_suffix = old + page.check_suffixes = True + break + return None + + def _rename_page(self, context): + # Need to init? + if self.last_name == "" and self.name: + self.last_name = self.name + return None + + # Empty page names not allowed! + if self.name == "": + self.make_default_name(self.seq_suffix) + return None + + # Since many objects will have page names attached to them, we'll be + # very nice and handle renames for the user. + for obj in bpy.data.objects: + if obj.plasma_object.page == self.last_name: + obj.plasma_object.page = self.name + self.last_name = self.name + return None + + def make_default_name(self, suffix): + self.seq_suffix = suffix + self.name = "Page%02i" % suffix + self.check_suffixes = True + + name = StringProperty(name="Name", + description="Name of the specified page", + update=_rename_page) + seq_suffix = IntProperty(name="ID", + description="A numerical ID for this page", + soft_min=0, # Negatives indicate global--advanced users only + default=0, # The add operator will autogen a default + update=_check_suffix) + auto_load = BoolProperty(name="Auto Load", + description="Load this page on link-in", + default=True) + + # Implementation details... + last_name = StringProperty(description="INTERNAL: Cached page name", + options={"HIDDEN"}) + last_seq_suffix = IntProperty(description="INTERNAL: Cached sequence suffix", + options={"HIDDEN"}) + check_suffixes = BoolProperty(description="INTERNAL: Should we sanity-check suffixes?", + options={"HIDDEN"}, + default=False) + + +class PlasmaAge(bpy.types.PropertyGroup): + day_length = FloatProperty(name="Day Length", + description="Length of a day (in hours) on this age", + default=24.0, + soft_min=0.1, + min=0.0) + seq_prefix = IntProperty(name="Sequence Prefix", + description="A unique numerical ID for this age", + soft_min=0, # Negative indicates global--advanced users only + default=100) + pages = CollectionProperty(name="Pages", + description="Registry pages for this age", + type=PlasmaPage) + + # Implementation details + active_page_index = IntProperty(name="Active Page Index") \ No newline at end of file diff --git a/korman/ui/__init__.py b/korman/ui/__init__.py new file mode 100644 index 0000000..00688be --- /dev/null +++ b/korman/ui/__init__.py @@ -0,0 +1,17 @@ +# 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 . + +from .ui_object import * +from .ui_world import * diff --git a/korman/ui/ui_object.py b/korman/ui/ui_object.py new file mode 100644 index 0000000..895d201 --- /dev/null +++ b/korman/ui/ui_object.py @@ -0,0 +1,40 @@ +# 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 . + +import bpy + +class ObjectButtonsPanel: + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + @classmethod + def poll(cls, context): + return context.object and context.scene.render.engine == "PLASMA_GAME" + + +class PlasmaObjectPanel(ObjectButtonsPanel, bpy.types.Panel): + bl_label = "Plasma Object" + + def draw_header(self, context): + self.layout.prop(context.object.plasma_object, "export", text="") + + def draw(self, context): + layout = self.layout + pl_obj = context.object.plasma_object + pl_age = context.scene.world.plasma_age + layout.active = pl_obj.export + + layout.prop_search(pl_obj, "page", pl_age, "pages", icon="BOOKMARKS") diff --git a/korman/ui/ui_world.py b/korman/ui/ui_world.py new file mode 100644 index 0000000..6af9b1f --- /dev/null +++ b/korman/ui/ui_world.py @@ -0,0 +1,69 @@ +# 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 . + +import bpy + +class AgeButtonsPanel: + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "world" + + @classmethod + def poll(cls, context): + return context.world and context.scene.render.engine == "PLASMA_GAME" + + +class PlasmaAgePanel(AgeButtonsPanel, bpy.types.Panel): + bl_label = "Plasma Age" + + def draw(self, context): + layout = self.layout + age = context.world.plasma_age + + # Core age settings + # ... + + # We want a list of pages and an editor below that + row = layout.row() + row.template_list(age, "pages", age, "active_page_index", rows=2) + col = row.column(align=True) + col.operator("world.plasma_page_add", icon="ZOOMIN", text="") + col.operator("world.plasma_page_remove", icon="ZOOMOUT", text="") + + # name/id editor for current selection + if age.active_page_index < len(age.pages): + active_page = age.pages[age.active_page_index] + split = layout.split(percentage=0.65) + col = split.column() + col.prop(active_page, "name") + col = split.column() + col.prop(active_page, "seq_suffix") + + +class PlasmaEnvironmentPanel(AgeButtonsPanel, bpy.types.Panel): + bl_label = "Plasma Environment" + + def draw(self, context): + layout = self.layout + fni = context.world.plasma_fni + + # Column-ize the colors + split = layout.split() + col = split.column() + col.prop(fni, "fog_color") + col = split.column() + col.prop(fni, "clear_color") + + layout.prop(fni, "yon")