From b2ed9313bdc99329f2844a57b7233ffd8314185b Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Mon, 28 Mar 2022 18:14:25 -0400 Subject: [PATCH] [graphics] First part of shrub extraction (#1258) * decompile 90% of shrubbery * some more progress * some more * big function decompiled * went through `draw-prototype-inline-array-shrub` and made more notes * shrub: start implementing extract_shrub * read through current notes and add the info to current decomp * decomp: allow skipping inline-asm from output * add code to BspHeader to get GOAL types for shrubs * add doc * wip * fix bad merge Co-authored-by: Tyler Wilding Co-authored-by: Tyler Wilding --- decompiler/CMakeLists.txt | 1 + decompiler/IR2/OpenGoalMapping.cpp | 18 + decompiler/IR2/OpenGoalMapping.h | 4 +- decompiler/analysis/inline_asm_rewrite.cpp | 2 + decompiler/config/all-types.gc | 81 ++- .../config/jak1_ntsc_black_label/hacks.jsonc | 6 +- .../jak1_ntsc_black_label/label_types.jsonc | 3 + .../jak1_ntsc_black_label/type_casts.jsonc | 84 +++ decompiler/level_extractor/BspHeader.cpp | 438 ++++++++++++- decompiler/level_extractor/BspHeader.h | 575 ++++++++++++----- decompiler/level_extractor/extract_level.cpp | 13 + decompiler/level_extractor/extract_shrub.cpp | 292 +++++++++ decompiler/level_extractor/extract_shrub.h | 28 + decompiler/level_extractor/extract_tie.cpp | 20 +- docs/scratch/shrub_asm.md | 32 +- game/graphics/opengl_renderer/Loader.cpp | 6 +- game/graphics/opengl_renderer/Loader.h | 4 +- .../opengl_renderer/OpenGLRenderer.cpp | 5 +- goal_src/engine/gfx/background.gc | 20 +- goal_src/engine/gfx/shrub/shrub-work.gc | 47 +- goal_src/engine/gfx/shrub/shrubbery-h.gc | 131 ++-- goal_src/engine/gfx/shrub/shrubbery.gc | 602 +++++++++++++++++- goal_src/engine/gfx/tie/prototype-h.gc | 9 +- .../engine/debug/default-menu_REF.gc | 12 +- .../reference/engine/draw/drawable_REF.gc | 10 +- .../reference/engine/gfx/background_REF.gc | 6 +- .../engine/gfx/shrub/shrub-work_REF.gc | 42 +- .../engine/gfx/shrub/shrubbery-h_REF.gc | 55 +- .../engine/gfx/tie/prototype-h_REF.gc | 13 +- .../reference/engine/gfx/tie/prototype_REF.gc | 4 +- 30 files changed, 2179 insertions(+), 384 deletions(-) create mode 100644 decompiler/level_extractor/extract_shrub.cpp create mode 100644 decompiler/level_extractor/extract_shrub.h diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index 8a59f292bd..1ea0e1ed25 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -60,6 +60,7 @@ add_library( level_extractor/extract_tfrag.cpp level_extractor/extract_tie.cpp level_extractor/BspHeader.cpp + level_extractor/extract_shrub.cpp ObjectFile/LinkedObjectFile.cpp ObjectFile/LinkedObjectFileCreation.cpp diff --git a/decompiler/IR2/OpenGoalMapping.cpp b/decompiler/IR2/OpenGoalMapping.cpp index ae486022a6..27b01bee45 100644 --- a/decompiler/IR2/OpenGoalMapping.cpp +++ b/decompiler/IR2/OpenGoalMapping.cpp @@ -8,6 +8,16 @@ typedef OpenGOALAsm::InstructionModifiers MOD; const std::map MIPS_ASM_TO_OPEN_GOAL_FUNCS = { // ----- EE ------- + // Instructions that are nopped + // {InstructionKind::MTC0, + // {".nop", {MOD::SKIP_IT}}}, // they only use this for performance counters / Count / + // Debug + // {InstructionKind::MTPC, {".nop", {MOD::SKIP_IT}}}, + // {InstructionKind::MFPC, {".nop", {MOD::SKIP_IT}}}, + // {InstructionKind::SYNCP, {".nop", {MOD::SKIP_IT}}}, + // {InstructionKind::SYNCL, {".nop", {MOD::SKIP_IT}}}, + + // Shifts and such {InstructionKind::PSLLW, {".pw.sll", {}}}, {InstructionKind::PSRAW, {".pw.sra", {}}}, {InstructionKind::PSUBW, {".psubw", {}}}, @@ -156,6 +166,10 @@ OpenGOALAsm::OpenGOALAsm(Instruction _instr) : m_instr(_instr) { if (func.funcTemplate.rfind("TODO", 0) == 0) { todo = true; } + if (std::find(func.modifiers.begin(), func.modifiers.end(), InstructionModifiers::SKIP_IT) != + func.modifiers.end()) { + skip = true; + } } } @@ -170,6 +184,10 @@ OpenGOALAsm::OpenGOALAsm(Instruction _instr, if (func.funcTemplate.rfind("TODO", 0) == 0) { todo = true; } + if (std::find(func.modifiers.begin(), func.modifiers.end(), InstructionModifiers::SKIP_IT) != + func.modifiers.end()) { + skip = true; + } } } diff --git a/decompiler/IR2/OpenGoalMapping.h b/decompiler/IR2/OpenGoalMapping.h index 73644bb586..41ec97d227 100644 --- a/decompiler/IR2/OpenGoalMapping.h +++ b/decompiler/IR2/OpenGoalMapping.h @@ -23,7 +23,8 @@ struct OpenGOALAsm { OFFSET, SWAP_FIRST_TWO_SOURCE_ARGS, ACC_THIRD_SRC_ARG, - QWORD_CAST + QWORD_CAST, + SKIP_IT }; struct Function { @@ -41,6 +42,7 @@ struct OpenGOALAsm { bool valid = true; bool todo = false; + bool skip = false; Instruction m_instr; std::optional m_dst; std::vector> m_src; diff --git a/decompiler/analysis/inline_asm_rewrite.cpp b/decompiler/analysis/inline_asm_rewrite.cpp index 6a167212b6..22e10c7373 100644 --- a/decompiler/analysis/inline_asm_rewrite.cpp +++ b/decompiler/analysis/inline_asm_rewrite.cpp @@ -45,6 +45,8 @@ bool rewrite_inline_asm_instructions(Form* top_level_form, asmOp.m_instr.to_string(f.ir2.env.file->labels)); new_entries.push_back(entry); continue; + } else if (asmOp.skip) { + continue; } else if (asmOp.todo) { // If its an invalid or unsupported exception, skip it /*lg::warn("[ASM Re-Write] - Inline assembly instruction marked with TODO - [{}]", diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index bdef22be8c..aefe2fb950 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -9497,11 +9497,11 @@ ;; - Types (deftype prototype-bucket (basic) - ((name basic :offset-assert 4) + ((name string :offset-assert 4) (flags uint32 :offset-assert 8) (in-level uint16 :offset-assert 12) (utextures uint16 :offset-assert 14) - (geometry drawable 4 :offset-assert 16) + (geometry drawable 4 :offset-assert 16) ;; 15, 20, 24 (dists vector :inline :offset-assert 32) (rdists vector :inline :offset-assert 48) (next uint32 4 :offset-assert 64) @@ -9515,6 +9515,10 @@ (rlength-mid float :offset 56) (stiffness float :offset 60) (next-clear uint128 :offset 64) + (next-clear-1 int32 :offset 64) + (next-clear-2 int32 :offset 68) + (next-clear-3 int32 :offset 72) + (next-clear-4 int32 :offset 76) (count-clear uint64 :offset 80) ) :method-count-assert 9 @@ -9523,8 +9527,14 @@ ) (deftype prototype-bucket-shrub (prototype-bucket) - ((mod-count uint16 4 :offset-assert 88) - (last uint32 4 :offset-assert 96) + (;; Geometry Array + ;; - prototype-generic-shrub / shrub-near (drawn with generic) + ;; - prototype-shrubbery + ;; - prototype-trans-shrubbery + ;; - billboard + (mod-count uint16 4 :offset-assert 88) + (last dma-packet 4 :offset-assert 96) + (count-clear-qword uint128 :offset 80) (last-clear uint128 :offset 96) ) :method-count-assert 9 @@ -12791,6 +12801,7 @@ ;; - Types +;; Single rectangle -- only one texture (aka adgif-shader) (deftype billboard (drawable) ((flat adgif-shader :inline :offset-assert 32) ) @@ -12800,18 +12811,19 @@ ) (deftype shrub-view-data (structure) - ((data uint128 3 :offset-assert 0) - (texture-giftag qword :inline :offset 0) + ((data uint128 3 :offset-assert 0) + (texture-giftag gs-gif-tag :inline :offset 0) (consts vector :inline :offset 16) (fog-clamp vector :inline :offset 32) - (tex-start-ptr int32 :offset 16) - (gifbufsum float :offset 16) - (mtx-buf-ptr int32 :offset 20) - (exp23 float :offset 20) - (fog-0 float :offset 24) - (fog-1 float :offset 28) - (fog-min float :offset 32) - (fog-max float :offset 36) + (tex-start-ptr int32 :score 999 :offset 16) + (gifbufsum float :score 999 :offset 16) + ;; suspected to be part of hacks to get around VU's lack of integer operations + (mtx-buf-ptr int32 :score 999 :offset 20) + (exp23 float :score 999 :offset 20) + (fog-0 float :score 999 :offset 24) + (fog-1 float :score 999 :offset 28) + (fog-min float :score 999 :offset 32) + (fog-max float :score 999 :offset 36) ) :method-count-assert 9 :size-assert #x30 @@ -12820,11 +12832,19 @@ (deftype shrubbery (drawable) ((textures (inline-array adgif-shader) :offset 4) + ;; header breakdown: + ;; [0] - number of textures / 2 + ;; [1] - number of vertices + ;; [2] - number of triangle strips + ;; [3] - ?? + ;; + ;; Number of Triangles in the Shrub = header[2] - 2 * header[1] (header qword :offset 8) (obj-qwc uint8 :offset 12) (vtx-qwc uint8 :offset 13) (col-qwc uint8 :offset 14) (stq-qwc uint8 :offset 15) + ;; start of static dma-chain (obj uint32 :score 999 :offset 16) (vtx uint32 :score 999 :offset 20) (col uint32 :score 999 :offset 24) @@ -12836,7 +12856,8 @@ ) (deftype instance-shrubbery (instance) - ((flat-normal vector :inline :offset-assert 64) + ((color-indices uint32 :offset 8) + (flat-normal vector :inline :offset-assert 64) (flat-hwidth float :offset 76) (color uint32 :offset 8) ) @@ -12856,7 +12877,7 @@ (deftype drawable-tree-instance-shrub (drawable-tree) ((info prototype-array-shrub-info :offset 8) - (colors-added uint32 :offset 12) ;; added + (colors-added time-of-day-palette :offset 12) ;; added ) :method-count-assert #x12 :size-assert #x24 @@ -12866,6 +12887,8 @@ (deftype generic-shrub-fragment (drawable) ((textures (inline-array adgif-shader) :score 999 :offset 4) (vtx-cnt uint32 :score 999 :offset 8) + ;; the total size of the textures array + ;; - each texture is 5 qwords (cnt-qwc uint8 :score 999 :offset 12) (vtx-qwc uint8 :score 999 :offset 13) (col-qwc uint8 :score 999 :offset 14) @@ -12933,7 +12956,7 @@ (chainb qword 8 :inline :offset-assert 176) (colors rgba 1024 :offset-assert 304) (matrix-tmpl qword 20 :inline :offset-assert 4400) - (count-tmpl qword 20 :inline :offset-assert 4720) + (count-tmpl vector4w 20 :inline :offset-assert 4720) (mscalf-tmpl dma-packet :inline :offset-assert 5040) (mscalf-ret-tmpl dma-packet :inline :offset-assert 5056) (adgif-tmpl dma-gif-packet :inline :offset-assert 5072) @@ -17058,19 +17081,19 @@ ;; - Functions (define-extern upload-generic-shrub (function dma-buffer generic-shrub-fragment int int dma-buffer)) -(define-extern shrub-num-tris (function shrubbery int)) -(define-extern shrub-init-frame function) -(define-extern shrub-upload-model function) -(define-extern shrub-do-init-frame function) -(define-extern shrub-upload-view-data function) -(define-extern shrub-init-view-data function) -(define-extern mem-usage-shrub-walk function) -(define-extern shrub-make-perspective-matrix (function matrix none)) -(define-extern shrub-time function) -(define-extern draw-inline-array-instance-shrub function) -(define-extern draw-prototype-inline-array-shrub function) +(define-extern shrub-num-tris (function shrubbery uint)) +(define-extern shrub-init-frame (function dma-buffer gs-test none)) +(define-extern shrub-upload-model (function shrubbery dma-buffer int symbol)) ;; third arg is `start-bank` from shrub-work +(define-extern shrub-do-init-frame (function dma-buffer symbol)) +(define-extern shrub-upload-view-data (function dma-buffer symbol)) +(define-extern shrub-init-view-data (function shrub-view-data symbol)) +(define-extern mem-usage-shrub-walk (function draw-node int memory-usage-block int draw-node)) +(define-extern shrub-make-perspective-matrix (function matrix matrix)) +(define-extern shrub-time (function int int int int int int)) ;; unused +(define-extern draw-inline-array-instance-shrub (function dma-buffer drawable int (inline-array prototype-bucket-shrub) none)) +(define-extern draw-prototype-inline-array-shrub (function int (inline-array prototype-bucket-shrub) pointer)) (define-extern shrub-upload-test (function generic-shrub-fragment none)) -(define-extern test-func function) +(define-extern test-func (function none)) ;; - Unknowns diff --git a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc index bdf186a273..fbaccc3a63 100644 --- a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc @@ -189,8 +189,7 @@ "draw-node-cull", // shrubbery - "test-func", - "draw-inline-array-instance-shrub", + "draw-inline-array-instance-shrub", // CFG // tfrag "stats-tfrag-asm", @@ -423,7 +422,8 @@ "draw-drawable-tree-dirt-tfrag": [6, 8, 13, 15], "draw-drawable-tree-ice-tfrag": [6, 8, 13, 15], "draw-drawable-tree-instance-tie": [10, 12, 18, 20, 26, 28, 37, 39], - + "draw-drawable-tree-instance-shrub": [5, 7, 9, 11], + "birth-pickup-at-point": [0], "draw-bones": [0, 1, 2, 8, 81], "draw-bones-hud": [7, 8], diff --git a/decompiler/config/jak1_ntsc_black_label/label_types.jsonc b/decompiler/config/jak1_ntsc_black_label/label_types.jsonc index c166bb0620..355241485e 100644 --- a/decompiler/config/jak1_ntsc_black_label/label_types.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/label_types.jsonc @@ -2106,6 +2106,9 @@ ], "generic-vu0": [["L1", "vu-function"]], + "shrubbery": [ + ["L133", "vu-function"] + ], // please do not add things after this entry! git is dumb. "object-file-that-doesnt-actually-exist-and-i-just-put-this-here-to-prevent-merge-conflicts-with-this-file": [] diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index f18c79b8fe..4eea47e2b7 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -7690,6 +7690,90 @@ [273, "t0", "(pointer uint64)"], [275, "t0", "(pointer gs-reg64)"] ], + "test-func": [ + [7, "f1", "float"] + ], + "(method 14 drawable-tree-instance-shrub)": [ + [[12, 151], "gp", "prototype-bucket-shrub"], + [15, "a1", "drawable-group"], + [39, "v1", "drawable-group"], + [61, "s3", "shrubbery"], + [85, "v1", "drawable-group"], + [107, "s3", "shrubbery"], + [151, "gp", "(inline-array prototype-bucket-shrub)"] + ], + + "(method 10 drawable-tree-instance-shrub)": [ + [3, "a1", "terrain-context"] + ], + + "draw-prototype-inline-array-shrub": [ + [[13, 55], "v1", "prototype-bucket-shrub"], + [[15, 30], "a2", "vector4w"], + [[42, 51], "a2", "vector4w"], + [[87, 93], "a0", "dma-packet"], + [[96, 102], "a0", "gs-gif-tag"], + [107, "a1", "gs-test"], + [107, "a0", "(pointer gs-test)"], + [109, "a0", "(pointer gs-reg64)"], + [[117, 136], "v1", "matrix3"], + [[140, 145], "v1", "dma-packet"], + [[238, 247], "a0", "dma-packet"], + [[261, 266], "v1", "dma-packet"], + [[359, 368], "a0", "dma-packet"], + [[382, 387], "v1", "dma-packet"], + [[451, 457], "a1", "dma-packet"], + [[460, 466], "a1", "dma-packet"], + [[524, 530], "a0", "dma-packet"], + [55, "v1", "(inline-array prototype-bucket-shrub)"], + [156, "v1", "terrain-context"], + [212, "gp", "prototype-bucket-shrub"], + [333, "gp", "prototype-bucket-shrub"], + [[479, 518], "gp", "prototype-bucket-shrub"], + [518, "gp", "(inline-array prototype-bucket-shrub)"], + [223, "v1", "drawable-group"], + [277, "v1", "terrain-context"], + [344, "v1", "drawable-group"], + [398, "v1", "terrain-context"], + [[498, 507], "a1", "prototype-bucket-shrub"], + [540, "v1", "terrain-context"] + ], + + "(method 8 drawable-tree-instance-shrub)": [ + [54, "v1", "drawable-group"] + ], + + "draw-drawable-tree-instance-shrub": [ + [85, "a0", "drawable-group"] + ], + + "shrub-init-frame": [ + [[6, 12], "a0", "dma-packet"], + [[13, 21], "a0", "gs-gif-tag"], + [24, "v1", "(pointer gs-test)"], + [26, "v1", "(pointer gs-reg64)"] + ], + + "shrub-do-init-frame": [ + [[10, 21], "a0", "dma-packet"], + [[24, 29], "a0", "dma-packet"], + [33, "v1", "(pointer vif-tag)"], + [[35, 41], "v1", "(pointer uint32)"], + [42, "v1", "(pointer vif-tag)"], + [[43, 51], "v1", "(pointer uint32)"], + [52, "v1", "(pointer vif-tag)"], + [54, "v1", "(pointer uint32)"] + ], + + "shrub-upload-view-data": [ + [[3, 16], "a0", "dma-packet"] + ], + + "shrub-upload-model": [ + [[17, 26], "a3", "dma-packet"], + [[33, 41], "a0", "dma-packet"], + [[47, 55], "a0", "dma-packet"] + ], "placeholder-do-not-add-below": [] } diff --git a/decompiler/level_extractor/BspHeader.cpp b/decompiler/level_extractor/BspHeader.cpp index 34c76180e8..ea1b42ac08 100644 --- a/decompiler/level_extractor/BspHeader.cpp +++ b/decompiler/level_extractor/BspHeader.cpp @@ -135,6 +135,10 @@ std::unique_ptr make_draw_node_child(TypedRef ref, auto result = std::make_unique(); result->read_from_file(ref, dts, stats); return result; + } else if (ref.type->get_name() == "instance-shrubbery") { + auto result = std::make_unique(); + result->read_from_file(ref, dts, stats); + return result; } else { throw Error("Unknown child of draw node: {}\n", ref.type->get_name()); } @@ -149,6 +153,8 @@ int get_child_stride(const std::string& type) { return 64; } else if (type == "drawable-actor") { return 32; + } else if (type == "instance-shrubbery") { + return 80; } else { throw Error("unknown child for stride: {}", type); } @@ -752,6 +758,13 @@ std::unique_ptr make_drawable_inline_array( result->read_from_file(ref, dts, stats); return result; } + + if (ref.type->get_name() == "drawable-inline-array-instance-shrub") { + auto result = std::make_unique(); + result->read_from_file(ref, dts, stats); + return result; + } + auto result = std::make_unique(); result->read_from_file(ref, dts, stats); return result; @@ -872,7 +885,6 @@ void PrototypeBucketTie::read_from_file(TypedRef ref, ASSERT(flags == 0 || flags == 2); in_level = read_plain_data_field(ref, "in-level", dts); utextures = read_plain_data_field(ref, "utextures", dts); - // todo drawables dists.read_from_file(get_field_ref(ref, "dists", dts)); rdists.read_from_file(get_field_ref(ref, "rdists", dts)); stiffness = read_plain_data_field(ref, "stiffness", dts); @@ -1106,6 +1118,424 @@ std::string DrawableTreeInstanceTie::my_type() const { return "drawable-tree-instance-tie"; } +////////////////////////// +// shrub +////////////////////////// + +namespace shrub_types { + +void DrawableTreeInstanceShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + // the usual drawable stuff + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + // unfortunately, shrub uses the arrays thing differently. + // there's just one top level array, and the nodes are a bit scattered in memory below that. + // it doesn't have the 8 child rule + auto data_ref = get_field_ref(ref, "data", dts); + if ((data_ref.byte_offset % 4) != 0) { + throw Error("misaligned data array"); + } + for (int idx = 0; idx < length; idx++) { + Ref array_slot_ref = data_ref; + array_slot_ref.byte_offset += idx * 4; + + Ref object_ref = deref_label(array_slot_ref); + object_ref.byte_offset -= 4; + + arrays.push_back(make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } + // confirm that we have the weird shrub pattern and only found one array. + ASSERT(length == 1); + + // now, let's try to discover the remaining arrays (instances). + // basically we just look after the top level array in memory. + // once we find something else (the time of day palette) we know we're at the end. + // the game finds these by traversing the tree, but this is a little easier, and gets us + // the familiar arrays that we used in tie/tfrag. + + Ref object_ref = deref_label(data_ref); + object_ref.byte_offset -= 4; + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + + bool done = false; + object_ref.byte_offset += 16; + while (!done) { + auto& word = object_ref.data->words_by_seg.at(object_ref.seg).at(object_ref.byte_offset / 4); + if (word.kind() == decompiler::LinkedWord::TYPE_PTR) { + if (word.symbol_name() == "drawable-inline-array-node") { + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } else if (word.symbol_name() == "drawable-inline-array-instance-shrub") { + discovered_arrays.push_back( + make_drawable_inline_array(typed_ref_from_basic(object_ref, dts), dts, stats)); + } else if (word.symbol_name() == "time-of-day-palette") { + done = true; + } else { + ASSERT(word.symbol_name() == "draw-node" || word.symbol_name() == "instance-shrubbery"); + } + } + object_ref.byte_offset += 16; + } + + // this "info" thing holds all the prototypes + auto pt = deref_label(get_field_ref(ref, "info", dts)); + pt.byte_offset -= 4; + info.read_from_file(typed_ref_from_basic(pt, dts), dts, stats); + + // time of day palette. we'll want these colors in the FR3 file. + auto palette = deref_label(get_field_ref(ref, "colors-added", dts)); + time_of_day.width = deref_u32(palette, 0); + ASSERT(time_of_day.width == 8); + time_of_day.height = deref_u32(palette, 1); + time_of_day.pad = deref_u32(palette, 2); + ASSERT(time_of_day.pad == 0); + for (int i = 0; i < int(8 * time_of_day.height); i++) { + time_of_day.colors.push_back(deref_u32(palette, 3 + i)); + } +} + +std::string DrawableTreeInstanceShrub::my_type() const { + return "drawable-tree-instance-shrub"; +} + +std::string DrawableTreeInstanceShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + if (!settings.expand_shrub) { + return ""; + } + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + if (settings.expand_shrub) { + for (size_t i = 0; i < discovered_arrays.size(); i++) { + result += fmt::format("{}arrays [{}] ({}):\n", is, i, discovered_arrays[i]->my_type()); + result += discovered_arrays[i]->print(settings, next_indent); + } + + result += fmt::format("{}prototypes:\n", is); + result += info.print(settings, next_indent); + } + + return result; +} + +void InstanceShrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* /*stats*/) { + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + bucket_index = read_plain_data_field(ref, "bucket-index", dts); + id = read_plain_data_field(ref, "id", dts); + origin.read_from_file(get_field_ref(ref, "origin", dts)); + wind_index = read_plain_data_field(ref, "wind-index", dts); + color_indices = read_plain_data_field(ref, "color-indices", dts); + flat_normal.read_from_file(get_field_ref(ref, "flat-normal", dts)); +} + +std::string InstanceShrubbery::print(const level_tools::PrintSettings& /*settings*/, + int indent) const { + std::string is(indent, ' '); + std::string result; + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + result += fmt::format("{}bucket-index: {}\n", is, bucket_index); + result += fmt::format("{}flat-normal: {}", is, flat_normal.print_meters()); + result += fmt::format("{}color-indices: {}\n", is, color_indices); + result += fmt::format("{}wind-index: {}\n", is, wind_index); + return result; +} + +void DrawableInlineArrayInstanceShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) { + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < length; i++) { + Ref obj_ref = data_ref; + obj_ref.byte_offset += 80 * i; // todo not a constant here + auto type = get_type_of_basic(obj_ref); + if (type != "instance-shrubbery") { + throw Error("bad draw node type: {}", type); + } + instances.emplace_back(); + instances.back().read_from_file(typed_ref_from_basic(obj_ref, dts), dts, stats); + } +} + +std::string DrawableInlineArrayInstanceShrub::print(const PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + if (settings.expand_shrub) { + for (size_t i = 0; i < instances.size(); i++) { + result += fmt::format("{}draw-nodes [{}] ({}):\n", is, i, instances[i].my_type()); + result += instances[i].print(settings, next_indent); + } + } + + return result; +} + +void PrototypeArrayShrubInfo::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + prototype_inline_array_shrub.read_from_file( + get_and_check_ref_to_basic(ref, "prototype-inline-array-shrub", + "prototype-inline-array-shrub", dts), + dts, stats); + wind_vectors = deref_label(get_field_ref(ref, "wind-vectors", dts)); +} + +std::string PrototypeArrayShrubInfo::print(const level_tools::PrintSettings& settings, + int indent) const { + return prototype_inline_array_shrub.print(settings, indent); +} + +void PrototypeInlineArrayShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + length = read_plain_data_field(ref, "length", dts); + auto data_ref = get_field_ref(ref, "data", dts); + + for (int i = 0; i < length; i++) { + Ref thing = data_ref; + // note: unlike tie, these are stored in an inline array. + thing.byte_offset += 112 * i; // todo - not a constant here + auto type = get_type_of_basic(thing); + if (type != "prototype-bucket-shrub") { + throw Error("bad type in PrototypeInlineArrayShrub data: {}\n", type); + } + data.emplace_back(); + data.back().read_from_file(typed_ref_from_basic(thing, dts), dts, stats); + } +} + +std::string PrototypeGenericShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}length: {}\n", is, length); + + for (u32 i = 0; i < shrubs.size(); i++) { + result += fmt::format("{}data [{}]:\n", is, i); + result += shrubs[i].print(settings, next_indent); + } + return result; +} + +void PrototypeGenericShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < length; i++) { + Ref thing = data_ref; + // note: unlike tie, these are stored in an inline array. + thing.byte_offset += 4 * i; // 4 byte pointer + thing = deref_label(thing); + thing.byte_offset -= 4; // basic offset + auto type = get_type_of_basic(thing); + if (type != "generic-shrub-fragment") { + throw Error("bad type in PrototypeGenericShrub data: {}\n", type); + } + shrubs.emplace_back(); + shrubs.back().read_from_file(typed_ref_from_basic(thing, dts), dts, stats); + } +} + +std::string PrototypeInlineArrayShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}length: {}\n", is, length); + + for (u32 i = 0; i < data.size(); i++) { + result += fmt::format("{}data [{}]:\n", is, i); + result += data[i].print(settings, next_indent); + } + return result; +} + +void PrototypeBucketShrub::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + name = read_string_field(ref, "name", dts, true); + flags = read_plain_data_field(ref, "flags", dts); + if (flags) { + // lid in misty has flag 2, not sure what it means yet. + fmt::print("proto: {} flags: {}\n", name, flags); + } + ASSERT(flags == 0 || flags == 2); + in_level = read_plain_data_field(ref, "in-level", dts); + utextures = read_plain_data_field(ref, "utextures", dts); + dists.read_from_file(get_field_ref(ref, "dists", dts)); + rdists.read_from_file(get_field_ref(ref, "rdists", dts)); + stiffness = read_plain_data_field(ref, "stiffness", dts); + // 64 to 112 should be zeros + for (int i = 0; i < 12; i++) { + ASSERT(deref_u32(ref.ref, 16 + i) == 0); + } + + auto geom_start = get_field_ref(ref, "geometry", dts); + + // first geometry is generic and we should always have it. + auto generic_geom_l = deref_label(geom_start); + generic_geom_l.byte_offset -= 4; + if (get_type_of_basic(generic_geom_l) != "prototype-generic-shrub") { + throw Error("bad generic shrub type: {}", get_type_of_basic(generic_geom_l)); + } + geom_start.byte_offset += 4; + + // second is same data, but in prototype-shrubbery form (for normal shrub renderer) + auto normal_geom = deref_label(geom_start); + normal_geom.byte_offset -= 4; + if (get_type_of_basic(normal_geom) != "prototype-shrubbery") { + throw Error("bad normal shrub type: {}", get_type_of_basic(normal_geom)); + } + shrubbery_geom.read_from_file(typed_ref_from_basic(normal_geom, dts), dts, stats); + geom_start.byte_offset += 4; + + generic_geom.read_from_file(typed_ref_from_basic(generic_geom_l, dts), dts, stats); + + // todo transparent version + // todo billboard version. +} + +std::string PrototypeBucketShrub::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + result += fmt::format("{}name: {}\n", is, name); + result += fmt::format("{}flags: {}\n", is, flags); + + result += fmt::format("{}generic-geometry [0]:\n", is); + result += generic_geom.print(settings, indent + 4); + + result += fmt::format("{}normal-geometry [1]:\n", is); + result += shrubbery_geom.print(settings, indent + 4); + + return result; +} + +void PrototypeShrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) { + id = read_plain_data_field(ref, "id", dts); + length = read_plain_data_field(ref, "length", dts); + bsphere.read_from_file(get_field_ref(ref, "bsphere", dts)); + + auto data_ref = get_field_ref(ref, "data", dts); + for (int i = 0; i < length; i++) { + Ref obj_ref = data_ref; + obj_ref.byte_offset += 32 * i; // todo not a constant here + auto type = get_type_of_basic(obj_ref); + if (type != "shrubbery") { + throw Error("bad draw node type: {}", type); + } + shrubs.emplace_back(); + shrubs.back().read_from_file(typed_ref_from_basic(obj_ref, dts), dts, stats); + } +} + +std::string PrototypeShrubbery::print(const level_tools::PrintSettings& settings, + int indent) const { + std::string is(indent, ' '); + std::string result; + int next_indent = indent + 4; + result += fmt::format("{}id: {}\n", is, id); + result += fmt::format("{}length: {}\n", is, length); + result += fmt::format("{}bsphere: {}", is, bsphere.print_meters()); + + for (size_t i = 0; i < shrubs.size(); i++) { + result += fmt::format("{}draw-nodes [{}] ({}):\n", is, i, shrubs[i].my_type()); + result += shrubs[i].print(settings, next_indent); + } + + return result; +} + +void copy_dma_to_vector(std::vector* out, Ref data_start, int qwc) { + out->resize(qwc * 16); + for (int i = 0; i < qwc * 4; i++) { + u32 val = deref_u32(data_start, i); + memcpy(out->data() + i * 4, &val, 4); + } +} + +void Shrubbery::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* /*stats*/) { + // read the easy ones. + obj_qwc = read_plain_data_field(ref, "obj-qwc", dts); + vtx_qwc = read_plain_data_field(ref, "vtx-qwc", dts); + col_qwc = read_plain_data_field(ref, "col-qwc", dts); + stq_qwc = read_plain_data_field(ref, "stq-qwc", dts); + + auto header_data = deref_label(get_field_ref(ref, "header", dts)); + // guess that the header is 24 * 4 = 96 bytes here. + // not sure what it's used for yet. + header.resize(24); + for (int i = 0; i < 24; i++) { + u32 val = deref_u32(header_data, i); + memcpy(header.data() + i, &val, 4); + } + + copy_dma_to_vector(&obj, deref_label(get_field_ref(ref, "obj", dts)), obj_qwc); + copy_dma_to_vector(&vtx, deref_label(get_field_ref(ref, "vtx", dts)), vtx_qwc); + copy_dma_to_vector(&col, deref_label(get_field_ref(ref, "col", dts)), col_qwc); + copy_dma_to_vector(&stq, deref_label(get_field_ref(ref, "stq", dts)), stq_qwc); + copy_dma_to_vector(&textures, deref_label(get_field_ref(ref, "textures", dts)), header[0] * 10); +} + +std::string Shrubbery::print(const level_tools::PrintSettings& /*settings*/, int indent) const { + std::string is(indent, ' '); + + return fmt::format("{} qwcs: {} {} {} {}, tex: {}\n", is, obj_qwc, vtx_qwc, col_qwc, stq_qwc, + header[0] * 2); +} + +std::string GenericShrubFragment::print(const level_tools::PrintSettings& /*settings*/, + int indent) const { + std::string is(indent, ' '); + return fmt::format("{} qwcs: {} {} {} {}: 0x{:x}\n", is, cnt_qwc, vtx_qwc, col_qwc, stq_qwc, + vtx_cnt); +} + +void GenericShrubFragment::read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* /*stats*/) { + cnt_qwc = read_plain_data_field(ref, "cnt-qwc", dts); + vtx_qwc = read_plain_data_field(ref, "vtx-qwc", dts); + col_qwc = read_plain_data_field(ref, "col-qwc", dts); + stq_qwc = read_plain_data_field(ref, "stq-qwc", dts); + vtx_cnt = read_plain_data_field(ref, "vtx-cnt", dts); + + copy_dma_to_vector(&textures, deref_label(get_field_ref(ref, "textures", dts)), cnt_qwc); + copy_dma_to_vector(&vtx, deref_label(get_field_ref(ref, "vtx", dts)), vtx_qwc); + copy_dma_to_vector(&col, deref_label(get_field_ref(ref, "col", dts)), col_qwc); + copy_dma_to_vector(&stq, deref_label(get_field_ref(ref, "stq", dts)), stq_qwc); +} + +} // namespace shrub_types + std::unique_ptr make_drawable_tree(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats) { @@ -1150,6 +1580,12 @@ std::unique_ptr make_drawable_tree(TypedRef ref, tree->read_from_file(ref, dts, stats); return tree; } + + if (ref.type->get_name() == "drawable-tree-instance-shrub") { + auto tree = std::make_unique(); + tree->read_from_file(ref, dts, stats); + return tree; + } auto tree = std::make_unique(); tree->read_from_file(ref, dts, stats); return tree; diff --git a/decompiler/level_extractor/BspHeader.h b/decompiler/level_extractor/BspHeader.h index 6ee2de7e7b..1aae78f489 100644 --- a/decompiler/level_extractor/BspHeader.h +++ b/decompiler/level_extractor/BspHeader.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "decompiler/util/goal_data_reader.h" @@ -13,45 +14,16 @@ class DecompilerTypeSystem; } // namespace decompiler namespace level_tools { - -struct Vector { - float data[4]; - - void read_from_file(Ref ref); - - std::string print(int indent = 0) const; - std::string print_meters(int indent = 0) const; -}; - -struct Matrix4h { - u16 data[16]; - void read_from_file(Ref ref); -}; - -struct FileInfo { - std::string file_type; - std::string file_name; - u32 major_version; - u32 minor_version; - - std::string maya_file_name; - std::string tool_debug; - std::string mdb_file_name; - - void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts); - - std::string print(int indent = 0) const; -}; - struct PrintSettings { - bool print_tfrag = true; - bool expand_draw_node = true; - bool expand_drawable_tree_tfrag = true; - bool expand_drawable_tree_trans_tfrag = true; - bool expand_drawable_tree_tie_proto = true; + bool print_tfrag = false; + bool expand_draw_node = false; + bool expand_drawable_tree_tfrag = false; + bool expand_drawable_tree_trans_tfrag = false; + bool expand_drawable_tree_tie_proto = false; bool expand_drawable_tree_tie_proto_data = false; bool expand_drawable_tree_instance_tie = false; bool expand_drawable_tree_actor = false; + bool expand_shrub = true; }; struct DrawStats { @@ -66,6 +38,45 @@ struct DrawStats { std::string print() const; }; +///////////////////// +// Common Types +///////////////////// + +// a normal vector of 4 floats. +struct Vector { + float data[4]; + + void read_from_file(Ref ref); + + std::string print(int indent = 0) const; + std::string print_meters(int indent = 0) const; +}; + +// a matrix with 16-bit integers. +// typically requires some unpacking step to get meaningful values. +struct Matrix4h { + u16 data[16]; + void read_from_file(Ref ref); +}; + +// A time-of-day color palette. +// this is just the raw data, doesn't have any unpacking/meaning. +struct TimeOfDayPalette { + u32 width; + u32 height; + u32 pad; + std::vector colors; +}; + +///////////////////// +// Drawable BVH +///////////////////// + +// these types are all generic container types/base classes. +// note that the unpacker just greedily chases things and doesn't deduplicate, so comparing +// pointers for equality won't tell you if two things are the same game object or not. + +// the base class for everything in the BVH tree system. struct Drawable { virtual void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -75,8 +86,9 @@ struct Drawable { virtual ~Drawable() = default; }; -struct DrawableInlineArray : public Drawable {}; - +// a node in the BVH tree. It's just used for organizing things within a bsphere. +// these nodes are always in inline arrays and have between 0 and 8 children. +// the leaves of these nodes are some other type of drawable - the thing you actually want to draw. struct DrawNode : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -92,6 +104,53 @@ struct DrawNode : public Drawable { float distance = 0; }; +// an inline array of drawable. There are more specific types for the actual arrays. +struct DrawableInlineArray : public Drawable {}; + +// an inline array of draw nodes. All draw nodes at a level are stored in a drawable inline array. +struct DrawableInlineArrayNode : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector draw_nodes; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// the generic base class for a "tree". A tree is a BVH with between 1 and 8 DrawNode roots. +// there's typically a tree per renderer. +struct DrawableTree : public Drawable {}; + +// how we represent drawable trees that we don't support yet - this isn't a real type in the game. +struct DrawableTreeUnknown : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + std::string type_name; +}; + +struct DrawableInlineArrayUnknown : public DrawableInlineArray { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + std::string type_name; +}; + +///////////////////// +// Actors +///////////////////// + +// there's a tree for actors - but we don't do anything with it yet. + struct EntityActor {}; struct DrawableActor : public Drawable { @@ -107,6 +166,25 @@ struct DrawableActor : public Drawable { std::string my_type() const override { return "drawable-actor"; } }; +struct DrawableTreeActor : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; + s16 length; + // todo time of day stuff + Vector bsphere; + + std::vector> arrays; +}; + +///////////////////// +// TFRAG +///////////////////// + struct TFragmentDebugData { u16 num_tris[4]; u16 num_dverts[4]; @@ -116,6 +194,7 @@ struct TFragmentDebugData { void read_from_file(Ref ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats); }; +// the "fragment" is just a collection of data that fits into the VU memory. struct TFragment : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -150,6 +229,67 @@ struct TFragment : public Drawable { // generic // 60 - 64 }; +// array of tfragments. This is used as part of the BVH tree +struct DrawableInlineArrayTFrag : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector tfragments; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// a top-level tfragment tree. +// it has a BVH tree of tfragments as well as a time of day palette. +struct DrawableTreeTfrag : public DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; + s16 length; + TimeOfDayPalette time_of_day; + Vector bsphere; + + std::vector> arrays; +}; + +// various specializations of tfragment. + +struct DrawableTreeTransTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-trans-tfrag"; } +}; + +struct DrawableTreeLowresTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-lowres-tfrag"; } +}; + +struct DrawableTreeDirtTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-dirt-tfrag"; } +}; + +struct DrawableTreeIceTfrag : public DrawableTreeTfrag { + std::string my_type() const override { return "drawable-tree-ice-tfrag"; } +}; + +struct DrawableInlineArrayTransTFrag : public DrawableInlineArrayTFrag { + std::string my_type() const override { return "drawable-inline-array-trans-tfrag"; } +}; + +///////////////////// +// TIE +///////////////////// + +// a tie-fragment is a chunk of geometry that can be sent to the VU. +// it's similar to a tfragment, but a bit simpler. +// each prototype is made up of fragments. struct TieFragment : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -173,6 +313,9 @@ struct TieFragment : public Drawable { // todo, lots more }; +// represents an instance of a prototype. +// each instance has its own color data, but the rest of the geometry/texture is shared between +// all instances of the same prototype. struct InstanceTie : public Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -181,9 +324,9 @@ struct InstanceTie : public Drawable { std::string my_type() const override { return "instance-tie"; } // (bucket-index uint16 :offset 6) - u16 bucket_index; + u16 bucket_index; // which prototype s16 id; - Vector bsphere; + Vector bsphere; // where we are located Matrix4h origin; u16 flags; u16 wind_index; @@ -193,101 +336,7 @@ struct InstanceTie : public Drawable { // todo, lots more }; -struct DrawableInlineArrayNode : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector draw_nodes; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayTFrag : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector tfragments; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayInstanceTie : public DrawableInlineArray { - s16 id; - s16 length; - Vector bsphere; - - std::vector instances; - - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; -}; - -struct DrawableInlineArrayTransTFrag : public DrawableInlineArrayTFrag { - std::string my_type() const override { return "drawable-inline-array-trans-tfrag"; } -}; - -struct DrawableInlineArrayUnknown : public DrawableInlineArray { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - std::string type_name; -}; - -struct DrawableTree : public Drawable {}; - -struct TimeOfDayPalette { - u32 width; - u32 height; - u32 pad; - std::vector colors; -}; - -struct DrawableTreeTfrag : public DrawableTree { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - - s16 id; - s16 length; - // todo time of day stuff - TimeOfDayPalette time_of_day; - Vector bsphere; - - std::vector> arrays; -}; - -struct DrawableTreeActor : public DrawableTree { - void read_from_file(TypedRef ref, - const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - - s16 id; - s16 length; - // todo time of day stuff - Vector bsphere; - - std::vector> arrays; -}; - +// a prototype is basically just a collection of fragments struct PrototypeTie : public DrawableInlineArray { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -301,6 +350,9 @@ struct PrototypeTie : public DrawableInlineArray { std::vector tie_fragments; }; +// a prototype bucket is a collection of 4 different prototypes (called geometries), one for each +// level of detail. All geometries share the same time of day palette. +// the bucket also refers to the fact that it collect instances during actual rendering. struct PrototypeBucketTie { std::string name; // 4 - 8 u32 flags; // 8 - 12 @@ -337,6 +389,8 @@ struct PrototypeBucketTie { std::string print(const PrintSettings& settings, int indent) const; }; +// an array of all the prototypes. The prototypes aren't stored in a BVH (it wouldn't make sense - +// they can be located all over the place)- there is just a plain array. struct PrototypeArrayTie { u32 length; u32 allocated_length; @@ -347,6 +401,8 @@ struct PrototypeArrayTie { std::string print(const PrintSettings& settings, int indent) const; }; +// the reason for this type is somewhat unknown, but it just contains the prototype array and +// wind vectors. The wind vector data is all 0's and is updated as the game runs. struct ProxyPrototypeArrayTie { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, DrawStats* stats); std::string print(const PrintSettings& settings, int indent) const; @@ -355,6 +411,22 @@ struct ProxyPrototypeArrayTie { Ref wind_vectors; }; +// array of instances. These are part of the BVH +struct DrawableInlineArrayInstanceTie : public DrawableInlineArray { + s16 id; + s16 length; + Vector bsphere; + + std::vector instances; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + DrawStats* stats) override; + std::string print(const PrintSettings& settings, int indent) const override; + std::string my_type() const override; +}; + +// TIE tree struct DrawableTreeInstanceTie : public DrawableTree { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, @@ -370,31 +442,211 @@ struct DrawableTreeInstanceTie : public DrawableTree { std::vector> arrays; }; -struct DrawableTreeTransTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-trans-tfrag"; } -}; +///////////////////////////////// +// SHRUB +///////////////////////////////// -struct DrawableTreeLowresTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-lowres-tfrag"; } -}; +namespace shrub_types { -struct DrawableTreeDirtTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-dirt-tfrag"; } -}; - -struct DrawableTreeIceTfrag : public DrawableTreeTfrag { - std::string my_type() const override { return "drawable-tree-ice-tfrag"; } -}; - -struct DrawableTreeUnknown : public DrawableTree { +struct Shrubbery : public level_tools::Drawable { void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts, - DrawStats* stats) override; - std::string print(const PrintSettings& settings, int indent) const override; - std::string my_type() const override; - std::string type_name; + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "shrubbery"; } + + // 4 - textures + std::vector textures; + + std::vector header; + u8 obj_qwc; // 12 + u8 vtx_qwc; // 13 + u8 col_qwc; // 14 + u8 stq_qwc; // 15 + + std::vector obj; // 16 + std::vector vtx; // 20 + std::vector col; // 24 + std::vector stq; // 28 }; +struct PrototypeShrubbery : public level_tools::DrawableInlineArray { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "prototype-shrubbery"; } + s16 id; // 0 + s16 length; // 4 + + level_tools::Vector bsphere; // 16 + std::vector shrubs; // 32 +}; + +struct Billboard {}; + +struct GenericShrubFragment : public level_tools::Drawable { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "generic-shrub-fragment"; } + + // + // (textures (inline-array adgif-shader) :score 999 :offset 4) + std::vector textures; + // (vtx-cnt uint32 :score 999 :offset 8) + u32 vtx_cnt; + // (cnt-qwc uint8 :score 999 :offset 12) + u8 cnt_qwc; + // (vtx-qwc uint8 :score 999 :offset 13) + u8 vtx_qwc; + // (col-qwc uint8 :score 999 :offset 14) + u8 col_qwc; + // (stq-qwc uint8 :score 999 :offset 15) + u8 stq_qwc; + // (cnt uint32 :score 999 :offset 16) + std::vector cnt; + // (vtx uint32 :score 999 :offset 20) + std::vector vtx; + // (col uint32 :score 999 :offset 24) + std::vector col; + // (stq uint32 :score 999 :offset 28) + std::vector stq; +}; + +// really a drawable-group, but we'll make it drawable here. +struct PrototypeGenericShrub : public level_tools::Drawable { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "prototype-generic-shrub"; } + s16 length; // 4 + + level_tools::Vector bsphere; // 16 + std::vector shrubs; // 32 +}; + +struct PrototypeBucketShrub { + std::string name; // 4 + u32 flags; // 8 + u16 in_level; // 12 + u16 utextures; // 14 + + // PrototypeShrubbery geometry[4]; // 16 + PrototypeGenericShrub generic_geom; // 0 + PrototypeShrubbery shrubbery_geom; // 1 + // todo transparent geom + // todo billboard geom + + level_tools::Vector dists; // 32 + // - near-plane + // - near-stiff + // - mid-plane + // - far-plane + level_tools::Vector rdists; // 48 + // - rlength-near + // - rlength-stiff + // - rlength-mid + // - stiffness + + float stiffness; // - 60 + + // the next/last/count/mod stuff is all 0's. + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; +}; + +struct PrototypeInlineArrayShrub { + s16 id; // 4 + s16 length; // 6 + level_tools::Vector bsphere; // 16 + std::vector data; // 32 + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; +}; + +struct PrototypeArrayShrubInfo { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats); + std::string print(const level_tools::PrintSettings& settings, int indent) const; + + PrototypeInlineArrayShrub prototype_inline_array_shrub; + Ref wind_vectors; +}; + +struct InstanceShrubbery : public level_tools::Drawable { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "instance-shrubbery"; } + + s16 id; // 4 + u16 bucket_index; // 6 + level_tools::Vector bsphere; // 16 + level_tools::Matrix4h origin; // 32 + u16 wind_index; // 62 + + // --- instance-shrubbery --- + u32 color_indices; // 8 - unlike tie, I think this is just an integer offset + level_tools::Vector flat_normal; // 64 (w is flat_hwidth) +}; + +struct DrawableInlineArrayInstanceShrub : public level_tools::DrawableInlineArray { + s16 id; + s16 length; + level_tools::Vector bsphere; + + std::vector instances; + + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override { return "drawable-inline-array-instance-shrub"; } +}; + +struct DrawableTreeInstanceShrub : public level_tools::DrawableTree { + void read_from_file(TypedRef ref, + const decompiler::DecompilerTypeSystem& dts, + level_tools::DrawStats* stats) override; + std::string print(const level_tools::PrintSettings& settings, int indent) const override; + std::string my_type() const override; + + s16 id; // 0 + s16 length; // 4 + PrototypeArrayShrubInfo info; // 8 + TimeOfDayPalette time_of_day; + level_tools::Vector bsphere; // 16 + + std::vector> arrays; + + // annoyingly, the shrub tree only has the top level in the array.... + // so we can't use the above "arrays" like we did in tfrag/tie. + // luckily, it seems to be possible to figure out the location of the remaining arrays. + // so we add this field to hold the arrays we found. + // note that the tree format is not quite the same as tfrag/tie - the 8 child max is no longer + // true, and the arrays are not truly by depth (the leaves aren't all the same depth) + // this means there may be multiple arrays of instances, and we'll need to check all of them. + std::vector> discovered_arrays; +}; + +} // namespace shrub_types + +//////////////////////////////// +// Main Level Type (bsp-header) +//////////////////////////////// + +// all the different trees are stored in a drawable-tree-array. struct DrawableTreeArray { s16 id; s16 length; @@ -406,11 +658,28 @@ struct DrawableTreeArray { std::vector> trees; }; +// levels may remap textures if they provide one that should be shared struct TextureRemap { u32 original_texid; u32 new_texid; }; +// The "file info" +struct FileInfo { + std::string file_type; + std::string file_name; + u32 major_version; + u32 minor_version; + + std::string maya_file_name; + std::string tool_debug; + std::string mdb_file_name; + + void read_from_file(TypedRef ref, const decompiler::DecompilerTypeSystem& dts); + + std::string print(int indent = 0) const; +}; + struct BspHeader { // (info file-info :offset 4) FileInfo file_info; diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 84165e828f..9a7c985b12 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -4,6 +4,7 @@ #include "decompiler/level_extractor/BspHeader.h" #include "decompiler/level_extractor/extract_tfrag.h" #include "decompiler/level_extractor/extract_tie.h" +#include "decompiler/level_extractor/extract_shrub.h" #include "common/util/compress.h" #include "common/util/FileUtil.h" @@ -182,6 +183,12 @@ void extract_from_level(ObjectFileDB& db, bsp_header.read_from_file(bsp_file.linked_data, db.dts, &draw_stats); ASSERT((int)bsp_header.drawable_tree_array.trees.size() == bsp_header.drawable_tree_array.length); + /* + level_tools::PrintSettings settings; + settings.expand_shrub = true; + fmt::print("{}\n", bsp_header.print(settings)); + */ + const std::set tfrag_trees = { "drawable-tree-tfrag", "drawable-tree-trans-tfrag", "drawable-tree-dirt-tfrag", "drawable-tree-ice-tfrag", "drawable-tree-lowres-tfrag", "drawable-tree-lowres-trans-tfrag"}; @@ -207,6 +214,12 @@ void extract_from_level(ObjectFileDB& db, ASSERT(as_tie_tree); extract_tie(as_tie_tree, fmt::format("{}-{}-tie", dgo_name, i++), bsp_header.texture_remap_table, tex_db, tfrag_level, dump_level); + } else if (draw_tree->my_type() == "drawable-tree-instance-shrub") { + auto as_shrub_tree = + dynamic_cast(draw_tree.get()); + ASSERT(as_shrub_tree); + extract_shrub(as_shrub_tree, fmt::format("{}-{}-shrub", dgo_name, i++), + bsp_header.texture_remap_table, tex_db, {}, tfrag_level, dump_level); } else { // fmt::print(" unsupported tree {}\n", draw_tree->my_type()); } diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp new file mode 100644 index 0000000000..06a4c9491f --- /dev/null +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -0,0 +1,292 @@ +#include + +#include "extract_shrub.h" + +#include "decompiler/ObjectFile/LinkedObjectFile.h" + +#include "common/util/FileUtil.h" + +namespace decompiler { +using namespace level_tools; + +std::array extract_shrub_matrix(const u16* data) { + std::array result; + for (int i = 0; i < 4; i++) { + s32 x = data[12 + i]; + x <<= 16; + x >>= 10; + result[3][i] = x; + } + + for (int vec = 0; vec < 3; vec++) { + for (int i = 0; i < 4; i++) { + s32 x = data[vec * 4 + i]; + x <<= 16; + x >>= 16; + result[vec][i] = (float)x / 4096.f; + } + } + + return result; +} + +struct ShrubVertex { + math::Vector xyz; + math::Vector st; + math::Vector rgba_generic; + bool adc = false; +}; + +struct ShrubDraw { + u32 start_vtx_idx = -1; + AdGifData adgif; + std::vector vertices; +}; + +struct ShrubFrag { + std::vector draws; +}; + +struct ShrubInstanceInfo { + u32 proto_idx; + u32 color_idx; + std::array mat; + math::Vector4f bsphere; +}; + +struct ShrubProtoInfo { + std::vector frags; + std::vector instances; +}; + +std::string debug_dump_proto_to_obj(const ShrubProtoInfo& proto) { + std::vector> verts; + std::vector> tcs; + std::vector> faces; + + for (auto& frag : proto.frags) { + for (auto& strip : frag.draws) { + // add verts... + ASSERT(strip.vertices.size() >= 3); + + int vert_idx = 0; + + int vtx_idx_queue[3]; + + int q_idx = 0; + int startup = 0; + while (vert_idx < (int)strip.vertices.size()) { + verts.push_back(strip.vertices.at(vert_idx).xyz / 65536); // no idea + tcs.push_back(math::Vector{strip.vertices.at(vert_idx).st.x(), + strip.vertices.at(vert_idx).st.y()}); + + vtx_idx_queue[q_idx++] = verts.size(); + + // wrap the index + if (q_idx == 3) { + q_idx = 0; + } + + // bump the startup + if (startup < 3) { + startup++; + } + + if (startup >= 3 && strip.vertices.at(vert_idx).adc) { + faces.push_back( + math::Vector{vtx_idx_queue[0], vtx_idx_queue[1], vtx_idx_queue[2]}); + } + vert_idx++; + } + } + } + + std::string result; + for (auto& vert : verts) { + result += fmt::format("v {} {} {}\n", vert.x(), vert.y(), vert.z()); + } + for (auto& tc : tcs) { + result += fmt::format("vt {} {}\n", tc.x(), tc.y()); + } + for (auto& face : faces) { + result += fmt::format("f {}/{} {}/{} {}/{}\n", face.x(), face.x(), face.y(), face.y(), face.z(), + face.z()); + } + + return result; +} + +ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto) { + ShrubProtoInfo result; + for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) { + auto& frag_out = result.frags.emplace_back(); + auto& frag = proto.generic_geom.shrubs.at(frag_idx); + + std::vector adgif_data; + adgif_data.resize(frag.textures.size() / sizeof(AdGifData)); + memcpy(adgif_data.data(), frag.textures.data(), frag.textures.size()); + for (size_t i = 0; i < adgif_data.size(); i++) { + auto& draw = frag_out.draws.emplace_back(); + + const auto& ag = adgif_data[i]; + int count = (ag.tex1_addr >> 32) & 0xfff; // drop the eop flag + draw.start_vtx_idx = ((ag.tex0_addr >> 32) & 0xffff) / 3; + + if (i > 0) { + auto& prev_draw = frag_out.draws[frag_out.draws.size() - 2]; + ASSERT(prev_draw.start_vtx_idx + prev_draw.vertices.size() + 3 == draw.start_vtx_idx); + } + + for (int vert_idx = 0; vert_idx < count; vert_idx++) { + auto& vert_out = draw.vertices.emplace_back(); + s16 vert_data[3]; + memcpy(vert_data, frag.vtx.data() + sizeof(u16) * 3 * (vert_idx + draw.start_vtx_idx), + 3 * sizeof(u16)); + vert_out.xyz = math::Vector3f(vert_data[0], vert_data[1], vert_data[2]); + + u16 st_data[2]; + memcpy(st_data, frag.stq.data() + sizeof(u16) * 2 * (vert_idx + draw.start_vtx_idx), + 2 * sizeof(u16)); + vert_out.st = math::Vector2f(st_data[0], st_data[1]); + vert_out.adc = (st_data[0] & 1) == 0; // adc in the low bit of texture coordinate + + memcpy(vert_out.rgba_generic.data(), frag.col.data() + 4 * (vert_idx + draw.start_vtx_idx), + 4); + } + } + + ASSERT(frag.vtx_cnt * 3 * sizeof(u16) <= frag.vtx.size()); + } + + /* + file_util::write_text_file( + file_util::get_file_path({fmt::format("debug_out/shrub/{}.obj", proto.name)}), + debug_dump_proto_to_obj(result)); + */ + return result; +} + +void extract_instance(const shrub_types::InstanceShrubbery& inst, + std::vector& protos) { + ShrubInstanceInfo result; + result.proto_idx = inst.bucket_index; + for (int i = 0; i < 4; i++) { + result.bsphere[i] = inst.bsphere.data[i]; + } + + // from ee asm + result.mat = extract_shrub_matrix(inst.origin.data); + result.mat[3][0] += result.bsphere[0]; + result.mat[3][1] += result.bsphere[1]; + result.mat[3][2] += result.bsphere[2]; + // result.wind_index = instance.wind_index; + + result.mat[0][3] = 0.f; + + protos.at(result.proto_idx).instances.push_back(result); +} + +/*! + * Transform a point in a prototype to the actual point location in the game world. + */ +math::Vector transform_shrub(const std::array mat, + const math::Vector3f& pt) { + auto temp = mat[0] * pt.x() + mat[1] * pt.y() + mat[2] * pt.z() + mat[3]; + math::Vector3f result; + result.x() = temp.x(); + result.y() = temp.y(); + result.z() = temp.z(); + return result; +} + +/*! + * Dump the entire tie tree to an obj. Used to debug the transform_tie function. If we get this + * right, it should fit in with .obj's produced from the tfrag debug. + */ +std::string dump_full_to_obj(const std::vector& protos) { + std::vector> verts; + std::vector> faces; + + for (auto& proto : protos) { + for (auto& inst : proto.instances) { + auto& mat = inst.mat; + for (auto& frag : proto.frags) { + for (auto& strip : frag.draws) { + // add verts... + ASSERT(strip.vertices.size() >= 3); + + int vert_idx = 0; + + int vtx_idx_queue[3]; + + int q_idx = 0; + int startup = 0; + while (vert_idx < (int)strip.vertices.size()) { + verts.push_back(transform_shrub(mat, strip.vertices.at(vert_idx).xyz) / + 65536); // no idea + + vtx_idx_queue[q_idx++] = verts.size(); + + // wrap the index + if (q_idx == 3) { + q_idx = 0; + } + + // bump the startup + if (startup < 3) { + startup++; + } + + if (startup >= 3 && strip.vertices.at(vert_idx).adc) { + faces.push_back( + math::Vector{vtx_idx_queue[0], vtx_idx_queue[1], vtx_idx_queue[2]}); + } + vert_idx++; + } + } + } + } + } + + std::string result; + for (auto& vert : verts) { + result += fmt::format("v {} {} {}\n", vert.x(), vert.y(), vert.z()); + } + + for (auto& face : faces) { + result += fmt::format("f {}/{} {}/{} {}/{}\n", face.x(), face.x(), face.y(), face.y(), face.z(), + face.z()); + } + + return result; +} + +void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, + const std::string& debug_name, + const std::vector& /*map*/, + const TextureDB& /*tex_db*/, + const std::vector>& /*expected_missing_textures*/, + tfrag3::Level& /*out*/, + bool dump_level) { + auto& protos = tree->info.prototype_inline_array_shrub; + std::vector proto_info; + for (auto& proto : protos.data) { + proto_info.push_back(extract_proto(proto)); + } + + for (auto& arr : tree->discovered_arrays) { + auto as_shrubs = dynamic_cast(arr.get()); + if (as_shrubs) { + for (auto& inst : as_shrubs->instances) { + extract_instance(inst, proto_info); + } + } + } + + if (dump_level) { + auto path = file_util::get_file_path({fmt::format("debug_out/shrub_all/{}.obj", debug_name)}); + file_util::create_dir_if_needed_for_file(path); + file_util::write_text_file(path, dump_full_to_obj(proto_info)); + } +} +} // namespace decompiler diff --git a/decompiler/level_extractor/extract_shrub.h b/decompiler/level_extractor/extract_shrub.h new file mode 100644 index 0000000000..772615ea92 --- /dev/null +++ b/decompiler/level_extractor/extract_shrub.h @@ -0,0 +1,28 @@ +#pragma once + +#include "decompiler/level_extractor/BspHeader.h" +#include "common/math/Vector.h" +#include "common/custom_data/Tfrag3Data.h" +#include "decompiler/data/TextureDB.h" + +namespace decompiler { + +/// +/// Extract shrubs from the level +/// +/// +/// +/// +/// +/// +/// +/// +void extract_shrub(const level_tools::shrub_types::DrawableTreeInstanceShrub* tree, + const std::string& debug_name, + const std::vector& map, + const TextureDB& tex_db, + const std::vector>& expected_missing_textures, + tfrag3::Level& out, + bool dump_level); + +} // namespace decompiler diff --git a/decompiler/level_extractor/extract_tie.cpp b/decompiler/level_extractor/extract_tie.cpp index 58d47e4a78..44663efc6b 100644 --- a/decompiler/level_extractor/extract_tie.cpp +++ b/decompiler/level_extractor/extract_tie.cpp @@ -8,9 +8,11 @@ namespace decompiler { -/*! - * Get the index of the first draw node in an array. Works for node or tfrag. - */ +/// +/// Get the index of the first draw node in an array. Works for node or tfrag. +/// +/// +/// u16 get_first_idx(const level_tools::DrawableInlineArray* array) { auto as_tie_instances = dynamic_cast(array); auto as_nodes = dynamic_cast(array); @@ -23,10 +25,14 @@ u16 get_first_idx(const level_tools::DrawableInlineArray* array) { } } -/*! - * Verify node indices follow the patterns we expect. Takes start as the expected first, - * writes the end. - */ +/// +/// Verify node indices follow the patterns we expect. Takes start as the expected first, writes the +/// end. +/// +/// +/// +/// +/// bool verify_node_indices_from_array(const level_tools::DrawableInlineArray* array, u16 start, u16* end) { diff --git a/docs/scratch/shrub_asm.md b/docs/scratch/shrub_asm.md index 6bafa8fffd..ad9d8217ff 100644 --- a/docs/scratch/shrub_asm.md +++ b/docs/scratch/shrub_asm.md @@ -32,7 +32,7 @@ Like with tfrag/tie, we will do the time of day interpolation in C++. The shrubs without wind effect will be converted into a single giant mesh. Doing it as a single mesh reduces the number of draw calls, and the entire mesh can be left in GPU memory the whole time. -The shrubs with wind effect wirgll be drawn as individual instances, as different shrubs need different wind matrices. It's likely going to be similar to `render_tree_wind`. +The shrubs with wind effect will be drawn as individual instances, as different shrubs need different wind matrices. It's likely going to be similar to `render_tree_wind`. The time-of-day effect will be done like in tfrag/tie. We will create a new time of day texture on each frame, based on the current time, and each vertex will index into a single large texture. This approach is nice because the interpolation/upload can be done in a single large batch. @@ -1194,25 +1194,25 @@ L86: vmadday.xyzw acc, vf29, vf12 sh t8, 6536(t0) vmaddz.xyzw vf12, vf30, vf12 - lw t4, 12(a2) + lw t4, 12(a2) ;; load the generic geometry? vmulax.xyzw acc, vf28, vf13 lw t5, 6532(t0) vmadday.xyzw acc, vf29, vf13 - lh t6, 2(t4) + lh t6, 2(t4) ;; generic frag count. vmaddaz.xyzw acc, vf30, vf13 lw a2, 6528(t0) vmaddw.xyzw vf13, vf31, vf0 lw t7, 6516(t0) vitof0.xyz vf24, vf24 sh t8, 6368(t0) -B58: +B58: ;; generic loop L87: daddiu t8, a3, -115 sll r0, r0, 0 blez t8, L90 - lw t8, 28(t4) + lw t8, 28(t4) ;; load the frag -B59: +B59: ;; dma L88: lw t3, 0(a0) sll r0, r0, 0 @@ -1272,7 +1272,7 @@ L91: daddiu a3, a3, 12 sqc2 vf12, 48(t3) sll r0, r0, 0 - lw ra, 4(t8) + lw ra, 4(t8) ;; ra = vtx-cnt sll r0, r0, 0 sqc2 vf13, 64(t3) sll r0, r0, 0 @@ -1280,9 +1280,9 @@ L91: sll r0, r0, 0 sw ra, 96(t3) sll r0, r0, 0 - lw ra, 12(t8) + lw ra, 12(t8) ;; ra = cnt sll r0, r0, 0 - lbu gp, 8(t8) + lbu gp, 8(t8) ;; gp = cnt-qwc sll r0, r0, 0 sw ra, 20(t9) sll r0, r0, 0 @@ -1290,25 +1290,25 @@ L91: sll r0, r0, 0 sb gp, 30(t9) sll r0, r0, 0 - lw ra, 24(t8) + lw ra, 24(t8) ;; ra = stq sll r0, r0, 0 - lbu gp, 11(t8) + lbu gp, 11(t8) ;; gp = stq-qwc sll r0, r0, 0 sw ra, 36(t9) sll r0, r0, 0 sb gp, 32(t9) sll r0, r0, 0 - lw ra, 20(t8) + lw ra, 20(t8) ;; ra = col sll r0, r0, 0 - lbu gp, 10(t8) + lbu gp, 10(t8) ;; gp = col-qwc sll r0, r0, 0 sw ra, 52(t9) sll r0, r0, 0 sb gp, 48(t9) sll r0, r0, 0 - lw ra, 16(t8) + lw ra, 16(t8) ;; ra = vtx sll r0, r0, 0 - lbu gp, 9(t8) + lbu gp, 9(t8) ;; gp = vtx-qwc sll r0, r0, 0 sw ra, 68(t9) sll r0, r0, 0 @@ -1483,4 +1483,4 @@ L98: sll r0, r0, 0 sll r0, r0, 0 sll r0, r0, 0 -``` \ No newline at end of file +``` diff --git a/game/graphics/opengl_renderer/Loader.cpp b/game/graphics/opengl_renderer/Loader.cpp index df07d0fd84..5427602881 100644 --- a/game/graphics/opengl_renderer/Loader.cpp +++ b/game/graphics/opengl_renderer/Loader.cpp @@ -428,7 +428,7 @@ bool Loader::upload_textures(Timer& timer, LevelData& data, TexturePool& texture return data.textures.size() == data.level->textures.size(); } -void Loader::update_blocking(std::string& status_out, TexturePool& tex_pool) { +void Loader::update_blocking(TexturePool& tex_pool) { fmt::print("NOTE: coming out of blackout on next frame, doing all loads now...\n"); bool missing_levels = true; @@ -457,7 +457,7 @@ void Loader::update_blocking(std::string& status_out, TexturePool& tex_pool) { } if (needs_run) { - update(status_out, tex_pool); + update(tex_pool); } } @@ -484,7 +484,7 @@ void Loader::update_blocking(std::string& status_out, TexturePool& tex_pool) { } } -void Loader::update(std::string& status_out, TexturePool& texture_pool) { +void Loader::update(TexturePool& texture_pool) { Timer loader_timer; // only main thread can touch this. diff --git a/game/graphics/opengl_renderer/Loader.h b/game/graphics/opengl_renderer/Loader.h index bad9e933da..adee94f30b 100644 --- a/game/graphics/opengl_renderer/Loader.h +++ b/game/graphics/opengl_renderer/Loader.h @@ -15,8 +15,8 @@ class Loader { static constexpr float SHARED_TEXTURE_LOAD_BUDGET = 3.f; Loader(); ~Loader(); - void update(std::string& status_out, TexturePool& tex_pool); - void update_blocking(std::string& status_out, TexturePool& tex_pool); + void update(TexturePool& tex_pool); + void update_blocking(TexturePool& tex_pool); struct LevelData { std::unique_ptr level; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index af93a866ab..730018e22c 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -291,11 +291,10 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) { auto prof = m_profiler.root()->make_scoped_child("loader"); if (m_last_pmode_alp == 0 && settings.pmode_alp_register != 0 && m_enable_fast_blackout_loads) { // blackout, load everything and don't worry about frame rate - m_render_state.loader->update_blocking(m_render_state.load_status_debug, - *m_render_state.texture_pool); + m_render_state.loader->update_blocking(*m_render_state.texture_pool); } else { - m_render_state.loader->update(m_render_state.load_status_debug, *m_render_state.texture_pool); + m_render_state.loader->update(*m_render_state.texture_pool); } } diff --git a/goal_src/engine/gfx/background.gc b/goal_src/engine/gfx/background.gc index 66534387d0..1123f746c4 100644 --- a/goal_src/engine/gfx/background.gc +++ b/goal_src/engine/gfx/background.gc @@ -199,7 +199,7 @@ ;; shrubbery ;;;;;;;;;;;;;;;; -#| + (set! (-> *instance-shrub-work* paused) (paused?)) (when (nonzero? (-> *background-work* shrub-tree-count)) (if *debug-segment* @@ -217,14 +217,14 @@ (let ((s5-0 (-> *background-work* shrub-trees gp-0)) (s4-0 (-> *background-work* shrub-levels gp-0)) ) - ;; setup colors for shrub - (if (nonzero? (-> s5-0 colors-added)) - (time-of-day-interp-colors - (-> *instance-shrub-work* colors) - (-> s5-0 colors-added) - (-> s4-0 mood) - ) - ) + ;; setup colors for shrub (skipping because I don't think we'll need it here...) + ; (if (nonzero? (-> s5-0 colors-added)) + ; (time-of-day-interp-colors + ; (-> *instance-shrub-work* colors) + ; (-> s5-0 colors-added) + ; (-> s4-0 mood) + ; ) + ; ) ;; and draw! (draw-drawable-tree-instance-shrub s5-0 s4-0) ) @@ -237,7 +237,7 @@ ) ) ) -|# + (let ((gp-1 (the-as level #f))) diff --git a/goal_src/engine/gfx/shrub/shrub-work.gc b/goal_src/engine/gfx/shrub/shrub-work.gc index 3474ad33d6..e005b882a3 100644 --- a/goal_src/engine/gfx/shrub/shrub-work.gc +++ b/goal_src/engine/gfx/shrub/shrub-work.gc @@ -7,6 +7,7 @@ ;; DECOMP BEGINS +;; definition for symbol *instance-shrub-work*, type instance-shrub-work (define *instance-shrub-work* (new 'static 'instance-shrub-work @@ -34,27 +35,27 @@ (new 'static 'qword :data (new 'static 'array uint32 4 #x10000005 #x0 #x0 #x6c0501a3)) ) :count-tmpl - (new 'static 'inline-array qword 20 - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #xa)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x1)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x2)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x3)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x4)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x5)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x6)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x7)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x8)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x9)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #xa)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x1)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x2)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x3)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x4)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x5)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x6)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x7)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x8)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x9)) + (new 'static 'inline-array vector4w 20 + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 10) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 1) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 2) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 3) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 4) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 5) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 6) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 7) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 8) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 9) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 10) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 1) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 2) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 3) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 4) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 5) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 6) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 7) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 8) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 9) ) :mscalf-tmpl (new 'static 'dma-packet @@ -477,10 +478,14 @@ ) ) +;; failed to figure out what this is: (set! (-> *instance-shrub-work* mscalf-tmpl vif0 imm) 103) +;; failed to figure out what this is: (set! (-> *instance-shrub-work* mscalf-ret-tmpl vif0 imm) 103) +;; definition for function upload-generic-shrub +;; Used lq/sq (defun upload-generic-shrub ((arg0 dma-buffer) (arg1 generic-shrub-fragment) (arg2 int) (arg3 int)) (let* ((v1-0 arg0) (t0-0 (the-as object (-> v1-0 base))) diff --git a/goal_src/engine/gfx/shrub/shrubbery-h.gc b/goal_src/engine/gfx/shrub/shrubbery-h.gc index a7e6eec276..232ce53c28 100644 --- a/goal_src/engine/gfx/shrub/shrubbery-h.gc +++ b/goal_src/engine/gfx/shrub/shrubbery-h.gc @@ -15,7 +15,7 @@ (deftype shrub-view-data (structure) ((data uint128 3 :offset-assert 0) - (texture-giftag qword :inline :offset 0) + (texture-giftag gs-gif-tag :inline :offset 0) ;; was qword (consts vector :inline :offset 16) (fog-clamp vector :inline :offset 32) (tex-start-ptr int32 :offset 16) @@ -50,7 +50,8 @@ ) (deftype instance-shrubbery (instance) - ((flat-normal vector :inline :offset-assert 64) + ((color-indices uint32 :offset 8) ;; added + (flat-normal vector :inline :offset-assert 64) (flat-hwidth float :offset 76) (color uint32 :offset 8) ) @@ -70,7 +71,7 @@ (deftype drawable-tree-instance-shrub (drawable-tree) ((info prototype-array-shrub-info :offset 8) - (colors-added uint32 :offset 12) ;; added + (colors-added time-of-day-palette :offset 12) ;; added ) :method-count-assert #x12 :size-assert #x24 @@ -194,66 +195,70 @@ ) (deftype instance-shrub-work (structure) - ((dummy qword 3 :inline :offset-assert 0) - (chaina qword 8 :inline :offset-assert 48) - (chainb qword 8 :inline :offset-assert 176) - (colors rgba 1024 :offset-assert 304) - (matrix-tmpl qword 20 :inline :offset-assert 4400) - (count-tmpl qword 20 :inline :offset-assert 4720) - (mscalf-tmpl dma-packet :inline :offset-assert 5040) - (mscalf-ret-tmpl dma-packet :inline :offset-assert 5056) - (adgif-tmpl dma-gif-packet :inline :offset-assert 5072) - (billboard-tmpl dma-gif-packet :inline :offset-assert 5104) - (billboard-const vector :inline :offset-assert 5136) - (shrub-near-packets shrub-near-packet 6 :inline :offset-assert 5152) - (dma-ref dma-packet :inline :offset-assert 6016) - (dma-end dma-packet :inline :offset-assert 6032) - (wind-const vector :inline :offset-assert 6048) - (constants vector :inline :offset-assert 6064) - (color-constant vector4w :inline :offset-assert 6080) - (hmge-d vector :inline :offset-assert 6096) - (hvdf-offset vector :inline :offset-assert 6112) - (wind-force vector :inline :offset-assert 6128) - (color vector :inline :offset-assert 6144) - (bb-color vector :inline :offset-assert 6160) - (min-dist vector :inline :offset-assert 6176) - (temp-vec vector :inline :offset-assert 6192) - (guard-plane plane 4 :inline :offset-assert 6208) - (plane plane 4 :inline :offset-assert 6272) - (last uint32 4 :offset-assert 6336) - (next uint32 4 :offset-assert 6352) - (count uint16 4 :offset-assert 6368) - (mod-count uint16 4 :offset-assert 6376) - (wind-vectors uint32 :offset-assert 6384) - (instance-ptr uint32 :offset-assert 6388) - (chain-ptr uint32 :offset-assert 6392) - (chain-ptr-next uint32 :offset-assert 6396) - (stack-ptr uint32 :offset-assert 6400) - (bucket-ptr uint32 :offset-assert 6404) - (src-ptr uint32 :offset-assert 6408) - (to-spr uint32 :offset-assert 6412) - (from-spr uint32 :offset-assert 6416) - (shrub-count uint32 :offset-assert 6420) - (node uint32 6 :offset 6428) - (length uint32 6 :offset-assert 6452) - (prototypes uint32 :offset-assert 6476) - (start-bank uint8 20 :offset 6484) - (buffer-index uint32 :offset-assert 6504) - (current-spr uint32 :offset-assert 6508) - (current-mem uint32 :offset-assert 6512) - (current-shrub-near-packet uint32 :offset-assert 6516) - (dma-buffer basic :offset 6524) - (near-last uint32 :offset-assert 6528) - (near-next uint32 :offset-assert 6532) - (near-count uint32 :offset-assert 6536) - (last-shrubs uint32 :offset-assert 6540) - (chains uint32 :offset-assert 6544) - (flags uint32 :offset-assert 6548) - (paused basic :offset-assert 6552) - (node-count uint32 :offset-assert 6556) - (inst-count uint32 :offset-assert 6560) - (wait-from-spr uint32 :offset-assert 6564) - (wait-to-spr uint32 :offset-assert 6568) + ((dummy qword 3 :inline :offset-assert 0) + (chaina qword 8 :inline :offset-assert 48) + (chainb qword 8 :inline :offset-assert 176) + (colors rgba 1024 :offset-assert 304) + (matrix-tmpl qword 20 :inline :offset-assert 4400) + (count-tmpl vector4w 20 :inline :offset-assert 4720) + (mscalf-tmpl dma-packet :inline :offset-assert 5040) + (mscalf-ret-tmpl dma-packet :inline :offset-assert 5056) + (adgif-tmpl dma-gif-packet :inline :offset-assert 5072) + (billboard-tmpl dma-gif-packet :inline :offset-assert 5104) + (billboard-const vector :inline :offset-assert 5136) + (shrub-near-packets shrub-near-packet 6 :inline :offset-assert 5152) + (dma-ref dma-packet :inline :offset-assert 6016) + (dma-end dma-packet :inline :offset-assert 6032) + (wind-const vector :inline :offset-assert 6048) + (constants vector :inline :offset-assert 6064) + (color-constant vector4w :inline :offset-assert 6080) + (hmge-d vector :inline :offset-assert 6096) + (hvdf-offset vector :inline :offset-assert 6112) + (wind-force vector :inline :offset-assert 6128) + (color vector :inline :offset-assert 6144) + (bb-color vector :inline :offset-assert 6160) + (min-dist vector :inline :offset-assert 6176) + (temp-vec vector :inline :offset-assert 6192) + (guard-plane plane 4 :inline :offset-assert 6208) + (plane plane 4 :inline :offset-assert 6272) + (last uint32 4 :offset-assert 6336) + (next uint32 4 :offset-assert 6352) + (count uint16 4 :offset-assert 6368) + (mod-count uint16 4 :offset-assert 6376) + (wind-vectors uint32 :offset-assert 6384) + (instance-ptr uint32 :offset-assert 6388) + (chain-ptr uint32 :offset-assert 6392) + (chain-ptr-next uint32 :offset-assert 6396) + (stack-ptr uint32 :offset-assert 6400) + (bucket-ptr uint32 :offset-assert 6404) + (src-ptr uint32 :offset-assert 6408) + (to-spr uint32 :offset-assert 6412) + (from-spr uint32 :offset-assert 6416) + (shrub-count uint32 :offset-assert 6420) + ;;(stack-ptr uint32 :offset-assert 6400) ;; this field appears twice? + (node uint32 6 :offset 6428) + (length uint32 6 :offset-assert 6452) + (prototypes uint32 :offset-assert 6476) + ;;(bucket-ptr uint32 :offset-assert 6404) appears twice + + (start-bank uint8 20 :offset 6484) + (buffer-index uint32 :offset-assert 6504) + (current-spr uint32 :offset-assert 6508) + (current-mem uint32 :offset-assert 6512) + (current-shrub-near-packet uint32 :offset-assert 6516) + ;;(to-spr uint32 :offset-assert 6412) + (dma-buffer basic :offset 6524) + (near-last uint32 :offset-assert 6528) + (near-next uint32 :offset-assert 6532) + (near-count uint32 :offset-assert 6536) + (last-shrubs uint32 :offset-assert 6540) + (chains uint32 :offset-assert 6544) + (flags uint32 :offset-assert 6548) + (paused basic :offset-assert 6552) + (node-count uint32 :offset-assert 6556) + (inst-count uint32 :offset-assert 6560) + (wait-from-spr uint32 :offset-assert 6564) + (wait-to-spr uint32 :offset-assert 6568) ) :method-count-assert 9 :size-assert #x19ac diff --git a/goal_src/engine/gfx/shrub/shrubbery.gc b/goal_src/engine/gfx/shrub/shrubbery.gc index f30a8372a0..5aee8ab280 100644 --- a/goal_src/engine/gfx/shrub/shrubbery.gc +++ b/goal_src/engine/gfx/shrub/shrubbery.gc @@ -5,6 +5,606 @@ ;; name in dgo: shrubbery ;; dgos: GAME, ENGINE +(defmethod login billboard ((obj billboard)) + "Set up the billboard adgif shader" + (adgif-shader-login (-> obj flat)) + obj + ) + +(defmethod mem-usage billboard ((obj billboard) (arg0 memory-usage-block) (arg1 int)) + "Compute the memory used for the billboard." + (set! (-> arg0 length) (max 34 (-> arg0 length))) + (set! (-> arg0 data 33 name) "billboard") + (+! (-> arg0 data 33 count) 1) + (let ((v1-6 (asize-of obj))) + (+! (-> arg0 data 33 used) v1-6) + (+! (-> arg0 data 33 total) (logand -16 (+ v1-6 15))) + ) + obj + ) + +(defun-recursive mem-usage-shrub-walk draw-node ((arg0 draw-node) (arg1 int) (arg2 memory-usage-block) (arg3 int)) + "Recursively iterate through the entire shrub tree and compute memory usage for instances." + (set! (-> arg2 length) (max 62 (-> arg2 length))) + (set! (-> arg2 data 61 name) "draw-node") + (+! (-> arg2 data 61 count) arg1) + (let ((v1-5 (* arg1 32))) + (+! (-> arg2 data 61 used) v1-5) + (+! (-> arg2 data 61 total) (logand -16 (+ v1-5 15))) + ) + (let ((s2-0 arg0)) + (dotimes (s1-0 arg1) + (let ((a1-2 (-> s2-0 child-count))) + (cond + ((logtest? (-> s2-0 flags) 1) + (mem-usage-shrub-walk (the-as draw-node (-> s2-0 child)) (the-as int a1-2) arg2 arg3) + ) + (else + (set! (-> arg2 length) (max 35 (-> arg2 length))) + (set! (-> arg2 data 34 name) "instance-shrubbery") + (+! (-> arg2 data 34 count) a1-2) + (let ((v1-18 (* (the-as uint 80) a1-2))) + (+! (-> arg2 data 34 used) v1-18) + (+! (-> arg2 data 34 total) (logand -16 (+ v1-18 15))) + ) + ) + ) + ) + (&+! s2-0 32) + ) + ) + arg0 + ) + +(defmethod mem-usage drawable-tree-instance-shrub ((obj drawable-tree-instance-shrub) (arg0 memory-usage-block) (arg1 int)) + "Compute memory usage for an entire shrub tree" + (set! (-> arg0 length) (max 1 (-> arg0 length))) + (set! (-> arg0 data 0 name) (symbol->string 'drawable-group)) + (+! (-> arg0 data 0 count) 1) + (let ((v1-7 32)) + (+! (-> arg0 data 0 used) v1-7) + (+! (-> arg0 data 0 total) (logand -16 (+ v1-7 15))) + ) + ;; time of day colors + (when (nonzero? (-> obj colors-added)) + (set! (-> arg0 length) (max 33 (-> arg0 length))) + (set! (-> arg0 data 32 name) "shrubbery-pal") + (+! (-> arg0 data 32 count) 1) + (let ((v1-19 (asize-of (-> obj colors-added)))) + (+! (-> arg0 data 32 used) v1-19) + (+! (-> arg0 data 32 total) (logand -16 (+ v1-19 15))) + ) + ) + ;; instance tree + (mem-usage-shrub-walk + (the-as draw-node (&+ (-> obj data 0) 32)) + (-> (the-as drawable-group (-> obj data 0)) length) + arg0 + arg1 + ) + ;; prototypes + (mem-usage (-> obj info prototype-inline-array-shrub) arg0 (logior arg1 1)) + obj + ) + +(defmethod login generic-shrub-fragment ((obj generic-shrub-fragment)) + "Set up shaders in a generic shrub fragment" + (let ((s5-0 (/ (-> obj cnt-qwc) (the-as uint 5)))) + (dotimes (s4-0 (the-as int s5-0)) + (adgif-shader-login-no-remap (-> obj textures s4-0)) + ) + ) + obj + ) + +(defmethod mem-usage generic-shrub-fragment ((obj generic-shrub-fragment) (arg0 memory-usage-block) (arg1 int)) + "Compute memory usage of generic shrub fragment" + (set! (-> arg0 length) (max 27 (-> arg0 length))) + (set! (-> arg0 data 25 name) "generic-shrub") + (+! (-> arg0 data 25 count) 1) + ;; the actual fragment object + (let ((v1-6 (asize-of obj))) + (+! (-> arg0 data 25 used) v1-6) + (+! (-> arg0 data 25 total) (logand -16 (+ v1-6 15))) + ) + ;; the referenced data + (set! (-> arg0 data 26 name) "generic-shrub-data") + (+! (-> arg0 data 26 count) 1) + (let ((v1-17 (* (+ (-> obj cnt-qwc) (-> obj vtx-qwc) (-> obj col-qwc) (-> obj stq-qwc)) 16))) + (+! (-> arg0 data 26 used) v1-17) + (+! (-> arg0 data 26 total) (logand -16 (+ v1-17 15))) + ) + obj + ) + +(defmethod inspect prototype-shrubbery ((obj prototype-shrubbery)) + "Inspect all prototypes in a prototype array." + (format #t "[~8x] ~A~%" obj (-> obj type)) + (format #t "~Tlength: ~D~%" (-> obj length)) + (format #t "~Tdata[~D]: @ #x~X~%" (-> obj length) (-> obj data)) + (dotimes (s5-0 (-> obj length)) + (format #t "~T [~D] ~A~%" s5-0 (-> obj data s5-0)) + ) + obj + ) + +(defmethod mem-usage prototype-shrubbery ((obj prototype-shrubbery) (arg0 memory-usage-block) (arg1 int)) + "Compute memory usage of all prototypes in a prototype array." + (set! (-> arg0 length) (max 1 (-> arg0 length))) + (set! (-> arg0 data 0 name) (symbol->string 'drawable-group)) + (+! (-> arg0 data 0 count) 1) + (let ((v1-7 32)) + (+! (-> arg0 data 0 used) v1-7) + (+! (-> arg0 data 0 total) (logand -16 (+ v1-7 15))) + ) + (dotimes (s3-0 (-> obj length)) + (mem-usage (-> obj data s3-0) arg0 arg1) + ) + obj + ) + +(defmethod login prototype-shrubbery ((obj prototype-shrubbery)) + "Login all prototypes in a prototype array" + (dotimes (s5-0 (-> obj length)) + (login (-> obj data s5-0)) + ) + obj + ) + +(defmethod asize-of prototype-shrubbery ((obj prototype-shrubbery)) + "Compute the allocation size of a prototype shrubbery array (dynamically sized)" + (the-as int (+ (-> prototype-shrubbery size) (* (+ (-> obj length) -1) 32))) + ) + +(defmethod login prototype-generic-shrub ((obj prototype-generic-shrub)) + "Initialize all fragments in a generic prototype." + (dotimes (s5-0 (-> obj length)) + (login (-> obj data s5-0)) + ) + obj + ) + +(defmethod login shrubbery ((obj shrubbery)) + "Initialize a shrubbery fragment." + (let ((s5-0 (* (-> obj header data 0) 2))) + (dotimes (s4-0 (the-as int s5-0)) + (adgif-shader-login-no-remap (-> obj textures s4-0)) + ) + ) + (shrubbery-login-post-texture obj) + obj + ) + +(defmethod mem-usage shrubbery ((obj shrubbery) (arg0 memory-usage-block) (arg1 int)) + "Compute the memory usage of a shrubbery fragment." + (set! (-> arg0 length) (max 28 (-> arg0 length))) + (set! (-> arg0 data 27 name) "shrubbery") + (+! (-> arg0 data 27 count) 1) + (let ((v1-6 (asize-of obj))) + (+! (-> arg0 data 27 used) v1-6) + (+! (-> arg0 data 27 total) (logand -16 (+ v1-6 15))) + ) + (set! (-> arg0 length) (max 30 (-> arg0 length))) + (set! (-> arg0 data 29 name) "shrubbery-vertex") + (+! (-> arg0 data 29 count) 1) + (let ((v1-16 (* (-> obj vtx-qwc) 16))) + (+! (-> arg0 data 29 used) v1-16) + (+! (-> arg0 data 29 total) (logand -16 (+ v1-16 15))) + ) + (set! (-> arg0 length) (max 31 (-> arg0 length))) + (set! (-> arg0 data 30 name) "shrubbery-color") + (+! (-> arg0 data 30 count) 1) + (let ((v1-26 (* (-> obj col-qwc) 16))) + (+! (-> arg0 data 30 used) v1-26) + (+! (-> arg0 data 30 total) (logand -16 (+ v1-26 15))) + ) + (set! (-> arg0 length) (max 29 (-> arg0 length))) + (set! (-> arg0 data 28 name) "shrubbery-object") + (+! (-> arg0 data 28 count) 1) + (let ((v1-36 (* (-> obj obj-qwc) 16))) + (+! (-> arg0 data 28 used) v1-36) + (+! (-> arg0 data 28 total) (logand -16 (+ v1-36 15))) + ) + (set! (-> arg0 length) (max 32 (-> arg0 length))) + (set! (-> arg0 data 31 name) "shrubbery-stq") + (+! (-> arg0 data 31 count) 1) + (let ((v1-46 (* (-> obj stq-qwc) 16))) + (+! (-> arg0 data 31 used) v1-46) + (+! (-> arg0 data 31 total) (logand -16 (+ v1-46 15))) + ) + obj + ) + +(defmethod login drawable-tree-instance-shrub ((obj drawable-tree-instance-shrub)) + "Initialize a shrubbery tree." + (if (nonzero? (-> obj info prototype-inline-array-shrub)) + (login (-> obj info prototype-inline-array-shrub)) + ) + obj + ) + +(define shrub-vu1-block (new 'static 'vu-function #|:length #x26a :qlength #x135|#)) + +(defun shrub-num-tris ((arg0 shrubbery)) + "Get the number of triangles in a shrubbery fragment." + (- (-> arg0 header data 2) (* (-> arg0 header data 1) 2)) + ) + +(defun shrub-make-perspective-matrix ((arg0 matrix)) + "Compute the matrix used by the shrubbery VU1 program." + (let* ((v1-0 arg0) + (t0-0 (-> *math-camera* camera-temp)) + (a1-1 (-> t0-0 vector 0 quad)) + (a2-0 (-> t0-0 vector 1 quad)) + (a3-0 (-> t0-0 vector 2 quad)) + (t0-1 (-> t0-0 vector 3 quad)) + ) + (set! (-> v1-0 vector 0 quad) a1-1) + (set! (-> v1-0 vector 1 quad) a2-0) + (set! (-> v1-0 vector 2 quad) a3-0) + (set! (-> v1-0 vector 3 quad) t0-1) + ) + (let ((f0-1 (/ 1.0 (-> *math-camera* pfog0)))) + (set! (-> arg0 vector 0 w) (* (-> arg0 vector 0 w) f0-1)) + (set! (-> arg0 vector 1 w) (* (-> arg0 vector 1 w) f0-1)) + (set! (-> arg0 vector 2 w) (* (-> arg0 vector 2 w) f0-1)) + (set! (-> arg0 vector 3 w) (* (-> arg0 vector 3 w) f0-1)) + ) + (+! (-> arg0 vector 0 x) (* (-> arg0 vector 0 w) (-> *math-camera* hvdf-off x))) + (+! (-> arg0 vector 1 x) (* (-> arg0 vector 1 w) (-> *math-camera* hvdf-off x))) + (+! (-> arg0 vector 2 x) (* (-> arg0 vector 2 w) (-> *math-camera* hvdf-off x))) + (+! (-> arg0 vector 3 x) (* (-> arg0 vector 3 w) (-> *math-camera* hvdf-off x))) + (+! (-> arg0 vector 0 y) (* (-> arg0 vector 0 w) (-> *math-camera* hvdf-off y))) + (+! (-> arg0 vector 1 y) (* (-> arg0 vector 1 w) (-> *math-camera* hvdf-off y))) + (+! (-> arg0 vector 2 y) (* (-> arg0 vector 2 w) (-> *math-camera* hvdf-off y))) + (+! (-> arg0 vector 3 y) (* (-> arg0 vector 3 w) (-> *math-camera* hvdf-off y))) + (+! (-> arg0 vector 0 z) (* (-> arg0 vector 0 w) (-> *math-camera* hvdf-off z))) + (+! (-> arg0 vector 1 z) (* (-> arg0 vector 1 w) (-> *math-camera* hvdf-off z))) + (+! (-> arg0 vector 2 z) (* (-> arg0 vector 2 w) (-> *math-camera* hvdf-off z))) + (+! (-> arg0 vector 3 z) (* (-> arg0 vector 3 w) (-> *math-camera* hvdf-off z))) + arg0 + ) + +(defun shrub-init-view-data ((arg0 shrub-view-data)) + "Initialize shrubbery constants in place" + (set! (-> arg0 texture-giftag tag) (new 'static 'gif-tag64 :nloop #x1 :nreg #x4)) + (set! (-> arg0 texture-giftag regs) (new 'static 'gif-tag-regs + :regs0 (gif-reg-id a+d) + :regs1 (gif-reg-id a+d) + :regs2 (gif-reg-id a+d) + :regs3 (gif-reg-id a+d) + ) + ) + (set! (-> arg0 texture-giftag word 3) (the-as uint #x40a00000)) + (set! (-> arg0 tex-start-ptr) (the-as int 25167696.0)) + (set! (-> arg0 mtx-buf-ptr) (the-as int 8388608.0)) + (set! (-> arg0 fog-0) (-> *math-camera* pfog0)) + (set! (-> arg0 fog-1) (-> *math-camera* pfog1)) + (set! (-> arg0 fog-min) (-> *math-camera* fog-min)) + (set! (-> arg0 fog-max) (-> *math-camera* fog-max)) + #f + ) + +(defun shrub-upload-view-data ((arg0 dma-buffer)) + "Generate DMA data to upload shrubbery constants to VU1." + (let ((s5-0 3)) + (let* ((v1-0 arg0) + (a0-1 (the-as object (-> v1-0 base))) + ) + (set! (-> (the-as dma-packet a0-1) dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc s5-0)) + (set! (-> (the-as dma-packet a0-1) vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) + (set! (-> (the-as dma-packet a0-1) vif1) (new 'static 'vif-tag :cmd (vif-cmd unpack-v4-32) :num s5-0)) + (set! (-> v1-0 base) (&+ (the-as pointer a0-1) 16)) + ) + (shrub-init-view-data (the-as shrub-view-data (-> arg0 base))) + (&+! (-> arg0 base) (* s5-0 16)) + ) + #f + ) + +(defun shrub-time ((arg0 int) (arg1 int) (arg2 int) (arg3 int) (arg4 int)) + (+ (* arg0 8) 29 (* 22 arg2) (* 11 arg1) (* (+ (* arg4 2) 15 (* 5 arg2) (* 13 arg0)) arg3) 53) + ) + +(defun shrub-do-init-frame ((arg0 dma-buffer)) + "Generate DMA to initialize the shrubbery renderer on VU1." + ;; upload VU1 program + (dma-buffer-add-vu-function arg0 shrub-vu1-block 1) + ;; upload constants + (shrub-upload-view-data arg0) + ;; run program initialization function + (let* ((v1-0 arg0) + (a0-3 (the-as object (-> v1-0 base))) + ) + (set! (-> (the-as dma-packet a0-3) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet a0-3) vif0) (new 'static 'vif-tag :cmd (vif-cmd mscalf) :msk #x1 :imm #x0)) + (set! (-> (the-as dma-packet a0-3) vif1) (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1)) + (set! (-> v1-0 base) (&+ (the-as pointer a0-3) 16)) + ) + ;; initialize VIF for shrubbery chains. + (let* ((v1-1 arg0) + (a0-5 (the-as object (-> v1-1 base))) + ) + (set! (-> (the-as dma-packet a0-5) dma) (new 'static 'dma-tag :qwc #x3 :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet a0-5) vif0) (new 'static 'vif-tag)) + (set! (-> (the-as dma-packet a0-5) vif1) (new 'static 'vif-tag)) + (set! (-> v1-1 base) (&+ (the-as pointer a0-5) 16)) + ) + (let ((v1-2 (-> arg0 base))) + (set! (-> (the-as (pointer vif-tag) v1-2) 0) (new 'static 'vif-tag :cmd (vif-cmd strow) :msk #x1)) + (set! (-> (the-as (pointer uint32) v1-2) 1) (the-as uint #x8080)) + (set! (-> (the-as (pointer uint32) v1-2) 2) (the-as uint #x8080)) + (set! (-> (the-as (pointer uint32) v1-2) 3) (the-as uint #x8080)) + (set! (-> (the-as (pointer uint32) v1-2) 4) (the-as uint 0)) + (set! (-> (the-as (pointer vif-tag) v1-2) 5) (new 'static 'vif-tag :cmd (vif-cmd stcol) :msk #x1)) + (set! (-> (the-as (pointer uint32) v1-2) 6) (the-as uint 4096)) + (set! (-> (the-as (pointer uint32) v1-2) 7) (the-as uint 4096)) + (set! (-> (the-as (pointer uint32) v1-2) 8) (the-as uint 4096)) + (set! (-> (the-as (pointer uint32) v1-2) 9) (the-as uint 4096)) + (set! (-> (the-as (pointer vif-tag) v1-2) 10) (new 'static 'vif-tag :cmd (vif-cmd stmask))) + (set! (-> (the-as (pointer uint32) v1-2) 11) (the-as uint #xa0a0a0a0)) + (set! (-> arg0 base) (&+ v1-2 48)) + ) + (set! *shrub-state* 2) + #f + ) + +(defun shrub-init-frame ((arg0 dma-buffer) (arg1 gs-test)) + "Generate DMA to initialize to shrubbery renderer VU1 and GS." + + ;; init VU1 + (shrub-do-init-frame arg0) + ;; init GS. + (let* ((v1-0 arg0) + (a0-2 (the-as object (-> v1-0 base))) + ) + (set! (-> (the-as dma-packet a0-2) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet a0-2) vif0) (new 'static 'vif-tag)) + (set! (-> (the-as dma-packet a0-2) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1)) + (set! (-> v1-0 base) (the-as pointer (the-as gs-gif-tag (&+ (the-as pointer a0-2) 16)))) + ) + (let* ((v1-1 arg0) + (a0-4 (the-as object (-> v1-1 base))) + ) + (set! (-> (the-as gs-gif-tag a0-4) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1)) + (set! (-> (the-as gs-gif-tag a0-4) regs) (new 'static 'gif-tag-regs + :regs0 (gif-reg-id a+d) + :regs1 (gif-reg-id a+d) + :regs2 (gif-reg-id a+d) + :regs3 (gif-reg-id a+d) + :regs4 (gif-reg-id a+d) + :regs5 (gif-reg-id a+d) + :regs6 (gif-reg-id a+d) + :regs7 (gif-reg-id a+d) + :regs8 (gif-reg-id a+d) + :regs9 (gif-reg-id a+d) + :regs10 (gif-reg-id a+d) + :regs11 (gif-reg-id a+d) + :regs12 (gif-reg-id a+d) + :regs13 (gif-reg-id a+d) + :regs14 (gif-reg-id a+d) + :regs15 (gif-reg-id a+d) + ) + ) + (set! (-> v1-1 base) (&+ (the-as pointer a0-4) 16)) + ) + (let ((v1-2 (-> arg0 base))) + (set! (-> (the-as (pointer gs-test) v1-2) 0) arg1) + (set! (-> (the-as (pointer gs-reg64) v1-2) 1) (gs-reg64 test-1)) + (set! (-> arg0 base) (&+ v1-2 16)) + ) + (none) + ) + +(defun shrub-upload-model ((arg0 shrubbery) (arg1 dma-buffer) (arg2 int)) + "Generate DMA to upload a shrub model" + (let* ((v1-0 arg1) + (a3-0 (the-as object (-> v1-0 base))) + ) + ;; upload the data + (set! (-> (the-as dma-packet a3-0) dma) + (new 'static 'dma-tag + :id (dma-tag-id ref) + :addr (-> arg0 obj) + :qwc (+ (-> arg0 obj-qwc) (-> arg0 vtx-qwc) (-> arg0 col-qwc) (-> arg0 stq-qwc)) + ) + ) + (set! (-> (the-as dma-packet a3-0) vif0) (new 'static 'vif-tag :cmd (vif-cmd base) :imm *shrub-state*)) + (set! (-> (the-as dma-packet a3-0) vif1) (new 'static 'vif-tag :cmd (vif-cmd offset))) + (set! (-> v1-0 base) (&+ (the-as pointer a3-0) 16)) + ) + ;; run the program to init a model + (cond + ((= arg2 1) + (let* ((v1-2 arg1) + (a0-9 (the-as object (-> v1-2 base))) + ) + (set! (-> (the-as dma-packet a0-9) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet a0-9) vif0) (new 'static 'vif-tag)) + (set! (-> (the-as dma-packet a0-9) vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x11)) + (set! (-> v1-2 base) (&+ (the-as pointer a0-9) 16)) + ) + ) + (else + (let* ((v1-3 arg1) + (a0-11 (the-as object (-> v1-3 base))) + ) + (set! (-> (the-as dma-packet a0-11) dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> (the-as dma-packet a0-11) vif0) (new 'static 'vif-tag)) + (set! (-> (the-as dma-packet a0-11) vif1) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x15)) + (set! (-> v1-3 base) (&+ (the-as pointer a0-11) 16)) + ) + ) + ) + ;; some data buffer thing. + (set! *shrub-state* (- 164 *shrub-state*)) + #f + ) + +;; TODO drawing... + +(defun draw-drawable-tree-instance-shrub ((arg0 drawable-tree-instance-shrub) (arg1 level)) + ;; (local-vars (a0-4 int) (a0-6 int) (a0-11 int) (a0-13 int)) + (set-vector! (-> *instance-shrub-work* min-dist) 4095996000.0 4095996000.0 4095996000.0 4095996000.0) + (set! (-> *instance-shrub-work* near-last) (the-as uint 0)) + (set! (-> *instance-shrub-work* near-next) (the-as uint 0)) + (set! (-> *instance-shrub-work* near-count) (the-as uint 0)) + (set! (-> *instance-shrub-work* wind-vectors) (-> arg0 info wind-vectors)) + (set! (-> *instance-shrub-work* wait-to-spr) (the-as uint 0)) + (set! (-> *instance-shrub-work* wait-from-spr) (the-as uint 0)) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask shrubbery shrub-near billboard trans-shrubbery)) + (let* ((v1-13 (-> arg0 info prototype-inline-array-shrub)) + (s4-0 (-> v1-13 length)) + (s3-0 (-> v1-13 data)) + ) + (countdown (a1-2 s4-0) + (let ((a2-3 (-> v1-13 data a1-2))) + (set! (-> a2-3 next-clear) (the-as uint128 0)) + (set! (-> a2-3 last-clear) (the-as uint128 0)) + (set! (-> a2-3 count-clear) (the-as uint 0)) + ) + 0 + ) + (let ((v1-19 (-> *display* frames (-> *display* on-screen) frame global-buf))) + (when (nonzero? (-> arg0 length)) + (let ((s5-0 (-> *display* frames (-> *display* on-screen) frame global-buf base))) + (reset! (-> *perf-stats* data (perf-stat-bucket inst-shrub))) + ; (draw-inline-array-instance-shrub + ; v1-19 + ; (&+ (-> arg0 data 0) 32) + ; (-> (the-as drawable-group (-> arg0 data 0)) length) + ; s3-0 + ; ) + (read! (-> *perf-stats* data (perf-stat-bucket inst-shrub))) + (reset! (-> *perf-stats* data (perf-stat-bucket proto-shrub))) + ; (draw-prototype-inline-array-shrub s4-0 s3-0) + (read! (-> *perf-stats* data (perf-stat-bucket proto-shrub))) + (let ((v1-28 *dma-mem-usage*)) + (when (nonzero? v1-28) + (set! (-> v1-28 length) (max 28 (-> v1-28 length))) + (set! (-> v1-28 data 27 name) "shrubbery") + (+! (-> v1-28 data 27 count) 1) + (+! (-> v1-28 data 27 used) + (&- (-> *display* frames (-> *display* on-screen) frame global-buf base) (the-as uint s5-0)) + ) + (set! (-> v1-28 data 27 total) (-> v1-28 data 27 used)) + ) + ) + ) + ) + ) + ) + (update-wait-stats + (-> *perf-stats* data (perf-stat-bucket inst-shrub)) + (the-as uint 0) + (-> *instance-shrub-work* wait-to-spr) + (-> *instance-shrub-work* wait-from-spr) + ) + (set! (-> arg1 closest-object 2) (-> *instance-shrub-work* min-dist x)) + ) + 0 + (none) + ) + +(defmethod draw drawable-tree-instance-shrub ((obj drawable-tree-instance-shrub) (arg0 drawable-tree-instance-shrub) (arg1 display-frame)) + (let* ((v1-1 (-> *background-work* shrub-tree-count)) + (a1-2 (-> (scratchpad-object terrain-context) bsp lev-index)) + (a1-5 (-> *level* level a1-2)) + ) + (set! (-> *background-work* shrub-trees v1-1) obj) + (set! (-> *background-work* shrub-levels v1-1) a1-5) + ) + (+! (-> *background-work* shrub-tree-count) 1) + (none) + ) + (defmethod unpack-vis drawable-tree-instance-shrub ((obj drawable-tree-instance-shrub) (arg0 (pointer int8)) (arg1 (pointer int8))) arg1 - ) \ No newline at end of file + ) + +(defmethod collect-stats drawable-tree-instance-shrub ((obj drawable-tree-instance-shrub)) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask shrubbery shrub-near billboard trans-shrubbery)) + (let* ((v1-3 (-> obj info prototype-inline-array-shrub)) + (gp-0 (the-as object (-> v1-3 data))) + ) + (countdown (s5-0 (-> v1-3 length)) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask shrub-near)) + (let ((v1-7 (-> (the-as prototype-bucket-shrub gp-0) count 0)) + (a1-0 (-> (the-as prototype-bucket-shrub gp-0) geometry 0)) + ) + (when (nonzero? v1-7) + (let ((a0-2 (-> (the-as drawable-group a1-0) length))) + (+! (-> *terrain-stats* shrub groups) 1) + (+! (-> *terrain-stats* shrub fragments) (* a0-2 (the-as int v1-7))) + ) + (+! (-> *terrain-stats* shrub instances) v1-7) + ) + ) + ) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask shrubbery)) + (let ((s4-0 (-> (the-as prototype-bucket-shrub gp-0) count 1)) + (v1-12 (-> (the-as prototype-bucket-shrub gp-0) geometry 1)) + ) + (when (nonzero? s4-0) + (let ((s3-0 (&+ v1-12 32)) + (s2-0 (-> (the-as drawable-group v1-12) length)) + ) + (+! (-> *terrain-stats* shrub groups) 1) + (+! (-> *terrain-stats* shrub fragments) s2-0) + (+! (-> *terrain-stats* shrub instances) s4-0) + (while (nonzero? s2-0) + (+! s2-0 -1) + (let ((a0-13 (* (shrub-num-tris (the-as shrubbery s3-0)) s4-0)) + (v1-24 (* (-> (the-as shrubbery s3-0) header data 2) s4-0)) + ) + (+! (-> *terrain-stats* shrub tris) a0-13) + (+! (-> *terrain-stats* shrub dverts) v1-24) + ) + (&+! s3-0 32) + ) + ) + ) + ) + ) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask trans-shrubbery)) + (let ((s4-1 (-> (the-as prototype-bucket-shrub gp-0) count 2)) + (v1-30 (-> (the-as prototype-bucket-shrub gp-0) geometry 2)) + ) + (when (nonzero? s4-1) + (let ((s3-1 (&+ v1-30 32)) + (s2-1 (-> (the-as drawable-group v1-30) length)) + ) + (+! (-> *terrain-stats* trans-shrub groups) 1) + (+! (-> *terrain-stats* trans-shrub fragments) s2-1) + (+! (-> *terrain-stats* trans-shrub instances) s4-1) + (while (nonzero? s2-1) + (+! s2-1 -1) + (let ((a0-24 (* (shrub-num-tris (the-as shrubbery s3-1)) s4-1)) + (v1-42 (* (-> (the-as shrubbery s3-1) header data 2) s4-1)) + ) + (+! (-> *terrain-stats* trans-shrub tris) a0-24) + (+! (-> *terrain-stats* trans-shrub dverts) v1-42) + ) + (&+! s3-1 32) + ) + ) + ) + ) + ) + (when (logtest? *vu1-enable-user* (vu1-renderer-mask billboard)) + (let ((v1-48 (-> (the-as prototype-bucket-shrub gp-0) count 3))) + (when (nonzero? v1-48) + (+! (-> *terrain-stats* billboard groups) 1) + (+! (-> *terrain-stats* billboard instances) v1-48) + (+! (-> *terrain-stats* billboard tris) (* v1-48 2)) + (+! (-> *terrain-stats* billboard dverts) (* v1-48 4)) + ) + ) + ) + (set! gp-0 (-> (the-as (inline-array prototype-bucket-shrub) gp-0) 1)) + ) + ) + ) + (none) + ) + diff --git a/goal_src/engine/gfx/tie/prototype-h.gc b/goal_src/engine/gfx/tie/prototype-h.gc index ee1249b28c..0f82cf5a52 100644 --- a/goal_src/engine/gfx/tie/prototype-h.gc +++ b/goal_src/engine/gfx/tie/prototype-h.gc @@ -13,7 +13,7 @@ ;; a geom, based on distance from the camera (or other settings) and will add itself to the list ;; for that geom. (deftype prototype-bucket (basic) - ((name basic :offset-assert 4) + ((name string :offset-assert 4) (flags uint32 :offset-assert 8) (in-level uint16 :offset-assert 12) (utextures uint16 :offset-assert 14) @@ -33,6 +33,10 @@ (rlength-mid float :offset 56) (stiffness float :offset 60) (next-clear uint128 :offset 64) + (next-clear-1 int32 :offset 64) + (next-clear-2 int32 :offset 68) + (next-clear-3 int32 :offset 72) + (next-clear-4 int32 :offset 76) (count-clear uint64 :offset 80) ) :method-count-assert 9 @@ -47,7 +51,8 @@ ;; specialization for shrub. We keep the end of the linked list too. (deftype prototype-bucket-shrub (prototype-bucket) ((mod-count uint16 4 :offset-assert 88) - (last uint32 4 :offset-assert 96) + (last dma-packet 4 :offset-assert 96) + (count-clear-qword uint128 :offset 80) (last-clear uint128 :offset 96) ) :method-count-assert 9 diff --git a/test/decompiler/reference/engine/debug/default-menu_REF.gc b/test/decompiler/reference/engine/debug/default-menu_REF.gc index 35950ac263..36f8686cf1 100644 --- a/test/decompiler/reference/engine/debug/default-menu_REF.gc +++ b/test/decompiler/reference/engine/debug/default-menu_REF.gc @@ -635,13 +635,7 @@ (let ((s3-0 (-> (the-as drawable-tree-instance-shrub v1-7) info prototype-inline-array-shrub))) (dotimes (s2-0 (-> s3-0 length)) (let ((a1-4 - (new - 'global - 'debug-menu-item-flag - (the-as string (-> s3-0 data s2-0 name)) - (-> s3-0 data s2-0 name) - dm-instance-pick-func - ) + (new 'global 'debug-menu-item-flag (-> s3-0 data s2-0 name) (-> s3-0 data s2-0 name) dm-instance-pick-func) ) ) (debug-menu-append-item *instance-shrub-menu* a1-4) @@ -656,7 +650,7 @@ (new 'debug 'debug-menu-item-flag - (the-as string (-> s3-1 array-data s2-1 name)) + (-> s3-1 array-data s2-1 name) (-> s3-1 array-data s2-1 name) dm-instance-pick-func ) @@ -668,7 +662,7 @@ (new 'debug 'debug-menu-item-flag - (the-as string (-> s3-1 array-data s2-1 name)) + (-> s3-1 array-data s2-1 name) (-> s3-1 array-data s2-1 name) dm-enable-instance-func ) diff --git a/test/decompiler/reference/engine/draw/drawable_REF.gc b/test/decompiler/reference/engine/draw/drawable_REF.gc index a3bca89d85..92c502e3d0 100644 --- a/test/decompiler/reference/engine/draw/drawable_REF.gc +++ b/test/decompiler/reference/engine/draw/drawable_REF.gc @@ -257,7 +257,7 @@ ((drawable-tree-instance-shrub) (let ((s2-0 (-> (the-as drawable-tree-instance-shrub v1-7) info prototype-inline-array-shrub))) (dotimes (s1-0 (-> s2-0 length)) - (if (string= arg0 (the-as string (-> s2-0 data s1-0 name))) + (if (string= arg0 (-> s2-0 data s1-0 name)) (return (-> s2-0 data s1-0)) ) ) @@ -266,7 +266,7 @@ ((drawable-tree-instance-tie) (let ((s2-1 (-> (the-as drawable-tree-instance-tie v1-7) prototypes prototype-array-tie))) (dotimes (s1-1 (-> s2-1 length)) - (if (string= arg0 (the-as string (-> s2-1 array-data s1-1 name))) + (if (string= arg0 (-> s2-1 array-data s1-1 name)) (return (-> s2-1 array-data s1-1)) ) ) @@ -353,7 +353,7 @@ ;; Used lq/sq (defun-debug draw-instance-info ((arg0 string)) (local-vars - (sv-16 int) + (sv-16 uint) (sv-32 uint) (sv-48 uint) (sv-64 int) @@ -448,14 +448,14 @@ s0-0 sv-16 sv-32 - (/ (* 2.0 (the float sv-16)) (the float (- sv-32 (the-as uint sv-16)))) + (/ (* 2.0 (the float sv-16)) (the float (- sv-32 sv-16))) sv-48 ) (+! s3-1 sv-16) (+! s4-2 sv-32) (set! f30-0 (+ 29.0 - (* 5.5 (the float (- sv-32 (the-as uint sv-16)))) + (* 5.5 (the float (- sv-32 sv-16))) (* 22.0 (the float sv-48)) (* 8.0 (the float sv-32)) (* 53.0 (the float (/ (+ s2-1 9) (the-as uint 10)))) diff --git a/test/decompiler/reference/engine/gfx/background_REF.gc b/test/decompiler/reference/engine/gfx/background_REF.gc index ddaf51678e..44f60467a4 100644 --- a/test/decompiler/reference/engine/gfx/background_REF.gc +++ b/test/decompiler/reference/engine/gfx/background_REF.gc @@ -120,7 +120,11 @@ (s4-0 (-> *background-work* shrub-levels gp-0)) ) (if (nonzero? (-> s5-0 colors-added)) - (time-of-day-interp-colors (-> *instance-shrub-work* colors) (-> s5-0 colors-added) (-> s4-0 mood)) + (time-of-day-interp-colors + (-> *instance-shrub-work* colors) + (the-as uint (-> s5-0 colors-added)) + (-> s4-0 mood) + ) ) (draw-drawable-tree-instance-shrub s5-0 s4-0) ) diff --git a/test/decompiler/reference/engine/gfx/shrub/shrub-work_REF.gc b/test/decompiler/reference/engine/gfx/shrub/shrub-work_REF.gc index 44c314c712..7f4ff5f4ed 100644 --- a/test/decompiler/reference/engine/gfx/shrub/shrub-work_REF.gc +++ b/test/decompiler/reference/engine/gfx/shrub/shrub-work_REF.gc @@ -29,27 +29,27 @@ (new 'static 'qword :data (new 'static 'array uint32 4 #x10000005 #x0 #x0 #x6c0501a3)) ) :count-tmpl - (new 'static 'inline-array qword 20 - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #xa)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x1)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x2)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x3)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x4)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x5)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x6)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x7)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x8)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #x9)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010142 #xa)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x1)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x2)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x3)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x4)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x5)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x6)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x7)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x8)) - (new 'static 'qword :data (new 'static 'array uint32 4 #x20000000 #x0 #x60010175 #x9)) + (new 'static 'inline-array vector4w 20 + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 10) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 1) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 2) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 3) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 4) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 5) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 6) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 7) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 8) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 9) + (new 'static 'vector4w :x #x20000000 :z #x60010142 :w 10) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 1) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 2) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 3) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 4) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 5) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 6) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 7) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 8) + (new 'static 'vector4w :x #x20000000 :z #x60010175 :w 9) ) :mscalf-tmpl (new 'static 'dma-packet diff --git a/test/decompiler/reference/engine/gfx/shrub/shrubbery-h_REF.gc b/test/decompiler/reference/engine/gfx/shrub/shrubbery-h_REF.gc index 50d36f9aeb..2bf1e22587 100644 --- a/test/decompiler/reference/engine/gfx/shrub/shrubbery-h_REF.gc +++ b/test/decompiler/reference/engine/gfx/shrub/shrubbery-h_REF.gc @@ -21,18 +21,18 @@ ;; definition of type shrub-view-data (deftype shrub-view-data (structure) - ((data uint128 3 :offset-assert 0) - (texture-giftag qword :inline :offset 0) - (consts vector :inline :offset 16) - (fog-clamp vector :inline :offset 32) - (tex-start-ptr int32 :offset 16) - (gifbufsum float :offset 16) - (mtx-buf-ptr int32 :offset 20) - (exp23 float :offset 20) - (fog-0 float :offset 24) - (fog-1 float :offset 28) - (fog-min float :offset 32) - (fog-max float :offset 36) + ((data uint128 3 :offset-assert 0) + (texture-giftag gs-gif-tag :inline :offset 0) + (consts vector :inline :offset 16) + (fog-clamp vector :inline :offset 32) + (tex-start-ptr int32 :offset 16) + (gifbufsum float :offset 16) + (mtx-buf-ptr int32 :offset 20) + (exp23 float :offset 20) + (fog-0 float :offset 24) + (fog-1 float :offset 28) + (fog-min float :offset 32) + (fog-max float :offset 36) ) :method-count-assert 9 :size-assert #x30 @@ -44,16 +44,16 @@ (format #t "[~8x] ~A~%" obj 'shrub-view-data) (format #t "~Tdata[3] @ #x~X~%" (-> obj data)) (format #t "~Ttexture-giftag: #~%" (-> obj data)) - (format #t "~Tconsts: #~%" (-> obj consts)) - (format #t "~Tfog-clamp: #~%" (-> obj fog-clamp)) - (format #t "~Ttex-start-ptr: ~D~%" (-> obj consts x)) - (format #t "~Tgifbufsum: ~f~%" (-> obj consts x)) - (format #t "~Tmtx-buf-ptr: ~D~%" (-> obj consts y)) - (format #t "~Texp23: ~f~%" (-> obj consts y)) - (format #t "~Tfog-0: ~f~%" (-> obj consts z)) - (format #t "~Tfog-1: ~f~%" (-> obj consts w)) - (format #t "~Tfog-min: ~f~%" (-> obj fog-clamp x)) - (format #t "~Tfog-max: ~f~%" (-> obj fog-clamp y)) + (format #t "~Tconsts: #~%" (&-> obj tex-start-ptr)) + (format #t "~Tfog-clamp: #~%" (&-> obj fog-min)) + (format #t "~Ttex-start-ptr: ~D~%" (-> obj tex-start-ptr)) + (format #t "~Tgifbufsum: ~f~%" (-> obj gifbufsum)) + (format #t "~Tmtx-buf-ptr: ~D~%" (-> obj mtx-buf-ptr)) + (format #t "~Texp23: ~f~%" (-> obj exp23)) + (format #t "~Tfog-0: ~f~%" (-> obj fog-0)) + (format #t "~Tfog-1: ~f~%" (-> obj fog-1)) + (format #t "~Tfog-min: ~f~%" (-> obj fog-min)) + (format #t "~Tfog-max: ~f~%" (-> obj fog-max)) obj ) @@ -95,9 +95,10 @@ ;; definition of type instance-shrubbery (deftype instance-shrubbery (instance) - ((flat-normal vector :inline :offset-assert 64) - (flat-hwidth float :offset 76) - (color uint32 :offset 8) + ((color-indices uint32 :offset 8) + (flat-normal vector :inline :offset-assert 64) + (flat-hwidth float :offset 76) + (color uint32 :offset 8) ) :method-count-assert 18 :size-assert #x50 @@ -131,7 +132,7 @@ ;; definition of type drawable-tree-instance-shrub (deftype drawable-tree-instance-shrub (drawable-tree) ((info prototype-array-shrub-info :offset 8) - (colors-added uint32 :offset 12) + (colors-added time-of-day-palette :offset 12) ) :method-count-assert 18 :size-assert #x24 @@ -292,7 +293,7 @@ (chainb qword 8 :inline :offset-assert 176) (colors rgba 1024 :offset-assert 304) (matrix-tmpl qword 20 :inline :offset-assert 4400) - (count-tmpl qword 20 :inline :offset-assert 4720) + (count-tmpl vector4w 20 :inline :offset-assert 4720) (mscalf-tmpl dma-packet :inline :offset-assert 5040) (mscalf-ret-tmpl dma-packet :inline :offset-assert 5056) (adgif-tmpl dma-gif-packet :inline :offset-assert 5072) diff --git a/test/decompiler/reference/engine/gfx/tie/prototype-h_REF.gc b/test/decompiler/reference/engine/gfx/tie/prototype-h_REF.gc index 94cde73c04..b47d99eb52 100644 --- a/test/decompiler/reference/engine/gfx/tie/prototype-h_REF.gc +++ b/test/decompiler/reference/engine/gfx/tie/prototype-h_REF.gc @@ -3,7 +3,7 @@ ;; definition of type prototype-bucket (deftype prototype-bucket (basic) - ((name basic :offset-assert 4) + ((name string :offset-assert 4) (flags uint32 :offset-assert 8) (in-level uint16 :offset-assert 12) (utextures uint16 :offset-assert 14) @@ -21,6 +21,10 @@ (rlength-mid float :offset 56) (stiffness float :offset 60) (next-clear uint128 :offset 64) + (next-clear-1 int32 :offset 64) + (next-clear-2 int32 :offset 68) + (next-clear-3 int32 :offset 72) + (next-clear-4 int32 :offset 76) (count-clear uint64 :offset 80) ) :method-count-assert 9 @@ -56,9 +60,10 @@ ;; definition of type prototype-bucket-shrub (deftype prototype-bucket-shrub (prototype-bucket) - ((mod-count uint16 4 :offset-assert 88) - (last uint32 4 :offset-assert 96) - (last-clear uint128 :offset 96) + ((mod-count uint16 4 :offset-assert 88) + (last dma-packet 4 :offset-assert 96) + (count-clear-qword uint128 :offset 80) + (last-clear uint128 :offset 96) ) :method-count-assert 9 :size-assert #x70 diff --git a/test/decompiler/reference/engine/gfx/tie/prototype_REF.gc b/test/decompiler/reference/engine/gfx/tie/prototype_REF.gc index e24ebea1e1..4d9e9addfb 100644 --- a/test/decompiler/reference/engine/gfx/tie/prototype_REF.gc +++ b/test/decompiler/reference/engine/gfx/tie/prototype_REF.gc @@ -74,7 +74,7 @@ (set! (-> arg0 length) (max 81 (-> arg0 length))) (set! (-> arg0 data 80 name) "string") (+! (-> arg0 data 80 count) 1) - (let ((v1-13 ((method-of-type string asize-of) (the-as string (-> obj name))))) + (let ((v1-13 (asize-of (-> obj name)))) (+! (-> arg0 data 80 used) v1-13) (+! (-> arg0 data 80 total) (logand -16 (+ v1-13 15))) ) @@ -127,7 +127,7 @@ (set! (-> arg0 length) (max 81 (-> arg0 length))) (set! (-> arg0 data 80 name) "string") (+! (-> arg0 data 80 count) 1) - (let ((v1-22 ((method-of-type string asize-of) (the-as string (-> obj name))))) + (let ((v1-22 (asize-of (-> obj name)))) (+! (-> arg0 data 80 used) v1-22) (+! (-> arg0 data 80 total) (logand -16 (+ v1-22 15))) )