mirror of
https://github.com/H-uru/korman.git
synced 2025-07-14 22:36:52 +00:00
Rudimentary Lightmap Baking
Still lots of considerations: - Need to make faux lightgroups to avoid baking Plasma RT lights - Still need to bake vertex colors - Still need to export Plasma RT lights (D'oh!) But, this is definitely a step in the right direction!
This commit is contained in:
@ -102,11 +102,19 @@ class _GLTexture:
|
||||
|
||||
|
||||
class _Texture:
|
||||
def __init__(self, texture):
|
||||
self.image = texture.image
|
||||
self.calc_alpha = texture.use_calculate_alpha
|
||||
self.mipmap = texture.use_mipmap
|
||||
self.use_alpha = texture.use_alpha
|
||||
def __init__(self, texture=None, image=None):
|
||||
assert (texture or image)
|
||||
|
||||
if texture is not None:
|
||||
self.image = texture.image
|
||||
self.calc_alpha = texture.use_calculate_alpha
|
||||
self.mipmap = texture.use_mipmap
|
||||
self.use_alpha = texture.use_alpha
|
||||
if image is not None:
|
||||
self.image = image
|
||||
self.calc_alpha = False
|
||||
self.mipmap = False
|
||||
self.use_alpha = image.use_alpha
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, _Texture):
|
||||
@ -121,10 +129,7 @@ class _Texture:
|
||||
return hash(self.image.name) ^ hash(self.calc_alpha)
|
||||
|
||||
def __str__(self):
|
||||
if self.mipmap:
|
||||
name = self._change_extension(self.image.name, ".dds")
|
||||
else:
|
||||
name = self._change_extension(self.image.name, ".bmp")
|
||||
name = self._change_extension(self.image.name, ".dds")
|
||||
if self.calc_alpha:
|
||||
name = "ALPHAGEN_{}".format(name)
|
||||
return name
|
||||
@ -149,6 +154,7 @@ class _Texture:
|
||||
|
||||
class MaterialConverter:
|
||||
def __init__(self, exporter):
|
||||
self._obj2mat = {}
|
||||
self._exporter = weakref.ref(exporter)
|
||||
self._pending = {}
|
||||
|
||||
@ -166,6 +172,12 @@ class MaterialConverter:
|
||||
self._propagate_material_settings(bm, layer)
|
||||
hsgmat.addLayer(layer.key)
|
||||
|
||||
# Cache this material for later
|
||||
if bo in self._obj2mat:
|
||||
self._obj2mat[bo].append(hsgmat.key)
|
||||
else:
|
||||
self._obj2mat[bo] = [hsgmat.key]
|
||||
|
||||
# Looks like we're done...
|
||||
return hsgmat.key
|
||||
|
||||
@ -214,7 +226,7 @@ class MaterialConverter:
|
||||
if texture.image is None:
|
||||
bitmap = self.add_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo)
|
||||
else:
|
||||
key = _Texture(texture)
|
||||
key = _Texture(texture=texture)
|
||||
if key not in self._pending:
|
||||
print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key)))
|
||||
self._pending[key] = [layer,]
|
||||
@ -226,6 +238,16 @@ class MaterialConverter:
|
||||
# We'll allow this, just for sanity's sake...
|
||||
pass
|
||||
|
||||
def export_prepared_layer(self, layer, image):
|
||||
"""This exports an externally prepared layer and image"""
|
||||
key = _Texture(image=image)
|
||||
if key not in self._pending:
|
||||
print(" Stashing '{}' for conversion as '{}'".format(image.name, str(key)))
|
||||
self._pending[key] = [layer,]
|
||||
else:
|
||||
print(" Found another user of '{}'".format(image.name))
|
||||
self._pending[key].append(layer)
|
||||
|
||||
def finalize(self):
|
||||
for key, layers in self._pending.items():
|
||||
name = str(key)
|
||||
@ -241,7 +263,7 @@ class MaterialConverter:
|
||||
|
||||
# Some basic mipmap settings.
|
||||
numLevels = math.floor(math.log(max(eWidth, eHeight), 2)) + 1 if key.mipmap else 1
|
||||
compression = plBitmap.kDirectXCompression if key.mipmap else plBitmap.kUncompressed
|
||||
compression = plBitmap.kDirectXCompression
|
||||
dxt = plBitmap.kDXT5 if key.use_alpha or key.calc_alpha else plBitmap.kDXT1
|
||||
|
||||
# Grab the image data from OpenGL and stuff it into the plBitmap
|
||||
@ -286,6 +308,9 @@ class MaterialConverter:
|
||||
mipmap = pages[page]
|
||||
layer.texture = mipmap.key
|
||||
|
||||
def get_materials(self, bo):
|
||||
return self._obj2mat[bo]
|
||||
|
||||
@property
|
||||
def _mgr(self):
|
||||
return self._exporter().mgr
|
||||
|
@ -217,7 +217,15 @@ class MeshConverter:
|
||||
return diface.key
|
||||
|
||||
def _export_mesh(self, bo):
|
||||
# First, we need to grab the object's mesh...
|
||||
# Step 0.8: If this mesh wants to be light mapped, we need to go ahead and generate it.
|
||||
if bo.plasma_modifiers.lightmap.enabled:
|
||||
print(" Baking lightmap...")
|
||||
print("====")
|
||||
bpy.context.scene.objects.active = bo
|
||||
bpy.ops.object.plasma_lightmap_autobake()
|
||||
print("====")
|
||||
|
||||
# Step 0.9: Update the mesh
|
||||
mesh = bo.data
|
||||
mesh.update(calc_tessface=True)
|
||||
|
||||
|
29
korman/helpers.py
Normal file
29
korman/helpers.py
Normal file
@ -0,0 +1,29 @@
|
||||
# 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/>.
|
||||
|
||||
class GoodNeighbor:
|
||||
"""Leave Things the Way You Found Them! (TM)"""
|
||||
|
||||
def __enter__(self):
|
||||
self._tracking = {}
|
||||
return self
|
||||
|
||||
def track(self, cls, attr, value):
|
||||
self._tracking[(cls, attr)] = getattr(cls, attr)
|
||||
setattr(cls, attr, value)
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
for (cls, attr), value in self._tracking.items():
|
||||
setattr(cls, attr, value)
|
@ -14,6 +14,7 @@
|
||||
# along with Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import op_export as exporter
|
||||
from . import op_lightmap as lightmap
|
||||
from . import op_modifier as modifier
|
||||
from . import op_world as world
|
||||
|
||||
|
@ -23,7 +23,6 @@ class ExportOperator(bpy.types.Operator):
|
||||
|
||||
bl_idname = "export.plasma_age"
|
||||
bl_label = "Export Age"
|
||||
bl_options = {"BLOCKING"}
|
||||
|
||||
# Export specific props
|
||||
version = bpy.props.EnumProperty(
|
||||
|
106
korman/operators/op_lightmap.py
Normal file
106
korman/operators/op_lightmap.py
Normal file
@ -0,0 +1,106 @@
|
||||
# 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 ..helpers import GoodNeighbor
|
||||
|
||||
class _LightmapOperator:
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.object is not None:
|
||||
return context.scene.render.engine == "PLASMA_GAME"
|
||||
|
||||
|
||||
class LightmapAutobakeOperator(_LightmapOperator, bpy.types.Operator):
|
||||
bl_idname = "object.plasma_lightmap_autobake"
|
||||
bl_label = "Bake Lightmap"
|
||||
bl_options = {"INTERNAL"}
|
||||
|
||||
def execute(self, context):
|
||||
with GoodNeighbor() as toggle:
|
||||
# We need to ensure that we bake onto the "BlahObject_LIGHTMAPGEN" image
|
||||
obj = context.active_object
|
||||
data_images = bpy.data.images
|
||||
im_name = "{}_LIGHTMAPGEN".format(obj.name)
|
||||
size = obj.plasma_modifiers.lightmap.resolution
|
||||
|
||||
im = data_images.get(im_name)
|
||||
if im is None:
|
||||
im = data_images.new(im_name, width=size, height=size)
|
||||
elif im.size != (size, size):
|
||||
# Force delete and recreate the image because the size is out of date
|
||||
im.user_clear()
|
||||
data_images.remove(im)
|
||||
im = data_images.new(im_name, width=size, height=size)
|
||||
|
||||
# This just wraps Blender's internal lightmap UV whatchacallit...
|
||||
# We want to ensure that we use the UV Layer "LIGHTMAPGEN" and fetch the size from
|
||||
# the lightmap modifier. What fun...
|
||||
mesh = context.active_object.data
|
||||
mesh.update()
|
||||
|
||||
# Search for LIGHTMAPGEN
|
||||
for uvtex in mesh.uv_textures:
|
||||
if uvtex.name == "LIGHTMAPGEN":
|
||||
toggle.track(mesh.uv_textures, "active", uvtex)
|
||||
break
|
||||
else:
|
||||
# Gotta make it
|
||||
uvtex = mesh.uv_textures.new("LIGHTMAPGEN")
|
||||
toggle.track(mesh.uv_textures, "active", uvtex)
|
||||
|
||||
# Now, enter edit mode on this mesh and unwrap.
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.uv.lightmap_pack(PREF_CONTEXT="ALL_FACES", PREF_IMG_PX_SIZE=size)
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
# Associate the image with all the new UVs
|
||||
for i in mesh.uv_textures.active.data:
|
||||
i.image = im
|
||||
|
||||
# Bake settings
|
||||
render = context.scene.render
|
||||
toggle.track(render, "bake_type", "FULL")
|
||||
toggle.track(render, "use_bake_to_vertex_color", False)
|
||||
|
||||
for mat in obj.data.materials:
|
||||
for tex in mat.texture_slots:
|
||||
if tex is not None and tex.use:
|
||||
toggle.track(tex, "use", False)
|
||||
|
||||
# Now, we *finally* bake the lightmap...
|
||||
# FIXME: Don't bake Plasma RT lights
|
||||
bpy.ops.object.bake_image()
|
||||
|
||||
# Done!
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LightmapAutobakePreviewOperator(_LightmapOperator, bpy.types.Operator):
|
||||
bl_idname = "object.plasma_lightmap_preview"
|
||||
bl_label = "Preview Lightmap"
|
||||
bl_options = {"INTERNAL"}
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.object.plasma_lightmap_autobake()
|
||||
|
||||
tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW")
|
||||
if tex is None:
|
||||
tex = bpy.data.textures.new("LIGHTMAPGEN_PREVIEW", "IMAGE")
|
||||
tex.extension = "CLIP"
|
||||
tex.image = bpy.data.images["{}_LIGHTMAPGEN".format(context.active_object.name)]
|
||||
|
||||
return {"FINISHED"}
|
@ -20,6 +20,7 @@ from .base import PlasmaModifierProperties
|
||||
from .logic import *
|
||||
from .physics import *
|
||||
from .region import *
|
||||
from .render import *
|
||||
|
||||
class PlasmaModifiers(bpy.types.PropertyGroup):
|
||||
def determine_next_id(self):
|
||||
|
82
korman/properties/modifiers/render.py
Normal file
82
korman/properties/modifiers/render.py
Normal file
@ -0,0 +1,82 @@
|
||||
# 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 *
|
||||
from PyHSPlasma import *
|
||||
|
||||
from .base import PlasmaModifierProperties
|
||||
|
||||
class PlasmaLightMapGen(PlasmaModifierProperties):
|
||||
pl_id = "lightmap"
|
||||
|
||||
bl_category = "Render"
|
||||
bl_label = "Lightmap"
|
||||
bl_description = "Auto-Bake Lightmap"
|
||||
|
||||
quality = EnumProperty(name="Quality",
|
||||
description="Resolution of lightmap",
|
||||
items=[
|
||||
("128", "128px", "128x128 pixels"),
|
||||
("256", "256px", "256x256 pixels"),
|
||||
("512", "512px", "512x512 pixels"),
|
||||
("1024", "1024px", "1024x1024 pixels"),
|
||||
])
|
||||
|
||||
def created(self, obj):
|
||||
self.display_name = "{}_LIGHTMAPGEN".format(obj.name)
|
||||
|
||||
def export(self, exporter, bo, so):
|
||||
mat_mgr = exporter.mesh.material
|
||||
materials = mat_mgr.get_materials(bo)
|
||||
lightmap_im = bpy.data.images.get("{}_LIGHTMAPGEN".format(bo.name))
|
||||
|
||||
# Find the stupid UVTex
|
||||
uvw_src = 0
|
||||
for i, uvtex in enumerate(bo.data.tessface_uv_textures):
|
||||
if uvtex.name == "LIGHTMAPGEN":
|
||||
uvw_src = i
|
||||
break
|
||||
else:
|
||||
# TODO: raise exception
|
||||
pass
|
||||
|
||||
for matKey in materials:
|
||||
layer = exporter.mgr.add_object(plLayer, name="{}_LIGHTMAPGEN".format(matKey.name), so=so)
|
||||
layer.UVWSrc = uvw_src
|
||||
|
||||
# Colors science'd from PRPs
|
||||
layer.ambient = hsColorRGBA(1.0, 1.0, 1.0)
|
||||
layer.preshade = hsColorRGBA(0.5, 0.5, 0.5)
|
||||
layer.runtime = hsColorRGBA(0.5, 0.5, 0.5)
|
||||
|
||||
# GMatState
|
||||
gstate = layer.state
|
||||
gstate.blendFlags |= hsGMatState.kBlendMult
|
||||
gstate.clampFlags |= (hsGMatState.kClampTextureU | hsGMatState.kClampTextureV)
|
||||
gstate.ZFlags |= hsGMatState.kZNoZWrite
|
||||
gstate.miscFlags |= hsGMatState.kMiscLightMap
|
||||
|
||||
mat = matKey.object
|
||||
mat.compFlags |= hsGMaterial.kCompIsLightMapped
|
||||
mat.addPiggyBack(layer.key)
|
||||
|
||||
# Mmm... cheating
|
||||
mat_mgr.export_prepared_layer(layer, lightmap_im)
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return int(self.quality)
|
@ -16,3 +16,4 @@
|
||||
from .logic import *
|
||||
from .physics import *
|
||||
from .region import *
|
||||
from .render import *
|
||||
|
31
korman/ui/modifiers/render.py
Normal file
31
korman/ui/modifiers/render.py
Normal file
@ -0,0 +1,31 @@
|
||||
# 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 lightmap(modifier, layout, context):
|
||||
col = layout.column(align=True)
|
||||
col.row(align=True).prop(modifier, "quality", expand=True)
|
||||
col.operator("object.plasma_lightmap_preview", "Preview Lightmap", icon="RENDER_STILL")
|
||||
|
||||
# Kind of clever stuff to show the user a preview...
|
||||
# We can't show images, so we make a hidden ImageTexture called LIGHTMAPGEN_PREVIEW. We check
|
||||
# the backing image name to see if it's for this lightmap. If so, you have a preview. If not,
|
||||
# well... It was nice knowing you!
|
||||
tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW")
|
||||
if tex is not None:
|
||||
im_name = "{}_LIGHTMAPGEN".format(context.active_object.name)
|
||||
if tex.image.name == im_name:
|
||||
layout.template_preview(tex, show_buttons=False)
|
Reference in New Issue
Block a user