From 242ed01afb486d1eebc68191b95124d20847a9f3 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 8 Jun 2014 21:19:17 -0400 Subject: [PATCH] Move exporter logic out of prop_object... ... And commence more work on the exporter. We now export CoordinateInterfaces! Go us. --- korman/exporter/convert.py | 115 ++++++++++++++++++++++++++----- korman/exporter/logger.py | 72 +++++++++++++++++++ korman/exporter/manager.py | 13 ++-- korman/exporter/mesh.py | 21 ++++++ korman/exporter/utils.py | 26 +++++++ korman/properties/prop_object.py | 8 --- 6 files changed, 224 insertions(+), 31 deletions(-) create mode 100644 korman/exporter/logger.py create mode 100644 korman/exporter/mesh.py create mode 100644 korman/exporter/utils.py diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index 789cce2..720c2b3 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -18,7 +18,10 @@ import os.path from PyHSPlasma import * from . import explosions +from . import logger from . import manager +from . import mesh +from . import utils class Exporter: # These are objects that we need to export as plSceneObjects @@ -32,26 +35,34 @@ class Exporter: return os.path.splitext(os.path.split(self._op.filepath)[1])[0] def run(self): - # Step 0: Init export resmgr and stuff - self.mgr = manager.ExportManager(globals()[self._op.version]) + with logger.ExportLogger("{}_export.log".format(self.age_name)) as _log: + # Step 0: Init export resmgr and stuff + self.mgr = manager.ExportManager(globals()[self._op.version]) + self.mesh = mesh.MeshConverter() + self.report = logger.ExportAnalysis() - # Step 1: Gather a list of objects that we need to export - # We should do this first so we can sanity check - # and give accurate progress reports - self._collect_objects() + # Step 1: Gather a list of objects that we need to export + # We should do this first so we can sanity check + # and give accurate progress reports + self._collect_objects() - # Step 2: Create the age info and the pages - self._export_age_info() + # Step 2: Create the age info and the pages + self._export_age_info() - # Step 2.9: Ensure that all Plasma Objects are in a valid page - # This creates the default page if it is used - self.mgr.sanity_check_object_pages(self.age_name, self._objects) + # Step 2.9: Ensure that all Plasma Objects are in a valid page + # This creates the default page if it is used + self.mgr.sanity_check_object_pages(self.age_name, self._objects) - # Step 3: Export all the things! - self._export_scene_objects() + # Step 3: Export all the things! + self._export_scene_objects() - # Step 4: FINALLY. Let's write the PRPs and crap. - self.mgr.save_age(self._op.filepath) + # Step 4: FINALLY. Let's write the PRPs and crap. + self.mgr.save_age(self._op.filepath) + + # Step 4.1: Save out the export report. + # If the export fails and this doesn't save, we have bigger problems than + # these little warnings and notices. + self.report.save() def _collect_objects(self): for obj in bpy.data.objects: @@ -72,9 +83,75 @@ class Exporter: mgr.create_page(age_name, page.name, page.seq_suffix) mgr.create_builtins(age_name, self._op.use_texture_page) + def _export_actor(self, so, bo): + """Exports a Coordinate Interface if we need one""" + empty = bo.type in {"CAMERA", "EMPTY", "LAMP"} + childobj = bo.parent is not None + + if empty or childobj: + self._export_coordinate_interface(so, bo) + + # If this object has a parent, then we will need to go upstream and add ourselves to the + # parent's CoordinateInterface... Because life just has to be backwards. + if childobj: + parent = bo.parent + if parent.plasma_object.enabled: + print("\tAttaching to parent SceneObject '{}'".format(parent.name)) + + # Instead of exporting a skeleton now, we'll just make an orphaned CI. + # The bl_obj export will make this work. + parent_ci = self.mgr.find_create_key(parent, plCoordinateInterface).object + parent_ci.addChild(so.key) + else: + self.report.warn("oversight", + "You have parented Plasma Object '{}' to '{}', which has not been marked for export. \ + The object may not appear in the correct location or animate properly.".format( + bo.name, parent.name)) + + def _export_coordinate_interface(self, so, bo): + """Ensures that the SceneObject has a CoordinateInterface""" + if not so.coord: + ci = self.mgr.find_create_key(bo, plCoordinateInterface) + so.coord = ci + ci = ci.object + ci.owner = so.key + + # Now we have the "fun" work of filling in the CI + ci.worldToLocal = utils.matrix44(bo.matrix_basis) + ci.localToWorld = ci.worldToLocal.inverse() + ci.parentToLocal = utils.matrix44(bo.matrix_local) + ci.localToParent = ci.parentToLocal.inverse() + def _export_scene_objects(self): for bl_obj in self._objects: - # Naive export all Plasma Objects. They will be responsible for calling back into the - # exporter to find/create drawable meshes, materials, etc. Not sure if this design will - # work well, but we're going to go with it for now. - bl_obj.plasma_object.export(self, bl_obj) \ No newline at end of file + print("=== Exporting plSceneObject ===") + + # First pass: do things specific to this object type. + # note the function calls: to export a MESH, it's _export_mesh_blobj + export_fn = "_export_{}_blobj".format(bl_obj.type.lower()) + try: + export_fn = getattr(self, export_fn) + except AttributeError: + print("WARNING: '{}' is a Plasma Object of Blender type '{}'".format(bl_obj.name, bl_obj.type)) + print("... And I have NO IDEA what to do with that! Tossing.") + continue + print("\tBlender Object '{}' of type '{}'".format(bl_obj.name, bl_obj.type)) + + # Create a sceneobject if one does not exist. + # Before we call the export_fn, we need to determine if this object is an actor of any + # sort, and barf out a CI. + sceneobject = self.mgr.find_create_key(bl_obj, plSceneObject).object + self._export_actor(sceneobject, bl_obj) + export_fn(sceneobject, bl_obj) + + # :( + print() + + def _export_empty_blobj(self, so, bo): + # We don't need to do anything here. This function just makes sure we don't error out + # or add a silly special case :( + pass + + def _export_mesh_blobj(self, so, bo): + # TODO + pass \ No newline at end of file diff --git a/korman/exporter/logger.py b/korman/exporter/logger.py new file mode 100644 index 0000000..2145ddb --- /dev/null +++ b/korman/exporter/logger.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 sys + +class ExportAnalysis: + """This is used to collect artist action items from the export process. You can warn about + portability issues, possible oversights, etc. The benefit here is that the user doesn't have + to look through all of the gobbledygook in the export log. + """ + + _notices = {} + _warnings = {} + + def save(self): + # TODO + pass + + def _stash(self, _d, category, message): + if category not in _d: + _d[category] = [message,] + else: + _d[category].append(message) + + def note(self, category, message): + self._stash(self._notices, category, message) + print("NOTICE {}: {}".format(category, message)) + + def warn(self, category, message): + self._stash(self._warnings, category, message) + print("WARNING {}: {}".format(category, message)) + + +class ExportLogger: + """Yet Another Logger(TM)""" + + def __init__(self, fn): + self._stdout = sys.stdout + self._stderr = sys.stderr + self._file = open(fn, "w") + + for i in dir(self._file): + if not hasattr(self, i): + setattr(self, i, getattr(self._file, i)) + + def __enter__(self): + sys.stdout = self._file + sys.stderr = self._file + + def __exit__(self, type, value, traceback): + sys.stdout = self._stdout + sys.stderr = self._stderr + + def write(self, str): + self._file.write(str) + self._stdout.write(str) + + def writelines(self, seq): + self._file.writelines(seq) + self._stdout.writelines(seq) diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py index 6d7705d..2fe7e07 100644 --- a/korman/exporter/manager.py +++ b/korman/exporter/manager.py @@ -109,12 +109,17 @@ class ExportManager: self._nodes[location] = None return location - def find_key(self, bl_obj, index): - """Given a blender Object and a pCre index, find an exported plKey""" + def find_create_key(self, bl_obj, pClass): + key = self.find_key(bl_obj, pClass) + if key is None: + key = self.add_object(pl=pClass, bl=bl_obj).key + return key + + def find_key(self, bl_obj, pClass): + """Given a blender Object and a Plasma class, find (or create) an exported plKey""" location = self._pages[bl_obj.plasma_object.page] - # NOTE: may need to replace with a python side dict for faster lookups - # evaluate when exporter has been fleshed out + index = plFactory.ClassIndex(pClass.__name__) for key in self.mgr.getKeys(location, index): if bl_obj.name == key.name: return key diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py new file mode 100644 index 0000000..21ae4f3 --- /dev/null +++ b/korman/exporter/mesh.py @@ -0,0 +1,21 @@ +# 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 PyHSPlasma import * + +class MeshConverter: + # TODO + pass diff --git a/korman/exporter/utils.py b/korman/exporter/utils.py new file mode 100644 index 0000000..2feb872 --- /dev/null +++ b/korman/exporter/utils.py @@ -0,0 +1,26 @@ +# 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 PyHSPlasma import * + +def matrix44(blmat): + """Converts a mathutils.Matrix to an hsMatrix44""" + hsmat = hsMatrix44() + for i in range(4): + hsmat[0, i] = blmat[i][0] + hsmat[1, i] = blmat[i][1] + hsmat[2, i] = blmat[i][2] + hsmat[3, i] = blmat[i][3] + return hsmat diff --git a/korman/properties/prop_object.py b/korman/properties/prop_object.py index f21a468..3a23474 100644 --- a/korman/properties/prop_object.py +++ b/korman/properties/prop_object.py @@ -51,14 +51,6 @@ class PlasmaObject(bpy.types.PropertyGroup): o.plasma_object.page = page.name break - def export(self, exporter, bl_obj): - """Plasma Object Export""" - - # This is where the magic happens... - if self.enabled: - # TODO: Something more useful than a blank object. - exporter.mgr.add_object(plSceneObject, bl=bl_obj) - enabled = BoolProperty(name="Export", description="Export this as a discrete object",