Browse Source

Initial bits of waveset7 support

NOTE: this current exports a waveset, but it is impoperly colored. We
still need to export the environment map.
pull/10/head
Adam Johnson 10 years ago
parent
commit
73390e523f
  1. 16
      korman/exporter/material.py
  2. 59
      korman/exporter/mesh.py
  3. 1
      korman/properties/modifiers/__init__.py
  4. 11
      korman/properties/modifiers/base.py
  5. 292
      korman/properties/modifiers/water.py
  6. 1
      korman/ui/modifiers/__init__.py
  7. 104
      korman/ui/modifiers/water.py

16
korman/exporter/material.py

@ -211,6 +211,22 @@ class MaterialConverter:
# Looks like we're done... # Looks like we're done...
return hsgmat.key 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): def _export_texture_slot(self, bo, bm, hsgmat, slots, idx):
slot = slots[idx] slot = slots[idx]
num_exported = 1 num_exported = 1

59
korman/exporter/mesh.py

@ -37,10 +37,9 @@ class _RenderLevel:
_MAJOR_SHIFT = 28 _MAJOR_SHIFT = 28
_MINOR_MASK = ((1 << _MAJOR_SHIFT) - 1) _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 self.level = 0
# Naive... BlendSpans (any blending on the first layer) are MAJOR_BLEND
if blendSpan: if blendSpan:
self.major = self.MAJOR_DEFAULT self.major = self.MAJOR_DEFAULT
@ -68,15 +67,22 @@ class _RenderLevel:
class _DrawableCriteria: class _DrawableCriteria:
def __init__(self, hsgmat, pass_index): def __init__(self, bo, hsgmat, pass_index):
for layer in hsgmat.layers: for layer in hsgmat.layers:
if layer.object.state.blendFlags & hsGMatState.kBlendMask: if layer.object.state.blendFlags & hsGMatState.kBlendMask:
self.blend_span = True self.blend_span = True
break break
else: else:
self.blend_span = False 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): def __eq__(self, other):
if not isinstance(other, _DrawableCriteria): if not isinstance(other, _DrawableCriteria):
@ -304,19 +310,34 @@ class MeshConverter:
def _export_material_spans(self, bo, mesh, materials): def _export_material_spans(self, bo, mesh, materials):
"""Exports all Materials and creates plGeometrySpans""" """Exports all Materials and creates plGeometrySpans"""
geospans = [None] * len(materials) waveset_mod = bo.plasma_modifiers.water_basic
for i, blmat in enumerate(materials): if waveset_mod.enabled:
matKey = self.material.export_material(bo, blmat) if len(materials) > 1:
geospans[i] = (self._create_geospan(bo, mesh, blmat, matKey), blmat.pass_index) msg = "'{}' is a WaveSet -- only one material is supported".format(bo.name)
return geospans 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): def _export_static_lighting(self, bo):
helpers.make_active_selection(bo) helpers.make_active_selection(bo)
lm = bo.plasma_modifiers.lightmap mods = bo.plasma_modifiers
lm = mods.lightmap
if lm.enabled: if lm.enabled:
print(" Baking lightmap...") print(" Baking lightmap...")
bpy.ops.object.plasma_lightmap_autobake(light_group=lm.light_group) 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: for vcol_layer in bo.data.vertex_colors:
name = vcol_layer.name.lower() name = vcol_layer.name.lower()
if name in _VERTEX_COLOR_LAYERS: if name in _VERTEX_COLOR_LAYERS:
@ -325,7 +346,6 @@ class MeshConverter:
print(" Baking crappy vertex color lighting...") print(" Baking crappy vertex color lighting...")
bpy.ops.object.plasma_vertexlight_autobake() bpy.ops.object.plasma_vertexlight_autobake()
def _find_create_dspan(self, bo, hsgmat, pass_index): def _find_create_dspan(self, bo, hsgmat, pass_index):
location = self._mgr.get_location(bo) location = self._mgr.get_location(bo)
if location not in self._dspans: 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... # This is where we figure out which DSpan this goes into. To vaguely summarize the rules...
# BlendSpans: anything with an alpha blended layer # 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" # 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. # 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]: if crit not in self._dspans[location]:
# AgeName_[District_]_Page_RenderLevel_Crit[Blend]Spans # 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) 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 = self._mgr.add_object(pl=plDrawableSpans, name=name, loc=location)
dspan.criteria = crit.criteria criteria = crit.criteria
# TODO: props 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.renderLevel = crit.render_level.level
dspan.sceneNode = node # AddViaNotify dspan.sceneNode = node # AddViaNotify

1
korman/properties/modifiers/__init__.py

@ -22,6 +22,7 @@ from .logic import *
from .physics import * from .physics import *
from .region import * from .region import *
from .render import * from .render import *
from .water import *
class PlasmaModifiers(bpy.types.PropertyGroup): class PlasmaModifiers(bpy.types.PropertyGroup):
def determine_next_id(self): def determine_next_id(self):

11
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""" """Indicates if this modifier requires the object to be a movable actor"""
return False 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? # Guess what?
# You can't register properties on a base class--Blender isn't smart enough to do inheritance, # 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 # you see... So, we'll store our definitions in a dict and make those properties on each subclass

292
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 <http://www.gnu.org/licenses/>.
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)

1
korman/ui/modifiers/__init__.py

@ -19,3 +19,4 @@ from .logic import *
from .physics import * from .physics import *
from .region import * from .region import *
from .render import * from .render import *
from .water import *

104
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 <http://www.gnu.org/licenses/>.
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")
Loading…
Cancel
Save