reze-engine 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -50
- package/dist/camera-animation.d.ts +20 -0
- package/dist/camera-animation.d.ts.map +1 -0
- package/dist/camera-animation.js +66 -0
- package/dist/camera.d.ts +12 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +44 -1
- package/dist/engine.d.ts +34 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +107 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/math.d.ts +1 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +17 -0
- package/dist/model.d.ts +9 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +21 -3
- package/dist/vmd-loader.d.ts +16 -0
- package/dist/vmd-loader.d.ts.map +1 -1
- package/dist/vmd-loader.js +41 -0
- package/package.json +1 -1
- package/src/camera-animation.ts +93 -0
- package/src/camera.ts +45 -1
- package/src/engine.ts +118 -3
- package/src/index.ts +3 -0
- package/src/math.ts +17 -0
- package/src/model.ts +25 -3
- package/src/vmd-loader.ts +54 -0
package/src/math.ts
CHANGED
|
@@ -535,6 +535,23 @@ export class Mat4 {
|
|
|
535
535
|
out[14] = pz
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
+
// Position + rotation + UNIFORM scale. Scales the rotation basis (columns 0–2) by s;
|
|
539
|
+
// uniform scale keeps normals correct after the shader's normalize() (no inverse-transpose).
|
|
540
|
+
static fromPositionRotationScaleInto(
|
|
541
|
+
px: number, py: number, pz: number,
|
|
542
|
+
qx: number, qy: number, qz: number, qw: number,
|
|
543
|
+
s: number,
|
|
544
|
+
out: Float32Array
|
|
545
|
+
): void {
|
|
546
|
+
Mat4.fromQuatInto(qx, qy, qz, qw, out, 0)
|
|
547
|
+
out[0] *= s; out[1] *= s; out[2] *= s
|
|
548
|
+
out[4] *= s; out[5] *= s; out[6] *= s
|
|
549
|
+
out[8] *= s; out[9] *= s; out[10] *= s
|
|
550
|
+
out[12] = px
|
|
551
|
+
out[13] = py
|
|
552
|
+
out[14] = pz
|
|
553
|
+
}
|
|
554
|
+
|
|
538
555
|
// In-place 4x4 inverse into out array. Returns true on success, false if singular (out untouched).
|
|
539
556
|
static inverseInto(m: Float32Array, out: Float32Array): boolean {
|
|
540
557
|
const a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3]
|
package/src/model.ts
CHANGED
|
@@ -199,6 +199,17 @@ export class Model {
|
|
|
199
199
|
return this._rotation
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/** Uniform scale (default 1). Used to fit a stage.pmx to the character. */
|
|
203
|
+
get scale(): number {
|
|
204
|
+
return this._scale
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Whether this model renders. Hidden models skip the main, shadow, and pick passes;
|
|
208
|
+
* physics keeps running so they resume consistently. */
|
|
209
|
+
get visible(): boolean {
|
|
210
|
+
return this._visible
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
setPosition(position: Vec3): void {
|
|
203
214
|
this._position.set(position)
|
|
204
215
|
this.rootMatrixDirty = true
|
|
@@ -209,6 +220,15 @@ export class Model {
|
|
|
209
220
|
this.rootMatrixDirty = true
|
|
210
221
|
}
|
|
211
222
|
|
|
223
|
+
setScale(scale: number): void {
|
|
224
|
+
this._scale = scale
|
|
225
|
+
this.rootMatrixDirty = true
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setVisible(visible: boolean): void {
|
|
229
|
+
this._visible = visible
|
|
230
|
+
}
|
|
231
|
+
|
|
212
232
|
private vertexData: Float32Array<ArrayBuffer>
|
|
213
233
|
private baseVertexData: Float32Array<ArrayBuffer> // Original vertex data before morphing
|
|
214
234
|
private vertexCount: number
|
|
@@ -254,6 +274,8 @@ export class Model {
|
|
|
254
274
|
// Skip-when-identity flag avoids the extra mat mul per bone when unused.
|
|
255
275
|
private _position: Vec3 = Vec3.zeros()
|
|
256
276
|
private _rotation: Quat = Quat.identity()
|
|
277
|
+
private _scale: number = 1
|
|
278
|
+
private _visible: boolean = true
|
|
257
279
|
private rootMatrixValues: Float32Array = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1])
|
|
258
280
|
private rootMatrixDirty: boolean = false
|
|
259
281
|
private rootIsIdentity: boolean = true
|
|
@@ -851,11 +873,11 @@ export class Model {
|
|
|
851
873
|
|
|
852
874
|
// Rebuild root matrix + cache identity-shortcut flag only when pos/rot changed.
|
|
853
875
|
if (this.rootMatrixDirty) {
|
|
854
|
-
const p = this._position, r = this._rotation
|
|
855
|
-
Mat4.
|
|
876
|
+
const p = this._position, r = this._rotation, s = this._scale
|
|
877
|
+
Mat4.fromPositionRotationScaleInto(p.x, p.y, p.z, r.x, r.y, r.z, r.w, s, this.rootMatrixValues)
|
|
856
878
|
this.rootIsIdentity =
|
|
857
879
|
p.x === 0 && p.y === 0 && p.z === 0 &&
|
|
858
|
-
r.x === 0 && r.y === 0 && r.z === 0 && r.w === 1
|
|
880
|
+
r.x === 0 && r.y === 0 && r.z === 0 && r.w === 1 && s === 1
|
|
859
881
|
this.rootMatrixDirty = false
|
|
860
882
|
}
|
|
861
883
|
|
package/src/vmd-loader.ts
CHANGED
|
@@ -20,6 +20,18 @@ export interface VMDKeyFrame {
|
|
|
20
20
|
morphFrames: MorphFrame[]
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** One MMD camera keyframe. The camera looks at `target` from `distance` away, oriented by
|
|
24
|
+
* `rotation` (euler radians), with `fov` degrees. `interpolation` is 24 bytes: 6 channels
|
|
25
|
+
* (posX, posY, posZ, rotation, distance, fov) × 4 bezier bytes (x1, x2, y1, y2). */
|
|
26
|
+
export interface CameraKeyframe {
|
|
27
|
+
frame: number
|
|
28
|
+
distance: number
|
|
29
|
+
target: Vec3
|
|
30
|
+
rotation: Vec3 // euler radians
|
|
31
|
+
fov: number // degrees
|
|
32
|
+
interpolation: Uint8Array // 24 bytes
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
export class VMDLoader {
|
|
24
36
|
private view: DataView
|
|
25
37
|
private offset = 0
|
|
@@ -46,6 +58,48 @@ export class VMDLoader {
|
|
|
46
58
|
return loader.parse()
|
|
47
59
|
}
|
|
48
60
|
|
|
61
|
+
/** Parse only the camera track (a dedicated camera VMD, or the camera block of any VMD).
|
|
62
|
+
* Returns [] if the file has no camera block (motion-only VMDs end after the morph block). */
|
|
63
|
+
static async loadCamera(url: string): Promise<CameraKeyframe[]> {
|
|
64
|
+
const loader = new VMDLoader(await fetch(url).then((r) => r.arrayBuffer()))
|
|
65
|
+
return loader.parseCamera()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static loadCameraFromBuffer(buffer: ArrayBuffer): CameraKeyframe[] {
|
|
69
|
+
return new VMDLoader(buffer).parseCamera()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Seek past the bone + morph blocks (fixed record sizes) to the camera block, then read it.
|
|
73
|
+
// bone frame = 111 B (15 name + 4 frame + 12 pos + 16 rot + 64 interp); morph = 23 B
|
|
74
|
+
// (15 name + 4 frame + 4 weight); camera = 61 B.
|
|
75
|
+
private parseCamera(): CameraKeyframe[] {
|
|
76
|
+
this.offset = 0
|
|
77
|
+
const header = this.getString(30)
|
|
78
|
+
if (!header.startsWith("Vocaloid Motion Data")) throw new Error("Invalid VMD file header")
|
|
79
|
+
this.skip(20)
|
|
80
|
+
const boneCount = this.getUint32()
|
|
81
|
+
this.skip(boneCount * 111)
|
|
82
|
+
const morphCount = this.getUint32()
|
|
83
|
+
this.skip(morphCount * 23)
|
|
84
|
+
if (this.offset + 4 > this.view.buffer.byteLength) return [] // no camera block
|
|
85
|
+
const cameraCount = this.getUint32()
|
|
86
|
+
|
|
87
|
+
const frames: CameraKeyframe[] = []
|
|
88
|
+
for (let i = 0; i < cameraCount; i++) {
|
|
89
|
+
const frame = this.getUint32()
|
|
90
|
+
const distance = this.getFloat32()
|
|
91
|
+
const target = new Vec3(this.getFloat32(), this.getFloat32(), this.getFloat32())
|
|
92
|
+
const rotation = new Vec3(this.getFloat32(), this.getFloat32(), this.getFloat32())
|
|
93
|
+
const interpolation = new Uint8Array(24)
|
|
94
|
+
for (let j = 0; j < 24; j++) interpolation[j] = this.getUint8()
|
|
95
|
+
const fov = this.getUint32()
|
|
96
|
+
this.skip(1) // perspective flag (0 = perspective) — unused
|
|
97
|
+
frames.push({ frame, distance, target, rotation, fov, interpolation })
|
|
98
|
+
}
|
|
99
|
+
frames.sort((a, b) => a.frame - b.frame)
|
|
100
|
+
return frames
|
|
101
|
+
}
|
|
102
|
+
|
|
49
103
|
private parse(): VMDKeyFrame[] {
|
|
50
104
|
// Read header (30 bytes)
|
|
51
105
|
const header = this.getString(30)
|