From 5c3eb882e36b796565c0bd1c746f50bb9c9b391a Mon Sep 17 00:00:00 2001 From: Mike Lester Date: Sat, 28 Dec 2024 13:55:39 -0700 Subject: [PATCH] JSYSTEM: Introducing 'ResourceResolver' concept for resources which reference other resources J2D BLO files reference other texture resources. The games need a way of resolving these references into the actual resources. A ResourceResolver is a simple callback of the form: `ResourceResolver = (resType: string, resName: string) => any` Note that the arc which contains those resources is not passed directly to the callback, because the BLO file does not know which arc it is being loaded from. Instead, the resolver is expected to have been created with the arc already specified. E.g. In Wind Waker this is handled by `dResControl_c.getResResolver(arcName: string): ResourceResolver` which returns a resolver function for the specified arc. Passing the JKRArchive directly to the BLO was also considered. But in the case of Wind Waker, the BTI images that are referenced have already been loaded by dResCtrl_c, so having the BLO create them again would result in duplicate resources. The callback seems to be the best way to resolve this. --- src/Common/JSYSTEM/J2Dv1.ts | 26 +++++++++++++++++++++----- src/ZeldaWindWaker/d_place_name.ts | 4 ++-- src/ZeldaWindWaker/d_resorce.ts | 15 +++++++++++++-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Common/JSYSTEM/J2Dv1.ts b/src/Common/JSYSTEM/J2Dv1.ts index 5785bff27..f3e26c0c3 100644 --- a/src/Common/JSYSTEM/J2Dv1.ts +++ b/src/Common/JSYSTEM/J2Dv1.ts @@ -13,7 +13,7 @@ import { GfxClipSpaceNearZ, GfxDevice } from "../../gfx/platform/GfxPlatform.js" import { computeModelMatrixT, projectionMatrixForCuboid } from "../../MathHelpers.js"; import { projectionMatrixConvertClipSpaceNearZ } from "../../gfx/helpers/ProjectionHelpers.js"; import { TSDraw } from "../../SuperMarioGalaxy/DDraw.js"; -import { BTIData } from "./JUTTexture.js"; +import { BTI, BTIData } from "./JUTTexture.js"; import { GXMaterialBuilder } from "../../gx/GXMaterialBuilder.js"; import { mat4, vec2, vec4 } from "gl-matrix"; import { GfxRenderCache } from "../../gfx/render/GfxRenderCache.js"; @@ -24,6 +24,9 @@ const drawParams = new DrawParams(); const scratchMat = mat4.create(); +// TODO: Find a better home for this +export type ResourceResolver = (resType: string, resName: string) => any | null; + interface ResRef { refType: number; resType: string; @@ -316,6 +319,12 @@ export class J2DPane { computeModelMatrixT(this.drawMtx, this.drawPos[0], this.drawPos[1], 0); } } + + protected resolveReferences(resolver: ResourceResolver) { + for(let child of this.children) { + child.resolveReferences(resolver); + } + } } //#endregion @@ -329,10 +338,7 @@ export class J2DPicture extends J2DPane { constructor(data: PAN1, private cache: GfxRenderCache, parent: J2DPane | null) { super(data, cache, parent); - // @TODO: If type > 4, load the image on construction - if (this.data.timg.refType !== 0 && this.data.timg.refType !== 2) { console.warn('Untested J2D feature'); } - if (this.data.tlut.refType !== 0) { console.warn('Untested J2D feature'); } if (this.data.uvBinding !== 15) { console.warn('Untested J2D feature'); } if (this.data.flags !== 0) { console.warn('Untested J2D feature'); } if (this.data.colorBlack !== 0 || this.data.colorWhite !== 0xFFFFFFFF) { console.warn('Untested J2D feature'); } @@ -448,6 +454,15 @@ export class J2DPicture extends J2DPane { mb.setUsePnMtxIdx(false); this.materialHelper = new GXMaterialHelperGfx(mb.finish()); } + + protected override resolveReferences(resolver: ResourceResolver) { + if(this.data.timg.refType > 1) { + if (this.data.timg.refType !== 2) { console.warn('Untested J2D feature'); } + this.tex = resolver(this.data.timg.resType, this.data.timg.resName); + if (this.tex) { this.prepare(); } + } + assert(this.data.tlut.refType === 0, 'TLUT references currently unsupported'); + } } //#endregion @@ -455,9 +470,10 @@ export class J2DPicture extends J2DPane { export class J2DScreen extends J2DPane { public color: Color - constructor(data: SCRN, cache: GfxRenderCache) { + constructor(data: SCRN, cache: GfxRenderCache, resolver: ResourceResolver) { super(data, cache, null); this.color = data.color; + this.resolveReferences(resolver); } public override draw(renderInstManager: GfxRenderInstManager, viewerRenderInput: ViewerRenderInput, ctx2D: J2DGrafContext, offsetX?: number, offsetY?: number): void { diff --git a/src/ZeldaWindWaker/d_place_name.ts b/src/ZeldaWindWaker/d_place_name.ts index 2f9a9a546..533527521 100644 --- a/src/ZeldaWindWaker/d_place_name.ts +++ b/src/ZeldaWindWaker/d_place_name.ts @@ -4,7 +4,7 @@ import { GfxRenderInstManager } from "../gfx/render/GfxRenderInstManager.js"; import { ViewerRenderInput } from "../viewer.js"; import { EDemoMode } from "./d_demo.js"; import { dProcName_e } from "./d_procname.js"; -import { dComIfG_resLoad, ResType } from "./d_resorce.js"; +import { dComIfG_resLoad, dRes_control_c, ResType } from "./d_resorce.js"; import { cPhs__Status, fGlobals, fopMsgM_Delete, fpc_bs__Constructor, fpcPf__Register, fpcSCtRq_Request, msg_class } from "./framework.js"; import { dGlobals } from "./Main.js"; @@ -103,7 +103,7 @@ export class d_place_name extends msg_class { img = new BTIData(globals.sceneContext.device, globals.renderer.renderCache, BTI.parse(imgData, filename).texture); } - this.screen = new J2DScreen(screen, globals.renderer.renderCache); + this.screen = new J2DScreen(screen, globals.renderer.renderCache, globals.resCtrl.getResResolver('PName')); this.screen.children[0].children[0].data.visible = false; this.screen.children[0].children[1].data.visible = false; const pic = this.screen.children[0].children[2] as J2DPicture; diff --git a/src/ZeldaWindWaker/d_resorce.ts b/src/ZeldaWindWaker/d_resorce.ts index 3520a9f4b..92d1dd5c7 100644 --- a/src/ZeldaWindWaker/d_resorce.ts +++ b/src/ZeldaWindWaker/d_resorce.ts @@ -13,7 +13,7 @@ import { dGlobals } from "./Main.js"; import { cPhs__Status } from "./framework.js"; import { cBgD_t } from "./d_bg.js"; import { NamedArrayBufferSlice } from "../DataFetcher.js"; -import { BLO, SCRN } from "../Common/JSYSTEM/J2Dv1.js"; +import { BLO, ResourceResolver, SCRN } from "../Common/JSYSTEM/J2Dv1.js"; export interface DZSChunkHeader { type: string; @@ -113,7 +113,18 @@ export class dRes_control_c { public getResByID(resType: T, arcName: string, resID: number, resList: dRes_info_c[]): ResAssetType { const resInfo = assertExists(this.findResInfo(arcName, resList)); return resInfo.getResByID(resType, resID); - } + } + + public getResResolver(arcName: string): ResourceResolver { + return (resType: string, resName: string) => { + switch(resType) { + case 'TIMG': return this.getObjectResByName(ResType.Bti, arcName, resName); + case 'TLUT': console.warn('TLUT resource references not yet supported'); debugger; return null; + case 'FONT': console.warn('FONT resource references not yet supported'); debugger; return null; + default: return null; + } + } + } public mountRes(device: GfxDevice, cache: GfxRenderCache, arcName: string, archive: JKRArchive, resList: dRes_info_c[]): void { if (this.findResInfo(arcName, resList) !== null)