This commit is contained in:
water
2022-09-01 21:52:09 -04:00
parent 5a21c823f5
commit 72ce3b77b3
14 changed files with 805 additions and 497 deletions
+3 -2
View File
@@ -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)
+449
View File
@@ -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<Vec3f>
*/
std::vector<math::Vector3f> extract_vec3f(const u8* data, u32 count, u32 stride) {
std::vector<math::Vector3f> 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<math::Vector2f> extract_vec2f(const u8* data, u32 count, u32 stride) {
std::vector<math::Vector2f> 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<math::Vector<u8, 4>> extract_color_from_vec4_u16(const u8* data,
u32 count,
u32 stride) {
std::vector<math::Vector<u8, 4>> result;
result.reserve(count);
for (u32 i = 0; i < count; i++) {
math::Vector<u16, 4> temp;
memcpy(&temp, data, sizeof(math::Vector<u16, 4>));
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<u32> 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<s8>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
return index_list_to_u32<u8>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_SHORT:
return index_list_to_u32<s16>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
return index_list_to_u32<u16>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_INT:
return index_list_to_u32<s32>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
return index_list_to_u32<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<std::string, int>& attributes,
const math::Matrix4f& w_T_local,
bool get_colors,
bool get_normals,
const std::string& debug_name) {
std::vector<tfrag3::PreloadedVertex> result;
std::vector<math::Vector<u8, 4>> 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<math::Vector3f> 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<double>& in) {
ASSERT(in.size() == 3);
return math::Vector3f{in[0], in[1], in[2]};
}
math::Vector4f vector4f_from_gltf(const std::vector<double>& 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<NodeWithTransform>* 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<NodeWithTransform> flatten_nodes_from_all_scenes(const tinygltf::Model& model) {
std::vector<NodeWithTransform> 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<tfrag3::PreloadedVertex>& vertices_in,
std::vector<tfrag3::PreloadedVertex>& vertices_out,
std::vector<u32>& 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<tfrag3::PreloadedVertex, u32, tfrag3::PreloadedVertex::hash> 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
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#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<u32>
*/
template <typename T>
std::vector<u32> index_list_to_u32(const u8* data, u32 num_verts, u32 offset, u32 stride) {
std::vector<u32> 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<math::Vector3f> extract_vec3f(const u8* data, u32 count, u32 stride);
std::vector<math::Vector2f> extract_vec2f(const u8* data, u32 count, u32 stride);
std::vector<math::Vector<u8, 4>> extract_color_from_vec4_u16(const u8* data, u32 count, u32 stride);
std::vector<u32> gltf_index_buffer(const tinygltf::Model& model, int indices_idx, u32 index_offset);
struct ExtractedVertices {
std::vector<tfrag3::PreloadedVertex> vtx;
std::vector<math::Vector<u8, 4>> vtx_colors;
std::vector<math::Vector3f> normals;
};
ExtractedVertices gltf_vertices(const tinygltf::Model& model,
const std::map<std::string, int>& 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<std::string, int> textures_by_name;
std::vector<tfrag3::Texture> 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<tfrag3::PreloadedVertex>& vertices_in,
std::vector<tfrag3::PreloadedVertex>& vertices_out,
std::vector<u32>& old_to_new_out);
std::vector<NodeWithTransform> flatten_nodes_from_all_scenes(const tinygltf::Model& model);
DrawMode draw_mode_from_sampler(const tinygltf::Sampler& sampler);
} // namespace gltf_util
+1
View File
@@ -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
@@ -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<tfrag3::MercVertex> 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
@@ -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<u32> new_indices;
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
tfrag3::MercModel new_model;
};
void extract(MercExtractData& out,
const tinygltf::Model& model,
const std::vector<NodeWithTransform>& all_nodes,
u32 index_offset,
u32 vertex_offset,
u32 tex_offset) {
ASSERT(out.new_vertices.empty());
std::map<int, tfrag3::MercDraw> 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<u32> 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<tfrag3::MercVertex>& 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<tfrag3::MercVertex>& old_verts) {
/*
* std::vector<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<decompiler::TextureDB::TextureData> 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<tfrag3::MercVertex>& 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;
}
@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <vector>
#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<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<tfrag3::Texture> 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<tfrag3::MercVertex>& old_verts);
@@ -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));
}
}
@@ -57,6 +57,7 @@ class Merc2 : public BucketRenderer {
u32 alloc_bones(int count);
std::optional<MercRef> 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 {
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include "common/custom_data/Tfrag3Data.h"
struct TexturePool {
std::unordered_map<std::string, int> textures_by_name;
std::vector<tfrag3::Texture> textures_by_idx;
};
-1
View File
@@ -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;
+4 -3
View File
@@ -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;
+3 -475
View File
@@ -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<u32>
*/
template <typename T>
std::vector<u32> index_list_to_u32(const u8* data, u32 num_verts, u32 offset, u32 stride) {
std::vector<u32> 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<Vec3f>
*/
std::vector<math::Vector3f> extract_vec3f(const u8* data, u32 count, u32 stride) {
std::vector<math::Vector3f> 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<math::Vector2f> extract_vec2f(const u8* data, u32 count, u32 stride) {
std::vector<math::Vector2f> 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<math::Vector<u8, 4>> extract_color_from_vec4_u16(const u8* data,
u32 count,
u32 stride) {
std::vector<math::Vector<u8, 4>> result;
result.reserve(count);
for (u32 i = 0; i < count; i++) {
math::Vector<u16, 4> temp;
memcpy(&temp, data, sizeof(math::Vector<u16, 4>));
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<u32> 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<s8>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
return index_list_to_u32<u8>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_SHORT:
return index_list_to_u32<s16>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
return index_list_to_u32<u16>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_INT:
return index_list_to_u32<s32>(data_ptr, count, index_offset, stride);
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
return index_list_to_u32<u32>(data_ptr, count, index_offset, stride);
default:
ASSERT_MSG(false, "unsupported component type");
}
}
struct ExtractedVertices {
std::vector<tfrag3::PreloadedVertex> vtx;
std::vector<math::Vector<u8, 4>> vtx_colors;
std::vector<math::Vector3f> normals;
};
/*!
* Extract positions, colors, and normals from a mesh.
*/
ExtractedVertices gltf_vertices(const tinygltf::Model& model,
const std::map<std::string, int>& attributes,
const math::Matrix4f& w_T_local,
bool get_colors,
bool get_normals,
const std::string& debug_name) {
std::vector<tfrag3::PreloadedVertex> result;
std::vector<math::Vector<u8, 4>> 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<math::Vector3f> 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<double>& in) {
ASSERT(in.size() == 3);
return math::Vector3f{in[0], in[1], in[2]};
}
math::Vector4f vector4f_from_gltf(const std::vector<double>& 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<NodeWithTransform>* 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<NodeWithTransform> flatten_nodes_from_all_scenes(const tinygltf::Model& model) {
std::vector<NodeWithTransform> 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<tfrag3::PreloadedVertex>& vertices_in,
std::vector<tfrag3::PreloadedVertex>& vertices_out,
std::vector<u32>& 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<tfrag3::PreloadedVertex, u32, tfrag3::PreloadedVertex::hash> 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<tfrag3::PreloadedVertex> new_verts;
std::vector<u32> 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,
+5 -2
View File
@@ -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;