Browse Source

Implement decal printing.

This allows for objects to print decals at runtime, like the baskets in
Eder Gira. Also, the same functionality can be hijacked for coincident
objects to exist as decals. This is basically a shortcut for adding hack
Z-flags to the base layer of a decal material.
pull/172/head
Adam Johnson 5 years ago
parent
commit
211a9dafee
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 23
      korman/exporter/decal.py
  2. 24
      korman/exporter/material.py
  3. 5
      korman/properties/modifiers/base.py
  4. 74
      korman/properties/modifiers/render.py
  5. 4
      korman/properties/modifiers/water.py
  6. 4
      korman/properties/prop_scene.py
  7. 26
      korman/ui/modifiers/render.py

23
korman/exporter/decal.py

@ -61,6 +61,29 @@ class DecalConverter:
if key.location == so_loc and getattr(decal_mgr, "waveSet", None) == waveset: if key.location == so_loc and getattr(decal_mgr, "waveSet", None) == waveset:
decal_mgr.addTarget(so_key) decal_mgr.addTarget(so_key)
def export_active_print_shape(self, print_shape, decal_name):
decal_mgrs = self._decal_managers.get(decal_name)
if decal_mgrs is None:
raise ExportError("'{}': Invalid decal manager '{}'", print_shape.key.name, decal_name)
for i in decal_mgrs:
print_shape.addDecalMgr(i)
def export_static_decal(self, bo):
mat_mgr = self._exporter().mesh.material
mat_keys = mat_mgr.get_materials(bo)
if not mat_keys:
raise ExportError("'{}': Cannot print decal onto object with no materials", bo.name)
zFlags = hsGMatState.kZIncLayer | hsGMatState.kZNoZWrite
for material in (i.object for i in mat_keys):
# Only useful in a debugging context
material.compFlags |= hsGMaterial.kCompDecal
# zFlags should only be applied to the material's base layer
# note: changing blend flags is unsafe here -- so don't even think about it!
layer = mat_mgr.get_base_layer(material)
layer.state.ZFlags |= zFlags
def generate_dynamic_decal(self, bo, decal_name): def generate_dynamic_decal(self, bo, decal_name):
decal = next((i for i in bpy.context.scene.plasma_scene.decal_managers if i.name == decal_name), None) decal = next((i for i in bpy.context.scene.plasma_scene.decal_managers if i.name == decal_name), None)
if decal is None: if decal is None:

24
korman/exporter/material.py

@ -180,9 +180,9 @@ class MaterialConverter:
"""Exports a Blender Material as an hsGMaterial""" """Exports a Blender Material as an hsGMaterial"""
# Sometimes, a material might need to be single-use. Right now, the most apparent example # Sometimes, a material might need to be single-use. Right now, the most apparent example
# of that situation is when a lightmap image is baked. Wavesets are in the same boat, but # of that situation is when a lightmap image is baked. There are others, but as of right now,
# that's a special case as of the writing of this code. # it can all be determined by what mods are attached.
single_user = self._requires_single_user_material(bo, bm) single_user = any((i.copy_material for i in bo.plasma_modifiers.modifiers))
if single_user: if single_user:
mat_name = "{}_AutoSingle".format(bm.name) if bo.name == bm.name else "{}_{}".format(bo.name, bm.name) mat_name = "{}_AutoSingle".format(bm.name) if bo.name == bm.name else "{}_{}".format(bo.name, bm.name)
self._report.msg("Exporting Material '{}' as single user '{}'", bm.name, mat_name, indent=1) self._report.msg("Exporting Material '{}' as single user '{}'", bm.name, mat_name, indent=1)
@ -1151,6 +1151,16 @@ class MaterialConverter:
def get_materials(self, bo): def get_materials(self, bo):
return self._obj2mat.get(bo, []) return self._obj2mat.get(bo, [])
def get_base_layer(self, hsgmat):
try:
layer = hsgmat.layers[0].object
except IndexError:
return None
else:
while layer.underLay is not None:
layer = layer.underLay.object
return layer
def get_bump_layer(self, bo): def get_bump_layer(self, bo):
return self._bump_mats.get(bo, None) return self._bump_mats.get(bo, None)
@ -1210,14 +1220,6 @@ class MaterialConverter:
def _report(self): def _report(self):
return self._exporter().report return self._exporter().report
def _requires_single_user_material(self, bo, bm):
modifiers = bo.plasma_modifiers
if modifiers.lightmap.bake_lightmap:
return True
if modifiers.water_basic.enabled:
return True
return False
def _test_image_alpha(self, image): def _test_image_alpha(self, image):
"""Tests to see if this image has any alpha data""" """Tests to see if this image has any alpha data"""

5
korman/properties/modifiers/base.py

@ -19,6 +19,11 @@ from bpy.props import *
from contextlib import contextmanager from contextlib import contextmanager
class PlasmaModifierProperties(bpy.types.PropertyGroup): class PlasmaModifierProperties(bpy.types.PropertyGroup):
@property
def copy_material(self):
"""Materials MUST be single-user"""
return False
def created(self): def created(self):
pass pass

74
korman/properties/modifiers/render.py

@ -34,7 +34,70 @@ class PlasmaDecalManagerRef(bpy.types.PropertyGroup):
options=set()) options=set())
class PlasmaDecalReceiveMod(PlasmaModifierProperties): class PlasmaDecalMod:
def _iter_decals(self, func):
for decal_ref in self.managers:
if decal_ref.enabled:
func(decal_ref.name)
@classmethod
def register(cls):
cls.managers = CollectionProperty(type=PlasmaDecalManagerRef)
cls.active_manager_index = IntProperty(options={"HIDDEN"})
class PlasmaDecalPrintMod(PlasmaDecalMod, PlasmaModifierProperties):
pl_id = "decal_print"
bl_category = "Render"
bl_label = "Print Decal"
bl_description = "Prints a decal onto an object"
decal_type = EnumProperty(name="Decal Type",
description="Type of decal to print onto another object",
items=[("DYNAMIC", "Dynamic", "This object prints a decal onto dynamic decal surfaces"),
("STATIC", "Static", "This object is a decal itself")],
options=set())
# Dynamic Decals
length = FloatProperty(name="Length",
min=0.1, soft_max=30.0, precision=2,
default=0.45,
options=set())
width = FloatProperty(name="Width",
min=0.1, soft_max=30.0, precision=2,
default=0.9,
options=set())
height = FloatProperty(name="Height",
min=0.1, soft_max=30.0, precision=2,
default=1.0,
options=set())
@property
def copy_material(self):
return self.decal_type == "STATIC"
def get_key(self, exporter, so):
if self.decal_type == "DYNAMIC":
pClass = plActivePrintShape if any((i.enabled for i in self.managers)) else plPrintShape
return exporter.mgr.find_create_key(pClass, so=so)
def export(self, exporter, bo, so):
if self.decal_type == "STATIC":
exporter.decal.export_static_decal(bo)
elif self.decal_type == "DYNAMIC":
print_shape = self.get_key(exporter, so).object
print_shape.length = self.length
print_shape.width = self.width
print_shape.height = self.height
def post_export(self, exporter, bo, so):
if self.decal_type == "DYNAMIC":
print_shape = self.get_key(exporter, so).object
f = functools.partial(exporter.decal.export_active_print_shape, print_shape)
self._iter_decals(f)
class PlasmaDecalReceiveMod(PlasmaDecalMod, PlasmaModifierProperties):
pl_id = "decal_receive" pl_id = "decal_receive"
bl_category = "Render" bl_category = "Render"
@ -44,11 +107,6 @@ class PlasmaDecalReceiveMod(PlasmaModifierProperties):
managers = CollectionProperty(type=PlasmaDecalManagerRef) managers = CollectionProperty(type=PlasmaDecalManagerRef)
active_manager_index = IntProperty(options={"HIDDEN"}) active_manager_index = IntProperty(options={"HIDDEN"})
def _iter_decals(self, func):
for decal_ref in self.managers:
if decal_ref.enabled:
func(decal_ref.name)
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
f = functools.partial(exporter.decal.generate_dynamic_decal, bo) f = functools.partial(exporter.decal.generate_dynamic_decal, bo)
self._iter_decals(f) self._iter_decals(f)
@ -231,6 +289,10 @@ class PlasmaLightMapGen(idprops.IDPropMixin, PlasmaModifierProperties, PlasmaMod
else: else:
return self.bake_type == "lightmap" return self.bake_type == "lightmap"
@property
def copy_material(self):
return self.bake_lightmap
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
# If we're exporting vertex colors, who gives a rat's behind? # If we're exporting vertex colors, who gives a rat's behind?
if not self.bake_lightmap: if not self.bake_lightmap:

4
korman/properties/modifiers/water.py

@ -238,6 +238,10 @@ class PlasmaWaterModifier(idprops.IDPropMixin, PlasmaModifierProperties, bpy.typ
min=-10.0, max=10.0, min=-10.0, max=10.0,
default=0.0) default=0.0)
@property
def copy_material(self):
return True
def export(self, exporter, bo, so): def export(self, exporter, bo, so):
waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so) waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so)
if self.wind_object: if self.wind_object:

4
korman/properties/prop_scene.py

@ -15,6 +15,7 @@
import bpy import bpy
from bpy.props import * from bpy.props import *
import itertools
from ..exporter.etlight import _NUM_RENDER_LAYERS from ..exporter.etlight import _NUM_RENDER_LAYERS
@ -48,7 +49,8 @@ class PlasmaDecalManager(bpy.types.PropertyGroup):
prev_value = self.name prev_value = self.name
for i in bpy.data.objects: for i in bpy.data.objects:
decal_receive = i.plasma_modifiers.decal_receive decal_receive = i.plasma_modifiers.decal_receive
for j in decal_receive.managers: decal_print = i.plasma_modifiers.decal_print
for j in itertools.chain(decal_receive.managers, decal_print.managers):
if j.name == prev_value: if j.name == prev_value:
j.name = value j.name = value
self.name = value self.name = value

26
korman/ui/modifiers/render.py

@ -27,6 +27,32 @@ class DecalMgrListUI(bpy.types.UIList):
layout.label("[Empty]") layout.label("[Empty]")
def decal_print(modifier, layout, context):
layout.prop(modifier, "decal_type")
layout = layout.column()
layout.enabled = modifier.decal_type == "DYNAMIC"
layout.label("Dimensions:")
row = layout.row(align=True)
row.prop(modifier, "length")
row.prop(modifier, "width")
row.prop(modifier, "height")
layout.separator()
ui_list.draw_modifier_list(layout, "DecalMgrListUI", modifier, "managers",
"active_manager_index", rows=2, maxrows=3)
try:
mgr_ref = modifier.managers[modifier.active_manager_index]
except:
pass
else:
scene = context.scene.plasma_scene
decal_mgr = next((i for i in scene.decal_managers if i.display_name == mgr_ref), None)
layout.alert = decal_mgr is None
layout.prop_search(mgr_ref, "name", scene, "decal_managers", icon="NONE")
layout.alert = False
def decal_receive(modifier, layout, context): def decal_receive(modifier, layout, context):
ui_list.draw_modifier_list(layout, "DecalMgrListUI", modifier, "managers", ui_list.draw_modifier_list(layout, "DecalMgrListUI", modifier, "managers",
"active_manager_index", rows=2, maxrows=3) "active_manager_index", rows=2, maxrows=3)

Loading…
Cancel
Save