From 620168c45acf9ce1f23b52f997afb6ccaa67819a Mon Sep 17 00:00:00 2001 From: Hat Kid <6624576+Hat-Kid@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:27:46 +0100 Subject: [PATCH 1/8] custom models: ignore invalid envmap material (#3785) If a model has a material with specular properties, but is missing a roughness texture, warn and treat it as a normal draw instead of erroring out. --- common/util/gltf_util.cpp | 17 +++++------------ common/util/gltf_util.h | 2 +- decompiler/level_extractor/merc_replacement.cpp | 8 +++----- goalc/build_actor/common/MercExtract.cpp | 8 +++----- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/common/util/gltf_util.cpp b/common/util/gltf_util.cpp index f9e549d0c..1a8f4de95 100644 --- a/common/util/gltf_util.cpp +++ b/common/util/gltf_util.cpp @@ -684,19 +684,12 @@ bool material_has_envmap(const tinygltf::Material& mat) { return mat.extensions.contains("KHR_materials_specular"); } -bool envmap_is_valid(const tinygltf::Material& mat, bool die) { +bool envmap_is_valid(const tinygltf::Material& mat) { if (material_has_envmap(mat) && mat.pbrMetallicRoughness.metallicRoughnessTexture.index < 0) { - std::string error = fmt::format( - "Material \"{}\" has specular property set, but is missing a metallic roughness texture! " - "Check " - "that the Specular IOR level for the material is at the default of 0.5 if this is " - "unintended.", - mat.name); - if (die) { - lg::die(error); - } else { - lg::error(error); - } + lg::warn(fmt::format( + "Material \"{}\" has specular property set, but is missing a metallic roughness texture, " + "ignoring envmap!", + mat.name)); return false; } return true; diff --git a/common/util/gltf_util.h b/common/util/gltf_util.h index 01cfbce12..ae0ca8be5 100644 --- a/common/util/gltf_util.h +++ b/common/util/gltf_util.h @@ -126,7 +126,7 @@ struct EnvmapSettings { EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat); bool material_has_envmap(const tinygltf::Material& mat); -bool envmap_is_valid(const tinygltf::Material& mat, bool die); +bool envmap_is_valid(const tinygltf::Material& mat); /*! * Find the index of the skin for this model. Returns nullopt if there is no skin, the index of the diff --git a/decompiler/level_extractor/merc_replacement.cpp b/decompiler/level_extractor/merc_replacement.cpp index c35647e2e..00565a7b5 100644 --- a/decompiler/level_extractor/merc_replacement.cpp +++ b/decompiler/level_extractor/merc_replacement.cpp @@ -17,7 +17,6 @@ void extract(const std::string& name, std::map draw_by_material; int mesh_count = 0; int prim_count = 0; - bool has_envmaps = false; int joints = 3; auto skin_idx = find_single_skin(model, all_nodes); if (skin_idx) { @@ -80,6 +79,7 @@ void extract(const std::string& name, tfrag3::MercEffect e; tfrag3::MercEffect envmap_eff; + envmap_eff.has_envmap = false; out.new_model.name = name; out.new_model.max_bones = joints; out.new_model.max_draws = 0; @@ -161,11 +161,9 @@ void extract(const std::string& name, for (const auto& [mat_idx, d_] : draw_by_material) { const auto& mat = model.materials[mat_idx]; - if (!material_has_envmap(mat)) { + if (!material_has_envmap(mat) || !envmap_is_valid(mat)) { process_normal_draw(e, mat_idx, d_); } else { - envmap_is_valid(mat, true); - has_envmaps = true; envmap_eff.has_envmap = true; process_envmap_draw(envmap_eff, mat_idx, d_); } @@ -175,7 +173,7 @@ void extract(const std::string& name, if (!e.all_draws.empty()) { out.new_model.effects.push_back(e); } - if (has_envmaps) { + if (envmap_eff.has_envmap) { out.new_model.effects.push_back(envmap_eff); } diff --git a/goalc/build_actor/common/MercExtract.cpp b/goalc/build_actor/common/MercExtract.cpp index db20ea695..e83a26c07 100644 --- a/goalc/build_actor/common/MercExtract.cpp +++ b/goalc/build_actor/common/MercExtract.cpp @@ -17,7 +17,6 @@ void extract(const std::string& name, std::map draw_by_material; int mesh_count = 0; int prim_count = 0; - bool has_envmaps = false; int joints = 3; auto skin_idx = find_single_skin(model, all_nodes); if (skin_idx) { @@ -92,6 +91,7 @@ void extract(const std::string& name, tfrag3::MercEffect e; tfrag3::MercEffect envmap_eff; + envmap_eff.has_envmap = false; out.new_model.name = name; out.new_model.max_bones = joints; out.new_model.max_draws = 0; @@ -173,11 +173,9 @@ void extract(const std::string& name, for (const auto& [mat_idx, d_] : draw_by_material) { const auto& mat = model.materials[mat_idx]; - if (!gltf_util::material_has_envmap(mat)) { + if (!gltf_util::material_has_envmap(mat) || !gltf_util::envmap_is_valid(mat)) { process_normal_draw(e, mat_idx, d_); } else { - gltf_util::envmap_is_valid(mat, false); - has_envmaps = true; envmap_eff.has_envmap = true; process_envmap_draw(envmap_eff, mat_idx, d_); } @@ -187,7 +185,7 @@ void extract(const std::string& name, if (!e.all_draws.empty()) { out.new_model.effects.push_back(e); } - if (has_envmaps) { + if (envmap_eff.has_envmap) { out.new_model.effects.push_back(envmap_eff); } From 8feb46dc45c6ee18091c1939158673a2d223fd1a Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 30 Nov 2024 22:27:52 -0500 Subject: [PATCH 2/8] ci: hopefully fix MacOS CI in preparation for release (#3788) Folder names were in common with the normal intel x86 build, the release wasn't going to work. Hopefully with these changes it will but, still technically untested. Don't have time to do so tonight. --- .github/workflows/release-pipeline.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-pipeline.yaml b/.github/workflows/release-pipeline.yaml index 40940b712..851a543f1 100644 --- a/.github/workflows/release-pipeline.yaml +++ b/.github/workflows/release-pipeline.yaml @@ -86,7 +86,7 @@ jobs: uses: ./.github/workflows/macos-build.yaml with: cmakePreset: "Release-macos-x86_64-clang-static" - cachePrefix: "static" + cachePrefix: "static-arm" uploadArtifacts: true secrets: inherit @@ -150,13 +150,13 @@ jobs: - name: Prepare ARM macOS Build Assets run: | mkdir -p ./ci-artifacts/macos-arm - ./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos-arm ./ci-artifacts/opengoal-macos-arm-static ./ + ./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos-arm ./ci-artifacts/opengoal-macos-static-arm ./ pushd ci-artifacts/macos-arm TAG_VAL=${{ needs.cut_release.outputs.new_tag }} tar czf ../final/opengoal-macos-arm-${TAG_VAL}.tar.gz . popd - chmod +x ./ci-artifacts/opengoal-macos-arm-static/lsp/lsp - cp ./ci-artifacts/opengoal-macos-arm-static/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-arm-${TAG_VAL}.bin + chmod +x ./ci-artifacts/opengoal-macos-static-arm/lsp/lsp + cp ./ci-artifacts/opengoal-macos-static-arm/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-arm-${TAG_VAL}.bin - name: Upload Assets env: From 477cefb6a5bef1d63eae979f8afed9e40d709467 Mon Sep 17 00:00:00 2001 From: Hat Kid <6624576+Hat-Kid@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:01:50 +0100 Subject: [PATCH 3/8] model replacements: prevent duplicate processing (#3789) Store a small database of which models have already been swapped out in a level to prevent duplicate processing. Also a small fix for cases where using a model replacement that has no armature would cause merc nightmares due to only having a `max_bones` of 3. --- decompiler/level_extractor/extract_level.cpp | 4 +- decompiler/level_extractor/extract_level.h | 34 +++++++++++ decompiler/level_extractor/extract_merc.cpp | 59 +++++++++++-------- decompiler/level_extractor/extract_merc.h | 5 +- .../level_extractor/merc_replacement.cpp | 4 +- goalc/build_level/jak1/build_level.cpp | 3 +- goalc/build_level/jak2/build_level.cpp | 3 +- goalc/build_level/jak3/build_level.cpp | 3 +- 8 files changed, 86 insertions(+), 29 deletions(-) diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 4aa68323a..773d23f25 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -112,10 +112,12 @@ void extract_art_groups_from_level(const ObjectFileDB& db, std::map& art_group_data) { if (db.obj_files_by_dgo.count(dgo_name)) { const auto& files = db.obj_files_by_dgo.at(dgo_name); + MercSwapInfo swapped_info; for (const auto& file : files) { if (file.name.length() > 3 && !file.name.compare(file.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(file); - extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version()); + extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version(), + swapped_info); extract_joint_group(ag_file, db.dts, db.version(), art_group_data); } } diff --git a/decompiler/level_extractor/extract_level.h b/decompiler/level_extractor/extract_level.h index ac4712f94..202923a61 100644 --- a/decompiler/level_extractor/extract_level.h +++ b/decompiler/level_extractor/extract_level.h @@ -10,6 +10,40 @@ namespace decompiler { +// info about what models have been replaced/added per level +struct MercSwapInfo { + std::map> per_level_merc_swaps; + std::map> per_level_custom_mdls; + + bool already_swapped(const std::string& model, const std::string& level) { + auto mdls_it = per_level_merc_swaps.find(level); + if (mdls_it != per_level_merc_swaps.end()) { + auto& mdls = mdls_it->second; + auto mdl = std::find(mdls.begin(), mdls.end(), model); + return mdl != mdls.end(); + } + return false; + } + + bool already_added(const std::string& model, const std::string& level) { + auto mdls_it = per_level_custom_mdls.find(level); + if (mdls_it != per_level_custom_mdls.end()) { + auto& mdls = mdls_it->second; + auto mdl = std::find(mdls.begin(), mdls.end(), model); + return mdl != mdls.end(); + } + return false; + } + + void add_to_swapped_list(const std::string& model, const std::string& level) { + per_level_merc_swaps[level].push_back(model); + } + + void add_to_custom_list(const std::string& model, const std::string& level) { + per_level_custom_mdls[level].push_back(model); + } +}; + // extract everything void extract_all_levels(const ObjectFileDB& db, const TextureDB& tex_db, diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index f5f5e1fa5..e88b4fd0a 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -1669,7 +1669,8 @@ void extract_merc(const ObjectFileData& ag_data, const std::vector& map, tfrag3::Level& out, bool dump_level, - GameVersion version) { + GameVersion version, + MercSwapInfo& swapped_info) { if (dump_level) { file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/merc"})); } @@ -1795,31 +1796,43 @@ void extract_merc(const ObjectFileData& ag_data, } } - // do model replacements if present - auto merc_replacement_folder = file_util::get_jak_project_dir() / "custom_assets" / - game_version_names[version] / "merc_replacements"; - if (file_util::file_exists(merc_replacement_folder.string())) { - auto merc_replacements = - file_util::find_files_in_dir(merc_replacement_folder, std::regex(".*\\.glb")); - for (auto& path : merc_replacements) { - auto name = path.stem().string(); - auto it = std::find_if(out.merc_data.models.begin(), out.merc_data.models.end(), - [&](const auto& m) { return m.name == name; }); - if (it != out.merc_data.models.end()) { - auto& model = *it; - replace_model(out, model, path); + // do model replacement if present + for (auto& ctrl : ctrls) { + auto merc_replacements_path = file_util::get_jak_project_dir() / "custom_assets" / + game_version_names[version] / "merc_replacements"; + if (!swapped_info.already_swapped(ctrl.name, out.level_name)) { + if (file_util::file_exists(merc_replacements_path.string())) { + std::string file_name(ctrl.name + ".glb"); + auto mdl_path = merc_replacements_path / file_name; + if (file_util::file_exists(mdl_path.string())) { + auto it = std::find_if(out.merc_data.models.begin(), out.merc_data.models.end(), + [&](const auto& m) { return m.name == ctrl.name; }); + if (it != out.merc_data.models.end()) { + auto& model = *it; + replace_model(out, model, mdl_path); + swapped_info.add_to_swapped_list(ctrl.name, out.level_name); + } + } + } else { + lg::info("{} in level {} was already swapped, skipping", ctrl.name, out.level_name); } } - } - // add custom models if present - auto lvl_name = out.level_name == "" ? "common" : out.level_name; - auto models_folder = file_util::get_jak_project_dir() / "custom_assets" / - game_version_names[version] / "models" / lvl_name; - if (file_util::file_exists(models_folder.string())) { - auto custom_models = file_util::find_files_in_dir(models_folder, std::regex(".*\\.glb")); - for (auto& mdl : custom_models) { - add_custom_model_to_level(out, mdl.stem().string(), mdl); + // add custom models if present + auto lvl_name = out.level_name == "" ? "common" : out.level_name; + auto models_folder = file_util::get_jak_project_dir() / "custom_assets" / + game_version_names[version] / "models" / lvl_name; + if (file_util::file_exists(models_folder.string())) { + auto custom_models = file_util::find_files_in_dir(models_folder, std::regex(".*\\.glb")); + for (auto& mdl : custom_models) { + auto name = mdl.stem().string(); + if (!swapped_info.already_added(name, lvl_name)) { + add_custom_model_to_level(out, name, mdl); + swapped_info.add_to_custom_list(name, lvl_name); + } else { + lg::info("custom model {} was already added to level {}, skipping", name, lvl_name); + } + } } } } diff --git a/decompiler/level_extractor/extract_merc.h b/decompiler/level_extractor/extract_merc.h index 0332c88e5..3a64620a3 100644 --- a/decompiler/level_extractor/extract_merc.h +++ b/decompiler/level_extractor/extract_merc.h @@ -1,5 +1,7 @@ #pragma once +#include "extract_level.h" + #include "common/custom_data/Tfrag3Data.h" #include "decompiler/ObjectFile/ObjectFileDB.h" @@ -14,5 +16,6 @@ void extract_merc(const ObjectFileData& ag_data, const std::vector& map, tfrag3::Level& out, bool dump_level, - GameVersion version); + GameVersion version, + MercSwapInfo& swapped_info); } // namespace decompiler \ No newline at end of file diff --git a/decompiler/level_extractor/merc_replacement.cpp b/decompiler/level_extractor/merc_replacement.cpp index 00565a7b5..d79a1acbf 100644 --- a/decompiler/level_extractor/merc_replacement.cpp +++ b/decompiler/level_extractor/merc_replacement.cpp @@ -81,7 +81,9 @@ void extract(const std::string& name, tfrag3::MercEffect envmap_eff; envmap_eff.has_envmap = false; out.new_model.name = name; - out.new_model.max_bones = joints; + // if we have a skeleton, use that joint count, otherwise use a high default value since the model + // we replace can have more + out.new_model.max_bones = joints != 3 ? joints : 100; out.new_model.max_draws = 0; auto process_normal_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) { diff --git a/goalc/build_level/jak1/build_level.cpp b/goalc/build_level/jak1/build_level.cpp index 810df3e7c..8395940fb 100644 --- a/goalc/build_level/jak1/build_level.cpp +++ b/goalc/build_level/jak1/build_level.cpp @@ -234,8 +234,9 @@ bool run_build_level(const std::string& input_file, if (ag.name.length() > 3 && !ag.name.compare(ag.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(ag); lg::info("custom level: extracting art group {}", ag_file.name_in_dgo); + decompiler::MercSwapInfo info; decompiler::extract_merc(ag_file, tex_db, db.dts, tex_remap, pc_level, false, - db.version()); + db.version(), info); } } } diff --git a/goalc/build_level/jak2/build_level.cpp b/goalc/build_level/jak2/build_level.cpp index 7ab7db5bd..ec0f6f9ae 100644 --- a/goalc/build_level/jak2/build_level.cpp +++ b/goalc/build_level/jak2/build_level.cpp @@ -150,8 +150,9 @@ bool run_build_level(const std::string& input_file, if (ag.name.length() > 3 && !ag.name.compare(ag.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(ag); lg::print("custom level: extracting art group {}\n", ag_file.name_in_dgo); + decompiler::MercSwapInfo info; decompiler::extract_merc(ag_file, tex_db, db.dts, tex_remap, pc_level, false, - db.version()); + db.version(), info); } } } diff --git a/goalc/build_level/jak3/build_level.cpp b/goalc/build_level/jak3/build_level.cpp index 57185b736..fd997cfa6 100644 --- a/goalc/build_level/jak3/build_level.cpp +++ b/goalc/build_level/jak3/build_level.cpp @@ -148,8 +148,9 @@ bool run_build_level(const std::string& input_file, if (ag.name.length() > 3 && !ag.name.compare(ag.name.length() - 3, 3, "-ag")) { const auto& ag_file = db.lookup_record(ag); lg::print("custom level: extracting art group {}\n", ag_file.name_in_dgo); + decompiler::MercSwapInfo info; decompiler::extract_merc(ag_file, tex_db, db.dts, tex_remap, pc_level, false, - db.version()); + db.version(), info); } } } From e18dbb625629a9b22e6634754d52c19030b360eb Mon Sep 17 00:00:00 2001 From: Hat Kid <6624576+Hat-Kid@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:43:41 +0100 Subject: [PATCH 4/8] jak3: fix cloth disappearing after 600 orbs (#3790) After collecting 600 orbs, cloth systems that didn't have any `alt-tex-name`s defined would vanish because the `static-cloth-params` macro doesn't default them to `#f`. --- goal_src/jak3/engine/common-obs/cloth-art-h.gc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/goal_src/jak3/engine/common-obs/cloth-art-h.gc b/goal_src/jak3/engine/common-obs/cloth-art-h.gc index 0527fa11e..f0f982b11 100644 --- a/goal_src/jak3/engine/common-obs/cloth-art-h.gc +++ b/goal_src/jak3/engine/common-obs/cloth-art-h.gc @@ -135,6 +135,9 @@ (defmacro static-cloth-params (ag-name args) `(let ((parms (new 'static 'cloth-params))) + (false! (-> parms alt-tex-name)) + (false! (-> parms alt-tex-name2)) + (false! (-> parms alt-tex-name3)) ,@(apply (lambda (x) (if (and (eq? (car x) 'mesh) (not (integer? (cadr x)))) `(set! (-> parms ,(car x)) ,(art-elt-index ag-name (cadr x))) `(set! (-> parms ,(car x)) ,(cadr x)) From 8690104ba09a84aefbe4bce4544875261c0861a7 Mon Sep 17 00:00:00 2001 From: Hat Kid <6624576+Hat-Kid@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:25:20 +0100 Subject: [PATCH 5/8] jak1: add merc version of `draw-bones-hud` (#3793) Add a version of `draw-bones-hud` that uses merc to support drawing foreground HUD elements that use custom models. --- goal_src/jak1/engine/draw/drawable.gc | 18 ++++ goal_src/jak1/engine/gfx/foreground/bones.gc | 101 +++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/goal_src/jak1/engine/draw/drawable.gc b/goal_src/jak1/engine/draw/drawable.gc index ff2f1ac74..f55589fb0 100644 --- a/goal_src/jak1/engine/draw/drawable.gc +++ b/goal_src/jak1/engine/draw/drawable.gc @@ -650,6 +650,24 @@ 0 (none)) +;; og:preserve-this added +(defun dma-add-process-drawable-hud-merc ((pd process-drawable) (dc draw-control) (arg2 symbol) (buf dma-buffer)) + (logclear! (-> dc status) (draw-status was-drawn)) + (when (not (logtest? (-> dc status) (draw-status hidden no-anim no-skeleton-update))) + (let ((vu-lights (scratchpad-object vu-lights :offset 64)) + (hud-lights *hud-lights*)) + (vector-copy! (-> vu-lights direction 0) (-> hud-lights direction 0)) + (vector-copy! (-> vu-lights direction 1) (-> hud-lights direction 1)) + (vector-copy! (-> vu-lights direction 2) (-> hud-lights direction 2)) + (vector-copy! (-> vu-lights color 0) (-> hud-lights color 0)) + (vector-copy! (-> vu-lights color 1) (-> hud-lights color 1)) + (vector-copy! (-> vu-lights color 2) (-> hud-lights color 2)) + (vector-copy! (-> vu-lights ambient) (-> hud-lights ambient))) + (lod-set! dc 0) + (logior! (-> dc status) (draw-status was-drawn)) + (draw-bones-hud-merc dc buf)) + (none)) + (defun add-process-drawable ((arg0 process-drawable) (arg1 draw-control) (arg2 symbol) (arg3 dma-buffer)) ((-> arg1 dma-add-func) arg0 arg1 arg2 arg3) (none)) diff --git a/goal_src/jak1/engine/gfx/foreground/bones.gc b/goal_src/jak1/engine/gfx/foreground/bones.gc index f16cd1bbc..e2342357c 100644 --- a/goal_src/jak1/engine/gfx/foreground/bones.gc +++ b/goal_src/jak1/engine/gfx/foreground/bones.gc @@ -1506,3 +1506,104 @@ (none)) (define-extern draw-bones-hud (function draw-control dma-buffer none)) + +;; og:preserve-this added +(defun draw-bones-hud-merc ((draw draw-control) (dma-buf dma-buffer)) + (local-vars (at-2 int) (t2-10 vu-lights) (t3-3 uint128) (t3-4 uint128) (t3-5 uint128) (t3-6 uint128)) + (let ((buf dma-buf)) + (let* ((dma-start (-> buf base)) + (joints (+ (-> draw mgeo num-joints) 3)) + (bone-calc (the bone-calculation (&+ dma-start 16))) + (bone-calc-size (* joints 128)) + (mat-data (the object (&+ dma-start 64))) + (spr-work (scratchpad-object terrain-context))) + ;; align the matrix data to 64 bytes (we know it is at least 16 byte aligned) + (let ((mat-aligned (logand (the int mat-data) 48))) + (when (nonzero? mat-aligned) + (set! mat-data (&- (&+ (the pointer mat-data) 64) (the uint mat-aligned))))) + (let ((regs (scratchpad-object bone-regs :offset 240))) + (set! (-> regs joint-ptr) (the (inline-array joint) (-> draw jgeo data 0))) + (set! (-> regs bone-ptr) (-> draw skeleton bones)) + (set! (-> regs num-bones) joints)) + (let ((t2-0 mat-data) + (bone-mem (scratchpad-object bone-memory :offset 16)) + (bone-list *bone-calculation-list*) + (t1-6 bone-calc)) + (let ((t4-0 (-> bone-mem work regs joint-ptr)) + (t5-0 (-> bone-mem work regs bone-ptr)) + (t6-1 (-> bone-mem work regs num-bones)) + (t7-0 t1-6)) + (set! (-> t7-0 flags) (bone-calc-flags bncfl01)) + (set! (-> t7-0 num-bones) t6-1) + (set! (-> t7-0 matrix-area) (the (inline-array matrix) t2-0)) + (set! (-> t7-0 joints) t4-0) + (set! (-> t7-0 bones) t5-0) + (set! (-> t7-0 next) (the bone-calculation 0))) + (if (nonzero? (-> bone-list next)) (set! (-> bone-list next next) t1-6)) + (if (zero? (-> bone-list first)) (set! (-> bone-list first) t1-6)) + (set! (-> bone-list next) t1-6)) + (&+ (the pointer bone-calc) 48) + (let ((a2-2 (the object (+ (the uint mat-data) bone-calc-size)))) + (set! (-> (the (pointer uint128) dma-start)) + (logior (-> spr-work work foreground bone-mem work next-tag quad) (shl (the-as int a2-2) 32))) + (when (= (-> draw data-format) 1) + (let ((mgeo (-> draw lod-set lod 0 geo))) + (dotimes (effect-idx (the-as int (-> mgeo header effect-count))) + (cond + ((nonzero? (-> mgeo effect effect-idx envmap-usage)) + (let* ((t1-7 (-> *merc-bucket-info* light)) + (lights (scratchpad-object vu-lights :offset 64)) + (t0-10 7) + (t1-8 (the-as object t1-7))) + (b! (< (+ t0-10 -4) 0) cfg-9 :delay (set! t2-10 lights)) + (nop!) + (label cfg-8) + (let ((t6-2 (-> t2-10 direction 0 quad))) + (nop!) + (let ((t3-2 (-> t2-10 direction 1 quad))) + (+! t0-10 -4) + (let ((t4-1 (-> t2-10 direction 2 quad))) + (set! t1-8 (&+ (the pointer t1-8) 64)) + (let ((t5-1 (-> t2-10 color 0 quad))) + (set! t2-10 (the vu-lights (-> t2-10 color 1))) + (store-qw (&+ (the pointer t1-8) -64) t6-2) + (let ((t6-3 (+ t0-10 -4))) + (store-qw (&+ (the pointer t1-8) -48) t3-2) + (nop!) + (store-qw (&+ (the-as pointer t1-8) -32) t4-1) + (b! (>= t6-3 0) cfg-8 :delay (store-qw (&+ (the-as pointer t1-8) -16) t5-1))))))) + (label cfg-9) + (b! (zero? t0-10) cfg-14 :delay (set! t3-3 (-> t2-10 direction 0 quad))) + (let ((t2-11 (-> t2-10 direction 1)) + (t1-9 (-> (the vu-lights t1-8) direction 1)) + (t0-11 (+ t0-10 -1))) + (store-qw (&+ t1-9 -16) t3-3) + (b! (zero? t0-11) cfg-14 :delay (set! t3-4 (-> t2-11 quad))) + (let ((t2-12 (&+ t2-11 16)) + (t1-10 (&+ t1-9 16)) + (t0-12 (+ t0-11 -1))) + (store-qw (&+ t1-10 -16) t3-4) + (b! (zero? t0-12) cfg-14 :delay (set! t3-5 (-> t2-12 quad))) + (let ((t2-13 (&+ t2-12 16)) + (t1-11 (&+ t1-10 16)) + (t0-13 (+ t0-12 -1))) + (store-qw (&+ t1-11 -16) t3-5) + (b! (zero? t0-13) cfg-14 :delay (set! t3-6 (-> t2-13 quad))) + (&+ t2-13 16) + (let ((t1-12 (&+ t1-11 16))) (+ t0-13 -1) (store-qw (&+ t1-12 -16) t3-6)))))) + (label cfg-14) + (set! (-> *merc-bucket-info* effect effect-idx color-fade) (new 'static 'rgba :r #x80 :g #x80 :b #x80 :a #x80)) + (set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 0)) + (set! (-> *merc-bucket-info* effect effect-idx ignore-alpha) (the-as uint 1))) + (else (set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 1)))))) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask merc)) + (set! (-> buf base) (pc-merc-draw-request draw (the pointer a2-2) (the pointer mat-data) #f (the (pointer float) 0))) + (set! a2-2 (-> buf base)))) + (let ((a0-17 (logand (the int a2-2) 48))) + (b! (zero? a0-17) cfg-22 :delay (set! at-2 #x20000000)) + (set! (-> (the (pointer int128) a2-2)) (the int128 at-2)) + (let ((v1-2 (the pointer a2-2))) + (set! a2-2 (+ (&- (the pointer a2-2) (the uint a0-17)) 64)) + (store-u32 (&+ v1-2 4) (the int a2-2)))) + (label cfg-22) + (set! (-> buf base) (the pointer a2-2)))))) From 1029516f6d83959731b073f41f3f207034628ace Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:41:45 -0500 Subject: [PATCH 6/8] [sound] Add instance limits (#3769) Add instance limits to the sound library, and a `dump-info` command to `sndplay` to print out all the sounds/flags in an SBK file. This is based on the decompiled 989snd library @Ziemas is working on (https://github.com/Ziemas/dec989snd/blob/main/src/sndhand.c#L77) Co-authored-by: water111 --- game/sound/989snd/blocksound_handler.cpp | 59 +++++++++++++++++++++++- game/sound/989snd/blocksound_handler.h | 7 ++- game/sound/989snd/musicbank.cpp | 11 ++--- game/sound/989snd/musicbank.h | 11 ++--- game/sound/989snd/player.cpp | 20 +++++++- game/sound/989snd/player.h | 5 +- game/sound/989snd/sfxblock.cpp | 26 ++++++++++- game/sound/989snd/sfxblock.h | 4 +- game/sound/989snd/sfxgrain.cpp | 5 +- game/sound/989snd/sndplay.cpp | 5 ++ game/sound/989snd/sound_handler.h | 11 +++++ game/sound/989snd/soundbank.h | 15 +++--- 12 files changed, 146 insertions(+), 33 deletions(-) diff --git a/game/sound/989snd/blocksound_handler.cpp b/game/sound/989snd/blocksound_handler.cpp index f1e189d5a..375bb5e1e 100644 --- a/game/sound/989snd/blocksound_handler.cpp +++ b/game/sound/989snd/blocksound_handler.cpp @@ -16,8 +16,14 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank, s32 sfx_vol, s32 sfx_pan, SndPlayParams& params, - u32 sound_id) - : m_group(sfx.VolGroup), m_sfx(sfx), m_vm(vm), m_bank(bank), m_sound_id(sound_id) { + u32 sound_id, + s32 start_tick) + : m_group(sfx.VolGroup), + m_sfx(sfx), + m_vm(vm), + m_bank(bank), + m_sound_id(sound_id), + m_start_tick(start_tick) { s32 vol, pan, pitch_mod, pitch_bend; if (sfx_vol == -1) { sfx_vol = sfx.Vol; @@ -298,4 +304,53 @@ void BlockSoundHandler::DoGrain() { m_countdown = m_sfx.Grains[m_next_grain].Delay + ret; } +SoundHandler* BlockSoundHandler::CheckInstanceLimit( + const std::map>& handlers, + s32 vol) { + if (!m_sfx.InstanceLimit) { + return nullptr; + } + + if (!m_sfx.Flags.has_instlimit()) { + return nullptr; + } + + BlockSoundHandler* weakest = nullptr; + int inst = 0; + + for (const auto& [id, handler_ptr] : handlers) { + // Only compare to BlockSoundHandlers + auto* handler = dynamic_cast(handler_ptr.get()); + if (!handler) { + continue; + } + + // See if this is playing the same sound + // 989snd checks both an orig_sound and a SH.Sound, but we never change the sound. + // We'd need to revisit this if we eventually support BRANCH grains. + if (&handler->m_sfx == &m_sfx) { + inst++; + if (!weakest || // + (m_sfx.Flags.instlimit_vol() && handler->m_app_volume < weakest->m_app_volume) || // + (m_sfx.Flags.instlimit_tick() && handler->m_start_tick < weakest->m_start_tick)) { + weakest = handler; + } + } + } + + // See if this handler would cause us to exceed the limit + if (m_sfx.InstanceLimit - 1 < inst) { + if (weakest && ((m_sfx.Flags.instlimit_vol() && weakest->m_app_volume < vol) || + m_sfx.Flags.instlimit_tick())) { + // existing weakest is worst + return weakest; + } else { + // new sound is weakest + return this; + } + } else { + return nullptr; + } +} + } // namespace snd diff --git a/game/sound/989snd/blocksound_handler.h b/game/sound/989snd/blocksound_handler.h index f1b30a80f..146da0e02 100644 --- a/game/sound/989snd/blocksound_handler.h +++ b/game/sound/989snd/blocksound_handler.h @@ -26,7 +26,8 @@ class BlockSoundHandler : public SoundHandler { s32 sfx_vol, s32 sfx_pan, SndPlayParams& params, - u32 sound_id); + u32 sound_id, + s32 start_tick); ~BlockSoundHandler() override; bool Tick() override; @@ -46,6 +47,9 @@ class BlockSoundHandler : public SoundHandler { void UpdatePitch(); + SoundHandler* CheckInstanceLimit(const std::map>& handlers, + s32 vol) override; + bool m_paused{false}; u8 m_group{0}; @@ -90,5 +94,6 @@ class BlockSoundHandler : public SoundHandler { u32 m_next_grain{0}; u32 m_sound_id{0}; + s32 m_start_tick{0}; }; } // namespace snd diff --git a/game/sound/989snd/musicbank.cpp b/game/sound/989snd/musicbank.cpp index bcef3f4f9..1abe67125 100644 --- a/game/sound/989snd/musicbank.cpp +++ b/game/sound/989snd/musicbank.cpp @@ -7,12 +7,8 @@ namespace snd { -std::optional> MusicBank::MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - s32 pm, - s32 pb) { +std::optional> +MusicBank::MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 tick) { auto& sound = Sounds[sound_id]; // FIXME: global midi list @@ -42,7 +38,8 @@ std::optional> MusicBank::MakeHandler(VoiceManager u32 sound_id, s32 vol, s32 pan, - SndPlayParams& params) { + SndPlayParams& params, + s32 tick) { return std::nullopt; } diff --git a/game/sound/989snd/musicbank.h b/game/sound/989snd/musicbank.h index 76a3b75d3..c23f8271e 100644 --- a/game/sound/989snd/musicbank.h +++ b/game/sound/989snd/musicbank.h @@ -66,17 +66,14 @@ class MusicBank : public SoundBank { std::span samples, std::span midi_data); - std::optional> MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - s32 pm, - s32 pb) override; + std::optional> + MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 tick) override; std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, - SndPlayParams& params) override; + SndPlayParams& params, + s32 tick) override; }; } // namespace snd diff --git a/game/sound/989snd/player.cpp b/game/sound/989snd/player.cpp index da8c7e6b1..6655c47a9 100644 --- a/game/sound/989snd/player.cpp +++ b/game/sound/989snd/player.cpp @@ -137,11 +137,19 @@ u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm return 0; } - auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb); + auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb, GetTick()); if (!handler.has_value()) { return 0; } + auto handler_to_stop = handler.value()->CheckInstanceLimit(mHandlers, vol); + if (handler_to_stop) { + handler_to_stop->Stop(); + if (handler_to_stop == handler.value().get()) { + return 0; + } + } + u32 handle = mHandleAllocator.GetId(); mHandlers.emplace(handle, std::move(handler.value())); // fmt::print("play_sound {}:{} - {}\n", bank_id, sound_id, handle); @@ -149,6 +157,16 @@ u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm return handle; } +void Player::DebugPrintAllSoundsInBank(BankHandle bank_id) { + std::scoped_lock lock(mTickLock); + auto* bank = mLoader.GetBankByHandle(bank_id); + if (!bank) { + lg::error("DebugPrintAllSoundsInBank: invalid bank"); + return; + } + bank->DebugPrintAllSounds(); +} + u32 Player::PlaySoundByName(BankHandle bank_id, char* bank_name, char* sound_name, diff --git a/game/sound/989snd/player.h b/game/sound/989snd/player.h index f03498c20..41e677179 100644 --- a/game/sound/989snd/player.h +++ b/game/sound/989snd/player.h @@ -2,10 +2,10 @@ // SPDX-License-Identifier: ISC #pragma once +#include #include #include #include -#include #include #include "ame_handler.h" @@ -42,6 +42,7 @@ class Player { s32 pan, s32 pm, s32 pb); + void DebugPrintAllSoundsInBank(BankHandle bank); void SetSoundReg(u32 sound_id, u8 reg, u8 value); void SetGlobalExcite(u8 value) { GlobalExcite = value; }; bool SoundStillActive(u32 sound_id); @@ -71,7 +72,7 @@ class Player { private: std::recursive_mutex mTickLock; // TODO does not need to recursive with some light restructuring IdAllocator mHandleAllocator; - std::unordered_map> mHandlers; + std::map> mHandlers; void Tick(s16Output* stream, int samples); diff --git a/game/sound/989snd/sfxblock.cpp b/game/sound/989snd/sfxblock.cpp index 9fc9920ec..07387c0f3 100644 --- a/game/sound/989snd/sfxblock.cpp +++ b/game/sound/989snd/sfxblock.cpp @@ -5,20 +5,24 @@ #include "common/log/log.h" +#include "third-party/magic_enum.hpp" + namespace snd { std::optional> SFXBlock::MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, - SndPlayParams& params) { + SndPlayParams& params, + s32 current_tick) { auto& SFX = Sounds[sound_id]; if (SFX.Grains.empty()) { return std::nullopt; } - auto handler = std::make_unique(*this, SFX, vm, vol, pan, params, sound_id); + auto handler = + std::make_unique(*this, SFX, vm, vol, pan, params, sound_id, current_tick); return handler; } @@ -31,4 +35,22 @@ std::optional SFXBlock::GetSoundByName(const char* name) { return std::nullopt; } +void SFXBlock::DebugPrintAllSounds() { + for (const auto& [name, id] : Names) { + printf("%s : %d\n", name.c_str(), id); + const auto& sound = Sounds.at(id); + printf(" Vol: %d\n", sound.Vol); + printf(" VolGroup: %d\n", sound.VolGroup); + printf(" Pan: %d\n", sound.Pan); + printf(" InstanceLimit: %d\n", sound.InstanceLimit); + printf(" Flags: 0x%x\n", sound.Flags.flags); + printf(" User: 0x%x 0x%x 0x%x 0x%x\n", sound.UserData.data[0], sound.UserData.data[1], + sound.UserData.data[2], sound.UserData.data[3]); + printf(" Grains\n"); + for (const auto& grain : sound.Grains) { + fmt::print(" {} ({})\n", magic_enum::enum_name(grain.Type), (int)grain.Type); + } + } +} + } // namespace snd diff --git a/game/sound/989snd/sfxblock.h b/game/sound/989snd/sfxblock.h index e3a91097c..4e779112e 100644 --- a/game/sound/989snd/sfxblock.h +++ b/game/sound/989snd/sfxblock.h @@ -52,13 +52,15 @@ class SFXBlock : public SoundBank { u32 sound_id, s32 vol, s32 pan, - SndPlayParams& params) override; + SndPlayParams& params, + s32 current_tick) override; std::optional GetName() override { return Name; }; std::optional GetSoundByName(const char* name) override; std::optional GetSoundUserData(u32 sound_id) override { return &Sounds.at(sound_id).UserData; }; + void DebugPrintAllSounds() override; }; } // namespace snd diff --git a/game/sound/989snd/sfxgrain.cpp b/game/sound/989snd/sfxgrain.cpp index 835e677ff..6bf63735f 100644 --- a/game/sound/989snd/sfxgrain.cpp +++ b/game/sound/989snd/sfxgrain.cpp @@ -158,7 +158,8 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STARTCHILDSOUND(BlockSoundHandler& handler) { s32 index = psp.sound_id; if (index >= 0) { - auto child_handler = block.MakeHandler(handler.m_vm, index, vol, pan, params); + auto child_handler = + block.MakeHandler(handler.m_vm, index, vol, pan, params, handler.m_start_tick); if (child_handler.has_value()) { handler.m_children.emplace_front(std::move(child_handler.value())); } @@ -257,7 +258,7 @@ s32 Grain::snd_SFX_GRAIN_TYPE_RAND_PLAY(BlockSoundHandler& handler) { auto cp = std::get(data); auto options = cp.param[0]; auto count = cp.param[1]; - auto previous = cp.param[2]; + auto& previous = cp.param[2]; int rnd = rand() % options; if (rnd == previous) { diff --git a/game/sound/989snd/sndplay.cpp b/game/sound/989snd/sndplay.cpp index cf1083873..ba934983f 100644 --- a/game/sound/989snd/sndplay.cpp +++ b/game/sound/989snd/sndplay.cpp @@ -28,6 +28,7 @@ int main(int argc, char* argv[]) { printf("commands:\n"); printf(" play [id]\n"); printf(" stop\n"); + printf(" dump-info\n"); while (true) { printf("> "); @@ -77,6 +78,10 @@ int main(int argc, char* argv[]) { printf("stopping all sounds\n"); player.StopAllSounds(); } + + if (parts[0] == "dump-info") { + player.DebugPrintAllSoundsInBank(bankid); + } } return 0; diff --git a/game/sound/989snd/sound_handler.h b/game/sound/989snd/sound_handler.h index 85ef18213..c7fe71596 100644 --- a/game/sound/989snd/sound_handler.h +++ b/game/sound/989snd/sound_handler.h @@ -2,6 +2,9 @@ // SPDX-License-Identifier: ISC #pragma once +#include +#include + #include "common/common_types.h" namespace snd { @@ -25,5 +28,13 @@ class SoundHandler { virtual void SetPBend(s32 /*mod*/){}; virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {} virtual u32 SoundID() const { return -1; } + + // Check if this handler violates an instance limit. If so, return pointer to the sound that + // should be removed. + virtual SoundHandler* CheckInstanceLimit( + const std::map>& handlers, + s32 vol) { + return nullptr; + } }; } // namespace snd diff --git a/game/sound/989snd/soundbank.h b/game/sound/989snd/soundbank.h index 3b20e5044..dac216b93 100644 --- a/game/sound/989snd/soundbank.h +++ b/game/sound/989snd/soundbank.h @@ -54,32 +54,31 @@ class SoundBank { u32 BankID; s8 BankNum; - virtual std::optional> MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - s32 pm, - s32 pb) { + virtual std::optional> + MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 current_tick) { SndPlayParams params{}; params.vol = vol; params.pan = pan; params.pitch_mod = pm; params.pitch_bend = pb; - return MakeHandler(vm, sound_id, -1, -1, params); + return MakeHandler(vm, sound_id, -1, -1, params, current_tick); }; virtual std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, - SndPlayParams& params) = 0; + SndPlayParams& params, + s32 current_tick) = 0; virtual std::optional GetName() { return std::nullopt; }; virtual std::optional GetSoundByName(const char* /*name*/) { return std::nullopt; }; virtual std::optional GetSoundUserData(u32 /*sound_id*/) { return std::nullopt; }; + + virtual void DebugPrintAllSounds() {} }; } // namespace snd From 6e4e5d2bf7263894f5d1d33704a4846899d4ba4e Mon Sep 17 00:00:00 2001 From: ZedB0T <89345505+Zedb0T@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:42:39 -0500 Subject: [PATCH 7/8] Add option to hide the imgui bar with a click event (#3791) Should cover edge cases where the bar is displayed and a keyboard type device is not connected to the system (Steam deck/touch input based device instead). While it should probably be impossible to display the bar in the first place on such devices it apparently isnt -> https://www.reddit.com/r/jakanddaxter/comments/1h4mht1/how_to_remove_grey_bar_at_top_on_steamdeck/ And even ignoring the whole steamdeck/no keyboard thing adding another way to hide it that is visible on the bar should help reduce people asking about how to hide it. (And hopefully help people who dont ask/dont know where to ask!) https://github.com/user-attachments/assets/7f132781-675c-451e-8b39-2a78fa098b9e --- game/graphics/opengl_renderer/debug_gui.cpp | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/game/graphics/opengl_renderer/debug_gui.cpp b/game/graphics/opengl_renderer/debug_gui.cpp index c1b407621..c8acd7f0f 100644 --- a/game/graphics/opengl_renderer/debug_gui.cpp +++ b/game/graphics/opengl_renderer/debug_gui.cpp @@ -188,10 +188,26 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) { } if (!Gfx::g_debug_settings.ignore_hide_imgui) { - ImGui::Text("%s", fmt::format("Toggle toolbar with {}", - sdl_util::get_keyboard_button_name( - Gfx::g_debug_settings.hide_imgui_key, InputModifiers())) - .c_str()); + std::string button_text = + fmt::format("Click here or Press {} to hide Toolbar", + sdl_util::get_keyboard_button_name(Gfx::g_debug_settings.hide_imgui_key, + InputModifiers())); + + ImVec2 text_size = ImGui::CalcTextSize(button_text.c_str()); + float button_width = text_size.x + ImGui::GetStyle().FramePadding.x * 2; + float button_height = text_size.y + ImGui::GetStyle().FramePadding.y * 2; + + ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetStyleColorVec4(ImGuiCol_MenuBarBg)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, + ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetStyleColorVec4(ImGuiCol_HeaderActive)); + + if (ImGui::Selectable(button_text.c_str(), false, ImGuiSelectableFlags_DontClosePopups, + ImVec2(button_width, button_height))) { + std::shared_ptr display = Display::GetMainDisplay(); + display->set_imgui_visible(false); + } + ImGui::PopStyleColor(3); } } ImGui::EndMainMenuBar(); From a2f9d36332e97f56fe2baa1a1e70cee94f7db960 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 7 Dec 2024 13:42:48 -0500 Subject: [PATCH 8/8] Fix animation processing interpolation bug, crash on no merc material (#3794) Fixes the issue with animations doing a full 180 degree rotation and disappearing. The quaternions in the GLB export "flip", and when interpolating animation frames in between a flip, we got bogus quaternions. Fixes https://github.com/open-goal/jak-project/issues/3786 Also fix a crash in `goalc` when using merc models without any materials. Co-authored-by: water111 --- goalc/build_actor/common/MercExtract.cpp | 7 +++--- .../common/animation_processing.cpp | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/goalc/build_actor/common/MercExtract.cpp b/goalc/build_actor/common/MercExtract.cpp index e83a26c07..9d90ab0ec 100644 --- a/goalc/build_actor/common/MercExtract.cpp +++ b/goalc/build_actor/common/MercExtract.cpp @@ -97,7 +97,6 @@ void extract(const std::string& name, out.new_model.max_draws = 0; auto process_normal_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) { - const auto& mat = model.materials[mat_idx]; eff.all_draws.push_back(d_); auto& draw = eff.all_draws.back(); draw.mode = gltf_util::make_default_draw_mode(); @@ -107,6 +106,8 @@ void extract(const std::string& name, draw.tree_tex_id = 0; return; } + 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); @@ -172,8 +173,8 @@ void extract(const std::string& name, }; for (const auto& [mat_idx, d_] : draw_by_material) { - const auto& mat = model.materials[mat_idx]; - if (!gltf_util::material_has_envmap(mat) || !gltf_util::envmap_is_valid(mat)) { + if (mat_idx < 0 || !gltf_util::material_has_envmap(model.materials[mat_idx]) || + !gltf_util::envmap_is_valid(model.materials[mat_idx])) { process_normal_draw(e, mat_idx, d_); } else { envmap_eff.has_envmap = true; diff --git a/goalc/build_actor/common/animation_processing.cpp b/goalc/build_actor/common/animation_processing.cpp index 8f0206893..55e75d8b6 100644 --- a/goalc/build_actor/common/animation_processing.cpp +++ b/goalc/build_actor/common/animation_processing.cpp @@ -21,7 +21,8 @@ int find_max_joint(const tinygltf::Animation& anim, const std::map& no template std::vector compute_keyframes(const std::vector& times, const std::vector& values, - float framerate) { + float framerate, + bool quaternion_interp) { std::vector ret; ASSERT(!times.empty()); ASSERT(times.size() == values.size()); @@ -36,8 +37,15 @@ std::vector compute_keyframes(const std::vector& times, } const float fraction = (t - times.at(i)) / (times.at(i + 1) - times.at(i)); - ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction); - // lg::info("{} + {:.3f}, {}", i, fraction, ret.back().to_string_aligned()); + if (quaternion_interp) { + float multiplier = 1; + if (values.at(i).dot(values.at(i + 1)) < 0) { + multiplier = -1; + } + ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction * multiplier); + } else { + ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction); + } t += 1.f / framerate; } return ret; @@ -47,12 +55,13 @@ template std::vector> extract_keyframed_gltf_vecn( const tinygltf::Model& model, const tinygltf::AnimationSampler& sampler, - float framerate) { + float framerate, + bool quaternion_interp) { std::vector times = gltf_util::extract_floats(model, sampler.input); std::vector> values = gltf_util::extract_vec(model, sampler.output, TINYGLTF_COMPONENT_TYPE_FLOAT); ASSERT(times.size() == values.size()); - return compute_keyframes(times, values, framerate); + return compute_keyframes(times, values, framerate, quaternion_interp); } } // namespace @@ -75,13 +84,13 @@ UncompressedJointAnim extract_anim_from_gltf(const tinygltf::Model& model, const auto& sampler = anim.samplers.at(channel.sampler); if (channel.target_path == "translation") { out.joints.at(channel_joint).trans_frames = - extract_keyframed_gltf_vecn<3>(model, sampler, framerate); + extract_keyframed_gltf_vecn<3>(model, sampler, framerate, false); } else if (channel.target_path == "rotation") { out.joints.at(channel_joint).quat_frames = - extract_keyframed_gltf_vecn<4>(model, sampler, framerate); + extract_keyframed_gltf_vecn<4>(model, sampler, framerate, true); } else if (channel.target_path == "scale") { out.joints.at(channel_joint).scale_frames = - extract_keyframed_gltf_vecn<3>(model, sampler, framerate); + extract_keyframed_gltf_vecn<3>(model, sampler, framerate, false); } else { lg::die("unknown target_path {}", channel.target_path); }