From 73390e523f5f2fb8d38728b80eb726085428a8ce Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Wed, 15 Jul 2015 22:55:54 -0400 Subject: [PATCH 1/3] Initial bits of waveset7 support NOTE: this current exports a waveset, but it is impoperly colored. We still need to export the environment map. --- korman/exporter/material.py | 16 ++ korman/exporter/mesh.py | 59 +++-- korman/properties/modifiers/__init__.py | 1 + korman/properties/modifiers/base.py | 11 + korman/properties/modifiers/water.py | 292 ++++++++++++++++++++++++ korman/ui/modifiers/__init__.py | 1 + korman/ui/modifiers/water.py | 104 +++++++++ 7 files changed, 467 insertions(+), 17 deletions(-) create mode 100644 korman/properties/modifiers/water.py create mode 100644 korman/ui/modifiers/water.py diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 3775162..d63a797 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -211,6 +211,22 @@ class MaterialConverter: # Looks like we're done... return hsgmat.key + def export_waveset_material(self, bo, bm): + print(" Exporting WaveSet Material '{}'".format(bm.name)) + + # WaveSets MUST have their own material + unique_name = "{}_WaveSet7".format(bm.name) + hsgmat = self._mgr.add_object(hsGMaterial, name=unique_name, bl=bo) + + # Materials MUST have one layer. Wavesets need alpha blending... + layer = self._mgr.add_object(plLayer, name=unique_name, bl=bo) + self._propagate_material_settings(bm, layer) + layer.state.blendFlags |= hsGMatState.kBlendAlpha + hsgmat.addLayer(layer.key) + + # Wasn't that easy? + return hsgmat.key + def _export_texture_slot(self, bo, bm, hsgmat, slots, idx): slot = slots[idx] num_exported = 1 diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 0e5e287..faee86b 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -37,10 +37,9 @@ class _RenderLevel: _MAJOR_SHIFT = 28 _MINOR_MASK = ((1 << _MAJOR_SHIFT) - 1) - def __init__(self, hsgmat, pass_index, blendSpan=False): + def __init__(self, bo, hsgmat, pass_index, blendSpan=False): self.level = 0 - # Naive... BlendSpans (any blending on the first layer) are MAJOR_BLEND if blendSpan: self.major = self.MAJOR_DEFAULT @@ -68,15 +67,22 @@ class _RenderLevel: class _DrawableCriteria: - def __init__(self, hsgmat, pass_index): + def __init__(self, bo, hsgmat, pass_index): for layer in hsgmat.layers: if layer.object.state.blendFlags & hsGMatState.kBlendMask: self.blend_span = True break else: self.blend_span = False - self.criteria = 0 # TODO - self.render_level = _RenderLevel(hsgmat, pass_index, self.blend_span) + + self.criteria = 0 + if self.blend_span: + for mod in bo.plasma_modifiers.modifiers: + if mod.requires_face_sort: + self.criteria |= plDrawable.kCritSortFaces + if mod.requires_span_sort: + self.sort_spans |= plDrawable.kCritSortSpans + self.render_level = _RenderLevel(bo, hsgmat, pass_index, self.blend_span) def __eq__(self, other): if not isinstance(other, _DrawableCriteria): @@ -304,19 +310,34 @@ class MeshConverter: def _export_material_spans(self, bo, mesh, materials): """Exports all Materials and creates plGeometrySpans""" - geospans = [None] * len(materials) - for i, 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 + waveset_mod = bo.plasma_modifiers.water_basic + if waveset_mod.enabled: + 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]) + geospan = self._create_geospan(bo, mesh, materials[0], 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)] + else: + geospans = [None] * len(materials) + for i, 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 def _export_static_lighting(self, bo): helpers.make_active_selection(bo) - lm = bo.plasma_modifiers.lightmap + mods = bo.plasma_modifiers + lm = mods.lightmap if lm.enabled: print(" Baking lightmap...") bpy.ops.object.plasma_lightmap_autobake(light_group=lm.light_group) - else: + elif not mods.water_basic.enabled: for vcol_layer in bo.data.vertex_colors: name = vcol_layer.name.lower() if name in _VERTEX_COLOR_LAYERS: @@ -325,7 +346,6 @@ class MeshConverter: print(" Baking crappy vertex color lighting...") bpy.ops.object.plasma_vertexlight_autobake() - def _find_create_dspan(self, bo, hsgmat, pass_index): location = self._mgr.get_location(bo) if location not in self._dspans: @@ -333,10 +353,11 @@ class MeshConverter: # This is where we figure out which DSpan this goes into. To vaguely summarize the rules... # BlendSpans: anything with an alpha blended layer - # [... document me ...] + # SortSpans: means we should sort the spans in this DSpan with all other span in this pass + # SortFaces: means we should sort the faces in this span only # We're using pass index to do just what it was designed for. Cyan has a nicer "depends on" # draw component, but pass index is the Blender way, so that's what we're doing. - crit = _DrawableCriteria(hsgmat, pass_index) + crit = _DrawableCriteria(bo, hsgmat, pass_index) if crit not in self._dspans[location]: # AgeName_[District_]_Page_RenderLevel_Crit[Blend]Spans @@ -345,8 +366,12 @@ class MeshConverter: name = "{}_{:08X}_{:X}{}".format(node.name, crit.render_level.level, crit.criteria, crit.span_type) dspan = self._mgr.add_object(pl=plDrawableSpans, name=name, loc=location) - dspan.criteria = crit.criteria - # TODO: props + criteria = crit.criteria + dspan.criteria = criteria + if criteria & plDrawable.kCritSortFaces: + dspan.props |= plDrawable.kPropSortFaces + if criteria & plDrawable.kCritSortSpans: + dspan.props |= plDrawable.kPropSortSpans dspan.renderLevel = crit.render_level.level dspan.sceneNode = node # AddViaNotify diff --git a/korman/properties/modifiers/__init__.py b/korman/properties/modifiers/__init__.py index 86b9a5e..6223ac8 100644 --- a/korman/properties/modifiers/__init__.py +++ b/korman/properties/modifiers/__init__.py @@ -22,6 +22,7 @@ from .logic import * from .physics import * from .region import * from .render import * +from .water import * class PlasmaModifiers(bpy.types.PropertyGroup): def determine_next_id(self): diff --git a/korman/properties/modifiers/base.py b/korman/properties/modifiers/base.py index 3aa81ed..e1a50b6 100644 --- a/korman/properties/modifiers/base.py +++ b/korman/properties/modifiers/base.py @@ -37,6 +37,17 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup): """Indicates if this modifier requires the object to be a movable actor""" return False + @property + def requires_face_sort(self): + """Indicates that the geometry's faces must be sorted by the engine""" + return False + + @property + def requires_span_sort(self): + """Indicates that the geometry's Spans must be sorted with those from other Drawables that + will render in the same pass""" + return False + # Guess what? # You can't register properties on a base class--Blender isn't smart enough to do inheritance, # you see... So, we'll store our definitions in a dict and make those properties on each subclass diff --git a/korman/properties/modifiers/water.py b/korman/properties/modifiers/water.py new file mode 100644 index 0000000..04ceb4f --- /dev/null +++ b/korman/properties/modifiers/water.py @@ -0,0 +1,292 @@ +# This file is part of Korman. +# +# Korman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Korman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Korman. If not, see . + +import bpy +from bpy.props import * +import math +from PyHSPlasma import * + +from .base import PlasmaModifierProperties +from ...exporter import ExportError + +class PlasmaWaterModifier(PlasmaModifierProperties, bpy.types.PropertyGroup): + pl_id = "water_basic" + + bl_category = "Water" + bl_label = "Basic Water" + bl_description = "Basic water properties" + + wind_object_name = StringProperty(name="Wind Object", + description="Object whose Y axis represents the wind direction") + wind_speed = FloatProperty(name="Wind Speed", + description="Magnitude of the wind", + default=1.0) + + specular_tint = FloatVectorProperty(name="Specular Tint", + subtype="COLOR", + min=0.0, max=1.0, + default=(1.0, 1.0, 1.0)) + specular_alpha = FloatProperty(name="Specular Alpha", + min=0.0, max=1.0, + default=0.3) + noise = IntProperty(name="Noise", + subtype="PERCENTAGE", + min=0, max=300, + default=50) + specular_start = FloatProperty(name="Specular Start", + min=0.0, max=1000.0, + default=50.0) + specular_end = FloatProperty(name="Specular End", + min=0.0, max=10000.0, + default=1000.0) + ripple_scale = FloatProperty(name="Ripple Scale", + min=5.0, max=1000.0, + default=25.0) + + depth_opacity = FloatProperty(name="Opacity End", + min=0.5, max=20.0, + default=3.0) + depth_reflection = FloatProperty(name="Reflection End", + min=0.5, max=20.0, + default=3.0) + depth_wave = FloatProperty(name="Wave End", + min=0.5, max=20.0, + default=4.0) + zero_opacity = FloatProperty(name="Opacity Start", + min=-10.0, max=10.0, + default=-1.0) + zero_reflection = FloatProperty(name="Reflection Start", + min=-10.0, max=10.0, + default=0.0) + zero_wave = FloatProperty(name="Wave Start", + min=-10.0, max=10.0, + default=0.0) + + def created(self, obj): + self.display_name = "{}_WaveSet7".format(obj.name) + + def export(self, exporter, bo, so): + waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so) + if self.wind_object_name: + wind_obj = bpy.data.objects.get(self.wind_object_name, None) + if wind_obj is None: + raise ExportError("{}: Wind Object '{}' not found".format(self.display_name, self.wind_object_name)) + if wind_obj.plasma_object.enabled and wind_obj.plasma_modifiers.animation.enabled: + waveset.refObj = exporter.mgr.find_create_key(plSceneObject, bl=wind_obj) + waveset.setFlag(plWaveSet7.kHasRefObject, True) + + # This is much like what happened in PyPRP + speed = self.wind_speed + matrix = wind_obj.maitrx_world + wind_dir = hsVector3(matrix[1][0] * speed, matrix[1][1] * speed, matrix[1][2] * speed) + else: + # Stolen shamelessly from PyPRP + wind_dir = hsVector3(0.0871562, 0.996195, 0.0) + + # Stuff we expose + state = waveset.state + state.rippleScale = self.ripple_scale + state.waterHeight = bo.location[2] + state.windDir = wind_dir + state.specVector = hsVector3(self.noise / 100, self.specular_start, self.specular_end) + state.specularTint = hsColorRGBA(*self.specular_tint, alpha=self.specular_alpha) + state.waterOffset = hsVector3(self.zero_opacity * -1.0, self.zero_reflection * -1.0, self.zero_wave * -1.0) + state.depthFalloff = hsVector3(self.depth_opacity, self.depth_reflection, self.depth_wave) + + # These are either unused, set from somewhere else at runtime, or hardcoded + state.waterTint = hsColorRGBA(1.0, 1.0, 1.0, 1.0) + state.maxColor = hsColorRGBA(1.0, 1.0, 1.0, 1.0) + state.shoreTint = hsColorRGBA(1.0, 1.0, 1.0, 1.0) + state.maxAtten = hsVector3(1.0, 1.0, 1.0) + state.minAtten = hsVector3(0.0, 0.0, 0.0) + + # Now, we have some related modifiers that may or may not be enabled... If not, we should + # make them export their defaults. + mods = bo.plasma_modifiers + if not mods.water_geostate.enabled: + mods.water_geostate.convert_default_wavestate(state.geoState) + if not mods.water_texstate.enabled: + mods.water_texstate.convert_default_wavestate(state.texState) + if not mods.water_shore.enabled: + mods.water_shore.convert_default(state) + + +class PlasmaShoreObject(bpy.types.PropertyGroup): + display_name = StringProperty(name="Display Name") + object_name = StringProperty(name="Shore Object", + description="Object that waves crash upon") + + +class PlasmaWaterShoreModifier(PlasmaModifierProperties): + pl_depends = {"water_basic"} + pl_id = "water_shore" + + bl_category = "Water" + bl_label = "Water Shore" + bl_description = "" + + # The basic modifier may want to export a default copy of us + _shore_tint_default = (0.2, 0.4, 0.4) + _shore_opacity_default = 40 + _wispiness_default = 50 + _period_default = 100.0 + _finger_default = 100.0 + _edge_opacity_default = 100 + _edge_radius_default = 100.0 + + shores = CollectionProperty(type=PlasmaShoreObject) + active_shore_index = IntProperty(options={"HIDDEN"}) + + shore_tint = FloatVectorProperty(name="Shore Tint", + subtype="COLOR", + min=0.0, max=1.0, + default=_shore_tint_default) + shore_opacity = IntProperty(name="Shore Opacity", + subtype="PERCENTAGE", + min=0, max=100, + default=_shore_opacity_default) + wispiness = IntProperty(name="Wispiness", + subtype="PERCENTAGE", + min=0, max=200, + default=_wispiness_default) + + period = FloatProperty(name="Period", + min=0.0, max=200.0, + default=_period_default) + finger = FloatProperty(name="Finger", + min=50.0, max=300.0, + default=_finger_default) + edge_opacity = IntProperty(name="Edge Opacity", + subtype="PERCENTAGE", + min=0, max=100, + default=_edge_opacity_default) + edge_radius = FloatProperty(name="Edge Radius", + subtype="PERCENTAGE", + min=50, max=300, + default=_edge_radius_default) + + def convert_default(self, wavestate): + wavestate.wispiness = self._wispiness_default / 100.0 + wavestate.minColor = hsColorRGBA(*self._shore_tint_default, alpha=(self._shore_opacity_default / 100.0)) + wavestate.edgeOpacity = self._edge_opacity_default / 100.0 + wavestate.edgeRadius = self._edge_radius_default / 100.0 + wavestate.period = self._period_default / 100.0 + wavestate.fingerLength = self._finger_default / 100.0 + + def created(self, obj): + self.display_name = "{}_WaterShore".format(obj.name) + + def export(self, exporter, bo, so): + waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so) + wavestate = waveset.state + + for i in self.shores: + shore = bpy.data.objects.get(i.object_name, None) + if shore is None: + raise ExportError("'{}': Shore Object '{}' does not exist".format(self.display_name, i.object_name)) + waveset.addShore(exporter.mgr.find_create_key(plSceneObject, bl=shore)) + + wavestate.wispiness = self.wispiness / 100.0 + wavestate.minColor = hsColorRGBA(*self.shore_tint, alpha=(self.shore_opacity / 100.0)) + wavestate.edgeOpacity = self.edge_opacity / 100.0 + wavestate.edgeRadius = self.edge_radius / 100.0 + wavestate.period = self.period / 100.0 + wavestate.fingerLength = self.finger / 100.0 + + +class PlasmaWaveState: + pl_depends = {"water_basic"} + + def convert_wavestate(self, state): + state.minLength = self.min_length + state.maxLength = self.max_length + state.ampOverLen = self.amplitude / 100.0 + state.chop = self.chop / 100.0 + state.angleDev = self.angle_dev + + def convert_default_wavestate(self, state): + cls = self.__class__ + state.minLength = cls._min_length_default + state.maxLength = cls._max_length_default + state.ampOverLen = cls._amplitude_default / 100.0 + state.chop = cls._chop_default / 100.0 + state.angleDev = cls._angle_dev_default + + @classmethod + def register(cls): + cls.min_length = FloatProperty(name="Min Length", + description="Smallest wave length", + min=0.1, max=50.0, + default=cls._min_length_default) + cls.max_length = FloatProperty(name="Max Length", + description="Largest wave length", + min=0.1, max=50.0, + default=cls._max_length_default) + cls.amplitude = IntProperty(name="Amplitude", + description="Multiplier for wave height", + subtype="PERCENTAGE", + min=0, max=100, + default=cls._amplitude_default) + cls.chop = IntProperty(name="Choppiness", + description="Sharpness of wave crests", + subtype="PERCENTAGE", + min=0, max=500, + default=cls._chop_default) + cls.angle_dev = FloatProperty(name="Wave Spread", + subtype="ANGLE", + min=math.radians(0.0), max=math.radians(180.0), + default=cls._angle_dev_default) + + +class PlasmaWaveGeoState(PlasmaWaveState, PlasmaModifierProperties): + pl_id = "water_geostate" + + bl_category = "Water" + bl_label = "Geometry Waves" + bl_description = "Mesh wave settings" + + _min_length_default = 4.0 + _max_length_default = 8.0 + _amplitude_default = 10 + _chop_default = 50 + _angle_dev_default = math.radians(20.0) + + def created(self, obj): + self.display_name = "{}_WaveGeoState".format(obj.name) + + def export(self, exporter, bo, so): + waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so) + self.convert_wavestate(waveset.state.geoState) + + +class PlasmaWaveTexState(PlasmaWaveState, PlasmaModifierProperties): + pl_id = "water_texstate" + + bl_category = "Water" + bl_label = "Texture Waves" + bl_description = "Texture wave settings" + + _min_length_default = 0.1 + _max_length_default = 4.0 + _amplitude_default = 10 + _chop_default = 50 + _angle_dev_default = math.radians(20.0) + + def created(self, obj): + self.display_name = "{}_WaveTexState".format(obj.name) + + def export(self, exporter, bo, so): + waveset = exporter.mgr.find_create_object(plWaveSet7, name=bo.name, so=so) + self.convert_wavestate(waveset.state.texState) diff --git a/korman/ui/modifiers/__init__.py b/korman/ui/modifiers/__init__.py index 0a838df..5cf2fe7 100644 --- a/korman/ui/modifiers/__init__.py +++ b/korman/ui/modifiers/__init__.py @@ -19,3 +19,4 @@ from .logic import * from .physics import * from .region import * from .render import * +from .water import * diff --git a/korman/ui/modifiers/water.py b/korman/ui/modifiers/water.py new file mode 100644 index 0000000..918edca --- /dev/null +++ b/korman/ui/modifiers/water.py @@ -0,0 +1,104 @@ +# This file is part of Korman. +# +# Korman is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Korman is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Korman. If not, see . + +import bpy + +def water_basic(modifier, layout, context): + layout.prop_search(modifier, "wind_object_name", bpy.data, "objects") + + row = layout.row() + row.prop(modifier, "wind_speed") + layout.separator() + + split = layout.split() + col = split.column() + col.prop(modifier, "specular_tint") + col.prop(modifier, "specular_alpha", text="Alpha") + + col.label("Specular:") + col.prop(modifier, "specular_start", text="Start") + col.prop(modifier, "specular_end", text="End") + + col.label("Misc:") + col.prop(modifier, "noise") + col.prop(modifier, "ripple_scale") + + col = split.column() + col.label("Opacity:") + col.prop(modifier, "zero_opacity", text="Start") + col.prop(modifier, "depth_opacity", text="End") + + col.label("Reflection:") + col.prop(modifier, "zero_reflection", text="Start") + col.prop(modifier, "depth_reflection", text="End") + + col.label("Wave:") + col.prop(modifier, "zero_wave", text="Start") + col.prop(modifier, "depth_wave", text="End") + +def _wavestate(modifier, layout, context): + split = layout.split() + col = split.column() + col.label("Size:") + col.prop(modifier, "min_length") + col.prop(modifier, "max_length") + col.prop(modifier, "amplitude") + + col = split.column() + col.label("Behavior:") + col.prop(modifier, "chop") + col.prop(modifier, "angle_dev") + +water_geostate = _wavestate +water_texstate = _wavestate + +class ShoreListUI(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + layout.prop(item, "display_name", emboss=False, text="", icon="MOD_WAVE") + + +def water_shore(modifier, layout, context): + row = layout.row() + row.template_list("ShoreListUI", "shores", modifier, "shores", modifier, "active_shore_index", + rows=2, maxrows=3) + col = row.column(align=True) + op = col.operator("object.plasma_modifier_collection_add", icon="ZOOMIN", text="") + op.modifier = modifier.pl_id + op.collection = "shores" + op.name_prefix = "Shore" + op.name_prop = "display_name" + op = col.operator("object.plasma_modifier_collection_remove", icon="ZOOMOUT", text="") + op.modifier = modifier.pl_id + op.collection = "shores" + op.index = modifier.active_shore_index + + # Display the active shore + if modifier.shores: + shore = modifier.shores[modifier.active_shore_index] + layout.prop_search(shore, "object_name", bpy.data, "objects", icon="MESH_DATA") + + split = layout.split() + col = split.column() + col.label("Basic:") + col.prop(modifier, "shore_tint") + col.prop(modifier, "shore_opacity") + col.prop(modifier, "wispiness") + + col = split.column() + col.label("Advanced:") + col.prop(modifier, "period") + col.prop(modifier, "finger") + col.prop(modifier, "edge_opacity") + col.prop(modifier, "edge_radius") From 5baca5c49385b7dd1b663602e0f052b6c226a73e Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 16 Jul 2015 00:01:38 -0400 Subject: [PATCH 2/3] WaveSet environment maps --- korman/exporter/material.py | 43 ++++++++++++++++------------ korman/properties/modifiers/water.py | 26 ++++++++++++++++- korman/ui/modifiers/water.py | 2 ++ 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/korman/exporter/material.py b/korman/exporter/material.py index d63a797..67ddb10 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -386,37 +386,31 @@ 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, hsgmat, 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? self._exporter().report.warn("IMAGE EnvironmentMaps are not supported. '{}' will not be exported!".format(layer.key.name)) pl_env = None layer.state.shadeFlags |= hsGMatState.kShadeEnvironMap - layer.texture = pl_env + layer.texture = pl_env.key - def _export_dynamic_env(self, bo, hsgmat, layer, texture, pl_class): + def export_dynamic_env(self, bo, hsgmat, 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 viewpt = bl_env.viewpoint_object + if viewpt is None: + viewpt = bo name = "{}_DynEnvMap".format(viewpt.name) - pl_env = self._mgr.find_key(pl_class, bl=bo, name=name) + pl_env = self._mgr.find_object(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): - pl_env_obj.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) - pl_env_obj.addMatLayer(layer.key) + if isinstance(pl_env, plDynamicCamMap): + pl_env.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) + pl_env.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... - # NOTE: that harvest_actor makes sure everyone alread knows we're going to have a CI - 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) @@ -429,7 +423,6 @@ class MaterialConverter: 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 @@ -439,6 +432,13 @@ class MaterialConverter: if isinstance(pl_env, plDynamicCamMap): faces = (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... + # NOTE: that harvest_actor makes sure everyone alread knows we're going to have a CI + root = self._mgr.find_create_key(plSceneObject, bl=viewpt) + pl_env.rootNode = root # FIXME: DCM camera + # FIXME: DynamicCamMap Camera + pl_env.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo)) pl_env.addMatLayer(layer.key) @@ -458,8 +458,13 @@ class MaterialConverter: else: faces = pl_env.faces + (pl_env,) - layer.UVWSrc = plLayerInterface.kUVWReflect - layer.state.miscFlags |= hsGMatState.kMiscUseRefractionXform + # DEMs can do just a position vector. We actually prefer this because the WaveSet exporter + # will probably want to steal it for diabolical purposes... + pl_env.position = hsVector3(*viewpt.location) + + if layer is not None: + 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: @@ -475,7 +480,7 @@ class MaterialConverter: i.viewportBottom = eRes i.ZDepth = 24 - return pl_env.key + return pl_env def _export_texture_type_image(self, bo, hsgmat, layer, slot): """Exports a Blender ImageTexture to a plLayer""" diff --git a/korman/properties/modifiers/water.py b/korman/properties/modifiers/water.py index 04ceb4f..826f665 100644 --- a/korman/properties/modifiers/water.py +++ b/korman/properties/modifiers/water.py @@ -33,6 +33,12 @@ class PlasmaWaterModifier(PlasmaModifierProperties, bpy.types.PropertyGroup): wind_speed = FloatProperty(name="Wind Speed", description="Magnitude of the wind", default=1.0) + envmap_name = StringProperty(name="EnvMap", + description="Texture defining an environment map for this water object") + envmap_radius = FloatProperty(name="Environment Sphere Radius", + description="How far away the first object you want to see is", + min=5.0, max=10000.0, + default=500.0) specular_tint = FloatVectorProperty(name="Specular Tint", subtype="COLOR", @@ -100,11 +106,29 @@ class PlasmaWaterModifier(PlasmaModifierProperties, bpy.types.PropertyGroup): state.rippleScale = self.ripple_scale state.waterHeight = bo.location[2] state.windDir = wind_dir - state.specVector = hsVector3(self.noise / 100, self.specular_start, self.specular_end) + state.specVector = hsVector3(self.noise / 100.0, self.specular_start, self.specular_end) state.specularTint = hsColorRGBA(*self.specular_tint, alpha=self.specular_alpha) state.waterOffset = hsVector3(self.zero_opacity * -1.0, self.zero_reflection * -1.0, self.zero_wave * -1.0) state.depthFalloff = hsVector3(self.depth_opacity, self.depth_reflection, self.depth_wave) + # Environment Map + if self.envmap_name: + texture = bpy.data.textures.get(self.envmap_name, None) + if texture is None: + raise ExportError("{}: Texture '{}' not found".format(self.display_name, self.envmap_name)) + if texture.type != "ENVIRONMENT_MAP": + raise ExportError("{}: Texture '{}' is not an ENVIRONMENT MAP".format(self.display_name, self.envmap_name)) + + # maybe, just maybe, we're absuing our privledges? + dem = exporter.mesh.material.export_dynamic_env(bo, None, None, texture, plDynamicEnvMap) + waveset.envMap = dem.key + state.envCenter = dem.position + state.envRefresh = dem.refreshRate + else: + state.envCenter = hsVector3(*bo.location) + state.envRefresh = 0.0 + state.envRadius = self.envmap_radius + # These are either unused, set from somewhere else at runtime, or hardcoded state.waterTint = hsColorRGBA(1.0, 1.0, 1.0, 1.0) state.maxColor = hsColorRGBA(1.0, 1.0, 1.0, 1.0) diff --git a/korman/ui/modifiers/water.py b/korman/ui/modifiers/water.py index 918edca..886e99f 100644 --- a/korman/ui/modifiers/water.py +++ b/korman/ui/modifiers/water.py @@ -17,9 +17,11 @@ import bpy def water_basic(modifier, layout, context): layout.prop_search(modifier, "wind_object_name", bpy.data, "objects") + layout.prop_search(modifier, "envmap_name", bpy.data, "textures") row = layout.row() row.prop(modifier, "wind_speed") + row.prop(modifier, "envmap_radius") layout.separator() split = layout.split() From 4341159fe38f4d30cd6121514688c381906e27d7 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 16 Jul 2015 14:26:42 -0400 Subject: [PATCH 3/3] Expose wavesets to PFMs --- korman/nodes/node_python.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/korman/nodes/node_python.py b/korman/nodes/node_python.py index a46ade6..eee490d 100644 --- a/korman/nodes/node_python.py +++ b/korman/nodes/node_python.py @@ -423,7 +423,8 @@ class PlasmaAttribObjectNode(PlasmaAttribNodeBase, bpy.types.Node): bl_idname = "PlasmaAttribObjectNode" bl_label = "Object Attribute" - pl_attrib = ("ptAttribSceneobject", "ptAttribSceneobjectList", "ptAttribAnimation") + pl_attrib = ("ptAttribSceneobject", "ptAttribSceneobjectList", "ptAttribAnimation", + "ptAttribWaveSet") object_name = StringProperty(name="Object", description="Object containing the required data") @@ -456,6 +457,11 @@ class PlasmaAttribObjectNode(PlasmaAttribNodeBase, bpy.types.Node): agmod = exporter.mgr.find_create_key(plAGModifier, so=ref_so, name=anim.display_name) agmaster = exporter.mgr.find_create_key(plAGMasterModifier, so=ref_so, name=anim.display_name) return agmaster + elif attrib == "ptAttribWaveSet": + waveset = bo.plasma_modifiers.water_basic + if not waveset.enabled: + self.raise_error("water modifier not enabled on '{}'".format(self.object_name)) + return exporter.mgr.find_create_key(plWaveSet7, so=ref_so, bl=bo) class PlasmaAttribStringNode(PlasmaAttribNodeBase, bpy.types.Node):