diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index c42003e3f4..e50365ad30 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -492,6 +492,7 @@ void MercDraw::serialize(Serializer& ser) { ser.from_ptr(&first_index); ser.from_ptr(&index_count); ser.from_ptr(&num_triangles); + ser.from_ptr(&no_strip); } void Blerc::serialize(Serializer& ser) { diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index bd951e7e88..35d27f01a1 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -18,7 +18,7 @@ namespace tfrag3 { // - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage // - if adding a new category to the memory usage, update extract_level to print it. -constexpr int TFRAG3_VERSION = 40; +constexpr int TFRAG3_VERSION = 41; enum MemoryUsageCategory { TEXTURE, @@ -530,6 +530,8 @@ struct MercDraw { u32 first_index; u32 index_count; u32 num_triangles; + // no strip hack for custom models + bool no_strip = false; void serialize(Serializer& ser); }; diff --git a/custom_assets/jak1/models/test-actor.glb b/custom_assets/jak1/models/test-actor.glb index 49bac261e1..7b69a61534 100644 Binary files a/custom_assets/jak1/models/test-actor.glb and b/custom_assets/jak1/models/test-actor.glb differ diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 15c12d15dc..7487f752d8 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -1093,6 +1093,7 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw, draw->first_bone = first_bone; draw->light_idx = lights; draw->num_triangles = mdraw.num_triangles; + draw->no_strip = mdraw.no_strip; if (ignore_alpha) { draw->flags |= IGNORE_ALPHA; } @@ -1283,8 +1284,8 @@ void Merc2::do_draws(const Draw* draw_array, 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)); } if (!normal_vtx_buffer_bound) { diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index b3ad2e8c2f..166d7d05af 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -190,6 +190,8 @@ class Merc2 { u8 flags; ModBuffers mod_vtx_buffer; u8 fade[4]; + // no strip hack for custom models + u8 no_strip; }; struct LevelDrawBucket { diff --git a/goal_src/jak1/game.gp b/goal_src/jak1/game.gp index 03785582cd..1bb71aa7de 100644 --- a/goal_src/jak1/game.gp +++ b/goal_src/jak1/game.gp @@ -224,9 +224,9 @@ :tool 'build-level :out '(,(string-append "$OUT/obj/" name ".go"))))) -(defmacro build-actor (name) +(defmacro build-actor (name &key (gen-mesh #f)) (let* ((path (string-append "custom_assets/jak1/models/" name ".glb"))) - `(defstep :in ,path + `(defstep :in '(,path ,(symbol->string gen-mesh)) :tool 'build-actor :out '(,(string-append "$OUT/obj/" name "-ag.go"))))) @@ -1660,7 +1660,8 @@ ;; generate the art group for a custom actor. ;; requires a .glb model file in custom_assets/jak1/models -(build-actor "test-actor") +;; to also generate a collide-mesh, add :gen-mesh #t +(build-actor "test-actor" :gen-mesh #t) ;;;;;;;;;;;;;;;;;;;;; ;; Game Engine Code diff --git a/goal_src/jak1/levels/test-zone/test-zone-obs.gc b/goal_src/jak1/levels/test-zone/test-zone-obs.gc index 6715ff573d..b2801c6cb8 100644 --- a/goal_src/jak1/levels/test-zone/test-zone-obs.gc +++ b/goal_src/jak1/levels/test-zone/test-zone-obs.gc @@ -19,7 +19,7 @@ (defskelgroup *test-actor-sg* test-actor test-actor-lod0-jg test-actor-idle-ja ((test-actor-lod0-mg (meters 9999999))) - :bounds (static-spherem 0 0 0 4.5) + :bounds (static-spherem 0 0 0 5) :texture-level 2 ) @@ -30,20 +30,30 @@ (set! (-> cshape no-reaction) (the (function collide-shape-moving collide-shape-intersect vector vector none) nothing) ) - ;; (let ((mesh (new 'process 'collide-shape-prim-mesh cshape (the uint 0) (the uint 0)))) - ;; (set! (-> mesh prim-core collide-as) (collide-kind enemy)) - ;; (set! (-> mesh collide-with) (collide-kind target)) - ;; (set! (-> mesh transform-index) 6) - ;; (set-vector! (-> mesh local-sphere) 0.0 0.0 0.0 (meters 4.5)) - ;; (set-root-prim! cshape mesh) - ;; ) - (let ((sphere (new 'process 'collide-shape-prim-sphere cshape (the uint 0)))) - (set! (-> sphere prim-core collide-as) (collide-kind enemy)) - (set! (-> sphere collide-with) (collide-kind target)) - (set! (-> sphere prim-core action) (collide-action solid)) - (set! (-> sphere prim-core offense) (collide-offense normal-attack)) - (set-vector! (-> sphere local-sphere) 0.0 0.0 0.0 (meters 2)) - (set-root-prim! cshape sphere) + (let ((cgroup (new 'process 'collide-shape-prim-group cshape (the uint 1) 0))) + (set! (-> cgroup prim-core collide-as) (collide-kind ground-object)) + (set! (-> cgroup collide-with) (collide-kind target)) + (set! (-> cgroup prim-core action) (collide-action solid rider-plat-sticky)) + (set! (-> cgroup transform-index) 0) + (set-vector! (-> cgroup local-sphere) 0.0 0.0 0.0 (meters 5)) + (set-root-prim! cshape cgroup) + (let ((mesh (new 'process 'collide-shape-prim-mesh cshape (the uint 0) (the uint 0)))) + (set! (-> mesh prim-core collide-as) (collide-kind ground-object)) + (set! (-> mesh collide-with) (collide-kind target)) + (set! (-> mesh prim-core action) (collide-action solid rider-plat-sticky)) + (set! (-> mesh prim-core offense) (collide-offense indestructible)) + (set! (-> mesh transform-index) 0) + (set-vector! (-> mesh local-sphere) 0.0 (meters 0) 0.0 (meters 5)) + (append-prim cgroup mesh) + ) + ; (let ((sphere (new 'process 'collide-shape-prim-sphere cshape (the uint 0)))) + ; (set! (-> sphere prim-core collide-as) (collide-kind enemy)) + ; (set! (-> sphere collide-with) (collide-kind target)) + ; (set! (-> sphere prim-core action) (collide-action solid)) + ; (set! (-> sphere prim-core offense) (collide-offense normal-attack)) + ; (set-vector! (-> sphere local-sphere) 0.0 0.0 0.0 (meters 2)) + ; (set-root-prim! cshape sphere) + ; ) ) (set! (-> cshape nav-radius) (* 0.75 (-> cshape root-prim local-sphere w))) (backup-collide-with-as cshape) @@ -91,9 +101,10 @@ :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) (case message (('attack 'touch) - (if (= (-> proc type) target) - (send-event proc 'attack #f (static-attack-info ((shove-up (meters 2.5)) (shove-back (meters 7.5))))) - ) + ; (if (= (-> proc type) target) + ; (send-event proc 'attack #f (static-attack-info ((shove-up (meters 2.5)) (shove-back (meters 7.5))))) + ; ) + #t ) ) ) @@ -110,6 +121,7 @@ (update-transforms! (-> self root)) ) ) + ; (debug-draw-tris (-> (res-lump-struct (-> self draw art-group data 0 extra) 'collide-mesh-group (array collide-mesh)) 0) self 2) ; (dotimes (i (-> self node-list length)) ; (let* ((joint (-> self node-list data i)) (jpos (vector<-cspace! (new-stack-vector0) joint))) ; (add-debug-sphere #t (bucket-id debug) jpos (meters 0.1) (static-rgba 0 #xff 0 #x40)) diff --git a/goalc/build_actor/common/MercExtract.cpp b/goalc/build_actor/common/MercExtract.cpp index bb49f7db58..f56c48a920 100644 --- a/goalc/build_actor/common/MercExtract.cpp +++ b/goalc/build_actor/common/MercExtract.cpp @@ -2,6 +2,8 @@ #include "common/log/log.h" +#include + void extract(const std::string& name, MercExtractData& out, const tinygltf::Model& model, @@ -17,10 +19,28 @@ void extract(const std::string& name, for (const auto& n : all_nodes) { const auto& node = model.nodes[n.node_idx]; + if (node.extras.Has("set_invisible") && node.extras.Get("set_invisible").Get()) { + continue; + } + // gltf_mesh_extract::PatResult mesh_default_collide = + // gltf_mesh_extract::custom_props_to_pat(node.extras, node.name); if (node.mesh >= 0) { const auto& mesh = model.meshes[node.mesh]; mesh_count++; for (const auto& prim : mesh.primitives) { + // get material + // const auto& mat_idx = prim.material; + // gltf_mesh_extract::PatResult pat = mesh_default_collide; + // if (mat_idx != -1) { + // const auto& mat = model.materials[mat_idx]; + // auto mat_pat = gltf_mesh_extract::custom_props_to_pat(mat.extras, mat.name); + // if (mat_pat.set) { + // pat = mat_pat; + // } + // } + // if (pat.set && pat.ignore) { + // continue; // skip, no collide here + // } prim_count++; // extract index buffer std::vector prim_indices = gltf_util::gltf_index_buffer( @@ -40,6 +60,7 @@ void extract(const std::string& name, draw.mode = gltf_util::make_default_draw_mode(); // todo rm draw.tree_tex_id = 0; // todo rm draw.num_triangles += prim_indices.size() / 3; + draw.no_strip = true; // if (draw.vis_groups.empty()) { // auto& grp = draw.vis_groups.emplace_back(); // grp.num_inds += prim_indices.size(); @@ -152,4 +173,103 @@ MercSwapData load_merc_model(u32 current_idx_count, merc_convert(result, extract_data); return result; -} \ No newline at end of file +} + +std::vector gen_collide_mesh_from_model( + const tinygltf::Model& model, + const std::vector& all_nodes, + int joint_idx) { + // data for a single primitive + struct PrimWork { + std::string mesh_name; + std::vector verts; + std::vector indices; + gltf_mesh_extract::PatResult pat; + }; + std::vector> mesh_data; + int mesh_count = 0; + // int prim_count = 0; + for (const auto& n : all_nodes) { + const auto& node = model.nodes[n.node_idx]; + gltf_mesh_extract::PatResult mesh_default_collide = + gltf_mesh_extract::custom_props_to_pat(node.extras, node.name); + if (node.mesh >= 0) { + const auto& mesh = model.meshes[node.mesh]; + mesh_count++; + std::vector prims; + for (const auto& prim : mesh.primitives) { + // get material + const auto& mat_idx = prim.material; + gltf_mesh_extract::PatResult pat = mesh_default_collide; + if (mat_idx != -1) { + const auto& mat = model.materials[mat_idx]; + auto mat_pat = gltf_mesh_extract::custom_props_to_pat(mat.extras, mat.name); + if (mat_pat.set) { + pat = mat_pat; + } + } + if (pat.set && pat.ignore) { + continue; // skip, no collide here + } + auto& prim_data = prims.emplace_back(); + prim_data.mesh_name = mesh.name; + prim_data.pat = pat; + // prim_count++; + // extract index buffer + std::vector prim_indices = gltf_util::gltf_index_buffer(model, prim.indices, 0); + ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, + fmt::format("Mesh {}: Unsupported triangle mode {}", mesh.name, prim.mode)); + // extract vertices + auto verts = + gltf_util::gltf_vertices(model, prim.attributes, n.w_T_node, true, true, mesh.name); + ASSERT_MSG(verts.vtx.size() <= 255, + fmt::format("primitive of mesh {} has too many vertices (max 255, actual {})", + mesh.name, verts.vtx.size())); + prim_data.verts.reserve(verts.vtx.size()); + for (auto& vert : verts.vtx) { + prim_data.verts.emplace_back(vert.x, vert.y, vert.z, 1.0); + } + prim_data.indices = prim_indices; + mesh_data.push_back(prims); + } + } + } + + std::vector cmeshes; + cmeshes.reserve(mesh_count); + // we extracted all of the prim data for each mesh, now combine + for (size_t p = 0; p < mesh_data.size(); p++) { + auto& prims = mesh_data.at(p); + std::vector verts; + std::vector tris; + int vert_count = 0; + for (auto& prim : prims) { + if (prim.pat.ignore) { + continue; + } + vert_count += verts.size(); + verts.insert(verts.end(), prim.verts.begin(), prim.verts.end()); + for (size_t i = 0; i < prim.indices.size(); i += 3) { + auto& tri = tris.emplace_back(); + tri.vert_idx[0] = prim.indices.at(i); + tri.vert_idx[1] = prim.indices.at(i + 1); + tri.vert_idx[2] = prim.indices.at(i + 2); + tri.pat = prim.pat.pat; + } + } + ASSERT_MSG(vert_count <= 255, fmt::format("Mesh {} has too many vertices (max 255, actual {})", + prims.at(p).mesh_name, vert_count)); + auto& cmesh = cmeshes.emplace_back(); + // TODO joint idx as a custom property in blender? + cmesh.joint_id = joint_idx; + cmesh.vertices.reserve(255); + cmesh.vertices.insert(cmesh.vertices.begin(), verts.begin(), verts.end()); + cmesh.num_verts = cmesh.vertices.size(); + cmesh.num_tris = tris.size(); + cmesh.tris.reserve(cmesh.num_tris); + for (size_t j = 0; j < cmesh.num_tris; j++) { + cmesh.tris.push_back(tris.at(j)); + } + } + return cmeshes; +} diff --git a/goalc/build_actor/common/MercExtract.h b/goalc/build_actor/common/MercExtract.h index 302abdfc52..6eea2e3ec7 100644 --- a/goalc/build_actor/common/MercExtract.h +++ b/goalc/build_actor/common/MercExtract.h @@ -2,6 +2,8 @@ #include "common/util/gltf_util.h" +#include + struct MercExtractData { gltf_util::TexturePool tex_pool; std::vector new_indices; @@ -32,4 +34,8 @@ MercSwapData load_merc_model(u32 current_idx_count, u32 current_vtx_count, u32 current_tex_count, const std::string& path, - const std::string& name); \ No newline at end of file + const std::string& name); +std::vector gen_collide_mesh_from_model( + const tinygltf::Model& model, + const std::vector& all_nodes, + int joint_idx); \ No newline at end of file diff --git a/goalc/build_actor/jak1/build_actor.cpp b/goalc/build_actor/jak1/build_actor.cpp index b58fdd0e00..f9b54ae60f 100644 --- a/goalc/build_actor/jak1/build_actor.cpp +++ b/goalc/build_actor/jak1/build_actor.cpp @@ -2,6 +2,8 @@ #include "common/log/log.h" +#include "goalc/build_actor/common/MercExtract.h" + #include "third-party/tiny_gltf/tiny_gltf.h" using namespace gltf_util; @@ -101,14 +103,77 @@ size_t JointAnimCompressedControl::generate(DataObjectGenerator& gen) const { return result; } -size_t ArtJointGeo::generate_res_lump(DataObjectGenerator& gen) const { +size_t CollideMesh::generate(DataObjectGenerator& gen) const { gen.align_to_basic(); - gen.add_type_tag("res-lump"); + gen.add_type_tag("collide-mesh"); size_t result = gen.current_offset_bytes(); + // gen.add_word(0xffffffff - joint_id); // 4 (joint-id) + gen.add_word(joint_id); // 4 (joint-id) + gen.add_word(num_tris); // 8 (num-tris) + gen.add_word(num_verts); // 12 (num-verts) + auto vertices_slot = gen.add_word(0); // 16 (vertex-data) + gen.add_word(0); // 20 (pad) + gen.add_word(0); // 24 (pad) + gen.add_word(0); // 28 (pad) + for (auto& tri : tris) { + u32 word = (tri.vert_idx[0] & 0xff) + ((tri.vert_idx[1] << 8) & 0xff00) + + ((tri.vert_idx[2] << 16) & 0xff0000); + gen.add_word(word); // 0 (tris | vertex-index | unused) + gen.add_word(tri.pat.val); // 4 (pat) + } + // vertex data start + gen.link_word_to_byte(vertices_slot, gen.current_offset_bytes()); + for (auto& vert : vertices) { + gen.add_word_float(vert.x()); + gen.add_word_float(vert.y()); + gen.add_word_float(vert.z()); + gen.add_word_float(vert.w()); + } + return result; +}; + +void ArtJointGeo::add_res() { + if (!cmeshes.empty()) { + lump.add_res( + std::make_unique("collide-mesh-group", "array", mesh_slot, DEFAULT_RES_TIME)); + } + // jgeo.lump.add_res( + // std::make_unique("texture-level", std::vector{2}, DEFAULT_RES_TIME)); + // jgeo.lump.add_res(std::make_unique( + // "trans-offset", std::vector{{0.0f, 2048.0f, 0.0f, 1.0f}}, + // DEFAULT_RES_TIME)); + // jgeo.lump.add_res( + // std::make_unique("joint-channel", std::vector{0}, DEFAULT_RES_TIME)); + // jgeo.lump.add_res(std::make_unique( + // "lod-dist", std::vector{5000.0f * METER_LENGTH, 6000.0f * METER_LENGTH}, + // DEFAULT_RES_TIME)); + lump.sort_res(); +} + +size_t ArtJointGeo::generate_mesh(DataObjectGenerator& gen) const { + std::vector data_slots; + std::vector content_slots; + gen.align_to_basic(); + gen.add_type_tag("array"); + size_t result = gen.current_offset_bytes(); + gen.add_word(cmeshes.size()); // 0 (length) + gen.add_word(cmeshes.size()); // 4 (allocated-length) + gen.add_type_tag("collide-mesh"); // 8 (content-type) + content_slots.reserve(cmeshes.size()); + for (auto& data : cmeshes) { + content_slots.push_back(gen.add_word(0)); // 12 (data) + } + gen.align(4); + for (size_t i = 0; i < content_slots.size(); i++) { + data_slots.push_back(cmeshes.at(i).generate(gen)); + } + for (size_t i = 0; i < content_slots.size(); i++) { + gen.link_word_to_byte(content_slots.at(i), data_slots.at(i)); + } return result; } -size_t ArtJointGeo::generate(DataObjectGenerator& gen) const { +size_t ArtJointGeo::generate(DataObjectGenerator& gen) { gen.align_to_basic(); gen.add_type_tag("art-joint-geo"); size_t result = gen.current_offset_bytes(); @@ -129,6 +194,8 @@ size_t ArtJointGeo::generate(DataObjectGenerator& gen) const { gen.link_word_to_byte(joint_slots.at(i), joint); g_joint_map[data.at(i).number] = joint; } + mesh_slot = generate_mesh(gen); + add_res(); auto res_header = lump.generate_header(gen, "res-lump"); gen.link_word_to_byte(res_slot, res_header); lump.generate_tag_list_and_data(gen, res_header); @@ -376,14 +443,28 @@ std::vector ArtGroup::save_object_file() const { return gen.generate_v4(); } -bool run_build_actor(const std::string& input_model, +int ArtGroup::get_joint_idx(const std::string& name) { + for (auto& elt : this->elts) { + if (elt && typeid(*elt) == typeid(ArtJointGeo)) { + auto jgeo = (ArtJointGeo*)elt; + for (auto& joint : jgeo->data) { + if (joint.name == name) { + return joint.number; + } + } + } + } + return -1; +} + +bool run_build_actor(const std::string& mdl_name, const std::string& ag_out, - const std::string& output_prefix) { + bool gen_collide_mesh) { std::string ag_name; - if (fs::exists(file_util::get_jak_project_dir() / input_model)) { - ag_name = fs::path(input_model).stem().string(); + if (fs::exists(file_util::get_jak_project_dir() / mdl_name)) { + ag_name = fs::path(mdl_name).stem().string(); } else { - ASSERT_MSG(false, "Model file not found: " + input_model); + ASSERT_MSG(false, "Model file not found: " + mdl_name); } ArtGroup ag(ag_name); @@ -411,21 +492,22 @@ bool run_build_actor(const std::string& input_model, main_pose(3, 3) = 1.0f; Joint main("main", 2, 1, main_pose); joints.emplace_back(main); - ArtJointGeo jgeo(ag.name, joints); + std::vector mesh; + if (gen_collide_mesh) { + tinygltf::TinyGLTF loader; + tinygltf::Model model; + std::string err, warn; + std::string path = (file_util::get_jak_project_dir() / mdl_name).string(); + 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); + mesh = gen_collide_mesh_from_model(model, all_nodes, 3); + } + ArtJointGeo jgeo(ag.name, mesh, joints); ArtJointAnim ja(ag.name, joints); - jgeo.lump.add_res( - std::make_unique("texture-level", std::vector{2}, DEFAULT_RES_TIME)); - // jgeo.lump.add_res(std::make_unique( - // "trans-offset", std::vector{{0.0f, 2048.0f, 0.0f, 1.0f}}, - // DEFAULT_RES_TIME)); - jgeo.lump.add_res( - std::make_unique("joint-channel", std::vector{0}, DEFAULT_RES_TIME)); - jgeo.lump.add_res(std::make_unique( - "lod-dist", std::vector{5000.0f * METER_LENGTH, 6000.0f * METER_LENGTH}, - DEFAULT_RES_TIME)); - jgeo.lump.sort_res(); - ag.elts.emplace_back(&jgeo); // dummy merc-ctrl ag.elts.emplace_back(nullptr); diff --git a/goalc/build_actor/jak1/build_actor.h b/goalc/build_actor/jak1/build_actor.h index 0634d08a09..9868cea3bc 100644 --- a/goalc/build_actor/jak1/build_actor.h +++ b/goalc/build_actor/jak1/build_actor.h @@ -139,23 +139,33 @@ struct CollideMesh { u32 num_tris; u32 num_verts; std::vector vertices; - CollideMeshTri tris; + std::vector tris; + + size_t generate(DataObjectGenerator& gen) const; + size_t calc_data_size() const { + // (size-of collide-mesh) + type ptr = 36 + return 36 + 16 * vertices.size() + sizeof(CollideMeshTri) * tris.size(); + } }; struct ArtJointGeo : ArtElement { std::vector data; - CollideMesh mesh; + std::vector cmeshes; ResLump lump; + size_t mesh_slot; - explicit ArtJointGeo(const std::string& name, const std::vector& joints) { + explicit ArtJointGeo(const std::string& name, + std::vector cmeshes, + std::vector& joints) { this->name = name + "-lod0"; length = joints.size(); for (auto& joint : joints) { data.push_back(joint); } + this->cmeshes = std::move(cmeshes); } - size_t generate(DataObjectGenerator& gen) const; - size_t generate_res_lump(DataObjectGenerator& gen) const; + void add_res(); + size_t generate(DataObjectGenerator& gen); size_t generate_mesh(DataObjectGenerator& gen) const; }; @@ -202,9 +212,10 @@ struct ArtGroup : Art { info.maya_file_name = "Unknown"; } std::vector save_object_file() const; + int get_joint_idx(const std::string& name); }; bool run_build_actor(const std::string& input_model, const std::string& output_file, - const std::string& output_prefix); + bool gen_collide_mesh); } // namespace jak1 diff --git a/goalc/build_actor/main.cpp b/goalc/build_actor/main.cpp index 9f83a0f4f4..b2525fb82c 100644 --- a/goalc/build_actor/main.cpp +++ b/goalc/build_actor/main.cpp @@ -13,7 +13,8 @@ int main(int argc, char** argv) { lg::initialize(); // game version - std::string game, input_model, output_file; + std::string game, mdl_name, output_file; + bool gen_collide_mesh; fs::path project_path_override; // path @@ -24,7 +25,7 @@ int main(int argc, char** argv) { lg::info("Build Actor Tool", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR); CLI::App app{"OpenGOAL Compiler / REPL"}; - app.add_option("input-model", input_model, + app.add_option("input-model", mdl_name, "Input model file (for example: custom_assets/jak1/models/test.glb)") ->required(); app.add_option("output-file", output_file, @@ -33,6 +34,7 @@ int main(int argc, char** argv) { app.add_option("-g,--game", game, "Game version (only jak1 for now)")->required(); app.add_option("--proj-path", project_path_override, "Specify the location of the 'data/' folder"); + app.add_flag("-m,--mesh", gen_collide_mesh, "Whether to generate a collide-mesh for this model"); app.validate_positionals(); CLI11_PARSE(app, argc, argv) @@ -53,7 +55,7 @@ int main(int argc, char** argv) { switch (game_version) { case GameVersion::Jak1: - jak1::run_build_actor(input_model, output_file, "jak1/"); + jak1::run_build_actor(mdl_name, output_file, gen_collide_mesh); break; default: ASSERT_NOT_REACHED_MSG("unsupported game version"); diff --git a/goalc/build_level/common/ResLump.cpp b/goalc/build_level/common/ResLump.cpp index e1d0114a39..36310570a4 100644 --- a/goalc/build_level/common/ResLump.cpp +++ b/goalc/build_level/common/ResLump.cpp @@ -240,6 +240,26 @@ int ResType::get_alignment() const { return 4; } +ResRef::ResRef(const std::string& name, const std::string& type, size_t ref, float key_frame) + : Res(name, key_frame), m_ref(ref), m_type(type) {} + +TagInfo ResRef::get_tag_info() const { + TagInfo result; + result.elt_type = m_type; + result.elt_count = 1; + result.inlined = false; + result.data_size = 4; + return result; +} + +void ResRef::write_data(DataObjectGenerator& gen) const { + gen.link_word_to_byte(gen.add_word(0), m_ref); +} + +int ResRef::get_alignment() const { + return 4; +} + void ResLump::add_res(std::unique_ptr res) { m_sorted = false; m_res.emplace_back(std::move(res)); @@ -345,12 +365,15 @@ void ResLump::generate_tag_list_and_data(DataObjectGenerator& gen, size_t header current_data_ptr += 4; gen.add_word(0); } - res->write_data(gen); - ASSERT(gen.current_offset_bytes() - current_data_ptr == rec.reported_size); + ASSERT_MSG(gen.current_offset_bytes() - current_data_ptr == rec.reported_size, + fmt::format("reported size of {} does not match actual size of {}", + rec.reported_size, gen.current_offset_bytes() - current_data_ptr)); current_data_ptr = gen.current_offset_bytes(); } - ASSERT(gen.current_offset_bytes() == data_end); + ASSERT_MSG(gen.current_offset_bytes() == data_end, + fmt::format("mismatch between current offset ({}) and data end ({})", + gen.current_offset_bytes(), data_end)); ASSERT(data_end == current_data_ptr); // update header diff --git a/goalc/build_level/common/ResLump.h b/goalc/build_level/common/ResLump.h index 755e448190..b9ffe7962a 100644 --- a/goalc/build_level/common/ResLump.h +++ b/goalc/build_level/common/ResLump.h @@ -138,6 +138,19 @@ class ResType : public Res { std::vector m_str; }; +class ResRef : public Res { + public: + ResRef(const std::string& name, const std::string& type, size_t ref, float key_frame); + + TagInfo get_tag_info() const override; + void write_data(DataObjectGenerator& gen) const override; + int get_alignment() const override; + + private: + size_t m_ref; + std::string m_type; +}; + /* (deftype res-lump (basic) ((length int32 :offset-assert 4) diff --git a/goalc/build_level/common/gltf_mesh_extract.cpp b/goalc/build_level/common/gltf_mesh_extract.cpp index 786e6ff9f4..05e5a40db5 100644 --- a/goalc/build_level/common/gltf_mesh_extract.cpp +++ b/goalc/build_level/common/gltf_mesh_extract.cpp @@ -13,8 +13,6 @@ #include "common/util/Timer.h" #include "common/util/gltf_util.h" -#include "third-party/tiny_gltf/tiny_gltf.h" - using namespace gltf_util; namespace gltf_mesh_extract { @@ -201,12 +199,6 @@ std::optional> subdivide_face_if_needed(jak1::Col } } -struct PatResult { - bool set = false; - bool ignore = false; - jak1::PatSurface pat; -}; - PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*debug_name*/) { PatResult result; if (!val.IsObject() || !val.Has("set_collision") || !val.Get("set_collision").Get()) { diff --git a/goalc/build_level/common/gltf_mesh_extract.h b/goalc/build_level/common/gltf_mesh_extract.h index 04867b3a38..d9f126ced6 100644 --- a/goalc/build_level/common/gltf_mesh_extract.h +++ b/goalc/build_level/common/gltf_mesh_extract.h @@ -6,6 +6,8 @@ #include "goalc/build_level/collide/common/collide_common.h" +#include "third-party/tiny_gltf/tiny_gltf.h" + namespace gltf_util { struct TexturePool; } @@ -36,6 +38,13 @@ struct Output { CollideOutput collide; }; +struct PatResult { + bool set = false; + bool ignore = false; + jak1::PatSurface pat; +}; + +PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*debug_name*/); void extract(const Input& in, Output& out); } // namespace gltf_mesh_extract \ No newline at end of file diff --git a/goalc/make/Tools.cpp b/goalc/make/Tools.cpp index 8bdb5142c3..ff6b245f4b 100644 --- a/goalc/make/Tools.cpp +++ b/goalc/make/Tools.cpp @@ -302,7 +302,7 @@ BuildActorTool::BuildActorTool() : Tool("build-actor") {} bool BuildActorTool::needs_run(const ToolInput& task, const PathMap& path_map) { (void)path_map; - if (task.input.size() != 1) { + if (task.input.size() > 2) { throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name())); } // std::vector deps{}; @@ -311,8 +311,10 @@ bool BuildActorTool::needs_run(const ToolInput& task, const PathMap& path_map) { } bool BuildActorTool::run(const ToolInput& task, const PathMap& path_map) { - if (task.input.size() != 1) { + (void)path_map; + if (task.input.size() > 2) { throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name())); } - return jak1::run_build_actor(task.input.at(0), task.output.at(0), path_map.output_prefix); + auto gen_mesh = task.input.at(1) == "#t"; + return jak1::run_build_actor(task.input.at(0), task.output.at(0), gen_mesh); } \ No newline at end of file