mirror of
https://github.com/H-uru/korman.git
synced 2025-07-14 02:27:36 -04:00
Refactor export logging
The export logger and export reporter have been merged together to form an eventually much more powerful export analysis feature. For now, the benefit is that general log messages don't have to be so fiddly with print statements and string formatting. You're welcome.
This commit is contained in:
@ -266,14 +266,19 @@ static int _generate_detail_map(pyGLTexture* self, uint8_t* buf, size_t bufsz, G
|
||||
return 0;
|
||||
}
|
||||
|
||||
static _LevelData _get_level_data(pyGLTexture* self, GLint level, bool bgra, bool quiet) {
|
||||
static _LevelData _get_level_data(pyGLTexture* self, GLint level, bool bgra, PyObject* report) {
|
||||
GLint width, height;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &height);
|
||||
GLenum fmt = bgra ? GL_BGRA_EXT : GL_RGBA;
|
||||
|
||||
if (!quiet)
|
||||
PySys_WriteStdout(" Level #%i: %ix%i\n", level, width, height);
|
||||
// Print out the debug message
|
||||
if (report && report != Py_None) {
|
||||
PyObjectRef msg_func = PyObject_GetAttrString(report, "msg");
|
||||
PyObjectRef args = Py_BuildValue("siii", "Level #{}: {}x{}", level, width, height);
|
||||
PyObjectRef kwargs = Py_BuildValue("{s:i}", "indent", 2);
|
||||
PyObjectRef result = PyObject_Call(msg_func, args, kwargs);
|
||||
}
|
||||
|
||||
size_t bufsz;
|
||||
bufsz = (width * height * 4);
|
||||
@ -284,18 +289,18 @@ static _LevelData _get_level_data(pyGLTexture* self, GLint level, bool bgra, boo
|
||||
|
||||
static PyObject* pyGLTexture_get_level_data(pyGLTexture* self, PyObject* args, PyObject* kwargs) {
|
||||
static char* kwlist[] = { _pycs("level"), _pycs("calc_alpha"), _pycs("bgra"),
|
||||
_pycs("quiet"), _pycs("fast"), NULL };
|
||||
_pycs("report"), _pycs("fast"), NULL };
|
||||
GLint level = 0;
|
||||
bool calc_alpha = false;
|
||||
bool bgra = false;
|
||||
bool quiet = false;
|
||||
PyObject* report = nullptr;
|
||||
bool fast = false;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ibbbb", kwlist, &level, &calc_alpha, &bgra, &quiet, &fast)) {
|
||||
PyErr_SetString(PyExc_TypeError, "get_level_data expects an optional int, bool, bool, bool, bool");
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ibbOb", kwlist, &level, &calc_alpha, &bgra, &report, &fast)) {
|
||||
PyErr_SetString(PyExc_TypeError, "get_level_data expects an optional int, bool, bool, obejct, bool");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_LevelData data = _get_level_data(self, level, bgra, quiet);
|
||||
_LevelData data = _get_level_data(self, level, bgra, report);
|
||||
if (fast)
|
||||
return pyBuffer_Steal(data.m_data, data.m_dataSize);
|
||||
|
||||
@ -372,7 +377,7 @@ static PyMethodDef pyGLTexture_Methods[] = {
|
||||
};
|
||||
|
||||
static PyObject* pyGLTexture_get_has_alpha(pyGLTexture* self, void*) {
|
||||
_LevelData data = _get_level_data(self, 0, false, true);
|
||||
_LevelData data = _get_level_data(self, 0, false, nullptr);
|
||||
for (size_t i = 3; i < data.m_dataSize; i += 4) {
|
||||
if (data.m_data[i] != 255) {
|
||||
delete[] data.m_data;
|
||||
|
@ -42,14 +42,13 @@ class Exporter:
|
||||
return Path(self._op.filepath).stem
|
||||
|
||||
def run(self):
|
||||
with logger.ExportLogger(self._op.filepath) as _log:
|
||||
print("Exporting '{}.age'".format(self.age_name))
|
||||
with logger.ExportLogger(self._op.filepath) as self.report:
|
||||
self.report.msg("Exporting '{}.age'", self.age_name)
|
||||
start = time.perf_counter()
|
||||
|
||||
# Step 0: Init export resmgr and stuff
|
||||
self.mgr = manager.ExportManager(self)
|
||||
self.mesh = mesh.MeshConverter(self)
|
||||
self.report = logger.ExportAnalysis()
|
||||
self.physics = physics.PhysicsConverter(self)
|
||||
self.light = rtlight.LightConverter(self)
|
||||
self.animation = animation.AnimationConverter(self)
|
||||
@ -95,10 +94,10 @@ class Exporter:
|
||||
|
||||
# And finally we crow about how awesomely fast we are...
|
||||
end = time.perf_counter()
|
||||
print("\nExported {}.age in {:.2f} seconds".format(self.age_name, end-start))
|
||||
self.report.msg("\nExported {}.age in {:.2f} seconds", self.age_name, end-start)
|
||||
|
||||
def _bake_static_lighting(self):
|
||||
oven = etlight.LightBaker()
|
||||
oven = etlight.LightBaker(self.report)
|
||||
oven.bake_static_lighting(self._objects)
|
||||
|
||||
def _collect_objects(self):
|
||||
@ -163,7 +162,7 @@ class Exporter:
|
||||
parent = bo.parent
|
||||
if parent is not None:
|
||||
if parent.plasma_object.enabled:
|
||||
print(" Attaching to parent SceneObject '{}'".format(parent.name))
|
||||
self.report.msg("Attaching to parent SceneObject '{}'", parent.name, indent=1)
|
||||
parent_ci = self._export_coordinate_interface(None, parent)
|
||||
parent_ci.addChild(so.key)
|
||||
else:
|
||||
@ -187,8 +186,9 @@ class Exporter:
|
||||
return so.coord.object
|
||||
|
||||
def _export_scene_objects(self):
|
||||
log_msg = self.report.msg
|
||||
for bl_obj in self._objects:
|
||||
print("\n[SceneObject '{}']".format(bl_obj.name))
|
||||
log_msg("\n[SceneObject '{}']".format(bl_obj.name))
|
||||
|
||||
# First pass: do things specific to this object type.
|
||||
# note the function calls: to export a MESH, it's _export_mesh_blobj
|
||||
@ -196,10 +196,10 @@ class Exporter:
|
||||
try:
|
||||
export_fn = getattr(self, export_fn)
|
||||
except AttributeError:
|
||||
print("WARNING: '{}' is a Plasma Object of Blender type '{}'".format(bl_obj.name, bl_obj.type))
|
||||
print("... And I have NO IDEA what to do with that! Tossing.")
|
||||
self.report.warn("""'{}' is a Plasma Object of Blender type '{}'
|
||||
... And I have NO IDEA what to do with that! Tossing.""".format(bl_obj.name, bl_obj.type))
|
||||
continue
|
||||
print(" Blender Object '{}' of type '{}'".format(bl_obj.name, bl_obj.type))
|
||||
log_msg("Blender Object '{}' of type '{}'".format(bl_obj.name, bl_obj.type), indent=1)
|
||||
|
||||
# Create a sceneobject if one does not exist.
|
||||
# Before we call the export_fn, we need to determine if this object is an actor of any
|
||||
@ -211,7 +211,7 @@ class Exporter:
|
||||
|
||||
# And now we puke out the modifiers...
|
||||
for mod in bl_obj.plasma_modifiers.modifiers:
|
||||
print(" Exporting '{}' modifier as '{}'".format(mod.bl_label, mod.key_name))
|
||||
log_msg("Exporting '{}' modifier".format(mod.bl_label), indent=1)
|
||||
mod.export(self, bl_obj, sceneobject)
|
||||
|
||||
def _export_empty_blobj(self, so, bo):
|
||||
@ -227,14 +227,14 @@ class Exporter:
|
||||
if bo.data.materials:
|
||||
self.mesh.export_object(bo)
|
||||
else:
|
||||
print(" No material(s) on the ObData, so no drawables")
|
||||
self.report.msg("No material(s) on the ObData, so no drawables", indent=1)
|
||||
|
||||
def _export_referenced_node_trees(self):
|
||||
print("\nChecking Logic Trees...")
|
||||
self.report.msg("\nChecking Logic Trees...")
|
||||
need_to_export = ((name, bo, so) for name, (bo, so) in self.want_node_trees.items()
|
||||
if name not in self.node_trees_exported)
|
||||
for tree, bo, so in need_to_export:
|
||||
print(" NodeTree '{}'".format(tree))
|
||||
self.report.msg("NodeTree '{}'", tree, indent=1)
|
||||
bpy.data.node_groups[tree].export(self, bo, so)
|
||||
|
||||
def _harvest_actors(self):
|
||||
@ -269,8 +269,6 @@ class Exporter:
|
||||
return False
|
||||
|
||||
def _post_process_scene_objects(self):
|
||||
print("\nPostprocessing SceneObjects...")
|
||||
|
||||
mat_mgr = self.mesh.material
|
||||
for bl_obj in self._objects:
|
||||
sceneobject = self.mgr.find_object(plSceneObject, bl=bl_obj)
|
||||
@ -292,5 +290,4 @@ class Exporter:
|
||||
for mod in bl_obj.plasma_modifiers.modifiers:
|
||||
proc = getattr(mod, "post_export", None)
|
||||
if proc is not None:
|
||||
print(" '{}' modifier '{}'".format(bl_obj.name, mod.key_name))
|
||||
proc(self, bl_obj, sceneobject)
|
||||
|
@ -16,6 +16,7 @@
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
from .logger import ExportLogger
|
||||
from .mesh import _VERTEX_COLOR_LAYERS
|
||||
from ..helpers import *
|
||||
|
||||
@ -24,8 +25,9 @@ _NUM_RENDER_LAYERS = 20
|
||||
class LightBaker:
|
||||
"""ExportTime Lighting"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, report=None):
|
||||
self._lightgroups = {}
|
||||
self._report = report if report is not None else ExportLogger()
|
||||
self._uvtexs = {}
|
||||
|
||||
def _apply_render_settings(self, toggle, vcols):
|
||||
@ -63,7 +65,7 @@ class LightBaker:
|
||||
def bake_static_lighting(self, objs):
|
||||
"""Bakes all static lighting for Plasma geometry"""
|
||||
|
||||
print("\nBaking Static Lighting...")
|
||||
self._report.msg("\nBaking Static Lighting...")
|
||||
bake = self._harvest_bakable_objects(objs)
|
||||
|
||||
with GoodNeighbor() as toggle:
|
||||
@ -82,24 +84,26 @@ class LightBaker:
|
||||
bpy.context.scene.layers = (True,) * _NUM_RENDER_LAYERS
|
||||
|
||||
# Step 1: Prepare... Apply UVs, etc, etc, etc
|
||||
print(" Preparing to bake...")
|
||||
self._report.msg("Preparing to bake...", indent=1)
|
||||
for key in bake.keys():
|
||||
if key[0] == "lightmap":
|
||||
for i in range(len(bake[key])-1, -1, -1):
|
||||
obj = bake[key][i]
|
||||
if not self._prep_for_lightmap(obj, toggle):
|
||||
print(" Lightmap '{}' will not be baked -- no applicable lights".format(obj.name))
|
||||
self._report.msg("Lightmap '{}' will not be baked -- no applicable lights",
|
||||
obj.name, indent=2)
|
||||
bake[key].pop(i)
|
||||
elif key[0] == "vcol":
|
||||
for i in range(len(bake[key])-1, -1, -1):
|
||||
obj = bake[key][i]
|
||||
if not self._prep_for_vcols(obj, toggle):
|
||||
if self._has_valid_material(obj):
|
||||
print(" VCols '{}' will not be baked -- no applicable lights".format(obj.name))
|
||||
self._report.msg("VCols '{}' will not be baked -- no applicable lights",
|
||||
obj.name, indent=2)
|
||||
bake[key].pop(i)
|
||||
else:
|
||||
raise RuntimeError(key[0])
|
||||
print(" ...")
|
||||
self._report.msg(" ...")
|
||||
|
||||
# Step 2: BAKE!
|
||||
for key, value in bake.items():
|
||||
@ -107,10 +111,10 @@ class LightBaker:
|
||||
continue
|
||||
|
||||
if key[0] == "lightmap":
|
||||
print(" {} Lightmap(s) [H:{:X}]".format(len(value), hash(key)))
|
||||
self._report.msg("{} Lightmap(s) [H:{:X}]", len(value), hash(key), indent=1)
|
||||
self._bake_lightmaps(value, key[1:])
|
||||
elif key[0] == "vcol":
|
||||
print(" {} Crap Light(s)".format(len(value)))
|
||||
self._report.msg("{} Crap Light(s)", len(value), indent=1)
|
||||
self._bake_vcols(value)
|
||||
else:
|
||||
raise RuntimeError(key[0])
|
||||
|
@ -16,61 +16,62 @@
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
class ExportAnalysis:
|
||||
"""This is used to collect artist action items from the export process. You can warn about
|
||||
portability issues, possible oversights, etc. The benefit here is that the user doesn't have
|
||||
to look through all of the gobbledygook in the export log.
|
||||
"""
|
||||
class ExportLogger:
|
||||
def __init__(self, age_path=None):
|
||||
self._porting = []
|
||||
self._warnings = []
|
||||
self._age_path = age_path
|
||||
self._file = None
|
||||
|
||||
_porting = []
|
||||
_warnings = []
|
||||
def __enter__(self):
|
||||
assert self._age_path is not None
|
||||
|
||||
# Make the log file name from the age file path -- this ensures we're not trying to write
|
||||
# the log file to the same directory Blender.exe is in, which might be a permission error
|
||||
my_path = Path(self._age_path)
|
||||
my_path = my_path.with_name("{}_export".format(my_path.stem)).with_suffix(".log")
|
||||
self._file = open(str(my_path), "w")
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self._file.close()
|
||||
return False
|
||||
|
||||
def msg(self, *args, **kwargs):
|
||||
assert args
|
||||
indent = kwargs.get("indent", 0)
|
||||
msg = "{}{}".format(" " * indent, args[0])
|
||||
if len(args) > 1:
|
||||
msg = msg.format(*args[1:], **kwargs)
|
||||
if self._file is None:
|
||||
print(msg)
|
||||
else:
|
||||
self._file.writelines((msg, "\n"))
|
||||
|
||||
def port(self, *args, **kwargs):
|
||||
assert args
|
||||
indent = kwargs.get("indent", 0)
|
||||
msg = "{}PORTING: {}".format(" " * indent, args[0])
|
||||
if len(args) > 1:
|
||||
msg = msg.format(*args[1:], **kwargs)
|
||||
if self._file is None:
|
||||
print(msg)
|
||||
else:
|
||||
self._file.writelines((msg, "\n"))
|
||||
self._porting.append(args[0])
|
||||
|
||||
def save(self):
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def port(self, message, indent=0):
|
||||
self._porting.append(message)
|
||||
print(" " * indent, end="")
|
||||
print("PORTING: {}".format(message))
|
||||
|
||||
def warn(self, message, indent=0):
|
||||
self._warnings.append(message)
|
||||
print(" " * indent, end="")
|
||||
print("WARNING: {}".format(message))
|
||||
|
||||
|
||||
class ExportLogger:
|
||||
"""Yet Another Logger(TM)"""
|
||||
|
||||
def __init__(self, ageFile):
|
||||
# Make the log file name from the age file path -- this ensures we're not trying to write
|
||||
# the log file to the same directory Blender.exe is in, which might be a permission error
|
||||
my_path = Path(ageFile)
|
||||
my_path = my_path.with_name("{}_export".format(my_path.stem)).with_suffix(".log")
|
||||
self._file = open(str(my_path), "w")
|
||||
|
||||
for i in dir(self._file):
|
||||
if not hasattr(self, i):
|
||||
setattr(self, i, getattr(self._file, i))
|
||||
|
||||
def __enter__(self):
|
||||
self._stdout, sys.stdout = sys.stdout, self._file
|
||||
self._stderr, sys.stderr = sys.stderr, self._file
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
sys.stdout = self._stdout
|
||||
sys.stderr = self._stderr
|
||||
|
||||
def flush(self):
|
||||
self._file.flush()
|
||||
self._stdout.flush()
|
||||
self._stderr.flush()
|
||||
|
||||
def write(self, str):
|
||||
self._file.write(str)
|
||||
self._stdout.write(str)
|
||||
|
||||
def writelines(self, seq):
|
||||
self._file.writelines(seq)
|
||||
self._stdout.writelines(seq)
|
||||
def warn(self, *args, **kwargs):
|
||||
assert args
|
||||
indent = kwargs.get("indent", 0)
|
||||
msg = "{}WARNING: {}".format(" " * indent, args[0])
|
||||
if len(args) > 1:
|
||||
msg = msg.format(*args[1:], **kwargs)
|
||||
if self._file is None:
|
||||
print(msg)
|
||||
else:
|
||||
self._file.writelines((msg, "\n"))
|
||||
self._warnings.append(args[0])
|
||||
|
@ -125,7 +125,7 @@ class MaterialConverter:
|
||||
|
||||
def export_material(self, bo, bm):
|
||||
"""Exports a Blender Material as an hsGMaterial"""
|
||||
print(" Exporting Material '{}'".format(bm.name))
|
||||
self._report.msg("Exporting Material '{}'", bm.name, indent=1)
|
||||
|
||||
hsgmat = self._mgr.add_object(hsGMaterial, name=bm.name, bl=bo)
|
||||
slots = [(idx, slot) for idx, slot in enumerate(bm.texture_slots) if slot is not None and slot.use \
|
||||
@ -198,7 +198,7 @@ class MaterialConverter:
|
||||
return hsgmat.key
|
||||
|
||||
def export_waveset_material(self, bo, bm):
|
||||
print(" Exporting WaveSet Material '{}'".format(bm.name))
|
||||
self._report.msg("Exporting WaveSet Material '{}'", bm.name, indent=1)
|
||||
|
||||
# WaveSets MUST have their own material
|
||||
unique_name = "{}_WaveSet7".format(bm.name)
|
||||
@ -215,7 +215,7 @@ class MaterialConverter:
|
||||
|
||||
def export_bumpmap_slot(self, bo, bm, hsgmat, slot, idx):
|
||||
name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name)
|
||||
print(" Exporting Plasma Bumpmap Layers for '{}'".format(name))
|
||||
self._report.msg("Exporting Plasma Bumpmap Layers for '{}'", name, indent=2)
|
||||
|
||||
# Okay, now we need to make 3 layers for the Du, Dw, and Dv
|
||||
du_layer = self._mgr.add_object(plLayer, name="{}_DU_BumpLut".format(name), bl=bo)
|
||||
@ -264,7 +264,7 @@ class MaterialConverter:
|
||||
def export_texture_slot(self, bo, bm, hsgmat, slot, idx, name=None, blend_flags=True):
|
||||
if name is None:
|
||||
name = "{}_{}".format(bm.name if bm is not None else bo.name, slot.name)
|
||||
print(" Exporting Plasma Layer '{}'".format(name))
|
||||
self._report.msg("Exporting Plasma Layer '{}'", name, indent=2)
|
||||
layer = self._mgr.add_object(plLayer, name=name, bl=bo)
|
||||
if bm is not None and not slot.use_map_normal:
|
||||
self._propagate_material_settings(bm, layer)
|
||||
@ -274,10 +274,10 @@ class MaterialConverter:
|
||||
for i, uvchan in enumerate(bo.data.uv_layers):
|
||||
if uvchan.name == slot.uv_layer:
|
||||
layer.UVWSrc = i
|
||||
print(" Using UV Map #{} '{}'".format(i, name))
|
||||
self._report.msg("Using UV Map #{} '{}'", i, name, indent=3)
|
||||
break
|
||||
else:
|
||||
print(" No UVMap specified... Blindly using the first one, maybe it exists :|")
|
||||
self._report.msg("No UVMap specified... Blindly using the first one, maybe it exists :|", indent=3)
|
||||
|
||||
# Transform
|
||||
xform = hsMatrix44()
|
||||
@ -447,7 +447,8 @@ class MaterialConverter:
|
||||
name = "{}_DynEnvMap".format(viewpt.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))
|
||||
self._report.msg("EnvMap for viewpoint {} already exported... NOTE: Your settings here will be overridden by the previous object!",
|
||||
viewpt.name, indent=3)
|
||||
if isinstance(pl_env, plDynamicCamMap):
|
||||
pl_env.addTargetNode(self._mgr.find_key(plSceneObject, bl=bo))
|
||||
pl_env.addMatLayer(layer.key)
|
||||
@ -457,7 +458,7 @@ class MaterialConverter:
|
||||
oRes = bl_env.resolution
|
||||
eRes = helpers.ensure_power_of_two(oRes)
|
||||
if oRes != eRes:
|
||||
print(" Overriding EnvMap size to ({}x{}) -- POT".format(eRes, eRes))
|
||||
self._report.msg("Overriding EnvMap size to ({}x{}) -- POT", eRes, eRes, indent=3)
|
||||
|
||||
# And now for the general ho'hum-ness
|
||||
pl_env = self._mgr.add_object(pl_class, bl=bo, name=name)
|
||||
@ -595,10 +596,11 @@ class MaterialConverter:
|
||||
detail_fade_start=layer_props.detail_fade_start, detail_fade_stop=layer_props.detail_fade_stop,
|
||||
detail_opacity_start=layer_props.detail_opacity_start, detail_opacity_stop=layer_props.detail_opacity_stop)
|
||||
if key not in self._pending:
|
||||
print(" Stashing '{}' for conversion as '{}'".format(texture.image.name, str(key)))
|
||||
self._report.msg("Stashing '{}' for conversion as '{}'",
|
||||
texture.image.name, str(key), indent=3)
|
||||
self._pending[key] = [layer.key,]
|
||||
else:
|
||||
print(" Found another user of '{}'".format(texture.image.name))
|
||||
self._report.msg("Found another user of '{}'", texture.image.name, indent=3)
|
||||
self._pending[key].append(layer.key)
|
||||
|
||||
def _export_texture_type_none(self, bo, layer, texture):
|
||||
@ -609,16 +611,16 @@ class MaterialConverter:
|
||||
"""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._report.msg("Stashing '{}' for conversion as '{}'", image.name, key, indent=2)
|
||||
self._pending[key] = [layer.key,]
|
||||
else:
|
||||
print(" Found another user of '{}'".format(key))
|
||||
self._report.msg("Found another user of '{}'", key, indent=2)
|
||||
self._pending[key].append(layer.key)
|
||||
|
||||
def finalize(self):
|
||||
for key, layers in self._pending.items():
|
||||
name = str(key)
|
||||
print("\n[Mipmap '{}']".format(name))
|
||||
self._report.msg("\n[Mipmap '{}']", name)
|
||||
|
||||
image = key.image
|
||||
oWidth, oHeight = image.size
|
||||
@ -628,7 +630,8 @@ class MaterialConverter:
|
||||
eWidth = helpers.ensure_power_of_two(oWidth)
|
||||
eHeight = helpers.ensure_power_of_two(oHeight)
|
||||
if (eWidth != oWidth) or (eHeight != oHeight):
|
||||
print(" Image is not a POT ({}x{}) resizing to {}x{}".format(oWidth, oHeight, eWidth, eHeight))
|
||||
self._report.msg("Image is not a POT ({}x{}) resizing to {}x{}",
|
||||
oWidth, oHeight, eWidth, eHeight, indent=1)
|
||||
self._resize_image(image, eWidth, eHeight)
|
||||
|
||||
# Some basic mipmap settings.
|
||||
@ -640,11 +643,11 @@ class MaterialConverter:
|
||||
with helper as glimage:
|
||||
if key.mipmap:
|
||||
numLevels = glimage.num_levels
|
||||
print(" Generating mip levels")
|
||||
self._report.msg("Generating mip levels", indent=1)
|
||||
glimage.generate_mipmap()
|
||||
else:
|
||||
numLevels = 1
|
||||
print(" Stuffing image data")
|
||||
self._report.msg("Stuffing image data", indent=1)
|
||||
|
||||
# Uncompressed bitmaps are BGRA
|
||||
fmt = compression == plBitmap.kUncompressed
|
||||
@ -653,7 +656,7 @@ class MaterialConverter:
|
||||
# this mipmap for per-page textures :(
|
||||
data = []
|
||||
for i in range(numLevels):
|
||||
data.append(glimage.get_level_data(i, key.calc_alpha, fmt))
|
||||
data.append(glimage.get_level_data(i, key.calc_alpha, fmt, report=self._report))
|
||||
|
||||
# Be a good citizen and reset the Blender Image to pre-futzing state
|
||||
image.reload()
|
||||
@ -663,9 +666,9 @@ class MaterialConverter:
|
||||
mgr = self._mgr
|
||||
pages = {}
|
||||
|
||||
print(" Adding to Layer(s)")
|
||||
self._report.msg("Adding to Layer(s)", indent=1)
|
||||
for layer in layers:
|
||||
print(" {}".format(layer.name))
|
||||
self._report.msg(layer.name, indent=2)
|
||||
page = mgr.get_textures_page(layer) # Layer's page or Textures.prp
|
||||
|
||||
# If we haven't created this plMipmap in the page (either layer's page or Textures.prp),
|
||||
@ -722,6 +725,10 @@ class MaterialConverter:
|
||||
layer.runtime = utils.color(bm.diffuse_color)
|
||||
layer.specular = utils.color(bm.specular_color)
|
||||
|
||||
@property
|
||||
def _report(self):
|
||||
return self._exporter().report
|
||||
|
||||
def _resize_image(self, image, width, height):
|
||||
image.scale(width, height)
|
||||
image.update()
|
||||
|
@ -177,8 +177,8 @@ class MeshConverter:
|
||||
|
||||
for loc in self._dspans.values():
|
||||
for dspan in loc.values():
|
||||
print("\n[DrawableSpans '{}']".format(dspan.key.name))
|
||||
print(" Composing geometry data")
|
||||
self._report.msg("\n[DrawableSpans '{}']", dspan.key.name)
|
||||
self._report.msg("Composing geometry data", indent=1)
|
||||
|
||||
# This mega-function does a lot:
|
||||
# 1. Converts SourceSpans (geospans) to Icicles and bakes geometry into plGBuffers
|
||||
@ -189,7 +189,7 @@ class MeshConverter:
|
||||
|
||||
# Might as well say something else just to fascinate anyone who is playing along
|
||||
# at home (and actually enjoys reading these lawgs)
|
||||
print(" Bounds and SpaceTree in the saddle")
|
||||
self._report.msg("Bounds and SpaceTree in the saddle", indent=1)
|
||||
|
||||
def _export_geometry(self, bo, mesh, materials, geospans):
|
||||
geodata = [_GeoData(len(mesh.vertices)) for i in materials]
|
||||
@ -422,7 +422,8 @@ class MeshConverter:
|
||||
_diindices = {}
|
||||
for geospan, pass_index in geospans:
|
||||
dspan = self._find_create_dspan(bo, geospan.material.object, pass_index)
|
||||
print(" Exported hsGMaterial '{}' geometry into '{}'".format(geospan.material.name, dspan.key.name))
|
||||
self._report.msg("Exported hsGMaterial '{}' geometry into '{}'",
|
||||
geospan.material.name, dspan.key.name, indent=1)
|
||||
idx = dspan.addSourceSpan(geospan)
|
||||
if dspan not in _diindices:
|
||||
_diindices[dspan] = [idx,]
|
||||
@ -497,3 +498,7 @@ class MeshConverter:
|
||||
@property
|
||||
def _mgr(self):
|
||||
return self._exporter().mgr
|
||||
|
||||
@property
|
||||
def _report(self):
|
||||
return self._exporter().report
|
||||
|
@ -43,19 +43,19 @@ class LightConverter:
|
||||
# If you change these calculations, be sure to update the AnimationConverter!
|
||||
intens, attenEnd = self.convert_attenuation(bl)
|
||||
if bl.falloff_type == "CONSTANT":
|
||||
print(" Attenuation: No Falloff")
|
||||
self._report.msg("Attenuation: No Falloff", indent=2)
|
||||
pl.attenConst = intens
|
||||
pl.attenLinear = 0.0
|
||||
pl.attenQuadratic = 0.0
|
||||
pl.attenCutoff = attenEnd
|
||||
elif bl.falloff_type == "INVERSE_LINEAR":
|
||||
print(" Attenuation: Inverse Linear")
|
||||
self._report.msg("Attenuation: Inverse Linear", indent=2)
|
||||
pl.attenConst = 1.0
|
||||
pl.attenLinear = self.convert_attenuation_linear(intens, attenEnd)
|
||||
pl.attenQuadratic = 0.0
|
||||
pl.attenCutoff = attenEnd
|
||||
elif bl.falloff_type == "INVERSE_SQUARE":
|
||||
print(" Attenuation: Inverse Square")
|
||||
self._report.msg("Attenuation: Inverse Square", indent=2)
|
||||
pl.attenConst = 1.0
|
||||
pl.attenLinear = 0.0
|
||||
pl.attenQuadratic = self.convert_attenuation_quadratic(intens, attenEnd)
|
||||
@ -75,18 +75,18 @@ class LightConverter:
|
||||
return max(0.0, (intensity * _FAR_POWER - 1.0) / pow(end, 2))
|
||||
|
||||
def _convert_area_lamp(self, bl, pl):
|
||||
print(" [LimitedDirLightInfo '{}']".format(bl.name))
|
||||
self._report.msg("[LimitedDirLightInfo '{}']", bl.name, indent=1)
|
||||
|
||||
pl.width = bl.size
|
||||
pl.depth = bl.size if bl.shape == "SQUARE" else bl.size_y
|
||||
pl.height = bl.plasma_lamp.size_height
|
||||
|
||||
def _convert_point_lamp(self, bl, pl):
|
||||
print(" [OmniLightInfo '{}']".format(bl.name))
|
||||
self._report.msg("[OmniLightInfo '{}']", bl.name, indent=1)
|
||||
self._convert_attenuation(bl, pl)
|
||||
|
||||
def _convert_spot_lamp(self, bl, pl):
|
||||
print(" [SpotLightInfo '{}']".format(bl.name))
|
||||
self._report.msg("[SpotLightInfo '{}']", bl.name, indent=1)
|
||||
self._convert_attenuation(bl, pl)
|
||||
|
||||
# Spot lights have a few more things...
|
||||
@ -102,7 +102,7 @@ class LightConverter:
|
||||
pl.falloff = 1.0
|
||||
|
||||
def _convert_sun_lamp(self, bl, pl):
|
||||
print(" [DirectionalLightInfo '{}']".format(bl.name))
|
||||
self._report.msg("[DirectionalLightInfo '{}']", bl.name, indent=1)
|
||||
|
||||
def export_rtlight(self, so, bo):
|
||||
bl_light = bo.data
|
||||
@ -132,18 +132,18 @@ class LightConverter:
|
||||
|
||||
# Apply the colors
|
||||
if bl_light.use_diffuse and not shadow_only:
|
||||
print(" Diffuse: {}".format(diff_str))
|
||||
self._report.msg("Diffuse: {}", diff_str, indent=2)
|
||||
pl_light.diffuse = hsColorRGBA(*diff_color)
|
||||
else:
|
||||
print(" Diffuse: OFF")
|
||||
self._report.msg("Diffuse: OFF", indent=2)
|
||||
pl_light.diffuse = hsColorRGBA(0.0, 0.0, 0.0, energy)
|
||||
|
||||
if bl_light.use_specular and not shadow_only:
|
||||
print(" Specular: {}".format(spec_str))
|
||||
self._report.msg("Specular: {}", spec_str, indent=2)
|
||||
pl_light.setProperty(plLightInfo.kLPHasSpecular, True)
|
||||
pl_light.specular = hsColorRGBA(*spec_color)
|
||||
else:
|
||||
print(" Specular: OFF")
|
||||
self._report.msg("Specular: OFF", indent=2)
|
||||
pl_light.specular = hsColorRGBA(0.0, 0.0, 0.0, energy)
|
||||
|
||||
rtlamp = bl_light.plasma_lamp
|
||||
@ -202,7 +202,7 @@ class LightConverter:
|
||||
# projection Lamp with our own faux Material. Unfortunately, Plasma only supports projecting
|
||||
# one layer. We could exploit the fUnderLay and fOverLay system to export everything, but meh.
|
||||
if len(tex_slots) > 1:
|
||||
self._exporter().warn("Only one texture slot can be exported per Lamp. Picking the first one: '{}'".format(slot.name), indent=3)
|
||||
self._report.warn("Only one texture slot can be exported per Lamp. Picking the first one: '{}'".format(slot.name), indent=3)
|
||||
layer = mat.export_texture_slot(bo, None, None, slot, 0, blend_flags=False)
|
||||
state = layer.state
|
||||
|
||||
@ -243,7 +243,7 @@ class LightConverter:
|
||||
def find_material_light_keys(self, bo, bm):
|
||||
"""Given a blender material, we find the keys of all matching Plasma RT Lights.
|
||||
NOTE: We return a tuple of lists: ([permaLights], [permaProjs])"""
|
||||
print(" Searching for runtime lights...")
|
||||
self._report.msg("Searching for runtime lights...", indent=1)
|
||||
permaLights = []
|
||||
permaProjs = []
|
||||
|
||||
@ -272,16 +272,17 @@ class LightConverter:
|
||||
break
|
||||
else:
|
||||
# didn't find a layer where both lamp and object were, skip it.
|
||||
print(" [{}] '{}': not in same layer, skipping...".format(lamp.type, obj.name))
|
||||
self._report.msg("[{}] '{}': not in same layer, skipping...",
|
||||
lamp.type, obj.name, indent=2)
|
||||
continue
|
||||
|
||||
# This is probably where PermaLight vs PermaProj should be sorted out...
|
||||
pl_light = self.get_light_key(obj, lamp, None)
|
||||
if self._is_projection_lamp(lamp):
|
||||
print(" [{}] PermaProj '{}'".format(lamp.type, obj.name))
|
||||
self._report.msg("[{}] PermaProj '{}'", lamp.type, obj.name, indent=2)
|
||||
permaProjs.append(pl_light)
|
||||
else:
|
||||
print(" [{}] PermaLight '{}'".format(lamp.type, obj.name))
|
||||
self._report.msg("[{}] PermaLight '{}'", lamp.type, obj.name, indent=2)
|
||||
permaLights.append(pl_light)
|
||||
|
||||
return (permaLights, permaProjs)
|
||||
@ -308,3 +309,7 @@ class LightConverter:
|
||||
@property
|
||||
def mgr(self):
|
||||
return self._exporter().mgr
|
||||
|
||||
@property
|
||||
def _report(self):
|
||||
return self._exporter().report
|
||||
|
@ -76,14 +76,14 @@ class GLTexture:
|
||||
# It will simplify our state tracking a bit.
|
||||
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_GENERATE_MIPMAP, 1)
|
||||
|
||||
def get_level_data(self, level=0, calc_alpha=False, bgra=False, quiet=False, fast=False):
|
||||
def get_level_data(self, level=0, calc_alpha=False, bgra=False, report=None, fast=False):
|
||||
"""Gets the uncompressed pixel data for a requested mip level, optionally calculating the alpha
|
||||
channel from the image color data
|
||||
"""
|
||||
width = self._get_tex_param(bgl.GL_TEXTURE_WIDTH, level)
|
||||
height = self._get_tex_param(bgl.GL_TEXTURE_HEIGHT, level)
|
||||
if not quiet:
|
||||
print(" Level #{}: {}x{}".format(level, width, height))
|
||||
if report is not None:
|
||||
report.msg("Level #{}: {}x{}", level, width, height, indent=2)
|
||||
|
||||
# Grab the image data
|
||||
size = width * height * 4
|
||||
@ -138,7 +138,7 @@ class GLTexture:
|
||||
|
||||
@property
|
||||
def has_alpha(self):
|
||||
data = self.get_level_data(quiet=True, fast=True)
|
||||
data = self.get_level_data(report=None, fast=True)
|
||||
for i in range(3, len(data), 4):
|
||||
if data[i] != 255:
|
||||
return True
|
||||
|
@ -427,18 +427,18 @@ class PlasmaVolumeSensorNode(PlasmaNodeBase, bpy.types.Node):
|
||||
suffix = "Exit"
|
||||
|
||||
theName = "{}_{}_{}".format(self.id_data.name, self.name, suffix)
|
||||
print(" [LogicModifier '{}']".format(theName))
|
||||
exporter.report.msg("[LogicModifier '{}']", theName, indent=2)
|
||||
logicKey = exporter.mgr.find_create_key(plLogicModifier, name=theName, so=so)
|
||||
logicmod = logicKey.object
|
||||
logicmod.setLogicFlag(plLogicModifier.kMultiTrigger, True)
|
||||
logicmod.notify = self.generate_notify_msg(exporter, so, "satisfies")
|
||||
|
||||
# Now, the detector objects
|
||||
print(" [ObjectInVolumeDetector '{}']".format(theName))
|
||||
exporter.report.msg("[ObjectInVolumeDetector '{}']", theName, indent=2)
|
||||
detKey = exporter.mgr.find_create_key(plObjectInVolumeDetector, name=theName, so=so)
|
||||
det = detKey.object
|
||||
|
||||
print(" [VolumeSensorConditionalObject '{}']".format(theName))
|
||||
exporter.report.msg("[VolumeSensorConditionalObject '{}']", theName, indent=2)
|
||||
volKey = exporter.mgr.find_create_key(plVolumeSensorConditionalObject, name=theName, so=so)
|
||||
volsens = volKey.object
|
||||
|
||||
|
@ -427,10 +427,10 @@ class PlasmaVisControl(PlasmaModifierProperties):
|
||||
else:
|
||||
this_sv = bo.plasma_modifiers.softvolume
|
||||
if this_sv.enabled:
|
||||
print(" [VisRegion] I'm a SoftVolume myself :)")
|
||||
exporter.report.msg("[VisRegion] I'm a SoftVolume myself :)", indent=1)
|
||||
rgn.region = this_sv.get_key(exporter, so)
|
||||
else:
|
||||
print(" [VisRegion] SoftVolume '{}'".format(self.softvolume))
|
||||
exporter.report.msg("[VisRegion] SoftVolume '{}'", self.softvolume, indent=1)
|
||||
sv_bo = bpy.data.objects.get(self.softvolume, None)
|
||||
if sv_bo is None:
|
||||
raise ExportError("'{}': Invalid object '{}' for VisControl soft volume".format(bo.name, self.softvolume))
|
||||
|
@ -173,7 +173,7 @@ class PlasmaSound(bpy.types.PropertyGroup):
|
||||
name = "Sfx-{}_{}".format(so.key.name, self.sound_data)
|
||||
else:
|
||||
name = "Sfx-{}_{}:{}".format(so.key.name, self.sound_data, channel)
|
||||
print(" [{}] {}".format(pClass.__name__[2:], name))
|
||||
exporter.report.msg("[{}] {}", pClass.__name__[2:], name, indent=1)
|
||||
sound = exporter.mgr.find_create_object(pClass, so=so, name=name)
|
||||
|
||||
# If this object is a soft volume itself, we will use our own soft region.
|
||||
|
Reference in New Issue
Block a user