Files
lu-toolbox/lu_toolbox/process_model.py

689 lines
29 KiB
Python

import bpy, bmesh
from bpy.props import *
from mathutils import Color, Matrix
from math import radians
import random
import numpy as np
from timeit import default_timer as timer
from .remove_hidden_faces import LUTB_OT_remove_hidden_faces
from .materials import *
from .divide_mesh import divide_mesh
IS_TRANSPARENT = "lu_toolbox_is_transparent"
LOD_SUFFIXES = ("LOD_0", "LOD_1", "LOD_2", "LOD_3")
class LUTB_OT_process_model(bpy.types.Operator):
"""Process LU model"""
bl_idname = "lutb.process_model"
bl_label = "Process Model"
@classmethod
def poll(cls, context):
return context.mode == "OBJECT"
def execute(self, context):
start = timer()
scene = context.scene
scene.render.engine = "CYCLES"
scene.cycles.device = "GPU" if scene.lutb_process_use_gpu else "CPU"
for obj in scene.collection.all_objects:
if not obj.type == "MESH":
continue
mat_names = {material.name.rsplit(".", 1)[0] for material in obj.data.materials}
if mat_names.issubset(MATERIALS_TRANSPARENT):
obj[IS_TRANSPARENT] = True
if scene.lutb_combine_objects:
self.combine_objects(context, scene.collection.children)
context.view_layer.update()
opaque_objects = []
transparent_objects = []
for collection in scene.collection.children:
if not collection.children:
self.report({"WARNING"},
f"Ignoring \"{collection.name}\": doesn't have any LOD collections"
)
continue
for lod_collection in collection.children:
if not lod_collection.name[-5:] in LOD_SUFFIXES:
self.report({"WARNING"},
f"Ignoring \"{lod_collection.name}\": not a LOD collection ({LOD_SUFFIXES})"
)
continue
for obj in lod_collection.all_objects:
if obj.type == "MESH":
if not obj.get(IS_TRANSPARENT):
opaque_objects.append(obj)
else:
transparent_objects.append(obj)
all_objects = opaque_objects + transparent_objects
if not all_objects:
return {"FINISHED"}
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, scene.collection.children)
self.apply_vertex_colors(context, all_objects)
if scene.lutb_setup_bake_mat:
self.setup_bake_mat(context, all_objects)
if scene.lutb_remove_hidden_faces:
for obj in transparent_objects:
obj.hide_render = True
self.remove_hidden_faces(context, opaque_objects)
for obj in transparent_objects:
obj.hide_render = False
new_objects = self.split_objects(context, scene.collection.children)
all_objects += new_objects
if scene.lutb_setup_lod_data:
self.setup_lod_data(context, scene.collection.children)
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 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
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
objs_opaque = []
for obj in lod_collection.all_objects:
if obj.type == "MESH" and not obj.get(IS_TRANSPARENT):
objs_opaque.append(obj)
if objs_opaque:
joined_opaque = self.join_objects(context, objs_opaque)
joined_opaque.name = lod_collection.name[:-6]
objs_transparent = []
for obj in lod_collection.all_objects:
if obj.type == "MESH" and obj.get(IS_TRANSPARENT):
objs_transparent.append(obj)
if scene.lutb_combine_transparent:
if objs_transparent:
joined_transparent = self.join_objects(context, objs_transparent)
joined_transparent.name = lod_collection.name[:-6]
joined_transparent[IS_TRANSPARENT] = True
def join_objects(self, context, objects):
bpy.ops.object.select_all(action="DESELECT")
for obj in objects:
obj.select_set(True)
joined = objects[0]
context.view_layer.objects.active = joined
bpy.ops.object.join()
bpy.ops.object.transform_apply()
return joined
def correct_colors(self, context, objects):
for obj in objects:
for material in obj.data.materials:
name = material.name.rsplit(".", 1)[0]
if color := MATERIALS_OPAQUE.get(name):
material.diffuse_color = color
elif color := MATERIALS_TRANSPARENT.get(name):
material.diffuse_color = color
def apply_color_variation(self, context, collections):
initial_state = random.getstate()
variation = context.scene.lutb_color_variation
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)
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
for obj in objects:
is_transparent = obj.get(IS_TRANSPARENT)
mesh = obj.data
n_loops = len(mesh.loops)
if not is_transparent:
if not (vc_lit := mesh.vertex_colors.get("Lit")):
vc_lit = mesh.vertex_colors.new(name="Lit")
lit_data = np.tile((0.0, 0.0, 0.0, 1.0), n_loops)
vc_lit.data.foreach_set("color", lit_data)
if not (vc_col := mesh.vertex_colors.get("Col")):
vc_col = mesh.vertex_colors.new(name="Col")
if len(mesh.materials) < 2:
if mesh.materials:
color = lin2srgb(mesh.materials[0].diffuse_color)
else:
color = (0.8, 0.8, 0.8, 1.0)
if is_transparent:
color[3] = scene.lutb_transparent_opacity / 100.0
color_data = np.tile(color, n_loops)
else:
colors = np.empty((len(mesh.materials), 4))
for i, material in enumerate(mesh.materials):
colors[i] = lin2srgb(material.diffuse_color)
if is_transparent:
colors[:, 3] = scene.lutb_transparent_opacity / 100.0
color_indices = np.zeros(len(mesh.loops), dtype=int)
for poly in mesh.polygons:
poly_loop_end = poly.loop_start + poly.loop_total
color_indices[poly.loop_start:poly_loop_end] = poly.material_index
color_data = colors[color_indices].flatten()
vc_col.data.foreach_set("color", color_data)
if not is_transparent:
if not (vc_alpha := mesh.vertex_colors.get("Alpha")):
vc_alpha = mesh.vertex_colors.new(name="Alpha")
alpha_data = np.tile((1.0, 1.0, 1.0, 1.0), n_loops)
vc_alpha.data.foreach_set("color", alpha_data)
if not (vc_glow := mesh.vertex_colors.get("Glow")):
vc_glow = mesh.vertex_colors.new(name="Glow")
mat_names = [mat.name.rsplit(".", 1)[0] for mat in mesh.materials]
if set(mat_names) & set(MATERIALS_GLOW):
colors = np.empty((len(mesh.materials), 4))
for i, (name, material) in enumerate(zip(mat_names, mesh.materials)):
color = MATERIALS_GLOW.get(name)
colors[i] = lin2srgb(color) if color else (0.0, 0.0, 0.0, 1.0)
color_indices = np.zeros(len(mesh.loops), dtype=int)
for poly in mesh.polygons:
poly_loop_end = poly.loop_start + poly.loop_total
color_indices[poly.loop_start:poly_loop_end] = poly.material_index
glow_data = colors[color_indices].flatten()
else:
glow_data = np.tile((0.0, 0.0, 0.0, 1.0), n_loops)
vc_glow.data.foreach_set("color", glow_data)
# no clue why setting .active doesn't work ...
mesh.vertex_colors.active_index = mesh.vertex_colors.keys().index("Col")
shading = context.area.spaces[0].shading
shading.type = "SOLID"
shading.light = "FLAT"
shading.color_type = "VERTEX"
shading.show_backface_culling = True
def setup_bake_mat(self, context, objects):
if not (bake_mat := context.scene.lutb_bake_mat):
bake_mat = context.scene.lutb_bake_mat = get_lutb_bake_mat(self)
mat_transparent = get_lutb_transparent_mat(self)
for obj in objects:
mesh = obj.data
mesh.materials.clear()
if obj.get(IS_TRANSPARENT):
mesh.materials.append(mat_transparent)
else:
mesh.materials.append(bake_mat)
def remove_hidden_faces(self, context, objects):
scene = context.scene
for obj in objects:
if obj.get(IS_TRANSPARENT):
continue
context.view_layer.objects.active = obj
bpy.ops.object.select_all(action="DESELECT")
obj.select_set(True)
bpy.ops.lutb.remove_hidden_faces(
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,
use_ground_plane=scene.lutb_hsr_use_ground_plane,
)
def split_objects(self, context, collections):
new_objects = []
for collection in collections:
for lod_collection in collection.children:
for obj in list(lod_collection.objects):
if obj.type == "MESH":
new_objects += divide_mesh(context, obj)
return new_objects
def setup_lod_data(self, context, collections):
scene = context.scene
for collection in collections:
if not collection.children:
continue
scene_node = bpy.data.objects.new(f"SceneNode_{collection.name}", None)
collection.objects.link(scene_node)
if scene.lutb_correct_orientation:
rotation = Matrix.Rotation(radians(90), 4, "X")
scene_node.matrix_world = rotation @ scene_node.matrix_world
ni_nodes = {}
used_lods = set()
for lod_collection in collection.children:
if (suffix := lod_collection.name[-5:]) in LOD_SUFFIXES:
used_lods.add(suffix)
for lod_collection in list(collection.children):
suffix = lod_collection.name[-5:]
if not suffix in LOD_SUFFIXES:
continue
for obj in list(lod_collection.all_objects):
is_transparent = bool(obj.get(IS_TRANSPARENT))
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"S{shader_prefix}_{type_prefix}_{obj_name}"[:60]
obj.name = name
if (node := ni_nodes.get(name)):
node_obj, node_lods = node
else:
node_obj = bpy.data.objects.new(name, None)
node_obj["type"] = "NiLODNode"
matrix_before = node_obj.matrix_world.copy()
node_obj.parent = scene_node
node_obj.matrix_world = matrix_before
collection.objects.link(node_obj)
node_lods = {}
node = (node_obj, node_lods)
ni_nodes[name] = node
if not (lod_obj := node_lods.get(suffix)):
lod_obj = bpy.data.objects.new(suffix, None)
lod_obj.parent = node_obj
collection.objects.link(lod_obj)
# DYNAMIC LOD HELL
if len(used_lods) == 1:
lod_obj["near_extent"] = scene.lutb_lod0
lod_obj["far_extent"] = scene.lutb_cull
elif suffix == LOD_SUFFIXES[0]:
lod_obj["near_extent"] = scene.lutb_lod0
if used_lods in [
{suffix, LOD_SUFFIXES[2]},
{suffix, LOD_SUFFIXES[2], LOD_SUFFIXES[3]}]:
lod_obj["far_extent"] = scene.lutb_lod2
elif used_lods == {suffix, LOD_SUFFIXES[3]}:
lod_obj["far_extent"] = scene.lutb_lod3
else:
lod_obj["far_extent"] = scene.lutb_lod1
elif suffix == LOD_SUFFIXES[1]:
if used_lods == {LOD_SUFFIXES[0], suffix}:
lod_obj["near_extent"] = scene.lutb_lod1
lod_obj["far_extent"] = scene.lutb_cull
elif used_lods in [
{suffix, LOD_SUFFIXES[2]},
{suffix, LOD_SUFFIXES[2], LOD_SUFFIXES[3]}]:
lod_obj["near_extent"] = scene.lutb_lod0
lod_obj["far_extent"] = scene.lutb_lod2
elif used_lods == {LOD_SUFFIXES[0], suffix, LOD_SUFFIXES[3]}:
lod_obj["near_extent"] = scene.lutb_lod1
lod_obj["far_extent"] = scene.lutb_lod3
elif used_lods == {suffix, LOD_SUFFIXES[3]}:
lod_obj["near_extent"] = scene.lutb_lod0
lod_obj["far_extent"] = scene.lutb_lod3
elif used_lods in [
{LOD_SUFFIXES[0], suffix, LOD_SUFFIXES[2]},
{LOD_SUFFIXES[0], suffix, LOD_SUFFIXES[2], LOD_SUFFIXES[3]}]:
lod_obj["near_extent"] = scene.lutb_lod1
lod_obj["far_extent"] = scene.lutb_lod2
elif suffix == LOD_SUFFIXES[2]:
if used_lods in [
{LOD_SUFFIXES[0], suffix},
{LOD_SUFFIXES[1], suffix},
{LOD_SUFFIXES[0], LOD_SUFFIXES[1], suffix}]:
lod_obj["near_extent"] = scene.lutb_lod2
lod_obj["far_extent"] = scene.lutb_cull
elif used_lods == {suffix, LOD_SUFFIXES[3]}:
lod_obj["near_extent"] = scene.lutb_lod0
lod_obj["far_extent"] = scene.lutb_lod3
elif used_lods in [
{LOD_SUFFIXES[0], suffix, LOD_SUFFIXES[3]},
{LOD_SUFFIXES[1], suffix, LOD_SUFFIXES[3]},
{LOD_SUFFIXES[0], LOD_SUFFIXES[1], suffix, LOD_SUFFIXES[3]}]:
lod_obj["near_extent"] = scene.lutb_lod2
lod_obj["far_extent"] = scene.lutb_lod3
elif suffix == LOD_SUFFIXES[3]:
lod_obj["near_extent"] = scene.lutb_lod3
lod_obj["far_extent"] = scene.lutb_cull
node_lods[suffix] = lod_obj
obj.parent = lod_obj
class LUToolboxPanel:
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "LU Toolbox"
class LUTB_PT_process_model(LUToolboxPanel, bpy.types.Panel):
bl_label = "Process Model"
def draw(self, context):
scene = context.scene
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.operator("lutb.process_model")
layout.separator(factor=0.5)
layout.prop(scene, "lutb_process_use_gpu")
layout.prop(scene, "lutb_combine_objects")
col = layout.column()
col.prop(scene, "lutb_combine_transparent")
col.enabled = scene.lutb_combine_objects
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"
bl_options = {"DEFAULT_CLOSED"}
def draw_header(self, context):
self.layout.prop(context.scene, "lutb_apply_vertex_colors", 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_apply_vertex_colors
layout.prop(scene, "lutb_correct_colors")
layout.prop(scene, "lutb_use_color_variation")
col = layout.column()
col.prop(scene, "lutb_color_variation")
col.enabled = scene.lutb_use_color_variation
layout.prop(scene, "lutb_transparent_opacity")
class LUTB_PT_setup_bake_mat(LUToolboxPanel, bpy.types.Panel):
bl_label = "Setup Bake Material"
bl_parent_id = "LUTB_PT_process_model"
bl_options = {"DEFAULT_CLOSED"}
def draw_header(self, context):
self.layout.prop(context.scene, "lutb_setup_bake_mat", 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_setup_bake_mat
layout.prop(scene, "lutb_bake_mat", text="")
class LUTB_PT_remove_hidden_faces(LUToolboxPanel, bpy.types.Panel):
bl_label = "Remove Hidden Faces"
bl_parent_id = "LUTB_PT_process_model"
bl_options = {"DEFAULT_CLOSED"}
def draw_header(self, context):
self.layout.prop(context.scene, "lutb_remove_hidden_faces", 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_remove_hidden_faces
layout.prop(scene, "lutb_hsr_autoremove")
layout.prop(scene, "lutb_hsr_vc_pre_pass")
row = layout.row()
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)
layout.prop(scene, "lutb_hsr_samples", slider=True)
class LUTB_PT_setup_metadata(LUToolboxPanel, bpy.types.Panel):
bl_label = "Setup Metadata"
bl_parent_id = "LUTB_PT_process_model"
bl_options = {"DEFAULT_CLOSED"}
def draw_header(self, context):
self.layout.prop(context.scene, "lutb_setup_lod_data", 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_setup_lod_data
layout.prop(scene, "lutb_correct_orientation")
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")
layout.prop(scene, "lutb_lod3")
layout.prop(scene, "lutb_cull")
def register():
bpy.utils.register_class(LUTB_OT_process_model)
bpy.utils.register_class(LUTB_PT_process_model)
bpy.utils.register_class(LUTB_PT_apply_vertex_colors)
bpy.utils.register_class(LUTB_PT_setup_bake_mat)
bpy.utils.register_class(LUTB_PT_remove_hidden_faces)
bpy.utils.register_class(LUTB_PT_setup_metadata)
bpy.types.Scene.lutb_process_use_gpu = BoolProperty(name="Use GPU", default=True)
bpy.types.Scene.lutb_combine_objects = BoolProperty(name="Combine Objects", default=True, description=""\
"Combine opaque bricks")
bpy.types.Scene.lutb_combine_transparent = BoolProperty(name="Combine Transparent", default=False, description=""\
"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. "\
"Note: Models imported with Toolbox import with the LU color palette natively")
bpy.types.Scene.lutb_use_color_variation = BoolProperty(name="Apply Color Variation", default=True, description=""\
"Randomly shift the brightness value of each brick")
bpy.types.Scene.lutb_color_variation = FloatProperty(name="Color Variation", subtype="PERCENTAGE", min=0.0, soft_max=15.0, max=100.0, default=5.0, description=""\
"Percentage of brightness value shift. Higher values result in more variation")
bpy.types.Scene.lutb_transparent_opacity = FloatProperty(name="Transparent Opacity", subtype="PERCENTAGE", min=0.0, max=100.0, default=58.82, description=""\
"Percentage of transparent brick opacity. "\
"This controls how see-through the models transparent bricks appear in LU. "\
"Lower values result in more transparency")
bpy.types.Scene.lutb_apply_vertex_colors = BoolProperty(name="Apply Vertex Colors", default=True, description=""\
"Apply vertex colors to the model")
bpy.types.Scene.lutb_setup_bake_mat = BoolProperty(name="Setup Bake Material", default=True, description=""\
"Apply new material to opaque bricks")
bpy.types.Scene.lutb_bake_mat = PointerProperty(name="Bake Material", type=bpy.types.Material, description=""\
"Choose the material that gets added to opaque bricks. "\
"If left blank, defaults to VertexColor material")
bpy.types.Scene.lutb_remove_hidden_faces = BoolProperty(name="Remove Hidden Faces", default=True,
description=LUTB_OT_remove_hidden_faces.__doc__)
bpy.types.Scene.lutb_hsr_autoremove = BoolProperty(name="Autoremove", default=True,
description=LUTB_OT_remove_hidden_faces.__annotations__["autoremove"].keywords["description"])
bpy.types.Scene.lutb_hsr_vc_pre_pass = BoolProperty(name="Vertex Color Pre-Pass", default=True,
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,
description=LUTB_OT_remove_hidden_faces.__annotations__["pixels_between_verts"].keywords["description"])
bpy.types.Scene.lutb_hsr_samples = IntProperty(name="Samples", min=0, default=8, soft_max=32,
description=LUTB_OT_remove_hidden_faces.__annotations__["samples"].keywords["description"])
bpy.types.Scene.lutb_hsr_use_ground_plane = BoolProperty(name="Use Ground Plane", default=False,
description=LUTB_OT_remove_hidden_faces.__annotations__["use_ground_plane"].keywords["description"])
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_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)
bpy.types.Scene.lutb_lod3 = FloatProperty(name="LOD 3", soft_min=0.0, default=280.0, soft_max=500.0)
bpy.types.Scene.lutb_cull = FloatProperty(name="Cull", soft_min=1000.0, default=10000.0, soft_max=50000.0)
def unregister():
del bpy.types.Scene.lutb_process_use_gpu
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
del bpy.types.Scene.lutb_color_variation
del bpy.types.Scene.lutb_transparent_opacity
del bpy.types.Scene.lutb_apply_vertex_colors
del bpy.types.Scene.lutb_setup_bake_mat
del bpy.types.Scene.lutb_bake_mat
del bpy.types.Scene.lutb_remove_hidden_faces
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
del bpy.types.Scene.lutb_hsr_use_ground_plane
del bpy.types.Scene.lutb_setup_lod_data
del bpy.types.Scene.lutb_correct_orientation
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
del bpy.types.Scene.lutb_cull
bpy.utils.unregister_class(LUTB_PT_setup_metadata)
bpy.utils.unregister_class(LUTB_PT_remove_hidden_faces)
bpy.utils.unregister_class(LUTB_PT_setup_bake_mat)
bpy.utils.unregister_class(LUTB_PT_apply_vertex_colors)
bpy.utils.unregister_class(LUTB_PT_process_model)
bpy.utils.unregister_class(LUTB_OT_process_model)