Skip to content

Commit

Permalink
JStudio: Added JMSG, RangeOutside, and RangeAdjust support
Browse files Browse the repository at this point in the history
  • Loading branch information
themikelester committed Dec 11, 2024
1 parent 8dae195 commit 5efd35e
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 38 deletions.
192 changes: 156 additions & 36 deletions src/Common/JSYSTEM/JStudio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ const scratchVec3a = vec3.create();
const scratchVec3b = vec3.create();
const scratchVec3c = vec3.create();

// TODO: Setup the JMessage system in a separate file
export namespace JMessage {
export class TControl {
public setMessageCode(packed: number): boolean { return true; };
}
}

//----------------------------------------------------------------------------------------------------------------------
// Stage Objects
// STB Objects
// These are created an managed by the game. Each Stage Object has a corresponding STB Object, connected by an Adaptor.
// The STB objects are manipulated by Sequences from the STB file each frame, and update the Stage Object via Adaptor.
//----------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -203,11 +210,11 @@ abstract class TAdaptor {
public enableLogging = true,
) { }

public abstract adaptor_do_prepare(obj: STBObject): void;
public abstract adaptor_do_begin(obj: STBObject): void;
public abstract adaptor_do_end(obj: STBObject): void;
public abstract adaptor_do_update(obj: STBObject, frameCount: number): void;
public abstract adaptor_do_data(obj: STBObject, id: number, data: DataView): void;
public adaptor_do_prepare(obj: STBObject): void {};
public adaptor_do_begin(obj: STBObject): void {};
public adaptor_do_end(obj: STBObject): void {};
public adaptor_do_update(obj: STBObject, frameCount: number): void {};
public adaptor_do_data(obj: STBObject, id: number, data: DataView): void {};

// Set a single VariableValue update function, with the option of using FuncVals
public adaptor_setVariableValue(obj: STBObject, keyIdx: number, data: ParagraphData) {
Expand Down Expand Up @@ -573,7 +580,7 @@ class TActorAdaptor extends TAdaptor {
return frame;
}

public adaptor_do_prepare(obj: STBObject): void {
public override adaptor_do_prepare(obj: STBObject): void {
this.variableValues[EActorTrack.AnimTransition].setOutput(this.object.JSGSetAnimationTransition.bind(this.object));

this.variableValues[EActorTrack.AnimFrame].setOutput((frame: number, adaptor: TAdaptor) => {
Expand All @@ -587,7 +594,7 @@ class TActorAdaptor extends TAdaptor {
});
}

public adaptor_do_begin(obj: STBObject): void {
public override adaptor_do_begin(obj: STBObject): void {
this.object.JSGFEnableFlag(1);

const pos = scratchVec3a;
Expand All @@ -611,11 +618,11 @@ class TActorAdaptor extends TAdaptor {
this.variableValues[EActorTrack.AnimFrame].setValue_immediate(this.object.JSGGetTextureAnimationFrame());
}

public adaptor_do_end(obj: STBObject): void {
public override adaptor_do_end(obj: STBObject): void {
this.object.JSGFDisableFlag(1);
}

public adaptor_do_update(obj: STBObject, frameCount: number): void {
public override adaptor_do_update(obj: STBObject, frameCount: number): void {
const pos = scratchVec3a;
const rot = scratchVec3b;
const scale = scratchVec3c;
Expand All @@ -633,7 +640,7 @@ class TActorAdaptor extends TAdaptor {
this.object.JSGSetScaling(scale);
}

public adaptor_do_data(obj: STBObject, id: number, data: DataView): void {
public override adaptor_do_data(obj: STBObject, id: number, data: DataView): void {
this.log(`SetData: ${id}`);
this.object.JSGSetData(id, data);
}
Expand Down Expand Up @@ -868,14 +875,14 @@ class TCameraAdaptor extends TAdaptor {
override object: TCamera
) { super(11); }

public adaptor_do_prepare(obj: STBObject): void {
public override adaptor_do_prepare(obj: STBObject): void {
this.variableValues[ECameraTrack.FovY].setOutput(this.object.JSGSetProjectionFovy.bind(this.object));
this.variableValues[ECameraTrack.Roll].setOutput(this.object.JSGSetViewRoll.bind(this.object));
this.variableValues[ECameraTrack.DistNear].setOutput(this.object.JSGSetProjectionNear.bind(this.object));
this.variableValues[ECameraTrack.DistFar].setOutput(this.object.JSGSetProjectionFar.bind(this.object));
}

public adaptor_do_begin(obj: STBObject): void {
public override adaptor_do_begin(obj: STBObject): void {
const camPos = scratchVec3a;
const targetPos = scratchVec3b;
this.object.JSGGetViewPosition(camPos);
Expand All @@ -892,11 +899,11 @@ class TCameraAdaptor extends TAdaptor {
this.variableValues[ECameraTrack.DistFar].setValue_immediate(this.object.JSGGetProjectionFar());
}

public adaptor_do_end(obj: STBObject): void {
public override adaptor_do_end(obj: STBObject): void {
this.object.JSGFDisableFlag(1);
}

public adaptor_do_update(obj: STBObject, frameCount: number): void {
public override adaptor_do_update(obj: STBObject, frameCount: number): void {
const camPos = scratchVec3a;
const targetPos = scratchVec3b;

Expand All @@ -910,7 +917,7 @@ class TCameraAdaptor extends TAdaptor {
this.object.JSGSetViewTargetPosition(targetPos);
}

public adaptor_do_data(obj: STBObject, id: number, data: DataView): void {
public override adaptor_do_data(obj: STBObject, id: number, data: DataView): void {
// This is not used by TWW. Untested.
debugger;
}
Expand Down Expand Up @@ -983,6 +990,39 @@ class TCameraObject extends STBObject {
}
}

//----------------------------------------------------------------------------------------------------------------------
// Message
//----------------------------------------------------------------------------------------------------------------------
class TMessageAdaptor extends TAdaptor {
constructor( private messageControl: JMessage.TControl ) { super(0, []); }

public adaptor_do_MESSAGE(data: ParagraphData): void {
if(this.enableLogging) console.debug('JMSG:', data.value);
switch (data.dataOp) {
case EDataOp.ObjectIdx: this.messageControl.setMessageCode(data.value); break;
default: assert(false);
}
}
}

class TMessageObject extends STBObject {
override adaptor: TMessageAdaptor;

constructor(
control: TControl,
blockObj: TBlockObject,
adaptor: TMessageAdaptor,
) { super(control, blockObj, adaptor) }

public override do_paragraph(file: Reader, dataSize: number, dataOffset: number, param: number): void {
const type = param >> 5;
const dataOp = param & 0x1F;
switch( type ) {
case 0x42: return this.adaptor.adaptor_do_MESSAGE(readData(dataOp, dataOffset, dataSize, file));
default: console.error('Unexpected JMSG paragraph type:', type);
}
}
}

//----------------------------------------------------------------------------------------------------------------------
// Parsing helpers
Expand Down Expand Up @@ -1057,13 +1097,21 @@ namespace FVB {
InterpSet = 0x16,
};

enum EExtrapolationType {
enum EExtrapolateType {
Raw,
Repeat,
Turn,
Clamp
}

enum EAdjustType {
Raw = 0,
BiasBegin = 1,
BiasEnd = 2,
BiasAve = 3,
Remap = 4,
};

class TBlock {
size: number;
type: number;
Expand All @@ -1072,6 +1120,7 @@ namespace FVB {
};

export abstract class TFunctionValue {
public idNo: number;
protected range: Attribute.Range | null = null;
protected refer: Attribute.Refer | null = null;
protected interpolate: Attribute.Interpolate | null = null;
Expand All @@ -1084,18 +1133,16 @@ namespace FVB {
public getAttrRefer() { return this.refer; }
public getAttrInterpolate() { return this.interpolate; }

public static toFunction_outside(type: EExtrapolationType): (frame: number, maxFrame: number) => number {
public setIdNo(idNo: number) { this.idNo = idNo; }

public static toFunction_outside(type: EExtrapolateType): (frame: number, maxFrame: number) => number {
switch (type) {
case EExtrapolationType.Raw: return (f, m) => f;
case EExtrapolationType.Repeat: return (f, m) => { f = f % m; return f < 0 ? f + m : f; }
case EExtrapolationType.Turn: return (f, m) => { f %= (2 * m); if (f < 0) f += m; return f > m ? 2 * m - f : f };
case EExtrapolationType.Clamp: return (f, m) => clamp(f, 0.0, m);
case EExtrapolateType.Raw: return (f, m) => f;
case EExtrapolateType.Repeat: return (f, m) => { f = f % m; return f < 0 ? f + m : f; }
case EExtrapolateType.Turn: return (f, m) => { f %= (2 * m); if (f < 0) f += m; return f > m ? 2 * m - f : f };
case EExtrapolateType.Clamp: return (f, m) => clamp(f, 0.0, m);
}
}

// static ExtrapolateParameter toFunction(TFunctionValue::TEOutside outside) {
// return toFunction_outside(outside);
// }
}

export abstract class TObject {
Expand Down Expand Up @@ -1171,10 +1218,28 @@ namespace FVB {
const interpType = file.view.getUint32(para.dataOffset + 0);
interp.set(interpType);
break;

case EPrepareOp.RangeOutside: {
assert(para.dataSize === 4);
const range = this.funcVal.getAttrRange();
assert(!!range, 'FVB Paragraph assumes FuncVal has range attribute, but it does not');
const underflow = file.view.getInt16(para.dataOffset + 0);
const overflow = file.view.getInt16(para.dataOffset + 2);
range.setExtrapolation(underflow, overflow);
break;
}

case EPrepareOp.RangeAdjust: {
assert(para.dataSize === 4);
const range = this.funcVal.getAttrRange();
assert(!!range, 'FVB Paragraph assumes FuncVal has range attribute, but it does not');
const adjust = file.view.getInt32(para.dataOffset + 0)
range.setAdjust(adjust);
break;
}


case EPrepareOp.RangeProgress:
case EPrepareOp.RangeAdjust:
case EPrepareOp.RangeOutside:
default:
console.warn('Unhandled FVB PrepareOp: ', para.type);
debugger;
Expand Down Expand Up @@ -1235,6 +1300,7 @@ namespace FVB {
if (!obj) { return false; }

obj.prepare(block, this.control, file);
obj.funcVal.setIdNo(this.control.objects.length);
this.control.objects.push(obj);

return true;
Expand Down Expand Up @@ -1273,7 +1339,9 @@ namespace FVB {
private diff: number = 0;

private progress: number = 0;
private adjust: number = 0;
private adjust: EAdjustType = 0;
private underflow: EExtrapolateType = 0;
private overflow: EExtrapolateType = 0;

public prepare() {
// Progress updated here
Expand All @@ -1286,10 +1354,47 @@ namespace FVB {
assert(this.diff >= 0);
}

public setAdjust(adjust: EAdjustType) {
this.adjust = adjust;
}

public setExtrapolation(underflow: EExtrapolateType, overflow: EExtrapolateType) {
this.underflow = underflow;
this.overflow = overflow;
}

public getParameter(time: number, startTime: number, endTime: number): number {
// @NOTE: Does not currently support, Progress, Adjust, or Outside modifications. These can only be set
// @NOTE: Does not currently support, Progress modifications. These can only be set
// in an FVB paragraph, so attempt to set them will be caught in FVB.TObject.prepare().
return time;

const progress = time;

if( this.adjust != 0 ) {
debugger; // Untested. Remove once confirmed working
}

switch (this.adjust) {
case EAdjustType.Raw: return this.extrapolate(progress);
case EAdjustType.BiasBegin: return this.extrapolate(progress + this.begin);
case EAdjustType.BiasEnd: return this.extrapolate(progress) + this.end;
case EAdjustType.BiasAve: return this.extrapolate(progress) + 0.5 * (this.begin + this.end);
case EAdjustType.Remap:
const temp = this.extrapolate(progress);
return startTime + ((temp - this.begin) * (endTime - startTime)) / this.diff;

default:
debugger;
return this.extrapolate(progress);
}
}

private extrapolate(progress: number) {
let t = progress
t -= this.begin;
if( t < 0.0 ) { t = FVB.TFunctionValue.toFunction_outside(this.underflow)(t, this.diff); }
else if( t >= this.diff) { t = FVB.TFunctionValue.toFunction_outside(this.overflow)(t, this.diff); }
t += this.begin;
return t;
}
}

Expand Down Expand Up @@ -1631,7 +1736,6 @@ namespace FVB {
}

public static composite_add(fvs: TFunctionValue[], dataVal: number, timeSec: number): number {
debugger; // Untested. Remove once confirmed working
let val = dataVal;
for (let fv of fvs) { val += fv.getValue(timeSec); }
return val;
Expand Down Expand Up @@ -1771,6 +1875,7 @@ export abstract class TBlockObject {
// This combines JStudio::TControl and JStudio::stb::TControl into a single class, for simplicity.
export class TControl {
public system: TSystem;
public msgControl: JMessage.TControl;
public fvbControl = new FVB.TControl();
public secondsPerFrame: number = 1 / 30.0;
private suspendFrames: number;
Expand All @@ -1786,8 +1891,9 @@ export class TControl {
// A special object that the STB file can use to suspend the demo (such as while waiting for player input)
private controlObject = new TControlObject(this);

constructor(system: TSystem) {
constructor(system: TSystem, msgControl: JMessage.TControl) {
this.system = system;
this.msgControl = msgControl;
}

public isSuspended() { return this.suspendFrames > 0; }
Expand Down Expand Up @@ -1837,8 +1943,8 @@ export class TControl {
public getFunctionValueByIdx(idx: number) { return this.fvbControl.objects[idx].funcVal; }
public getFunctionValueByName(name: string) { return this.fvbControl.objects.find(v => v.id === name)?.funcVal; }

// Really this is a stb::TFactory method
public createObject(blockObj: TBlockObject): STBObject | null {
// Really this is a stb::TFactory `createObject` method
public createStageObject(blockObj: TBlockObject): STBObject | null {
let objConstructor;
let objType: JStage.EObject;
switch (blockObj.type) {
Expand All @@ -1862,6 +1968,18 @@ export class TControl {
return obj;
}

public createMessageObject(blockObj: TBlockObject): STBObject | null {
if( blockObj.type == 'JMSG' ) {
const adaptor = new TMessageAdaptor(this.msgControl);
const obj = new TMessageObject(this, blockObj, adaptor);

if (obj) { adaptor.adaptor_do_prepare(obj); }
this.objects.push(obj);
return obj;
}
return null;
}

public destroyObject_all() {
this.objects = [];
this.fvbControl.destroyObject_all();
Expand Down Expand Up @@ -1899,7 +2017,9 @@ export class TParse {
return true;
}

const obj = this.control.createObject(blockObj);
let obj = this.control.createStageObject(blockObj);
if(!obj) { obj = this.control.createMessageObject(blockObj); }

if (!obj) {
if (flags & 0x40) {
console.debug('Unhandled flag during parseBlockObject: 0x40');
Expand Down
5 changes: 3 additions & 2 deletions src/ZeldaWindWaker/d_demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mat4, ReadonlyVec3, vec3 } from "gl-matrix";
import ArrayBufferSlice from "../ArrayBufferSlice.js";
import { J3DModelInstance } from "../Common/JSYSTEM/J3D/J3DGraphBase.js";
import { LoopMode, TPT1, TTK1 } from "../Common/JSYSTEM/J3D/J3DLoader.js";
import { JStage, TActor, TCamera, TControl, TParse, TSystem } from "../Common/JSYSTEM/JStudio.js";
import { JMessage, JStage, TActor, TCamera, TControl, TParse, TSystem } from "../Common/JSYSTEM/JStudio.js";
import { getMatrixAxisY } from "../MathHelpers.js";
import { assert } from "../util.js";
import { ResType } from "./d_resorce.js";
Expand Down Expand Up @@ -427,7 +427,8 @@ export class dDemo_manager_c {

private parser: TParse;
private system = new dDemo_system_c(this.globals);
private control: TControl = new TControl(this.system);
private messageControl = new JMessage.TControl(); // TODO
private control: TControl = new TControl(this.system, this.messageControl);

constructor(
private globals: dGlobals
Expand Down

0 comments on commit 5efd35e

Please sign in to comment.