mirror of
https://github.com/Squareville/lu-toolbox.git
synced 2026-02-13 03:18:50 -06:00
Compare commits
36 Commits
v2.0-alpha
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd02e3240e | ||
|
|
bbdfafc2b6 | ||
| 473879e900 | |||
|
|
5c0c04e4a0 | ||
| 9a9b5fe58e | |||
|
|
f9ce932bed | ||
|
|
1b3f6672e9 | ||
|
|
7a9a8e2cbb | ||
|
|
61ba3c69dd | ||
|
|
88e6159f40 | ||
| 6ac59d4be3 | |||
| 569761f972 | |||
|
|
223e607628 | ||
|
|
8138542fe0 | ||
| 5fb6338ca9 | |||
|
|
24d5708d7a | ||
|
|
22df835784 | ||
| db5b172f95 | |||
| ba9beb1212 | |||
| 081c6aac0f | |||
|
|
713620a30e | ||
|
|
fe1ea064a8 | ||
|
|
0cc478b5e4 | ||
|
|
52a40f3d9f | ||
|
|
67aa93348a | ||
|
|
d8ad70de8c | ||
|
|
5d41b3e199 | ||
|
|
79d4bf454b | ||
|
|
bbabeac716 | ||
|
|
30e3c78bde | ||
|
|
a961554aeb | ||
|
|
66b5f24d9d | ||
|
|
34ebba2ff5 | ||
|
|
4009b815c7 | ||
|
|
33018f7d12 | ||
| 8860921d66 |
@@ -1,4 +1,4 @@
|
||||
[](https://download.blender.org/release/Blender2.93/)
|
||||
[](https://download.blender.org/release/Blender3.1/)
|
||||
[](https://github.com/30350n/lu-toolbox/blob/master/LICENSE)
|
||||
# LEGO Universe Toolbox
|
||||
A Blender Add-on which adds a bunch of useful tools to prepare models for use in LEGO Universe.
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
bl_info = {
|
||||
"name": "LU Toolbox",
|
||||
"author": "Bobbe",
|
||||
"version": (1, 7, 0),
|
||||
"version": (2, 4, 0),
|
||||
"blender": (2, 93, 0),
|
||||
"location": "3D View -> Sidebar -> LU Toolbox",
|
||||
"category": "Import-Export",
|
||||
"support": "COMMUNITY",
|
||||
}
|
||||
|
||||
import bpy
|
||||
import importlib
|
||||
|
||||
module_names = ("process_model", "bake_lighting", "remove_hidden_faces", "importldd")
|
||||
module_names = (
|
||||
"process_model",
|
||||
"icon_render",
|
||||
"bake_lighting",
|
||||
"remove_hidden_faces",
|
||||
"importldd"
|
||||
)
|
||||
|
||||
modules = []
|
||||
|
||||
|
||||
for module_name in module_names:
|
||||
if module_name in locals():
|
||||
modules.append(importlib.reload(locals()[module_name]))
|
||||
|
||||
@@ -25,24 +25,43 @@ class LUTB_PT_bake_lighting(bpy.types.Panel):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.prop(scene, "lutb_bake_samples")
|
||||
layout.prop(scene, "lutb_bake_fast_gi_bounces")
|
||||
layout.prop(scene, "lutb_bake_glow_strength")
|
||||
layout.prop(scene, "lutb_bake_use_gpu")
|
||||
layout.prop(scene, "lutb_bake_selected_only")
|
||||
col = layout.column()
|
||||
col.prop(scene, "lutb_bake_use_white_ambient")
|
||||
col.active = not scene.lutb_bake_ao_only
|
||||
layout.prop(scene, "lutb_bake_smooth_lit")
|
||||
layout.prop(scene, "lutb_bake_ao_only")
|
||||
col = layout.column()
|
||||
col.prop(scene, "lutb_bake_glow_multiplier")
|
||||
col.prop(scene, "lutb_bake_ao_samples")
|
||||
col.active = scene.lutb_bake_ao_only
|
||||
col = layout.column()
|
||||
col.prop(scene, "lutb_bake_force_to_white")
|
||||
col.active = not scene.lutb_bake_use_mat_override
|
||||
|
||||
class LUTB_PT_mat_override(bpy.types.Panel):
|
||||
layout.prop(scene, "lutb_bake_samples")
|
||||
layout.prop(scene, "lutb_bake_fast_gi_bounces")
|
||||
layout.prop(scene, "lutb_bake_glow_strength")
|
||||
|
||||
class LUTB_PT_bake_ao_only(bpy.types.Panel):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "LU Toolbox"
|
||||
bl_label = "AO Only"
|
||||
bl_parent_id = "LUTB_PT_bake_lighting"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
def draw_header(self, context):
|
||||
self.layout.prop(context.scene, "lutb_bake_ao_only", text="")
|
||||
|
||||
def draw(self, context):
|
||||
scene = context.scene
|
||||
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
layout.active = scene.lutb_bake_ao_only
|
||||
|
||||
layout.prop(scene, "lutb_bake_glow_multiplier")
|
||||
layout.prop(scene, "lutb_bake_ao_samples")
|
||||
|
||||
class LUTB_PT_bake_mat_override(bpy.types.Panel):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "LU Toolbox"
|
||||
@@ -127,22 +146,18 @@ class LUTB_OT_bake_lighting(bpy.types.Operator):
|
||||
obj.hide_render = True
|
||||
hidden_objects.append(obj)
|
||||
|
||||
target_objects = scene.collection.all_objects
|
||||
if scene.lutb_bake_selected_only:
|
||||
target_objects = context.selected_objects
|
||||
|
||||
old_active_obj = context.object
|
||||
for obj in (selected := list(context.selected_objects)):
|
||||
old_selected_objects = context.selected_objects
|
||||
for obj in list(target_objects):
|
||||
if obj.type != "MESH" or obj.get(IS_TRANSPARENT):
|
||||
continue
|
||||
|
||||
other_lod_colls = set()
|
||||
for lod_collection in obj.users_collection:
|
||||
for collection in bpy.data.collections:
|
||||
if lod_collection.name in collection.children:
|
||||
other_lod_colls |= set(collection.children) - {lod_collection,}
|
||||
|
||||
for other_lod_coll in list(other_lod_colls):
|
||||
if other_lod_coll.hide_render:
|
||||
other_lod_colls.remove(other_lod_coll)
|
||||
else:
|
||||
other_lod_coll.hide_render = True
|
||||
if not obj.name in context.view_layer.objects:
|
||||
self.report({"WARNING"}, f"Skipping \"{obj.name}\". (not in viewlayer)")
|
||||
continue
|
||||
|
||||
mesh = obj.data
|
||||
|
||||
@@ -160,9 +175,17 @@ class LUTB_OT_bake_lighting(bpy.types.Operator):
|
||||
if vc_lit := mesh.vertex_colors.get("Lit"):
|
||||
mesh.vertex_colors.active_index = mesh.vertex_colors.keys().index(vc_lit.name)
|
||||
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
other_lod_colls = set()
|
||||
for lod_collection in obj.users_collection:
|
||||
for collection in bpy.data.collections:
|
||||
if lod_collection.name in collection.children:
|
||||
other_lod_colls |= set(collection.children) - {lod_collection,}
|
||||
|
||||
for other_lod_coll in list(other_lod_colls):
|
||||
if other_lod_coll.hide_render:
|
||||
other_lod_colls.remove(other_lod_coll)
|
||||
else:
|
||||
other_lod_coll.hide_render = True
|
||||
|
||||
old_material = mesh.materials[0]
|
||||
if scene.lutb_bake_use_mat_override:
|
||||
@@ -176,11 +199,25 @@ class LUTB_OT_bake_lighting(bpy.types.Operator):
|
||||
if node.type == "BSDF_PRINCIPLED":
|
||||
node.inputs['Emission Strength'].default_value = emission_strength
|
||||
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
|
||||
context_override = context.copy()
|
||||
context_override["scene"] = scene_override
|
||||
bpy.ops.object.bake(context_override)
|
||||
try:
|
||||
bpy.ops.object.bake(context_override)
|
||||
except RuntimeError as e:
|
||||
if "is not enabled for rendering" in str(e):
|
||||
self.report({"WARNING"}, f"Skipping \"{obj.name}\". (not enabled for rendering)")
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
mesh.materials[0] = old_material
|
||||
|
||||
mesh.materials[0] = old_material
|
||||
for other_lod_coll in other_lod_colls:
|
||||
other_lod_coll.hide_render = False
|
||||
|
||||
has_edge_split_modifier = "EDGE_SPLIT" in {mod.type for mod in obj.modifiers}
|
||||
if scene.lutb_bake_smooth_lit and not has_edge_split_modifier:
|
||||
@@ -202,20 +239,17 @@ class LUTB_OT_bake_lighting(bpy.types.Operator):
|
||||
lit_data[:, 3] = alpha_data.reshape((n_loops, 4))[:, 0]
|
||||
vc_lit.data.foreach_set("color", lit_data.flatten())
|
||||
|
||||
for other_lod_coll in other_lod_colls:
|
||||
other_lod_coll.hide_render = False
|
||||
|
||||
bpy.data.scenes.remove(scene_override)
|
||||
|
||||
if ao_only_world_override:
|
||||
bpy.data.worlds.remove(ao_only_world_override)
|
||||
|
||||
for obj in selected:
|
||||
obj.select_set(True)
|
||||
|
||||
for obj in hidden_objects:
|
||||
obj.hide_render = False
|
||||
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for obj in old_selected_objects:
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = old_active_obj
|
||||
|
||||
end = timer()
|
||||
@@ -226,9 +260,11 @@ class LUTB_OT_bake_lighting(bpy.types.Operator):
|
||||
def register():
|
||||
bpy.utils.register_class(LUTB_OT_bake_lighting)
|
||||
bpy.utils.register_class(LUTB_PT_bake_lighting)
|
||||
bpy.utils.register_class(LUTB_PT_mat_override)
|
||||
bpy.utils.register_class(LUTB_PT_bake_ao_only)
|
||||
bpy.utils.register_class(LUTB_PT_bake_mat_override)
|
||||
|
||||
bpy.types.Scene.lutb_bake_use_gpu = BoolProperty(name="Use GPU", default=True)
|
||||
bpy.types.Scene.lutb_bake_selected_only = BoolProperty(name="Selected Only")
|
||||
bpy.types.Scene.lutb_bake_smooth_lit = BoolProperty(name="Smooth Vertex Colors", default=True)
|
||||
bpy.types.Scene.lutb_bake_samples = IntProperty(name="Samples", default=256, min=1, description=""\
|
||||
"Number of samples to render for each vertex")
|
||||
@@ -245,6 +281,7 @@ def register():
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.lutb_bake_use_gpu
|
||||
del bpy.types.Scene.lutb_bake_selected_only
|
||||
del bpy.types.Scene.lutb_bake_smooth_lit
|
||||
del bpy.types.Scene.lutb_bake_samples
|
||||
del bpy.types.Scene.lutb_bake_fast_gi_bounces
|
||||
@@ -257,6 +294,7 @@ def unregister():
|
||||
del bpy.types.Scene.lutb_bake_use_mat_override
|
||||
del bpy.types.Scene.lutb_bake_mat_override
|
||||
|
||||
bpy.utils.unregister_class(LUTB_PT_mat_override)
|
||||
bpy.utils.unregister_class(LUTB_PT_bake_mat_override)
|
||||
bpy.utils.unregister_class(LUTB_PT_bake_ao_only)
|
||||
bpy.utils.unregister_class(LUTB_PT_bake_lighting)
|
||||
bpy.utils.unregister_class(LUTB_OT_bake_lighting)
|
||||
|
||||
209
lu_toolbox/icon_render.py
Normal file
209
lu_toolbox/icon_render.py
Normal file
@@ -0,0 +1,209 @@
|
||||
import bpy, bmesh
|
||||
from bpy.props import BoolProperty
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
from math import radians
|
||||
import numpy as np
|
||||
|
||||
from .process_model import LOD_SUFFIXES
|
||||
from .materials import *
|
||||
|
||||
class LUTB_OT_setup_icon_render(bpy.types.Operator):
|
||||
"""Setup Icon Render for LU Model"""
|
||||
bl_idname = "lutb.setup_icon_render"
|
||||
bl_label = "Setup Icon Render"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.mode == "OBJECT"
|
||||
|
||||
def execute(self, context):
|
||||
# we will only increase the sample count for rendering if we need to (currently only if there are any transparent pieces) - jamie
|
||||
increase_samples = False
|
||||
|
||||
scene = context.scene
|
||||
|
||||
for collection in scene.collection.children:
|
||||
for lod_collection in collection.children[:]:
|
||||
if lod_collection.name[-5:] in LOD_SUFFIXES[1:]:
|
||||
collection.children.unlink(lod_collection)
|
||||
|
||||
combine_objects_before = scene.lutb_combine_objects
|
||||
scene.lutb_combine_objects = False
|
||||
|
||||
apply_vertex_colors_before = scene.lutb_apply_vertex_colors
|
||||
scene.lutb_apply_vertex_colors = True
|
||||
|
||||
correct_colors_before = scene.lutb_correct_colors
|
||||
scene.lutb_correct_colors = scene.lutb_ir_correct_colors
|
||||
|
||||
color_variation_before = scene.lutb_color_variation
|
||||
scene.lutb_color_variation = scene.lutb_ir_color_variation
|
||||
|
||||
setup_bake_mat_before = scene.lutb_setup_bake_mat
|
||||
scene.lutb_setup_bake_mat = False
|
||||
|
||||
remove_hidden_faces_before = scene.lutb_remove_hidden_faces
|
||||
scene.lutb_remove_hidden_faces = False
|
||||
|
||||
# hacky way to inject modified color corrections
|
||||
if scene.lutb_ir_correct_colors:
|
||||
color_corrections = (
|
||||
[MATERIALS_OPAQUE, ICON_MATERIALS_OPAQUE, None],
|
||||
[MATERIALS_TRANSPARENT, ICON_MATERIALS_TRANSPARENT, None],
|
||||
[MATERIALS_GLOW, ICON_MATERIALS_GLOW, None],
|
||||
[MATERIALS_METALLIC, ICON_MATERIALS_METALLIC, None],
|
||||
)
|
||||
for color_correction in color_corrections:
|
||||
target, updates, _ = color_correction
|
||||
color_correction[2] = target.copy()
|
||||
target.update(updates)
|
||||
|
||||
bpy.ops.lutb.process_model()
|
||||
|
||||
if scene.lutb_ir_correct_colors:
|
||||
for target, _, original in color_corrections:
|
||||
target.update(original)
|
||||
|
||||
scene.lutb_combine_objects = combine_objects_before
|
||||
scene.lutb_apply_vertex_colors = apply_vertex_colors_before
|
||||
scene.lutb_correct_colors = correct_colors_before
|
||||
scene.lutb_color_variation = color_variation_before
|
||||
scene.lutb_setup_bake_mat = setup_bake_mat_before
|
||||
scene.lutb_remove_hidden_faces = remove_hidden_faces_before
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.mesh.tris_convert_to_quads(shape_threshold=radians(50))
|
||||
|
||||
for obj in context.selected_objects:
|
||||
if obj.type != "MESH":
|
||||
continue
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
bevel_weight = bm.edges.layers.bevel_weight.new("Bevel Weight")
|
||||
for edge in bm.edges:
|
||||
if len(edge.link_faces) == 1:
|
||||
edge[bevel_weight] = 1.0
|
||||
bmesh.update_edit_mesh(obj.data, loop_triangles=False, destructive=False)
|
||||
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.mesh.remove_doubles()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
for obj in context.selected_objects:
|
||||
if obj.type != "MESH":
|
||||
continue
|
||||
if scene.lutb_ir_bevel_edges:
|
||||
bevel_mod = obj.modifiers.new("Bevel", "BEVEL")
|
||||
bevel_mod.width = 0.02
|
||||
bevel_mod.segments = 4
|
||||
bevel_mod.limit_method = "WEIGHT"
|
||||
bevel_mod.harden_normals = True
|
||||
|
||||
if scene.lutb_ir_subdivide:
|
||||
brick_id = obj.name.split("brick_")[-1].split("_")[1]
|
||||
if not brick_id in ICON_RENDER_DISABLE_SUBDIV:
|
||||
subdiv_mod = obj.modifiers.new("Subdivision", "SUBSURF")
|
||||
subdiv_mod.levels = 1
|
||||
subdiv_mod.render_levels = 2
|
||||
|
||||
mesh = obj.data
|
||||
obj.data.use_auto_smooth = True
|
||||
obj.data.auto_smooth_angle = radians(180)
|
||||
|
||||
for i, material in enumerate(mesh.materials):
|
||||
name = material.name.rsplit(".", 1)[0]
|
||||
if name in MATERIALS_OPAQUE:
|
||||
mesh.materials[i] = get_lutb_ir_opaque_mat(self)
|
||||
# another hack to change a material setting, for the same reasons as the comment below...
|
||||
# anyway, it was deemed that having this enabled was making the plastic look too soft and washed-out in many scenarios - jamie
|
||||
if mesh.materials[i].node_tree:
|
||||
for node in mesh.materials[i].node_tree.nodes:
|
||||
if node.type == "BSDF_PRINCIPLED":
|
||||
node.inputs["Subsurface"].default_value = 0
|
||||
elif name in MATERIALS_TRANSPARENT:
|
||||
mesh.materials[i] = get_lutb_ir_transparent_mat(self)
|
||||
# next two lines are a silly hacky fix cause i dont wanna mess with the magical mystery box that is resources.blend, and hollis didnt know why it was broken anyway - jamie
|
||||
mesh.materials[i].blend_method = "HASHED"
|
||||
mesh.materials[i].shadow_method = "HASHED"
|
||||
increase_samples = True
|
||||
elif name in MATERIALS_METALLIC:
|
||||
mesh.materials[i] = get_lutb_ir_metal_mat(self)
|
||||
|
||||
ir_scene = get_lutb_ir_scene(self)
|
||||
|
||||
for collection in scene.collection.children[:]:
|
||||
for obj in collection.objects:
|
||||
if obj.type == "EMPTY" and obj.name.startswith("SceneNode_"):
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
lod_collection = collection.children[0]
|
||||
obj_bounds = np.empty((len(lod_collection.objects) * 2, 3))
|
||||
for i, obj in enumerate(lod_collection.objects):
|
||||
obj_bounds[i * 2 + 0] = obj.matrix_world @ Vector(obj.bound_box[0])
|
||||
obj_bounds[i * 2 + 1] = obj.matrix_world @ Vector(obj.bound_box[6])
|
||||
|
||||
dimensions = obj_bounds.max(0) - obj_bounds.min(0)
|
||||
offset = Matrix.Translation(-(obj_bounds.min(0) + dimensions * 0.5))
|
||||
scale = Matrix.Scale(1 / np.abs(dimensions).max(), 4)
|
||||
for obj in lod_collection.objects:
|
||||
obj.matrix_world = scale @ offset @ obj.matrix_world
|
||||
|
||||
scene.collection.children.unlink(collection)
|
||||
ir_scene.collection.children.link(collection)
|
||||
|
||||
context.window.scene = ir_scene
|
||||
|
||||
for area in context.screen.areas:
|
||||
if area.type == "VIEW_3D":
|
||||
area.spaces[0].shading.type = "RENDERED"
|
||||
|
||||
if increase_samples:
|
||||
bpy.context.scene.eevee.taa_render_samples = 1024
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class LUTB_PT_icon_render(bpy.types.Panel):
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "LU Icon Render"
|
||||
bl_label = "Icon Render"
|
||||
|
||||
def draw(self, context):
|
||||
scene = context.scene
|
||||
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.operator(LUTB_OT_setup_icon_render.bl_idname)
|
||||
|
||||
layout.separator(factor=0.5)
|
||||
|
||||
layout.prop(scene, "lutb_ir_correct_colors")
|
||||
layout.prop(scene, "lutb_ir_color_variation")
|
||||
layout.prop(scene, "lutb_ir_bevel_edges")
|
||||
layout.prop(scene, "lutb_ir_subdivide")
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(LUTB_OT_setup_icon_render)
|
||||
bpy.utils.register_class(LUTB_PT_icon_render)
|
||||
|
||||
bpy.types.Scene.lutb_ir_correct_colors = BoolProperty(name="Correct Colors", default=True,
|
||||
description=bpy.types.Scene.lutb_correct_colors.keywords["description"])
|
||||
bpy.types.Scene.lutb_ir_color_variation = BoolProperty(name="Apply Color Variation", default=False,
|
||||
description=bpy.types.Scene.lutb_use_color_variation.keywords["description"])
|
||||
bpy.types.Scene.lutb_ir_bevel_edges = BoolProperty(name="Bevel Edges", default=True)
|
||||
bpy.types.Scene.lutb_ir_subdivide = BoolProperty(name="Subdivide", default=True)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.lutb_ir_correct_colors
|
||||
del bpy.types.Scene.lutb_ir_color_variation
|
||||
del bpy.types.Scene.lutb_ir_bevel_edges
|
||||
del bpy.types.Scene.lutb_ir_subdivide
|
||||
|
||||
bpy.utils.unregister_class(LUTB_PT_icon_render)
|
||||
bpy.utils.unregister_class(LUTB_OT_setup_icon_render)
|
||||
@@ -1,6 +1,6 @@
|
||||
# based on pyldd2obj by jonnysp and lxfml import plugin by sttng
|
||||
# modified by aronwk-aaron to work better with LU-Toolbox
|
||||
import bpy
|
||||
import bpy, bmesh
|
||||
import mathutils
|
||||
from bpy_extras.io_utils import (
|
||||
ImportHelper,
|
||||
@@ -16,6 +16,7 @@ import zipfile
|
||||
from xml.dom import minidom
|
||||
import uuid
|
||||
import random
|
||||
import numpy as np
|
||||
|
||||
from .materials import (
|
||||
MATERIALS_OPAQUE,
|
||||
@@ -209,7 +210,7 @@ def convertldd_data(self, context, filepath, importLOD0, importLOD1, importLOD2,
|
||||
else:
|
||||
self.report({'INFO'}, f'LOD3 does not exist, skipping')
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, e)
|
||||
self.report({'ERROR'}, str(e))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -1004,17 +1005,13 @@ class Converter:
|
||||
uniqueId = str(uuid.uuid4().hex)
|
||||
material_string = '_' + '_'.join(pa.materials)
|
||||
written_obj = geo.designID + material_string
|
||||
brick_name = f"brick_{currentpart}_{written_obj}"
|
||||
|
||||
if (len(pa.Bones) > flexflag):
|
||||
# Flex parts are "unique". Ensure they get a unique filename
|
||||
written_obj = written_obj + "_" + uniqueId
|
||||
|
||||
brick_object = bpy.data.objects.new("brick{0}_{1}".format(currentpart, written_obj), None)
|
||||
col.objects.link(brick_object)
|
||||
brick_object.empty_display_size = 1.25
|
||||
brick_object.empty_display_type = 'PLAIN_AXES'
|
||||
|
||||
if not (len(pa.Bones) > flexflag):
|
||||
part_matrix = global_matrix
|
||||
else:
|
||||
# Flex parts don't need to be moved, but non-flex parts need
|
||||
transform_matrix = mathutils.Matrix(
|
||||
(
|
||||
@@ -1028,11 +1025,15 @@ class Converter:
|
||||
# Random Scale for brick seams
|
||||
scalefact = (geo.maxGeoBounding - 0.000 * random.uniform(0.0, 1.000)) / geo.maxGeoBounding
|
||||
|
||||
scale_matrix = mathutils.Matrix.Scale(scalefact, 4)
|
||||
part_matrix = global_matrix @ transform_matrix @ scale_matrix
|
||||
|
||||
# miny used for floor plane later
|
||||
if miny > float(n42):
|
||||
miny = n42
|
||||
|
||||
last_color = 0
|
||||
geo_meshes = []
|
||||
for part in geo.Parts:
|
||||
|
||||
written_geo = str(geo.designID) + '_' + str(part)
|
||||
@@ -1085,15 +1086,13 @@ class Converter:
|
||||
mesh.normals_split_custom_set_from_vertices(normals)
|
||||
mesh.use_auto_smooth = True
|
||||
|
||||
geometriecache["geo{0}".format(written_geo)] = mesh
|
||||
geometriecache["geo{0}".format(written_geo)] = mesh.copy()
|
||||
|
||||
else:
|
||||
mesh = geometriecache["geo{0}".format(written_geo)].copy()
|
||||
mesh.materials.clear()
|
||||
|
||||
geo_obj = bpy.data.objects.new(mesh.name, mesh)
|
||||
geo_obj.parent = brick_object
|
||||
col.objects.link(geo_obj)
|
||||
geo_meshes.append(mesh)
|
||||
|
||||
# try catch here for possible problems in materials assignment of various g, g1, g2, .. files in lxf file
|
||||
try:
|
||||
@@ -1123,14 +1122,44 @@ class Converter:
|
||||
for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
|
||||
uv_layer[loop_index].uv = uvs[mesh.loops[loop_index].vertex_index]
|
||||
|
||||
if not (len(pa.Bones) > flexflag):
|
||||
# Transform (move) only non-flex parts
|
||||
brick_object.matrix_world = global_matrix @ transform_matrix
|
||||
brick_object.scale = (scalefact, scalefact, scalefact)
|
||||
used_materials = []
|
||||
used_material_indices = {}
|
||||
bm = bmesh.new()
|
||||
for mesh in geo_meshes:
|
||||
index_remapping = np.empty(len(mesh.materials), dtype=int)
|
||||
for i, material in enumerate(mesh.materials):
|
||||
mat_name = material.name.rsplit(".", 1)[0]
|
||||
if (index := used_material_indices.get(mat_name)) is not None:
|
||||
index_remapping[i] = index
|
||||
bpy.data.materials.remove(material)
|
||||
else:
|
||||
index_remapping[i] = len(used_material_indices)
|
||||
used_material_indices[mat_name] = index_remapping[i]
|
||||
used_materials.append(material)
|
||||
|
||||
else:
|
||||
# Flex parts need only to be aligned the Blender coordinate system
|
||||
brick_object.matrix_world = global_matrix
|
||||
material_indices = np.empty(len(mesh.polygons), dtype=int)
|
||||
mesh.polygons.foreach_get("material_index", material_indices)
|
||||
remapped_indices = index_remapping[material_indices]
|
||||
mesh.polygons.foreach_set("material_index", remapped_indices)
|
||||
|
||||
bm.from_mesh(mesh)
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
brick_mesh = bpy.data.meshes.new(brick_name)
|
||||
bm.to_mesh(brick_mesh)
|
||||
for material in used_materials:
|
||||
brick_mesh.materials.append(material)
|
||||
|
||||
if useNormals:
|
||||
brick_mesh.use_auto_smooth = True
|
||||
|
||||
brick_obj = bpy.data.objects.new(brick_name, brick_mesh)
|
||||
brick_obj.matrix_world = part_matrix
|
||||
col.objects.link(brick_obj)
|
||||
|
||||
for mesh in geometriecache.values():
|
||||
if type(mesh) == bpy.types.Mesh:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
useplane = True
|
||||
if useplane is True: # write the floor plane in case True
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
from pathlib import Path
|
||||
import bpy
|
||||
|
||||
from .color_conversions import *
|
||||
|
||||
LUTB_BAKE_MAT = "VertexColor"
|
||||
LUTB_TRANSPARENT_MAT = "VertexColorTransparent"
|
||||
LUTB_FORCE_WHITE_MAT = "ForceWhite"
|
||||
LUTB_OTHER_MATS = ["VertexColorAO"]
|
||||
LUTB_BAKE_MATS = (LUTB_BAKE_MAT, LUTB_TRANSPARENT_MAT, LUTB_FORCE_WHITE_MAT, *LUTB_OTHER_MATS)
|
||||
|
||||
LUTB_IR_OPAQUE_MAT = "ItemRender_Opaque"
|
||||
LUTB_IR_TRANSPARENT_MAT = "ItemRender_Transparent"
|
||||
LUTB_IR_METAL_MAT = "ItemRender_Metal"
|
||||
LUTB_IR_MATS = (LUTB_IR_OPAQUE_MAT, LUTB_IR_TRANSPARENT_MAT, LUTB_IR_METAL_MAT)
|
||||
|
||||
LUTB_IR_SCENE = "ItemRender"
|
||||
|
||||
# COLORS HERE ARE EXPECTED TO BE IN LINEAR COLOR SPACE
|
||||
# IF YOU INPUT AN SRGB COLOR LIKE LU/LDD USE IT MUST BE CONVERTED TO LINEAR EITHER MANUALLY OR BY CALLING SRGB2LIN AS YOU WILL OCCASIONALLY SEE BELOW
|
||||
# SIGNED, JAMIE (WHO WAS A SMIDGE ANNOYED AT THIS BUT ULTIMATELY PASSES NO JUDGEMENT ON THE AUTHORS OF THIS TOOL)
|
||||
|
||||
# Solid/Opaque
|
||||
MATERIALS_OPAQUE = {
|
||||
@@ -112,21 +126,21 @@ MATERIALS_OPAQUE["325"] = MATERIALS_OPAQUE["222"]
|
||||
|
||||
# Transparent
|
||||
MATERIALS_TRANSPARENT = {
|
||||
"20" : (0.930111, 0.672443, 0.250158, 1.0),
|
||||
"40" : (0.854993, 0.854993, 0.854993, 1.0),
|
||||
"41" : (0.745405, 0.023153, 0.022174, 1.0),
|
||||
"42" : (0.467784, 0.745404, 0.863157, 1.0),
|
||||
"43" : (0.08022, 0.439657, 0.806952, 1.0),
|
||||
"44" : (0.947307, 0.863157, 0.141263, 1.0),
|
||||
"47" : (0.791298, 0.132868, 0.059511, 1.0),
|
||||
"48" : (0.119539, 0.450786, 0.155927, 1.0),
|
||||
"49" : (0.930111, 0.83077, 0.099899, 1.0),
|
||||
"111" : (0.496933, 0.445201, 0.341914, 1.0),
|
||||
"113" : (0.854993, 0.337164, 0.545725, 1.0),
|
||||
"126" : (0.332452, 0.296138, 0.571125, 1.0),
|
||||
"143" : (0.623961, 0.760525, 0.930111, 1.0),
|
||||
"182" : (0.8388, 0.181164, 0.004391, 1.0),
|
||||
"311" : (0.42869, 0.64448, 0.061246, 1.0),
|
||||
"20" : (0.930111, 0.672443, 0.250158, 1.0),
|
||||
"40" : (0.854993, 0.854993, 0.854993, 1.0),
|
||||
"41" : srgb2lin((0.674509, 0.0, 0.0, 1.0)),
|
||||
"42" : srgb2lin((0.244106, 0.720966, 0.772058, 1.0)),
|
||||
"43" : srgb2lin((0.031372, 0.285668, 0.643137, 1.0)),
|
||||
"44" : srgb2lin((0.858, 0.771375, 0.0, 1.0)),
|
||||
"47" : srgb2lin((0.986, 0.336526, 0.120035, 1.0)),
|
||||
"48" : srgb2lin((0.0, 0.391, 0.0, 1.0)),
|
||||
"49" : srgb2lin((0.697, 1.0, 0.0, 1.0)),
|
||||
"111" : srgb2lin((0.741177, 0.670588, 0.639216, 1.0)),
|
||||
"113" : srgb2lin((0.754717, 0.060520, 0.541647, 1.0)),
|
||||
"126" : srgb2lin((0.267974, 0.196078, 0.627451, 1.0)),
|
||||
"143" : srgb2lin((0.325985, 0.551358, 0.821, 1.0)),
|
||||
"182" : srgb2lin((0.913726, 0.524575, 0.0156863, 1.0)),
|
||||
"311" : srgb2lin((0.454640, 0.788235, 0.0980392, 1.0)),
|
||||
}
|
||||
|
||||
# Duplicate Transparent
|
||||
@@ -189,6 +203,7 @@ CUSTOM_VARIATION = {
|
||||
"21" : 1.4,
|
||||
"23" : 1.25,
|
||||
"24" : 1.5,
|
||||
"26" : 0.4,
|
||||
"28" : 0.8,
|
||||
"37" : 0.8,
|
||||
"135" : 0.85,
|
||||
@@ -204,7 +219,33 @@ CUSTOM_VARIATION = {
|
||||
"326" : 1.75,
|
||||
}
|
||||
|
||||
for dictionary in (MATERIALS_OPAQUE, MATERIALS_TRANSPARENT, MATERIALS_GLOW, MATERIALS_METALLIC, CUSTOM_VARIATION):
|
||||
ICON_MATERIALS_OPAQUE = {
|
||||
"1" : srgb2lin((0.7, 0.7, 0.7, 1.0)),
|
||||
"26" : srgb2lin((0.01, 0.01, 0.01, 1.0)),
|
||||
}
|
||||
|
||||
ICON_MATERIALS_TRANSPARENT = {
|
||||
|
||||
}
|
||||
|
||||
ICON_MATERIALS_GLOW = {
|
||||
|
||||
}
|
||||
|
||||
ICON_MATERIALS_METALLIC = {
|
||||
|
||||
}
|
||||
|
||||
ICON_RENDER_DISABLE_SUBDIV = {
|
||||
|
||||
}
|
||||
|
||||
dicts = (
|
||||
MATERIALS_OPAQUE, MATERIALS_TRANSPARENT, MATERIALS_GLOW, MATERIALS_METALLIC,
|
||||
ICON_MATERIALS_OPAQUE, ICON_MATERIALS_TRANSPARENT, ICON_MATERIALS_GLOW,
|
||||
ICON_MATERIALS_METALLIC, CUSTOM_VARIATION,
|
||||
)
|
||||
for dictionary in dicts:
|
||||
for keys, value in list(dictionary.items()):
|
||||
if not type(keys) == str:
|
||||
dictionary.pop(keys)
|
||||
@@ -212,49 +253,48 @@ for dictionary in (MATERIALS_OPAQUE, MATERIALS_TRANSPARENT, MATERIALS_GLOW, MATE
|
||||
dictionary[key] = value
|
||||
|
||||
def get_lutb_bake_mat(parent_op=None):
|
||||
if not LUTB_BAKE_MAT in bpy.data.materials:
|
||||
append_resources(parent_op)
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_BAKE_MAT)
|
||||
|
||||
def get_lutb_transparent_mat(parent_op=None):
|
||||
if not LUTB_TRANSPARENT_MAT in bpy.data.materials:
|
||||
append_resources(parent_op)
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_TRANSPARENT_MAT)
|
||||
|
||||
def get_lutb_force_white_mat(parent_op=None):
|
||||
if not LUTB_FORCE_WHITE_MAT in bpy.data.materials:
|
||||
append_resources(parent_op)
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_FORCE_WHITE_MAT)
|
||||
|
||||
def get_lutb_ir_opaque_mat(parent_op=None):
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_IR_OPAQUE_MAT)
|
||||
|
||||
def get_lutb_ir_transparent_mat(parent_op=None):
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_IR_TRANSPARENT_MAT)
|
||||
|
||||
def get_lutb_ir_metal_mat(parent_op=None):
|
||||
append_resources(parent_op)
|
||||
return bpy.data.materials.get(LUTB_IR_METAL_MAT)
|
||||
|
||||
def get_lutb_ir_scene(parent_op=None, copy=True):
|
||||
append_resources(parent_op)
|
||||
return bpy.data.scenes.get(LUTB_IR_SCENE).copy()
|
||||
|
||||
def append_resources(parent_op=None):
|
||||
blend_file = Path(__file__).parent / "resources.blend"
|
||||
|
||||
for mat_name in (LUTB_BAKE_MAT, LUTB_TRANSPARENT_MAT, LUTB_FORCE_WHITE_MAT, *LUTB_OTHER_MATS):
|
||||
for mat_name in (*LUTB_BAKE_MATS, *LUTB_IR_MATS):
|
||||
if not mat_name in bpy.data.materials:
|
||||
bpy.ops.wm.append(directory=str(blend_file / "Material"), filename=mat_name)
|
||||
|
||||
if not mat_name in bpy.data.materials and parent_op:
|
||||
parent_op.report({"WARNING"},
|
||||
f"Failed to append \"{mat_name}\" from \"{blend_file}\"."
|
||||
)
|
||||
|
||||
def srgb2lin(color):
|
||||
result = []
|
||||
for srgb in color:
|
||||
if srgb <= 0.0404482362771082:
|
||||
lin = srgb / 12.92
|
||||
else:
|
||||
lin = pow(((srgb + 0.055) / 1.055), 2.4)
|
||||
result.append(lin)
|
||||
return result
|
||||
|
||||
|
||||
def lin2srgb(color):
|
||||
result = []
|
||||
for lin in color:
|
||||
if lin > 0.0031308:
|
||||
srgb = 1.055 * (pow(lin, (1.0 / 2.4))) - 0.055
|
||||
else:
|
||||
srgb = 12.92 * lin
|
||||
result.append(srgb)
|
||||
return result
|
||||
scene_name = LUTB_IR_SCENE
|
||||
if not scene_name in bpy.data.scenes:
|
||||
bpy.ops.wm.append(directory=str(blend_file / "Scene"), filename=scene_name)
|
||||
if not scene_name in bpy.data.scenes and parent_op:
|
||||
parent_op.report({"WARNING"},
|
||||
f"Failed to append \"{scene_name}\" from \"{blend_file}\"."
|
||||
)
|
||||
|
||||
19
lu_toolbox/materials/color_conversions.py
Normal file
19
lu_toolbox/materials/color_conversions.py
Normal file
@@ -0,0 +1,19 @@
|
||||
def srgb2lin(color):
|
||||
result = []
|
||||
for srgb in color:
|
||||
if srgb <= 0.0404482362771082:
|
||||
lin = srgb / 12.92
|
||||
else:
|
||||
lin = pow(((srgb + 0.055) / 1.055), 2.4)
|
||||
result.append(lin)
|
||||
return result
|
||||
|
||||
def lin2srgb(color):
|
||||
result = []
|
||||
for lin in color:
|
||||
if lin > 0.0031308:
|
||||
srgb = 1.055 * (pow(lin, (1.0 / 2.4))) - 0.055
|
||||
else:
|
||||
srgb = 12.92 * lin
|
||||
result.append(srgb)
|
||||
return result
|
||||
Binary file not shown.
@@ -31,8 +31,6 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
scene.render.engine = "CYCLES"
|
||||
scene.cycles.device = "GPU" if scene.lutb_process_use_gpu else "CPU"
|
||||
|
||||
self.precombine_bricks(context, scene.collection.children)
|
||||
|
||||
for obj in scene.collection.all_objects:
|
||||
if not obj.type == "MESH":
|
||||
continue
|
||||
@@ -77,12 +75,15 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
if not scene.lutb_keep_uvs:
|
||||
self.clear_uvs(all_objects)
|
||||
|
||||
if scene.lutb_reset_orientation:
|
||||
self.reset_orientation(all_objects)
|
||||
|
||||
if scene.lutb_apply_vertex_colors:
|
||||
if scene.lutb_correct_colors:
|
||||
self.correct_colors(context, all_objects)
|
||||
|
||||
if scene.lutb_use_color_variation:
|
||||
self.apply_color_variation(context, all_objects)
|
||||
self.apply_color_variation(context, scene.collection.children)
|
||||
|
||||
self.apply_vertex_colors(context, all_objects)
|
||||
|
||||
@@ -107,98 +108,27 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for obj in all_objects:
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = all_objects[0]
|
||||
|
||||
end = timer()
|
||||
print(f"finished process model in {end - start:.2f}s")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def precombine_bricks(self, context, collections):
|
||||
bricks = {}
|
||||
for collection in collections:
|
||||
if not collection.children:
|
||||
continue
|
||||
|
||||
for lod_collection in collection.children:
|
||||
if not lod_collection.name[-5:] in LOD_SUFFIXES:
|
||||
continue
|
||||
|
||||
for obj in lod_collection.all_objects:
|
||||
if not (obj.type == "MESH" and obj.parent):
|
||||
continue
|
||||
|
||||
if brick := bricks.get(obj.parent):
|
||||
brick.append(obj)
|
||||
else:
|
||||
bricks[obj.parent] = [obj]
|
||||
|
||||
if not bricks:
|
||||
return
|
||||
|
||||
combined_bricks = {}
|
||||
for parent_empty, children in bricks.items():
|
||||
bm = bmesh.new()
|
||||
materials = {}
|
||||
|
||||
for child in children:
|
||||
mesh = child.data
|
||||
for old_mat_index, material in enumerate(mesh.materials):
|
||||
mat_name = material.name.rsplit(".", 1)[0]
|
||||
if mat := materials.get(mat_name):
|
||||
new_mat_index = mat[1]
|
||||
else:
|
||||
new_mat_index = len(materials)
|
||||
materials[mat_name] = (material, new_mat_index)
|
||||
|
||||
if old_mat_index != new_mat_index:
|
||||
for polygon in mesh.polygons:
|
||||
if polygon.material_index == old_mat_index:
|
||||
polygon.material_index = new_mat_index
|
||||
|
||||
bm.from_mesh(mesh)
|
||||
|
||||
combined = children[0]
|
||||
combined.name = parent_empty.name
|
||||
combined.parent = None
|
||||
combined.matrix_world = parent_empty.matrix_world.copy()
|
||||
bm.to_mesh(combined.data)
|
||||
|
||||
combined.data.materials.clear()
|
||||
# dictionaries are guaranteed to be ordered in 3.7+ (see PEP 468)
|
||||
for material, _ in materials.values():
|
||||
combined.data.materials.append(material)
|
||||
|
||||
bpy.data.objects.remove(parent_empty)
|
||||
for obj in children[1:]:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
combined_bricks[combined.name] = combined
|
||||
|
||||
brick_base_mats = {}
|
||||
for name, obj in combined_bricks.items():
|
||||
if name[-4:-3] == ".":
|
||||
base_name = name.rsplit(".", 1)[0]
|
||||
if not (base_mats := brick_base_mats.get(base_name)):
|
||||
if not (obj_base := combined_bricks.get(base_name)):
|
||||
continue
|
||||
|
||||
mats = obj_base.data.materials.values()
|
||||
base_mats = {mat.name.rsplit(".", 1)[0]: mat for mat in mats}
|
||||
brick_base_mats[base_name] = base_mats
|
||||
|
||||
for i, mat in enumerate(obj.data.materials):
|
||||
if (base_mat := base_mats.get(mat.name.rsplit(".", 1)[0])):
|
||||
obj.data.materials[i] = base_mat
|
||||
|
||||
for obj in list(collection.all_objects):
|
||||
if obj.type == "EMPTY":
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
def clear_uvs(self, objects):
|
||||
for obj in objects:
|
||||
for uv_layer in reversed(obj.data.uv_layers):
|
||||
obj.data.uv_layers.remove(uv_layer)
|
||||
|
||||
def reset_orientation(self, objects):
|
||||
for obj in objects:
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply()
|
||||
obj.rotation_euler = (radians(-90), 0, 0)
|
||||
bpy.ops.object.transform_apply()
|
||||
obj.rotation_euler = (radians(90), 0, 0)
|
||||
obj.select_set(False)
|
||||
|
||||
def combine_objects(self, context, collections):
|
||||
scene = context.scene
|
||||
|
||||
@@ -250,19 +180,25 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
elif color := MATERIALS_TRANSPARENT.get(name):
|
||||
material.diffuse_color = color
|
||||
|
||||
def apply_color_variation(self, context, objects):
|
||||
def apply_color_variation(self, context, collections):
|
||||
initial_state = random.getstate()
|
||||
variation = context.scene.lutb_color_variation
|
||||
for obj in objects:
|
||||
for material in obj.data.materials:
|
||||
color = Color(material.diffuse_color[:3])
|
||||
gamma = color.v ** (1 / 2.224)
|
||||
|
||||
custom_variation = CUSTOM_VARIATION.get(material.name.rsplit(".", 1)[0])
|
||||
var = variation if custom_variation is None else variation * custom_variation
|
||||
gamma += random.uniform(-var / 200, var / 200)
|
||||
for collection in collections:
|
||||
for lod_collection in collection.children:
|
||||
random.setstate(initial_state)
|
||||
for obj in list(lod_collection.objects):
|
||||
if obj.type == "MESH":
|
||||
for material in obj.data.materials:
|
||||
color = Color(material.diffuse_color[:3])
|
||||
gamma = color.v ** (1 / 2.224)
|
||||
|
||||
color.v = min(max(0, gamma), 1) ** 2.224
|
||||
material.diffuse_color = (*color, 1.0)
|
||||
custom_variation = CUSTOM_VARIATION.get(material.name.rsplit(".", 1)[0])
|
||||
var = variation if custom_variation is None else variation * custom_variation
|
||||
gamma += random.uniform(-var / 200, var / 200)
|
||||
|
||||
color.v = min(max(0, gamma), 1) ** 2.224
|
||||
material.diffuse_color = (*color, 1.0)
|
||||
|
||||
def apply_vertex_colors(self, context, objects):
|
||||
scene = context.scene
|
||||
@@ -374,6 +310,7 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
autoremove=scene.lutb_hsr_autoremove,
|
||||
vc_pre_pass=scene.lutb_hsr_vc_pre_pass,
|
||||
vc_pre_pass_samples=scene.lutb_hsr_vc_pre_pass_samples,
|
||||
ignore_lights=scene.lutb_hsr_ignore_lights,
|
||||
tris_to_quads=scene.lutb_hsr_tris_to_quads,
|
||||
pixels_between_verts=scene.lutb_hsr_pixels_between_verts,
|
||||
samples=scene.lutb_hsr_samples,
|
||||
@@ -418,10 +355,10 @@ class LUTB_OT_process_model(bpy.types.Operator):
|
||||
for obj in list(lod_collection.all_objects):
|
||||
is_transparent = bool(obj.get(IS_TRANSPARENT))
|
||||
|
||||
shader_prefix = "S01" if is_transparent else scene.lutb_shader_prefix
|
||||
shader_prefix = "01" if is_transparent else scene.lutb_shader_opaque
|
||||
type_prefix = "Alpha" if is_transparent else "Opaque"
|
||||
obj_name = obj.name.rsplit(".", 1)[0]
|
||||
name = f"{shader_prefix}_{type_prefix}_{obj_name}"[:60]
|
||||
name = f"S{shader_prefix}_{type_prefix}_{obj_name}"[:60]
|
||||
obj.name = name
|
||||
|
||||
if (node := ni_nodes.get(name)):
|
||||
@@ -534,6 +471,8 @@ class LUTB_PT_process_model(LUToolboxPanel, bpy.types.Panel):
|
||||
|
||||
layout.prop(scene, "lutb_keep_uvs")
|
||||
|
||||
layout.prop(scene, "lutb_reset_orientation")
|
||||
|
||||
class LUTB_PT_apply_vertex_colors(LUToolboxPanel, bpy.types.Panel):
|
||||
bl_label = "Apply Vertex Colors"
|
||||
bl_parent_id = "LUTB_PT_process_model"
|
||||
@@ -600,6 +539,7 @@ class LUTB_PT_remove_hidden_faces(LUToolboxPanel, bpy.types.Panel):
|
||||
row.prop(scene, "lutb_hsr_vc_pre_pass_samples", slider=True)
|
||||
row.enabled = scene.lutb_hsr_vc_pre_pass
|
||||
|
||||
layout.prop(scene, "lutb_hsr_ignore_lights")
|
||||
layout.prop(scene, "lutb_hsr_tris_to_quads")
|
||||
layout.prop(scene, "lutb_hsr_use_ground_plane")
|
||||
layout.prop(scene, "lutb_hsr_pixels_between_verts", slider=True)
|
||||
@@ -622,7 +562,10 @@ class LUTB_PT_setup_metadata(LUToolboxPanel, bpy.types.Panel):
|
||||
layout.active = scene.lutb_setup_lod_data
|
||||
|
||||
layout.prop(scene, "lutb_correct_orientation")
|
||||
layout.prop(scene, "lutb_shader_prefix")
|
||||
layout.prop(scene, "lutb_shader_opaque")
|
||||
layout.prop(scene, "lutb_shader_glow")
|
||||
layout.prop(scene, "lutb_shader_metal")
|
||||
layout.prop(scene, "lutb_shader_superemissive")
|
||||
layout.prop(scene, "lutb_lod0")
|
||||
layout.prop(scene, "lutb_lod1")
|
||||
layout.prop(scene, "lutb_lod2")
|
||||
@@ -644,6 +587,8 @@ def register():
|
||||
"Combine transparent bricks")
|
||||
bpy.types.Scene.lutb_keep_uvs = BoolProperty(name="Keep UVs", default=False, description=""\
|
||||
"Keep the original mesh UVs. Disabling this results in a model with no UVs")
|
||||
bpy.types.Scene.lutb_reset_orientation = BoolProperty(name="Reset Orientation", default=True, description=""\
|
||||
"Reset the orientation so the model is properly upright for visual effects.")
|
||||
|
||||
bpy.types.Scene.lutb_correct_colors = BoolProperty(name="Correct Colors", default=False, description=""\
|
||||
"Remap model colors to LU color palette. "\
|
||||
@@ -674,6 +619,8 @@ def register():
|
||||
description=LUTB_OT_remove_hidden_faces.__annotations__["vc_pre_pass"].keywords["description"])
|
||||
bpy.types.Scene.lutb_hsr_vc_pre_pass_samples = IntProperty(name="Pre-Pass Samples", min=0, default=32, soft_max=64,
|
||||
description=LUTB_OT_remove_hidden_faces.__annotations__["vc_pre_pass_samples"].keywords["description"])
|
||||
bpy.types.Scene.lutb_hsr_ignore_lights = BoolProperty(name="Ignore Lights", default=True,
|
||||
description=LUTB_OT_remove_hidden_faces.__annotations__["ignore_lights"].keywords["description"])
|
||||
bpy.types.Scene.lutb_hsr_tris_to_quads = BoolProperty(name="Tris to Quads", default=True,
|
||||
description=LUTB_OT_remove_hidden_faces.__annotations__["tris_to_quads"].keywords["description"])
|
||||
bpy.types.Scene.lutb_hsr_pixels_between_verts = IntProperty(name="Pixels Between Vertices", min=0, default=5, soft_max=15,
|
||||
@@ -685,7 +632,10 @@ def register():
|
||||
|
||||
bpy.types.Scene.lutb_setup_lod_data = BoolProperty(name="Setup LOD Data", default=True)
|
||||
bpy.types.Scene.lutb_correct_orientation = BoolProperty(name="Correct Orientation", default=True)
|
||||
bpy.types.Scene.lutb_shader_prefix = StringProperty(name="Shader Prefix", default="S01")
|
||||
bpy.types.Scene.lutb_shader_opaque = StringProperty(name="Opaque Shader", default="01")
|
||||
bpy.types.Scene.lutb_shader_glow = StringProperty(name="Glow Shader", default="72")
|
||||
bpy.types.Scene.lutb_shader_metal = StringProperty(name="Metal Shader", default="88")
|
||||
bpy.types.Scene.lutb_shader_superemissive = StringProperty(name="SuperEmissive Shader", default="19")
|
||||
bpy.types.Scene.lutb_lod0 = FloatProperty(name="LOD 0", soft_min=0.0, default=0.0, soft_max=25.0)
|
||||
bpy.types.Scene.lutb_lod1 = FloatProperty(name="LOD 1", soft_min=0.0, default=50.0, soft_max=100.0)
|
||||
bpy.types.Scene.lutb_lod2 = FloatProperty(name="LOD 2", soft_min=0.0, default=100.0, soft_max=280.0)
|
||||
@@ -697,6 +647,7 @@ def unregister():
|
||||
del bpy.types.Scene.lutb_combine_objects
|
||||
del bpy.types.Scene.lutb_combine_transparent
|
||||
del bpy.types.Scene.lutb_keep_uvs
|
||||
del bpy.types.Scene.lutb_reset_orientation
|
||||
|
||||
del bpy.types.Scene.lutb_correct_colors
|
||||
del bpy.types.Scene.lutb_use_color_variation
|
||||
@@ -712,6 +663,7 @@ def unregister():
|
||||
del bpy.types.Scene.lutb_hsr_autoremove
|
||||
del bpy.types.Scene.lutb_hsr_vc_pre_pass
|
||||
del bpy.types.Scene.lutb_hsr_vc_pre_pass_samples
|
||||
del bpy.types.Scene.lutb_hsr_ignore_lights
|
||||
del bpy.types.Scene.lutb_hsr_tris_to_quads
|
||||
del bpy.types.Scene.lutb_hsr_pixels_between_verts
|
||||
del bpy.types.Scene.lutb_hsr_samples
|
||||
@@ -719,7 +671,10 @@ def unregister():
|
||||
|
||||
del bpy.types.Scene.lutb_setup_lod_data
|
||||
del bpy.types.Scene.lutb_correct_orientation
|
||||
del bpy.types.Scene.lutb_shader_prefix
|
||||
del bpy.types.Scene.lutb_shader_opaque
|
||||
del bpy.types.Scene.lutb_shader_glow
|
||||
del bpy.types.Scene.lutb_shader_metal
|
||||
del bpy.types.Scene.lutb_shader_superemissive
|
||||
del bpy.types.Scene.lutb_lod0
|
||||
del bpy.types.Scene.lutb_lod1
|
||||
del bpy.types.Scene.lutb_lod2
|
||||
|
||||
@@ -22,6 +22,9 @@ class LUTB_OT_remove_hidden_faces(bpy.types.Operator):
|
||||
"definitely visible.")
|
||||
vc_pre_pass_samples : IntProperty(min=1, default=32, description=""\
|
||||
"Number of samples to render for vertex color pre-pass")
|
||||
ignore_lights : BoolProperty(default=True, description=""\
|
||||
"Hide all custom lights while processing."\
|
||||
"Disable this if you want to manually affect the lighting.")
|
||||
tris_to_quads : BoolProperty(default=True, description=""\
|
||||
"Convert models triangles to quads for faster, more efficient HSR. "\
|
||||
"Quads are then converted back to tris afterwards. "\
|
||||
@@ -63,9 +66,15 @@ class LUTB_OT_remove_hidden_faces(bpy.types.Operator):
|
||||
|
||||
hidden_objects = []
|
||||
for obj in list(scene.collection.all_objects):
|
||||
if obj not in {target_obj, ground_plane} and not obj.hide_render:
|
||||
obj.hide_render = True
|
||||
hidden_objects.append(obj)
|
||||
if obj.hide_render:
|
||||
continue
|
||||
if obj in {target_obj, ground_plane}:
|
||||
continue
|
||||
if not self.ignore_lights and obj.type == "LIGHT":
|
||||
continue
|
||||
|
||||
obj.hide_render = True
|
||||
hidden_objects.append(obj)
|
||||
|
||||
scene_override = self.setup_scene_override(context)
|
||||
|
||||
@@ -105,8 +114,22 @@ class LUTB_OT_remove_hidden_faces(bpy.types.Operator):
|
||||
if self.autoremove:
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bpy.ops.mesh.delete(type="FACE")
|
||||
bpy.ops.mesh.select_all(action="SELECT")
|
||||
bpy.ops.mesh.quads_convert_to_tris(quad_method="FIXED")
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
end = timer()
|
||||
n = len(hidden_indices)
|
||||
total = len(select)
|
||||
operation = "removed" if self.autoremove else "found"
|
||||
print(
|
||||
f"hsr info: {operation} {n}/{total} hidden faces ({n / total:.2%}) "\
|
||||
f"in {end - start:.2f}s"
|
||||
)
|
||||
|
||||
else:
|
||||
print("hsr info: found no hidden faces")
|
||||
|
||||
bpy.data.scenes.remove(scene_override)
|
||||
|
||||
for obj in hidden_objects:
|
||||
@@ -115,15 +138,6 @@ class LUTB_OT_remove_hidden_faces(bpy.types.Operator):
|
||||
if ground_plane:
|
||||
bpy.data.objects.remove(ground_plane)
|
||||
|
||||
end = timer()
|
||||
n = len(hidden_indices)
|
||||
total = len(select)
|
||||
operation = "removed" if self.autoremove else "found"
|
||||
print(
|
||||
f"hsr info: {operation} {n}/{total} hidden faces ({n / total:.2%}) "\
|
||||
f"in {end - start:.2f}s"
|
||||
)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def add_ground_plane(self, context):
|
||||
|
||||
Reference in New Issue
Block a user