You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
8.6 KiB

# 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 os.path
from PyHSPlasma import *
import time
from . import explosions
from . import logger
from . import manager
from . import mesh
from . import physics
from . import rtlight
from . import sumfile
from . import utils
class Exporter:
def __init__(self, op):
self._op = op # Blender export operator
self._objects = []
@property
def age_name(self):
return os.path.splitext(os.path.split(self._op.filepath)[1])[0]
def run(self):
with logger.ExportLogger(self._op.filepath) as _log:
print("Exporting '{}.age'".format(self.age_name))
start = time.process_time()
# Step 0: Init export resmgr and stuff
self.mgr = manager.ExportManager(self)
self.mesh = mesh.MeshConverter(self)
self.report = logger.ExportAnalysis()
self.physics = physics.PhysicsConverter(self)
self.light = rtlight.LightConverter(self)
self.sumfile = sumfile.SumFile()
# Step 1: Create the age info and the pages
self._export_age_info()
# Step 2: Gather a list of objects that we need to export, given what the user has told
# us to export (both in the Age and Object Properties)... fun
self._collect_objects()
# Step 3: Export all the things!
self._export_scene_objects()
# Step 4: Finalize...
self.mesh.material.finalize()
self.mesh.finalize()
# Step 5: FINALLY. Let's write the PRPs and crap.
self.mgr.save_age(self._op.filepath)
# Step 5.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()
# And finally we crow about how awesomely fast we are...
end = time.process_time()
print("\nExported {}.age in {:.2f} seconds".format(self.age_name, end-start))
def _collect_objects(self):
# Grab a naive listing of enabled pages
age = bpy.context.scene.world.plasma_age
pages_enabled = frozenset([page.name for page in age.pages if page.enabled])
all_pages = frozenset([page.name for page in age.pages])
# Because we can have an unnamed or a named default page, we need to see if that is enabled...
for page in age.pages:
if page.seq_suffix == 0:
default_enabled = page.enabled
default_inited = True
break
else:
default_enabled = True
default_inited = False
# Now we loop through the objects with some considerations:
# - The default page may or may not be defined. If it is, it can be disabled. If not, it
# can only ever be enabled.
# - Don't create the Default page unless it is used (implicit or explicit). It is a failure
# to export a useless file.
# - Any arbitrary page can be disabled, so check our frozenset.
# - Also, someone might have specified an invalid page, so keep track of that.
error = explosions.UndefinedPageError()
for obj in bpy.data.objects:
if obj.plasma_object.enabled:
page = obj.plasma_object.page
if not page and not default_inited:
self.mgr.create_page(self.age_name, "Default", 0)
default_inited = True
if (default_enabled and not page) or (page in pages_enabled):
self._objects.append(obj)
elif page not in all_pages:
error.add(page, obj.name)
error.raise_if_error()
def _export_age_info(self):
# Make life slightly easier...
age_info = bpy.context.scene.world.plasma_age
age_name = self.age_name
mgr = self.mgr
# Generate the plAgeInfo
mgr.AddAge(age_info.export(self))
# Create all the pages we need
for page in age_info.pages:
if page.enabled:
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"""
if self.mgr.has_coordiface(bo):
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.
parent = bo.parent
if parent is not None:
if parent.plasma_object.enabled:
print(" Attaching 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(plCoordinateInterface, bl=bo, so=so).object
parent_ci.addChild(so.key)
else:
self.report.warn("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:
print(" Exporting CoordinateInterface")
ci = self.mgr.find_create_key(plCoordinateInterface, bl=bo, so=so).object
# Now we have the "fun" work of filling in the CI
10 years ago
ci.localToWorld = utils.matrix44(bo.matrix_basis)
ci.worldToLocal = ci.localToWorld.inverse()
ci.localToParent = utils.matrix44(bo.matrix_local)
ci.parentToLocal = ci.localToParent.inverse()
def _export_scene_objects(self):
for bl_obj in self._objects:
print("\n[SceneObject '{}']".format(bl_obj.name))
# 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(" Blender 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(plSceneObject, bl=bl_obj).object
self._export_actor(sceneobject, bl_obj)
export_fn(sceneobject, bl_obj)
# And now we puke out the modifiers...
for mod in bl_obj.plasma_modifiers.modifiers:
print(" Exporting '{}' modifier as '{}'".format(mod.bl_label, mod.display_name))
mod.export(self, bl_obj, sceneobject)
# Last, but not least, apply synch settings
bl_obj.plasma_net.export(bl_obj, sceneobject)
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_lamp_blobj(self, so, bo):
# We'll just redirect this to the RT Light converter...
self.light.export_rtlight(so, bo)
def _export_mesh_blobj(self, so, bo):
if bo.data.materials:
self.mesh.export_object(bo)
else:
print(" No material(s) on the ObData, so no drawables")