From 09f1fce8d71c865945ebe138234704e6b9affb89 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 27 Dec 2024 08:35:22 -0700 Subject: [PATCH 01/19] Starting point for d_a_title --- src/ZeldaWindWaker/Main.ts | 5 +++++ src/ZeldaWindWaker/d_a.ts | 17 +++++++++++++++++ src/ZeldaWindWaker/d_procname.ts | 1 + 3 files changed, 23 insertions(+) diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index ce51ff087..f8cf2f02b 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -987,6 +987,11 @@ class DemoDesc extends SceneDesc implements Viewer.SceneDesc { fopAcM_create(globals.frameworkGlobals, dProcName_e.d_a_py_lk, 0, null, globals.mStayNo, null, null, 0xFF, -1); } + // From dStage_playerInit + if (this.stbFilename == 'title.stb') { + fopAcM_create(globals.frameworkGlobals, dProcName_e.d_a_title, 0, null, globals.mStayNo, null, null, 0xFF, -1); + } + // noclip modification: ensure all the actors are created before we load the cutscene await new Promise(resolve => { (function waitForActors(){ if (globals.frameworkGlobals.ctQueue.length === 0) return resolve(null); diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index f789f63d6..bad323a49 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5753,6 +5753,22 @@ class d_a_py_lk extends fopAc_ac_c implements ModeFuncExec { } } +class d_a_title extends fopAc_ac_c { + public static PROCESS_NAME = dProcName_e.d_a_title; + + public override subload(globals: dGlobals): cPhs__Status { + return cPhs__Status.Next; + } + + public override execute(globals: dGlobals, deltaTimeFrames: number): void { + + } + + public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { + + } +} + interface constructor extends fpc_bs__Constructor { PROCESS_NAME: dProcName_e; } @@ -5787,4 +5803,5 @@ export function d_a__RegisterConstructors(globals: fGlobals): void { R(d_a_npc_ls1); R(d_a_npc_zl1); R(d_a_py_lk); + R(d_a_title); } diff --git a/src/ZeldaWindWaker/d_procname.ts b/src/ZeldaWindWaker/d_procname.ts index b42b80846..5ea34b9cb 100644 --- a/src/ZeldaWindWaker/d_procname.ts +++ b/src/ZeldaWindWaker/d_procname.ts @@ -29,6 +29,7 @@ export const enum dProcName_e { d_a_vrbox = 0x01BA, d_a_vrbox2 = 0x01BB, d_a_bg = 0x01BC, + d_a_title = 0x01C1, d_a_swhit0 = 0x01C9, d_camera = 0x01E2, d_kyeff = 0x01E4, From 22e56303bc97c34d2f396c1e8985bd9355de5578 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 27 Dec 2024 12:58:56 -0700 Subject: [PATCH 02/19] d_a_title loads the ship model and animations --- src/ZeldaWindWaker/d_a.ts | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index bad323a49..e05fee1bc 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5755,17 +5755,118 @@ class d_a_py_lk extends fopAc_ac_c implements ModeFuncExec { class d_a_title extends fopAc_ac_c { public static PROCESS_NAME = dProcName_e.d_a_title; + public static arcName = 'TlogoE' // Tlogo, TlogoE, TlogoE[0-9] + + private modelShip: J3DModelInstance; + private bckShip = new mDoExt_bckAnm(); + private bpkShip = new mDoExt_btpAnm(); + + private frameCounter = -50; + private offsetX = 0.0; public override subload(globals: dGlobals): cPhs__Status { + const status = dComIfG_resLoad(globals, d_a_title.arcName); + if (status !== cPhs__Status.Complete) + return status; + + this.proc_init2D(globals); + this.proc_init3D(globals); + return cPhs__Status.Next; } public override execute(globals: dGlobals, deltaTimeFrames: number): void { + // TODO: + this.frameCounter += deltaTimeFrames; + + // calc_2d_alpha() + this.bpkShip.frameCtrl.setFrame(1); + this.bckShip.play(deltaTimeFrames); + this.set_mtx(); } public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { + if (this.bpkShip.frameCtrl.getFrame() != 0.0) { + this.bckShip.entry(this.modelShip); + this.bpkShip.entry(this.modelShip); + mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, viewerInput, globals.dlst.ui); + } + } + + private proc_init2D(globals: dGlobals) { + + } + + private proc_init3D(globals: dGlobals) { + const modelDataShip = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xD); + this.modelShip = new J3DModelInstance(modelDataShip); + // J3DModelData* modelData_ship = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_TITLE_SHIP, TLOGOE_BDL_TITLE_SHIP, TLOGOE0_BDL_TITLE_SHIP)); + // JUT_ASSERT(VERSION_SELECT(0xD1, 0xFC, 0xFC), modelData_ship != NULL); + + // mModel_ship = mDoExt_J3DModel__create(modelData_ship, 0x80000U, 0x37441423U); + // JUT_ASSERT(VERSION_SELECT(0xD6, 0x101, 0x101), mModel_ship != NULL); + + // J3DModelData* modelData_sub = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_SUBTITLE_START_ANIM, TLOGOE_BDL_SUBTITLE_START_ANIM_E, TLOGOE0_BDL_SUBTITLE_START_ANIM_E)); + // JUT_ASSERT(VERSION_SELECT(0xDA, 0x105, 0x105), modelData_sub != NULL); + + // mModel_subtitle = mDoExt_J3DModel__create(modelData_sub, 0x80000U, 0x37441422U); + // JUT_ASSERT(VERSION_SELECT(0xDF, 0x10A, 0x10A), mModel_subtitle != NULL); + + // J3DModelData* modelData_kirari = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_SUBTITLE_KIRARI, TLOGOE_BDL_SUBTITLE_KIRARI_E, TLOGOE0_BDL_SUBTITLE_KIRARI_E)); + // JUT_ASSERT(VERSION_SELECT(0xE3, 0x10E, 0x10E), modelData_kirari != NULL); + + // mModel_kirari = mDoExt_J3DModel__create(modelData_kirari, 0x80000U, 0x37441422U); + // JUT_ASSERT(VERSION_SELECT(0xE8, 0x113, 0x113), mModel_kirari != NULL); + + const bckDataShip = globals.resCtrl.getObjectRes(ResType.Bck, d_a_title.arcName, 0x8); + this.bckShip.init(modelDataShip, bckDataShip, true, LoopMode.Repeat, 1.0, 0, -1, false); + + const bpkDataShip = globals.resCtrl.getObjectRes(ResType.Bpk, d_a_title.arcName, 0x10); + this.bpkShip.init(modelDataShip, bpkDataShip, true, LoopMode.Repeat, 1.0, 0, -1, false); + + this.bpkShip.frameCtrl.setFrame(0.0); + this.bpkShip.frameCtrl.setRate(1.0); + + // J3DAnmTextureSRTKey* btk_sub = static_cast(dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BTK_SUBTITLE_START_ANIM, TLOGOE_BTK_SUBTITLE_START_ANIM_E, TLOGOE0_BTK_SUBTITLE_START_ANIM_E))); + // JUT_ASSERT(VERSION_SELECT(0x106, 0x131, 0x131), btk_sub != NULL); + + // BOOL ok_btk_subtitle = mBtkSub.init(modelData_sub, btk_sub, TRUE, J3DFrameCtrl::LOOP_ONCE_e, 1.0f, 0, -1, false, 0); + // JUT_ASSERT(VERSION_SELECT(0x10D, 0x138, 0x138), ok_btk_subtitle != FALSE); + + // J3DAnmTextureSRTKey* btk_kirari = static_cast(dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BTK_SUBTITLE_KIRARI, TLOGOE_BTK_SUBTITLE_KIRARI_E, TLOGOE0_BTK_SUBTITLE_KIRARI_E))); + // JUT_ASSERT(VERSION_SELECT(0x112, 0x13D, 0x13D), btk_kirari != NULL); + + // BOOL ok_btk_kirari = mBtkKirari.init(modelData_kirari, btk_kirari, TRUE, J3DFrameCtrl::LOOP_REPEAT_e, 1.0f, 0, -1, false, 0); + // JUT_ASSERT(VERSION_SELECT(0x119, 0x144, 0x144), ok_btk_kirari != FALSE); + + // mDoExt_restoreCurrentHeap(); + this.set_mtx(); + } + + private set_mtx() { + vec3.set(this.modelShip.baseScale, 0.9, 0.9, 0.9); + mat4.fromTranslation(this.modelShip.modelMatrix, [0, 0, 1000]); + mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); + + // pos.set(m094 + attr().field_0x00, attr().field_0x04, 0.0f); + // mDoMtx_stack_c::transS(pos.x, pos.y, 1000.0f); + // mDoMtx_stack_c::ZXYrotM(0, 0x4000, 0); + // mModel_ship->setBaseTRMtx(mDoMtx_stack_c::get()); + + // scale.set(attr().field_0x18, attr().field_0x1C, 1.0f); + // mModel_subtitle->setBaseScale(scale); + // mModel_kirari->setBaseScale(scale); + + // pos.set(attr().field_0x10, attr().field_0x14, 0.0f); + // mDoMtx_stack_c::transS(pos.x, pos.y, -10000.0f); + // mDoMtx_stack_c::ZXYrotM(0, -0x8000, 0); + // mModel_subtitle->setBaseTRMtx(mDoMtx_stack_c::get()); + + // mDoMtx_stack_c::transS(pos.x, pos.y, -10010.0f); + // mDoMtx_stack_c::ZXYrotM(0, -0x8000, 0); + // mModel_kirari->setBaseTRMtx(mDoMtx_stack_c::get()); } } From f5929d027fb03b6b92c793efb45dec09be053971 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 27 Dec 2024 13:11:52 -0700 Subject: [PATCH 03/19] Light d_a_title with an immediate light that should be set by the Opa2D draw list --- src/ZeldaWindWaker/d_a.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index e05fee1bc..23dac31c1 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -1,6 +1,6 @@ import { ReadonlyMat4, ReadonlyVec3, mat4, quat, vec2, vec3 } from "gl-matrix"; -import { TransparentBlack, colorCopy, colorFromRGBA8, colorNewCopy, colorNewFromRGBA8 } from "../Color.js"; +import { TransparentBlack, White, colorCopy, colorFromRGBA8, colorNewCopy, colorNewFromRGBA8 } from "../Color.js"; import { calcANK1JointAnimationTransform } from "../Common/JSYSTEM/J3D/J3DGraphAnimator.js"; import { J3DModelData, J3DModelInstance, buildEnvMtx } from "../Common/JSYSTEM/J3D/J3DGraphBase.js"; import { JointTransformInfo, LoopMode, TRK1, TTK1 } from "../Common/JSYSTEM/J3D/J3DLoader.js"; @@ -5787,10 +5787,16 @@ class d_a_title extends fopAc_ac_c { } public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { + // TODO: This should be a global immediate light set by the Opa2D draw list + const light = this.modelShip.getGXLightReference(0); + light.Position = [-35000.0, 0.0, -30000.0]; + light.Direction = [0, 0, 0]; + light.Color = White; + if (this.bpkShip.frameCtrl.getFrame() != 0.0) { this.bckShip.entry(this.modelShip); this.bpkShip.entry(this.modelShip); - mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, viewerInput, globals.dlst.ui); + mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, globals.dlst.ui); } } From 24ce6f8d9f1ce005ff873b947e0b50bca216f4c3 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 27 Dec 2024 13:23:34 -0700 Subject: [PATCH 04/19] Hackily set up new ortho view and proj matrices within d_a_title --- src/Common/JSYSTEM/J2Dv1.ts | 2 +- src/ZeldaWindWaker/d_a.ts | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 5f8d2276f..ec1c7f91c 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -67,7 +67,7 @@ const enum J2DUVBinding { }; export class J2DGrafContext { - private sceneParams = new SceneParams(); + public sceneParams = new SceneParams(); public aspectRatio: number; constructor(device: GfxDevice, x: number, y: number, private w: number, private h: number, far: number, near: number) { diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 23dac31c1..1b979f45e 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -38,6 +38,7 @@ import { fopAcIt_JudgeByID, fopAcM_create, fopAcM_prm_class, fopAc_ac_c } from " import { cPhs__Status, fGlobals, fpcPf__Register, fpcSCtRq_Request, fpc_bs__Constructor } from "./framework.js"; import { mDoExt_McaMorf, mDoExt_bckAnm, mDoExt_brkAnm, mDoExt_btkAnm, mDoExt_btpAnm, mDoExt_modelEntryDL, mDoExt_modelUpdateDL } from "./m_do_ext.js"; import { MtxPosition, MtxTrans, calc_mtx, mDoMtx_XYZrotM, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, mDoMtx_ZXYrotM, mDoMtx_ZrotM, mDoMtx_ZrotS, quatM } from "./m_do_mtx.js"; +import { J2DGrafContext } from "../Common/JSYSTEM/J2Dv1.js"; // Framework'd actors @@ -5787,6 +5788,22 @@ class d_a_title extends fopAc_ac_c { } public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { + let oldViewMtx = globals.camera.viewFromWorldMatrix; + let oldProjMtx = globals.camera.clipFromViewMatrix; + + // From mDoGph_Painter(). Set up new view and ortho proj matrices. + // TODO: This should be set by the Opa2D draw list + const adjustedWidth = 480 * viewerInput.backbufferWidth / viewerInput.backbufferHeight; + mat4.fromTranslation(scratchMat4a, [adjustedWidth * 0.5, 240, 1000]); + mDoMtx_ZrotM(scratchMat4a, -0x8000); + globals.camera.viewFromWorldMatrix = scratchMat4a; + const orthoCtx = new J2DGrafContext(globals.sceneContext.device, -9.0, -21.0, adjustedWidth + 10, 503.0, 100000.0, -100000.0); + globals.camera.clipFromViewMatrix = orthoCtx.sceneParams.u_Projection; + mat4.mul(globals.camera.clipFromWorldMatrix, globals.camera.clipFromViewMatrix, globals.camera.viewFromWorldMatrix); + globals.camera.frustum.updateClipFrustum(globals.camera.clipFromWorldMatrix, globals.camera.clipSpaceNearZ); + const template = renderInstManager.pushTemplate(); + orthoCtx.setOnRenderInst(template); + // TODO: This should be a global immediate light set by the Opa2D draw list const light = this.modelShip.getGXLightReference(0); light.Position = [-35000.0, 0.0, -30000.0]; @@ -5798,6 +5815,13 @@ class d_a_title extends fopAc_ac_c { this.bpkShip.entry(this.modelShip); mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, globals.dlst.ui); } + + // TODO: This should be set by the Opa2D draw list + globals.camera.viewFromWorldMatrix = oldViewMtx; + globals.camera.clipFromViewMatrix = oldProjMtx; + mat4.mul(globals.camera.clipFromWorldMatrix, globals.camera.clipFromViewMatrix, globals.camera.viewFromWorldMatrix); + globals.camera.frustum.updateClipFrustum(globals.camera.clipFromWorldMatrix, globals.camera.clipSpaceNearZ); + renderInstManager.popTemplate(); } private proc_init2D(globals: dGlobals) { From 30cdfc27697c39081517b6c67985be772ce02f0f Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Fri, 27 Dec 2024 14:10:43 -0700 Subject: [PATCH 05/19] d_a_title animation timers --- src/ZeldaWindWaker/d_a.ts | 50 ++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 1b979f45e..93403353b 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5762,8 +5762,11 @@ class d_a_title extends fopAc_ac_c { private bckShip = new mDoExt_bckAnm(); private bpkShip = new mDoExt_btpAnm(); - private frameCounter = -50; - private offsetX = 0.0; + private anmFrameCounter = 0 + private delayFrameCounter = 120; + private shipFrameCounter = -50; + private enterMode = 0; + private shipOffsetX: number; public override subload(globals: dGlobals): cPhs__Status { const status = dComIfG_resLoad(globals, d_a_title.arcName); @@ -5778,10 +5781,21 @@ class d_a_title extends fopAc_ac_c { public override execute(globals: dGlobals, deltaTimeFrames: number): void { // TODO: - this.frameCounter += deltaTimeFrames; + if (this.delayFrameCounter > 0) { + this.delayFrameCounter -= deltaTimeFrames; + + if (this.delayFrameCounter == 0) { + // TODO: mDoAud_seStart(JA_SE_TITLE_WIND); + } + } else { + this.calc_2d_alpha(deltaTimeFrames); + } - // calc_2d_alpha() - this.bpkShip.frameCtrl.setFrame(1); + if (this.enterMode == 2) { + this.enterMode = 3; + } else if (this.enterMode == 3) { + this.shipFrameCounter += deltaTimeFrames; + } this.bckShip.play(deltaTimeFrames); this.set_mtx(); @@ -5877,7 +5891,7 @@ class d_a_title extends fopAc_ac_c { private set_mtx() { vec3.set(this.modelShip.baseScale, 0.9, 0.9, 0.9); - mat4.fromTranslation(this.modelShip.modelMatrix, [0, 0, 1000]); + mat4.fromTranslation(this.modelShip.modelMatrix, [0 + this.shipOffsetX, 0, 1000]); mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); // pos.set(m094 + attr().field_0x00, attr().field_0x04, 0.0f); @@ -5898,6 +5912,30 @@ class d_a_title extends fopAc_ac_c { // mDoMtx_stack_c::ZXYrotM(0, -0x8000, 0); // mModel_kirari->setBaseTRMtx(mDoMtx_stack_c::get()); } + + private calc_2d_alpha(deltaTimeFrames: number) { + this.anmFrameCounter += deltaTimeFrames; + if (this.anmFrameCounter >= 200 && this.enterMode == 0) { + this.enterMode = 1; + } + + if (this.enterMode == 0) { + if (this.shipFrameCounter < 0) { + this.shipFrameCounter += deltaTimeFrames; + } + // TODO: + } else { + // TODO: + } + + if (this.shipFrameCounter <= 0) { + this.shipOffsetX = (this.shipFrameCounter * this.shipFrameCounter) * -0.1; + this.bpkShip.frameCtrl.setFrame(100.0 + (this.shipFrameCounter * 2)); + } else { + this.shipOffsetX = (this.shipFrameCounter * this.shipFrameCounter) * 0.1; + this.bpkShip.frameCtrl.setFrame(100.0 - (this.shipFrameCounter * 2)); + } + } } interface constructor extends fpc_bs__Constructor { From 467498bcf6db06e22ad18144178603ba82a3d122 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sat, 28 Dec 2024 07:27:04 -0700 Subject: [PATCH 06/19] Don't letterbox during the title demo --- src/ZeldaWindWaker/d_camera.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeldaWindWaker/d_camera.ts b/src/ZeldaWindWaker/d_camera.ts index 996eb1857..6bee01c6f 100644 --- a/src/ZeldaWindWaker/d_camera.ts +++ b/src/ZeldaWindWaker/d_camera.ts @@ -125,7 +125,7 @@ export class dCamera_c extends leafdraw_class { if (demoCam.flags & EDemoCamFlags.HasNearZ) { this.near = demoCam.projNear; } if (demoCam.flags & EDemoCamFlags.HasFarZ) { this.far = demoCam.projFar; } - this.cameraMode = CameraMode.Cinematic; + this.cameraMode = (globals.scnPlay.demo.getName() == 'title') ? CameraMode.Default : CameraMode.Cinematic; globals.sceneContext.inputManager.isMouseEnabled = false; } else { this.cameraMode = CameraMode.Default; From 2b82214707f38d4975ea1553ca90ae8169bd937f Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sat, 28 Dec 2024 07:29:44 -0700 Subject: [PATCH 07/19] Fix bpk loading for d_a_title, the ship now fades in --- src/ZeldaWindWaker/d_a.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 93403353b..a66747894 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5760,13 +5760,13 @@ class d_a_title extends fopAc_ac_c { private modelShip: J3DModelInstance; private bckShip = new mDoExt_bckAnm(); - private bpkShip = new mDoExt_btpAnm(); + private bpkShip = new mDoExt_brkAnm(); private anmFrameCounter = 0 private delayFrameCounter = 120; private shipFrameCounter = -50; private enterMode = 0; - private shipOffsetX: number; + private shipOffsetX: number = 0; public override subload(globals: dGlobals): cPhs__Status { const status = dComIfG_resLoad(globals, d_a_title.arcName); @@ -5780,7 +5780,6 @@ class d_a_title extends fopAc_ac_c { } public override execute(globals: dGlobals, deltaTimeFrames: number): void { - // TODO: if (this.delayFrameCounter > 0) { this.delayFrameCounter -= deltaTimeFrames; @@ -5891,7 +5890,7 @@ class d_a_title extends fopAc_ac_c { private set_mtx() { vec3.set(this.modelShip.baseScale, 0.9, 0.9, 0.9); - mat4.fromTranslation(this.modelShip.modelMatrix, [0 + this.shipOffsetX, 0, 1000]); + mat4.fromTranslation(this.modelShip.modelMatrix, [this.shipOffsetX, 0, 1000]); mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); // pos.set(m094 + attr().field_0x00, attr().field_0x04, 0.0f); From 2bc1d866d720787fc17c19e4c631fefd516bdfe8 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sat, 28 Dec 2024 08:13:04 -0700 Subject: [PATCH 08/19] d_a_title now loads its J2DScreen --- src/ZeldaWindWaker/d_a.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index a66747894..9afa05c20 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -38,7 +38,7 @@ import { fopAcIt_JudgeByID, fopAcM_create, fopAcM_prm_class, fopAc_ac_c } from " import { cPhs__Status, fGlobals, fpcPf__Register, fpcSCtRq_Request, fpc_bs__Constructor } from "./framework.js"; import { mDoExt_McaMorf, mDoExt_bckAnm, mDoExt_brkAnm, mDoExt_btkAnm, mDoExt_btpAnm, mDoExt_modelEntryDL, mDoExt_modelUpdateDL } from "./m_do_ext.js"; import { MtxPosition, MtxTrans, calc_mtx, mDoMtx_XYZrotM, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, mDoMtx_ZXYrotM, mDoMtx_ZrotM, mDoMtx_ZrotS, quatM } from "./m_do_mtx.js"; -import { J2DGrafContext } from "../Common/JSYSTEM/J2Dv1.js"; +import { J2DGrafContext, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; // Framework'd actors @@ -5761,6 +5761,7 @@ class d_a_title extends fopAc_ac_c { private modelShip: J3DModelInstance; private bckShip = new mDoExt_bckAnm(); private bpkShip = new mDoExt_brkAnm(); + private screen: J2DScreen; private anmFrameCounter = 0 private delayFrameCounter = 120; @@ -5835,10 +5836,27 @@ class d_a_title extends fopAc_ac_c { mat4.mul(globals.camera.clipFromWorldMatrix, globals.camera.clipFromViewMatrix, globals.camera.viewFromWorldMatrix); globals.camera.frustum.updateClipFrustum(globals.camera.clipFromWorldMatrix, globals.camera.clipSpaceNearZ); renderInstManager.popTemplate(); + + this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); } private proc_init2D(globals: dGlobals) { - + const screenData = globals.resCtrl.getObjectResByName(ResType.Blo, d_a_title.arcName, "title_logo_e.blo"); + assert(screenData !== null); + this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName)); + + // m0A0[2] = this.screen->search('pres'); + // m0A0[3] = this.screen->search('nint'); + // m0A0[0] = this.screen->search('zeld'); + // m0A0[1] = this.screen->search('zelj'); + // m0A0[4] = this.screen->search('eft1'); + // m0A0[5] = this.screen->search('eft2'); + + // for (s32 i = 0; i < (s32)ARRAY_SIZE(pane); i++) { + // fopMsgM_setPaneData(&pane[i], m0A0[i]); + // fopMsgM_setNowAlpha(&pane[i], 0.0f); + // fopMsgM_setAlpha(&pane[i]); + // } } private proc_init3D(globals: dGlobals) { From d56e39716d868099d72c084639de78c51c757881 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sun, 29 Dec 2024 12:13:32 -0700 Subject: [PATCH 09/19] J2D: Add setAlpha/getAlpha functions --- src/Common/JSYSTEM/J2Dv1.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index ec1c7f91c..6e13ff2b2 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -257,10 +257,10 @@ export class J2DPane { public children: J2DPane[] = []; // @TODO: Make private, provide search mechanism private parent: J2DPane | null = null; - public drawMtx = mat4.create(); - public drawAlpha = 1.0; - public drawPos = vec2.create(); - public drawDimensions = vec2.create(); + protected drawMtx = mat4.create(); + protected drawAlpha = 1.0; + protected drawPos = vec2.create(); + protected drawDimensions = vec2.create(); constructor(public data: PAN1, cache: GfxRenderCache, parent: J2DPane | null = null) { this.parent = parent; @@ -288,6 +288,14 @@ export class J2DPane { this.data.visible = false; } + public setAlpha(alpha: number) { + this.data.alpha = alpha * 0xFF; + } + + public getAlpha(alpha: number) { + this.data.alpha = alpha / 0xFF; + } + // NOTE: Overwritten by child classes which actually do some rendering, such as J2DPicture public drawSelf(renderInstManager: GfxRenderInstManager, ctx2D: J2DGrafContext, offsetX: number, offsetY: number) { } From 92cb903e8d3c13bfca88129887f349f688c662c4 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sun, 29 Dec 2024 12:13:57 -0700 Subject: [PATCH 10/19] d_a_title 2D alpha animations --- src/ZeldaWindWaker/d_a.ts | 86 ++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 9afa05c20..2e3ea3a22 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -38,7 +38,7 @@ import { fopAcIt_JudgeByID, fopAcM_create, fopAcM_prm_class, fopAc_ac_c } from " import { cPhs__Status, fGlobals, fpcPf__Register, fpcSCtRq_Request, fpc_bs__Constructor } from "./framework.js"; import { mDoExt_McaMorf, mDoExt_bckAnm, mDoExt_brkAnm, mDoExt_btkAnm, mDoExt_btpAnm, mDoExt_modelEntryDL, mDoExt_modelUpdateDL } from "./m_do_ext.js"; import { MtxPosition, MtxTrans, calc_mtx, mDoMtx_XYZrotM, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, mDoMtx_ZXYrotM, mDoMtx_ZrotM, mDoMtx_ZrotS, quatM } from "./m_do_mtx.js"; -import { J2DGrafContext, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; +import { J2DGrafContext, J2DPane, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; // Framework'd actors @@ -5754,6 +5754,15 @@ class d_a_py_lk extends fopAc_ac_c implements ModeFuncExec { } } +const enum TitlePane { + MainTitle, + JapanSubtitle, + PressStart, + Nintendo, + Effect1, + Effect2, +} + class d_a_title extends fopAc_ac_c { public static PROCESS_NAME = dProcName_e.d_a_title; public static arcName = 'TlogoE' // Tlogo, TlogoE, TlogoE[0-9] @@ -5762,10 +5771,12 @@ class d_a_title extends fopAc_ac_c { private bckShip = new mDoExt_bckAnm(); private bpkShip = new mDoExt_brkAnm(); private screen: J2DScreen; + private panes: J2DPane[] = []; private anmFrameCounter = 0 private delayFrameCounter = 120; private shipFrameCounter = -50; + private blinkFrameCounter = 0; private enterMode = 0; private shipOffsetX: number = 0; @@ -5844,19 +5855,19 @@ class d_a_title extends fopAc_ac_c { const screenData = globals.resCtrl.getObjectResByName(ResType.Blo, d_a_title.arcName, "title_logo_e.blo"); assert(screenData !== null); this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName)); + this.screen.color = White; + this.screen.setAlpha(1.0); // TODO: This isn't here originally - // m0A0[2] = this.screen->search('pres'); - // m0A0[3] = this.screen->search('nint'); - // m0A0[0] = this.screen->search('zeld'); - // m0A0[1] = this.screen->search('zelj'); - // m0A0[4] = this.screen->search('eft1'); - // m0A0[5] = this.screen->search('eft2'); + this.panes[TitlePane.MainTitle] = this.screen.search('zeld')!; + this.panes[TitlePane.JapanSubtitle] = this.screen.search('zelj')!; + this.panes[TitlePane.PressStart] = this.screen.search('pres')!; + this.panes[TitlePane.Nintendo] = this.screen.search('nint')!; + this.panes[4] = this.screen.search('eft1')!; + this.panes[5] = this.screen.search('eft2')!; - // for (s32 i = 0; i < (s32)ARRAY_SIZE(pane); i++) { - // fopMsgM_setPaneData(&pane[i], m0A0[i]); - // fopMsgM_setNowAlpha(&pane[i], 0.0f); - // fopMsgM_setAlpha(&pane[i]); - // } + for (let pane of this.panes) { + pane.setAlpha(0.0); + } } private proc_init3D(globals: dGlobals) { @@ -5940,9 +5951,56 @@ class d_a_title extends fopAc_ac_c { if (this.shipFrameCounter < 0) { this.shipFrameCounter += deltaTimeFrames; } - // TODO: + + // TODO: Emitters + + if (this.anmFrameCounter <= 30) { + this.panes[TitlePane.MainTitle].setAlpha(0.0); + } else if (this.anmFrameCounter <= 80) { + this.panes[TitlePane.MainTitle].setAlpha((this.anmFrameCounter - 30) / 50.0); + } else { + this.panes[TitlePane.MainTitle].setAlpha(1.0); + } + + // TODO: Viewable japanese version + this.panes[TitlePane.JapanSubtitle].setAlpha(0.0); + + // TODO: Emitters + + if (this.anmFrameCounter <= 150) { + this.panes[TitlePane.Nintendo].setAlpha(0.0); + } else if (this.anmFrameCounter <= 170) { + this.panes[TitlePane.Nintendo].setAlpha((this.anmFrameCounter - 150) / 20.0); + } else { + this.panes[TitlePane.Nintendo].setAlpha(1.0); + } + + if (this.anmFrameCounter <= 160) { + this.panes[TitlePane.PressStart].setAlpha(0.0); + } else if (this.anmFrameCounter <= 180) { + this.panes[TitlePane.PressStart].setAlpha((this.anmFrameCounter - 160) / 20.0); + } else { + this.panes[TitlePane.PressStart].setAlpha(1.0); + } } else { - // TODO: + // TODO: Emitters + + this.panes[TitlePane.MainTitle].setAlpha(1.0); + this.panes[TitlePane.JapanSubtitle].setAlpha(0.0); + + // mBtkSub.setFrame(mBtkSub.getEndFrame()); + this.panes[TitlePane.Nintendo].setAlpha(1.0); + if (this.blinkFrameCounter >= 100) { + this.blinkFrameCounter = 0; + } else { + this.blinkFrameCounter += deltaTimeFrames; + } + + if (this.blinkFrameCounter >= 50) { + this.panes[TitlePane.PressStart].setAlpha((this.blinkFrameCounter - 50) / 50.0); + } else { + this.panes[TitlePane.PressStart].setAlpha((50 - this.blinkFrameCounter) / 50.0); + } } if (this.shipFrameCounter <= 0) { From a271479eb533555f28b769fc065a0ea59a94f728 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sun, 29 Dec 2024 15:45:47 -0700 Subject: [PATCH 11/19] d_a_title: Handle subtitle animations (wipe-in and shimmer) --- src/ZeldaWindWaker/d_a.ts | 104 ++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 2e3ea3a22..3d29677b3 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5768,8 +5768,12 @@ class d_a_title extends fopAc_ac_c { public static arcName = 'TlogoE' // Tlogo, TlogoE, TlogoE[0-9] private modelShip: J3DModelInstance; + private modelSubtitle: J3DModelInstance; + private modelSubtitleShimmer: J3DModelInstance; private bckShip = new mDoExt_bckAnm(); private bpkShip = new mDoExt_brkAnm(); + private btkSubtitle = new mDoExt_btkAnm(); + private btkShimmer = new mDoExt_btkAnm(); private screen: J2DScreen; private panes: J2DPane[] = []; @@ -5777,6 +5781,7 @@ class d_a_title extends fopAc_ac_c { private delayFrameCounter = 120; private shipFrameCounter = -50; private blinkFrameCounter = 0; + private shimmerFrameCounter = (cM_rndF(120) + 10 + 130.0); private enterMode = 0; private shipOffsetX: number = 0; @@ -5834,11 +5839,10 @@ class d_a_title extends fopAc_ac_c { light.Position = [-35000.0, 0.0, -30000.0]; light.Direction = [0, 0, 0]; light.Color = White; - - if (this.bpkShip.frameCtrl.getFrame() != 0.0) { - this.bckShip.entry(this.modelShip); - this.bpkShip.entry(this.modelShip); - mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, globals.dlst.ui); + + { + this.model_draw(globals, renderInstManager); + this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); } // TODO: This should be set by the Opa2D draw list @@ -5847,8 +5851,6 @@ class d_a_title extends fopAc_ac_c { mat4.mul(globals.camera.clipFromWorldMatrix, globals.camera.clipFromViewMatrix, globals.camera.viewFromWorldMatrix); globals.camera.frustum.updateClipFrustum(globals.camera.clipFromWorldMatrix, globals.camera.clipSpaceNearZ); renderInstManager.popTemplate(); - - this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); } private proc_init2D(globals: dGlobals) { @@ -5874,23 +5876,11 @@ class d_a_title extends fopAc_ac_c { const modelDataShip = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xD); this.modelShip = new J3DModelInstance(modelDataShip); - // J3DModelData* modelData_ship = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_TITLE_SHIP, TLOGOE_BDL_TITLE_SHIP, TLOGOE0_BDL_TITLE_SHIP)); - // JUT_ASSERT(VERSION_SELECT(0xD1, 0xFC, 0xFC), modelData_ship != NULL); - - // mModel_ship = mDoExt_J3DModel__create(modelData_ship, 0x80000U, 0x37441423U); - // JUT_ASSERT(VERSION_SELECT(0xD6, 0x101, 0x101), mModel_ship != NULL); + const modelDataSub = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xC); + this.modelSubtitle = new J3DModelInstance(modelDataSub); - // J3DModelData* modelData_sub = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_SUBTITLE_START_ANIM, TLOGOE_BDL_SUBTITLE_START_ANIM_E, TLOGOE0_BDL_SUBTITLE_START_ANIM_E)); - // JUT_ASSERT(VERSION_SELECT(0xDA, 0x105, 0x105), modelData_sub != NULL); - - // mModel_subtitle = mDoExt_J3DModel__create(modelData_sub, 0x80000U, 0x37441422U); - // JUT_ASSERT(VERSION_SELECT(0xDF, 0x10A, 0x10A), mModel_subtitle != NULL); - - // J3DModelData* modelData_kirari = (J3DModelData*)dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BDL_SUBTITLE_KIRARI, TLOGOE_BDL_SUBTITLE_KIRARI_E, TLOGOE0_BDL_SUBTITLE_KIRARI_E)); - // JUT_ASSERT(VERSION_SELECT(0xE3, 0x10E, 0x10E), modelData_kirari != NULL); - - // mModel_kirari = mDoExt_J3DModel__create(modelData_kirari, 0x80000U, 0x37441422U); - // JUT_ASSERT(VERSION_SELECT(0xE8, 0x113, 0x113), mModel_kirari != NULL); + const modelDataKirari = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xB); + this.modelSubtitleShimmer = new J3DModelInstance(modelDataKirari); const bckDataShip = globals.resCtrl.getObjectRes(ResType.Bck, d_a_title.arcName, 0x8); this.bckShip.init(modelDataShip, bckDataShip, true, LoopMode.Repeat, 1.0, 0, -1, false); @@ -5901,20 +5891,30 @@ class d_a_title extends fopAc_ac_c { this.bpkShip.frameCtrl.setFrame(0.0); this.bpkShip.frameCtrl.setRate(1.0); - // J3DAnmTextureSRTKey* btk_sub = static_cast(dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BTK_SUBTITLE_START_ANIM, TLOGOE_BTK_SUBTITLE_START_ANIM_E, TLOGOE0_BTK_SUBTITLE_START_ANIM_E))); - // JUT_ASSERT(VERSION_SELECT(0x106, 0x131, 0x131), btk_sub != NULL); + const btkDataSub = globals.resCtrl.getObjectRes(ResType.Btk, d_a_title.arcName, 0x14); + this.btkSubtitle.init(modelDataSub, btkDataSub, true, LoopMode.Once, 1.0, 0, -1, false); - // BOOL ok_btk_subtitle = mBtkSub.init(modelData_sub, btk_sub, TRUE, J3DFrameCtrl::LOOP_ONCE_e, 1.0f, 0, -1, false, 0); - // JUT_ASSERT(VERSION_SELECT(0x10D, 0x138, 0x138), ok_btk_subtitle != FALSE); + const btkDataShimmer = globals.resCtrl.getObjectRes(ResType.Btk, d_a_title.arcName, 0x13); + this.btkShimmer.init(modelDataKirari, btkDataShimmer, true, LoopMode.Once, 1.0, 0, -1, false); - // J3DAnmTextureSRTKey* btk_kirari = static_cast(dComIfG_getObjectRes(ARCNAME, VERSION_SELECT(TLOGO_BTK_SUBTITLE_KIRARI, TLOGOE_BTK_SUBTITLE_KIRARI_E, TLOGOE0_BTK_SUBTITLE_KIRARI_E))); - // JUT_ASSERT(VERSION_SELECT(0x112, 0x13D, 0x13D), btk_kirari != NULL); + this.set_mtx(); + } - // BOOL ok_btk_kirari = mBtkKirari.init(modelData_kirari, btk_kirari, TRUE, J3DFrameCtrl::LOOP_REPEAT_e, 1.0f, 0, -1, false, 0); - // JUT_ASSERT(VERSION_SELECT(0x119, 0x144, 0x144), ok_btk_kirari != FALSE); + private model_draw(globals: dGlobals, renderInstManager: GfxRenderInstManager) { + + if (this.btkSubtitle.frameCtrl.getFrame() != 0.0) { + this.btkShimmer.entry(this.modelSubtitleShimmer) + mDoExt_modelUpdateDL(globals, this.modelSubtitleShimmer, renderInstManager, globals.dlst.ui); + + this.btkSubtitle.entry(this.modelSubtitle); + mDoExt_modelUpdateDL(globals, this.modelSubtitle, renderInstManager, globals.dlst.ui); + } - // mDoExt_restoreCurrentHeap(); - this.set_mtx(); + if (this.bpkShip.frameCtrl.getFrame() != 0.0) { + this.bckShip.entry(this.modelShip); + this.bpkShip.entry(this.modelShip); + mDoExt_modelUpdateDL(globals, this.modelShip, renderInstManager, globals.dlst.ui); + } } private set_mtx() { @@ -5922,23 +5922,14 @@ class d_a_title extends fopAc_ac_c { mat4.fromTranslation(this.modelShip.modelMatrix, [this.shipOffsetX, 0, 1000]); mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); - // pos.set(m094 + attr().field_0x00, attr().field_0x04, 0.0f); - // mDoMtx_stack_c::transS(pos.x, pos.y, 1000.0f); - // mDoMtx_stack_c::ZXYrotM(0, 0x4000, 0); - // mModel_ship->setBaseTRMtx(mDoMtx_stack_c::get()); + vec3.set(this.modelSubtitle.baseScale, 1.0, 1.0, 1.0); + vec3.set(this.modelSubtitleShimmer.baseScale, 1.0, 1.0, 1.0); - // scale.set(attr().field_0x18, attr().field_0x1C, 1.0f); - // mModel_subtitle->setBaseScale(scale); - // mModel_kirari->setBaseScale(scale); + mat4.fromTranslation(this.modelSubtitle.modelMatrix, [-57.0, -3.0, -10000.0]); + mDoMtx_ZXYrotM(this.modelSubtitle.modelMatrix, [0, -0x8000, 0]); - // pos.set(attr().field_0x10, attr().field_0x14, 0.0f); - // mDoMtx_stack_c::transS(pos.x, pos.y, -10000.0f); - // mDoMtx_stack_c::ZXYrotM(0, -0x8000, 0); - // mModel_subtitle->setBaseTRMtx(mDoMtx_stack_c::get()); - - // mDoMtx_stack_c::transS(pos.x, pos.y, -10010.0f); - // mDoMtx_stack_c::ZXYrotM(0, -0x8000, 0); - // mModel_kirari->setBaseTRMtx(mDoMtx_stack_c::get()); + mat4.fromTranslation(this.modelSubtitleShimmer.modelMatrix, [-57.0, -3.0, -10010.0]); + mDoMtx_ZXYrotM(this.modelSubtitleShimmer.modelMatrix, [0, -0x8000, 0]); } private calc_2d_alpha(deltaTimeFrames: number) { @@ -5966,6 +5957,10 @@ class d_a_title extends fopAc_ac_c { this.panes[TitlePane.JapanSubtitle].setAlpha(0.0); // TODO: Emitters + + if (this.anmFrameCounter >= 80) { + this.btkSubtitle.play(deltaTimeFrames); + } if (this.anmFrameCounter <= 150) { this.panes[TitlePane.Nintendo].setAlpha(0.0); @@ -5988,7 +5983,7 @@ class d_a_title extends fopAc_ac_c { this.panes[TitlePane.MainTitle].setAlpha(1.0); this.panes[TitlePane.JapanSubtitle].setAlpha(0.0); - // mBtkSub.setFrame(mBtkSub.getEndFrame()); + this.btkSubtitle.frameCtrl.setFrame(this.btkSubtitle.frameCtrl.endFrame); this.panes[TitlePane.Nintendo].setAlpha(1.0); if (this.blinkFrameCounter >= 100) { this.blinkFrameCounter = 0; @@ -6003,6 +5998,17 @@ class d_a_title extends fopAc_ac_c { } } + if (this.shimmerFrameCounter <= 0) { + const finished = this.btkShimmer.play(deltaTimeFrames); + if(finished) { + this.btkShimmer.frameCtrl.setFrame(0.0); + this.btkShimmer.frameCtrl.setRate(1.0); + this.shimmerFrameCounter = cM_rndF(120) + 10; + } + } else { + this.shimmerFrameCounter -= deltaTimeFrames; + } + if (this.shipFrameCounter <= 0) { this.shipOffsetX = (this.shipFrameCounter * this.shipFrameCounter) * -0.1; this.bpkShip.frameCtrl.setFrame(100.0 + (this.shipFrameCounter * 2)); From c6747830697b0d198e1322afc1f087d10fbce952 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 08:49:12 -0700 Subject: [PATCH 12/19] Wind Waker: Add new display list set `ui2D`. Draws after `ui`. Corresponds to the 2DOpa and 2DXlu lists from the game. --- src/ZeldaWindWaker/Main.ts | 2 ++ src/ZeldaWindWaker/d_drawlist.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index f8cf2f02b..6871593d5 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -543,6 +543,8 @@ export class WindWakerRenderer implements Viewer.SceneGfx { this.executeList(passRenderer, dlst.effect[EffectDrawGroup.Main]); this.executeList(passRenderer, dlst.wetherEffect); this.executeListSet(passRenderer, dlst.ui); + + this.executeListSet(passRenderer, dlst.ui2D); }); }); diff --git a/src/ZeldaWindWaker/d_drawlist.ts b/src/ZeldaWindWaker/d_drawlist.ts index 6aabf1530..1f625413f 100644 --- a/src/ZeldaWindWaker/d_drawlist.ts +++ b/src/ZeldaWindWaker/d_drawlist.ts @@ -198,6 +198,13 @@ export class dDlst_list_c { new GfxRenderInstList(gfxRenderInstCompareNone, GfxRenderInstExecutionOrder.Backwards), new GfxRenderInstList(gfxRenderInstCompareNone, GfxRenderInstExecutionOrder.Backwards), ]; + + // These correspond to 2DOpa and 2DXlu + public ui2D: dDlst_list_Set = [ + new GfxRenderInstList(gfxRenderInstCompareNone, GfxRenderInstExecutionOrder.Forwards), + new GfxRenderInstList(gfxRenderInstCompareNone, GfxRenderInstExecutionOrder.Forwards) + ]; + public alphaModel = new GfxRenderInstList(gfxRenderInstCompareNone, GfxRenderInstExecutionOrder.Forwards); public peekZ = new PeekZManager(128); public alphaModel0: dDlst_alphaModel_c; From cd2544825ad1b973e28e99e0e8bc31fac6be00f2 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 08:49:37 -0700 Subject: [PATCH 13/19] d_a_title 2D elements now render into the `ui2D` list, fixing the ordering issues --- src/ZeldaWindWaker/d_a.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 3d29677b3..55807ffbb 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5842,6 +5842,8 @@ class d_a_title extends fopAc_ac_c { { this.model_draw(globals, renderInstManager); + + renderInstManager.setCurrentList(globals.dlst.ui2D[0]); this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); } From e8282570493e08b6ae81c6289046fc34ba4f95f4 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 09:25:07 -0700 Subject: [PATCH 14/19] J2D: Add support for colorBlack/colorWhite J2DPane data --- src/Common/JSYSTEM/J2Dv1.ts | 45 +++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 6e13ff2b2..be870409b 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -384,8 +384,6 @@ export class J2DPicture extends J2DPane { if (this.data.uvBinding !== 15) { console.warn('Untested J2D feature'); } if (this.data.flags !== 0) { console.warn('Untested J2D feature'); } - if (!colorEqual(this.data.colorBlack, TransparentBlack) || !colorEqual(this.data.colorWhite, White)) - console.warn(`Untested J2D feature colorBlack ${this.data.colorBlack}, colorWhite ${this.data.colorWhite}`); if (!colorEqual(this.data.colorCorners[0], White) || !colorEqual(this.data.colorCorners[1], White) || !colorEqual(this.data.colorCorners[2], White) || !colorEqual(this.data.colorCorners[3], White)) console.warn(`Untested J2D feature colorCorners ${this.data.colorCorners}`); } @@ -406,7 +404,10 @@ export class J2DPicture extends J2DPane { this.sdraw.setOnRenderInst(renderInst); this.materialHelper.setOnRenderInst(renderInstManager.gfxRenderCache, renderInst); - materialParams.u_Color[ColorKind.C0].a = this.drawAlpha; + materialParams.u_Color[ColorKind.C0] = this.data.colorBlack; + materialParams.u_Color[ColorKind.C1] = this.data.colorWhite; + materialParams.u_Color[ColorKind.C2].a = this.drawAlpha; + this.tex.fillTextureMapping(materialParams.m_TextureMapping[0]); this.materialHelper.allocateMaterialParamsDataOnInst(renderInst, materialParams); renderInst.setSamplerBindingsFromTextureMappings(materialParams.m_TextureMapping); @@ -487,20 +488,36 @@ export class J2DPicture extends J2DPane { this.sdraw.endDraw(this.cache); const mb = new GXMaterialBuilder('J2DPane'); + let tevStage = 0; + // Assume alpha is enabled. This is byte 1 on a JUTTexture, but noclip doesn't read it mb.setChanCtrl(GX.ColorChannelID.COLOR0A0, false, GX.ColorSrc.REG, GX.ColorSrc.VTX, 0, GX.DiffuseFunction.NONE, GX.AttenuationFunction.NONE); + // 0: Multiply tex and vertex colors and alpha - mb.setTevOrder(0, GX.TexCoordID.TEXCOORD0, GX.TexMapID.TEXMAP0, GX.RasColorChannelID.COLOR0A0); - mb.setTevColorIn(0, GX.CC.ZERO, GX.CC.TEXC, GX.CC.RASC, GX.CC.ZERO); - mb.setTevAlphaIn(0, GX.CA.ZERO, GX.CA.TEXA, GX.CA.RASA, GX.CA.ZERO); - mb.setTevColorOp(0, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); - mb.setTevAlphaOp(0, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); - // 1: Multiply result alpha by dynamic alpha (this.drawAlpha) - mb.setTevOrder(1, GX.TexCoordID.TEXCOORD_NULL, GX.TexMapID.TEXMAP_NULL, GX.RasColorChannelID.COLOR_ZERO); - mb.setTevColorIn(1, GX.CC.CPREV, GX.CC.ZERO, GX.CC.ZERO, GX.CC.ZERO); - mb.setTevAlphaIn(1, GX.CA.ZERO, GX.CA.APREV, GX.CA.A0, GX.CA.ZERO); - mb.setTevColorOp(1, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); - mb.setTevAlphaOp(1, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + mb.setTevOrder(tevStage, GX.TexCoordID.TEXCOORD0, GX.TexMapID.TEXMAP0, GX.RasColorChannelID.COLOR0A0); + mb.setTevColorIn(tevStage, GX.CC.ZERO, GX.CC.TEXC, GX.CC.RASC, GX.CC.ZERO); + mb.setTevAlphaIn(tevStage, GX.CA.ZERO, GX.CA.TEXA, GX.CA.RASA, GX.CA.ZERO); + mb.setTevColorOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + mb.setTevAlphaOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + tevStage += 1; + + // 1: Lerp between the Black and White colors based on previous stage result + if (!colorEqual(this.data.colorBlack, TransparentBlack) || !colorEqual(this.data.colorWhite, White)) { + mb.setTevOrder(tevStage, GX.TexCoordID.TEXCOORD_NULL, GX.TexMapID.TEXMAP_NULL, GX.RasColorChannelID.COLOR_ZERO); + mb.setTevColorIn(tevStage, GX.CC.C0, GX.CC.C1, GX.CC.CPREV, GX.CC.ZERO); + mb.setTevAlphaIn(tevStage, GX.CA.A0, GX.CA.A1, GX.CA.APREV, GX.CA.ZERO); + mb.setTevColorOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + mb.setTevAlphaOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + tevStage += 1; + } + + // 2: Multiply result alpha by dynamic alpha (this.drawAlpha) + mb.setTevOrder(tevStage, GX.TexCoordID.TEXCOORD_NULL, GX.TexMapID.TEXMAP_NULL, GX.RasColorChannelID.COLOR_ZERO); + mb.setTevColorIn(tevStage, GX.CC.CPREV, GX.CC.ZERO, GX.CC.ZERO, GX.CC.ZERO); + mb.setTevAlphaIn(tevStage, GX.CA.ZERO, GX.CA.APREV, GX.CA.A2, GX.CA.ZERO); + mb.setTevColorOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + mb.setTevAlphaOp(tevStage, GX.TevOp.ADD, GX.TevBias.ZERO, GX.TevScale.SCALE_1, true, GX.Register.PREV); + tevStage += 1; mb.setTexCoordGen(GX.TexCoordID.TEXCOORD0, GX.TexGenType.MTX2x4, GX.TexGenSrc.TEX0, GX.TexGenMatrix.IDENTITY); mb.setZMode(false, GX.CompareType.ALWAYS, false); From 9888a1290837ec6fc86132a3e2392e3c056bb204 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 13:33:55 -0700 Subject: [PATCH 15/19] J2D: Fixed up viewport/ortho matrix computation for pixel-perfect placement ... when in the original aspect ratio. When in a different ratio, elements will be placed the same relative distance from the left origin. --- src/Common/JSYSTEM/J2Dv1.ts | 54 +++++++++++++++++++++--------- src/ZeldaWindWaker/Main.ts | 10 +++--- src/ZeldaWindWaker/d_a.ts | 7 ++-- src/ZeldaWindWaker/d_place_name.ts | 2 +- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index be870409b..3bbb4d370 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -15,7 +15,7 @@ import { projectionMatrixConvertClipSpaceNearZ } from "../../gfx/helpers/Project import { TSDraw } from "../../SuperMarioGalaxy/DDraw.js"; import { BTIData } from "./JUTTexture.js"; import { GXMaterialBuilder } from "../../gx/GXMaterialBuilder.js"; -import { mat4, vec2 } from "gl-matrix"; +import { mat4, vec2, vec4 } from "gl-matrix"; import { GfxRenderCache } from "../../gfx/render/GfxRenderCache.js"; const materialParams = new MaterialParams(); @@ -67,21 +67,45 @@ const enum J2DUVBinding { }; export class J2DGrafContext { + private clipSpaceNearZ: GfxClipSpaceNearZ; public sceneParams = new SceneParams(); - public aspectRatio: number; - - constructor(device: GfxDevice, x: number, y: number, private w: number, private h: number, far: number, near: number) { - this.aspectRatio = w / h; - // NOTE: Y axis is inverted here (bottom = height), to match the original J2D convention - projectionMatrixForCuboid(this.sceneParams.u_Projection, x, w, h, y, near, far); - const clipSpaceNearZ = device.queryVendorInfo().clipSpaceNearZ; - projectionMatrixConvertClipSpaceNearZ(this.sceneParams.u_Projection, clipSpaceNearZ, GfxClipSpaceNearZ.NegativeOne); + public viewport = vec4.create(); + public ortho = vec4.create(); + + public near: number; + public far: number; + + // Used to preserve the aspect ratio of assets that were designed for 640x480, but being rendered at a different resolution. + public aspectRatioCorrection: number = 1.0; + + constructor(device: GfxDevice, vpX: number, vpY: number, vpWidth: number, vpHeight: number, near: number, far: number) { + this.clipSpaceNearZ = device.queryVendorInfo().clipSpaceNearZ; + vec4.set(this.viewport, vpX, vpY, vpWidth, vpHeight); + vec4.set(this.ortho, 0, 0, vpWidth, vpHeight); + this.near = far; + this.far = far; + } + + public setOrtho(left: number, top: number, right: number, bottom: number, far: number, near: number) { + vec4.set(this.ortho, left, right, bottom, top); + this.near = near; + this.far = far; } - public setupView(backbufferWidth: number, backbufferHeight: number): void { - const screenAspect = backbufferWidth / backbufferHeight; - const grafAspect = this.w / this.h; - this.aspectRatio = grafAspect / screenAspect; + public setPort(backbufferWidth: number, backbufferHeight: number) { + // To support dynamic aspect ratios, we keep the original screenspace height and the original aspect ratio. + // So changing the window width will not cause 2D elements to scale, but changing the window height will. + const dstAspect = backbufferWidth / backbufferHeight; + const srcAspect = this.viewport[2] / this.viewport[3]; + this.aspectRatioCorrection = dstAspect / srcAspect; + + const left = this.ortho[0] * this.aspectRatioCorrection; + const right = this.ortho[1] * this.aspectRatioCorrection; + const bottom = this.ortho[2] + 0.5; + const top = this.ortho[3]; + + projectionMatrixForCuboid(this.sceneParams.u_Projection, left, right, bottom, top, this.near, this.far); + projectionMatrixConvertClipSpaceNearZ(this.sceneParams.u_Projection, this.clipSpaceNearZ, GfxClipSpaceNearZ.NegativeOne); } public setOnRenderInst(renderInst: GfxRenderInst): void { @@ -304,10 +328,8 @@ export class J2DPane { const boundsValid = this.data.w > 0 && this.data.h > 0; if (this.data.visible && boundsValid) { - // To support dynamic aspect ratios, we keep the original screenspace height and the original aspect ratio. - // So changing the window width will not cause 2D elements to scale, but changing the window height will. vec2.set(this.drawPos, this.data.x, this.data.y); - vec2.set(this.drawDimensions, this.data.w * ctx2D.aspectRatio, this.data.h); + vec2.set(this.drawDimensions, this.data.w, this.data.h); this.drawAlpha = this.data.alpha / 0xFF; if (this.parent) { diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index 6871593d5..78f2dfd7d 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -499,6 +499,10 @@ export class WindWakerRenderer implements Viewer.SceneGfx { const dlst = this.globals.dlst; dlst.peekZ.beginFrame(device); + // From mDoGph_Painter, + this.globals.scnPlay.currentGrafPort.setOrtho(-9.0, -21.0, 650.0, 503.0, 100000.0, -100000.0); + this.globals.scnPlay.currentGrafPort.setPort(viewerInput.backbufferWidth, viewerInput.backbufferHeight); + this.executeDrawAll(device, viewerInput); const renderInstManager = this.renderHelper.renderInstManager; @@ -726,7 +730,7 @@ class d_s_play extends fopScn { public placenameIndex: Placename; public placenameState: PlacenameState; - public orthoGraf2D: J2DGrafContext; + public currentGrafPort: J2DGrafContext; public override load(globals: dGlobals, userData: any): cPhs__Status { super.load(globals, userData); @@ -738,7 +742,7 @@ class d_s_play extends fopScn { this.grassPacket = new GrassPacket(globals); this.woodPacket = new WoodPacket(globals); - this.orthoGraf2D = new J2DGrafContext(globals.modelCache.device, 0.0, 0.0, 608.0, 448.0, -1.0, 0.0); + this.currentGrafPort = new J2DGrafContext(globals.modelCache.device, 0.0, 0.0, 608.0, 448.0, -1.0, 1.0); globals.scnPlay = this; @@ -760,8 +764,6 @@ class d_s_play extends fopScn { public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: Viewer.ViewerRenderInput): void { super.draw(globals, renderInstManager, viewerInput); - this.orthoGraf2D.setupView(viewerInput.backbufferWidth, viewerInput.backbufferHeight); - // Magma/Grass/Trees/Bushes/Flowers const frameCount = viewerInput.time / 1000.0 * 30; diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 55807ffbb..b574dba6d 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5823,11 +5823,10 @@ class d_a_title extends fopAc_ac_c { // From mDoGph_Painter(). Set up new view and ortho proj matrices. // TODO: This should be set by the Opa2D draw list - const adjustedWidth = 480 * viewerInput.backbufferWidth / viewerInput.backbufferHeight; - mat4.fromTranslation(scratchMat4a, [adjustedWidth * 0.5, 240, 1000]); + const orthoCtx = globals.scnPlay.currentGrafPort; + mat4.fromTranslation(scratchMat4a, [orthoCtx.aspectRatioCorrection * 320, 240, 1000]); mDoMtx_ZrotM(scratchMat4a, -0x8000); globals.camera.viewFromWorldMatrix = scratchMat4a; - const orthoCtx = new J2DGrafContext(globals.sceneContext.device, -9.0, -21.0, adjustedWidth + 10, 503.0, 100000.0, -100000.0); globals.camera.clipFromViewMatrix = orthoCtx.sceneParams.u_Projection; mat4.mul(globals.camera.clipFromWorldMatrix, globals.camera.clipFromViewMatrix, globals.camera.viewFromWorldMatrix); globals.camera.frustum.updateClipFrustum(globals.camera.clipFromWorldMatrix, globals.camera.clipSpaceNearZ); @@ -5844,7 +5843,7 @@ class d_a_title extends fopAc_ac_c { this.model_draw(globals, renderInstManager); renderInstManager.setCurrentList(globals.dlst.ui2D[0]); - this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); + this.screen.draw(renderInstManager, orthoCtx); } // TODO: This should be set by the Opa2D draw list diff --git a/src/ZeldaWindWaker/d_place_name.ts b/src/ZeldaWindWaker/d_place_name.ts index d323cfc8e..cfcbfb4a5 100644 --- a/src/ZeldaWindWaker/d_place_name.ts +++ b/src/ZeldaWindWaker/d_place_name.ts @@ -114,7 +114,7 @@ export class d_place_name extends msg_class { public override draw(globals: dGlobals, renderInstManager: GfxRenderInstManager, viewerInput: ViewerRenderInput): void { renderInstManager.setCurrentList(globals.dlst.ui[0]); - this.screen.draw(renderInstManager, globals.scnPlay.orthoGraf2D); + this.screen.draw(renderInstManager, globals.scnPlay.currentGrafPort); } public override execute(globals: dGlobals, deltaTimeFrames: number): void { From 0bfa9159bf6632453e2af2290d81dc192a0f68af Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 14:16:45 -0700 Subject: [PATCH 16/19] Wind Waker: Correct ortho viewport numbers --- src/ZeldaWindWaker/Main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index 78f2dfd7d..2e3a259de 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -742,7 +742,7 @@ class d_s_play extends fopScn { this.grassPacket = new GrassPacket(globals); this.woodPacket = new WoodPacket(globals); - this.currentGrafPort = new J2DGrafContext(globals.modelCache.device, 0.0, 0.0, 608.0, 448.0, -1.0, 1.0); + this.currentGrafPort = new J2DGrafContext(globals.modelCache.device, 0.0, 0.0, 640.0, 480.0, -1.0, 1.0); globals.scnPlay = this; From 5b9e2d2bafbfe574af9b0554479f69c203382735 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 14:33:16 -0700 Subject: [PATCH 17/19] J2D: Introduce J2DAnchorPos to correct for wider aspect ratios J2DScreens are typically 640x480. When rendering in a different aspect ratio, the height will scale to match but the new and old widths will differ. The new J2DAnchorPos determines at what X position the elements will be rendered. Either anchored in the center, or on the left/right side of the screen. E.g. With J2DAnchorPos.Left an element that was 10% from the left edge of the original 640x480, it will be 10% from the left screen edge. --- src/Common/JSYSTEM/J2Dv1.ts | 22 +++++++++++++++++++++- src/ZeldaWindWaker/d_a.ts | 4 ++-- src/ZeldaWindWaker/d_place_name.ts | 4 ++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 3bbb4d370..3899cbf12 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -54,6 +54,18 @@ function parseResourceReference(buffer: ArrayBufferSlice, offset: number, resTyp return { refType, resType, resName, arcName, _nextOffset: nextOffset }; } +/** + * When rendering a J2D element that was originally designed for 4:3 in a wider aspect ratio, the screenspace Y + * positions are maintained but the X positions must be adjusted. Often we simply want to keep the elements centered, + * but occasionally we want to anchor them to the left or right. + * See also: J2DGrafContext.setPort() and dPlaceName.load() + */ +export enum J2DAnchorPos { + Left, + Center, + Right +} + /** * If set, the UVs for a quad will be pinned (bound) to the quad edge. If not set, the UVs will be clipped by the quad. * For instance, if the texture is 200 pixels wide, but the quad is 100 pixels wide and Right is not set, the texture @@ -566,14 +578,22 @@ export class J2DPicture extends J2DPane { //#region J2DScreen export class J2DScreen extends J2DPane { public color: Color; + public anchorPos: J2DAnchorPos; - constructor(data: SCRN, cache: GfxRenderCache, resolver: ResourceResolver) { + constructor(data: SCRN, cache: GfxRenderCache, resolver: ResourceResolver, anchorPos: J2DAnchorPos) { super(data, cache, null); this.color = data.color; + this.anchorPos = anchorPos; this.resolveReferences(resolver); } public override draw(renderInstManager: GfxRenderInstManager, ctx2D: J2DGrafContext, offsetX?: number, offsetY?: number): void { + switch(this.anchorPos) { + case J2DAnchorPos.Left: this.data.x = 0; break; + case J2DAnchorPos.Center: this.data.x = (ctx2D.aspectRatioCorrection - 1.0) * ctx2D.viewport[2] * 0.5; break; + case J2DAnchorPos.Right: this.data.x = (ctx2D.aspectRatioCorrection - 1.0) * ctx2D.viewport[2]; break; + } + super.draw(renderInstManager, ctx2D, offsetX, offsetY); } diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index b574dba6d..87791c775 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -38,7 +38,7 @@ import { fopAcIt_JudgeByID, fopAcM_create, fopAcM_prm_class, fopAc_ac_c } from " import { cPhs__Status, fGlobals, fpcPf__Register, fpcSCtRq_Request, fpc_bs__Constructor } from "./framework.js"; import { mDoExt_McaMorf, mDoExt_bckAnm, mDoExt_brkAnm, mDoExt_btkAnm, mDoExt_btpAnm, mDoExt_modelEntryDL, mDoExt_modelUpdateDL } from "./m_do_ext.js"; import { MtxPosition, MtxTrans, calc_mtx, mDoMtx_XYZrotM, mDoMtx_XrotM, mDoMtx_YrotM, mDoMtx_YrotS, mDoMtx_ZXYrotM, mDoMtx_ZrotM, mDoMtx_ZrotS, quatM } from "./m_do_mtx.js"; -import { J2DGrafContext, J2DPane, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; +import { J2DAnchorPos, J2DGrafContext, J2DPane, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; // Framework'd actors @@ -5857,7 +5857,7 @@ class d_a_title extends fopAc_ac_c { private proc_init2D(globals: dGlobals) { const screenData = globals.resCtrl.getObjectResByName(ResType.Blo, d_a_title.arcName, "title_logo_e.blo"); assert(screenData !== null); - this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName)); + this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName), J2DAnchorPos.Center); this.screen.color = White; this.screen.setAlpha(1.0); // TODO: This isn't here originally diff --git a/src/ZeldaWindWaker/d_place_name.ts b/src/ZeldaWindWaker/d_place_name.ts index cfcbfb4a5..ad79f4ccf 100644 --- a/src/ZeldaWindWaker/d_place_name.ts +++ b/src/ZeldaWindWaker/d_place_name.ts @@ -1,4 +1,4 @@ -import { J2DPicture, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; +import { J2DAnchorPos, J2DPicture, J2DScreen } from "../Common/JSYSTEM/J2Dv1.js"; import { BTI, BTIData } from "../Common/JSYSTEM/JUTTexture.js"; import { GfxRenderInstManager } from "../gfx/render/GfxRenderInstManager.js"; import { assertExists } from "../util.js"; @@ -89,7 +89,7 @@ export class d_place_name extends msg_class { return status; const screenData = globals.resCtrl.getObjectRes(ResType.Blo, `PName`, 0x04); - this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver('PName')); + this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver('PName'), J2DAnchorPos.Left); this.screen.search('blc1')!.hide(); this.screen.search('blc2')!.hide(); From 4afc2900a8c80f67f2b25cda02ef0e8d78bf796d Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 1 Jan 2025 08:58:35 -0700 Subject: [PATCH 18/19] Formatting cleanup --- src/Common/JSYSTEM/J2Dv1.ts | 30 +++++++++++++++--------------- src/ZeldaWindWaker/Main.ts | 18 ++++++++++-------- src/ZeldaWindWaker/d_a.ts | 32 ++++++++++++++++---------------- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 3899cbf12..54dde8b9b 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -57,13 +57,13 @@ function parseResourceReference(buffer: ArrayBufferSlice, offset: number, resTyp /** * When rendering a J2D element that was originally designed for 4:3 in a wider aspect ratio, the screenspace Y * positions are maintained but the X positions must be adjusted. Often we simply want to keep the elements centered, - * but occasionally we want to anchor them to the left or right. + * but occasionally we want to anchor them to the left or right side of the screen (e.g. Wind Waker place names). * See also: J2DGrafContext.setPort() and dPlaceName.load() */ export enum J2DAnchorPos { Left, Center, - Right + Right } /** @@ -79,7 +79,7 @@ const enum J2DUVBinding { }; export class J2DGrafContext { - private clipSpaceNearZ: GfxClipSpaceNearZ; + private clipSpaceNearZ: GfxClipSpaceNearZ; public sceneParams = new SceneParams(); public viewport = vec4.create(); public ortho = vec4.create(); @@ -115,7 +115,7 @@ export class J2DGrafContext { const right = this.ortho[1] * this.aspectRatioCorrection; const bottom = this.ortho[2] + 0.5; const top = this.ortho[3]; - + projectionMatrixForCuboid(this.sceneParams.u_Projection, left, right, bottom, top, this.near, this.far); projectionMatrixConvertClipSpaceNearZ(this.sceneParams.u_Projection, this.clipSpaceNearZ, GfxClipSpaceNearZ.NegativeOne); } @@ -246,8 +246,8 @@ export class BLO { const panes: PAN1[] = []; const screen: SCRN = { - parent: null, type: 'SCRN', children: [], visible: true, - x: 0, y: 0, w: inf1.width, h: inf1.height, color: inf1.color, rot: 0, tag: '', basePos: 0, + parent: null, type: 'SCRN', children: [], visible: true, + x: 0, y: 0, w: inf1.width, h: inf1.height, color: inf1.color, rot: 0, tag: '', basePos: 0, alpha: inf1.color.a, inheritAlpha: false, offset: 0, }; @@ -324,12 +324,12 @@ export class J2DPane { this.data.visible = false; } - public setAlpha(alpha: number) { - this.data.alpha = alpha * 0xFF; + public setAlpha(alpha: number) { + this.data.alpha = alpha * 0xFF; } - - public getAlpha(alpha: number) { - this.data.alpha = alpha / 0xFF; + + public getAlpha(alpha: number) { + this.data.alpha = alpha / 0xFF; } // NOTE: Overwritten by child classes which actually do some rendering, such as J2DPicture @@ -363,7 +363,7 @@ export class J2DPane { } } } - + public search(tag: string): J2DPane | null { if (this.data.tag === tag) return this; @@ -526,7 +526,7 @@ export class J2DPicture extends J2DPane { // Assume alpha is enabled. This is byte 1 on a JUTTexture, but noclip doesn't read it mb.setChanCtrl(GX.ColorChannelID.COLOR0A0, false, GX.ColorSrc.REG, GX.ColorSrc.VTX, 0, GX.DiffuseFunction.NONE, GX.AttenuationFunction.NONE); - + // 0: Multiply tex and vertex colors and alpha mb.setTevOrder(tevStage, GX.TexCoordID.TEXCOORD0, GX.TexMapID.TEXMAP0, GX.RasColorChannelID.COLOR0A0); mb.setTevColorIn(tevStage, GX.CC.ZERO, GX.CC.TEXC, GX.CC.RASC, GX.CC.ZERO); @@ -562,7 +562,7 @@ export class J2DPicture extends J2DPane { protected override resolveReferences(resolver: ResourceResolver): void { const timg = this.data.timg; - if (timg.refType > 1) { + if (timg.refType > 1) { if (timg.refType !== 2) console.warn(`Untested J2D feature refType ${timg.refType}`); this.tex = resolver(JUTResType.TIMG, timg.resName); @@ -588,7 +588,7 @@ export class J2DScreen extends J2DPane { } public override draw(renderInstManager: GfxRenderInstManager, ctx2D: J2DGrafContext, offsetX?: number, offsetY?: number): void { - switch(this.anchorPos) { + switch (this.anchorPos) { case J2DAnchorPos.Left: this.data.x = 0; break; case J2DAnchorPos.Center: this.data.x = (ctx2D.aspectRatioCorrection - 1.0) * ctx2D.viewport[2] * 0.5; break; case J2DAnchorPos.Right: this.data.x = (ctx2D.aspectRatioCorrection - 1.0) * ctx2D.viewport[2]; break; diff --git a/src/ZeldaWindWaker/Main.ts b/src/ZeldaWindWaker/Main.ts index 2e3a259de..4dd77efd8 100644 --- a/src/ZeldaWindWaker/Main.ts +++ b/src/ZeldaWindWaker/Main.ts @@ -940,9 +940,9 @@ class DemoDesc extends SceneDesc implements Viewer.SceneDesc { public override stageDir: string, public override name: string, public override roomList: number[], - public stbFilename: string, + public stbFilename: string, public layer: number, - public offsetPos?:vec3, + public offsetPos?: vec3, public rotY: number = 0, public startCode?: number, public eventFlags?: number, @@ -966,7 +966,7 @@ class DemoDesc extends SceneDesc implements Viewer.SceneDesc { globals.scnPlay.demo.remove(); // TODO: Don't render until the camera has been placed for this demo. The cuts are jarring. - + // noclip modification: This normally happens on room load. Do it here instead so that we don't waste time // loading .arcs for cutscenes that aren't going to be played const lbnk = globals.roomCtrl.status[this.roomList[0]].data.lbnk; @@ -987,7 +987,7 @@ class DemoDesc extends SceneDesc implements Viewer.SceneDesc { await globals.modelCache.waitForLoad(); // Most cutscenes expect the Link actor to be loaded - if(!fopAcM_searchFromName(globals, 'Link', 0, 0)) { + if (!fopAcM_searchFromName(globals, 'Link', 0, 0)) { fopAcM_create(globals.frameworkGlobals, dProcName_e.d_a_py_lk, 0, null, globals.mStayNo, null, null, 0xFF, -1); } @@ -997,10 +997,12 @@ class DemoDesc extends SceneDesc implements Viewer.SceneDesc { } // noclip modification: ensure all the actors are created before we load the cutscene - await new Promise(resolve => { (function waitForActors(){ - if (globals.frameworkGlobals.ctQueue.length === 0) return resolve(null); - setTimeout(waitForActors, 30); - })(); }); + await new Promise(resolve => { + (function waitForActors() { + if (globals.frameworkGlobals.ctQueue.length === 0) return resolve(null); + setTimeout(waitForActors, 30); + })(); + }); // @TODO: Set noclip layer visiblity based on this.layer diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 87791c775..979d7d04e 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5775,7 +5775,7 @@ class d_a_title extends fopAc_ac_c { private btkSubtitle = new mDoExt_btkAnm(); private btkShimmer = new mDoExt_btkAnm(); private screen: J2DScreen; - private panes: J2DPane[] = []; + private panes: J2DPane[] = []; private anmFrameCounter = 0 private delayFrameCounter = 120; @@ -5789,17 +5789,17 @@ class d_a_title extends fopAc_ac_c { const status = dComIfG_resLoad(globals, d_a_title.arcName); if (status !== cPhs__Status.Complete) return status; - + this.proc_init2D(globals); this.proc_init3D(globals); - + return cPhs__Status.Next; } public override execute(globals: dGlobals, deltaTimeFrames: number): void { if (this.delayFrameCounter > 0) { this.delayFrameCounter -= deltaTimeFrames; - + if (this.delayFrameCounter == 0) { // TODO: mDoAud_seStart(JA_SE_TITLE_WIND); } @@ -5812,7 +5812,7 @@ class d_a_title extends fopAc_ac_c { } else if (this.enterMode == 3) { this.shipFrameCounter += deltaTimeFrames; } - + this.bckShip.play(deltaTimeFrames); this.set_mtx(); } @@ -5838,7 +5838,7 @@ class d_a_title extends fopAc_ac_c { light.Position = [-35000.0, 0.0, -30000.0]; light.Direction = [0, 0, 0]; light.Color = White; - + { this.model_draw(globals, renderInstManager); @@ -5860,14 +5860,14 @@ class d_a_title extends fopAc_ac_c { this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName), J2DAnchorPos.Center); this.screen.color = White; this.screen.setAlpha(1.0); // TODO: This isn't here originally - + this.panes[TitlePane.MainTitle] = this.screen.search('zeld')!; this.panes[TitlePane.JapanSubtitle] = this.screen.search('zelj')!; this.panes[TitlePane.PressStart] = this.screen.search('pres')!; this.panes[TitlePane.Nintendo] = this.screen.search('nint')!; this.panes[4] = this.screen.search('eft1')!; this.panes[5] = this.screen.search('eft2')!; - + for (let pane of this.panes) { pane.setAlpha(0.0); } @@ -5876,7 +5876,7 @@ class d_a_title extends fopAc_ac_c { private proc_init3D(globals: dGlobals) { const modelDataShip = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xD); this.modelShip = new J3DModelInstance(modelDataShip); - + const modelDataSub = globals.resCtrl.getObjectRes(ResType.Model, d_a_title.arcName, 0xC); this.modelSubtitle = new J3DModelInstance(modelDataSub); @@ -5902,11 +5902,11 @@ class d_a_title extends fopAc_ac_c { } private model_draw(globals: dGlobals, renderInstManager: GfxRenderInstManager) { - + if (this.btkSubtitle.frameCtrl.getFrame() != 0.0) { this.btkShimmer.entry(this.modelSubtitleShimmer) mDoExt_modelUpdateDL(globals, this.modelSubtitleShimmer, renderInstManager, globals.dlst.ui); - + this.btkSubtitle.entry(this.modelSubtitle); mDoExt_modelUpdateDL(globals, this.modelSubtitle, renderInstManager, globals.dlst.ui); } @@ -5921,16 +5921,16 @@ class d_a_title extends fopAc_ac_c { private set_mtx() { vec3.set(this.modelShip.baseScale, 0.9, 0.9, 0.9); mat4.fromTranslation(this.modelShip.modelMatrix, [this.shipOffsetX, 0, 1000]); - mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); + mDoMtx_ZXYrotM(this.modelShip.modelMatrix, [0, 0x4000, 0]); vec3.set(this.modelSubtitle.baseScale, 1.0, 1.0, 1.0); vec3.set(this.modelSubtitleShimmer.baseScale, 1.0, 1.0, 1.0); mat4.fromTranslation(this.modelSubtitle.modelMatrix, [-57.0, -3.0, -10000.0]); - mDoMtx_ZXYrotM(this.modelSubtitle.modelMatrix, [0, -0x8000, 0]); + mDoMtx_ZXYrotM(this.modelSubtitle.modelMatrix, [0, -0x8000, 0]); mat4.fromTranslation(this.modelSubtitleShimmer.modelMatrix, [-57.0, -3.0, -10010.0]); - mDoMtx_ZXYrotM(this.modelSubtitleShimmer.modelMatrix, [0, -0x8000, 0]); + mDoMtx_ZXYrotM(this.modelSubtitleShimmer.modelMatrix, [0, -0x8000, 0]); } private calc_2d_alpha(deltaTimeFrames: number) { @@ -5962,7 +5962,7 @@ class d_a_title extends fopAc_ac_c { if (this.anmFrameCounter >= 80) { this.btkSubtitle.play(deltaTimeFrames); } - + if (this.anmFrameCounter <= 150) { this.panes[TitlePane.Nintendo].setAlpha(0.0); } else if (this.anmFrameCounter <= 170) { @@ -6001,7 +6001,7 @@ class d_a_title extends fopAc_ac_c { if (this.shimmerFrameCounter <= 0) { const finished = this.btkShimmer.play(deltaTimeFrames); - if(finished) { + if (finished) { this.btkShimmer.frameCtrl.setFrame(0.0); this.btkShimmer.frameCtrl.setRate(1.0); this.shimmerFrameCounter = cM_rndF(120) + 10; From 04f5353911bf934a6c8bd024e87b3c43356933b9 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Wed, 1 Jan 2025 09:13:12 -0700 Subject: [PATCH 19/19] J2D: Fix J2DScreen default alpha to 0xFF --- src/Common/JSYSTEM/J2Dv1.ts | 2 +- src/ZeldaWindWaker/d_a.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 54dde8b9b..1b6687f75 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -248,7 +248,7 @@ export class BLO { const screen: SCRN = { parent: null, type: 'SCRN', children: [], visible: true, x: 0, y: 0, w: inf1.width, h: inf1.height, color: inf1.color, rot: 0, tag: '', basePos: 0, - alpha: inf1.color.a, inheritAlpha: false, offset: 0, + alpha: 0xFF, inheritAlpha: true, offset: 0, }; let parentStack: (PAN1 | null)[] = [screen]; diff --git a/src/ZeldaWindWaker/d_a.ts b/src/ZeldaWindWaker/d_a.ts index 979d7d04e..180b12df7 100644 --- a/src/ZeldaWindWaker/d_a.ts +++ b/src/ZeldaWindWaker/d_a.ts @@ -5859,7 +5859,6 @@ class d_a_title extends fopAc_ac_c { assert(screenData !== null); this.screen = new J2DScreen(screenData, globals.renderer.renderCache, globals.resCtrl.getResResolver(d_a_title.arcName), J2DAnchorPos.Center); this.screen.color = White; - this.screen.setAlpha(1.0); // TODO: This isn't here originally this.panes[TitlePane.MainTitle] = this.screen.search('zeld')!; this.panes[TitlePane.JapanSubtitle] = this.screen.search('zelj')!;