mirror of
https://github.com/H-uru/korman.git
synced 2025-07-14 22:36:52 +00:00
Refactor lighting code to limit bake_image calls
This commit is contained in:
@ -20,6 +20,7 @@ import time
|
||||
|
||||
from . import animation
|
||||
from . import explosions
|
||||
from . import etlight
|
||||
from . import logger
|
||||
from . import manager
|
||||
from . import mesh
|
||||
@ -65,6 +66,11 @@ class Exporter:
|
||||
# that the artist made requires something to have a CoordinateInterface
|
||||
self._harvest_actors()
|
||||
|
||||
# Step 2.9: It is assumed that static lighting is available for the mesh exporter.
|
||||
# Indeed, in PyPRP it was a manual step. So... BAKE NAO!
|
||||
if self._op.bake_lighting:
|
||||
self._bake_static_lighting()
|
||||
|
||||
# Step 3: Export all the things!
|
||||
self._export_scene_objects()
|
||||
|
||||
@ -91,6 +97,10 @@ class Exporter:
|
||||
end = time.process_time()
|
||||
print("\nExported {}.age in {:.2f} seconds".format(self.age_name, end-start))
|
||||
|
||||
def _bake_static_lighting(self):
|
||||
oven = etlight.LightBaker()
|
||||
oven.bake_static_lighting(self._objects)
|
||||
|
||||
def _collect_objects(self):
|
||||
# Grab a naive listing of enabled pages
|
||||
age = bpy.context.scene.world.plasma_age
|
||||
|
315
korman/exporter/etlight.py
Normal file
315
korman/exporter/etlight.py
Normal file
@ -0,0 +1,315 @@
|
||||
# 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 .mesh import _VERTEX_COLOR_LAYERS
|
||||
from ..helpers import *
|
||||
|
||||
_NUM_RENDER_LAYERS = 20
|
||||
|
||||
class LightBaker:
|
||||
"""ExportTime Lighting"""
|
||||
|
||||
def __init__(self):
|
||||
self._lightgroups = {}
|
||||
self._uvtexs = {}
|
||||
|
||||
def _apply_render_settings(self, toggle):
|
||||
render = bpy.context.scene.render
|
||||
toggle.track(render, "use_textures", False)
|
||||
toggle.track(render, "use_shadows", True)
|
||||
toggle.track(render, "use_envmaps", False)
|
||||
toggle.track(render, "use_raytrace", True)
|
||||
toggle.track(render, "bake_type", "FULL")
|
||||
toggle.track(render, "use_bake_clear", True)
|
||||
|
||||
def _associate_image_with_uvtex(self, uvtex, im):
|
||||
# Associate the image with all the new UVs
|
||||
# NOTE: no toggle here because it's the artist's problem if they are looking at our
|
||||
# super swagalicious LIGHTMAPGEN uvtexture...
|
||||
for i in uvtex.data:
|
||||
i.image = im
|
||||
|
||||
def _bake_lightmaps(self, objs, layers, toggle):
|
||||
scene = bpy.context.scene
|
||||
scene.layers = layers
|
||||
toggle.track(scene.render, "use_bake_to_vertex_color", False)
|
||||
self._select_only(objs)
|
||||
bpy.ops.object.bake_image()
|
||||
|
||||
def _bake_vcols(self, objs, toggle):
|
||||
bpy.context.scene.layers = (True,) * _NUM_RENDER_LAYERS
|
||||
toggle.track(bpy.context.scene.render, "use_bake_to_vertex_color", True)
|
||||
self._select_only(objs)
|
||||
bpy.ops.object.bake_image()
|
||||
|
||||
def bake_static_lighting(self, objs):
|
||||
"""Bakes all static lighting for Plasma geometry"""
|
||||
|
||||
print("\nBaking Static Lighting...")
|
||||
bake = self._harvest_bakable_objects(objs)
|
||||
|
||||
with GoodNeighbor() as toggle:
|
||||
try:
|
||||
# reduce the amount of indentation
|
||||
self._bake_static_lighting(bake, toggle)
|
||||
finally:
|
||||
# this stuff has been observed to be problematic with GoodNeighbor
|
||||
self._pop_lightgroups()
|
||||
self._restore_uvtexs()
|
||||
|
||||
def _bake_static_lighting(self, bake, toggle):
|
||||
# Step 0.9: Make all layers visible.
|
||||
# This prevents context operators from phailing.
|
||||
bpy.context.scene.layers = (True,) * _NUM_RENDER_LAYERS
|
||||
|
||||
# Step 1: Prepare... Apply UVs, etc, etc, etc
|
||||
for key, value in bake.copy().items():
|
||||
if key[0] == "lightmap":
|
||||
for i in value:
|
||||
if not self._prep_for_lightmap(i, toggle):
|
||||
bake[key].remove(i)
|
||||
elif key[0] == "vcol":
|
||||
for i in value:
|
||||
if not self._prep_for_vcols(i, toggle):
|
||||
bake[key].remove(i)
|
||||
else:
|
||||
raise RuntimeError(key[0])
|
||||
|
||||
# Step 2: BAKE!
|
||||
self._apply_render_settings(toggle)
|
||||
for key, value in bake.items():
|
||||
if not value:
|
||||
continue
|
||||
|
||||
if key[0] == "lightmap":
|
||||
print(" {} Lightmap(s) [H:{:X}]".format(len(value), hash(key)))
|
||||
self._bake_lightmaps(value, key[1:], toggle)
|
||||
elif key[0] == "vcol":
|
||||
print(" {} Crap Light(s)".format(len(value)))
|
||||
self._bake_vcols(value, toggle)
|
||||
else:
|
||||
raise RuntimeError(key[0])
|
||||
|
||||
def _generate_lightgroup(self, mesh, user_lg=None):
|
||||
"""Makes a new light group for the baking process that excludes all Plasma RT lamps"""
|
||||
|
||||
if user_lg is not None:
|
||||
user_lg = bpy.data.groups.get(user_lg)
|
||||
shouldibake = (user_lg and user_lg.objects)
|
||||
|
||||
for material in mesh.materials:
|
||||
if material is None:
|
||||
# material is not assigned to this material... (why is this even a thing?)
|
||||
continue
|
||||
|
||||
# Already done it?
|
||||
name = material.name
|
||||
lg = material.light_group
|
||||
if name in self._lightgroups:
|
||||
# No, this is not Pythonic, but bpy_prop_collection is always "True",
|
||||
# even when empty. Sigh.
|
||||
return bool(len(lg.objects))
|
||||
else:
|
||||
self._lightgroups[name] = lg
|
||||
|
||||
if user_lg is None:
|
||||
if not lg or len(lg.objects) == 0:
|
||||
source = [i for i in bpy.data.objects if i.type == "LAMP"]
|
||||
else:
|
||||
source = lg.objects
|
||||
dest = bpy.data.groups.new("_LIGHTMAPGEN_{}".format(name))
|
||||
|
||||
# Only use non-RT lights
|
||||
for obj in source:
|
||||
if obj.plasma_object.enabled:
|
||||
continue
|
||||
dest.objects.link(obj)
|
||||
shouldibake = True
|
||||
else:
|
||||
dest = user_lg
|
||||
material.light_group = dest
|
||||
return shouldibake
|
||||
|
||||
def _get_lightmap_uvtex(self, mesh, modifier):
|
||||
if modifier.uv_map:
|
||||
return mesh.uv_textures[modifier.uv_map]
|
||||
for i in mesh.uv_textures:
|
||||
if i.name != "LIGHTMAPGEN":
|
||||
return i
|
||||
return None
|
||||
|
||||
def _harvest_bakable_objects(self, objs):
|
||||
# The goal here is to minimize the calls to bake_image, so we are going to collect everything
|
||||
# that needs to be baked and sort it out by configuration.
|
||||
bake = { ("vcol",): [] }
|
||||
for i in objs:
|
||||
if i.type != "MESH":
|
||||
continue
|
||||
|
||||
mods = i.plasma_modifiers
|
||||
if mods.lightmap.enabled:
|
||||
## TODO: customizable render layers
|
||||
#key = ("lightmap",) + mods.lightmap.render_layers
|
||||
key = ("lightmap",) + ((True,) * 20)
|
||||
if key in bake:
|
||||
bake[key].append(i)
|
||||
else:
|
||||
bake[key] = [i,]
|
||||
elif not mods.water_basic.enabled:
|
||||
vcols = i.data.vertex_colors
|
||||
for j in _VERTEX_COLOR_LAYERS:
|
||||
if j in vcols:
|
||||
break
|
||||
else:
|
||||
bake[("vcol",)].append(i)
|
||||
return bake
|
||||
|
||||
def _pop_lightgroups(self):
|
||||
for mat_name, lg in self._lightgroups.items():
|
||||
material = bpy.data.materials[mat_name]
|
||||
_fake = material.light_group
|
||||
if _fake is not None and _fake.name.startswith("_LIGHTMAPGEN"):
|
||||
for i in _fake.objects:
|
||||
_fake.objects.unlink(i)
|
||||
_fake.user_clear()
|
||||
bpy.data.groups.remove(_fake)
|
||||
material.light_group = lg
|
||||
self._lightgroups.clear()
|
||||
|
||||
def _prep_for_lightmap(self, bo, toggle):
|
||||
mesh = bo.data
|
||||
modifier = bo.plasma_modifiers.lightmap
|
||||
uv_textures = mesh.uv_textures
|
||||
|
||||
# Create a special light group for baking
|
||||
if not self._generate_lightgroup(mesh, modifier.light_group):
|
||||
return False
|
||||
|
||||
# We need to ensure that we bake onto the "BlahObject_LIGHTMAPGEN" image
|
||||
data_images = bpy.data.images
|
||||
im_name = "{}_LIGHTMAPGEN.png".format(bo.name)
|
||||
size = modifier.resolution
|
||||
|
||||
im = data_images.get(im_name)
|
||||
if im is None:
|
||||
im = data_images.new(im_name, width=size, height=size)
|
||||
elif im.size[0] != 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)
|
||||
|
||||
# If there is a cached LIGHTMAPGEN uvtexture, nuke it
|
||||
uvtex = uv_textures.get("LIGHTMAPGEN", None)
|
||||
if uvtex is not None:
|
||||
uv_textures.remove(uvtex)
|
||||
|
||||
# Make sure the object can be baked to. NOTE this also makes sure we can enter edit mode
|
||||
# TROLLING LOL LOL LOL
|
||||
toggle.track(bo, "hide", False)
|
||||
toggle.track(bo, "hide_render", False)
|
||||
toggle.track(bo, "hide_select", False)
|
||||
|
||||
# Because the way Blender tracks active UV layers is massively stupid...
|
||||
self._uvtexs[mesh.name] = uv_textures.active.name
|
||||
|
||||
# We must make this the active object before touching any operators
|
||||
bpy.context.scene.objects.active = bo
|
||||
|
||||
# Originally, we used the lightmap unpack UV operator to make our UV texture, however,
|
||||
# this tended to create sharp edges. There was already a discussion about this on the
|
||||
# Guild of Writers forum, so I'm implementing a code version of dendwaler's process,
|
||||
# as detailed here: https://forum.guildofwriters.org/viewtopic.php?p=62572#p62572
|
||||
uv_base = self._get_lightmap_uvtex(mesh, modifier)
|
||||
if uv_base is not None:
|
||||
uv_textures.active = uv_base
|
||||
# this will copy the UVs to the new UV texture
|
||||
uvtex = uv_textures.new("LIGHTMAPGEN")
|
||||
uv_textures.active = uvtex
|
||||
self._associate_image_with_uvtex(uvtex, im)
|
||||
# here we go...
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.uv.average_islands_scale()
|
||||
bpy.ops.uv.pack_islands()
|
||||
else:
|
||||
# same thread, see Sirius's suggestion RE smart unwrap. this seems to yield good
|
||||
# results in my tests. it will be good enough for quick exports.
|
||||
uvtex = uv_textures.new("LIGHTMAPGEN")
|
||||
self._associate_image_with_uvtex(uvtex, im)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.uv.smart_project()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
# Now, set the new LIGHTMAPGEN uv layer as what we want to render to...
|
||||
# NOTE that this will need to be reset by us to what the user had previously
|
||||
# Not using toggle.track due to observed oddities
|
||||
for i in uv_textures:
|
||||
value = i.name == "LIGHTMAPGEN"
|
||||
i.active = value
|
||||
i.active_render = value
|
||||
|
||||
# Indicate we should bake
|
||||
return True
|
||||
|
||||
def _prep_for_vcols(self, bo, toggle):
|
||||
mesh = bo.data
|
||||
vcols = mesh.vertex_colors
|
||||
|
||||
# Create a special light group for baking
|
||||
if not self._generate_lightgroup(mesh):
|
||||
return False
|
||||
|
||||
# Make sure the object can be baked to. NOTE this also makes sure we can enter edit mode
|
||||
# TROLLING LOL LOL LOL
|
||||
toggle.track(bo, "hide", False)
|
||||
toggle.track(bo, "hide_render", False)
|
||||
toggle.track(bo, "hide_select", False)
|
||||
|
||||
# I have heard tale of some moar "No valid image to bake to" boogs if there is a really
|
||||
# old copy of the autocolor layer on the mesh. Nuke it.
|
||||
autocolor = vcols.get("autocolor")
|
||||
if autocolor is not None:
|
||||
vcols.remove(autocolor)
|
||||
autocolor = vcols.new("autocolor")
|
||||
toggle.track(vcols, "active", autocolor)
|
||||
|
||||
# Mark "autocolor" as our active render layer
|
||||
for vcol_layer in mesh.vertex_colors:
|
||||
autocol = vcol_layer.name == "autocolor"
|
||||
toggle.track(vcol_layer, "active_render", autocol)
|
||||
toggle.track(vcol_layer, "active", autocol)
|
||||
mesh.update()
|
||||
|
||||
# Indicate we should bake
|
||||
return True
|
||||
|
||||
def _restore_uvtexs(self):
|
||||
for mesh_name, uvtex_name in self._uvtexs.items():
|
||||
mesh = bpy.data.meshes[mesh_name]
|
||||
for i in mesh.uv_textures:
|
||||
i.active = uvtex_name == i.name
|
||||
mesh.uv_textures.active = mesh.uv_textures[uvtex_name]
|
||||
|
||||
def _select_only(self, objs):
|
||||
if isinstance(objs, bpy.types.Object):
|
||||
for i in bpy.data.objects:
|
||||
i.select = i == objs
|
||||
else:
|
||||
for i in bpy.data.objects:
|
||||
i.select = i in objs
|
@ -278,9 +278,6 @@ class MeshConverter:
|
||||
if not materials:
|
||||
return None
|
||||
|
||||
# Step 0.9: If this mesh wants to be lit, we need to go ahead and generate it.
|
||||
self._export_static_lighting(bo)
|
||||
|
||||
with helpers.TemporaryObject(mesh, bpy.data.meshes.remove):
|
||||
# Step 1: Export all of the doggone materials.
|
||||
geospans = self._export_material_spans(bo, mesh, materials)
|
||||
@ -330,22 +327,6 @@ class MeshConverter:
|
||||
geospans[i] = (self._create_geospan(bo, mesh, blmat, matKey), blmat.pass_index)
|
||||
return geospans
|
||||
|
||||
def _export_static_lighting(self, bo):
|
||||
bpy.context.scene.objects.active = bo
|
||||
mods = bo.plasma_modifiers
|
||||
lm = mods.lightmap
|
||||
if lm.enabled:
|
||||
print(" Baking lightmap...")
|
||||
bpy.ops.object.plasma_lightmap_autobake(light_group=lm.light_group)
|
||||
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:
|
||||
break
|
||||
else:
|
||||
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:
|
||||
|
@ -47,22 +47,6 @@ class TemporaryObject:
|
||||
return getattr(self._obj, attr)
|
||||
|
||||
|
||||
def ensure_object_can_bake(bo, toggle):
|
||||
"""Ensures that we can use Blender's baking operators on this object. Side effect: also ensures
|
||||
that the object will enter edit mode when requested."""
|
||||
scene = bpy.context.scene
|
||||
# we don't toggle.track this because it actually updates some kind of Blender internals...
|
||||
# therefore the old and new value are equal. the export operator will take care of this for us
|
||||
scene.layers = (True,) * len(scene.layers)
|
||||
|
||||
toggle.track(bo, "hide", False)
|
||||
toggle.track(bo, "hide_render", False)
|
||||
toggle.track(bo, "hide_select", False)
|
||||
|
||||
# Ensure only this object is selected
|
||||
for i in bpy.data.objects:
|
||||
i.select = i == bo
|
||||
|
||||
def ensure_power_of_two(value):
|
||||
return pow(2, math.floor(math.log(value, 2)))
|
||||
|
||||
|
@ -37,13 +37,9 @@ class ExportOperator(bpy.types.Operator):
|
||||
"description": "Profiles the exporter using cProfile",
|
||||
"default": False}),
|
||||
|
||||
"regenerate_lightmaps": (BoolProperty, {"name": "Regenerate Lightmaps",
|
||||
"description": "(Re-)Bake all lightmaps on export",
|
||||
"default": True}),
|
||||
|
||||
"regenerate_shading": (BoolProperty, {"name": "Regenerate Vertex Shading",
|
||||
"description": "(Re-)Bake all vertex shading on export",
|
||||
"default": True}),
|
||||
"bake_lighting": (BoolProperty, {"name": "Bake Static Lights",
|
||||
"description": "Bake all lightmaps and vertex shading on export",
|
||||
"default": True}),
|
||||
|
||||
"version": (EnumProperty, {"name": "Version",
|
||||
"description": "Version of the Plasma Engine to target",
|
||||
@ -60,8 +56,7 @@ class ExportOperator(bpy.types.Operator):
|
||||
|
||||
# The crazy mess we're doing with props on the fly means we have to explicitly draw them :(
|
||||
layout.prop(age, "version")
|
||||
layout.prop(age, "regenerate_lightmaps")
|
||||
layout.prop(age, "regenerate_shading")
|
||||
layout.prop(age, "bake_lighting")
|
||||
layout.prop(age, "profile_export")
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
@ -15,192 +15,16 @@
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from ..helpers import *
|
||||
|
||||
def _fetch_lamp_objects():
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == "LAMP":
|
||||
yield obj
|
||||
from ..exporter.etlight import LightBaker
|
||||
|
||||
class _LightingOperator:
|
||||
def __init__(self):
|
||||
self._old_lightgroups = {}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.object is not None:
|
||||
return context.scene.render.engine == "PLASMA_GAME"
|
||||
|
||||
def _apply_render_settings(self, render, toggle):
|
||||
toggle.track(render, "use_textures", False)
|
||||
toggle.track(render, "use_shadows", True)
|
||||
toggle.track(render, "use_envmaps", False)
|
||||
toggle.track(render, "use_raytrace", True)
|
||||
toggle.track(render, "bake_type", "FULL")
|
||||
toggle.track(render, "use_bake_clear", True)
|
||||
|
||||
def _generate_lightgroups(self, mesh, user_lg=None):
|
||||
"""Makes a new light group for the baking process that excludes all Plasma RT lamps"""
|
||||
shouldibake = (user_lg and user_lg.objects)
|
||||
|
||||
for material in mesh.materials:
|
||||
if material is None:
|
||||
# material is not assigned to this material... (why is this even a thing?)
|
||||
continue
|
||||
|
||||
lg = material.light_group
|
||||
self._old_lightgroups[material] = lg
|
||||
|
||||
if user_lg is None:
|
||||
# TODO: faux-lightgroup caching for the entire export process. you dig?
|
||||
if not lg or len(lg.objects) == 0:
|
||||
source = _fetch_lamp_objects()
|
||||
else:
|
||||
source = lg.objects
|
||||
dest = bpy.data.groups.new("_LIGHTMAPGEN_{}".format(material.name))
|
||||
|
||||
# Only use non-RT lights
|
||||
for obj in source:
|
||||
if obj.plasma_object.enabled:
|
||||
continue
|
||||
dest.objects.link(obj)
|
||||
shouldibake = True
|
||||
else:
|
||||
dest = user_lg
|
||||
material.light_group = dest
|
||||
return shouldibake
|
||||
|
||||
def _pop_lightgroups(self):
|
||||
for material, lg in self._old_lightgroups.items():
|
||||
_fake = material.light_group
|
||||
if _fake is not None and _fake.name.startswith("_LIGHTMAPGEN"):
|
||||
for i in _fake.objects:
|
||||
_fake.objects.unlink(i)
|
||||
_fake.user_clear()
|
||||
bpy.data.groups.remove(_fake)
|
||||
material.light_group = lg
|
||||
self._old_lightgroups.clear()
|
||||
|
||||
|
||||
class LightmapAutobakeOperator(_LightingOperator, bpy.types.Operator):
|
||||
bl_idname = "object.plasma_lightmap_autobake"
|
||||
bl_label = "Bake Lightmap"
|
||||
bl_options = {"INTERNAL"}
|
||||
|
||||
light_group = StringProperty(name="Light Group")
|
||||
force = BoolProperty(name="Force Lightmap Generation", default=False)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def _associate_image_with_uvtex(self, uvtex, im):
|
||||
# Associate the image with all the new UVs
|
||||
# NOTE: no toggle here because it's the artist's problem if they are looking at our
|
||||
# super swagalicious LIGHTMAPGEN uvtexture...
|
||||
for i in uvtex.data:
|
||||
i.image = im
|
||||
|
||||
def _get_base_uvtex(self, mesh, modifier):
|
||||
if modifier.uv_map:
|
||||
return mesh.uv_textures[modifier.uv_map]
|
||||
for i in mesh.uv_textures:
|
||||
if i.name != "LIGHTMAPGEN":
|
||||
return i
|
||||
return None
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
mesh = obj.data
|
||||
modifier = obj.plasma_modifiers.lightmap
|
||||
uv_textures = mesh.uv_textures
|
||||
|
||||
with GoodNeighbor() as toggle:
|
||||
# We need to ensure that we bake onto the "BlahObject_LIGHTMAPGEN" image
|
||||
data_images = bpy.data.images
|
||||
im_name = "{}_LIGHTMAPGEN.png".format(obj.name)
|
||||
size = modifier.resolution
|
||||
|
||||
im = data_images.get(im_name)
|
||||
if im is None:
|
||||
im = data_images.new(im_name, width=size, height=size)
|
||||
elif im.size[0] != 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)
|
||||
elif not (context.scene.world.plasma_age.regenerate_lightmaps or self.force):
|
||||
# we have a lightmap that matches our specs, so what gives???
|
||||
# this baking process is one slow thing. only do it if the user wants us to!
|
||||
return {"CANCELLED"}
|
||||
|
||||
# If there is a cached LIGHTMAPGEN uvtexture, nuke it
|
||||
uvtex = uv_textures.get("LIGHTMAPGEN", None)
|
||||
if uvtex is not None:
|
||||
uv_textures.remove(uvtex)
|
||||
|
||||
# Make sure the object can be baked to. NOTE this also makes sure we can enter edit mode
|
||||
# TROLLING LOL LOL LOL
|
||||
ensure_object_can_bake(obj, toggle)
|
||||
|
||||
# Because the way Blender tracks active UV layers is massively stupid...
|
||||
og_uv_map = uv_textures.active
|
||||
|
||||
# Originally, we used the lightmap unpack UV operator to make our UV texture, however,
|
||||
# this tended to create sharp edges. There was already a discussion about this on the
|
||||
# Guild of Writers forum, so I'm implementing a code version of dendwaler's process,
|
||||
# as detailed here: http://forum.guildofwriters.org/viewtopic.php?p=62572#p62572
|
||||
uv_base = self._get_base_uvtex(mesh, modifier)
|
||||
if uv_base is not None:
|
||||
uv_textures.active = uv_base
|
||||
# this will copy the UVs to the new UV texture
|
||||
uvtex = uv_textures.new("LIGHTMAPGEN")
|
||||
uv_textures.active = uvtex
|
||||
self._associate_image_with_uvtex(uvtex, im)
|
||||
# here we go...
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.uv.average_islands_scale()
|
||||
bpy.ops.uv.pack_islands()
|
||||
else:
|
||||
# same thread, see Sirius's suggestion RE smart unwrap. this seems to yield good
|
||||
# results in my tests. it will be good enough for quick exports.
|
||||
uvtex = uv_textures.new("LIGHTMAPGEN")
|
||||
self._associate_image_with_uvtex(uvtex, im)
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.uv.smart_project()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
# Now, set the new LIGHTMAPGEN uv layer as what we want to render to...
|
||||
for i in uv_textures:
|
||||
value = i.name == "LIGHTMAPGEN"
|
||||
i.active = value
|
||||
i.active_render = value
|
||||
|
||||
# Bake settings
|
||||
render = context.scene.render
|
||||
toggle.track(render, "use_bake_to_vertex_color", False)
|
||||
self._apply_render_settings(render, toggle)
|
||||
|
||||
# Now, we *finally* bake the lightmap...
|
||||
try:
|
||||
light_group = bpy.data.groups[self.light_group] if self.light_group else None
|
||||
if self._generate_lightgroups(mesh, light_group):
|
||||
bpy.ops.object.bake_image()
|
||||
im.pack(as_png=True)
|
||||
self._pop_lightgroups()
|
||||
finally:
|
||||
for i, uv_tex in enumerate(uv_textures):
|
||||
# once this executes the og_uv_map is apparently no longer in the UVTextures :/
|
||||
# search by name to find the REAL uv texture that we need.
|
||||
if uv_tex.name == og_uv_map.name:
|
||||
uv_textures.active = uv_tex
|
||||
uv_textures.active_index = i
|
||||
break
|
||||
|
||||
# Done!
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LightmapAutobakePreviewOperator(_LightingOperator, bpy.types.Operator):
|
||||
bl_idname = "object.plasma_lightmap_preview"
|
||||
@ -213,7 +37,8 @@ class LightmapAutobakePreviewOperator(_LightingOperator, bpy.types.Operator):
|
||||
super().__init__()
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.object.plasma_lightmap_autobake(light_group=self.light_group, force=True)
|
||||
bake = LightBaker()
|
||||
bake.bake_static_lighting([context.active_object,])
|
||||
|
||||
tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW")
|
||||
if tex is None:
|
||||
@ -222,53 +47,3 @@ class LightmapAutobakePreviewOperator(_LightingOperator, bpy.types.Operator):
|
||||
tex.image = bpy.data.images["{}_LIGHTMAPGEN.png".format(context.active_object.name)]
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VertexColorLightingOperator(_LightingOperator, bpy.types.Operator):
|
||||
bl_idname = "object.plasma_vertexlight_autobake"
|
||||
bl_label = "Bake Vertex Color Lighting"
|
||||
bl_options = {"INTERNAL"}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def execute(self, context):
|
||||
with GoodNeighbor() as toggle:
|
||||
obj = context.active_object
|
||||
mesh = obj.data
|
||||
vcols = mesh.vertex_colors
|
||||
|
||||
# I have heard tale of some moar "No valid image to bake to" boogs if there is a really
|
||||
# old copy of the autocolor layer on the mesh. Nuke it.
|
||||
autocolor = vcols.get("autocolor")
|
||||
if autocolor is not None:
|
||||
if context.scene.world.plasma_age.regenerate_shading:
|
||||
vcols.remove(autocolor)
|
||||
else:
|
||||
# we have autocolor already, don't regenerate it because they don't want it
|
||||
return {"CANCELLED"}
|
||||
autocolor = vcols.new("autocolor")
|
||||
toggle.track(vcols, "active", autocolor)
|
||||
|
||||
# Mark "autocolor" as our active render layer
|
||||
for vcol_layer in mesh.vertex_colors:
|
||||
autocol = vcol_layer.name == "autocolor"
|
||||
toggle.track(vcol_layer, "active_render", autocol)
|
||||
toggle.track(vcol_layer, "active", autocol)
|
||||
mesh.update()
|
||||
|
||||
# Bake settings
|
||||
render = context.scene.render
|
||||
toggle.track(render, "use_bake_to_vertex_color", True)
|
||||
self._apply_render_settings(render, toggle)
|
||||
|
||||
# Really and truly make sure we can bake...
|
||||
ensure_object_can_bake(obj, toggle)
|
||||
|
||||
# Bake
|
||||
if self._generate_lightgroups(mesh):
|
||||
bpy.ops.object.bake_image()
|
||||
self._pop_lightgroups()
|
||||
|
||||
# And done!
|
||||
return {"FINISHED"}
|
||||
|
Reference in New Issue
Block a user