mirror of
https://github.com/sndrec/WebMonkeyBall.git
synced 2026-02-03 10:13:33 +00:00
partial scrolling texture support
This commit is contained in:
@@ -29,6 +29,7 @@ export const TevLayerFlags = {
|
||||
TypeAlphaBlend: 1 << 13,
|
||||
TypeWorldSpecular: 1 << 15,
|
||||
Unk16: 1 << 16,
|
||||
EnableUvScroll: 1 << 17,
|
||||
};
|
||||
|
||||
export const ModelFlags = {
|
||||
|
||||
@@ -355,6 +355,12 @@ export class AnimGroup {
|
||||
const rp = scratchRenderParams;
|
||||
rp.reset();
|
||||
rp.lighting = state.lighting;
|
||||
const textureScroll = this.agData.textureScroll;
|
||||
if (textureScroll && (this.stageData.gameSource === "smb2" || this.stageData.gameSource === "mb2ws")) {
|
||||
const timeSeconds = state.time.getAnimTimeSeconds();
|
||||
vec3.set(scratchVec3b, textureScroll.speed[0] * timeSeconds, textureScroll.speed[1] * timeSeconds, 0);
|
||||
mat4.fromTranslation(rp.texMtx, scratchVec3b);
|
||||
}
|
||||
|
||||
const viewFromAnimGroup = scratchMat4a;
|
||||
mat4.mul(viewFromAnimGroup, viewFromWorld, this.worldFromAg);
|
||||
|
||||
@@ -114,6 +114,18 @@ export class BgObjectInst {
|
||||
if (texMtx !== undefined) {
|
||||
mat4.copy(renderParams.texMtx, texMtx);
|
||||
}
|
||||
const textureScroll = this.bgObjectData.textureScroll;
|
||||
if (textureScroll) {
|
||||
const timeSeconds = state.time.getAnimTimeSeconds();
|
||||
const scroll = scratchVec3c;
|
||||
vec3.set(scroll, textureScroll.speed[0] * timeSeconds, textureScroll.speed[1] * timeSeconds, 0);
|
||||
if (texMtx !== undefined) {
|
||||
mat4.fromTranslation(scratchMat4a, scroll);
|
||||
mat4.mul(renderParams.texMtx, renderParams.texMtx, scratchMat4a);
|
||||
} else {
|
||||
mat4.fromTranslation(renderParams.texMtx, scroll);
|
||||
}
|
||||
}
|
||||
|
||||
mat4.mul(renderParams.viewFromModel, viewMatrix, this.worldFromModel);
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ export enum TevLayerFlags {
|
||||
TypeWorldSpecular = 1 << 15,
|
||||
|
||||
Unk16 = 1 << 16,
|
||||
EnableUvScroll = 1 << 17,
|
||||
}
|
||||
|
||||
export type TevLayer = {
|
||||
|
||||
@@ -77,10 +77,16 @@ function fillWorldSpecularMapping(renderCache: GfxRenderCache, mapping: Material
|
||||
mapping.height = WORLD_SPECULAR_TEX_HEIGHT;
|
||||
}
|
||||
|
||||
function buildDiffuseLayer(mb: GXMaterialBuilder, state: BuildState, colorIn: GX.CC, alphaIn: GX.CA) {
|
||||
function buildDiffuseLayer(
|
||||
mb: GXMaterialBuilder,
|
||||
state: BuildState,
|
||||
colorIn: GX.CC,
|
||||
alphaIn: GX.CA,
|
||||
texGenMatrix: GX.TexGenMatrix
|
||||
) {
|
||||
mb.setTevDirect(state.stage);
|
||||
mb.setTevSwapMode(state.stage, SWAP_TABLES[0], SWAP_TABLES[0]);
|
||||
mb.setTexCoordGen(state.texCoord, GX.TexGenType.MTX2x4, state.texGenSrc, GX.TexGenMatrix.TEXMTX1);
|
||||
mb.setTexCoordGen(state.texCoord, GX.TexGenType.MTX2x4, state.texGenSrc, texGenMatrix);
|
||||
mb.setTevOrder(state.stage, state.texCoord, state.texMap, GX.RasColorChannelID.COLOR0A0);
|
||||
|
||||
mb.setTevColorIn(state.stage, GX.CC.ZERO, GX.CC.TEXC, colorIn, GX.CC.ZERO);
|
||||
@@ -162,10 +168,16 @@ function buildWorldSpecularLayer(mb: GXMaterialBuilder, state: BuildState, color
|
||||
state.texGenSrc++;
|
||||
}
|
||||
|
||||
function buildAlphaBlendLayer(mb: GXMaterialBuilder, state: BuildState, colorIn: GX.CC, alphaIn: GX.CA) {
|
||||
function buildAlphaBlendLayer(
|
||||
mb: GXMaterialBuilder,
|
||||
state: BuildState,
|
||||
colorIn: GX.CC,
|
||||
alphaIn: GX.CA,
|
||||
texGenMatrix: GX.TexGenMatrix
|
||||
) {
|
||||
mb.setTevDirect(state.stage);
|
||||
mb.setTevSwapMode(state.stage, SWAP_TABLES[0], SWAP_TABLES[1]);
|
||||
mb.setTexCoordGen(state.texCoord, GX.TexGenType.MTX2x4, state.texGenSrc, GX.TexGenMatrix.TEXMTX1);
|
||||
mb.setTexCoordGen(state.texCoord, GX.TexGenType.MTX2x4, state.texGenSrc, texGenMatrix);
|
||||
mb.setTevOrder(state.stage, state.texCoord, state.texMap, GX.RasColorChannelID.COLOR0A0);
|
||||
|
||||
mb.setTevColorIn(state.stage, GX.CC.ZERO, GX.CC.ZERO, GX.CC.ZERO, colorIn);
|
||||
@@ -397,15 +409,19 @@ export class MaterialInst {
|
||||
} else {
|
||||
for (let layerIdx = 0; layerIdx < this.tevLayers.length; layerIdx++) {
|
||||
const layer = this.tevLayers[layerIdx];
|
||||
const texGenMatrix =
|
||||
layer.tevLayerData.flags & Gma.TevLayerFlags.EnableUvScroll
|
||||
? GX.TexGenMatrix.TEXMTX1
|
||||
: GX.TexGenMatrix.TEXMTX2;
|
||||
const layerTypeFlags =
|
||||
layer.tevLayerData.flags &
|
||||
(Gma.TevLayerFlags.TypeAlphaBlend |
|
||||
Gma.TevLayerFlags.TypeViewSpecular |
|
||||
Gma.TevLayerFlags.TypeWorldSpecular);
|
||||
if (layerTypeFlags === 0) {
|
||||
buildDiffuseLayer(mb, buildState, colorIn, alphaIn);
|
||||
buildDiffuseLayer(mb, buildState, colorIn, alphaIn, texGenMatrix);
|
||||
} else if (layerTypeFlags & Gma.TevLayerFlags.TypeAlphaBlend) {
|
||||
buildAlphaBlendLayer(mb, buildState, colorIn, alphaIn);
|
||||
buildAlphaBlendLayer(mb, buildState, colorIn, alphaIn, texGenMatrix);
|
||||
} else if (layerTypeFlags & Gma.TevLayerFlags.TypeViewSpecular) {
|
||||
buildViewSpecularLayer(mb, buildState, colorIn, alphaIn);
|
||||
} else if (layerTypeFlags & Gma.TevLayerFlags.TypeWorldSpecular) {
|
||||
@@ -497,6 +513,7 @@ export class MaterialInst {
|
||||
materialColor.a *= colorMul.a;
|
||||
|
||||
mat4.copy(materialParams.u_TexMtx[1], renderParams.texMtx);
|
||||
mat4.identity(materialParams.u_TexMtx[2]);
|
||||
|
||||
ambientColor.r *= colorMul.r;
|
||||
ambientColor.g *= colorMul.g;
|
||||
|
||||
@@ -117,6 +117,11 @@ export type BgObject = {
|
||||
translucency: number;
|
||||
anim: BgAnim | null;
|
||||
flipbookAnims: FlipbookAnims | null;
|
||||
textureScroll?: TextureScroll;
|
||||
};
|
||||
|
||||
export type TextureScroll = {
|
||||
speed: vec2;
|
||||
};
|
||||
|
||||
// export type TextureScroll = {
|
||||
@@ -201,6 +206,7 @@ export type AnimGroup = {
|
||||
originRot: vec3;
|
||||
animType: AnimType;
|
||||
anim: AnimGroupAnim | null;
|
||||
textureScroll?: TextureScroll;
|
||||
// conveyorVel: vec3;
|
||||
|
||||
coliTris: ColiTri[];
|
||||
|
||||
@@ -658,6 +658,7 @@ function convertBgObject(obj: any): BgObject {
|
||||
translucency: obj.translucency ?? 0,
|
||||
anim: convertBgAnim(obj.anim),
|
||||
flipbookAnims: convertFlipbookAnims(obj.flipbooks),
|
||||
textureScroll: obj.textureScroll ? { speed: toVec2(obj.textureScroll.speed) } : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -713,6 +714,7 @@ export function convertSmb2StageDef(stage: any): Stage {
|
||||
posZKeyframes: keyframesOrEmpty(group.anim.posZKeyframes),
|
||||
}
|
||||
: null,
|
||||
textureScroll: group.textureScroll ? { speed: toVec2(group.textureScroll.speed) } : undefined,
|
||||
coliTris: group.triangles.map((tri: any) => ({
|
||||
pos: toVec3(tri.pos),
|
||||
normal: toVec3(tri.normal),
|
||||
|
||||
54
src/stage.ts
54
src/stage.ts
@@ -48,6 +48,7 @@ const STAGE_CYLINDER_SIZE = 0x1c;
|
||||
const STAGE_ANIM_GROUP_MODEL_SIZE = 0x0c;
|
||||
const STAGE_FALLOUT_BOX_SIZE = 0x20;
|
||||
const STAGE_BG_OBJECT_SIZE = 0x38;
|
||||
const STAGE2_BG_OBJECT_SIZE = 0x38;
|
||||
const STAGE_BG_ANIM_SIZE = 0x60;
|
||||
const STAGE_FLIPBOOK_SIZE = 0x10;
|
||||
const STAGE_NIGHT_WINDOW_SIZE = 0x14;
|
||||
@@ -208,6 +209,16 @@ class StageParser {
|
||||
return TEXT_DECODER.decode(this.data.subarray(offset, end));
|
||||
}
|
||||
|
||||
parseTextureScroll(offset) {
|
||||
if (offset === null) {
|
||||
return null;
|
||||
}
|
||||
if (offset < 0 || offset + 8 > this.data.length) {
|
||||
return null;
|
||||
}
|
||||
return { speed: this.readVec2(offset) };
|
||||
}
|
||||
|
||||
readStringList(offset) {
|
||||
if (offset === null) {
|
||||
return [];
|
||||
@@ -275,6 +286,9 @@ class StageParser {
|
||||
if (offset === null) {
|
||||
return null;
|
||||
}
|
||||
if (offset < 0 || offset + STAGE_BG_ANIM_SIZE > this.data.length) {
|
||||
return null;
|
||||
}
|
||||
const anim = {
|
||||
loopStartSeconds: this.readF32(offset),
|
||||
loopEndSeconds: this.readF32(offset + 0x04),
|
||||
@@ -323,6 +337,9 @@ class StageParser {
|
||||
if (offset === null) {
|
||||
return null;
|
||||
}
|
||||
if (offset < 0 || offset + STAGE_FLIPBOOK_SIZE > this.data.length) {
|
||||
return null;
|
||||
}
|
||||
const nightWindowAnimCount = this.readS32(offset);
|
||||
const nightWindowAnimsPtr = this.readPtr(offset + 0x04);
|
||||
const stormFireAnimCount = this.readS32(offset + 0x08);
|
||||
@@ -888,6 +905,40 @@ class StageParserSmb2 extends StageParser {
|
||||
return names;
|
||||
}
|
||||
|
||||
parseBgObjects(offset, count) {
|
||||
if (offset === null || count <= 0) {
|
||||
return [];
|
||||
}
|
||||
if (offset < 0 || offset >= this.data.length) {
|
||||
return [];
|
||||
}
|
||||
const maxCount = Math.floor((this.data.length - offset) / STAGE2_BG_OBJECT_SIZE);
|
||||
const safeCount = Math.min(count, maxCount);
|
||||
const objs = new Array(safeCount);
|
||||
for (let i = 0; i < safeCount; i += 1) {
|
||||
const base = offset + i * STAGE2_BG_OBJECT_SIZE;
|
||||
const namePtr = this.readPtr(base + 0x04);
|
||||
const effectHeaderPtr = this.readPtr(base + 0x34);
|
||||
const textureScroll = effectHeaderPtr !== null
|
||||
? this.parseTextureScroll(this.readPtr(effectHeaderPtr + 0x10))
|
||||
: null;
|
||||
objs[i] = {
|
||||
flags: this.readU32(base),
|
||||
name: namePtr ? this.readString(namePtr) : '',
|
||||
pos: this.readVec3(base + 0x0c),
|
||||
rotX: this.readS16(base + 0x18),
|
||||
rotY: this.readS16(base + 0x1a),
|
||||
rotZ: this.readS16(base + 0x1c),
|
||||
scale: this.readVec3(base + 0x20),
|
||||
translucency: this.readF32(base + 0x2c),
|
||||
anim: this.parseBgAnim(this.readPtr(base + 0x30)),
|
||||
flipbooks: this.parseFlipbooks(this.readPtr(base + 0x34)),
|
||||
textureScroll,
|
||||
};
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
parseAnimGroup(offset) {
|
||||
const origin = this.readVec3(offset);
|
||||
const initRot = this.readS16Vec(offset + 0x0c);
|
||||
@@ -936,7 +987,8 @@ class StageParserSmb2 extends StageParser {
|
||||
const initialPlaybackState = this.readS32(offset + 0xcc);
|
||||
const loopStartSeconds = this.readF32(offset + 0xd0);
|
||||
const loopEndSeconds = this.readF32(offset + 0xd4);
|
||||
const textureScroll = this.readPtr(offset + 0xd8);
|
||||
const textureScrollPtr = this.readPtr(offset + 0xd8);
|
||||
const textureScroll = this.parseTextureScroll(textureScrollPtr);
|
||||
|
||||
const gridData = this.parseGridCellTris(gridCellTrisPtr, gridCellCountX, gridCellCountZ);
|
||||
const triangleCount = gridData.maxIndex + 1;
|
||||
|
||||
Reference in New Issue
Block a user