Browse Source

Merge pull request #38 from Hoikas/rtproj

Projection Lights
pull/42/head
Adam Johnson 9 years ago committed by GitHub
parent
commit
9c8b286c07
  1. 46
      korman/exporter/material.py
  2. 2
      korman/exporter/mesh.py
  3. 93
      korman/exporter/rtlight.py
  4. 2
      korman/properties/modifiers/water.py

46
korman/exporter/material.py

@ -104,7 +104,7 @@ class MaterialConverter:
# export many slots in one go. Think stencils. # export many slots in one go. Think stencils.
i = 0 i = 0
while i < len(slots): 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 # 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 # material had no Textures, we will need to initialize a default layer
@ -138,16 +138,18 @@ class MaterialConverter:
# Wasn't that easy? # Wasn't that easy?
return hsgmat.key 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] slot = slots[idx]
num_exported = 1 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)) print(" Exporting Plasma Layer '{}'".format(name))
layer = self._mgr.add_object(plLayer, name=name, bl=bo) layer = self._mgr.add_object(plLayer, name=name, bl=bo)
if bm is not None:
self._propagate_material_settings(bm, layer) self._propagate_material_settings(bm, layer)
# UVW Channel # UVW Channel
if slot.texture_coords == "UV":
for i, uvchan in enumerate(bo.data.uv_layers): for i, uvchan in enumerate(bo.data.uv_layers):
if uvchan.name == slot.uv_layer: if uvchan.name == slot.uv_layer:
layer.UVWSrc = i layer.UVWSrc = i
@ -162,8 +164,12 @@ class MaterialConverter:
xform.setScale(hsVector3(*slot.scale)) xform.setScale(hsVector3(*slot.scale))
layer.transform = xform 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 state = layer.state
if slot.use_stencil: if canStencil:
hsgmat.compFlags |= hsGMaterial.kCompNeedsBlendChannel hsgmat.compFlags |= hsGMaterial.kCompNeedsBlendChannel
state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor state.blendFlags |= hsGMatState.kBlendAlpha | hsGMatState.kBlendAlphaMult | hsGMatState.kBlendNoTexColor
if slot.texture.type == "BLEND": if slot.texture.type == "BLEND":
@ -176,7 +182,7 @@ class MaterialConverter:
if len(slots) == nextIdx: if len(slots) == nextIdx:
raise ExportError("Texture Slot '{}' wants to be a stencil, but there are no more TextureSlots.".format(slot.name)) raise ExportError("Texture Slot '{}' wants to be a stencil, but there are no more TextureSlots.".format(slot.name))
print(" --- BEGIN STENCIL ---") print(" --- BEGIN STENCIL ---")
self._export_texture_slot(bo, bm, hsgmat, slots, nextIdx) self.export_texture_slot(bo, bm, hsgmat, slots, nextIdx)
print(" --- END STENCIL ---") print(" --- END STENCIL ---")
num_exported += 1 num_exported += 1
@ -186,7 +192,7 @@ class MaterialConverter:
prev_state.miscFlags |= hsGMatState.kMiscBindNext | hsGMatState.kMiscRestartPassHere prev_state.miscFlags |= hsGMatState.kMiscBindNext | hsGMatState.kMiscRestartPassHere
if not prev_state.blendFlags & hsGMatState.kBlendMask: if not prev_state.blendFlags & hsGMatState.kBlendMask:
prev_state.blendFlags |= hsGMatState.kBlendAlpha prev_state.blendFlags |= hsGMatState.kBlendAlpha
else: elif blend_flags:
# Standard layer flags ahoy # Standard layer flags ahoy
if slot.blend_type == "ADD": if slot.blend_type == "ADD":
state.blendFlags |= hsGMatState.kBlendAddColorTimesAlpha state.blendFlags |= hsGMatState.kBlendAddColorTimesAlpha
@ -204,18 +210,24 @@ class MaterialConverter:
state.blendFlags |= hsGMatState.kBlendAlphaTestHigh state.blendFlags |= hsGMatState.kBlendAlphaTestHigh
# Export the specific texture type # 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 # 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)
if hsgmat is None:
return layer
else:
hsgmat.addLayer(layer.key) hsgmat.addLayer(layer.key)
return num_exported 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""" """Exports animations on this texture and chains the Plasma layers as needed"""
def harvest_fcurves(bl_id, collection, data_path=None): def harvest_fcurves(bl_id, collection, data_path=None):
if bl_id is None:
return None
anim = bl_id.animation_data anim = bl_id.animation_data
if anim is not None: if anim is not None:
action = anim.action action = anim.action
@ -241,7 +253,7 @@ class MaterialConverter:
# and chain this biotch up as best we can. # and chain this biotch up as best we can.
layer_animation = None layer_animation = None
for attr, converter in self._animation_exporters.items(): 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 ctrl is not None:
if layer_animation is None: if layer_animation is None:
name = "{}_LayerAnim".format(base_layer.key.name) name = "{}_LayerAnim".format(base_layer.key.name)
@ -274,7 +286,7 @@ class MaterialConverter:
# Well, we had some FCurves but they were garbage... Too bad. # Well, we had some FCurves but they were garbage... Too bad.
return base_layer 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: for i in fcurves:
if i.data_path == "plasma_layer.opacity": if i.data_path == "plasma_layer.opacity":
base_layer.state.blendFlags |= hsGMatState.kBlendAlpha base_layer.state.blendFlags |= hsGMatState.kBlendAlpha
@ -282,7 +294,7 @@ class MaterialConverter:
return ctrl return ctrl
return None 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] 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] 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) ctrl = self._exporter().animation.make_matrix44_controller(pos_fcurves, scale_fcurves, tex_slot.offset, tex_slot.scale)
return ctrl 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""" """Exports a Blender EnvironmentMapTexture to a plLayer"""
texture = slot.texture texture = slot.texture
@ -300,7 +312,7 @@ class MaterialConverter:
pl_env = plDynamicCamMap pl_env = plDynamicCamMap
else: else:
pl_env = plDynamicEnvMap 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: else:
# We should really export a CubicEnvMap here, but we have a good setup for DynamicEnvMaps # 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? # 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.state.shadeFlags |= hsGMatState.kShadeEnvironMap
layer.texture = pl_env.key 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 # 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... # viewpoint object has not already been exported...
bl_env = texture.environment_map bl_env = texture.environment_map
@ -408,7 +420,7 @@ class MaterialConverter:
return pl_env 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""" """Exports a Blender ImageTexture to a plLayer"""
texture = slot.texture texture = slot.texture
@ -457,7 +469,7 @@ class MaterialConverter:
print(" Found another user of '{}'".format(texture.image.name)) print(" Found another user of '{}'".format(texture.image.name))
self._pending[key].append(layer.key) 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... # We'll allow this, just for sanity's sake...
pass pass

2
korman/exporter/mesh.py

@ -135,7 +135,7 @@ class MeshConverter:
for i in permaLights: for i in permaLights:
geospan.addPermaLight(i) geospan.addPermaLight(i)
for i in permaProjs: for i in permaProjs:
geospan.addPermaProjs(i) geospan.addPermaProj(i)
# If this object has a CI, we don't need xforms here... # If this object has a CI, we don't need xforms here...
if self._exporter().has_coordiface(bo): if self._exporter().has_coordiface(bo):

93
korman/exporter/rtlight.py

@ -37,27 +37,6 @@ class LightConverter:
"SUN": self._convert_sun_lamp, "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): def _convert_attenuation(self, bl, pl):
intens = bl.energy intens = bl.energy
if intens < 0: if intens < 0:
@ -85,6 +64,27 @@ class LightConverter:
else: else:
raise BlenderOptionNotSupportedError(bl.falloff_type) 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): def _convert_sun_lamp(self, bl, pl):
print(" [DirectionalLightInfo '{}']".format(bl.name)) print(" [DirectionalLightInfo '{}']".format(bl.name))
@ -162,9 +162,51 @@ class LightConverter:
sv_key = sv_mod.get_key(self._exporter()) sv_key = sv_mod.get_key(self._exporter())
pl_light.softVolume = sv_key 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* # *Sigh*
pl_light.sceneNode = self.mgr.get_scene_node(location=so.key.location) 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): def find_material_light_keys(self, bo, bm):
"""Given a blender material, we find the keys of all matching Plasma RT Lights. """Given a blender material, we find the keys of all matching Plasma RT Lights.
NOTE: We return a tuple of lists: ([permaLights], [permaProjs])""" NOTE: We return a tuple of lists: ([permaLights], [permaProjs])"""
@ -204,9 +246,7 @@ class LightConverter:
pl_light = self.get_light_key(obj, lamp, None) pl_light = self.get_light_key(obj, lamp, None)
if self._is_projection_lamp(lamp): if self._is_projection_lamp(lamp):
print(" [{}] PermaProj '{}'".format(lamp.type, obj.name)) print(" [{}] PermaProj '{}'".format(lamp.type, obj.name))
permaProj.append(pl_light) permaProjs.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
else: else:
print(" [{}] PermaLight '{}'".format(lamp.type, obj.name)) print(" [{}] PermaLight '{}'".format(lamp.type, obj.name))
permaLights.append(pl_light) permaLights.append(pl_light)
@ -220,6 +260,11 @@ class LightConverter:
except LookupError: except LookupError:
raise BlenderOptionNotSupportedError("Object ('{}') lamp type '{}'".format(bo.name, bl_light.type)) 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): def _is_projection_lamp(self, bl_light):
for tex in bl_light.texture_slots: for tex in bl_light.texture_slots:
if tex is None or tex.texture is None: if tex is None or tex.texture is None:

2
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)) raise ExportError("{}: Texture '{}' is not an ENVIRONMENT MAP".format(self.key_name, self.envmap_name))
# maybe, just maybe, we're absuing our privledges? # 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 waveset.envMap = dem.key
state.envCenter = dem.position state.envCenter = dem.position
state.envRefresh = dem.refreshRate state.envRefresh = dem.refreshRate

Loading…
Cancel
Save