Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cameras in M2Model #109

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ export * from './model/M2SkinProfile.js';
export * from './model/M2Batch.js';
export * from './model/M2Material.js';
export * from './model/M2Texture.js';
export * from './model/M2Camera.js';
export * from './model/M2Track.js';
export * from './model/M2SplineKey.js';
88 changes: 88 additions & 0 deletions src/lib/model/M2Camera.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { M2Track, M2SplineKey } from '../index.js';

class M2Camera {
#cameraId: number;
#fieldOfView: number;
#farClip: number;
#nearClip: number;
#positionTrack: M2Track<M2SplineKey<number[]>>;
#positionBase: number[];
#targetTrack: M2Track<M2SplineKey<number[]>>;
#targetBase: number[];
#rollTrack: M2Track<M2SplineKey<number[]>>;

constructor(
cameraId: number,
fieldOfView: number,
farClip: number,
nearClip: number,
positionTrack: M2Track<M2SplineKey<number[]>>,
positionBase: number[],
targetTrack: M2Track<M2SplineKey<number[]>>,
targetBase: number[],
rollTrack: M2Track<M2SplineKey<number[]>>,
) {
this.#cameraId = cameraId;
this.#fieldOfView = fieldOfView;
this.#farClip = farClip;
this.#nearClip = nearClip;
this.#positionTrack = positionTrack;
this.#positionBase = positionBase;
this.#targetTrack = targetTrack;
this.#targetBase = targetBase;
this.#rollTrack = rollTrack;
}

get cameraId() {
return this.#cameraId;
}

get fieldOfView() {
return this.#fieldOfView;
}

get farClip() {
return this.#farClip;
}

get nearClip() {
return this.#nearClip;
}

get positionTrack() {
return this.#positionTrack;
}

get positionBase() {
return this.#positionBase;
}

get targetTrack() {
return this.#targetTrack;
}

get targetBase() {
return this.#targetBase;
}

get rollTrack() {
return this.#rollTrack;
}

toObject() {
return {
cameraId: this.#cameraId,
fieldOfView: this.#fieldOfView,
farClip: this.#farClip,
nearClip: this.#nearClip,
positionTrack: this.#positionTrack.toObject(),
positionBase: this.#positionBase,
targetTrack: this.#targetTrack.toObject(),
targetBase: this.#targetBase,
rollTrack: this.#rollTrack.toObject(),
};
}
}

export default M2Camera;
export { M2Camera };
52 changes: 44 additions & 8 deletions src/lib/model/M2Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import { IoMode, IoSource, openStream } from '@wowserhq/io';
import * as io from '@wowserhq/io';
import * as m2Io from './io/m2.js';
import { M2_BONE_FLAG, M2_MODEL_FLAG } from './const.js';
import {
M2Sequence,
M2Track,
M2TextureTransform,
M2TextureWeight,
M2Color,
M2Bone,
} from './types.js';
import { M2Sequence, M2TextureTransform, M2TextureWeight, M2Color, M2Bone } from './types.js';
import M2Texture, { M2_TEXTURE_COMBINER, M2_TEXTURE_COORD } from './M2Texture.js';
import M2Material from './M2Material.js';
import { m2typedArray } from './io/common.js';
import M2Bounds from './M2Bounds.js';
import M2Camera from './M2Camera.js';
import M2Track from './M2Track.js';
import M2SplineKey from './M2SplineKey.js';

class M2Model {
#name: string;
Expand Down Expand Up @@ -44,6 +40,8 @@ class M2Model {
#sequences: M2Sequence[] = [];
#loops: Uint32Array;

#cameras: M2Camera[] = [];

get bones() {
return this.#bones;
}
Expand Down Expand Up @@ -124,6 +122,10 @@ class M2Model {
return this.#vertices;
}

get cameras() {
return this.#cameras;
}

load(source: IoSource) {
const stream = openStream(source, IoMode.Read);

Expand Down Expand Up @@ -164,6 +166,8 @@ class M2Model {

this.#sequences = data.sequences;

this.#loadCameras(data);

return this;
}

Expand All @@ -180,6 +184,38 @@ class M2Model {
);
}
}

createTrack(data: any): M2Track<M2SplineKey<number[]>> {
const splineKeys: M2SplineKey<number[]>[] = [];
for (const spline of data.sequenceKeys) {
splineKeys.push(new M2SplineKey<number[]>(spline.value, spline.inTan, spline.outTan));
}

return new M2Track<M2SplineKey<number[]>>(
data.trackType,
data.loopIndex,
data.sequenceTimes,
splineKeys,
);
}

#loadCameras(data: any) {
for (const cameraData of data.cameras) {
this.#cameras.push(
new M2Camera(
cameraData.cameraId,
cameraData.fieldOfView,
cameraData.farClip,
cameraData.nearClip,
this.createTrack(cameraData.positionTrack),
cameraData.positionBase,
this.createTrack(cameraData.targetTrack),
cameraData.targetBase,
this.createTrack(cameraData.rollTrack),
),
);
}
}
}

export default M2Model;
Expand Down
34 changes: 34 additions & 0 deletions src/lib/model/M2SplineKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class M2SplineKey<T> {
#value: T;
#inTan: T;
#outTan: T;

constructor(value: T, inTan: T, outTan: T) {
this.#value = value;
this.#inTan = inTan;
this.#outTan = outTan;
}

get value() {
return this.#value;
}

get inTan() {
return this.#inTan;
}

get outTan() {
return this.#outTan;
}

toObject() {
return {
value: this.#value,
inTan: this.#inTan,
outTan: this.#outTan,
};
}
}

export default M2SplineKey;
export { M2SplineKey };
45 changes: 45 additions & 0 deletions src/lib/model/M2Track.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
type Serializable = {
toObject();
};

class M2Track<T extends Serializable> {
#type: number;
#loopIndex: number;
#sequenceTimes: Uint32Array;
#sequenceKeys: T[];

constructor(type: number, loopIndex: number, sequenceTimes: Uint32Array, sequenceKeys: T[]) {
this.#type = type;
this.#loopIndex = loopIndex;
this.#sequenceTimes = sequenceTimes;
this.#sequenceKeys = sequenceKeys;
}

get type() {
return this.#type;
}

get loopIndex() {
return this.#loopIndex;
}

get sequenceTimes() {
return this.#sequenceTimes;
}

get sequenceKeys() {
return this.#sequenceKeys;
}

toObject() {
return {
type: this.#type,
loopIndex: this.#loopIndex,
sequenceTimes: this.#sequenceTimes,
sequenceKeys: this.#sequenceKeys.map((sequenceKey) => sequenceKey.toObject()),
};
}
}

export default M2Track;
export { M2Track };
13 changes: 10 additions & 3 deletions src/lib/model/io/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ const m2array = (type: IoType): IoType => new M2ArrayIo(type);

const m2typedArray = (type: IoType, elements = 1): IoType => new M2TypedArrayIo(type, elements);

const m2splineKey = (type: IoType): IoType =>
io.struct({
value: type,
inTan: type,
outTan: type,
});

const m2string: IoType = new M2StringIo();

const m2range: IoType = io.struct({
Expand All @@ -20,12 +27,12 @@ const m2bounds: IoType = io.struct({
radius: io.float32le,
});

const m2track = (type: IoType, elements = 1): IoType =>
const m2track = (type: IoType): IoType =>
io.struct({
trackType: io.uint16le,
loopIndex: io.uint16le,
sequenceTimes: m2array(m2typedArray(io.uint32le)),
sequenceKeys: m2array(m2typedArray(type, elements)),
sequenceKeys: m2array(type),
});

export { m2array, m2typedArray, m2string, m2range, m2bounds, m2track };
export { m2array, m2typedArray, m2string, m2range, m2bounds, m2track, m2splineKey };
39 changes: 26 additions & 13 deletions src/lib/model/io/m2.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import * as io from '@wowserhq/io';
import { IoType } from '@wowserhq/io';
import { m2array, m2typedArray, m2bounds, m2range, m2string, m2track } from './common.js';
import {
m2array,
m2typedArray,
m2bounds,
m2range,
m2string,
m2track,
m2splineKey,
} from './common.js';

const m2sequence = io.struct({
id: io.uint16le,
Expand All @@ -23,9 +31,9 @@ const m2compBone = io.struct({
parentIndex: io.int16le,
distToParent: io.uint16le,
boneNameCrc: io.uint32le,
translationTrack: m2track(io.float32le, 3),
rotationTrack: m2track(io.int16le, 4),
scaleTrack: m2track(io.float32le, 3),
translationTrack: m2track(m2typedArray(io.float32le, 3)),
rotationTrack: m2track(m2typedArray(io.int16le, 4)),
scaleTrack: m2track(m2typedArray(io.float32le, 3)),
pivot: io.typedArray(io.float32le, { size: 3 }),
});

Expand All @@ -38,8 +46,8 @@ const m2vertex = io.struct({
});

const m2color = io.struct({
colorTrack: m2track(io.float32le, 3),
alphaTrack: m2track(io.int16le),
colorTrack: m2track(m2typedArray(io.float32le, 3)),
alphaTrack: m2track(m2typedArray(io.int16le)),
});

const m2texture = io.struct({
Expand All @@ -49,13 +57,13 @@ const m2texture = io.struct({
});

const m2textureWeight = io.struct({
weightTrack: m2track(io.int16le),
weightTrack: m2track(m2typedArray(io.int16le)),
});

const m2textureTransform = io.struct({
translationTrack: m2track(io.float32le, 3),
rotationTrack: m2track(io.float32le, 4),
scalingTrack: m2track(io.float32le, 3),
translationTrack: m2track(m2typedArray(io.float32le, 3)),
rotationTrack: m2track(m2typedArray(io.float32le, 4)),
scalingTrack: m2track(m2typedArray(io.float32le, 3)),
});

const m2material = io.struct({
Expand All @@ -68,7 +76,7 @@ const m2attachment = io.struct({
boneIndex: io.uint16le,
padding: io.uint16le,
position: io.typedArray(io.float32le, { size: 3 }),
visibilityTrack: m2track(io.uint8),
visibilityTrack: m2track(m2typedArray(io.uint8)),
});

const m2event = io.struct({
Expand All @@ -80,6 +88,8 @@ const m2event = io.struct({
todo: io.typedArray(io.uint8, { size: 12 }),
});

const m2vector = io.typedArray(io.float32le, { size: 3 });

const m2light = io.struct({
lightType: io.uint16le,
boneIndex: io.uint16le,
Expand All @@ -93,8 +103,11 @@ const m2camera = io.struct({
fieldOfView: io.float32le,
farClip: io.float32le,
nearClip: io.float32le,
/* TODO tracks */
todo: io.typedArray(io.uint8, { size: 84 }),
positionTrack: m2track(m2splineKey(m2vector)),
positionBase: m2vector,
targetTrack: m2track(m2splineKey(m2vector)),
targetBase: m2vector,
rollTrack: m2track(m2splineKey(io.float32le)),
});

const m2ribbon = io.struct({
Expand Down
Loading