diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 4b973e4..d2a2ba9 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -187,7 +187,9 @@ class MaterialConverter: self._report.msg("Exporting Material '{}' as single user '{}'", bm.name, mat_name, indent=1) hgmat = None else: - mat_name = bm.name + # Ensure that RT-lit objects don't infect the static-lit objects. + mat_prefix = "RTLit_" if bo.plasma_modifiers.lighting.rt_lights else "" + mat_name = "".join((mat_prefix, bm.name)) self._report.msg("Exporting Material '{}'", mat_name, indent=1) hsgmat = self._mgr.find_key(hsGMaterial, name=mat_name, bl=bo) if hsgmat is not None: @@ -222,7 +224,8 @@ class MaterialConverter: if slot.use_stencil: stencils.append((idx, slot)) else: - tex_layer = self.export_texture_slot(bo, bm, hsgmat, slot, idx) + tex_name = "{}_{}".format(mat_name, slot.name) + tex_layer = self.export_texture_slot(bo, bm, hsgmat, slot, idx, name=tex_name) if restart_pass_next: tex_layer.state.miscFlags |= hsGMatState.kMiscRestartPassHere restart_pass_next = False @@ -249,7 +252,7 @@ class MaterialConverter: # 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 if not hsgmat.layers: - layer = self._mgr.find_create_object(plLayer, name="{}_AutoLayer".format(bm.name), bl=bo) + layer = self._mgr.find_create_object(plLayer, name="{}_AutoLayer".format(mat_name), bl=bo) self._propagate_material_settings(bo, bm, layer) hsgmat.addLayer(layer.key) @@ -349,7 +352,7 @@ class MaterialConverter: return hsgmat.key def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx): - name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name) + name = "{}_{}".format(hsgmat.key.name, slot.name) self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2) # Okay, now we need to make 3 layers for the Du, Dw, and Dv @@ -1163,6 +1166,30 @@ class MaterialConverter: def get_bump_layer(self, bo): return self._bump_mats.get(bo, None) + def get_material_ambient(self, bo, bm) -> hsColorRGBA: + emit_scale = bm.emit * 0.5 + if emit_scale > 0.0: + return hsColorRGBA(bm.diffuse_color.r * emit_scale, + bm.diffuse_color.g * emit_scale, + bm.diffuse_color.b * emit_scale, + 1.0) + else: + return utils.color(bpy.context.scene.world.ambient_color) + + def get_material_preshade(self, bo, bm, color=None) -> hsColorRGBA: + if bo.plasma_modifiers.lighting.rt_lights: + return hsColorRGBA.kBlack + if color is None: + color = bm.diffuse_color + return utils.color(color) + + def get_material_runtime(self, bo, bm, color=None) -> hsColorRGBA: + if not bo.plasma_modifiers.lighting.rt_lights: + return hsColorRGBA.kBlack + if color is None: + color = bm.diffuse_color + return utils.color(color) + def get_texture_animation_key(self, bo, bm, texture): """Finds or creates the appropriate key for sending messages to an animated Texture""" @@ -1204,23 +1231,17 @@ class MaterialConverter: if bm.use_shadeless: state.shadeFlags |= hsGMatState.kShadeWhite + if bm.emit: + state.shadeFlags |= hsGMatState.kShadeEmissive + # Colors - layer.ambient = utils.color(bpy.context.scene.world.ambient_color) - layer.preshade = utils.color(bm.diffuse_color) - layer.runtime = utils.color(bm.diffuse_color) + layer.ambient = self.get_material_ambient(bo, bm) + layer.preshade = self.get_material_preshade(bo, bm) + layer.runtime = self.get_material_runtime(bo, bm) layer.specular = utils.color(bm.specular_color) layer.specularPower = min(100.0, float(bm.specular_hardness)) - layer.LODBias = -1.0 # Seems to be the Plasma default - - if bm.emit > 0.0: - # Use the diffuse colour as the emit, scaled by the emit amount - # (maximum 2.0, so we'll also scale that by 0.5) - emit_scale = bm.emit * 0.5 - layer.ambient = hsColorRGBA(bm.diffuse_color.r * emit_scale, - bm.diffuse_color.g * emit_scale, - bm.diffuse_color.b * emit_scale, - 1.0) + layer.LODBias = -1.0 def _requires_single_user(self, bo, bm): if bo.data.show_double_sided: diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index b0a5da2..e629395 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -14,6 +14,7 @@ # along with Korman. If not, see . import bpy +import itertools from PyHSPlasma import * from math import fabs import weakref @@ -29,6 +30,29 @@ _WARN_VERTS_PER_SPAN = 0x8000 _VERTEX_COLOR_LAYERS = {"col", "color", "colour"} +class _GeoSpan: + def __init__(self, bo, bm, geospan, pass_index=None): + self.geospan = geospan + self.pass_index = pass_index if pass_index is not None else 0 + self.mult_color = self._determine_mult_color(bo, bm) + + def _determine_mult_color(self, bo, bm): + """Determines the color all vertex colors should be multipled by in this span.""" + if self.geospan.props & plGeometrySpan.kDiffuseFoldedIn: + color = bm.diffuse_color + base_layer = self._find_bottom_of_stack() + return (color.r, color.b, color.g, base_layer.opacity) + if not bo.plasma_modifiers.lighting.preshade: + return (0.0, 0.0, 0.0, 0.0) + return (1.0, 1.0, 1.0, 1.0) + + def _find_bottom_of_stack(self) -> plLayerInterface: + base_layer = self.geospan.material.object.layers[0].object + while base_layer.underLay is not None: + base_layer = base_layer.underLay.object + return base_layer + + class _RenderLevel: MAJOR_OPAQUE = 0 MAJOR_FRAMEBUF = 1 @@ -40,16 +64,12 @@ class _RenderLevel: _MINOR_MASK = ((1 << _MAJOR_SHIFT) - 1) def __init__(self, bo, pass_index, blend_span=False): - self.level = 0 - if pass_index > 0: - self.major = self.MAJOR_FRAMEBUF - self.minor = pass_index * 4 + if blend_span: + self.level = self._determine_level(bo, blend_span) else: - self.major = self.MAJOR_BLEND if blend_span else self.MAJOR_OPAQUE - - # We use the blender material's pass index (which we stashed in the hsGMaterial) to increment - # the render pass, just like it says... - self.level += pass_index + self.level = 0 + # Gulp... Hope you know what you're doing... + self.minor += pass_index * 4 def __eq__(self, other): return self.level == other.level @@ -60,15 +80,38 @@ class _RenderLevel: def _get_major(self): return self.level >> self._MAJOR_SHIFT def _set_major(self, value): - self.level = ((value << self._MAJOR_SHIFT) & 0xFFFFFFFF) | self.minor + self.level = self._calc_level(value, self.minor) major = property(_get_major, _set_major) def _get_minor(self): return self.level & self._MINOR_MASK def _set_minor(self, value): - self.level = ((self.major << self._MAJOR_SHIFT) & 0xFFFFFFFF) | value + self.level = self._calc_level(self.major, value) minor = property(_get_minor, _set_minor) + def _calc_level(self, major : int, minor : int=0) -> int: + return ((major << self._MAJOR_SHIFT) & 0xFFFFFFFF) | minor + + def _determine_level(self, bo : bpy.types.Object, blend_span : bool) -> int: + mods = bo.plasma_modifiers + if mods.test_property("draw_framebuf"): + return self._calc_level(self.MAJOR_FRAMEBUF) + elif mods.test_property("draw_opaque"): + return self._calc_level(self.MAJOR_OPAQUE) + elif mods.test_property("draw_no_defer"): + blend_span = False + + blend_mod = mods.blend + if blend_mod.enabled and blend_mod.has_dependencies: + level = self._calc_level(self.MAJOR_FRAMEBUF) + for i in blend_mod.iter_dependencies(): + level = max(level, self._determine_level(i, blend_span)) + return level + 4 + elif blend_span: + return self._calc_level(self.MAJOR_BLEND) + else: + return self._calc_level(self.MAJOR_DEFAULT) + class _DrawableCriteria: def __init__(self, bo, geospan, pass_index): @@ -96,12 +139,12 @@ class _DrawableCriteria: def _face_sort_allowed(self, bo): # For now, only test the modifiers # This will need to be tweaked further for GUIs... - return not any((i.no_face_sort for i in bo.plasma_modifiers.modifiers)) + return not bo.plasma_modifiers.test_property("no_face_sort") def _span_sort_allowed(self, bo): # For now, only test the modifiers # This will need to be tweaked further for GUIs... - return not any((i.no_face_sort for i in bo.plasma_modifiers.modifiers)) + return not bo.plasma_modifiers.test_property("no_face_sort") @property def span_type(self): @@ -118,7 +161,6 @@ class _GeoData: self.vertices = [] - class _MeshManager: def __init__(self, report=None): if report is not None: @@ -214,15 +256,53 @@ class MeshConverter(_MeshManager): return (num_user_texs, total_texs, max_user_texs) - def _create_geospan(self, bo, mesh, bm, hsgmatKey): + def _check_vtx_alpha(self, mesh, material_idx): + if material_idx is not None: + polygons = (i for i in mesh.polygons if i.material_index == material_idx) + else: + polygons = mesh.polygons + alpha_layer = self._find_vtx_alpha_layer(mesh.vertex_colors) + if alpha_layer is None: + return False + alpha_loops = (alpha_layer[i.loop_start:i.loop_start+i.loop_total] for i in polygons) + opaque = (sum(i.color) == len(i.color) for i in itertools.chain.from_iterable(alpha_loops)) + has_alpha = not all(opaque) + return has_alpha + + def _check_vtx_nonpreshaded(self, bo, mesh, material_idx, base_layer): + def check_layer_shading_animation(layer): + if isinstance(layer, plLayerAnimationBase): + return layer.opacityCtl is not None or layer.preshadeCtl is not None or layer.runtimeCtl is not None + if layer.underLay is not None: + return check_layer_shading_animation(layer.underLay.object) + return False + + # TODO: if this is an avatar, we can't be non-preshaded. + if check_layer_shading_animation(base_layer): + return False + + # Reject emissive and shadeless because the kLiteMaterial equation has lots of options + # that are taken away by VtxNonPreshaded that are useful here. + if material_idx is not None: + bm = mesh.materials[material_idx] + if bm.emit or bm.use_shadeless: + return False + + mods = bo.plasma_modifiers + if mods.lighting.rt_lights: + return True + if mods.lightmap.bake_lightmap: + return True + if self._check_vtx_alpha(mesh, material_idx): + return True + + return False + + def _create_geospan(self, bo, mesh, material_idx, bm, hsgmatKey): """Initializes a plGeometrySpan from a Blender Object and an hsGMaterial""" geospan = plGeometrySpan() geospan.material = hsgmatKey - # Mark us as needing a BlendSpan if the material require blending - if hsgmatKey.object.layers[0].object.state.blendFlags & hsGMatState.kBlendMask: - geospan.props |= plGeometrySpan.kRequiresBlending - # GeometrySpan format # For now, we really only care about the number of UVW Channels user_uvws, total_uvws, max_user_uvws = self._calc_num_uvchans(bo, mesh) @@ -230,10 +310,22 @@ class MeshConverter(_MeshManager): raise explosions.TooManyUVChannelsError(bo, bm, user_uvws, max_user_uvws) geospan.format = total_uvws - # Begin total guesswork WRT flags - mods = bo.plasma_modifiers - if mods.lightmap.enabled: + def is_alpha_blended(layer): + if layer.state.blendFlags & hsGMatState.kBlendMask: + return True + if layer.underLay is not None: + return is_alpha_blended(layer.underLay.object) + return False + + base_layer = hsgmatKey.object.layers[0].object + if is_alpha_blended(base_layer) or self._check_vtx_alpha(mesh, material_idx): + geospan.props |= plGeometrySpan.kRequiresBlending + if self._check_vtx_nonpreshaded(bo, mesh, material_idx, base_layer): geospan.props |= plGeometrySpan.kLiteVtxNonPreshaded + if (geospan.props & plGeometrySpan.kLiteMask) != plGeometrySpan.kLiteMaterial: + geospan.props |= plGeometrySpan.kDiffuseFoldedIn + + mods = bo.plasma_modifiers if mods.lighting.rt_lights: geospan.props |= plGeometrySpan.kPropRunTimeLight if not bm.use_shadows: @@ -275,7 +367,7 @@ class MeshConverter(_MeshManager): dspan.composeGeometry(True, True) inc_progress() - def _export_geometry(self, bo, mesh, materials, geospans): + def _export_geometry(self, bo, mesh, materials, geospans, mat2span_LUT): # Recall that materials is a mapping of exported materials to blender material indices. # Therefore, geodata maps blender material indices to working geometry data. # Maybe the logic is a bit inverted, but it keeps the inner loop simple. @@ -284,16 +376,8 @@ class MeshConverter(_MeshManager): # Locate relevant vertex color layers now... lm = bo.plasma_modifiers.lightmap - has_vtx_alpha = False - color, alpha = None, None - for vcol_layer in mesh.tessface_vertex_colors: - name = vcol_layer.name.lower() - if name in _VERTEX_COLOR_LAYERS: - color = vcol_layer.data - elif name == "autocolor" and color is None and not lm.bake_lightmap: - color = vcol_layer.data - elif name == "alpha": - alpha = vcol_layer.data + color = None if lm.bake_lightmap else self._find_vtx_color_layer(mesh.tessface_vertex_colors) + alpha = self._find_vtx_alpha_layer(mesh.tessface_vertex_colors) # Convert Blender faces into things we can stuff into libHSPlasma for i, tessface in enumerate(mesh.tessfaces): @@ -323,10 +407,8 @@ class MeshConverter(_MeshManager): else: src = alpha[i] # average color becomes the alpha value - tessface_alphas = (((src.color1[0] + src.color1[1] + src.color1[2]) / 3), - ((src.color2[0] + src.color2[1] + src.color2[2]) / 3), - ((src.color3[0] + src.color3[1] + src.color3[2]) / 3), - ((src.color4[0] + src.color4[1] + src.color4[2]) / 3)) + tessface_alphas = ((sum(src.color1) / 3), (sum(src.color2) / 3), + (sum(src.color3) / 3), (sum(src.color4) / 3)) if bumpmap is not None: gradPass = [] @@ -356,10 +438,16 @@ class MeshConverter(_MeshManager): for j, vertex in enumerate(tessface.vertices): uvws = tuple([uvw[j] for uvw in tessface_uvws]) - # Grab VCols - vertex_color = (int(tessface_colors[j][0] * 255), int(tessface_colors[j][1] * 255), - int(tessface_colors[j][2] * 255), int(tessface_alphas[j] * 255)) - has_vtx_alpha |= bool(tessface_alphas[j] < 1.0) + # Calculate vertex colors. + if mat2span_LUT: + mult_color = geospans[mat2span_LUT[tessface.material_index]].mult_color + else: + mult_color = (1.0, 1.0, 1.0, 1.0) + tessface_color, tessface_alpha = tessface_colors[j], tessface_alphas[j] + vertex_color = (int(tessface_color[0] * mult_color[0] * 255), + int(tessface_color[1] * mult_color[1] * 255), + int(tessface_color[2] * mult_color[2] * 255), + int(tessface_alpha * mult_color[0] * 255)) # Now, we'll index into the vertex dict using the per-face elements :( # We're using tuples because lists are not hashable. The many mathutils and PyHSPlasma @@ -416,7 +504,7 @@ class MeshConverter(_MeshManager): # Time to finish it up... for i, data in enumerate(geodata.values()): - geospan = geospans[i][0] + geospan = geospans[i].geospan numVerts = len(data.vertices) numUVs = geospan.format & plGeometrySpan.kUVCountMask @@ -437,10 +525,6 @@ class MeshConverter(_MeshManager): uvMap[numUVs - 1].normalize() vtx.uvs = uvMap - # Mark us for blending if we have a alpha vertex paint layer - if has_vtx_alpha: - geospan.props |= plGeometrySpan.kRequiresBlending - # If we're still here, let's add our data to the GeometrySpan geospan.indices = data.triangles geospan.vertices = data.vertices @@ -523,18 +607,18 @@ class MeshConverter(_MeshManager): return None # Step 1: Export all of the doggone materials. - geospans = self._export_material_spans(bo, mesh, materials) + geospans, mat2span_LUT = self._export_material_spans(bo, mesh, materials) # Step 2: Export Blender mesh data to Plasma GeometrySpans - self._export_geometry(bo, mesh, materials, geospans) + self._export_geometry(bo, mesh, materials, geospans, mat2span_LUT) # Step 3: Add plGeometrySpans to the appropriate DSpan and create indices _diindices = {} - for geospan, pass_index in geospans: - dspan = self._find_create_dspan(bo, geospan, pass_index) + for i in geospans: + dspan = self._find_create_dspan(bo, i.geospan, i.pass_index) self._report.msg("Exported hsGMaterial '{}' geometry into '{}'", - geospan.material.name, dspan.key.name, indent=1) - idx = dspan.addSourceSpan(geospan) + i.geospan.material.name, dspan.key.name, indent=1) + idx = dspan.addSourceSpan(i.geospan) diidx = _diindices.setdefault(dspan, []) diidx.append(idx) @@ -554,20 +638,25 @@ class MeshConverter(_MeshManager): if len(materials) > 1: msg = "'{}' is a WaveSet -- only one material is supported".format(bo.name) self._exporter().report.warn(msg, indent=1) - matKey = self.material.export_waveset_material(bo, materials[0][1]) - geospan = self._create_geospan(bo, mesh, materials[0][1], matKey) + blmat = materials[0][1] + matKey = self.material.export_waveset_material(bo, blmat) + geospan = self._create_geospan(bo, mesh, None, blmat, matKey) # FIXME: Can some of this be generalized? geospan.props |= (plGeometrySpan.kWaterHeight | plGeometrySpan.kLiteVtxNonPreshaded | plGeometrySpan.kPropReverseSort | plGeometrySpan.kPropNoShadow) geospan.waterHeight = bo.location[2] - return [(geospan, 0)] + return [_GeoSpan(bo, blmat, geospan)], None else: geospans = [None] * len(materials) - for i, (_, blmat) in enumerate(materials): + mat2span_LUT = {} + for i, (blmat_idx, blmat) in enumerate(materials): matKey = self.material.export_material(bo, blmat) - geospans[i] = (self._create_geospan(bo, mesh, blmat, matKey), blmat.pass_index) - return geospans + geospans[i] = _GeoSpan(bo, blmat, + self._create_geospan(bo, mesh, blmat_idx, blmat, matKey), + blmat.pass_index) + mat2span_LUT[blmat_idx] = i + return geospans, mat2span_LUT def _find_create_dspan(self, bo, geospan, pass_index): location = self._mgr.get_location(bo) @@ -603,6 +692,21 @@ class MeshConverter(_MeshManager): else: return self._dspans[location][crit] + def _find_vtx_alpha_layer(self, color_collection): + alpha_layer = next((i for i in color_collection if i.name.lower() == "alpha"), None) + if alpha_layer is not None: + return alpha_layer.data + return None + + def _find_vtx_color_layer(self, color_collection): + manual_layer = next((i for i in color_collection if i.name.lower() in _VERTEX_COLOR_LAYERS), None) + if manual_layer is not None: + return manual_layer.data + baked_layer = color_collection.get("autocolor") + if baked_layer is not None: + return baked_layer.data + return None + @property def _mgr(self): return self._exporter().mgr diff --git a/korman/idprops.py b/korman/idprops.py index 66328d2..b7c55a6 100644 --- a/korman/idprops.py +++ b/korman/idprops.py @@ -127,6 +127,9 @@ def poll_animated_objects(self, value): def poll_camera_objects(self, value): return value.type == "CAMERA" +def poll_drawable_objects(self, value): + return value.type == "MESH" and any(value.data.materials) + def poll_empty_objects(self, value): return value.type == "EMPTY" diff --git a/korman/properties/modifiers/__init__.py b/korman/properties/modifiers/__init__.py index f4905d3..ba179df 100644 --- a/korman/properties/modifiers/__init__.py +++ b/korman/properties/modifiers/__init__.py @@ -66,6 +66,10 @@ class PlasmaModifiers(bpy.types.PropertyGroup): setattr(cls, i.pl_id, bpy.props.PointerProperty(type=i)) bpy.types.Object.plasma_modifiers = bpy.props.PointerProperty(type=cls) + def test_property(self, property : str) -> bool: + """Tests a property on all enabled Plasma modifiers""" + return any((getattr(i, property) for i in self.modifiers)) + class PlasmaModifierSpec(bpy.types.PropertyGroup): pass diff --git a/korman/properties/modifiers/base.py b/korman/properties/modifiers/base.py index 5f4ea34..0f52486 100644 --- a/korman/properties/modifiers/base.py +++ b/korman/properties/modifiers/base.py @@ -30,10 +30,30 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup): def destroyed(self): pass + @property + def draw_opaque(self): + """Render geometry before the avatar""" + return False + + @property + def draw_framebuf(self): + """Render geometry after the avatar but before other blended geometry""" + return False + + @property + def draw_no_defer(self): + """Disallow geometry being sorted into a blending span""" + return False + @property def enabled(self): return self.display_order >= 0 + @property + def face_sort(self): + """Indicates that the geometry's faces should be sorted by the engine""" + return False + def harvest_actors(self): return () diff --git a/korman/properties/modifiers/render.py b/korman/properties/modifiers/render.py index 856840c..3683039 100644 --- a/korman/properties/modifiers/render.py +++ b/korman/properties/modifiers/render.py @@ -25,6 +25,98 @@ from ...exporter import utils from ...exporter.explosions import ExportError from ... import idprops +class PlasmaBlendOntoObject(bpy.types.PropertyGroup): + blend_onto = PointerProperty(name="Blend Onto", + description="Object to render first", + options=set(), + type=bpy.types.Object, + poll=idprops.poll_drawable_objects) + enabled = BoolProperty(name="Enabled", + default=True, + options=set()) + + +class PlasmaBlendMod(PlasmaModifierProperties): + pl_id = "blend" + + bl_category = "Render" + bl_label = "Blending" + bl_description = "Advanced Blending Options" + + render_level = EnumProperty(name="Render Pass", + description="Suggested render pass for this object.", + items=[("AUTO", "(Auto)", "Let Korman decide when to render this object."), + ("OPAQUE", "Before Avatar", "Prefer for the object to draw before the avatar."), + ("FRAMEBUF", "Frame Buffer", "Prefer for the object to draw after the avatar but before other blended objects."), + ("BLEND", "Blended", "Prefer for the object to draw after most other geometry in the blended pass.")], + options=set()) + sort_faces = EnumProperty(name="Sort Faces", + description="", + items=[("AUTO", "(Auto)", "Let Korman decide if faces should be sorted."), + ("ALWAYS", "Always", "Force the object's faces to be sorted."), + ("NEVER", "Never", "Force the object's faces to never be sorted.")], + options=set()) + + dependencies = CollectionProperty(type=PlasmaBlendOntoObject) + active_dependency_index = IntProperty(options={"HIDDEN"}) + + def export(self, exporter, bo, so): + # What'er you lookin at? + pass + + @property + def draw_opaque(self): + return self.render_level == "OPAQUE" + + @property + def draw_framebuf(self): + return self.render_level == "FRAMEBUF" + + @property + def draw_no_defer(self): + return self.render_level != "BLEND" + + @property + def face_sort(self): + return self.sort_faces == "ALWAYS" + + @property + def no_face_sort(self): + return self.sort_faces == "NEVER" + + @property + def has_dependencies(self): + return bool(self.dependencies) + + @property + def has_circular_dependency(self): + return self._check_circular_dependency() + + def _check_circular_dependency(self, objects=None): + if objects is None: + objects = set() + elif self.name in objects: + return True + objects.add(self.name) + + for i in self.iter_dependencies(): + # New deep copy of the set for each dependency, so an object can be reused as a + # dependant's dependant. + this_branch = set(objects) + sub_mod = i.plasma_modifiers.blend + if sub_mod.enabled and sub_mod._check_circular_dependency(this_branch): + return True + return False + + def iter_dependencies(self): + for i in (j.blend_onto for j in self.dependencies if j.blend_onto is not None and j.enabled): + yield i + + def sanity_check(self): + if self.has_circular_dependency: + raise ExportError("'{}': Circular Render Dependency detected!".format(self.name)) + + class PlasmaDecalManagerRef(bpy.types.PropertyGroup): enabled = BoolProperty(name="Enabled", default=True, @@ -439,6 +531,8 @@ class PlasmaLightingMod(PlasmaModifierProperties): return True if self.id_data.plasma_object.has_transform_animation: return True + if mods.collision.enabled and mods.collision.dynamic: + return True return False diff --git a/korman/ui/modifiers/render.py b/korman/ui/modifiers/render.py index 64d3c9b..7c6dfa6 100644 --- a/korman/ui/modifiers/render.py +++ b/korman/ui/modifiers/render.py @@ -18,6 +18,36 @@ import bpy from .. import ui_list from ...exporter.mesh import _VERTEX_COLOR_LAYERS +class BlendOntoListUI(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + if item.blend_onto is None: + layout.label("[No Object Specified]", icon="ERROR") + else: + layout.label(item.blend_onto.name, icon="OBJECT_DATA") + layout.prop(item, "enabled", text="") + + +def blend(modifier, layout, context): + # Warn if there are render dependencies and a manual render level specification -- this + # could lead to unpredictable results. + layout.alert = modifier.render_level != "AUTO" and bool(modifier.dependencies) + layout.prop(modifier, "render_level") + layout.alert = False + layout.prop(modifier, "sort_faces") + + layout.separator() + layout.label("Render Dependencies:") + ui_list.draw_modifier_list(layout, "BlendOntoListUI", modifier, "dependencies", + "active_dependency_index", rows=2, maxrows=4) + try: + dependency_ref = modifier.dependencies[modifier.active_dependency_index] + except: + pass + else: + layout.alert = dependency_ref.blend_onto is None + layout.prop(dependency_ref, "blend_onto") + + class DecalMgrListUI(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): if item.name: