diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 84361c6..3352112 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -104,7 +104,7 @@ class MaterialConverter: # export many slots in one go. Think stencils. i = 0 while i < len(slots): - i += self._export_texture_slot(bo, bm, hsgmat, slots, i) + i += self.export_texture_slot(bo, bm, hsgmat, slots, i) # Plasma makes several assumptions that every hsGMaterial has at least one layer. If this # material had no Textures, we will need to initialize a default layer @@ -138,23 +138,25 @@ class MaterialConverter: # Wasn't that easy? return hsgmat.key - def _export_texture_slot(self, bo, bm, hsgmat, slots, idx): + def export_texture_slot(self, bo, bm, hsgmat, slots, idx, blend_flags=True): slot = slots[idx] num_exported = 1 - name = "{}_{}".format(bm.name, slot.name) + name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) print(" Exporting Plasma Layer '{}'".format(name)) layer = self._mgr.add_object(plLayer, name=name, bl=bo) - self._propagate_material_settings(bm, layer) + if bm is not None: + self._propagate_material_settings(bm, layer) # UVW Channel - for i, uvchan in enumerate(bo.data.uv_layers): - if uvchan.name == slot.uv_layer: - layer.UVWSrc = i - print(" Using UV Map #{} '{}'".format(i, name)) - break - else: - print(" No UVMap specified... Blindly using the first one, maybe it exists :|") + if slot.texture_coords == "UV": + for i, uvchan in enumerate(bo.data.uv_layers): + if uvchan.name == slot.uv_layer: + layer.UVWSrc = i + print(" Using UV Map #{} '{}'".format(i, name)) + break + else: + print(" No UVMap specified... Blindly using the first one, maybe it exists :|") # Transform xform = hsMatrix44() @@ -162,8 +164,12 @@ class MaterialConverter: xform.setScale(hsVector3(*slot.scale)) layer.transform = xform + wantStencil, canStencil = slot.use_stencil, slot.use_stencil and bm is not None + if wantStencil and not canStencil: + self._exporter().report.warn("{} wants to stencil, but this is not a real Material".format(slot.name)) + state = layer.state - if slot.use_stencil: + if canStencil: hsgmat.compFlags |= hsGMaterial.kCompNeedsBlendChannel state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor if slot.texture.type == "BLEND": @@ -176,7 +182,7 @@ class MaterialConverter: if len(slots) == nextIdx: raise ExportError("Texture Slot '{}' wants to be a stencil, but there are no more TextureSlots.".format(slot.name)) print(" --- BEGIN STENCIL ---") - self._export_texture_slot(bo, bm, hsgmat, slots, nextIdx) + self.export_texture_slot(bo, bm, hsgmat, slots, nextIdx) print(" --- END STENCIL ---") num_exported += 1 @@ -186,7 +192,7 @@ class MaterialConverter: prev_state.miscFlags |= hsGMatState.kMiscBindNext | hsGMatState.kMiscRestartPassHere if not prev_state.blendFlags & hsGMatState.kBlendMask: prev_state.blendFlags |= hsGMatState.kBlendAlpha - else: + elif blend_flags: # Standard layer flags ahoy if slot.blend_type == "ADD": state.blendFlags |= hsGMatState.kBlendAddColorTimesAlpha @@ -204,18 +210,24 @@ class MaterialConverter: state.blendFlags |= hsGMatState.kBlendAlphaTestHigh # Export the specific texture type - self._tex_exporters[texture.type](bo, hsgmat, layer, slot) + self._tex_exporters[texture.type](bo, layer, slot) # Export any layer animations - layer = self._export_layer_animations(bo, bm, slot, idx, hsgmat, layer) + layer = self._export_layer_animations(bo, bm, slot, idx, layer) - hsgmat.addLayer(layer.key) - return num_exported + if hsgmat is None: + return layer + else: + hsgmat.addLayer(layer.key) + return num_exported - def _export_layer_animations(self, bo, bm, tex_slot, idx, hsgmat, base_layer): + def _export_layer_animations(self, bo, bm, tex_slot, idx, base_layer): """Exports animations on this texture and chains the Plasma layers as needed""" def harvest_fcurves(bl_id, collection, data_path=None): + if bl_id is None: + return None + anim = bl_id.animation_data if anim is not None: action = anim.action @@ -241,7 +253,7 @@ class MaterialConverter: # and chain this biotch up as best we can. layer_animation = None for attr, converter in self._animation_exporters.items(): - ctrl = converter(bm, tex_slot, base_layer, fcurves) + ctrl = converter(tex_slot, base_layer, fcurves) if ctrl is not None: if layer_animation is None: name = "{}_LayerAnim".format(base_layer.key.name) @@ -274,7 +286,7 @@ class MaterialConverter: # Well, we had some FCurves but they were garbage... Too bad. return base_layer - def _export_layer_opacity_animation(self, bm, tex_slot, base_layer, fcurves): + def _export_layer_opacity_animation(self, tex_slot, base_layer, fcurves): for i in fcurves: if i.data_path == "plasma_layer.opacity": base_layer.state.blendFlags |= hsGMatState.kBlendAlpha @@ -282,7 +294,7 @@ class MaterialConverter: return ctrl return None - def _export_layer_transform_animation(self, bm, tex_slot, base_layer, fcurves): + def _export_layer_transform_animation(self, tex_slot, base_layer, fcurves): pos_fcurves = [i for i in fcurves if i.data_path.find("offset") != -1] scale_fcurves = [i for i in fcurves if i.data_path.find("scale") != -1] @@ -290,7 +302,7 @@ class MaterialConverter: ctrl = self._exporter().animation.make_matrix44_controller(pos_fcurves, scale_fcurves, tex_slot.offset, tex_slot.scale) return ctrl - def _export_texture_type_environment_map(self, bo, hsgmat, layer, slot): + def _export_texture_type_environment_map(self, bo, layer, slot): """Exports a Blender EnvironmentMapTexture to a plLayer""" texture = slot.texture @@ -300,7 +312,7 @@ class MaterialConverter: pl_env = plDynamicCamMap else: pl_env = plDynamicEnvMap - pl_env = self.export_dynamic_env(bo, hsgmat, layer, texture, pl_env) + pl_env = self.export_dynamic_env(bo, layer, texture, pl_env) else: # We should really export a CubicEnvMap here, but we have a good setup for DynamicEnvMaps # that create themselves when the explorer links in, so really... who cares about CEMs? @@ -309,7 +321,7 @@ class MaterialConverter: layer.state.shadeFlags |= hsGMatState.kShadeEnvironMap layer.texture = pl_env.key - def export_dynamic_env(self, bo, hsgmat, layer, texture, pl_class): + def export_dynamic_env(self, bo, layer, texture, 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... bl_env = texture.environment_map @@ -408,7 +420,7 @@ class MaterialConverter: return pl_env - def _export_texture_type_image(self, bo, hsgmat, layer, slot): + def _export_texture_type_image(self, bo, layer, slot): """Exports a Blender ImageTexture to a plLayer""" texture = slot.texture @@ -457,7 +469,7 @@ class MaterialConverter: print(" Found another user of '{}'".format(texture.image.name)) self._pending[key].append(layer.key) - def _export_texture_type_none(self, bo, hsgmat, layer, texture): + def _export_texture_type_none(self, bo, layer, texture): # We'll allow this, just for sanity's sake... pass diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 02e90e2..2bf5a9e 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -135,7 +135,7 @@ class MeshConverter: for i in permaLights: geospan.addPermaLight(i) for i in permaProjs: - geospan.addPermaProjs(i) + geospan.addPermaProj(i) # If this object has a CI, we don't need xforms here... if self._exporter().has_coordiface(bo): diff --git a/korman/exporter/rtlight.py b/korman/exporter/rtlight.py index 2ff63e8..ec7495c 100644 --- a/korman/exporter/rtlight.py +++ b/korman/exporter/rtlight.py @@ -37,27 +37,6 @@ class LightConverter: "SUN": self._convert_sun_lamp, } - def _convert_point_lamp(self, bl, pl): - print(" [OmniLightInfo '{}']".format(bl.name)) - self._convert_attenuation(bl, pl) - - def _convert_spot_lamp(self, bl, pl): - print(" [SpotLightInfo '{}']".format(bl.name)) - self._convert_attenuation(bl, pl) - - # Spot lights have a few more things... - spot_size = bl.spot_size - pl.spotOuter = spot_size - - blend = max(0.001, bl.spot_blend) - pl.spotInner = spot_size - (blend*spot_size) - - if bl.use_halo: - pl.falloff = bl.halo_intensity - else: - pl.falloff = 1.0 - - def _convert_attenuation(self, bl, pl): intens = bl.energy if intens < 0: @@ -85,6 +64,27 @@ class LightConverter: else: raise BlenderOptionNotSupportedError(bl.falloff_type) + + def _convert_point_lamp(self, bl, pl): + print(" [OmniLightInfo '{}']".format(bl.name)) + self._convert_attenuation(bl, pl) + + def _convert_spot_lamp(self, bl, pl): + print(" [SpotLightInfo '{}']".format(bl.name)) + self._convert_attenuation(bl, pl) + + # Spot lights have a few more things... + spot_size = bl.spot_size + pl.spotOuter = spot_size + + blend = max(0.001, bl.spot_blend) + pl.spotInner = spot_size - (blend*spot_size) + + if bl.use_halo: + pl.falloff = bl.halo_intensity + else: + pl.falloff = 1.0 + def _convert_sun_lamp(self, bl, pl): print(" [DirectionalLightInfo '{}']".format(bl.name)) @@ -162,9 +162,51 @@ class LightConverter: sv_key = sv_mod.get_key(self._exporter()) pl_light.softVolume = sv_key + # Is this a projector? + projectors = tuple(self.get_projectors(bl_light)) + if projectors: + self._export_rt_projector(bo, pl_light, projectors) + # *Sigh* pl_light.sceneNode = self.mgr.get_scene_node(location=so.key.location) + def _export_rt_projector(self, bo, pl_light, tex_slots): + mat = self._exporter().mesh.material + slot = tex_slots[0] + + # There is a Material available in the caller, but that is for the parent Mesh. We are a + # projection Lamp with our own faux Material. Unfortunately, Plasma only supports projecting + # one layer. We could exploit the fUnderLay and fOverLay system to export everything, but meh. + if len(tex_slots) > 1: + self._exporter().warn("Only one texture slot can be exported per Lamp. Picking the first one: '{}'".format(slot.name), indent=3) + layer = mat.export_texture_slot(bo, None, None, tex_slots, 0, blend_flags=False) + state = layer.state + + # Colors science'd from PRPs + layer.preshade = hsColorRGBA(0.5, 0.5, 0.5) + layer.runtime = hsColorRGBA(0.5, 0.5, 0.5) + + # Props for projectors... + # Note that we tell the material exporter to (try not to) do any blend flags for us + layer.UVWSrc |= plLayer.kUVWPosition + if bo.data.type == "SPOT": + state.miscFlags |= hsGMatState.kMiscPerspProjection + else: + state.miscFlags |= hsGMatState.kMiscOrthoProjection + state.ZFlags |= hsGMatState.kZNoZWrite + pl_light.setProperty(plLightInfo.kLPMovable, True) + pl_light.setProperty(plLightInfo.kLPCastShadows, False) + + if slot.blend_type == "ADD": + state.blendFlags |= hsGMatState.kBlendAdd + pl_light.setProperty(plLightInfo.kLPOverAll, True) + elif slot.blend_type == "MULTIPLY": + # From PlasmaMAX + state.blendFlags |= hsGMatState.kBlendMult | hsGMatState.kBlendInvertColor | hsGMatState.kBlendInvertFinalColor + pl_light.setProperty(plLightInfo.kLPOverAll, True) + + pl_light.projection = layer.key + def find_material_light_keys(self, bo, bm): """Given a blender material, we find the keys of all matching Plasma RT Lights. NOTE: We return a tuple of lists: ([permaLights], [permaProjs])""" @@ -204,9 +246,7 @@ class LightConverter: pl_light = self.get_light_key(obj, lamp, None) if self._is_projection_lamp(lamp): print(" [{}] PermaProj '{}'".format(lamp.type, obj.name)) - permaProj.append(pl_light) - # TODO: run this through the material exporter... - # need to do some work to make the texture slot code not assume it's working with a material + permaProjs.append(pl_light) else: print(" [{}] PermaLight '{}'".format(lamp.type, obj.name)) permaLights.append(pl_light) @@ -220,6 +260,11 @@ class LightConverter: except LookupError: raise BlenderOptionNotSupportedError("Object ('{}') lamp type '{}'".format(bo.name, bl_light.type)) + def get_projectors(self, bl_light): + for tex in bl_light.texture_slots: + if tex is not None and tex.texture is not None: + yield tex + def _is_projection_lamp(self, bl_light): for tex in bl_light.texture_slots: if tex is None or tex.texture is None: diff --git a/korman/properties/modifiers/water.py b/korman/properties/modifiers/water.py index dd653cf..2b41733 100644 --- a/korman/properties/modifiers/water.py +++ b/korman/properties/modifiers/water.py @@ -117,7 +117,7 @@ class PlasmaWaterModifier(PlasmaModifierProperties, bpy.types.PropertyGroup): raise ExportError("{}: Texture '{}' is not an ENVIRONMENT MAP".format(self.key_name, self.envmap_name)) # maybe, just maybe, we're absuing our privledges? - dem = exporter.mesh.material.export_dynamic_env(bo, None, None, texture, plDynamicEnvMap) + dem = exporter.mesh.material.export_dynamic_env(bo, None, texture, plDynamicEnvMap) waveset.envMap = dem.key state.envCenter = dem.position state.envRefresh = dem.refreshRate