From 72ce3b77b31bffed55c51e589b6028434656df78 Mon Sep 17 00:00:00 2001 From: water Date: Thu, 1 Sep 2022 21:52:09 -0400 Subject: [PATCH] temp --- common/CMakeLists.txt | 5 +- common/util/gltf_util.cpp | 449 ++++++++++++++++ common/util/gltf_util.h | 71 +++ decompiler/CMakeLists.txt | 1 + decompiler/level_extractor/extract_merc.cpp | 40 ++ .../level_extractor/merc_replacement.cpp | 197 ++++++++ decompiler/level_extractor/merc_replacement.h | 23 + .../opengl_renderer/foreground/Merc2.cpp | 9 +- .../opengl_renderer/foreground/Merc2.h | 2 + goalc/build_level/TexturePool.h | 12 - goalc/build_level/Tfrag.h | 1 - goalc/build_level/build_level.cpp | 7 +- goalc/build_level/gltf_mesh_extract.cpp | 478 +----------------- goalc/build_level/gltf_mesh_extract.h | 7 +- 14 files changed, 805 insertions(+), 497 deletions(-) create mode 100644 common/util/gltf_util.cpp create mode 100644 common/util/gltf_util.h create mode 100644 decompiler/level_extractor/merc_replacement.cpp create mode 100644 decompiler/level_extractor/merc_replacement.h delete mode 100644 goalc/build_level/TexturePool.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ae9f7d870c..487343831c 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -50,9 +50,10 @@ add_library(common util/print_float.cpp util/FontUtils.cpp util/FrameLimiter.cpp - util/unicode_util.cpp) + util/unicode_util.cpp + util/gltf_util.cpp) -target_link_libraries(common fmt lzokay replxx libzstd_static) +target_link_libraries(common fmt lzokay replxx libzstd_static tiny_gltf) if(WIN32) target_link_libraries(common wsock32 ws2_32 windowsapp) diff --git a/common/util/gltf_util.cpp b/common/util/gltf_util.cpp new file mode 100644 index 0000000000..e2b4e27003 --- /dev/null +++ b/common/util/gltf_util.cpp @@ -0,0 +1,449 @@ +#include "gltf_util.h" + +#include "common/log/log.h" + +namespace gltf_util { +/*! + * Convert a GLTF position buffer or similar to std::vector + */ +std::vector extract_vec3f(const u8* data, u32 count, u32 stride) { + std::vector result; + result.reserve(count); + for (u32 i = 0; i < count; i++) { + memcpy(&result.emplace_back(), data, sizeof(math::Vector3f)); + data += stride; + } + return result; +} + +std::vector extract_vec2f(const u8* data, u32 count, u32 stride) { + std::vector result; + result.reserve(count); + for (u32 i = 0; i < count; i++) { + memcpy(&result.emplace_back(), data, sizeof(math::Vector2f)); + data += stride; + } + return result; +} + +/*! + * Convert a GLTF color buffer (u16 format) to u8 colors. + */ +std::vector> extract_color_from_vec4_u16(const u8* data, + u32 count, + u32 stride) { + std::vector> result; + result.reserve(count); + for (u32 i = 0; i < count; i++) { + math::Vector temp; + memcpy(&temp, data, sizeof(math::Vector)); + data += stride; + result.emplace_back(temp.x() >> 8, temp.y() >> 8, temp.z() >> 8, temp.w() >> 8); + } + return result; +} + +/*! + * Convert a GLTF index buffer + */ +std::vector gltf_index_buffer(const tinygltf::Model& model, + int indices_idx, + u32 index_offset) { + const auto& indices_accessor = model.accessors[indices_idx]; + const auto& buffer_view = model.bufferViews[indices_accessor.bufferView]; + const auto& buffer = model.buffers[buffer_view.buffer]; + const auto data_ptr = buffer.data.data() + buffer_view.byteOffset + indices_accessor.byteOffset; + const auto stride = indices_accessor.ByteStride(buffer_view); + const auto count = indices_accessor.count; + + switch (indices_accessor.componentType) { + case TINYGLTF_COMPONENT_TYPE_BYTE: + return index_list_to_u32(data_ptr, count, index_offset, stride); + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + return index_list_to_u32(data_ptr, count, index_offset, stride); + case TINYGLTF_COMPONENT_TYPE_SHORT: + return index_list_to_u32(data_ptr, count, index_offset, stride); + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + return index_list_to_u32(data_ptr, count, index_offset, stride); + case TINYGLTF_COMPONENT_TYPE_INT: + return index_list_to_u32(data_ptr, count, index_offset, stride); + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + return index_list_to_u32(data_ptr, count, index_offset, stride); + default: + ASSERT_MSG(false, "unsupported component type"); + } +} + +/*! + * Extract positions, colors, and normals from a mesh. + */ +ExtractedVertices gltf_vertices(const tinygltf::Model& model, + const std::map& attributes, + const math::Matrix4f& w_T_local, + bool get_colors, + bool get_normals, + const std::string& debug_name) { + std::vector result; + std::vector> vtx_colors; + + { + const auto& position_attrib = attributes.find("POSITION"); + ASSERT_MSG(position_attrib != attributes.end(), "Did not find position attribute."); + + const auto attrib_accessor = model.accessors[position_attrib->second]; + const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; + const auto& buffer = model.buffers[buffer_view.buffer]; + const auto data_ptr = buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; + const auto byte_stride = attrib_accessor.ByteStride(buffer_view); + const auto count = attrib_accessor.count; + + ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC3, "POSITION wasn't vec3"); + ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, + "POSITION wasn't float"); + // for (auto& attrib : attributes) { + // fmt::print("attrib: {}\n", attrib.first); + //} + auto mesh_verts = extract_vec3f(data_ptr, count, byte_stride); + result.reserve(mesh_verts.size()); + for (auto& vert : mesh_verts) { + auto& new_vert = result.emplace_back(); + math::Vector4f v_in(vert.x(), vert.y(), vert.z(), 1); + math::Vector4f v_w = w_T_local * v_in; + new_vert.x = v_w.x() * 4096; + new_vert.y = v_w.y() * 4096; + new_vert.z = v_w.z() * 4096; + } + } + + if (get_colors) { + const auto& color_attrib = attributes.find("COLOR_0"); + if (color_attrib == attributes.end()) { + lg::error("Mesh {} didn't have any colors, using white", debug_name); + for (size_t i = 0; i < result.size(); i++) { + vtx_colors.emplace_back(0x80, 0x80, 0x80, 0xff); + } + } else { + const auto attrib_accessor = model.accessors[color_attrib->second]; + const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; + const auto& buffer = model.buffers[buffer_view.buffer]; + const auto data_ptr = + buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; + const auto byte_stride = attrib_accessor.ByteStride(buffer_view); + const auto count = attrib_accessor.count; + + ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC4, "COLOR_0 wasn't vec4"); + ASSERT_MSG( + attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT, + fmt::format("COLOR_0 wasn't float, got {} instead", attrib_accessor.componentType)); + auto colors = extract_color_from_vec4_u16(data_ptr, count, byte_stride); + vtx_colors.insert(vtx_colors.end(), colors.begin(), colors.end()); + } + + // ASSERT_MSG(color_attrib != attributes.end(), "Did not find color attribute."); + } + + bool got_texture = false; + { + const auto& texcoord_attrib = attributes.find("TEXCOORD_0"); + if (texcoord_attrib != attributes.end()) { + const auto attrib_accessor = model.accessors[texcoord_attrib->second]; + const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; + const auto& buffer = model.buffers[buffer_view.buffer]; + const auto data_ptr = + buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; + const auto byte_stride = attrib_accessor.ByteStride(buffer_view); + const auto count = attrib_accessor.count; + + ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC2, "TEXCOORD wasn't vec2"); + ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, + "TEXCOORD wasn't float"); + auto mesh_verts = extract_vec2f(data_ptr, count, byte_stride); + ASSERT(mesh_verts.size() == result.size()); + got_texture = true; + for (size_t i = 0; i < mesh_verts.size(); i++) { + result[i].s = mesh_verts[i].x(); + result[i].t = mesh_verts[i].y(); + } + } else { + if (!get_normals) { + // don't warn if we're just getting collision + lg::warn("No texcoord attribute for mesh: {}", debug_name); + } + } + } + + std::vector normals; + if (get_normals) { + const auto& normal_attrib = attributes.find("NORMAL"); + if (normal_attrib != attributes.end()) { + const auto attrib_accessor = model.accessors[normal_attrib->second]; + const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; + const auto& buffer = model.buffers[buffer_view.buffer]; + const auto data_ptr = + buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; + const auto byte_stride = attrib_accessor.ByteStride(buffer_view); + const auto count = attrib_accessor.count; + + ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC3, "NORMAL wasn't vec3"); + ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, + "NORMAL wasn't float"); + normals = extract_vec3f(data_ptr, count, byte_stride); + for (auto& nrm : normals) { + math::Vector4f nrm4(nrm.x(), nrm.y(), nrm.z(), 0.f); + nrm = (w_T_local * nrm4).xyz(); + } + ASSERT(normals.size() == result.size()); + } else { + lg::error("No NORMAL attribute for mesh: {}", debug_name); + } + } + + for (auto& v : result) { + v.color_index = 0; + if (!got_texture) { + v.s = 0; + v.t = 0; + } + + v.q_unused = 0; + v.pad[0] = 0; + v.pad[1] = 0; + v.pad[2] = 0; + } + // TODO: other properties + return {result, vtx_colors, normals}; +} + +DrawMode make_default_draw_mode() { + DrawMode mode; + mode.set_depth_write_enable(true); + mode.set_depth_test(GsTest::ZTest::GEQUAL); + mode.set_alpha_blend(DrawMode::AlphaBlend::DISABLED); + mode.set_aref(0); + mode.set_alpha_fail(GsTest::AlphaFail::KEEP); + mode.set_clamp_s_enable(false); + mode.set_clamp_t_enable(false); + mode.disable_filt(); // for checkerboard... + mode.enable_tcc(); // ? + mode.disable_at(); + mode.enable_zt(); + mode.disable_ab(); + mode.disable_decal(); + mode.enable_fog(); + return mode; +} + +int texture_pool_add_texture(TexturePool* pool, const tinygltf::Image& tex) { + const auto& existing = pool->textures_by_name.find(tex.name); + if (existing != pool->textures_by_name.end()) { + lg::info("Reusing image: {}", tex.name); + return existing->second; + } else { + lg::info("adding new texture: {}, size {} kB", tex.name, tex.width * tex.height * 4 / 1024); + } + + ASSERT(tex.bits == 8); + ASSERT(tex.component == 4); + ASSERT(tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE); + + size_t idx = pool->textures_by_idx.size(); + pool->textures_by_name[tex.name] = idx; + auto& tt = pool->textures_by_idx.emplace_back(); + tt.w = tex.width; + tt.h = tex.height; + tt.debug_name = tex.name; + tt.debug_tpage_name = "custom-level"; + tt.load_to_pool = false; + tt.combo_id = 0; // doesn't matter, not a pool tex + tt.data.resize(tt.w * tt.h); + ASSERT(tex.image.size() >= tt.data.size()); + memcpy(tt.data.data(), tex.image.data(), tt.data.size() * 4); + return idx; +} + +int texture_pool_debug_checker(TexturePool* pool) { + const auto& existing = pool->textures_by_name.find("DEBUG_CHECKERBOARD"); + if (existing == pool->textures_by_name.end()) { + size_t idx = pool->textures_by_idx.size(); + pool->textures_by_name["DEBUG_CHECKERBOARD"] = idx; + auto& tex = pool->textures_by_idx.emplace_back(); + tex.w = 16; + tex.h = 16; + tex.debug_name = "DEBUG_CHECKERBOARD"; + tex.debug_tpage_name = "DEBUG"; + tex.load_to_pool = false; + tex.combo_id = 0; // doesn't matter, not a pool tex + tex.data.resize(16 * 16); + u32 c0 = 0xa0303030; + u32 c1 = 0xa0e0e0e0; + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + tex.data[i * 16 + j] = (((i / 4) & 1) ^ ((j / 4) & 1)) ? c1 : c0; + } + } + return idx; + } else { + return existing->second; + } +} + +math::Matrix4f affine_translation(const math::Vector3f& translation) { + math::Matrix4f result = math::Matrix4f::identity(); + result(0, 3) = translation[0]; + result(1, 3) = translation[1]; + result(2, 3) = translation[2]; + result(3, 3) = 1; + return result; +} + +math::Matrix4f affine_scale(const math::Vector3f& scale) { + math::Matrix4f result = math::Matrix4f::zero(); + result(0, 0) = scale[0]; + result(1, 1) = scale[1]; + result(2, 2) = scale[2]; + result(3, 3) = 1; + return result; +} + +math::Matrix4f affine_rot_qxyzw(const math::Vector4f& quat) { + math::Matrix4f result = math::Matrix4f::zero(); + result(3, 3) = 1; + result(0, 0) = 1.0 - 2.0 * (quat.y() * quat.y() + quat.z() * quat.z()); + result(0, 1) = 2.0 * (quat.x() * quat.y() - quat.z() * quat.w()); + result(0, 2) = 2.0 * (quat.x() * quat.z() + quat.y() * quat.w()); + result(1, 0) = 2.0 * (quat.x() * quat.y() + quat.z() * quat.w()); + result(1, 1) = 1.0 - 2.0 * (quat.x() * quat.x() + quat.z() * quat.z()); + result(1, 2) = 2.0 * (quat.y() * quat.z() - quat.x() * quat.w()); + result(2, 0) = 2.0 * (quat.x() * quat.z() - quat.y() * quat.w()); + result(2, 1) = 2.0 * (quat.y() * quat.z() + quat.x() * quat.w()); + result(2, 2) = 1.0 - 2.0 * (quat.x() * quat.x() + quat.y() * quat.y()); + return result; +} + +math::Vector3f vector3f_from_gltf(const std::vector& in) { + ASSERT(in.size() == 3); + return math::Vector3f{in[0], in[1], in[2]}; +} + +math::Vector4f vector4f_from_gltf(const std::vector& in) { + ASSERT(in.size() == 4); + return math::Vector4f{in[0], in[1], in[2], in[3]}; +} + +math::Matrix4f matrix_from_node(const tinygltf::Node& node) { + if (!node.matrix.empty()) { + math::Matrix4f result; + for (int i = 0; i < 16; i++) { + result.data()[i] = node.matrix[i]; + } + return result; + } else { + // from trs + math::Matrix4f t, r, s; + if (!node.translation.empty()) { + t = affine_translation(vector3f_from_gltf(node.translation)); + } else { + t = math::Matrix4f::identity(); + } + + if (!node.rotation.empty()) { + r = affine_rot_qxyzw(vector4f_from_gltf(node.rotation)); + } else { + r = math::Matrix4f::identity(); + } + + if (!node.scale.empty()) { + s = affine_scale(vector3f_from_gltf(node.scale)); + } else { + s = math::Matrix4f::identity(); + } + + return t * r * s; + } +} +/*! + * Recursively walk the tree of nodes, flatten, and compute w_T_node for each. + */ +void node_find_helper(const tinygltf::Model& model, + const math::Matrix4f& w_T_parent, + int node_idx, + std::vector* out) { + const auto& node = model.nodes.at(node_idx); + math::Matrix4f w_T_node = w_T_parent * matrix_from_node(node); + out->push_back({node_idx, w_T_node}); + for (auto& child : node.children) { + node_find_helper(model, w_T_node, child, out); + } +} + +std::vector flatten_nodes_from_all_scenes(const tinygltf::Model& model) { + std::vector out; + for (auto& scene : model.scenes) { + for (auto& nidx : scene.nodes) { + math::Matrix4f identity = math::Matrix4f::identity(); + node_find_helper(model, identity, nidx, &out); + } + } + return out; +} + +void dedup_vertices(const std::vector& vertices_in, + std::vector& vertices_out, + std::vector& old_to_new_out) { + ASSERT(vertices_out.empty()); + ASSERT(old_to_new_out.empty()); + old_to_new_out.resize(vertices_in.size(), -1); + + std::unordered_map vtx_to_new; + + for (size_t in_idx = 0; in_idx < vertices_in.size(); in_idx++) { + auto& vtx = vertices_in[in_idx]; + const auto& lookup = vtx_to_new.find(vtx); + if (lookup == vtx_to_new.end()) { + // first time seeing this one + size_t new_idx = vertices_out.size(); + vertices_out.push_back(vtx); + old_to_new_out[in_idx] = new_idx; + vtx_to_new[vtx] = new_idx; + } else { + old_to_new_out[in_idx] = lookup->second; + } + } +} + +DrawMode draw_mode_from_sampler(const tinygltf::Sampler& sampler) { + DrawMode mode = make_default_draw_mode(); + if (sampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) { + ASSERT(sampler.minFilter == TINYGLTF_TEXTURE_FILTER_NEAREST); + mode.set_filt_enable(false); + } else { + ASSERT(sampler.minFilter != TINYGLTF_TEXTURE_FILTER_NEAREST); + mode.set_filt_enable(true); + } + + switch (sampler.wrapS) { + case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE: + mode.set_clamp_s_enable(true); + break; + case TINYGLTF_TEXTURE_WRAP_REPEAT: + mode.set_clamp_s_enable(false); + break; + default: + ASSERT(false); + } + + switch (sampler.wrapT) { + case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE: + mode.set_clamp_t_enable(true); + break; + case TINYGLTF_TEXTURE_WRAP_REPEAT: + mode.set_clamp_t_enable(false); + break; + default: + ASSERT(false); + } + + return mode; +} + +} // namespace gltf_util \ No newline at end of file diff --git a/common/util/gltf_util.h b/common/util/gltf_util.h new file mode 100644 index 0000000000..71dcc47bfa --- /dev/null +++ b/common/util/gltf_util.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "common/custom_data/Tfrag3Data.h" +#include "common/math/Vector.h" + +#include "third-party/tiny_gltf/tiny_gltf.h" + +namespace gltf_util { + +/*! + * Convert a GLTF index buffer to std::vector + */ +template +std::vector index_list_to_u32(const u8* data, u32 num_verts, u32 offset, u32 stride) { + std::vector result; + result.reserve(num_verts); + for (u32 i = 0; i < num_verts; i++) { + T val; + memcpy(&val, data, sizeof(T)); + result.push_back(offset + val); + data += stride; + } + return result; +} + +std::vector extract_vec3f(const u8* data, u32 count, u32 stride); +std::vector extract_vec2f(const u8* data, u32 count, u32 stride); +std::vector> extract_color_from_vec4_u16(const u8* data, u32 count, u32 stride); +std::vector gltf_index_buffer(const tinygltf::Model& model, int indices_idx, u32 index_offset); + +struct ExtractedVertices { + std::vector vtx; + std::vector> vtx_colors; + std::vector normals; +}; + +ExtractedVertices gltf_vertices(const tinygltf::Model& model, + const std::map& attributes, + const math::Matrix4f& w_T_local, + bool get_colors, + bool get_normals, + const std::string& debug_name); +DrawMode make_default_draw_mode(); + +struct TexturePool { + std::unordered_map textures_by_name; + std::vector textures_by_idx; +}; + +int texture_pool_add_texture(TexturePool* pool, const tinygltf::Image& tex); +int texture_pool_debug_checker(TexturePool* pool); + +struct NodeWithTransform { + int node_idx; + math::Matrix4f w_T_node; +}; + +void dedup_vertices(const std::vector& vertices_in, + std::vector& vertices_out, + std::vector& old_to_new_out); + +std::vector flatten_nodes_from_all_scenes(const tinygltf::Model& model); + +DrawMode draw_mode_from_sampler(const tinygltf::Sampler& sampler); + +} // namespace gltf_util \ No newline at end of file diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index 0374470012..6c91965a92 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -63,6 +63,7 @@ add_library( level_extractor/fr3_to_gltf.cpp level_extractor/MercData.cpp level_extractor/tfrag_tie_fixup.cpp + level_extractor/merc_replacement.cpp ObjectFile/LinkedObjectFile.cpp ObjectFile/LinkedObjectFileCreation.cpp diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index 73c1873c58..5fef11b0f8 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -5,6 +5,7 @@ #include "decompiler/level_extractor/MercData.h" #include "decompiler/level_extractor/extract_common.h" +#include "decompiler/level_extractor/merc_replacement.h" #include "decompiler/util/goal_data_reader.h" namespace decompiler { @@ -985,5 +986,44 @@ void extract_merc(const ObjectFileData& ag_data, } } } + + // replace eichar-lod0: + for (auto& model : out.merc_data.models) { + if (model.name == "eichar-lod0" && model.max_bones < 100) { + fmt::print("DOING SWAP!!!!!!!!!!!!!!!!!!!! {} {} {}\n", model.effects.size(), model.max_bones, + model.max_draws); + + std::vector old_verts; + for (auto& e : model.effects) { + for (auto& d : e.draws) { + for (size_t i = 0; i < d.index_count; i++) { + auto idx = out.merc_data.indices.at(i + d.first_index); + if (idx != UINT32_MAX) { + old_verts.push_back(out.merc_data.vertices[idx]); + } + } + } + } + + auto swap_info = load_replacement_merc_model( + out.merc_data.indices.size(), out.merc_data.vertices.size(), out.textures.size(), + file_util::get_file_path({"custom_levels/jakswap/jak2_2.glb"}), old_verts); + model = swap_info.new_model; + model.name = "eichar-lod0"; + fmt::print("swapping: {} inds, {} verts, {} tex\n", swap_info.new_indices.size(), + swap_info.new_vertices.size(), swap_info.new_textures.size()); + size_t old_start = out.merc_data.vertices.size(); + for (auto& ind : swap_info.new_indices) { + ASSERT(ind >= old_start); + } + out.merc_data.indices.insert(out.merc_data.indices.end(), swap_info.new_indices.begin(), + swap_info.new_indices.end()); + out.merc_data.vertices.insert(out.merc_data.vertices.end(), swap_info.new_vertices.begin(), + swap_info.new_vertices.end()); + out.textures.insert(out.textures.end(), swap_info.new_textures.begin(), + swap_info.new_textures.end()); + break; + } + } } } // namespace decompiler diff --git a/decompiler/level_extractor/merc_replacement.cpp b/decompiler/level_extractor/merc_replacement.cpp new file mode 100644 index 0000000000..8c698b0553 --- /dev/null +++ b/decompiler/level_extractor/merc_replacement.cpp @@ -0,0 +1,197 @@ +#include "merc_replacement.h" + +#include "common/log/log.h" +#include "common/util/gltf_util.h" + +#include "third-party/tiny_gltf/tiny_gltf.h" + +using namespace gltf_util; +namespace { + +struct MercExtractData { + TexturePool tex_pool; + std::vector new_indices; + std::vector new_vertices; + std::vector> new_colors; + + tfrag3::MercModel new_model; +}; + +void extract(MercExtractData& out, + const tinygltf::Model& model, + const std::vector& all_nodes, + u32 index_offset, + u32 vertex_offset, + u32 tex_offset) { + ASSERT(out.new_vertices.empty()); + std::map draw_by_material; + int mesh_count = 0; + int prim_count = 0; + + for (const auto& n : all_nodes) { + const auto& node = model.nodes[n.node_idx]; + if (node.mesh >= 0) { + const auto& mesh = model.meshes[node.mesh]; + mesh_count++; + for (const auto& prim : mesh.primitives) { + prim_count++; + // extract index buffer + std::vector prim_indices = + gltf_index_buffer(model, prim.indices, out.new_vertices.size() + vertex_offset); + ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode"); + // extract vertices + auto verts = gltf_vertices(model, prim.attributes, n.w_T_node, true, true, mesh.name); + out.new_vertices.insert(out.new_vertices.end(), verts.vtx.begin(), verts.vtx.end()); + out.new_colors.insert(out.new_colors.end(), verts.vtx_colors.begin(), + verts.vtx_colors.end()); + ASSERT(out.new_colors.size() == out.new_vertices.size()); + + // TODO: just putting it all in one material + auto& draw = draw_by_material[prim.material]; + draw.mode = make_default_draw_mode(); // todo rm + draw.tree_tex_id = 0; // todo rm + draw.num_triangles += prim_indices.size() / 3; + // if (draw.vis_groups.empty()) { + // auto& grp = draw.vis_groups.emplace_back(); + // grp.num_inds += prim_indices.size(); + // grp.num_tris += draw.num_triangles; + // grp.vis_idx_in_pc_bvh = UINT32_MAX; + // } else { + // auto& grp = draw.vis_groups.back(); + // grp.num_inds += prim_indices.size(); + // grp.num_tris += draw.num_triangles; + // grp.vis_idx_in_pc_bvh = UINT32_MAX; + // } + draw.index_count = prim_indices.size(); + draw.first_index = index_offset + out.new_indices.size(); + + out.new_indices.insert(out.new_indices.end(), prim_indices.begin(), prim_indices.end()); + } + } + } + + tfrag3::MercEffect e; + out.new_model.max_bones = 120; // idk + out.new_model.max_draws = 200; + for (const auto& [mat_idx, d_] : draw_by_material) { + e.draws.push_back(d_); + auto& draw = e.draws.back(); + draw.mode = make_default_draw_mode(); + + if (mat_idx == -1) { + lg::warn("Draw had a material index of -1, using default texture."); + draw.tree_tex_id = 0; + continue; + } + const auto& mat = model.materials[mat_idx]; + int tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index; + if (tex_idx == -1) { + lg::warn("Material {} has no texture, using default texture.", mat.name); + draw.tree_tex_id = 0; + continue; + } + + const auto& tex = model.textures[tex_idx]; + ASSERT(tex.sampler >= 0); + ASSERT(tex.source >= 0); + draw.mode = draw_mode_from_sampler(model.samplers.at(tex.sampler)); + + const auto& img = model.images[tex.source]; + draw.tree_tex_id = tex_offset + texture_pool_add_texture(&out.tex_pool, img); + } + lg::info("total of {} unique materials", e.draws.size()); + out.new_model.effects.push_back(e); + out.new_model.effects.push_back(e); + out.new_model.effects.push_back(e); + out.new_model.effects.push_back(e); + + lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count, + out.new_vertices.size()); +} + +const tfrag3::MercVertex& find_closest(const std::vector& old, + float x, + float y, + float z) { + float best_dist = 1e10; + int best_idx = 0; + + for (int i = 0; i < old.size(); i++) { + auto& v = old[i]; + float dx = v.pos[0] - x; + float dy = v.pos[1] - y; + float dz = v.pos[2] - z; + float dist = (dx * dx) + (dy * dy) + (dz * dz); + if (dist < best_dist) { + best_dist = dist; + best_idx = i; + } + } + + return old[best_idx]; +} + +void merc_convert(MercSwapData& out, + const MercExtractData& in, + const std::vector& old_verts) { + /* + * std::vector new_indices; + std::vector new_vertices; + std::vector new_textures; + tfrag3::MercModel new_model; + */ + + // easy + out.new_model = in.new_model; + out.new_indices = in.new_indices; + out.new_textures = in.tex_pool.textures_by_idx; + + // convert vertices + for (size_t i = 0; i < in.new_vertices.size(); i++) { + const auto& y = in.new_vertices[i]; + const auto& copy_from = find_closest(old_verts, y.x, y.y, y.z); + auto& x = out.new_vertices.emplace_back(); + x.pos[0] = y.x; + x.pos[1] = y.y; + x.pos[2] = y.z; + x.normal[0] = copy_from.normal[0]; + x.normal[1] = copy_from.normal[1]; + x.normal[2] = copy_from.normal[2]; + x.weights[0] = copy_from.weights[0]; + x.weights[1] = copy_from.weights[1]; + x.weights[2] = copy_from.weights[2]; + x.st[0] = y.s; + x.st[1] = y.t; + x.rgba[0] = in.new_colors[i][0]; + x.rgba[1] = in.new_colors[i][1]; + x.rgba[2] = in.new_colors[i][2]; + x.rgba[3] = in.new_colors[i][3]; + x.mats[0] = copy_from.mats[0]; + x.mats[1] = copy_from.mats[1]; + x.mats[2] = copy_from.mats[2]; + } +} +} // namespace + +MercSwapData load_replacement_merc_model(u32 current_idx_count, + u32 current_vtx_count, + u32 current_tex_count, + const std::string& path, + const std::vector& old_verts) { + MercSwapData result; + lg::info("Reading gltf mesh: {}", path); + tinygltf::TinyGLTF loader; + tinygltf::Model model; + std::string err, warn; + bool res = loader.LoadBinaryFromFile(&model, &err, &warn, path); + ASSERT_MSG(warn.empty(), warn.c_str()); + ASSERT_MSG(err.empty(), err.c_str()); + ASSERT_MSG(res, "Failed to load GLTF file!"); + auto all_nodes = flatten_nodes_from_all_scenes(model); + + MercExtractData extract_data; + extract(extract_data, model, all_nodes, current_idx_count, current_vtx_count, current_tex_count); + merc_convert(result, extract_data, old_verts); + + return result; +} \ No newline at end of file diff --git a/decompiler/level_extractor/merc_replacement.h b/decompiler/level_extractor/merc_replacement.h new file mode 100644 index 0000000000..4d36d2c41c --- /dev/null +++ b/decompiler/level_extractor/merc_replacement.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "common/custom_data/Tfrag3Data.h" + +#include "decompiler/data/TextureDB.h" + +// Data produced by loading a replacement model +struct MercSwapData { + std::vector new_indices; + std::vector new_vertices; + std::vector new_textures; + tfrag3::MercModel new_model; +}; + +MercSwapData load_replacement_merc_model(u32 current_idx_count, + u32 current_vtx_count, + u32 current_tex_count, + const std::string& path, + const std::vector& old_verts); diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 0af7fc6a72..5592a6b2f0 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -44,10 +44,14 @@ void Merc2::init_pc_model(const DmaTransfer& setup, SharedRenderState* render_st // get the model from the loader m_current_model = render_state->loader->get_merc_model(name); + m_no_strip_hack = false; // update stats m_stats.num_models++; if (m_current_model) { + if (m_current_model->model->name == "eichar-lod0") { + m_no_strip_hack = true; + } for (const auto& effect : m_current_model->model->effects) { m_stats.num_effects++; m_stats.num_predicted_draws += effect.draws.size(); @@ -511,6 +515,7 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN draw->light_idx = lights; draw->num_triangles = mdraw.num_triangles; draw->ignore_alpha = ignore_alpha; + draw->no_strip = m_no_strip_hack; } } @@ -620,8 +625,8 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil prof.add_tri(draw.num_triangles); glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer, sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat)); - glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT, - (void*)(sizeof(u32) * draw.first_index)); + glDrawElements(draw.no_strip ? GL_TRIANGLES : GL_TRIANGLE_STRIP, draw.index_count, + GL_UNSIGNED_INT, (void*)(sizeof(u32) * draw.first_index)); } } diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index 8b1dbeb093..1253ffae36 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -57,6 +57,7 @@ class Merc2 : public BucketRenderer { u32 alloc_bones(int count); std::optional m_current_model = std::nullopt; + bool m_no_strip_hack = false; u16 m_current_effect_enable_bits = 0; u16 m_current_ignore_alpha_bits = 0; @@ -126,6 +127,7 @@ class Merc2 : public BucketRenderer { u16 first_bone; u16 light_idx; u8 ignore_alpha; + u8 no_strip; }; struct LevelDrawBucket { diff --git a/goalc/build_level/TexturePool.h b/goalc/build_level/TexturePool.h deleted file mode 100644 index 5309eb7515..0000000000 --- a/goalc/build_level/TexturePool.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "common/custom_data/Tfrag3Data.h" - -struct TexturePool { - std::unordered_map textures_by_name; - std::vector textures_by_idx; -}; \ No newline at end of file diff --git a/goalc/build_level/Tfrag.h b/goalc/build_level/Tfrag.h index 5fe9198a0c..c34526af7d 100644 --- a/goalc/build_level/Tfrag.h +++ b/goalc/build_level/Tfrag.h @@ -4,7 +4,6 @@ #include "common/custom_data/Tfrag3Data.h" -#include "goalc/build_level/TexturePool.h" #include "goalc/build_level/gltf_mesh_extract.h" class DataObjectGenerator; diff --git a/goalc/build_level/build_level.cpp b/goalc/build_level/build_level.cpp index 240974794e..e7b89a081c 100644 --- a/goalc/build_level/build_level.cpp +++ b/goalc/build_level/build_level.cpp @@ -2,6 +2,7 @@ #include "common/log/log.h" #include "common/util/FileUtil.h" #include "common/util/compress.h" +#include "common/util/gltf_util.h" #include "common/util/json_util.h" #include "goalc/build_level/Entity.h" @@ -40,9 +41,9 @@ bool run_build_level(const std::string& input_file, const std::string& output_prefix) { auto level_json = parse_commented_json( file_util::read_text_file(file_util::get_file_path({input_file})), input_file); - LevelFile file; // GOAL level file - tfrag3::Level pc_level; // PC level file - TexturePool tex_pool; // pc level texture pool + LevelFile file; // GOAL level file + tfrag3::Level pc_level; // PC level file + gltf_util::TexturePool tex_pool; // pc level texture pool // process input mesh from blender gltf_mesh_extract::Input mesh_extract_in; diff --git a/goalc/build_level/gltf_mesh_extract.cpp b/goalc/build_level/gltf_mesh_extract.cpp index 7ef450bd7c..67ae5b1299 100644 --- a/goalc/build_level/gltf_mesh_extract.cpp +++ b/goalc/build_level/gltf_mesh_extract.cpp @@ -9,459 +9,22 @@ #include "common/log/log.h" #include "common/math/geometry.h" #include "common/util/Timer.h" +#include "common/util/gltf_util.h" #include "goalc/build_level/color_quantization.h" #include "third-party/tiny_gltf/tiny_gltf.h" +using namespace gltf_util; namespace gltf_mesh_extract { -namespace { - -/*! - * Convert a GLTF index buffer to std::vector - */ -template -std::vector index_list_to_u32(const u8* data, u32 num_verts, u32 offset, u32 stride) { - std::vector result; - result.reserve(num_verts); - for (u32 i = 0; i < num_verts; i++) { - T val; - memcpy(&val, data, sizeof(T)); - result.push_back(offset + val); - data += stride; - } - return result; -} - -/*! - * Convert a GLTF position buffer or similar to std::vector - */ -std::vector extract_vec3f(const u8* data, u32 count, u32 stride) { - std::vector result; - result.reserve(count); - for (u32 i = 0; i < count; i++) { - memcpy(&result.emplace_back(), data, sizeof(math::Vector3f)); - data += stride; - } - return result; -} - -std::vector extract_vec2f(const u8* data, u32 count, u32 stride) { - std::vector result; - result.reserve(count); - for (u32 i = 0; i < count; i++) { - memcpy(&result.emplace_back(), data, sizeof(math::Vector2f)); - data += stride; - } - return result; -} - -/*! - * Convert a GLTF color buffer (u16 format) to u8 colors. - */ -std::vector> extract_color_from_vec4_u16(const u8* data, - u32 count, - u32 stride) { - std::vector> result; - result.reserve(count); - for (u32 i = 0; i < count; i++) { - math::Vector temp; - memcpy(&temp, data, sizeof(math::Vector)); - data += stride; - result.emplace_back(temp.x() >> 8, temp.y() >> 8, temp.z() >> 8, temp.w() >> 8); - } - return result; -} - -/*! - * Convert a GLTF index buffer - */ -std::vector gltf_index_buffer(const tinygltf::Model& model, - int indices_idx, - u32 index_offset) { - const auto& indices_accessor = model.accessors[indices_idx]; - const auto& buffer_view = model.bufferViews[indices_accessor.bufferView]; - const auto& buffer = model.buffers[buffer_view.buffer]; - const auto data_ptr = buffer.data.data() + buffer_view.byteOffset + indices_accessor.byteOffset; - const auto stride = indices_accessor.ByteStride(buffer_view); - const auto count = indices_accessor.count; - - switch (indices_accessor.componentType) { - case TINYGLTF_COMPONENT_TYPE_BYTE: - return index_list_to_u32(data_ptr, count, index_offset, stride); - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: - return index_list_to_u32(data_ptr, count, index_offset, stride); - case TINYGLTF_COMPONENT_TYPE_SHORT: - return index_list_to_u32(data_ptr, count, index_offset, stride); - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: - return index_list_to_u32(data_ptr, count, index_offset, stride); - case TINYGLTF_COMPONENT_TYPE_INT: - return index_list_to_u32(data_ptr, count, index_offset, stride); - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: - return index_list_to_u32(data_ptr, count, index_offset, stride); - default: - ASSERT_MSG(false, "unsupported component type"); - } -} - -struct ExtractedVertices { - std::vector vtx; - std::vector> vtx_colors; - std::vector normals; -}; - -/*! - * Extract positions, colors, and normals from a mesh. - */ -ExtractedVertices gltf_vertices(const tinygltf::Model& model, - const std::map& attributes, - const math::Matrix4f& w_T_local, - bool get_colors, - bool get_normals, - const std::string& debug_name) { - std::vector result; - std::vector> vtx_colors; - - { - const auto& position_attrib = attributes.find("POSITION"); - ASSERT_MSG(position_attrib != attributes.end(), "Did not find position attribute."); - - const auto attrib_accessor = model.accessors[position_attrib->second]; - const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; - const auto& buffer = model.buffers[buffer_view.buffer]; - const auto data_ptr = buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; - const auto byte_stride = attrib_accessor.ByteStride(buffer_view); - const auto count = attrib_accessor.count; - - ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC3, "POSITION wasn't vec3"); - ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, - "POSITION wasn't float"); - // for (auto& attrib : attributes) { - // fmt::print("attrib: {}\n", attrib.first); - //} - auto mesh_verts = extract_vec3f(data_ptr, count, byte_stride); - result.reserve(mesh_verts.size()); - for (auto& vert : mesh_verts) { - auto& new_vert = result.emplace_back(); - math::Vector4f v_in(vert.x(), vert.y(), vert.z(), 1); - math::Vector4f v_w = w_T_local * v_in; - new_vert.x = v_w.x() * 4096; - new_vert.y = v_w.y() * 4096; - new_vert.z = v_w.z() * 4096; - } - } - - if (get_colors) { - const auto& color_attrib = attributes.find("COLOR_0"); - if (color_attrib == attributes.end()) { - lg::error("Mesh {} didn't have any colors, using white", debug_name); - for (size_t i = 0; i < result.size(); i++) { - vtx_colors.emplace_back(0x80, 0x80, 0x80, 0xff); - } - } else { - const auto attrib_accessor = model.accessors[color_attrib->second]; - const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; - const auto& buffer = model.buffers[buffer_view.buffer]; - const auto data_ptr = - buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; - const auto byte_stride = attrib_accessor.ByteStride(buffer_view); - const auto count = attrib_accessor.count; - - ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC4, "COLOR_0 wasn't vec4"); - ASSERT_MSG( - attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT, - fmt::format("COLOR_0 wasn't float, got {} instead", attrib_accessor.componentType)); - auto colors = extract_color_from_vec4_u16(data_ptr, count, byte_stride); - vtx_colors.insert(vtx_colors.end(), colors.begin(), colors.end()); - } - - // ASSERT_MSG(color_attrib != attributes.end(), "Did not find color attribute."); - } - - bool got_texture = false; - { - const auto& texcoord_attrib = attributes.find("TEXCOORD_0"); - if (texcoord_attrib != attributes.end()) { - const auto attrib_accessor = model.accessors[texcoord_attrib->second]; - const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; - const auto& buffer = model.buffers[buffer_view.buffer]; - const auto data_ptr = - buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; - const auto byte_stride = attrib_accessor.ByteStride(buffer_view); - const auto count = attrib_accessor.count; - - ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC2, "TEXCOORD wasn't vec2"); - ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, - "TEXCOORD wasn't float"); - auto mesh_verts = extract_vec2f(data_ptr, count, byte_stride); - ASSERT(mesh_verts.size() == result.size()); - got_texture = true; - for (size_t i = 0; i < mesh_verts.size(); i++) { - result[i].s = mesh_verts[i].x(); - result[i].t = mesh_verts[i].y(); - } - } else { - if (!get_normals) { - // don't warn if we're just getting collision - lg::warn("No texcoord attribute for mesh: {}", debug_name); - } - } - } - - std::vector normals; - if (get_normals) { - const auto& normal_attrib = attributes.find("NORMAL"); - if (normal_attrib != attributes.end()) { - const auto attrib_accessor = model.accessors[normal_attrib->second]; - const auto& buffer_view = model.bufferViews[attrib_accessor.bufferView]; - const auto& buffer = model.buffers[buffer_view.buffer]; - const auto data_ptr = - buffer.data.data() + buffer_view.byteOffset + attrib_accessor.byteOffset; - const auto byte_stride = attrib_accessor.ByteStride(buffer_view); - const auto count = attrib_accessor.count; - - ASSERT_MSG(attrib_accessor.type == TINYGLTF_TYPE_VEC3, "NORMAL wasn't vec3"); - ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT, - "NORMAL wasn't float"); - normals = extract_vec3f(data_ptr, count, byte_stride); - for (auto& nrm : normals) { - math::Vector4f nrm4(nrm.x(), nrm.y(), nrm.z(), 0.f); - nrm = (w_T_local * nrm4).xyz(); - } - ASSERT(normals.size() == result.size()); - } else { - lg::error("No NORMAL attribute for mesh: {}", debug_name); - } - } - - for (auto& v : result) { - v.color_index = 0; - if (!got_texture) { - v.s = 0; - v.t = 0; - } - - v.q_unused = 0; - v.pad[0] = 0; - v.pad[1] = 0; - v.pad[2] = 0; - } - // TODO: other properties - return {result, vtx_colors, normals}; -} - -DrawMode make_default_draw_mode() { - DrawMode mode; - mode.set_depth_write_enable(true); - mode.set_depth_test(GsTest::ZTest::GEQUAL); - mode.set_alpha_blend(DrawMode::AlphaBlend::DISABLED); - mode.set_aref(0); - mode.set_alpha_fail(GsTest::AlphaFail::KEEP); - mode.set_clamp_s_enable(false); - mode.set_clamp_t_enable(false); - mode.disable_filt(); // for checkerboard... - mode.enable_tcc(); // ? - mode.disable_at(); - mode.enable_zt(); - mode.disable_ab(); - mode.disable_decal(); - mode.enable_fog(); - return mode; -} - -int texture_pool_debug_checker(TexturePool* pool) { - const auto& existing = pool->textures_by_name.find("DEBUG_CHECKERBOARD"); - if (existing == pool->textures_by_name.end()) { - size_t idx = pool->textures_by_idx.size(); - pool->textures_by_name["DEBUG_CHECKERBOARD"] = idx; - auto& tex = pool->textures_by_idx.emplace_back(); - tex.w = 16; - tex.h = 16; - tex.debug_name = "DEBUG_CHECKERBOARD"; - tex.debug_tpage_name = "DEBUG"; - tex.load_to_pool = false; - tex.combo_id = 0; // doesn't matter, not a pool tex - tex.data.resize(16 * 16); - u32 c0 = 0xa0303030; - u32 c1 = 0xa0e0e0e0; - for (int i = 0; i < 16; i++) { - for (int j = 0; j < 16; j++) { - tex.data[i * 16 + j] = (((i / 4) & 1) ^ ((j / 4) & 1)) ? c1 : c0; - } - } - return idx; - } else { - return existing->second; - } -} - -int texture_pool_add_texture(TexturePool* pool, const tinygltf::Image& tex) { - const auto& existing = pool->textures_by_name.find(tex.name); - if (existing != pool->textures_by_name.end()) { - lg::info("Reusing image: {}", tex.name); - return existing->second; - } else { - lg::info("adding new texture: {}, size {} kB", tex.name, tex.width * tex.height * 4 / 1024); - } - - ASSERT(tex.bits == 8); - ASSERT(tex.component == 4); - ASSERT(tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE); - - size_t idx = pool->textures_by_idx.size(); - pool->textures_by_name[tex.name] = idx; - auto& tt = pool->textures_by_idx.emplace_back(); - tt.w = tex.width; - tt.h = tex.height; - tt.debug_name = tex.name; - tt.debug_tpage_name = "custom-level"; - tt.load_to_pool = false; - tt.combo_id = 0; // doesn't matter, not a pool tex - tt.data.resize(tt.w * tt.h); - ASSERT(tex.image.size() >= tt.data.size()); - memcpy(tt.data.data(), tex.image.data(), tt.data.size() * 4); - return idx; -} -} // namespace - -math::Matrix4f affine_translation(const math::Vector3f& translation) { - math::Matrix4f result = math::Matrix4f::identity(); - result(0, 3) = translation[0]; - result(1, 3) = translation[1]; - result(2, 3) = translation[2]; - result(3, 3) = 1; - return result; -} - -math::Matrix4f affine_scale(const math::Vector3f& scale) { - math::Matrix4f result = math::Matrix4f::zero(); - result(0, 0) = scale[0]; - result(1, 1) = scale[1]; - result(2, 2) = scale[2]; - result(3, 3) = 1; - return result; -} - -math::Matrix4f affine_rot_qxyzw(const math::Vector4f& quat) { - math::Matrix4f result = math::Matrix4f::zero(); - result(3, 3) = 1; - result(0, 0) = 1.0 - 2.0 * (quat.y() * quat.y() + quat.z() * quat.z()); - result(0, 1) = 2.0 * (quat.x() * quat.y() - quat.z() * quat.w()); - result(0, 2) = 2.0 * (quat.x() * quat.z() + quat.y() * quat.w()); - result(1, 0) = 2.0 * (quat.x() * quat.y() + quat.z() * quat.w()); - result(1, 1) = 1.0 - 2.0 * (quat.x() * quat.x() + quat.z() * quat.z()); - result(1, 2) = 2.0 * (quat.y() * quat.z() - quat.x() * quat.w()); - result(2, 0) = 2.0 * (quat.x() * quat.z() - quat.y() * quat.w()); - result(2, 1) = 2.0 * (quat.y() * quat.z() + quat.x() * quat.w()); - result(2, 2) = 1.0 - 2.0 * (quat.x() * quat.x() + quat.y() * quat.y()); - return result; -} - -math::Vector3f vector3f_from_gltf(const std::vector& in) { - ASSERT(in.size() == 3); - return math::Vector3f{in[0], in[1], in[2]}; -} - -math::Vector4f vector4f_from_gltf(const std::vector& in) { - ASSERT(in.size() == 4); - return math::Vector4f{in[0], in[1], in[2], in[3]}; -} - -math::Matrix4f matrix_from_node(const tinygltf::Node& node) { - if (!node.matrix.empty()) { - math::Matrix4f result; - for (int i = 0; i < 16; i++) { - result.data()[i] = node.matrix[i]; - } - return result; - } else { - // from trs - math::Matrix4f t, r, s; - if (!node.translation.empty()) { - t = affine_translation(vector3f_from_gltf(node.translation)); - } else { - t = math::Matrix4f::identity(); - } - - if (!node.rotation.empty()) { - r = affine_rot_qxyzw(vector4f_from_gltf(node.rotation)); - } else { - r = math::Matrix4f::identity(); - } - - if (!node.scale.empty()) { - s = affine_scale(vector3f_from_gltf(node.scale)); - } else { - s = math::Matrix4f::identity(); - } - - return t * r * s; - } -} - -struct NodeWithTransform { - int node_idx; - math::Matrix4f w_T_node; -}; - -/*! - * Recursively walk the tree of nodes, flatten, and compute w_T_node for each. - */ -void node_find_helper(const tinygltf::Model& model, - const math::Matrix4f& w_T_parent, - int node_idx, - std::vector* out) { - const auto& node = model.nodes.at(node_idx); - math::Matrix4f w_T_node = w_T_parent * matrix_from_node(node); - out->push_back({node_idx, w_T_node}); - for (auto& child : node.children) { - node_find_helper(model, w_T_node, child, out); - } -} - -std::vector flatten_nodes_from_all_scenes(const tinygltf::Model& model) { - std::vector out; - for (auto& scene : model.scenes) { - for (auto& nidx : scene.nodes) { - math::Matrix4f identity = math::Matrix4f::identity(); - node_find_helper(model, identity, nidx, &out); - } - } - return out; -} - -void dedup_vertices(const std::vector& vertices_in, - std::vector& vertices_out, - std::vector& old_to_new_out) { - ASSERT(vertices_out.empty()); - ASSERT(old_to_new_out.empty()); - old_to_new_out.resize(vertices_in.size(), -1); - - std::unordered_map vtx_to_new; - - for (size_t in_idx = 0; in_idx < vertices_in.size(); in_idx++) { - auto& vtx = vertices_in[in_idx]; - const auto& lookup = vtx_to_new.find(vtx); - if (lookup == vtx_to_new.end()) { - // first time seeing this one - size_t new_idx = vertices_out.size(); - vertices_out.push_back(vtx); - old_to_new_out[in_idx] = new_idx; - vtx_to_new[vtx] = new_idx; - } else { - old_to_new_out[in_idx] = lookup->second; - } - } -} - void dedup_vertices(TfragOutput& data) { Timer timer; size_t original_size = data.vertices.size(); std::vector new_verts; std::vector old_to_new; - dedup_vertices(data.vertices, new_verts, old_to_new); + gltf_util::dedup_vertices(data.vertices, new_verts, old_to_new); data.vertices = std::move(new_verts); for (auto& draw : data.strip_draws) { @@ -475,41 +38,6 @@ void dedup_vertices(TfragOutput& data) { data.vertices.size(), 100.f * data.vertices.size() / original_size); } -DrawMode draw_mode_from_sampler(const tinygltf::Sampler& sampler) { - DrawMode mode = make_default_draw_mode(); - if (sampler.magFilter == TINYGLTF_TEXTURE_FILTER_NEAREST) { - ASSERT(sampler.minFilter == TINYGLTF_TEXTURE_FILTER_NEAREST); - mode.set_filt_enable(false); - } else { - ASSERT(sampler.minFilter != TINYGLTF_TEXTURE_FILTER_NEAREST); - mode.set_filt_enable(true); - } - - switch (sampler.wrapS) { - case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE: - mode.set_clamp_s_enable(true); - break; - case TINYGLTF_TEXTURE_WRAP_REPEAT: - mode.set_clamp_s_enable(false); - break; - default: - ASSERT(false); - } - - switch (sampler.wrapT) { - case TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE: - mode.set_clamp_t_enable(true); - break; - case TINYGLTF_TEXTURE_WRAP_REPEAT: - mode.set_clamp_t_enable(false); - break; - default: - ASSERT(false); - } - - return mode; -} - void extract(const Input& in, TfragOutput& out, const tinygltf::Model& model, diff --git a/goalc/build_level/gltf_mesh_extract.h b/goalc/build_level/gltf_mesh_extract.h index 1c5c62a894..a2a77062c5 100644 --- a/goalc/build_level/gltf_mesh_extract.h +++ b/goalc/build_level/gltf_mesh_extract.h @@ -4,14 +4,17 @@ #include "common/custom_data/Tfrag3Data.h" -#include "goalc/build_level/TexturePool.h" #include "goalc/build_level/collide_common.h" +namespace gltf_util { +struct TexturePool; +} + namespace gltf_mesh_extract { struct Input { std::string filename; - TexturePool* tex_pool = nullptr; + gltf_util::TexturePool* tex_pool = nullptr; bool get_colors = true; bool auto_wall_enable = true; float auto_wall_angle = 30.f;