From 22c92a39393601695996f320e5843986a4186c8e Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Thu, 17 Oct 2024 22:26:12 -0600 Subject: [PATCH 01/22] Update zww_extractor.ts to create the output directory if it does not already exist Without this change, the script will fail if data/ZeldaWindWaker does not already exist --- src/ZeldaWindWaker/tools/zww_extractor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ZeldaWindWaker/tools/zww_extractor.ts b/src/ZeldaWindWaker/tools/zww_extractor.ts index 379a982e4..6e698bc9c 100644 --- a/src/ZeldaWindWaker/tools/zww_extractor.ts +++ b/src/ZeldaWindWaker/tools/zww_extractor.ts @@ -3,7 +3,7 @@ import ArrayBufferSlice from "../../ArrayBufferSlice.js"; import * as BYML from "../../byml.js"; import * as Yaz0 from '../../Common/Compression/Yaz0.js'; import * as JKRArchive from "../../Common/JSYSTEM/JKRArchive.js"; -import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync } from "fs"; +import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from "fs"; import { assertExists, hexzero, assert, readString } from "../../util.js"; import { Endianness } from "../../endian.js"; import { loadRustLib } from "../../rustlib.js"; @@ -332,6 +332,7 @@ function extractExtra(binaries: Binary[]) { }; const data = BYML.write(crg1, BYML.FileType.CRG1); + mkdirSync(pathBaseOut, { recursive: true }); writeFileSync(`${pathBaseOut}/extra.crg1_arc`, Buffer.from(data)); } From a28b3f5ba25fa1bb994f2d40e036ffbc7067045a Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Thu, 17 Oct 2024 22:35:19 -0600 Subject: [PATCH 02/22] zww_extractor.ts now copies the res/* into the output directory NoClip expects these files to be present in the output directory. --- src/ZeldaWindWaker/tools/zww_extractor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ZeldaWindWaker/tools/zww_extractor.ts b/src/ZeldaWindWaker/tools/zww_extractor.ts index 6e698bc9c..c8e210727 100644 --- a/src/ZeldaWindWaker/tools/zww_extractor.ts +++ b/src/ZeldaWindWaker/tools/zww_extractor.ts @@ -3,7 +3,7 @@ import ArrayBufferSlice from "../../ArrayBufferSlice.js"; import * as BYML from "../../byml.js"; import * as Yaz0 from '../../Common/Compression/Yaz0.js'; import * as JKRArchive from "../../Common/JSYSTEM/JKRArchive.js"; -import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from "fs"; +import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync, mkdirSync, cpSync } from "fs"; import { assertExists, hexzero, assert, readString } from "../../util.js"; import { Endianness } from "../../endian.js"; import { loadRustLib } from "../../rustlib.js"; @@ -334,6 +334,7 @@ function extractExtra(binaries: Binary[]) { const data = BYML.write(crg1, BYML.FileType.CRG1); mkdirSync(pathBaseOut, { recursive: true }); writeFileSync(`${pathBaseOut}/extra.crg1_arc`, Buffer.from(data)); + cpSync(`${pathBaseIn}/res`, pathBaseOut, {recursive: true}) } async function loadBinaries(): Promise { From ca68e17e9713d639d5dea3ec2cbbd22c51fe9fc4 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 18 Oct 2024 01:03:07 -0600 Subject: [PATCH 03/22] Add d_wood.o symbols to zww_extractor.ts This should be everything we need to render bushes --- src/ZeldaWindWaker/tools/zww_extractor.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ZeldaWindWaker/tools/zww_extractor.ts b/src/ZeldaWindWaker/tools/zww_extractor.ts index c8e210727..90b3709e5 100644 --- a/src/ZeldaWindWaker/tools/zww_extractor.ts +++ b/src/ZeldaWindWaker/tools/zww_extractor.ts @@ -292,6 +292,20 @@ function extractExtra(binaries: Binary[]) { extractSymbol(datas, framework, 'd_grass.o', 'l_vtxAttrFmtList$4529'); extractSymbol(datas, framework, 'd_grass.o', 'l_vtxDescList$4528'); + // main.dol : d_wood.o + // console.log(JSON.stringify(framework.map.entries.filter(e => e.filename == 'd_wood.o'), null, 2)); + extractSymbol(datas, framework, 'd_wood.o', 'l_matDL__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_Oba_swood_b_cutDL__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_Oba_swood_bDL__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_texCoord__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_color__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_pos__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_Txa_swood_bTEX__Q25dWood20@unnamed@d_wood_cpp@'); + extractSymbol(datas, framework, 'd_wood.o', 'l_shadowVtxDescList$5139'); + extractSymbol(datas, framework, 'd_wood.o', 'l_shadowVtxAttrFmtList$5140'); + extractSymbol(datas, framework, 'd_wood.o', 'l_vtxDescList$5156'); + extractSymbol(datas, framework, 'd_wood.o', 'l_vtxAttrFmtList$5157'); + // main.dol : d_stage.o extractSymbol(datas, framework, `d_stage.o`, `l_objectName`); // Maps actor names to ID and Subtype From 94ee6609ed4059991af920f6204d1426c5478f3d Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 18 Oct 2024 01:12:51 -0600 Subject: [PATCH 04/22] Added d_wood.ts. Basic bush rendering. This is the start of one of the remaining missing d_s_play components, Wood (which is the TWW misnomer for bushes). Instead of following the old Grass.ts pattern, this is architected as similar to the d_wood.cpp as possible. This makes it simpler to compare to the decompiled results. A few functions are implemented, and the rendering is based heavily off of d_tree so it may not be completely correct. But basic model rendering and drop shadows are present. --- src/ZeldaWindWaker/Main.ts | 9 +- src/ZeldaWindWaker/d_a.ts | 11 + src/ZeldaWindWaker/d_wood.ts | 446 ++++++++++++++++++++++++++++++++ src/ZeldaWindWaker/framework.ts | 1 + 4 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 src/ZeldaWindWaker/d_wood.ts diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index ef1b98c37..2bb5daeaa 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -27,6 +27,7 @@ import { GfxrAttachmentSlot, GfxrRenderTargetDescription } from '../gfx/render/G import { GfxRenderInstList, GfxRenderInstManager } from '../gfx/render/GfxRenderInstManager.js'; import { GXRenderHelperGfx, fillSceneParamsDataOnTemplate } from '../gx/gx_render.js'; import { FlowerPacket, GrassPacket, TreePacket } from './Grass.js'; +import { Packet_c as WoodPacket } from './d_wood.js'; import { LegacyActor__RegisterFallbackConstructor } from './LegacyActor.js'; import { dDlst_2DStatic_c, d_a__RegisterConstructors } from './d_a.js'; import { d_a_sea } from './d_a_sea.js'; @@ -739,6 +740,7 @@ class d_s_play extends fopScn { public flowerPacket: FlowerPacket; public treePacket: TreePacket; public grassPacket: GrassPacket; + public woodPacket: WoodPacket; public vrboxLoaded: boolean = false; @@ -748,6 +750,7 @@ class d_s_play extends fopScn { this.treePacket = new TreePacket(globals); this.flowerPacket = new FlowerPacket(globals); this.grassPacket = new GrassPacket(globals); + this.woodPacket = new WoodPacket(globals); globals.scnPlay = this; @@ -757,22 +760,25 @@ class d_s_play extends fopScn { public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: Viewer.ViewerRenderInput): void { super.draw(globals, renderInstManager, viewerInput); - // Grass/Flowers/Trees + // Magma/Grass/Trees/Bushes/Flowers const frameCount = viewerInput.time / 1000.0 * 30; this.flowerPacket.calc(frameCount); this.treePacket.calc(frameCount); this.grassPacket.calc(frameCount); + this.woodPacket.calc(frameCount); this.flowerPacket.update(globals); this.treePacket.update(globals); this.grassPacket.update(globals); + this.woodPacket.update(); fopDw_Draw(globals.frameworkGlobals, globals, renderInstManager, viewerInput); this.flowerPacket.draw(globals, renderInstManager, viewerInput); this.treePacket.draw(globals, renderInstManager, viewerInput); this.grassPacket.draw(globals, renderInstManager, viewerInput); + this.woodPacket.draw(globals, renderInstManager, viewerInput); } public override delete(globals: dGlobals): void { @@ -782,6 +788,7 @@ class d_s_play extends fopScn { this.flowerPacket.destroy(device); this.treePacket.destroy(device); this.grassPacket.destroy(device); + this.woodPacket.destroy(device); } } diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index ab1909def..808e3f1fc 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -4116,6 +4116,16 @@ class d_a_oship extends fopAc_ac_c implements ModeFuncExec { } } +class d_a_obj_wood extends fopAc_ac_c { + public static PROCESS_NAME = fpc__ProcessName.d_a_obj_wood; + + public override subload(globals: dGlobals): cPhs__Status { + globals.scnPlay.woodPacket.put_unit(this.pos, this.roomNo); + // globals.scnPlay.treePacket.newData(this.pos, 0, this.roomNo); + return cPhs__Status.Next; + } +} + const enum d_a_obj_flame_mode { wait, wait2, l_before, l_u, u, u_l, l_after } const enum d_a_obj_em_state { Off, TurnOn, On, TurnOff } class d_a_obj_flame extends fopAc_ac_c { @@ -4711,6 +4721,7 @@ export function d_a__RegisterConstructors(globals: fGlobals): void { } R(d_a_grass); + R(d_a_obj_wood); R(d_a_ep); R(d_a_bg); R(d_a_vrbox); diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts new file mode 100644 index 000000000..dd0d0276c --- /dev/null +++ b/src/ZeldaWindWaker/d_wood.ts @@ -0,0 +1,446 @@ + +import { dGlobals } from './Main.js'; +import { GfxRendererLayer, GfxRenderInstManager, makeSortKey } from '../gfx/render/GfxRenderInstManager.js'; +import { ViewerRenderInput } from '../viewer.js'; +import { mat4, ReadonlyVec3, vec3 } from 'gl-matrix'; +import { dBgS_GndChk } from './d_bg.js'; +import { nArray } from '../gfx/platform/GfxPlatformUtil.js'; +import { dKy_GxFog_set } from './d_kankyo.js'; +import { colorCopy, colorFromRGBA } from '../Color.js'; +import { ColorKind, DrawParams, GXMaterialHelperGfx, GXShapeHelperGfx, loadedDataCoalescerComboGfx, MaterialParams } from '../gx/gx_render.js'; +import { BTI_Texture, BTIData } from '../Common/JSYSTEM/JUTTexture.js'; +import { TextureMapping } from '../TextureHolder.js'; +import { GfxBufferCoalescerCombo } from '../gfx/helpers/BufferHelpers.js'; +import ArrayBufferSlice from '../ArrayBufferSlice.js'; +import { compileVtxLoader, DisplayListRegisters, displayListRegistersInitGX, displayListRegistersRun, getAttributeByteSize, GX_Array, GX_VtxAttrFmt, GX_VtxDesc } from '../gx/gx_displaylist.js'; +import { parseMaterial } from '../gx/gx_material.js'; +import { Endianness } from '../endian.js'; +import * as GX from '../gx/gx_enum.js'; +import { GfxDevice } from '../gfx/platform/GfxPlatform.js'; + +//----------------------------------------- +// Globals +//----------------------------------------- +let globals: dGlobals; + +const scratchVec3a = vec3.create(); +const scratchVec3b = vec3.create(); +const scratchVec3c = vec3.create(); +const scratchVec3d = vec3.create(); +const scratchMat4a = mat4.create(); +const materialParams = new MaterialParams(); +const drawParams = new DrawParams(); + +//----------------------------------------- +// Helpers +//----------------------------------------- +function distanceCull(camPos: ReadonlyVec3, objPos: ReadonlyVec3, maxDist = 20000) { + const distSq = vec3.squaredDistance(camPos, objPos); + return distSq >= maxDist ** 2; +} + +function setColorFromRoomNo(globals: dGlobals, materialParams: MaterialParams, roomNo: number): void { + colorCopy(materialParams.u_Color[ColorKind.C0], globals.roomStatus[roomNo].tevStr.colorC0); + colorCopy(materialParams.u_Color[ColorKind.C1], globals.roomStatus[roomNo].tevStr.colorK0); +} + +interface J3DPacket { + draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void; +} + +//----------------------------------------- +// NoClip Rendering (TODO: Cleanup) +//----------------------------------------- +function parseGxVtxAttrFmtV(buffer: ArrayBufferSlice) { + const attrFmts = buffer.createTypedArray(Uint32Array, 0, buffer.byteLength / 4, Endianness.BIG_ENDIAN); + const result: GX_VtxAttrFmt[] = []; + for (let i = 0; attrFmts[i + 0] !== 255; i += 4) { + const attr = attrFmts[i + 0]; + const cnt = attrFmts[i + 1]; + const type = attrFmts[i + 2]; + const frac = attrFmts[i + 3]; + result[attr] = { compCnt: cnt, compShift: frac, compType: type }; + } + return result; +} + +function parseGxVtxDescList(buffer: ArrayBufferSlice) { + const attrTypePairs = buffer.createTypedArray(Uint32Array, 0, buffer.byteLength / 4, Endianness.BIG_ENDIAN); + const vtxDesc: GX_VtxDesc[] = []; + for (let i = 0; attrTypePairs[i + 0] !== 255; i += 2) { + const attr = attrTypePairs[i + 0]; + const type = attrTypePairs[i + 1]; + vtxDesc[attr] = { type }; + } + return vtxDesc; +} + +function createTexture(r: DisplayListRegisters, data: ArrayBufferSlice, name: string): BTI_Texture { + const minFilterTable = [ + GX.TexFilter.NEAR, + GX.TexFilter.NEAR_MIP_NEAR, + GX.TexFilter.NEAR_MIP_LIN, + GX.TexFilter.NEAR, + GX.TexFilter.LINEAR, + GX.TexFilter.LIN_MIP_NEAR, + GX.TexFilter.LIN_MIP_LIN, + ]; + + const image0 = r.bp[GX.BPRegister.TX_SETIMAGE0_I0_ID]; + const width = ((image0 >>> 0) & 0x3FF) + 1; + const height = ((image0 >>> 10) & 0x3FF) + 1; + const format: GX.TexFormat = (image0 >>> 20) & 0x0F; + const mode0 = r.bp[GX.BPRegister.TX_SETMODE0_I0_ID]; + const wrapS: GX.WrapMode = (mode0 >>> 0) & 0x03; + const wrapT: GX.WrapMode = (mode0 >>> 2) & 0x03; + const magFilter: GX.TexFilter = (mode0 >>> 4) & 0x01; + const minFilter: GX.TexFilter = minFilterTable[(mode0 >>> 5) & 0x07]; + const lodBias = ((mode0 >>> 9) & 0x05) * 32.0; + const maxAnisotropy = (mode0 >>> 19) & 0x03; + const mode1 = r.bp[GX.BPRegister.TX_SETMODE1_I0_ID]; + const minLOD = (mode1 >>> 0) & 0xF; + const maxLOD = (mode1 >>> 8) & 0xF; + console.assert(minLOD === 0); + console.assert(lodBias === 0, 'Non-zero LOD bias. This is untested'); + + const texture: BTI_Texture = { + name, + width, height, format, + data, + mipCount: 1 + maxLOD - minLOD, + paletteFormat: GX.TexPalette.RGB565, + paletteData: null, + wrapS, wrapT, + minFilter, magFilter, + minLOD, maxLOD, lodBias, maxAnisotropy, + }; + + return texture; +} + +class WoodModel { + public shadowTextureMapping = nArray(1, () => new TextureMapping()); + public shadowTextureData: BTIData; + public shadowMaterial: GXMaterialHelperGfx; + + public bushTextureMapping = new TextureMapping(); + public bushTextureData: BTIData; + public bushMaterial: GXMaterialHelperGfx; + + public shapeMain: GXShapeHelperGfx; + public shapeShadow: GXShapeHelperGfx; + + public bufferCoalescer: GfxBufferCoalescerCombo; + + constructor(globals: dGlobals) { + const device = globals.modelCache.device, cache = globals.renderer.renderCache; + + // Wood re-uses data from d_tree + const l_shadowPos = globals.findExtraSymbolData('d_tree.o', 'g_dTree_shadowPos'); + const l_shadowMatDL = globals.findExtraSymbolData('d_tree.o', 'g_dTree_shadowMatDL'); + + const g_dTree_Oba_kage_32DL = globals.findExtraSymbolData('d_tree.o', 'g_dTree_Oba_kage_32DL'); + const l_Txa_kage_32TEX = globals.findExtraSymbolData('d_tree.o', 'l_Txa_kage_32TEX'); + + const l_matDL = globals.findExtraSymbolData('d_wood.o', 'l_matDL__Q25dWood20@unnamed@d_wood_cpp@'); + const l_Oba_swood_b_cutDL = globals.findExtraSymbolData('d_wood.o', 'l_Oba_swood_b_cutDL__Q25dWood20@unnamed@d_wood_cpp@'); + const l_Oba_swood_bDL = globals.findExtraSymbolData('d_wood.o', 'l_Oba_swood_bDL__Q25dWood20@unnamed@d_wood_cpp@'); + const l_texCoord = globals.findExtraSymbolData('d_wood.o', 'l_texCoord__Q25dWood20@unnamed@d_wood_cpp@'); + const l_color = globals.findExtraSymbolData('d_wood.o', 'l_color__Q25dWood20@unnamed@d_wood_cpp@'); + const l_pos = globals.findExtraSymbolData('d_wood.o', 'l_pos__Q25dWood20@unnamed@d_wood_cpp@'); + const l_Txa_swood_bTEX = globals.findExtraSymbolData('d_wood.o', 'l_Txa_swood_bTEX__Q25dWood20@unnamed@d_wood_cpp@'); + const l_shadowVtxDescList = globals.findExtraSymbolData('d_wood.o', 'l_shadowVtxDescList$5139'); + const l_shadowVtxAttrFmtList = globals.findExtraSymbolData('d_wood.o', 'l_shadowVtxAttrFmtList$5140'); + const l_vtxDescList = globals.findExtraSymbolData('d_wood.o', 'l_vtxDescList$5156'); + const l_vtxAttrFmtList = globals.findExtraSymbolData('d_wood.o', 'l_vtxAttrFmtList$5157'); + + // @HACK: The tex coord array is being read as all zero. Hardcode it. + const l_shadowTexCoord = new ArrayBufferSlice(new Uint8Array([0, 0, 1, 0, 1, 1, 0, 1]).buffer); + + const matRegisters = new DisplayListRegisters(); + + // Shadow material + displayListRegistersInitGX(matRegisters); + displayListRegistersRun(matRegisters, l_shadowMatDL); + const shadowMat = parseMaterial(matRegisters, 'd_tree::l_shadowMatDL'); + + this.shadowMaterial = new GXMaterialHelperGfx(shadowMat); + const shadowTexture = createTexture(matRegisters, l_Txa_kage_32TEX, 'l_Txa_kage_32TEX'); + this.shadowTextureData = new BTIData(device, cache, shadowTexture); + this.shadowTextureData.fillTextureMapping(this.shadowTextureMapping[0]); + + // Shadow vert format + const shadowVatFormat = parseGxVtxAttrFmtV(l_shadowVtxAttrFmtList); + const shadowVcd = parseGxVtxDescList(l_shadowVtxDescList); + const shadowVtxLoader = compileVtxLoader(shadowVatFormat, shadowVcd); + + // Shadow verts + const shadowVtxArrays: GX_Array[] = []; + shadowVtxArrays[GX.Attr.POS] = { buffer: l_shadowPos, offs: 0, stride: getAttributeByteSize(shadowVatFormat, GX.Attr.POS) }; + shadowVtxArrays[GX.Attr.TEX0] = { buffer: l_shadowTexCoord, offs: 0, stride: getAttributeByteSize(shadowVatFormat, GX.Attr.TEX0) }; + const vtx_l_shadowDL = shadowVtxLoader.runVertices(shadowVtxArrays, g_dTree_Oba_kage_32DL); + + // Bush material + displayListRegistersInitGX(matRegisters); + displayListRegistersRun(matRegisters, l_matDL); + this.bushMaterial = new GXMaterialHelperGfx(parseMaterial(matRegisters, 'd_tree::l_matDL')); + const bushTexture = createTexture(matRegisters, l_Txa_swood_bTEX, 'l_Txa_swood_bTEX'); + this.bushTextureData = new BTIData(device, cache, bushTexture); + this.bushTextureData.fillTextureMapping(this.bushTextureMapping); + + // Bush Vert Format + const vatFormat = parseGxVtxAttrFmtV(l_vtxAttrFmtList); + const vcd = parseGxVtxDescList(l_vtxDescList); + const vtxLoader = compileVtxLoader(vatFormat, vcd); + + // Tree Verts + const vtxArrays: GX_Array[] = []; + vtxArrays[GX.Attr.POS] = { buffer: l_pos, offs: 0, stride: getAttributeByteSize(vatFormat, GX.Attr.POS) }; + vtxArrays[GX.Attr.CLR0] = { buffer: l_color, offs: 0, stride: getAttributeByteSize(vatFormat, GX.Attr.CLR0) }; + vtxArrays[GX.Attr.TEX0] = { buffer: l_texCoord, offs: 0, stride: getAttributeByteSize(vatFormat, GX.Attr.TEX0) }; + + const vtx_l_Oba_swood_bDL = vtxLoader.runVertices(vtxArrays, l_Oba_swood_bDL); + const vtx_l_Oba_swood_b_cutDL = vtxLoader.runVertices(vtxArrays, l_Oba_swood_b_cutDL); + + // Coalesce all VBs and IBs into single buffers and upload to the GPU + this.bufferCoalescer = loadedDataCoalescerComboGfx(device, [vtx_l_Oba_swood_bDL, vtx_l_shadowDL]); + + // Build an input layout and input state from the vertex layout and data + const b = this.bufferCoalescer.coalescedBuffers; + + // Build an input layout and input state from the vertex layout and data + this.shapeMain = new GXShapeHelperGfx(device, cache, b[0].vertexBuffers, b[0].indexBuffer, vtxLoader.loadedVertexLayout, vtx_l_Oba_swood_bDL); + this.shapeShadow = new GXShapeHelperGfx(device, cache, b[1].vertexBuffers, b[1].indexBuffer, shadowVtxLoader.loadedVertexLayout, vtx_l_shadowDL); + } + + public destroy(device: GfxDevice): void { + this.bufferCoalescer.destroy(device); + } +} + +//----------------------------------------- +// Types +//----------------------------------------- +const enum UnitFlags { + Active = 1 << 0, + FrustumCulled = 1 << 1, + Cut = 1 << 2, +} + +class Room_c { + mpUnits: Unit_c[] = []; +} + +class Anm_c { + +} + +class Unit_c { + mPos = vec3.create(); + mFlags: UnitFlags; + // int mAnmIdx; + // Mtx field_0x018; + mShadowMtx: mat4 = mat4.create(); + mModelMtx: mat4 = mat4.create(); + // Mtx field_0x0a8; + // Unit_c* mpNext; + // u8 field_0xdc[0x18C - 0xDC]; + + public set_ground(): number { + // @TODO: This is copied from d_tree. Should actually implement the d_wood version. + + const chk = new dBgS_GndChk(); + vec3.copy(chk.pos, this.mPos); + chk.pos[1] += 50; + + const y = globals.scnPlay.bgS.GroundCross(chk); + if (y > -Infinity) { + this.mPos[1] = y; + const pla = globals.scnPlay.bgS.GetTriPla(chk.polyInfo.bgIdx, chk.polyInfo.triIdx) + vec3.copy(scratchVec3a, pla.n); + } else { + this.mPos[1] = y; + vec3.set(scratchVec3a, 0, 1, 0); + } + + const normal = scratchVec3a; + const right = vec3.set(scratchVec3c, 1, 0, 0); + const forward = vec3.cross(scratchVec3d, normal, right); + vec3.cross(right, normal, forward); + + // Get the normal from the raycast, rotate shadow to match surface + this.mShadowMtx[0] = right[0]; + this.mShadowMtx[1] = right[1]; + this.mShadowMtx[2] = right[2]; + this.mShadowMtx[3] = this.mPos[0]; + + this.mShadowMtx[4] = normal[0]; + this.mShadowMtx[5] = normal[1]; + this.mShadowMtx[6] = normal[2]; + this.mShadowMtx[7] = 1.0 + y; + + this.mShadowMtx[8] = forward[0]; + this.mShadowMtx[9] = forward[1]; + this.mShadowMtx[10] = forward[2]; + this.mShadowMtx[11] = this.mPos[2]; + + mat4.transpose(this.mShadowMtx, this.mShadowMtx); + + return y; + } + + public set_mtx(anim: Anm_c): void { + // @TODO: Set main and show mtxs + mat4.mul(this.mModelMtx, mat4.fromTranslation(scratchMat4a, this.mPos), mat4.create()); + } + + public clear(): void { + + } + + public cc_hit_before_cut(packet: Packet_c): void { + + } + + public cc_hit_after_cut(packet: Packet_c): void { + + } + + public proc(packet: Packet_c): void { + + } +} + +export class Packet_c implements J3DPacket { + private mUnit: Unit_c[] = []; + private mRoom: Room_c[] = nArray(64, () => new Room_c()); + private mAnm: Anm_c[] = nArray(72, () => new Anm_c()); + + private _mModel: WoodModel; + + // void delete_room(s32 room_no); + // void calc_cc(); + // void update(); + // s32 search_empty_UnitID() const; + // s32 search_anm(Anm_c::Mode_e mode); + + constructor(lGlobals: dGlobals) { + globals = lGlobals; + this._mModel = new WoodModel(lGlobals); + } + + destroy(device: GfxDevice) { + this._mModel.destroy(device); + } + + put_unit(pos: vec3, room_no: number): number { + const unit = new Unit_c(); + unit.mFlags = UnitFlags.Active; + vec3.copy(unit.mPos, pos); + // TODO: assign anm + const groundY = unit.set_ground(); + if (groundY) { + this.mRoom[room_no].mpUnits.push(unit); + return this.mUnit.push(unit); + } + return -1; + } + + public calc(frameCount: number) { + + } + + public update() { + for (let i = 0; i < this.mUnit.length; i++) { + const unit = this.mUnit[i]; + if (unit.mFlags & UnitFlags.Active) { + // TODO: Frustum Culling + // unit.mFlags |= UnitFlags.FrustumCulled; + unit.set_mtx(this.mAnm); + } + } + + // TODO: Add to the Render List + } + + public draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { + // for (s32 i = 0; i < (s32)ARRAY_SIZE(mRoom); room++, i++) { + // for (Unit_c *data = room->mpUnit; data != NULL; data = data->mpNext) { + // if ((pUnit->mFlags & 2) == 0) { + // GFLoadPosMtxImm(pUnit->field_0x0a8, 0); + // GXCallDisplayList(dl, dlSize); + // } + // } + // } + + const worldToView = viewerInput.camera.viewMatrix; + const worldCamPos = mat4.getTranslation(scratchVec3b, viewerInput.camera.worldMatrix); + + // Draw shadows + let template = renderInstManager.pushTemplate(); + { + template.sortKey = makeSortKey(GfxRendererLayer.TRANSLUCENT); + dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); + // Set the shadow color. Pulled from d_tree::l_shadowColor$4656 + colorFromRGBA(materialParams.u_Color[ColorKind.C0], 0, 0, 0, 0x64 / 0xFF); + this._mModel.shadowMaterial.allocateMaterialParamsDataOnInst(template, materialParams); + this._mModel.shadowMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); + template.setSamplerBindingsFromTextureMappings(this._mModel.shadowTextureMapping); + + + for (let r = 0; r < this.mRoom.length; r++) { + const units = this.mRoom[r].mpUnits; + for (let i = 0; i < units.length; i++) { + const unit = units[i]; + + if (unit.mFlags & UnitFlags.FrustumCulled) + continue; + if (distanceCull(worldCamPos, unit.mPos)) + continue; + + // @TODO: Fix this + setColorFromRoomNo(globals, materialParams, r); + + const shadowRenderInst = renderInstManager.newRenderInst(); + this._mModel.shapeShadow.setOnRenderInst(shadowRenderInst); + mat4.mul(drawParams.u_PosMtx[0], worldToView, unit.mShadowMtx); + this._mModel.shadowMaterial.allocateDrawParamsDataOnInst(shadowRenderInst, drawParams); + renderInstManager.submitRenderInst(shadowRenderInst); + } + } + } + renderInstManager.popTemplate(); + + // Draw bushes + template = renderInstManager.pushTemplate(); + { + template.setSamplerBindingsFromTextureMappings([this._mModel.bushTextureMapping]); + const materialParamsOffs = this._mModel.bushMaterial.allocateMaterialParamsDataOnInst(template, materialParams); + this._mModel.bushMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); + + colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, 1); + + for (let r = 0; r < this.mRoom.length; r++) { + const units = this.mRoom[r].mpUnits; + for (let i = 0; i < units.length; i++) { + const unit = units[i]; + + if (unit.mFlags & UnitFlags.FrustumCulled) + continue; + if (distanceCull(worldCamPos, unit.mPos)) + continue; + + // @TODO: Fix this + setColorFromRoomNo(globals, materialParams, r); + + const renderInst = renderInstManager.newRenderInst(); + this._mModel.shapeMain.setOnRenderInst(renderInst); + mat4.mul(drawParams.u_PosMtx[0], worldToView, unit.mModelMtx); + this._mModel.bushMaterial.allocateDrawParamsDataOnInst(renderInst, drawParams); + renderInstManager.submitRenderInst(renderInst); + } + } + } + renderInstManager.popTemplate(); + } +} diff --git a/src/ZeldaWindWaker/framework.ts b/src/ZeldaWindWaker/framework.ts index ec6091062..05a227733 100644 --- a/src/ZeldaWindWaker/framework.ts +++ b/src/ZeldaWindWaker/framework.ts @@ -26,6 +26,7 @@ export const enum fpc__ProcessName { d_a_ep = 0x00BA, d_a_ff = 0x00BC, d_a_kamome = 0x00C3, + d_a_obj_wood = 0x010C, d_a_obj_flame = 0x010D, d_a_tbox = 0x0126, d_a_kytag00 = 0x0181, From a5799f036dddf204c23e8ed9299ed0f88ac1e31d Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 21 Oct 2024 09:53:59 -0600 Subject: [PATCH 05/22] Add cLib_chaseS() function to SComponent This is used by d_wood's animation functions --- src/ZeldaWindWaker/SComponent.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/ZeldaWindWaker/SComponent.ts b/src/ZeldaWindWaker/SComponent.ts index 7aba3920d..d02edf026 100644 --- a/src/ZeldaWindWaker/SComponent.ts +++ b/src/ZeldaWindWaker/SComponent.ts @@ -129,6 +129,24 @@ export function cM__Deg2Short(v: number): number { return cM__Rad2Short(v * MathConstants.DEG_TO_RAD); } +// Move `value` closer to `target` by the amount specified in `step`. +// If `value` would cross `target`, it is set to `target`. Returns 1 if value has reached the target, 0 otherwise. +export function cLib_chaseS(value: { x: number }, target: number, step: number): number { + if (step) { + if (value.x > target) { + step = -step; + } + value.x += step; + if (step * (value.x - target) >= 0) { + value.x = target; + return 1; + } + } else if (value.x == target) { + return 1; + } + return 0; +} + export function cLib_targetAngleX(p0: ReadonlyVec3, p1: ReadonlyVec3): number { const dy = p1[1] - p0[1]; const dist = cLib_distanceXZ(p0, p1); From a3efb18d6503adabad9ee36600e2f1558f7e1662 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 21 Oct 2024 09:55:55 -0600 Subject: [PATCH 06/22] Add mDoMtx_copy() to m_do_mtx Since the src and dst parameters are switched from usual, I think it's worth adding this so that it's easy to copy paste from decomp --- src/ZeldaWindWaker/m_do_mtx.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ZeldaWindWaker/m_do_mtx.ts b/src/ZeldaWindWaker/m_do_mtx.ts index 1041875b5..045275641 100644 --- a/src/ZeldaWindWaker/m_do_mtx.ts +++ b/src/ZeldaWindWaker/m_do_mtx.ts @@ -1,5 +1,5 @@ -import { ReadonlyVec3, mat4, quat, vec3 } from "gl-matrix"; +import { ReadonlyMat4, ReadonlyVec3, mat4, quat, vec3 } from "gl-matrix"; import { computeModelMatrixR, transformVec3Mat4w1 } from "../MathHelpers.js"; import { cM__Short2Rad } from "./SComponent.js"; @@ -39,6 +39,10 @@ export function mDoMtx_XYZrotM(dst: mat4, v: vec3): void { mat4.rotateX(dst, dst, cM__Short2Rad(v[0])); } +export function mDoMtx_copy(src: ReadonlyMat4, dst: mat4): void { + mat4.copy(dst, src); +} + export const calc_mtx = mat4.create(); export function MtxTrans(pos: vec3, concat: boolean, m: mat4 = calc_mtx): void { From 5c67defe550700c13990326caa62a85d9200efcd Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 21 Oct 2024 10:08:35 -0600 Subject: [PATCH 07/22] Update d_wood.ts to about 70% Idle animation fully implemented --- src/ZeldaWindWaker/d_wood.ts | 595 ++++++++++++++++++++++++++++++----- 1 file changed, 511 insertions(+), 84 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index dd0d0276c..3583720c8 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -17,6 +17,32 @@ import { parseMaterial } from '../gx/gx_material.js'; import { Endianness } from '../endian.js'; import * as GX from '../gx/gx_enum.js'; import { GfxDevice } from '../gfx/platform/GfxPlatform.js'; +import { dKyw_get_wind_pow, dKyw_get_wind_vec } from './d_kankyo_wether.js'; +import { cLib_chaseS, cM__Short2Rad, cM_atan2s } from './SComponent.js'; +import { dStage_roomStatus_c } from './d_stage.js'; +import { mDoMtx_copy, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, MtxTrans } from './m_do_mtx.js'; +import { assert } from '../util.js'; + +//----------------------------------------- +// Types +//----------------------------------------- +const enum UnitFlags { + Inactive = 0, + Active = 1 << 0, + IsFrustumCulled = 1 << 1, + IsCut = 1 << 2, +} + +enum AnimMode_e { + Cut = 0, // Chopping down + PushInto = 1, // Attacked or collided with, but not chopped + PushBack = 2, // Second half of PushInto, returning to normal + Fan = 3, // When hit with fan (does nothing) + Norm = 4, // Idle animation + ToNorm = 5, // Blend back to the normal animation + + _Max +}; //----------------------------------------- // Globals @@ -31,6 +57,74 @@ const scratchMat4a = mat4.create(); const materialParams = new MaterialParams(); const drawParams = new DrawParams(); +const kUnitCount = 200; +const kRoomCount = 64; +const kAnimCount = 72; + +let sAnimInitNum = 0; +let sAnmNormNum = 0; + +//----------------------------------------- +// Extracted Data +//----------------------------------------- +const l_animAttrs: { + /* 0x0 */ unkUShort0: number; + /* 0x2 */ unkShort1: number; + /* 0x4 */ unkShort2: number; + /* 0x6 */ unkShort3: number; + /* 0x8 */ unkFloat: number; +}[][] = [ + [{ + unkUShort0: 0, + unkShort1: 0x50, + unkShort2: 0x5DC, + unkShort3: 0x32, + unkFloat: 0.6 + }, { + unkUShort0: 0, + unkShort1: 0x1E, + unkShort2: 0xC80, + unkShort3: 0xA, + unkFloat: 0.2, + }], [{ + unkUShort0: 1, + unkShort1: 0x96, + unkShort2: 0x4B0, + unkShort3: 0x96, + unkFloat: 0.6, + }, { + unkUShort0: 2, + unkShort1: 0x32, + unkShort2: 0x898, + unkShort3: 0x1E, + unkFloat: 0.2, + }], [{ + unkUShort0: 2, + unkShort1: 0xC8, + unkShort2: 0x898, + unkShort3: 0x12C, + unkFloat: 0.6, + }, { + unkUShort0: 1, + unkShort1: 0x1E, + unkShort2: 0xFA0, + unkShort3: 0x32, + unkFloat: 0.2, + }], [{ + unkUShort0: 2, + unkShort1: 0xC8, + unkShort2: 0x1838, + unkShort3: 0x1F4, + unkFloat: 0.6, + }, { + unkUShort0: 1, + unkShort1: 0x1E, + unkShort2: 0x3E8, + unkShort3: 0x32, + unkFloat: 0.2, + }] + ]; + //----------------------------------------- // Helpers //----------------------------------------- @@ -128,6 +222,7 @@ class WoodModel { public bushMaterial: GXMaterialHelperGfx; public shapeMain: GXShapeHelperGfx; + public shapeTrunk: GXShapeHelperGfx; public shapeShadow: GXShapeHelperGfx; public bufferCoalescer: GfxBufferCoalescerCombo; @@ -203,14 +298,15 @@ class WoodModel { const vtx_l_Oba_swood_b_cutDL = vtxLoader.runVertices(vtxArrays, l_Oba_swood_b_cutDL); // Coalesce all VBs and IBs into single buffers and upload to the GPU - this.bufferCoalescer = loadedDataCoalescerComboGfx(device, [vtx_l_Oba_swood_bDL, vtx_l_shadowDL]); + this.bufferCoalescer = loadedDataCoalescerComboGfx(device, [vtx_l_Oba_swood_bDL, vtx_l_Oba_swood_b_cutDL, vtx_l_shadowDL]); // Build an input layout and input state from the vertex layout and data const b = this.bufferCoalescer.coalescedBuffers; // Build an input layout and input state from the vertex layout and data this.shapeMain = new GXShapeHelperGfx(device, cache, b[0].vertexBuffers, b[0].indexBuffer, vtxLoader.loadedVertexLayout, vtx_l_Oba_swood_bDL); - this.shapeShadow = new GXShapeHelperGfx(device, cache, b[1].vertexBuffers, b[1].indexBuffer, shadowVtxLoader.loadedVertexLayout, vtx_l_shadowDL); + this.shapeTrunk = new GXShapeHelperGfx(device, cache, b[1].vertexBuffers, b[1].indexBuffer, vtxLoader.loadedVertexLayout, vtx_l_Oba_swood_b_cutDL); + this.shapeShadow = new GXShapeHelperGfx(device, cache, b[2].vertexBuffers, b[2].indexBuffer, shadowVtxLoader.loadedVertexLayout, vtx_l_shadowDL); } public destroy(device: GfxDevice): void { @@ -219,32 +315,222 @@ class WoodModel { } //----------------------------------------- -// Types +// Classes //----------------------------------------- -const enum UnitFlags { - Active = 1 << 0, - FrustumCulled = 1 << 1, - Cut = 1 << 2, -} - +/** + * A linked list of Units, so that they can be traversed room by room + */ class Room_c { - mpUnits: Unit_c[] = []; + mRootUnit: Unit_c; + + entry_unit(unit: Unit_c): void { + unit.mNextUnit = this.mRootUnit; + this.mRootUnit = unit; + return; + } + + delete_all_unit() { + while (this.mRootUnit) { + let unit = this.mRootUnit; + this.mRootUnit = unit.mNextUnit!; + unit.clear(); + } + } } class Anm_c { + /* 0x00 */ mModelMtx: mat4 = mat4.create(); + /* 0x30 */ mTrunkModelMtx: mat4 = mat4.create(); + + /* 0x60 */ mMode: AnimMode_e = AnimMode_e._Max; + + /* 0x64 */ mCountdown: number; + /* 0x66 */ mWindDir: number; // The direction towards the actor who instigated this animation + /* 0x68 */ mWindPow: number; // 0.0 - 1.0 + /* 0x6c */ mPosOffsetY: number; + /* 0x70 */ mPosOffsetZ: number; + /* 0x74 */ mVelY: number; + + /* 0x78 */ mRotY: number[] = [0, 0]; + /* 0x7c */ mRotX: number[] = [0, 0]; + /* 0x80 */ mUnkArr2: number[] = [0, 0]; + /* 0x84 */ mUnkArr3: number[] = [0, 0]; + + /* 0x88 */ mNextAnimIdx: number; // Corresponds to the index in Packet_c::mAnm; + + /* 0x8A */ mAlphaScale: number = 0xFF; + + public play(packet: Packet_c): void { + switch (this.mMode) { + case AnimMode_e.Cut: return this.mode_cut(packet); + case AnimMode_e.PushInto: return this.mode_push_into(packet); + case AnimMode_e.PushBack: return this.mode_push_back(packet); + case AnimMode_e.Fan: return this.mode_fan(packet); + case AnimMode_e.Norm: return this.mode_norm(packet); + case AnimMode_e.ToNorm: return this.mode_to_norm(packet); + default: return; + } + } + public copy_angamp(anm: Anm_c): void { + + } + + // Animations are assigned from the Packet to specific Wood instances (Bushes) when a new animation starts + // Each animation mode has an mode_*_init() function which is called when the animation is started + // The mode_*() function is called to update the animation each frame, until finished + + // Animate when cut with a weapon + public mode_cut_init(anm: Anm_c, targetAngle: number): void { + for (let i = 0; i < 2; i++) { + this.mRotY[i] = 0; + this.mRotX[i] = 0; + this.mUnkArr2[i] = 0; + this.mUnkArr3[i] = 0; + } + + this.mWindDir = targetAngle; + this.mVelY = 18.0; + this.mPosOffsetY = 0.0; + this.mPosOffsetZ = 0.0; + this.mAlphaScale = 0xff; + this.mCountdown = 20; + this.mMode = AnimMode_e.Cut; + } + + public mode_cut(packet: Packet_c): void { + this.mVelY = this.mVelY - 3.0; + if (this.mVelY < -40.0) { + this.mVelY = -40.0; + } + + this.mPosOffsetY = this.mPosOffsetY + this.mVelY; + this.mPosOffsetZ = this.mPosOffsetZ + 2.5; + this.mRotX[0] = this.mRotX[0] - 200; + + mDoMtx_YrotS(scratchMat4a, this.mWindDir); + MtxTrans([0.0, this.mPosOffsetY, this.mPosOffsetZ], true, scratchMat4a); + mDoMtx_XrotM(scratchMat4a, this.mRotX[0]); + mDoMtx_YrotM(scratchMat4a, -this.mWindDir); + mDoMtx_copy(scratchMat4a, this.mModelMtx); + + // Fade out the bush as it falls + if (this.mCountdown < 20) { + let alphaScale = this.mAlphaScale - 14; + if (alphaScale < 0) { + alphaScale = 0; + } + this.mAlphaScale = alphaScale; + } + + if (this.mCountdown > 0) { + this.mCountdown = this.mCountdown + -1; + } + } + + // Animate when pushed into + public mode_push_into_init(anm: Anm_c, targetAngle: number): void { + + } + + public mode_push_into(packet: Packet_c): void { + + } + + + // Animate when pushed back + public mode_push_back_init(): void { + + } + + public mode_push_back(packet: Packet_c): void { + + } + + + // Animate when hit with the fan item (does nothing) + public mode_fan(packet: Packet_c): void { + + } + + + // Animate normally (not interacting with character) + public mode_norm_init(): void { + this.mMode = AnimMode_e.Norm; + + for (let i = 0; i < 2; i++) { + this.mRotY[i] = (sAnimInitNum << 0xd); + this.mRotX[i] = (sAnimInitNum << 0xd); + this.mUnkArr2[i] = l_animAttrs[0][i].unkShort1; + this.mUnkArr3[i] = l_animAttrs[0][i].unkShort3; + } + + this.mAlphaScale = 0xff; + + sAnimInitNum = (sAnimInitNum + 1) % 8; + } + + public mode_norm(packet: Packet_c): void { + let phase; + if (this.mWindPow < 0.33) { + phase = 0; + } else { + if (this.mWindPow < 0.66) { + phase = 1; + } else { + phase = 2; + } + } + + let fVar1 = 0.0; + let fVar6 = fVar1; + for (let i = 0; i < 2; i++) { + const animAttr = l_animAttrs[phase][i]; + const unk2 = animAttr.unkShort2; + const unk1 = animAttr.unkShort1; + const unk3 = animAttr.unkShort3; + const unk4 = animAttr.unkFloat; + + this.mRotY[i] += animAttr.unkUShort0; + this.mRotX[i] += unk2; + cLib_chaseS({ x: this.mUnkArr2[i] }, unk1, 2); + cLib_chaseS({ x: this.mUnkArr2[i] }, unk3, 2); + + fVar1 += this.mUnkArr2[i] * Math.cos(cM__Short2Rad((this.mRotY[i]))); + fVar6 += this.mUnkArr3[i] * (unk4 + Math.cos(cM__Short2Rad((this.mRotX[i])))); + } + + mDoMtx_YrotS(this.mModelMtx, fVar1 + this.mWindDir); + mDoMtx_XrotM(this.mModelMtx, fVar6); + mDoMtx_YrotM(this.mModelMtx, -this.mWindDir); + } + + public mode_norm_set_wind(pow: number, dir: number): void { + this.mWindDir = dir; + this.mWindPow = pow; + } + + + // Unsure? + public mode_to_norm_init(anmIdx: number): void { + + } + + public mode_to_norm(packet: Packet_c): void { + + } } class Unit_c { mPos = vec3.create(); - mFlags: UnitFlags; - // int mAnmIdx; - // Mtx field_0x018; - mShadowMtx: mat4 = mat4.create(); - mModelMtx: mat4 = mat4.create(); - // Mtx field_0x0a8; - // Unit_c* mpNext; - // u8 field_0xdc[0x18C - 0xDC]; + mFlags: UnitFlags = 0; + mAnmIdx: number = 0; + mModelViewMtx: mat4 = mat4.create(); + mTrunkModelViewMtx: mat4 = mat4.create(); + mShadowModelMtx: mat4 = mat4.create(); + mShadowModelViewMtx: mat4 = mat4.create(); + + mNextUnit: Unit_c | null = null; // The next unit in the same room (a linked list) public set_ground(): number { // @TODO: This is copied from d_tree. Should actually implement the d_wood version. @@ -269,33 +555,49 @@ class Unit_c { vec3.cross(right, normal, forward); // Get the normal from the raycast, rotate shadow to match surface - this.mShadowMtx[0] = right[0]; - this.mShadowMtx[1] = right[1]; - this.mShadowMtx[2] = right[2]; - this.mShadowMtx[3] = this.mPos[0]; + this.mShadowModelMtx[0] = right[0]; + this.mShadowModelMtx[1] = right[1]; + this.mShadowModelMtx[2] = right[2]; + this.mShadowModelMtx[3] = this.mPos[0]; - this.mShadowMtx[4] = normal[0]; - this.mShadowMtx[5] = normal[1]; - this.mShadowMtx[6] = normal[2]; - this.mShadowMtx[7] = 1.0 + y; + this.mShadowModelMtx[4] = normal[0]; + this.mShadowModelMtx[5] = normal[1]; + this.mShadowModelMtx[6] = normal[2]; + this.mShadowModelMtx[7] = 1.0 + y; - this.mShadowMtx[8] = forward[0]; - this.mShadowMtx[9] = forward[1]; - this.mShadowMtx[10] = forward[2]; - this.mShadowMtx[11] = this.mPos[2]; + this.mShadowModelMtx[8] = forward[0]; + this.mShadowModelMtx[9] = forward[1]; + this.mShadowModelMtx[10] = forward[2]; + this.mShadowModelMtx[11] = this.mPos[2]; - mat4.transpose(this.mShadowMtx, this.mShadowMtx); + mat4.transpose(this.mShadowModelMtx, this.mShadowModelMtx); return y; } - public set_mtx(anim: Anm_c): void { - // @TODO: Set main and show mtxs - mat4.mul(this.mModelMtx, mat4.fromTranslation(scratchMat4a, this.mPos), mat4.create()); + /** + * Compute modelView matrices for the body, trunk, and drop shadow + * @param anim + */ + public set_mtx(anims: Anm_c[]): void { + mDoMtx_copy(anims[this.mAnmIdx].mModelMtx, scratchMat4a); + scratchMat4a[12] += this.mPos[0]; + scratchMat4a[13] += this.mPos[1]; + scratchMat4a[14] += this.mPos[2]; + mat4.mul(this.mModelViewMtx, globals.camera.viewMatrix, scratchMat4a); + + mDoMtx_copy(anims[this.mAnmIdx].mTrunkModelMtx, scratchMat4a); + scratchMat4a[12] += this.mPos[0]; + scratchMat4a[13] += this.mPos[1]; + scratchMat4a[14] += this.mPos[2]; + mat4.mul(this.mTrunkModelViewMtx, globals.camera.viewMatrix, scratchMat4a); + + mat4.mul(this.mShadowModelViewMtx, globals.camera.viewMatrix, this.mShadowModelMtx); } public clear(): void { - + this.mFlags = UnitFlags.Inactive; + this.mNextUnit = null; } public cc_hit_before_cut(packet: Packet_c): void { @@ -303,51 +605,165 @@ class Unit_c { } public cc_hit_after_cut(packet: Packet_c): void { - + // Does nothing } public proc(packet: Packet_c): void { - + // If this unit is active, and performing a non-normal animation... + if (this.mFlags & UnitFlags.Active) { + if (this.mAnmIdx >= 8) { + const anim = packet.get_anm(this.mAnmIdx); + if (anim.mMode == AnimMode_e.ToNorm) { + if (anim.mCountdown <= 0) { + this.mAnmIdx = anim.mNextAnimIdx; + anim.mMode = AnimMode_e._Max; + } + } else if (anim.mMode == AnimMode_e.Cut) { + if (anim.mCountdown <= 0) { + const newAnimIdx = packet.search_anm(AnimMode_e.Norm); + this.mAnmIdx = newAnimIdx; + anim.mMode = AnimMode_e._Max; + this.mFlags |= UnitFlags.IsCut; + } + } else if (anim.mMode == AnimMode_e._Max) { + this.mAnmIdx = packet.search_anm(AnimMode_e.Norm); + } + } + } } } export class Packet_c implements J3DPacket { - private mUnit: Unit_c[] = []; - private mRoom: Room_c[] = nArray(64, () => new Room_c()); - private mAnm: Anm_c[] = nArray(72, () => new Anm_c()); + private mUnit: Unit_c[] = nArray(kUnitCount, () => new Unit_c()); + private mRoom: Room_c[] = nArray(kRoomCount, () => new Room_c()); + private mAnm: Anm_c[] = nArray(kAnimCount, () => new Anm_c()); private _mModel: WoodModel; // void delete_room(s32 room_no); - // void calc_cc(); - // void update(); - // s32 search_empty_UnitID() const; - // s32 search_anm(Anm_c::Mode_e mode); constructor(lGlobals: dGlobals) { globals = lGlobals; this._mModel = new WoodModel(lGlobals); + + for (let i = 0; i < 8; i++) { + this.mAnm[i].mode_norm_init(); + } } destroy(device: GfxDevice) { this._mModel.destroy(device); } + get_anm(idx: number): Anm_c { + return this.mAnm[idx]; + } + + search_anm(i_mode: AnimMode_e): number { + let animIdx: number; + + assert((i_mode >= 0) && (i_mode < AnimMode_e._Max)); + + if (i_mode == AnimMode_e.Norm) { + animIdx = sAnmNormNum++; + sAnmNormNum = sAnmNormNum % 8; + } else { + // Return the first anim slot which has an unset mode + animIdx = 8; + for (let i = 0; i < 64; i++) { + if (this.mAnm[animIdx].mMode == AnimMode_e._Max) { + return animIdx; + } + animIdx++; + } + + // If none are available, return the first one which has a higher mode + animIdx = 8; + for (let i = 0; i < 64; i++) { + if (i_mode < this.mAnm[animIdx].mMode) { + return animIdx; + } + animIdx++; + } + + // If no available anim slot is found, return -1 + animIdx = -1; + } + + return animIdx; + } + + search_empty_UnitID(): number { + for (let i = 0; i < kUnitCount; i++) { + if (this.mUnit[i].mFlags == 0) { + return i; + } + } + + return kUnitCount; + } + put_unit(pos: vec3, room_no: number): number { - const unit = new Unit_c(); - unit.mFlags = UnitFlags.Active; - vec3.copy(unit.mPos, pos); - // TODO: assign anm - const groundY = unit.set_ground(); - if (groundY) { - this.mRoom[room_no].mpUnits.push(unit); - return this.mUnit.push(unit); + const unitIdx = this.search_empty_UnitID(); + if (unitIdx != kUnitCount) { + const unit = this.mUnit[unitIdx]; + unit.mFlags = UnitFlags.Active; + + vec3.copy(unit.mPos, pos); + + unit.mAnmIdx = this.search_anm(AnimMode_e.Norm); + + const groundY = unit.set_ground(); + if (groundY) { + this.mRoom[room_no].entry_unit(unit); + } else { + unit.clear(); + } + } + return unitIdx; + } + + // Calculate collisions + public calc_cc() { + const roomIdx = globals.mStayNo; + + if ((roomIdx >= 0) && (roomIdx < kRoomCount)) { + // dComIfG_Ccsp() -> SetMassAttr(L_attr.kCollisionRad1, L_attr.kCollisionHeight1, (u8)0x13, 1); + + const room = this.mRoom[roomIdx]; + for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { + if ((unit.mFlags & UnitFlags.IsCut) == 0) { + unit.cc_hit_before_cut(this); + } + } + + // dComIfG_Ccsp() -> SetMassAttr(L_attr.kCollisionRad2, L_attr.kCollisionHeight2, (u8)0x12, 1); + for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { + if ((unit.mFlags & UnitFlags.IsCut) != 0) { + unit.cc_hit_after_cut(this); + } + } } - return -1; } public calc(frameCount: number) { + this.calc_cc(); + const windVec = dKyw_get_wind_vec(globals.g_env_light); + const windPow = dKyw_get_wind_pow(globals.g_env_light); + const windAngle = cM_atan2s(windVec[0], windVec[2]); + + for (let i = 0; i < 8; i++) { + this.mAnm[i].mode_norm_set_wind(0.2, windAngle); + } + + for (let i = 0; i < kAnimCount; i++) { + this.mAnm[i].play(this); + } + + for (let i = 0; i < kUnitCount; i++) { + this.mUnit[i].proc(this); + } } public update() { @@ -355,7 +771,7 @@ export class Packet_c implements J3DPacket { const unit = this.mUnit[i]; if (unit.mFlags & UnitFlags.Active) { // TODO: Frustum Culling - // unit.mFlags |= UnitFlags.FrustumCulled; + // unit.mFlags |= UnitFlags.IsFrustumCulled; unit.set_mtx(this.mAnm); } } @@ -364,46 +780,29 @@ export class Packet_c implements J3DPacket { } public draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { - // for (s32 i = 0; i < (s32)ARRAY_SIZE(mRoom); room++, i++) { - // for (Unit_c *data = room->mpUnit; data != NULL; data = data->mpNext) { - // if ((pUnit->mFlags & 2) == 0) { - // GFLoadPosMtxImm(pUnit->field_0x0a8, 0); - // GXCallDisplayList(dl, dlSize); - // } - // } - // } - const worldToView = viewerInput.camera.viewMatrix; const worldCamPos = mat4.getTranslation(scratchVec3b, viewerInput.camera.worldMatrix); - // Draw shadows + // Draw drop shadows let template = renderInstManager.pushTemplate(); { template.sortKey = makeSortKey(GfxRendererLayer.TRANSLUCENT); - dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); // Set the shadow color. Pulled from d_tree::l_shadowColor$4656 colorFromRGBA(materialParams.u_Color[ColorKind.C0], 0, 0, 0, 0x64 / 0xFF); this._mModel.shadowMaterial.allocateMaterialParamsDataOnInst(template, materialParams); this._mModel.shadowMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); template.setSamplerBindingsFromTextureMappings(this._mModel.shadowTextureMapping); - for (let r = 0; r < this.mRoom.length; r++) { - const units = this.mRoom[r].mpUnits; - for (let i = 0; i < units.length; i++) { - const unit = units[i]; - - if (unit.mFlags & UnitFlags.FrustumCulled) + for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { + if (unit.mFlags & UnitFlags.IsFrustumCulled) continue; if (distanceCull(worldCamPos, unit.mPos)) continue; - // @TODO: Fix this - setColorFromRoomNo(globals, materialParams, r); - const shadowRenderInst = renderInstManager.newRenderInst(); this._mModel.shapeShadow.setOnRenderInst(shadowRenderInst); - mat4.mul(drawParams.u_PosMtx[0], worldToView, unit.mShadowMtx); + mat4.copy(drawParams.u_PosMtx[0], unit.mShadowModelViewMtx); this._mModel.shadowMaterial.allocateDrawParamsDataOnInst(shadowRenderInst, drawParams); renderInstManager.submitRenderInst(shadowRenderInst); } @@ -418,24 +817,52 @@ export class Packet_c implements J3DPacket { const materialParamsOffs = this._mModel.bushMaterial.allocateMaterialParamsDataOnInst(template, materialParams); this._mModel.bushMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); + // @TODO: + // GFSetAlphaCompare(GX_GREATER, 0x80, GX_AOP_OR, GX_GREATER, 0x80); + + // Set alpha color colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, 1); for (let r = 0; r < this.mRoom.length; r++) { - const units = this.mRoom[r].mpUnits; - for (let i = 0; i < units.length; i++) { - const unit = units[i]; + // Set the room color and fog params + setColorFromRoomNo(globals, materialParams, r); + dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); - if (unit.mFlags & UnitFlags.FrustumCulled) + for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { + if (unit.mFlags & UnitFlags.IsFrustumCulled) continue; if (distanceCull(worldCamPos, unit.mPos)) continue; - // @TODO: Fix this - setColorFromRoomNo(globals, materialParams, r); - + // If this bush is not chopped down, draw the main body + if (unit.mFlags & ~UnitFlags.IsCut) { + // TODO: Enable alpha when the bush is fading after being cut down + // u32 alphaScale = this->mAnm[pUnit->mAnmIdx].mAlphaScale; + // alphaColor.a = alphaScale; + + // if ((alphaScale & 0xff) != 0xff) { + // GFSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_GREATER, + // 0); // Disable Alpha Test + // } + + const renderInst = renderInstManager.newRenderInst(); + this._mModel.shapeMain.setOnRenderInst(renderInst); + mat4.copy(drawParams.u_PosMtx[0], unit.mModelViewMtx); + this._mModel.bushMaterial.allocateDrawParamsDataOnInst(renderInst, drawParams); + renderInstManager.submitRenderInst(renderInst); + + // if ((alphaScale & 0xff) != 0xff) { + // GFSetAlphaCompare(GX_GREATER, L_Alpha_Cutoff, GX_AOP_OR, GX_GREATER, + // L_Alpha_Cutoff); // Alpha Test < 50% + // } + // alphaColor.a = 0xff; + // GFSetTevColor(GX_TEVREG2, alphaColor); + } + + // Always draw the trunk const renderInst = renderInstManager.newRenderInst(); - this._mModel.shapeMain.setOnRenderInst(renderInst); - mat4.mul(drawParams.u_PosMtx[0], worldToView, unit.mModelMtx); + this._mModel.shapeTrunk.setOnRenderInst(renderInst); + mat4.copy(drawParams.u_PosMtx[0], unit.mTrunkModelViewMtx); this._mModel.bushMaterial.allocateDrawParamsDataOnInst(renderInst, drawParams); renderInstManager.submitRenderInst(renderInst); } From 283aac30e5623f247893e5528f7cee4b71d2ebee Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 21 Oct 2024 17:34:32 -0600 Subject: [PATCH 08/22] d_wood alpha improvements * Enable alpha testing on bushes (greatly improves aliasing on leaf edges) * Render to the XLU BG display list. This fixes an ordering issue. We must render after most bg objects because of the alpha testing. * Disable alpha test when fading out after a bush has been cut --- src/ZeldaWindWaker/d_wood.ts | 61 ++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 3583720c8..036c59731 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -61,6 +61,8 @@ const kUnitCount = 200; const kRoomCount = 64; const kAnimCount = 72; +const kAlphaCutoff = 0x80 / 0xFF; + let sAnimInitNum = 0; let sAnmNormNum = 0; @@ -278,7 +280,16 @@ class WoodModel { // Bush material displayListRegistersInitGX(matRegisters); displayListRegistersRun(matRegisters, l_matDL); - this.bushMaterial = new GXMaterialHelperGfx(parseMaterial(matRegisters, 'd_tree::l_matDL')); + + const material = parseMaterial(matRegisters, 'd_tree::l_matDL'); + material.alphaTest.op = GX.AlphaOp.OR; + material.alphaTest.compareA = GX.CompareType.GREATER; + material.alphaTest.compareB = GX.CompareType.GREATER; + material.alphaTest.referenceA = kAlphaCutoff; + material.alphaTest.referenceB = kAlphaCutoff; + material.hasDynamicAlphaTest = true; + this.bushMaterial = new GXMaterialHelperGfx(material); + const bushTexture = createTexture(matRegisters, l_Txa_swood_bTEX, 'l_Txa_swood_bTEX'); this.bushTextureData = new BTIData(device, cache, bushTexture); this.bushTextureData.fillTextureMapping(this.bushTextureMapping); @@ -358,7 +369,7 @@ class Anm_c { /* 0x88 */ mNextAnimIdx: number; // Corresponds to the index in Packet_c::mAnm; - /* 0x8A */ mAlphaScale: number = 0xFF; + /* 0x8A */ mAlpha: number = 0xFF; public play(packet: Packet_c): void { switch (this.mMode) { @@ -393,7 +404,7 @@ class Anm_c { this.mVelY = 18.0; this.mPosOffsetY = 0.0; this.mPosOffsetZ = 0.0; - this.mAlphaScale = 0xff; + this.mAlpha = 0xff; this.mCountdown = 20; this.mMode = AnimMode_e.Cut; } @@ -416,11 +427,11 @@ class Anm_c { // Fade out the bush as it falls if (this.mCountdown < 20) { - let alphaScale = this.mAlphaScale - 14; + let alphaScale = this.mAlpha - 14; if (alphaScale < 0) { alphaScale = 0; } - this.mAlphaScale = alphaScale; + this.mAlpha = alphaScale; } if (this.mCountdown > 0) { @@ -465,7 +476,7 @@ class Anm_c { this.mUnkArr3[i] = l_animAttrs[0][i].unkShort3; } - this.mAlphaScale = 0xff; + this.mAlpha = 0xff; sAnimInitNum = (sAnimInitNum + 1) % 8; } @@ -783,6 +794,9 @@ export class Packet_c implements J3DPacket { const worldToView = viewerInput.camera.viewMatrix; const worldCamPos = mat4.getTranslation(scratchVec3b, viewerInput.camera.worldMatrix); + // Render to the XLU BG display list (after the bg terrain). We want to render late since we are alpha tested. + renderInstManager.setCurrentList(globals.dlst.bg[1]); + // Draw drop shadows let template = renderInstManager.pushTemplate(); { @@ -813,13 +827,14 @@ export class Packet_c implements J3DPacket { // Draw bushes template = renderInstManager.pushTemplate(); { + // Enable alpha testing at 50% + materialParams.u_DynamicAlphaRefA = kAlphaCutoff; + materialParams.u_DynamicAlphaRefB = kAlphaCutoff; + template.setSamplerBindingsFromTextureMappings([this._mModel.bushTextureMapping]); const materialParamsOffs = this._mModel.bushMaterial.allocateMaterialParamsDataOnInst(template, materialParams); this._mModel.bushMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); - // @TODO: - // GFSetAlphaCompare(GX_GREATER, 0x80, GX_AOP_OR, GX_GREATER, 0x80); - // Set alpha color colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, 1); @@ -835,15 +850,16 @@ export class Packet_c implements J3DPacket { continue; // If this bush is not chopped down, draw the main body - if (unit.mFlags & ~UnitFlags.IsCut) { - // TODO: Enable alpha when the bush is fading after being cut down - // u32 alphaScale = this->mAnm[pUnit->mAnmIdx].mAlphaScale; - // alphaColor.a = alphaScale; + if ((unit.mFlags & UnitFlags.IsCut) == 0) { + // The cut animation reduces alpha over time + const cutAlpha = this.mAnm[unit.mAnmIdx].mAlpha; + colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, cutAlpha / 0xFF); - // if ((alphaScale & 0xff) != 0xff) { - // GFSetAlphaCompare(GX_GREATER, 0, GX_AOP_OR, GX_GREATER, - // 0); // Disable Alpha Test - // } + // If this bush is fading out, disable alpha testing + if (cutAlpha != 0xff) { + materialParams.u_DynamicAlphaRefA = 0; + materialParams.u_DynamicAlphaRefB = 0; + } const renderInst = renderInstManager.newRenderInst(); this._mModel.shapeMain.setOnRenderInst(renderInst); @@ -851,12 +867,11 @@ export class Packet_c implements J3DPacket { this._mModel.bushMaterial.allocateDrawParamsDataOnInst(renderInst, drawParams); renderInstManager.submitRenderInst(renderInst); - // if ((alphaScale & 0xff) != 0xff) { - // GFSetAlphaCompare(GX_GREATER, L_Alpha_Cutoff, GX_AOP_OR, GX_GREATER, - // L_Alpha_Cutoff); // Alpha Test < 50% - // } - // alphaColor.a = 0xff; - // GFSetTevColor(GX_TEVREG2, alphaColor); + // Return alpha test to normal (50%) + if (cutAlpha != 0xff) { + materialParams.u_DynamicAlphaRefA = kAlphaCutoff; + materialParams.u_DynamicAlphaRefB = kAlphaCutoff; + } } // Always draw the trunk From 0744ebad9a3be3b61d29c824ddbf5f0004483e6c Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 21 Oct 2024 20:38:39 -0600 Subject: [PATCH 09/22] Add frustum culling to d_wood --- src/ZeldaWindWaker/d_wood.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 036c59731..ea95d3632 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -62,6 +62,8 @@ const kRoomCount = 64; const kAnimCount = 72; const kAlphaCutoff = 0x80 / 0xFF; +const kClipCenterYOffset = 40.0; +const kClipRadius = 100.0; let sAnimInitNum = 0; let sAnmNormNum = 0; @@ -130,11 +132,6 @@ const l_animAttrs: { //----------------------------------------- // Helpers //----------------------------------------- -function distanceCull(camPos: ReadonlyVec3, objPos: ReadonlyVec3, maxDist = 20000) { - const distSq = vec3.squaredDistance(camPos, objPos); - return distSq >= maxDist ** 2; -} - function setColorFromRoomNo(globals: dGlobals, materialParams: MaterialParams, roomNo: number): void { colorCopy(materialParams.u_Color[ColorKind.C0], globals.roomStatus[roomNo].tevStr.colorC0); colorCopy(materialParams.u_Color[ColorKind.C1], globals.roomStatus[roomNo].tevStr.colorK0); @@ -781,9 +778,18 @@ export class Packet_c implements J3DPacket { for (let i = 0; i < this.mUnit.length; i++) { const unit = this.mUnit[i]; if (unit.mFlags & UnitFlags.Active) { - // TODO: Frustum Culling - // unit.mFlags |= UnitFlags.IsFrustumCulled; - unit.set_mtx(this.mAnm); + // Frustum culling + const clipPos = vec3.set(scratchVec3a, unit.mPos[0], unit.mPos[1] + kClipCenterYOffset, unit.mPos[2]); + + // s32 res = mDoLib_clipper::clip(j3dSys.getViewMtx(), clipPos, kClipRadius); + const culled = !globals.camera.frustum.containsSphere(clipPos, kClipRadius); + + if( culled ) { + unit.mFlags |= UnitFlags.IsFrustumCulled; + } else { + unit.mFlags &= ~UnitFlags.IsFrustumCulled; + unit.set_mtx(this.mAnm); + } } } @@ -811,8 +817,6 @@ export class Packet_c implements J3DPacket { for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { if (unit.mFlags & UnitFlags.IsFrustumCulled) continue; - if (distanceCull(worldCamPos, unit.mPos)) - continue; const shadowRenderInst = renderInstManager.newRenderInst(); this._mModel.shapeShadow.setOnRenderInst(shadowRenderInst); @@ -846,8 +850,6 @@ export class Packet_c implements J3DPacket { for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { if (unit.mFlags & UnitFlags.IsFrustumCulled) continue; - if (distanceCull(worldCamPos, unit.mPos)) - continue; // If this bush is not chopped down, draw the main body if ((unit.mFlags & UnitFlags.IsCut) == 0) { From bc9d19b8961ccc14a56bc162ffc022466ba0a979 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 16:49:43 -0600 Subject: [PATCH 10/22] d_wood: const enum fixup --- src/ZeldaWindWaker/d_wood.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index ea95d3632..05d508b1f 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -26,14 +26,14 @@ import { assert } from '../util.js'; //----------------------------------------- // Types //----------------------------------------- -const enum UnitFlags { +const enum UnitState_e { Inactive = 0, Active = 1 << 0, IsFrustumCulled = 1 << 1, IsCut = 1 << 2, } -enum AnimMode_e { +const enum AnimMode_e { Cut = 0, // Chopping down PushInto = 1, // Attacked or collided with, but not chopped PushBack = 2, // Second half of PushInto, returning to normal @@ -531,7 +531,7 @@ class Anm_c { class Unit_c { mPos = vec3.create(); - mFlags: UnitFlags = 0; + mFlags: UnitState_e = 0; mAnmIdx: number = 0; mModelViewMtx: mat4 = mat4.create(); mTrunkModelViewMtx: mat4 = mat4.create(); @@ -604,7 +604,7 @@ class Unit_c { } public clear(): void { - this.mFlags = UnitFlags.Inactive; + this.mFlags = UnitState_e.Inactive; this.mNextUnit = null; } @@ -618,7 +618,7 @@ class Unit_c { public proc(packet: Packet_c): void { // If this unit is active, and performing a non-normal animation... - if (this.mFlags & UnitFlags.Active) { + if (this.mFlags & UnitState_e.Active) { if (this.mAnmIdx >= 8) { const anim = packet.get_anm(this.mAnmIdx); if (anim.mMode == AnimMode_e.ToNorm) { @@ -631,7 +631,7 @@ class Unit_c { const newAnimIdx = packet.search_anm(AnimMode_e.Norm); this.mAnmIdx = newAnimIdx; anim.mMode = AnimMode_e._Max; - this.mFlags |= UnitFlags.IsCut; + this.mFlags |= UnitState_e.IsCut; } } else if (anim.mMode == AnimMode_e._Max) { this.mAnmIdx = packet.search_anm(AnimMode_e.Norm); @@ -715,7 +715,7 @@ export class Packet_c implements J3DPacket { const unitIdx = this.search_empty_UnitID(); if (unitIdx != kUnitCount) { const unit = this.mUnit[unitIdx]; - unit.mFlags = UnitFlags.Active; + unit.mFlags = UnitState_e.Active; vec3.copy(unit.mPos, pos); @@ -740,14 +740,14 @@ export class Packet_c implements J3DPacket { const room = this.mRoom[roomIdx]; for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { - if ((unit.mFlags & UnitFlags.IsCut) == 0) { + if ((unit.mFlags & UnitState_e.IsCut) == 0) { unit.cc_hit_before_cut(this); } } // dComIfG_Ccsp() -> SetMassAttr(L_attr.kCollisionRad2, L_attr.kCollisionHeight2, (u8)0x12, 1); for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { - if ((unit.mFlags & UnitFlags.IsCut) != 0) { + if ((unit.mFlags & UnitState_e.IsCut) != 0) { unit.cc_hit_after_cut(this); } } @@ -777,7 +777,7 @@ export class Packet_c implements J3DPacket { public update() { for (let i = 0; i < this.mUnit.length; i++) { const unit = this.mUnit[i]; - if (unit.mFlags & UnitFlags.Active) { + if (unit.mFlags & UnitState_e.Active) { // Frustum culling const clipPos = vec3.set(scratchVec3a, unit.mPos[0], unit.mPos[1] + kClipCenterYOffset, unit.mPos[2]); @@ -785,9 +785,9 @@ export class Packet_c implements J3DPacket { const culled = !globals.camera.frustum.containsSphere(clipPos, kClipRadius); if( culled ) { - unit.mFlags |= UnitFlags.IsFrustumCulled; + unit.mFlags |= UnitState_e.IsFrustumCulled; } else { - unit.mFlags &= ~UnitFlags.IsFrustumCulled; + unit.mFlags &= ~UnitState_e.IsFrustumCulled; unit.set_mtx(this.mAnm); } } @@ -815,7 +815,7 @@ export class Packet_c implements J3DPacket { for (let r = 0; r < this.mRoom.length; r++) { for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { - if (unit.mFlags & UnitFlags.IsFrustumCulled) + if (unit.mFlags & UnitState_e.IsFrustumCulled) continue; const shadowRenderInst = renderInstManager.newRenderInst(); @@ -848,11 +848,11 @@ export class Packet_c implements J3DPacket { dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { - if (unit.mFlags & UnitFlags.IsFrustumCulled) + if (unit.mFlags & UnitState_e.IsFrustumCulled) continue; // If this bush is not chopped down, draw the main body - if ((unit.mFlags & UnitFlags.IsCut) == 0) { + if ((unit.mFlags & UnitState_e.IsCut) == 0) { // The cut animation reduces alpha over time const cutAlpha = this.mAnm[unit.mAnmIdx].mAlpha; colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, cutAlpha / 0xFF); From e21426cacb60cb27d0b5bbf47adcdad63cfba36c Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 16:56:57 -0600 Subject: [PATCH 11/22] Revert "zww_extractor.ts now copies the res/* into the output directory" This reverts commit a28b3f5ba25fa1bb994f2d40e036ffbc7067045a. --- src/ZeldaWindWaker/tools/zww_extractor.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ZeldaWindWaker/tools/zww_extractor.ts b/src/ZeldaWindWaker/tools/zww_extractor.ts index 90b3709e5..fe3ae3984 100644 --- a/src/ZeldaWindWaker/tools/zww_extractor.ts +++ b/src/ZeldaWindWaker/tools/zww_extractor.ts @@ -3,7 +3,7 @@ import ArrayBufferSlice from "../../ArrayBufferSlice.js"; import * as BYML from "../../byml.js"; import * as Yaz0 from '../../Common/Compression/Yaz0.js'; import * as JKRArchive from "../../Common/JSYSTEM/JKRArchive.js"; -import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync, mkdirSync, cpSync } from "fs"; +import { openSync, readSync, closeSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from "fs"; import { assertExists, hexzero, assert, readString } from "../../util.js"; import { Endianness } from "../../endian.js"; import { loadRustLib } from "../../rustlib.js"; @@ -348,7 +348,6 @@ function extractExtra(binaries: Binary[]) { const data = BYML.write(crg1, BYML.FileType.CRG1); mkdirSync(pathBaseOut, { recursive: true }); writeFileSync(`${pathBaseOut}/extra.crg1_arc`, Buffer.from(data)); - cpSync(`${pathBaseIn}/res`, pathBaseOut, {recursive: true}) } async function loadBinaries(): Promise { From 9981b0bbddd173a3ef467648c83354e0e5583667 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 17:32:27 -0600 Subject: [PATCH 12/22] d_wood: Cleanup sway attribute data and naming Also name the last of the unknown variables --- src/ZeldaWindWaker/d_wood.ts | 177 ++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 05d508b1f..bce02a541 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -44,6 +44,13 @@ const enum AnimMode_e { _Max }; +enum AttrSway_e { + Light, + Medium, + Strong, + Extreme, +}; + //----------------------------------------- // Globals //----------------------------------------- @@ -71,63 +78,62 @@ let sAnmNormNum = 0; //----------------------------------------- // Extracted Data //----------------------------------------- -const l_animAttrs: { - /* 0x0 */ unkUShort0: number; - /* 0x2 */ unkShort1: number; - /* 0x4 */ unkShort2: number; - /* 0x6 */ unkShort3: number; - /* 0x8 */ unkFloat: number; -}[][] = [ - [{ - unkUShort0: 0, - unkShort1: 0x50, - unkShort2: 0x5DC, - unkShort3: 0x32, - unkFloat: 0.6 - }, { - unkUShort0: 0, - unkShort1: 0x1E, - unkShort2: 0xC80, - unkShort3: 0xA, - unkFloat: 0.2, - }], [{ - unkUShort0: 1, - unkShort1: 0x96, - unkShort2: 0x4B0, - unkShort3: 0x96, - unkFloat: 0.6, - }, { - unkUShort0: 2, - unkShort1: 0x32, - unkShort2: 0x898, - unkShort3: 0x1E, - unkFloat: 0.2, - }], [{ - unkUShort0: 2, - unkShort1: 0xC8, - unkShort2: 0x898, - unkShort3: 0x12C, - unkFloat: 0.6, - }, { - unkUShort0: 1, - unkShort1: 0x1E, - unkShort2: 0xFA0, - unkShort3: 0x32, - unkFloat: 0.2, - }], [{ - unkUShort0: 2, - unkShort1: 0xC8, - unkShort2: 0x1838, - unkShort3: 0x1F4, - unkFloat: 0.6, - }, { - unkUShort0: 1, - unkShort1: 0x1E, - unkShort2: 0x3E8, - unkShort3: 0x32, - unkFloat: 0.2, - }] - ]; +const kSwayAttrs: { + phaseVelY: number; + ampY: number; + phaseVelX: number; + ampX: number; + phaseBiasX: number; +}[][] = + [[{ + phaseVelY: 0x00C8, + ampY: 0x50, + phaseVelX: 0x5DC, + ampX: 0x32, + phaseBiasX: 0.6 + }, { + phaseVelY: 0x00B4, + ampY: 0x1E, + phaseVelX: 0xC80, + ampX: 0xA, + phaseBiasX: 0.2, + }], [{ + phaseVelY: 0x01f4, + ampY: 0x96, + phaseVelX: 0x4B0, + ampX: 0x96, + phaseBiasX: 0.6, + }, { + phaseVelY: 0x02BC, + ampY: 0x32, + phaseVelX: 0x898, + ampX: 0x1E, + phaseBiasX: 0.2, + }], [{ + phaseVelY: 0x0258, + ampY: 0xC8, + phaseVelX: 0x898, + ampX: 0x12C, + phaseBiasX: 0.6, + }, { + phaseVelY: 0x01BC, + ampY: 0x1E, + phaseVelX: 0xFA0, + ampX: 0x32, + phaseBiasX: 0.2, + }], [{ + phaseVelY: 0x0258, + ampY: 0xC8, + phaseVelX: 0x1838, + ampX: 0x1F4, + phaseBiasX: 0.6, + }, { + phaseVelY: 0x01BC, + ampY: 0x1E, + phaseVelX: 0x3E8, + ampX: 0x32, + phaseBiasX: 0.2, + }]] //----------------------------------------- // Helpers @@ -359,10 +365,10 @@ class Anm_c { /* 0x70 */ mPosOffsetZ: number; /* 0x74 */ mVelY: number; - /* 0x78 */ mRotY: number[] = [0, 0]; - /* 0x7c */ mRotX: number[] = [0, 0]; - /* 0x80 */ mUnkArr2: number[] = [0, 0]; - /* 0x84 */ mUnkArr3: number[] = [0, 0]; + /* 0x78 */ mPhaseY: number[] = [0, 0]; + /* 0x7c */ mPhaseX: number[] = [0, 0]; + /* 0x80 */ mAmpY: number[] = [0, 0]; + /* 0x84 */ mAmpX: number[] = [0, 0]; /* 0x88 */ mNextAnimIdx: number; // Corresponds to the index in Packet_c::mAnm; @@ -391,10 +397,10 @@ class Anm_c { // Animate when cut with a weapon public mode_cut_init(anm: Anm_c, targetAngle: number): void { for (let i = 0; i < 2; i++) { - this.mRotY[i] = 0; - this.mRotX[i] = 0; - this.mUnkArr2[i] = 0; - this.mUnkArr3[i] = 0; + this.mPhaseY[i] = 0; + this.mPhaseX[i] = 0; + this.mAmpY[i] = 0; + this.mAmpX[i] = 0; } this.mWindDir = targetAngle; @@ -414,11 +420,11 @@ class Anm_c { this.mPosOffsetY = this.mPosOffsetY + this.mVelY; this.mPosOffsetZ = this.mPosOffsetZ + 2.5; - this.mRotX[0] = this.mRotX[0] - 200; + this.mPhaseX[0] = this.mPhaseX[0] - 200; mDoMtx_YrotS(scratchMat4a, this.mWindDir); MtxTrans([0.0, this.mPosOffsetY, this.mPosOffsetZ], true, scratchMat4a); - mDoMtx_XrotM(scratchMat4a, this.mRotX[0]); + mDoMtx_XrotM(scratchMat4a, this.mPhaseX[0]); mDoMtx_YrotM(scratchMat4a, -this.mWindDir); mDoMtx_copy(scratchMat4a, this.mModelMtx); @@ -467,10 +473,10 @@ class Anm_c { this.mMode = AnimMode_e.Norm; for (let i = 0; i < 2; i++) { - this.mRotY[i] = (sAnimInitNum << 0xd); - this.mRotX[i] = (sAnimInitNum << 0xd); - this.mUnkArr2[i] = l_animAttrs[0][i].unkShort1; - this.mUnkArr3[i] = l_animAttrs[0][i].unkShort3; + this.mPhaseY[i] = (sAnimInitNum << 0xd); + this.mPhaseX[i] = (sAnimInitNum << 0xd); + this.mAmpY[i] = kSwayAttrs[0][i].ampY; + this.mAmpX[i] = kSwayAttrs[0][i].ampX; } this.mAlpha = 0xff; @@ -481,31 +487,26 @@ class Anm_c { public mode_norm(packet: Packet_c): void { let phase; if (this.mWindPow < 0.33) { - phase = 0; + phase = AttrSway_e.Light; } else { if (this.mWindPow < 0.66) { - phase = 1; + phase = AttrSway_e.Medium; } else { - phase = 2; + phase = AttrSway_e.Strong; } } let fVar1 = 0.0; let fVar6 = fVar1; for (let i = 0; i < 2; i++) { - const animAttr = l_animAttrs[phase][i]; - const unk2 = animAttr.unkShort2; - const unk1 = animAttr.unkShort1; - const unk3 = animAttr.unkShort3; - const unk4 = animAttr.unkFloat; - - this.mRotY[i] += animAttr.unkUShort0; - this.mRotX[i] += unk2; - cLib_chaseS({ x: this.mUnkArr2[i] }, unk1, 2); - cLib_chaseS({ x: this.mUnkArr2[i] }, unk3, 2); - - fVar1 += this.mUnkArr2[i] * Math.cos(cM__Short2Rad((this.mRotY[i]))); - fVar6 += this.mUnkArr3[i] * (unk4 + Math.cos(cM__Short2Rad((this.mRotX[i])))); + const swayAttr = kSwayAttrs[phase][i]; + this.mPhaseY[i] += swayAttr.phaseVelY; + this.mPhaseX[i] += swayAttr.phaseVelX; + cLib_chaseS({ x: this.mAmpY[i] }, swayAttr.ampY, 2); + cLib_chaseS({ x: this.mAmpX[i] }, swayAttr.ampX, 2); + + fVar1 += this.mAmpY[i] * Math.cos(cM__Short2Rad((this.mPhaseY[i]))); + fVar6 += this.mAmpX[i] * (swayAttr.phaseBiasX + Math.cos(cM__Short2Rad((this.mPhaseX[i])))); } mDoMtx_YrotS(this.mModelMtx, fVar1 + this.mWindDir); @@ -784,7 +785,7 @@ export class Packet_c implements J3DPacket { // s32 res = mDoLib_clipper::clip(j3dSys.getViewMtx(), clipPos, kClipRadius); const culled = !globals.camera.frustum.containsSphere(clipPos, kClipRadius); - if( culled ) { + if (culled) { unit.mFlags |= UnitState_e.IsFrustumCulled; } else { unit.mFlags &= ~UnitState_e.IsFrustumCulled; From 805a1d0a34ff37f59fd0adf240b41ab072fcacbc Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 17:34:29 -0600 Subject: [PATCH 13/22] Remove cLib_chaseS implementation, use cLib_chaseF instead The implementation is identical in JS (in C++ it's the short version vs float) --- src/ZeldaWindWaker/SComponent.ts | 20 ++------------------ src/ZeldaWindWaker/d_wood.ts | 6 +++--- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/ZeldaWindWaker/SComponent.ts b/src/ZeldaWindWaker/SComponent.ts index d02edf026..55cb84c33 100644 --- a/src/ZeldaWindWaker/SComponent.ts +++ b/src/ZeldaWindWaker/SComponent.ts @@ -129,24 +129,6 @@ export function cM__Deg2Short(v: number): number { return cM__Rad2Short(v * MathConstants.DEG_TO_RAD); } -// Move `value` closer to `target` by the amount specified in `step`. -// If `value` would cross `target`, it is set to `target`. Returns 1 if value has reached the target, 0 otherwise. -export function cLib_chaseS(value: { x: number }, target: number, step: number): number { - if (step) { - if (value.x > target) { - step = -step; - } - value.x += step; - if (step * (value.x - target) >= 0) { - value.x = target; - return 1; - } - } else if (value.x == target) { - return 1; - } - return 0; -} - export function cLib_targetAngleX(p0: ReadonlyVec3, p1: ReadonlyVec3): number { const dy = p1[1] - p0[1]; const dist = cLib_distanceXZ(p0, p1); @@ -186,6 +168,8 @@ export function cLib_chasePosXZ(dst: vec3, target: ReadonlyVec3, maxVel: number) } } +// Move `value` closer to `target` by the amount specified in `step`. +// If `value` would cross `target`, it is set to `target`. Returns 1 if value has reached the target, 0 otherwise. export function cLib_chaseF(dst: number, target: number, step: number): number { if (step !== 0) { if (dst > target) { diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index bce02a541..482f13ddc 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -18,7 +18,7 @@ import { Endianness } from '../endian.js'; import * as GX from '../gx/gx_enum.js'; import { GfxDevice } from '../gfx/platform/GfxPlatform.js'; import { dKyw_get_wind_pow, dKyw_get_wind_vec } from './d_kankyo_wether.js'; -import { cLib_chaseS, cM__Short2Rad, cM_atan2s } from './SComponent.js'; +import { cLib_chaseF, cM__Short2Rad, cM_atan2s } from './SComponent.js'; import { dStage_roomStatus_c } from './d_stage.js'; import { mDoMtx_copy, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, MtxTrans } from './m_do_mtx.js'; import { assert } from '../util.js'; @@ -502,8 +502,8 @@ class Anm_c { const swayAttr = kSwayAttrs[phase][i]; this.mPhaseY[i] += swayAttr.phaseVelY; this.mPhaseX[i] += swayAttr.phaseVelX; - cLib_chaseS({ x: this.mAmpY[i] }, swayAttr.ampY, 2); - cLib_chaseS({ x: this.mAmpX[i] }, swayAttr.ampX, 2); + this.mAmpY[i] = cLib_chaseF(this.mAmpY[i], swayAttr.ampY, 2); + this.mAmpX[i] = cLib_chaseF(this.mAmpX[i], swayAttr.ampX, 2); fVar1 += this.mAmpY[i] * Math.cos(cM__Short2Rad((this.mPhaseY[i]))); fVar6 += this.mAmpX[i] * (swayAttr.phaseBiasX + Math.cos(cM__Short2Rad((this.mPhaseX[i])))); From 4f57aaf34ea62b62fe595ff35cded236f02f7e33 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 17:40:45 -0600 Subject: [PATCH 14/22] d_wood: Add public/private to all member functions --- src/ZeldaWindWaker/d_wood.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 482f13ddc..b4c42d134 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -337,13 +337,13 @@ class WoodModel { class Room_c { mRootUnit: Unit_c; - entry_unit(unit: Unit_c): void { + public entry_unit(unit: Unit_c): void { unit.mNextUnit = this.mRootUnit; this.mRootUnit = unit; return; } - delete_all_unit() { + public delete_all_unit() { while (this.mRootUnit) { let unit = this.mRootUnit; this.mRootUnit = unit.mNextUnit!; @@ -394,7 +394,6 @@ class Anm_c { // Each animation mode has an mode_*_init() function which is called when the animation is started // The mode_*() function is called to update the animation each frame, until finished - // Animate when cut with a weapon public mode_cut_init(anm: Anm_c, targetAngle: number): void { for (let i = 0; i < 2; i++) { this.mPhaseY[i] = 0; @@ -412,6 +411,7 @@ class Anm_c { this.mMode = AnimMode_e.Cut; } + // Animate when cut with a weapon public mode_cut(packet: Packet_c): void { this.mVelY = this.mVelY - 3.0; if (this.mVelY < -40.0) { @@ -442,21 +442,20 @@ class Anm_c { } } - // Animate when pushed into public mode_push_into_init(anm: Anm_c, targetAngle: number): void { } + // Animate when pushed into public mode_push_into(packet: Packet_c): void { } - - // Animate when pushed back public mode_push_back_init(): void { } + // Second half of the push into animation public mode_push_back(packet: Packet_c): void { } @@ -467,8 +466,6 @@ class Anm_c { } - - // Animate normally (not interacting with character) public mode_norm_init(): void { this.mMode = AnimMode_e.Norm; @@ -484,6 +481,7 @@ class Anm_c { sAnimInitNum = (sAnimInitNum + 1) % 8; } + // Animate normally (not interacting with character) public mode_norm(packet: Packet_c): void { let phase; if (this.mWindPow < 0.33) { @@ -519,12 +517,11 @@ class Anm_c { this.mWindPow = pow; } - - // Unsure? public mode_to_norm_init(anmIdx: number): void { } + // Blend back to the normal animation public mode_to_norm(packet: Packet_c): void { } @@ -660,15 +657,15 @@ export class Packet_c implements J3DPacket { } } - destroy(device: GfxDevice) { + public destroy(device: GfxDevice) { this._mModel.destroy(device); } - get_anm(idx: number): Anm_c { + public get_anm(idx: number): Anm_c { return this.mAnm[idx]; } - search_anm(i_mode: AnimMode_e): number { + public search_anm(i_mode: AnimMode_e): number { let animIdx: number; assert((i_mode >= 0) && (i_mode < AnimMode_e._Max)); @@ -702,7 +699,7 @@ export class Packet_c implements J3DPacket { return animIdx; } - search_empty_UnitID(): number { + public search_empty_UnitID(): number { for (let i = 0; i < kUnitCount; i++) { if (this.mUnit[i].mFlags == 0) { return i; @@ -712,7 +709,7 @@ export class Packet_c implements J3DPacket { return kUnitCount; } - put_unit(pos: vec3, room_no: number): number { + public put_unit(pos: vec3, room_no: number): number { const unitIdx = this.search_empty_UnitID(); if (unitIdx != kUnitCount) { const unit = this.mUnit[unitIdx]; From 6bf9d19e73c15df874ee8c4fc957b425d07f5f28 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 17:51:29 -0600 Subject: [PATCH 15/22] d_wood: don't cache globals, pass it as a param everywhere it's needed --- src/ZeldaWindWaker/Main.ts | 4 ++-- src/ZeldaWindWaker/d_a.ts | 2 +- src/ZeldaWindWaker/d_wood.ts | 29 +++++++++++------------------ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index 2bb5daeaa..c1ccba05c 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -766,12 +766,12 @@ class d_s_play extends fopScn { this.flowerPacket.calc(frameCount); this.treePacket.calc(frameCount); this.grassPacket.calc(frameCount); - this.woodPacket.calc(frameCount); + this.woodPacket.calc(globals, frameCount); this.flowerPacket.update(globals); this.treePacket.update(globals); this.grassPacket.update(globals); - this.woodPacket.update(); + this.woodPacket.update(globals); fopDw_Draw(globals.frameworkGlobals, globals, renderInstManager, viewerInput); diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 808e3f1fc..436b32e5e 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -4120,7 +4120,7 @@ class d_a_obj_wood extends fopAc_ac_c { public static PROCESS_NAME = fpc__ProcessName.d_a_obj_wood; public override subload(globals: dGlobals): cPhs__Status { - globals.scnPlay.woodPacket.put_unit(this.pos, this.roomNo); + globals.scnPlay.woodPacket.put_unit(globals, this.pos, this.roomNo); // globals.scnPlay.treePacket.newData(this.pos, 0, this.roomNo); return cPhs__Status.Next; } diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index b4c42d134..46b8f8bdb 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -54,8 +54,6 @@ enum AttrSway_e { //----------------------------------------- // Globals //----------------------------------------- -let globals: dGlobals; - const scratchVec3a = vec3.create(); const scratchVec3b = vec3.create(); const scratchVec3c = vec3.create(); @@ -138,11 +136,6 @@ const kSwayAttrs: { //----------------------------------------- // Helpers //----------------------------------------- -function setColorFromRoomNo(globals: dGlobals, materialParams: MaterialParams, roomNo: number): void { - colorCopy(materialParams.u_Color[ColorKind.C0], globals.roomStatus[roomNo].tevStr.colorC0); - colorCopy(materialParams.u_Color[ColorKind.C1], globals.roomStatus[roomNo].tevStr.colorK0); -} - interface J3DPacket { draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void; } @@ -538,7 +531,7 @@ class Unit_c { mNextUnit: Unit_c | null = null; // The next unit in the same room (a linked list) - public set_ground(): number { + public set_ground(globals: dGlobals): number { // @TODO: This is copied from d_tree. Should actually implement the d_wood version. const chk = new dBgS_GndChk(); @@ -585,7 +578,7 @@ class Unit_c { * Compute modelView matrices for the body, trunk, and drop shadow * @param anim */ - public set_mtx(anims: Anm_c[]): void { + public set_mtx(globals: dGlobals, anims: Anm_c[]): void { mDoMtx_copy(anims[this.mAnmIdx].mModelMtx, scratchMat4a); scratchMat4a[12] += this.mPos[0]; scratchMat4a[13] += this.mPos[1]; @@ -649,7 +642,6 @@ export class Packet_c implements J3DPacket { // void delete_room(s32 room_no); constructor(lGlobals: dGlobals) { - globals = lGlobals; this._mModel = new WoodModel(lGlobals); for (let i = 0; i < 8; i++) { @@ -709,7 +701,7 @@ export class Packet_c implements J3DPacket { return kUnitCount; } - public put_unit(pos: vec3, room_no: number): number { + public put_unit(globals: dGlobals, pos: vec3, room_no: number): number { const unitIdx = this.search_empty_UnitID(); if (unitIdx != kUnitCount) { const unit = this.mUnit[unitIdx]; @@ -719,7 +711,7 @@ export class Packet_c implements J3DPacket { unit.mAnmIdx = this.search_anm(AnimMode_e.Norm); - const groundY = unit.set_ground(); + const groundY = unit.set_ground(globals); if (groundY) { this.mRoom[room_no].entry_unit(unit); } else { @@ -730,7 +722,7 @@ export class Packet_c implements J3DPacket { } // Calculate collisions - public calc_cc() { + public calc_cc(globals: dGlobals) { const roomIdx = globals.mStayNo; if ((roomIdx >= 0) && (roomIdx < kRoomCount)) { @@ -752,8 +744,8 @@ export class Packet_c implements J3DPacket { } } - public calc(frameCount: number) { - this.calc_cc(); + public calc(globals: dGlobals, frameCount: number) { + this.calc_cc(globals); const windVec = dKyw_get_wind_vec(globals.g_env_light); const windPow = dKyw_get_wind_pow(globals.g_env_light); @@ -772,7 +764,7 @@ export class Packet_c implements J3DPacket { } } - public update() { + public update(globals: dGlobals) { for (let i = 0; i < this.mUnit.length; i++) { const unit = this.mUnit[i]; if (unit.mFlags & UnitState_e.Active) { @@ -786,7 +778,7 @@ export class Packet_c implements J3DPacket { unit.mFlags |= UnitState_e.IsFrustumCulled; } else { unit.mFlags &= ~UnitState_e.IsFrustumCulled; - unit.set_mtx(this.mAnm); + unit.set_mtx(globals, this.mAnm); } } } @@ -842,7 +834,8 @@ export class Packet_c implements J3DPacket { for (let r = 0; r < this.mRoom.length; r++) { // Set the room color and fog params - setColorFromRoomNo(globals, materialParams, r); + colorCopy(materialParams.u_Color[ColorKind.C0], globals.roomStatus[r].tevStr.colorC0); + colorCopy(materialParams.u_Color[ColorKind.C1], globals.roomStatus[r].tevStr.colorK0); dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { From d46ece9f79b1f4a50b153ae137d89e300fae04aa Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 18:04:48 -0600 Subject: [PATCH 16/22] d_wood: Fix shadow scale. Use math helpers for simpler matrix calculations. --- src/ZeldaWindWaker/d_wood.ts | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 46b8f8bdb..9827b22a2 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -22,6 +22,7 @@ import { cLib_chaseF, cM__Short2Rad, cM_atan2s } from './SComponent.js'; import { dStage_roomStatus_c } from './d_stage.js'; import { mDoMtx_copy, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, MtxTrans } from './m_do_mtx.js'; import { assert } from '../util.js'; +import { scaleMatrix, setMatrixAxis, setMatrixTranslation } from '../MathHelpers.js'; //----------------------------------------- // Types @@ -554,22 +555,10 @@ class Unit_c { vec3.cross(right, normal, forward); // Get the normal from the raycast, rotate shadow to match surface - this.mShadowModelMtx[0] = right[0]; - this.mShadowModelMtx[1] = right[1]; - this.mShadowModelMtx[2] = right[2]; - this.mShadowModelMtx[3] = this.mPos[0]; - - this.mShadowModelMtx[4] = normal[0]; - this.mShadowModelMtx[5] = normal[1]; - this.mShadowModelMtx[6] = normal[2]; - this.mShadowModelMtx[7] = 1.0 + y; - - this.mShadowModelMtx[8] = forward[0]; - this.mShadowModelMtx[9] = forward[1]; - this.mShadowModelMtx[10] = forward[2]; - this.mShadowModelMtx[11] = this.mPos[2]; - + setMatrixAxis(this.mShadowModelMtx, right, normal, forward); mat4.transpose(this.mShadowModelMtx, this.mShadowModelMtx); + setMatrixTranslation(this.mShadowModelMtx, [this.mPos[0], y + 1.0, this.mPos[2]]); + scaleMatrix(this.mShadowModelMtx, this.mShadowModelMtx, 1.5, 1.0, 1.5); return y; } From c6f5bbcfeab9c7dff455b0931202fdb492e40922 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 18:10:51 -0600 Subject: [PATCH 17/22] d_wood: Correctly use the g_dTree_shadowTexCoord Removes old hardcoded texcoord hack --- src/ZeldaWindWaker/d_wood.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 9827b22a2..bb999aebe 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -232,9 +232,9 @@ class WoodModel { // Wood re-uses data from d_tree const l_shadowPos = globals.findExtraSymbolData('d_tree.o', 'g_dTree_shadowPos'); const l_shadowMatDL = globals.findExtraSymbolData('d_tree.o', 'g_dTree_shadowMatDL'); - const g_dTree_Oba_kage_32DL = globals.findExtraSymbolData('d_tree.o', 'g_dTree_Oba_kage_32DL'); const l_Txa_kage_32TEX = globals.findExtraSymbolData('d_tree.o', 'l_Txa_kage_32TEX'); + const g_dTree_shadowTexCoord = globals.findExtraSymbolData('d_tree.o', 'g_dTree_shadowTexCoord'); const l_matDL = globals.findExtraSymbolData('d_wood.o', 'l_matDL__Q25dWood20@unnamed@d_wood_cpp@'); const l_Oba_swood_b_cutDL = globals.findExtraSymbolData('d_wood.o', 'l_Oba_swood_b_cutDL__Q25dWood20@unnamed@d_wood_cpp@'); @@ -248,9 +248,6 @@ class WoodModel { const l_vtxDescList = globals.findExtraSymbolData('d_wood.o', 'l_vtxDescList$5156'); const l_vtxAttrFmtList = globals.findExtraSymbolData('d_wood.o', 'l_vtxAttrFmtList$5157'); - // @HACK: The tex coord array is being read as all zero. Hardcode it. - const l_shadowTexCoord = new ArrayBufferSlice(new Uint8Array([0, 0, 1, 0, 1, 1, 0, 1]).buffer); - const matRegisters = new DisplayListRegisters(); // Shadow material @@ -271,7 +268,7 @@ class WoodModel { // Shadow verts const shadowVtxArrays: GX_Array[] = []; shadowVtxArrays[GX.Attr.POS] = { buffer: l_shadowPos, offs: 0, stride: getAttributeByteSize(shadowVatFormat, GX.Attr.POS) }; - shadowVtxArrays[GX.Attr.TEX0] = { buffer: l_shadowTexCoord, offs: 0, stride: getAttributeByteSize(shadowVatFormat, GX.Attr.TEX0) }; + shadowVtxArrays[GX.Attr.TEX0] = { buffer: g_dTree_shadowTexCoord, offs: 0, stride: getAttributeByteSize(shadowVatFormat, GX.Attr.TEX0) }; const vtx_l_shadowDL = shadowVtxLoader.runVertices(shadowVtxArrays, g_dTree_Oba_kage_32DL); // Bush material From fc9ff957500b0a06789a88898e2f4fd17c346b3a Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 22:11:52 -0600 Subject: [PATCH 18/22] d_wood: Remove unnecessary transpose --- src/ZeldaWindWaker/d_wood.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index bb999aebe..1309ce075 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -553,7 +553,6 @@ class Unit_c { // Get the normal from the raycast, rotate shadow to match surface setMatrixAxis(this.mShadowModelMtx, right, normal, forward); - mat4.transpose(this.mShadowModelMtx, this.mShadowModelMtx); setMatrixTranslation(this.mShadowModelMtx, [this.mPos[0], y + 1.0, this.mPos[2]]); scaleMatrix(this.mShadowModelMtx, this.mShadowModelMtx, 1.5, 1.0, 1.5); From b191a8a50ce0c54583558704649e5be50bb32e4f Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 22:34:30 -0600 Subject: [PATCH 19/22] d_wood: Replace Room_c linked list with a 2D array This differes from the cpp, but suits JS better --- src/ZeldaWindWaker/d_wood.ts | 111 ++++++++++++----------------------- 1 file changed, 36 insertions(+), 75 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 1309ce075..dbf0c817d 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -322,27 +322,6 @@ class WoodModel { //----------------------------------------- // Classes //----------------------------------------- -/** - * A linked list of Units, so that they can be traversed room by room - */ -class Room_c { - mRootUnit: Unit_c; - - public entry_unit(unit: Unit_c): void { - unit.mNextUnit = this.mRootUnit; - this.mRootUnit = unit; - return; - } - - public delete_all_unit() { - while (this.mRootUnit) { - let unit = this.mRootUnit; - this.mRootUnit = unit.mNextUnit!; - unit.clear(); - } - } -} - class Anm_c { /* 0x00 */ mModelMtx: mat4 = mat4.create(); /* 0x30 */ mTrunkModelMtx: mat4 = mat4.create(); @@ -527,8 +506,6 @@ class Unit_c { mShadowModelMtx: mat4 = mat4.create(); mShadowModelViewMtx: mat4 = mat4.create(); - mNextUnit: Unit_c | null = null; // The next unit in the same room (a linked list) - public set_ground(globals: dGlobals): number { // @TODO: This is copied from d_tree. Should actually implement the d_wood version. @@ -581,7 +558,6 @@ class Unit_c { public clear(): void { this.mFlags = UnitState_e.Inactive; - this.mNextUnit = null; } public cc_hit_before_cut(packet: Packet_c): void { @@ -618,8 +594,7 @@ class Unit_c { } export class Packet_c implements J3DPacket { - private mUnit: Unit_c[] = nArray(kUnitCount, () => new Unit_c()); - private mRoom: Room_c[] = nArray(kRoomCount, () => new Room_c()); + private mUnit: Unit_c[][] = nArray(kRoomCount, () => []); private mAnm: Anm_c[] = nArray(kAnimCount, () => new Anm_c()); private _mModel: WoodModel; @@ -676,34 +651,20 @@ export class Packet_c implements J3DPacket { return animIdx; } - public search_empty_UnitID(): number { - for (let i = 0; i < kUnitCount; i++) { - if (this.mUnit[i].mFlags == 0) { - return i; - } - } - - return kUnitCount; - } + public put_unit(globals: dGlobals, pos: vec3, room_no: number) { + const unit = new Unit_c(); + unit.mFlags = UnitState_e.Active; - public put_unit(globals: dGlobals, pos: vec3, room_no: number): number { - const unitIdx = this.search_empty_UnitID(); - if (unitIdx != kUnitCount) { - const unit = this.mUnit[unitIdx]; - unit.mFlags = UnitState_e.Active; + vec3.copy(unit.mPos, pos); - vec3.copy(unit.mPos, pos); + unit.mAnmIdx = this.search_anm(AnimMode_e.Norm); - unit.mAnmIdx = this.search_anm(AnimMode_e.Norm); - - const groundY = unit.set_ground(globals); - if (groundY) { - this.mRoom[room_no].entry_unit(unit); - } else { - unit.clear(); - } + const groundY = unit.set_ground(globals); + if (groundY) { + this.mUnit[room_no].push(unit); + } else { + unit.clear(); } - return unitIdx; } // Calculate collisions @@ -712,16 +673,14 @@ export class Packet_c implements J3DPacket { if ((roomIdx >= 0) && (roomIdx < kRoomCount)) { // dComIfG_Ccsp() -> SetMassAttr(L_attr.kCollisionRad1, L_attr.kCollisionHeight1, (u8)0x13, 1); - - const room = this.mRoom[roomIdx]; - for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { + for (let unit of this.mUnit[roomIdx]) { if ((unit.mFlags & UnitState_e.IsCut) == 0) { unit.cc_hit_before_cut(this); } } // dComIfG_Ccsp() -> SetMassAttr(L_attr.kCollisionRad2, L_attr.kCollisionHeight2, (u8)0x12, 1); - for (let unit = room.mRootUnit; unit != null; unit = unit.mNextUnit!) { + for (let unit of this.mUnit[roomIdx]) { if ((unit.mFlags & UnitState_e.IsCut) != 0) { unit.cc_hit_after_cut(this); } @@ -744,30 +703,32 @@ export class Packet_c implements J3DPacket { this.mAnm[i].play(this); } - for (let i = 0; i < kUnitCount; i++) { - this.mUnit[i].proc(this); + for (let i = 0; i < kRoomCount; i++) { + for (let unit of this.mUnit[i]) { + unit.proc(this); + } } } public update(globals: dGlobals) { - for (let i = 0; i < this.mUnit.length; i++) { - const unit = this.mUnit[i]; - if (unit.mFlags & UnitState_e.Active) { - // Frustum culling - const clipPos = vec3.set(scratchVec3a, unit.mPos[0], unit.mPos[1] + kClipCenterYOffset, unit.mPos[2]); - - // s32 res = mDoLib_clipper::clip(j3dSys.getViewMtx(), clipPos, kClipRadius); - const culled = !globals.camera.frustum.containsSphere(clipPos, kClipRadius); - - if (culled) { - unit.mFlags |= UnitState_e.IsFrustumCulled; - } else { - unit.mFlags &= ~UnitState_e.IsFrustumCulled; - unit.set_mtx(globals, this.mAnm); + for (let i = 0; i < kRoomCount; i++) { + for (let unit of this.mUnit[i]) { + if (unit.mFlags & UnitState_e.Active) { + // Frustum culling + const clipPos = vec3.set(scratchVec3a, unit.mPos[0], unit.mPos[1] + kClipCenterYOffset, unit.mPos[2]); + + // s32 res = mDoLib_clipper::clip(j3dSys.getViewMtx(), clipPos, kClipRadius); + const culled = !globals.camera.frustum.containsSphere(clipPos, kClipRadius); + + if (culled) { + unit.mFlags |= UnitState_e.IsFrustumCulled; + } else { + unit.mFlags &= ~UnitState_e.IsFrustumCulled; + unit.set_mtx(globals, this.mAnm); + } } } } - // TODO: Add to the Render List } @@ -788,8 +749,8 @@ export class Packet_c implements J3DPacket { this._mModel.shadowMaterial.setOnRenderInst(renderInstManager.gfxRenderCache, template); template.setSamplerBindingsFromTextureMappings(this._mModel.shadowTextureMapping); - for (let r = 0; r < this.mRoom.length; r++) { - for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { + for (let i = 0; i < kRoomCount; i++) { + for (let unit of this.mUnit[i]) { if (unit.mFlags & UnitState_e.IsFrustumCulled) continue; @@ -817,13 +778,13 @@ export class Packet_c implements J3DPacket { // Set alpha color colorFromRGBA(materialParams.u_Color[ColorKind.C2], 1, 1, 1, 1); - for (let r = 0; r < this.mRoom.length; r++) { + for (let r = 0; r < kRoomCount; r++) { // Set the room color and fog params colorCopy(materialParams.u_Color[ColorKind.C0], globals.roomStatus[r].tevStr.colorC0); colorCopy(materialParams.u_Color[ColorKind.C1], globals.roomStatus[r].tevStr.colorK0); dKy_GxFog_set(globals.g_env_light, materialParams.u_FogBlock, viewerInput.camera); - for (let unit = this.mRoom[r].mRootUnit; unit != null; unit = unit.mNextUnit!) { + for (let unit of this.mUnit[r]) { if (unit.mFlags & UnitState_e.IsFrustumCulled) continue; From acf6ac3337e1bbdf7a4c5a7e37d82cd540dbbda4 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 22:36:07 -0600 Subject: [PATCH 20/22] d_wood: Remove byte offset comments from classes --- src/ZeldaWindWaker/d_wood.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index dbf0c817d..400d71356 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -323,26 +323,26 @@ class WoodModel { // Classes //----------------------------------------- class Anm_c { - /* 0x00 */ mModelMtx: mat4 = mat4.create(); - /* 0x30 */ mTrunkModelMtx: mat4 = mat4.create(); + mModelMtx: mat4 = mat4.create(); + mTrunkModelMtx: mat4 = mat4.create(); - /* 0x60 */ mMode: AnimMode_e = AnimMode_e._Max; + mMode: AnimMode_e = AnimMode_e._Max; - /* 0x64 */ mCountdown: number; - /* 0x66 */ mWindDir: number; // The direction towards the actor who instigated this animation - /* 0x68 */ mWindPow: number; // 0.0 - 1.0 - /* 0x6c */ mPosOffsetY: number; - /* 0x70 */ mPosOffsetZ: number; - /* 0x74 */ mVelY: number; + mCountdown: number; + mWindDir: number; // The direction towards the actor who instigated this animation + mWindPow: number; // 0.0 - 1.0 + mPosOffsetY: number; + mPosOffsetZ: number; + mVelY: number; - /* 0x78 */ mPhaseY: number[] = [0, 0]; - /* 0x7c */ mPhaseX: number[] = [0, 0]; - /* 0x80 */ mAmpY: number[] = [0, 0]; - /* 0x84 */ mAmpX: number[] = [0, 0]; + mPhaseY: number[] = [0, 0]; + mPhaseX: number[] = [0, 0]; + mAmpY: number[] = [0, 0]; + mAmpX: number[] = [0, 0]; - /* 0x88 */ mNextAnimIdx: number; // Corresponds to the index in Packet_c::mAnm; + mNextAnimIdx: number; // Corresponds to the index in Packet_c::mAnm; - /* 0x8A */ mAlpha: number = 0xFF; + mAlpha: number = 0xFF; public play(packet: Packet_c): void { switch (this.mMode) { From c01ae25d5fe071b9aa24aa894815827be5a0fcc6 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 23 Oct 2024 22:41:48 -0600 Subject: [PATCH 21/22] d_wood: Remove some unused animations --- src/ZeldaWindWaker/d_wood.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index 400d71356..e6d032e8b 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -63,7 +63,6 @@ const scratchMat4a = mat4.create(); const materialParams = new MaterialParams(); const drawParams = new DrawParams(); -const kUnitCount = 200; const kRoomCount = 64; const kAnimCount = 72; @@ -364,7 +363,7 @@ class Anm_c { // Each animation mode has an mode_*_init() function which is called when the animation is started // The mode_*() function is called to update the animation each frame, until finished - public mode_cut_init(anm: Anm_c, targetAngle: number): void { + public mode_cut_init(targetAngle: number): void { for (let i = 0; i < 2; i++) { this.mPhaseY[i] = 0; this.mPhaseX[i] = 0; @@ -382,7 +381,7 @@ class Anm_c { } // Animate when cut with a weapon - public mode_cut(packet: Packet_c): void { + public mode_cut(): void { this.mVelY = this.mVelY - 3.0; if (this.mVelY < -40.0) { this.mVelY = -40.0; @@ -452,7 +451,7 @@ class Anm_c { } // Animate normally (not interacting with character) - public mode_norm(packet: Packet_c): void { + public mode_norm(): void { let phase; if (this.mWindPow < 0.33) { phase = AttrSway_e.Light; From 5209d78b13b33d315f0c1edf5954e01937d1aee9 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Thu, 24 Oct 2024 01:00:53 -0600 Subject: [PATCH 22/22] d_wood: Whoops. Fix some build errors related to extra params --- src/ZeldaWindWaker/d_wood.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZeldaWindWaker/d_wood.ts b/src/ZeldaWindWaker/d_wood.ts index e6d032e8b..30523356c 100644 --- a/src/ZeldaWindWaker/d_wood.ts +++ b/src/ZeldaWindWaker/d_wood.ts @@ -381,7 +381,7 @@ class Anm_c { } // Animate when cut with a weapon - public mode_cut(): void { + public mode_cut(packet: Packet_c): void { this.mVelY = this.mVelY - 3.0; if (this.mVelY < -40.0) { this.mVelY = -40.0; @@ -451,7 +451,7 @@ class Anm_c { } // Animate normally (not interacting with character) - public mode_norm(): void { + public mode_norm(packet: Packet_c): void { let phase; if (this.mWindPow < 0.33) { phase = AttrSway_e.Light;