From fc9407a332684ebf73f41cfd3a4bfec2abb553fc Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 20 Jun 2015 00:29:26 -0400 Subject: [PATCH] Export dynamic environment maps This means we now export BOTH plDyanmicEnvMap and plDynamicCamMap. The latter will only be exported on MOUL+, sadly. The former is "cube" mapping and the latter is "plane" mapping. Instead of manually specifying the refresh rate, I decided that Blender's "static" option means that it only refreshes once (and never again) and animated means we refresh every 0.01 second, which seems to be the Cyan standard for "update every frame, blast it!" NOTE: when cameras are exporting, we need to support Camera based DCMs. Static image based CEMs coming soon... --- korman/exporter/convert.py | 11 ++-- korman/exporter/manager.py | 2 +- korman/exporter/material.py | 100 +++++++++++++++++++++++++++++++++++- korman/exporter/mesh.py | 2 +- korman/helpers.py | 4 ++ 5 files changed, 109 insertions(+), 10 deletions(-) diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index b66c96c..5a907d0 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -130,7 +130,7 @@ class Exporter: 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) + 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. @@ -148,16 +148,15 @@ class Exporter: The object may not appear in the correct location or animate properly.".format( bo.name, parent.name)) - def _export_coordinate_interface(self, so, bo): + def export_coordinate_interface(self, so, bl, name=None): """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 + ci = self.mgr.find_create_key(plCoordinateInterface, bl=bl, so=so, name=name).object # Now we have the "fun" work of filling in the CI - ci.localToWorld = utils.matrix44(bo.matrix_basis) + ci.localToWorld = utils.matrix44(bl.matrix_basis) ci.worldToLocal = ci.localToWorld.inverse() - ci.localToParent = utils.matrix44(bo.matrix_local) + ci.localToParent = utils.matrix44(bl.matrix_local) ci.parentToLocal = ci.localToParent.inverse() def _export_scene_objects(self): diff --git a/korman/exporter/manager.py b/korman/exporter/manager.py index dd04f7a..9d6ef91 100644 --- a/korman/exporter/manager.py +++ b/korman/exporter/manager.py @@ -93,7 +93,7 @@ class ExportManager: if isinstance(pl, plObjInterface): if so is None: - key = self.find_key(plSceneObject, bl) + key = self.find_key(plSceneObject, bl, name) # prevent race conditions if key is None: so = self.add_object(plSceneObject, name=name, loc=location) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 9b6cce4..d5979cf 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -21,6 +21,7 @@ from PyHSPlasma import * import weakref from . import explosions +from .. import helpers from . import utils # BGL doesn't know about this as of Blender 2.74 @@ -226,6 +227,101 @@ class MaterialConverter: getattr(self, export_fn)(bo, hsgmat, layer, texture) hsgmat.addLayer(layer.key) + def _export_texture_type_environment_map(self, bo, hsgmat, layer, texture): + """Exports a Blender EnvironmentMapTexture to a plLayer""" + + bl_env = texture.environment_map + if bl_env.source in {"STATIC", "ANIMATED"}: + if bl_env.mapping == "PLANE" and self._mgr.getVer() >= pvMoul: + pl_env = plDynamicCamMap + else: + pl_env = plDynamicEnvMap + pl_env = self._export_dynamic_env(bo, hsgmat, layer, bl_env, pl_env) + else: + raise NotSupportedError() + + layer.texture = pl_env + + def _export_dynamic_env(self, bo, hsgmat, layer, bl_env, pl_class): + # To protect the user from themselves, let's check to make sure that a DEM/DCM matching this + # viewpoint object has not already been exported... + viewpt = bl_env.viewpoint_object + name = "{}_DynEnvMap".format(viewpt.name) + pl_env = self._mgr.find_key(pl_class, bl=bo, name=name) + if pl_env is not None: + print(" EnvMap for viewpoint {} already exported... NOTE: Your settings here will be overridden by the previous object!".format(viewpt.name)) + pl_env_obj = pl_env.object + if isinstance(pl_env_obj, plDynamicCamMap): + dcm.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) + dcm.addMatLayer(layer.key) + return pl_env + + # It matters not whether or not the viewpoint object is a Plasma Object, it is exported as at + # least a SceneObject and CoordInterface so that we can touch it... + root = self._mgr.find_create_key(plSceneObject, bl=bo, name=viewpt.name) + self._exporter().export_coordinate_interface(root.object, bl=bo, name=viewpt.name) + # FIXME: DynamicCamMap Camera + + # Ensure POT + oRes = bl_env.resolution + eRes = helpers.ensure_power_of_two(oRes) + if oRes != eRes: + print(" Overriding EnvMap size to ({}x{}) -- POT".format(eRes, eRes)) + + # And now for the general ho'hum-ness + pl_env = self._mgr.add_object(pl_class, bl=bo, name=name) + pl_env.hither = bl_env.clip_start + pl_env.yon = bl_env.clip_end + pl_env.refreshRate = 0.01 if bl_env.source == "ANIMATED" else 0.0 + pl_env.incCharacters = True + pl_env.rootNode = root # FIXME: DCM camera + + # Perhaps the DEM/DCM fog should be separately configurable at some point? + pl_fog = bpy.context.scene.world.plasma_fni + pl_env.color = utils.color(pl_fog.fog_color) + pl_env.fogStart = pl_fog.fog_start + + if isinstance(pl_env, plDynamicCamMap): + faces = (pl_env,) + + pl_env.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) + pl_env.addMatLayer(layer.key) + + # This is really just so we don't raise any eyebrows if anyone is looking at the files. + # If you're disabling DCMs, then you're obviuously trolling! + # Cyan generates a single color image, but we'll just set the layer colors and go away. + fake_layer = self._mgr.add_object(plLayer, bl=bo, name="{}_DisabledDynEnvMap".format(viewpt.name)) + fake_layer.ambient = layer.ambient + fake_layer.preshade = layer.preshade + fake_layer.runtime = layer.runtime + fake_layer.specular = layer.specular + pl_env.disableTexture = fake_layer.key + + if pl_env.camera is None: + layer.UVWSrc = plLayerInterface.kUVWPosition + layer.state.miscFlags |= (hsGMatState.kMiscCam2Screen | hsGMatState.kMiscPerspProjection) + else: + faces = pl_env.faces + (pl_env,) + + layer.UVWSrc = plLayerInterface.kUVWReflect + layer.state.miscFlags |= hsGMatState.kMiscUseRefractionXform + + # Because we might be working with a multi-faced env map. It's even worse than have two faces... + for i in faces: + i.setConfig(plBitmap.kRGB8888) + i.flags |= plBitmap.kIsTexture + i.flags &= ~plBitmap.kAlphaChannelFlag + i.width = eRes + i.height = eRes + i.proportionalViewport = False + i.viewportLeft = 0 + i.viewportTop = 0 + i.viewportRight = eRes + i.viewportBottom = eRes + i.ZDepth = 24 + + return pl_env.key + def _export_texture_type_image(self, bo, hsgmat, layer, texture): """Exports a Blender ImageTexture to a plLayer""" @@ -280,8 +376,8 @@ class MaterialConverter: image = key.image oWidth, oHeight = image.size - eWidth = pow(2, math.floor(math.log(oWidth, 2))) - eHeight = pow(2, math.floor(math.log(oHeight, 2))) + eWidth = helpers.ensure_power_of_two(oWidth) + eHeight = helpers.ensure_power_of_two(oHeight) if (eWidth != oWidth) or (eHeight != oHeight): print(" Image is not a POT ({}x{}) resizing to {}x{}".format(oWidth, oHeight, eWidth, eHeight)) self._resize_image(image, eWidth, eHeight) diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 6992cc9..eb041fe 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -143,7 +143,7 @@ class MeshConverter: for dspan in loc.values(): print("\n[DrawableSpans '{}']".format(dspan.key.name)) print(" Composing geometry data") - + # This mega-function does a lot: # 1. Converts SourceSpans (geospans) to Icicles and bakes geometry into plGBuffers # 2. Calculates the Icicle bounds diff --git a/korman/helpers.py b/korman/helpers.py index 98ffe63..c87680c 100644 --- a/korman/helpers.py +++ b/korman/helpers.py @@ -14,6 +14,7 @@ # along with Korman. If not, see . import bpy +import math class GoodNeighbor: """Leave Things the Way You Found Them! (TM)""" @@ -31,6 +32,9 @@ class GoodNeighbor: setattr(cls, attr, value) +def ensure_power_of_two(value): + return pow(2, math.floor(math.log(value, 2))) + def make_active_selection(bo): """Selects a single Blender Object and makes it active""" for i in bpy.data.objects: