From 5b9e2d2bafbfe574af9b0554479f69c203382735 Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Mon, 30 Dec 2024 14:33:16 -0700 Subject: [PATCH] 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();