Browse Source

Merge pull request #75 from Hoikas/proc-mods

Fix #63
pull/82/head
Adam Johnson 7 years ago committed by GitHub
parent
commit
1b45152734
  1. 63
      korman/exporter/convert.py
  2. 11
      korman/exporter/etlight.py
  3. 130
      korman/exporter/mesh.py
  4. 8
      korman/operators/op_lightmap.py

63
korman/exporter/convert.py

@ -53,7 +53,8 @@ class Exporter:
self.animation = animation.AnimationConverter(self) self.animation = animation.AnimationConverter(self)
self.sumfile = sumfile.SumFile() self.sumfile = sumfile.SumFile()
# Step 0.9: Init the progress mgr # Step 0.8: Init the progress mgr
self.mesh.add_progress_presteps(self.report)
self.report.progress_add_step("Collecting Objects") self.report.progress_add_step("Collecting Objects")
self.report.progress_add_step("Harvesting Actors") self.report.progress_add_step("Harvesting Actors")
if self._op.bake_lighting: if self._op.bake_lighting:
@ -65,44 +66,46 @@ class Exporter:
self.report.progress_add_step("Composing Geometry") self.report.progress_add_step("Composing Geometry")
self.report.progress_start("EXPORTING AGE") self.report.progress_start("EXPORTING AGE")
# Step 1: Create the age info and the pages # Step 0.9: Apply modifiers to all meshes temporarily.
self._export_age_info() with self.mesh:
# Step 1: Create the age info and the pages
self._export_age_info()
# Step 2: Gather a list of objects that we need to export, given what the user has told # Step 2: Gather a list of objects that we need to export, given what the user has told
# us to export (both in the Age and Object Properties)... fun # us to export (both in the Age and Object Properties)... fun
self._collect_objects() self._collect_objects()
# Step 2.5: Run through all the objects we collected in Step 2 and see if any relationships # Step 2.5: Run through all the objects we collected in Step 2 and see if any relationships
# that the artist made requires something to have a CoordinateInterface # that the artist made requires something to have a CoordinateInterface
self._harvest_actors() self._harvest_actors()
# Step 2.9: It is assumed that static lighting is available for the mesh exporter. # 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! # Indeed, in PyPRP it was a manual step. So... BAKE NAO!
if self._op.bake_lighting: if self._op.bake_lighting:
self._bake_static_lighting() self._bake_static_lighting()
# Step 3: Export all the things! # Step 3: Export all the things!
self._export_scene_objects() self._export_scene_objects()
# Step 3.1: Ensure referenced logic node trees are exported # Step 3.1: Ensure referenced logic node trees are exported
self._export_referenced_node_trees() self._export_referenced_node_trees()
# Step 3.2: Now that all Plasma Objects (save Mipmaps) are exported, we do any post # Step 3.2: Now that all Plasma Objects (save Mipmaps) are exported, we do any post
# processing that needs to inspect those objects # processing that needs to inspect those objects
self._post_process_scene_objects() self._post_process_scene_objects()
# Step 4: Finalize... # Step 4: Finalize...
self.mesh.material.finalize() self.mesh.material.finalize()
self.mesh.finalize() self.mesh.finalize()
# Step 5: FINALLY. Let's write the PRPs and crap. # Step 5: FINALLY. Let's write the PRPs and crap.
self.mgr.save_age(Path(self._op.filepath)) self.mgr.save_age(Path(self._op.filepath))
# Step 5.1: Save out the export report. # Step 5.1: Save out the export report.
# If the export fails and this doesn't save, we have bigger problems than # If the export fails and this doesn't save, we have bigger problems than
# these little warnings and notices. # these little warnings and notices.
self.report.progress_end() self.report.progress_end()
self.report.save() self.report.save()
def _bake_static_lighting(self): def _bake_static_lighting(self):
oven = etlight.LightBaker(self.report) oven = etlight.LightBaker(self.report)

11
korman/exporter/etlight.py

@ -17,24 +17,25 @@ import bpy
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
from .logger import ExportProgressLogger from .logger import ExportProgressLogger
from .mesh import _VERTEX_COLOR_LAYERS from .mesh import _MeshManager, _VERTEX_COLOR_LAYERS
from ..helpers import * from ..helpers import *
_NUM_RENDER_LAYERS = 20 _NUM_RENDER_LAYERS = 20
class LightBaker: class LightBaker(_MeshManager):
"""ExportTime Lighting""" """ExportTime Lighting"""
def __init__(self, report=None): def __init__(self, report=None):
self._lightgroups = {} self._lightgroups = {}
if report is None: if report is None:
self._report = ExportProgressLogger() self._report = ExportProgressLogger()
self.add_progress_steps(self._report) self.add_progress_steps(self._report, True)
self._report.progress_start("PREVIEWING LIGHTING") self._report.progress_start("PREVIEWING LIGHTING")
self._own_report = True self._own_report = True
else: else:
self._report = report self._report = report
self._own_report = False self._own_report = False
super().__init__(self._report)
self._uvtexs = {} self._uvtexs = {}
def __del__(self): def __del__(self):
@ -42,7 +43,9 @@ class LightBaker:
self._report.progress_end() self._report.progress_end()
@staticmethod @staticmethod
def add_progress_steps(report): def add_progress_steps(report, add_base=False):
if add_base:
_MeshManager.add_progress_presteps(report)
report.progress_add_step("Searching for Bahro") report.progress_add_step("Searching for Bahro")
report.progress_add_step("Baking Static Lighting") report.progress_add_step("Baking Static Lighting")

130
korman/exporter/mesh.py

@ -18,6 +18,7 @@ from PyHSPlasma import *
from math import fabs from math import fabs
import weakref import weakref
from ..exporter.logger import ExportProgressLogger
from . import explosions from . import explosions
from .. import helpers from .. import helpers
from . import material from . import material
@ -111,7 +112,71 @@ class _GeoData:
self.vertices = [] self.vertices = []
class MeshConverter:
class _MeshManager:
def __init__(self, report=None):
if report is not None:
self._report = report
self._overrides = {}
@staticmethod
def add_progress_presteps(report):
report.progress_add_step("Applying Blender Mods")
def _build_prop_dict(self, bstruct):
props = {}
for i in bstruct.bl_rna.properties:
ident = i.identifier
if ident == "rna_type":
continue
props[ident] = getattr(bstruct, ident) if getattr(i, "array_length", 0) == 0 else tuple(getattr(bstruct, ident))
return props
def __enter__(self):
self._report.progress_advance()
self._report.progress_range = len(bpy.data.objects)
# Some modifiers like "Array" will procedurally generate new geometry that will impact
# lightmap generation. The Blender Internal renderer does not seem to be smart enough to
# take this into account. Thus, we temporarily apply modifiers to ALL meshes (even ones that
# are not exported) such that we can generate proper lighting.
scene = bpy.context.scene
for i in bpy.data.objects:
if i.type == "MESH" and i.is_modified(scene, "RENDER"):
# Remember, storing actual pointers to the Blender objects can cause bad things to
# happen because Blender's memory management SUCKS!
self._overrides[i.name] = { "mesh": i.data.name, "modifiers": [] }
i.data = i.to_mesh(scene, True, "RENDER", calc_tessface=False)
# If the modifiers are left on the object, the lightmap bake can break under some
# situations. Therefore, we now cache the modifiers and clear them away...
if i.plasma_object.enabled:
cache_mods = self._overrides[i.name]["modifiers"]
for mod in i.modifiers:
cache_mods.append(self._build_prop_dict(mod))
i.modifiers.clear()
self._report.progress_increment()
return self
def __exit__(self, type, value, traceback):
data_bos, data_meshes = bpy.data.objects, bpy.data.meshes
for obj_name, override in self._overrides.items():
bo = data_bos.get(obj_name)
# Reapply the old mesh
trash_mesh, bo.data = bo.data, data_meshes.get(override["mesh"])
data_meshes.remove(trash_mesh)
# If modifiers were removed, reapply them now.
for cached_mod in override["modifiers"]:
mod = bo.modifiers.new(cached_mod["name"], cached_mod["type"])
for key, value in cached_mod.items():
if key in {"name", "type"}:
continue
setattr(mod, key, value)
class MeshConverter(_MeshManager):
def __init__(self, exporter): def __init__(self, exporter):
self._exporter = weakref.ref(exporter) self._exporter = weakref.ref(exporter)
self.material = material.MaterialConverter(exporter) self.material = material.MaterialConverter(exporter)
@ -119,6 +184,9 @@ class MeshConverter:
self._dspans = {} self._dspans = {}
self._mesh_geospans = {} self._mesh_geospans = {}
# _report is a property on this subclass
super().__init__()
def _calc_num_uvchans(self, bo, mesh): def _calc_num_uvchans(self, bo, mesh):
max_user_texs = plGeometrySpan.kUVCountMask max_user_texs = plGeometrySpan.kUVCountMask
num_user_texs = len(mesh.tessface_uv_textures) num_user_texs = len(mesh.tessface_uv_textures)
@ -403,8 +471,11 @@ class MeshConverter:
diface.addDrawable(dspan_key, idx) diface.addDrawable(dspan_key, idx)
def _export_mesh(self, bo): def _export_mesh(self, bo):
# Step 0.7: Update the mesh such that we can do things and schtuff... # Previously, this called bo.to_mesh to apply modifiers. However, due to limitations in the
mesh = bo.to_mesh(bpy.context.scene, True, "RENDER", calc_tessface=True) # lightmap generation, this is now done for all modified mesh objects before any Plasma data
# is exported.
mesh = bo.data
mesh.calc_tessface()
# Step 0.8: Figure out which materials are attached to this object. Because Blender is backwards, # Step 0.8: Figure out which materials are attached to this object. Because Blender is backwards,
# we can actually have materials that are None. gotdawgit!!! # we can actually have materials that are None. gotdawgit!!!
@ -412,33 +483,32 @@ class MeshConverter:
if not materials: if not materials:
return None return None
with helpers.TemporaryObject(mesh, bpy.data.meshes.remove): # Step 1: Export all of the doggone materials.
# Step 1: Export all of the doggone materials. geospans = self._export_material_spans(bo, mesh, materials)
geospans = self._export_material_spans(bo, mesh, materials)
# Step 2: Export Blender mesh data to Plasma GeometrySpans
# Step 2: Export Blender mesh data to Plasma GeometrySpans self._export_geometry(bo, mesh, materials, geospans)
self._export_geometry(bo, mesh, materials, geospans)
# Step 3: Add plGeometrySpans to the appropriate DSpan and create indices
# Step 3: Add plGeometrySpans to the appropriate DSpan and create indices _diindices = {}
_diindices = {} for geospan, pass_index in geospans:
for geospan, pass_index in geospans: dspan = self._find_create_dspan(bo, geospan.material.object, pass_index)
dspan = self._find_create_dspan(bo, geospan.material.object, pass_index) self._report.msg("Exported hsGMaterial '{}' geometry into '{}'",
self._report.msg("Exported hsGMaterial '{}' geometry into '{}'", geospan.material.name, dspan.key.name, indent=1)
geospan.material.name, dspan.key.name, indent=1) idx = dspan.addSourceSpan(geospan)
idx = dspan.addSourceSpan(geospan) if dspan not in _diindices:
if dspan not in _diindices: _diindices[dspan] = [idx,]
_diindices[dspan] = [idx,] else:
else: _diindices[dspan].append(idx)
_diindices[dspan].append(idx)
# Step 3.1: Harvest Span indices and create the DIIndices
# Step 3.1: Harvest Span indices and create the DIIndices drawables = []
drawables = [] for dspan, indices in _diindices.items():
for dspan, indices in _diindices.items(): dii = plDISpanIndex()
dii = plDISpanIndex() dii.indices = indices
dii.indices = indices idx = dspan.addDIIndex(dii)
idx = dspan.addDIIndex(dii) drawables.append((dspan.key, idx))
drawables.append((dspan.key, idx)) return drawables
return drawables
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"""

8
korman/operators/op_lightmap.py

@ -41,10 +41,10 @@ class LightmapAutobakePreviewOperator(_LightingOperator, bpy.types.Operator):
with GoodNeighbor() as toggle: with GoodNeighbor() as toggle:
toggle.track(context.scene, "layers", tuple(context.scene.layers)) toggle.track(context.scene, "layers", tuple(context.scene.layers))
bake = LightBaker() with LightBaker() as bake:
if not bake.bake_static_lighting([context.active_object,]): if not bake.bake_static_lighting([context.active_object,]):
self.report({"INFO"}, "No valid lights found to bake.") self.report({"INFO"}, "No valid lights found to bake.")
return {"FINISHED"} return {"FINISHED"}
tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW") tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW")
if tex is None: if tex is None:

Loading…
Cancel
Save