Portal64/tools/level_scripts/static_export.lua
2023-10-02 21:20:52 -06:00

217 lines
7.2 KiB
Lua

local sk_definition_writer = require('sk_definition_writer')
local sk_scene = require('sk_scene')
local sk_mesh = require('sk_mesh')
local sk_math = require('sk_math')
local sk_input = require('sk_input')
local room_export = require('tools.level_scripts.room_export')
local animation = require('tools.level_scripts.animation')
local signals = require('tools.level_scripts.signals')
sk_definition_writer.add_header('"../build/assets/materials/static.h"')
sk_definition_writer.add_header('"levels/level_definition.h"')
local portalable_surfaces = {
concrete_modular_wall001d = true,
concrete_modular_ceiling001a = true,
concrete_modular_floor001a = true,
}
local signal_elements = {}
local coplanar_tolerance = 0.1
local function is_coplanar(mesh, plane)
for _, vertex in pairs(mesh.vertices) do
if math.abs(plane:distance_to_point(vertex)) > coplanar_tolerance then
return false
end
end
return true
end
local function should_join_mesh(entry)
return entry.plane ~= nil
end
local function insert_or_merge(static_list, new_entry)
if not should_join_mesh(new_entry) then
table.insert(static_list, new_entry)
return
end
for _, other_entry in pairs(static_list) do
if should_join_mesh(other_entry) and
other_entry.material_index == new_entry.material_index and
other_entry.room_index == new_entry.room_index and
is_coplanar(new_entry.chunk.mesh, other_entry.plane) then
other_entry.chunk.mesh = other_entry.chunk.mesh:join(new_entry.chunk.mesh)
other_entry.original_bb = other_entry.chunk.mesh.bb
return
end
end
table.insert(static_list, new_entry)
end
local function list_static_nodes(nodes)
local result = {}
local bb_scale = sk_input.settings.fixed_point_scale
for k, v in pairs(nodes) do
local renderChunks = sk_mesh.generate_render_chunks(v.node)
local signal = sk_scene.find_named_argument(v.arguments, "indicator_lights")
for _, chunkV in pairs(renderChunks) do
local transform_index = animation.get_bone_index_for_node(v.node)
local original_bb = chunkV.mesh.bb
if transform_index then
local bone = animation.get_bone_for_index(transform_index)
local parent_pos = bone.full_transformation:decompose()
chunkV.mesh = chunkV.mesh:transform(bone.full_transformation:inverse())
original_bb.min = original_bb.min - parent_pos
original_bb.max = original_bb.max - parent_pos
end
local plane = sk_math.plane3_with_point(chunkV.mesh.normals[1], chunkV.mesh.vertices[1])
local accept_portals =chunkV.mesh.material and portalable_surfaces[chunkV.mesh.material.name] and not sk_scene.find_flag_argument(v.arguments, "no_portals")
if transform_index or signal or accept_portals or not is_coplanar(chunkV.mesh, plane) then
plane = nil
end
insert_or_merge(result, {
node = v.node,
chunk = chunkV,
material_index = chunkV.material.macro_name,
transform_index = transform_index,
room_index = room_export.node_nearest_room_index(v.node) or 0,
accept_portals = accept_portals,
signal = signal,
original_bb = original_bb,
plane = plane,
})
end
end
return result;
end
local function proccessStaticNodes(nodes)
local result = {}
local bb_scale = sk_input.settings.fixed_point_scale
local source_nodes = list_static_nodes(nodes)
for _, source_node in pairs(source_nodes) do
local gfxName = sk_mesh.generate_mesh({source_node.chunk}, "_geo", {defaultMaterial = source_node.chunk.material})
local mesh_bb = source_node.original_bb * bb_scale
mesh_bb.min.x = math.floor(mesh_bb.min.x + 0.5)
mesh_bb.min.y = math.floor(mesh_bb.min.y + 0.5)
mesh_bb.min.z = math.floor(mesh_bb.min.z + 0.5)
mesh_bb.max.x = math.floor(mesh_bb.max.x + 0.5)
mesh_bb.max.y = math.floor(mesh_bb.max.y + 0.5)
mesh_bb.max.z = math.floor(mesh_bb.max.z + 0.5)
table.insert(result, {
node = source_node.node,
mesh = source_node.chunk.mesh,
mesh_bb = mesh_bb,
display_list = sk_definition_writer.raw(gfxName),
material_index = sk_definition_writer.raw(source_node.chunk.material.macro_name),
transform_index = source_node.transform_index,
room_index = source_node.room_index,
accept_portals = source_node.accept_portals,
signal = source_node.signal,
})
end
table.sort(result, function(a, b)
return a.room_index < b.room_index
end)
return result;
end
local static_nodes = proccessStaticNodes(sk_scene.nodes_for_type('@static'))
local static_content_elements = {}
local room_ranges = {}
local static_bounding_boxes = {}
for index, static_node in pairs(static_nodes) do
if static_node.signal then
local signal_number = signals.signal_number_for_name(static_node.signal)
while #signal_elements < signal_number do
table.insert(signal_elements, {})
end
table.insert(signal_elements[signal_number], #static_content_elements)
end
table.insert(static_content_elements, {
displayList = static_node.display_list,
materialIndex = static_node.material_index,
transformIndex = static_node.transform_index and (static_node.transform_index - 1) or sk_definition_writer.raw('NO_TRANSFORM_INDEX'),
})
table.insert(static_bounding_boxes, {
static_node.mesh_bb.min.x,
static_node.mesh_bb.min.y,
static_node.mesh_bb.min.z,
static_node.mesh_bb.max.x,
static_node.mesh_bb.max.y,
static_node.mesh_bb.max.z,
})
good_index = index - 1
while (#room_ranges <= static_node.room_index) do
table.insert(room_ranges, {
good_index,
good_index
})
end
local room_range = room_ranges[static_node.room_index + 1]
room_range[2] = good_index + 1
end
sk_definition_writer.add_definition("static", "struct StaticContentElement[]", "_geo", static_content_elements)
sk_definition_writer.add_definition("room_mapping", "struct Rangeu16[]", "_geo", room_ranges)
sk_definition_writer.add_definition('bounding_boxes', 'struct BoundingBoxs16[]', '_geo', static_bounding_boxes)
local signal_indices = {}
local signal_ranges = {}
for _, element in pairs(signal_elements) do
table.insert(signal_ranges, {#signal_indices, #signal_indices + #element})
for _, index in pairs(element) do
table.insert(signal_indices, index)
end
end
sk_definition_writer.add_definition("signal_ranges", "struct Rangeu16[]", "_geo", signal_ranges)
sk_definition_writer.add_definition('signal_indices', 'u16[]', '_geo', signal_indices)
return {
static_nodes = static_nodes,
static_content_elements = static_content_elements,
static_bounding_boxes = static_bounding_boxes,
room_ranges = room_ranges,
signal_ranges = signal_ranges,
signal_indices = signal_indices,
}