reze-engine 0.6.4 → 0.6.6

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/src/ik-solver.ts CHANGED
@@ -7,6 +7,9 @@
7
7
  import { Mat4, Quat, Vec3 } from "./math"
8
8
  import { Bone, IKLink, IKSolver, IKChainInfo } from "./model"
9
9
 
10
+ // Callback type for updating world matrix (provided by model to handle append transformations)
11
+ export type UpdateWorldMatrixFn = (boneIndex: number, applyIK: boolean) => void
12
+
10
13
  const enum InternalEulerRotationOrder {
11
14
  YXZ = 0,
12
15
  ZYX = 1,
@@ -89,10 +92,11 @@ export class IKSolverSystem {
89
92
  localRotations: Quat[],
90
93
  localTranslations: Vec3[],
91
94
  worldMatrices: Mat4[],
92
- ikChainInfo: IKChainInfo[]
95
+ ikChainInfo: IKChainInfo[],
96
+ updateWorldMatrix?: UpdateWorldMatrixFn
93
97
  ): void {
94
98
  for (const solver of ikSolvers) {
95
- this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
99
+ this.solveIK(solver, bones, localRotations, localTranslations, worldMatrices, ikChainInfo, updateWorldMatrix)
96
100
  }
97
101
  }
98
102
 
@@ -102,7 +106,8 @@ export class IKSolverSystem {
102
106
  localRotations: Quat[],
103
107
  localTranslations: Vec3[],
104
108
  worldMatrices: Mat4[],
105
- ikChainInfo: IKChainInfo[]
109
+ ikChainInfo: IKChainInfo[],
110
+ updateWorldMatrix?: UpdateWorldMatrixFn
106
111
  ): void {
107
112
  if (solver.links.length === 0) return
108
113
 
@@ -126,10 +131,17 @@ export class IKSolverSystem {
126
131
  }
127
132
 
128
133
  // Update chain bones and target bone world matrices (initial state, no IK yet)
129
- for (let i = chains.length - 1; i >= 0; i--) {
130
- this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
134
+ if (updateWorldMatrix) {
135
+ for (let i = chains.length - 1; i >= 0; i--) {
136
+ updateWorldMatrix(chains[i].boneIndex, false)
137
+ }
138
+ updateWorldMatrix(targetBoneIndex, false)
139
+ } else {
140
+ for (let i = chains.length - 1; i >= 0; i--) {
141
+ this.updateWorldMatrix(chains[i].boneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
142
+ }
143
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
131
144
  }
132
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
133
145
 
134
146
  if (this.getDistance(ikBoneIndex, targetBoneIndex, worldMatrices) < this.EPSILON) return
135
147
 
@@ -152,7 +164,8 @@ export class IKSolverSystem {
152
164
  localTranslations,
153
165
  worldMatrices,
154
166
  ikChainInfo,
155
- i < halfIteration
167
+ i < halfIteration,
168
+ updateWorldMatrix
156
169
  )
157
170
  }
158
171
  }
@@ -182,7 +195,8 @@ export class IKSolverSystem {
182
195
  localTranslations: Vec3[],
183
196
  worldMatrices: Mat4[],
184
197
  ikChainInfo: IKChainInfo[],
185
- useAxis: boolean
198
+ useAxis: boolean,
199
+ updateWorldMatrix?: UpdateWorldMatrixFn
186
200
  ): void {
187
201
  const chainBoneIndex = chain.boneIndex
188
202
  const chainPosition = this.getWorldTranslation(chainBoneIndex, worldMatrices)
@@ -254,13 +268,21 @@ export class IKSolverSystem {
254
268
  }
255
269
  }
256
270
 
257
- // Update world matrices for affected bones (using IK-modified rotations)
258
- for (let i = chainIndex; i >= 0; i--) {
259
- const link = solver.links[i]
260
- this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
271
+ // Update world matrices for affected bones (using callback - handles append correctly)
272
+ if (updateWorldMatrix) {
273
+ for (let i = chainIndex; i >= 0; i--) {
274
+ const link = solver.links[i]
275
+ updateWorldMatrix(link.boneIndex, true) // applyIK = true
276
+ }
277
+ updateWorldMatrix(targetBoneIndex, false)
278
+ } else {
279
+ for (let i = chainIndex; i >= 0; i--) {
280
+ const link = solver.links[i]
281
+ this.updateWorldMatrix(link.boneIndex, bones, localRotations, localTranslations, worldMatrices, ikChainInfo)
282
+ }
283
+ this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
284
+ this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
261
285
  }
262
- this.updateWorldMatrix(ikBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
263
- this.updateWorldMatrix(targetBoneIndex, bones, localRotations, localTranslations, worldMatrices, undefined)
264
286
  }
265
287
 
266
288
  private static limitAngle(angle: number, min: number, max: number, useAxis: boolean): number {
@@ -410,3 +432,4 @@ export class IKSolverSystem {
410
432
  }
411
433
  }
412
434
  }
435
+
package/src/model.ts CHANGED
@@ -531,62 +531,14 @@ export class Model {
531
531
  const now = this.tweenTimeMs
532
532
  const dur = durationMs && durationMs > 0 ? durationMs : 0
533
533
 
534
- // Compute bind pose world positions for all bones
535
- const skeleton = this.skeleton
536
- const computeBindPoseWorldPosition = (idx: number): Vec3 => {
537
- const bone = skeleton.bones[idx]
538
- const bindPos = new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
539
- if (bone.parentIndex >= 0 && bone.parentIndex < skeleton.bones.length) {
540
- const parentWorldPos = computeBindPoseWorldPosition(bone.parentIndex)
541
- return parentWorldPos.add(bindPos)
542
- } else {
543
- return bindPos
544
- }
545
- }
546
-
547
534
  for (const [name, vmdRelativeTranslation] of Object.entries(boneTranslations)) {
548
535
  const idx = this.runtimeSkeleton.nameIndex[name] ?? -1
549
536
  if (idx < 0 || idx >= this.skeleton.bones.length) continue
550
537
 
551
- const bone = this.skeleton.bones[idx]
552
538
  const translations = this.runtimeSkeleton.localTranslations
553
- const localRot = this.runtimeSkeleton.localRotations
554
-
555
- // VMD translation is relative to bind pose world position
556
- // targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
557
- const bindPoseWorldPos = computeBindPoseWorldPosition(idx)
558
- const targetWorldPos = bindPoseWorldPos.add(vmdRelativeTranslation)
559
-
560
- // Convert target world position to local translation
561
- // We need parent's bind pose world position to transform to parent space
562
- let parentBindPoseWorldPos: Vec3
563
- if (bone.parentIndex >= 0) {
564
- parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex)
565
- } else {
566
- parentBindPoseWorldPos = Vec3.zeros()
567
- }
568
-
569
- // Transform target world position to parent's local space
570
- // In bind pose, parent's world matrix is just a translation
571
- const parentSpacePos = targetWorldPos.subtract(parentBindPoseWorldPos)
572
-
573
- // Subtract bindTranslation to get position after bind translation
574
- const afterBindTranslation = parentSpacePos.subtract(
575
- new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
576
- )
577
-
578
- // Apply inverse rotation to get local translation
579
- const localRotation = localRot[idx]
580
- // Clone to avoid mutating, then conjugate and normalize
581
- const invRotation = localRotation.clone().conjugate().normalize()
582
- const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w)
583
- const rm = rotationMat.values
584
- const localTranslation = new Vec3(
585
- rm[0] * afterBindTranslation.x + rm[4] * afterBindTranslation.y + rm[8] * afterBindTranslation.z,
586
- rm[1] * afterBindTranslation.x + rm[5] * afterBindTranslation.y + rm[9] * afterBindTranslation.z,
587
- rm[2] * afterBindTranslation.x + rm[6] * afterBindTranslation.y + rm[10] * afterBindTranslation.z
588
- )
589
539
 
540
+ // Convert VMD relative translation to local translation
541
+ const localTranslation = this.convertVMDTranslationToLocal(idx, vmdRelativeTranslation)
590
542
  const [tx, ty, tz] = [localTranslation.x, localTranslation.y, localTranslation.z]
591
543
 
592
544
  if (dur === 0) {
@@ -626,6 +578,73 @@ export class Model {
626
578
  }
627
579
  }
628
580
 
581
+ /**
582
+ * Convert VMD-style relative translation (relative to bind pose world position) to local translation
583
+ * This helper is used by both moveBones and getPoseAtTime to ensure consistent translation handling
584
+ * @param boneIdx - Bone index
585
+ * @param vmdRelativeTranslation - VMD relative translation
586
+ * @param rotation - Optional rotation to use for conversion. If not provided, uses current localRotation.
587
+ * Use animation rotation (from frame) to avoid conflicts when IK modifies rotation.
588
+ */
589
+ private convertVMDTranslationToLocal(boneIdx: number, vmdRelativeTranslation: Vec3, rotation?: Quat): Vec3 {
590
+ const skeleton = this.skeleton
591
+ const bones = skeleton.bones
592
+ const localRot = this.runtimeSkeleton.localRotations
593
+
594
+ // Compute bind pose world positions for all bones
595
+ const computeBindPoseWorldPosition = (idx: number): Vec3 => {
596
+ const bone = bones[idx]
597
+ const bindPos = new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
598
+ if (bone.parentIndex >= 0 && bone.parentIndex < bones.length) {
599
+ const parentWorldPos = computeBindPoseWorldPosition(bone.parentIndex)
600
+ return parentWorldPos.add(bindPos)
601
+ } else {
602
+ return bindPos
603
+ }
604
+ }
605
+
606
+ const bone = bones[boneIdx]
607
+
608
+ // VMD translation is relative to bind pose world position
609
+ // targetWorldPos = bindPoseWorldPos + vmdRelativeTranslation
610
+ const bindPoseWorldPos = computeBindPoseWorldPosition(boneIdx)
611
+ const targetWorldPos = bindPoseWorldPos.add(vmdRelativeTranslation)
612
+
613
+ // Convert target world position to local translation
614
+ // We need parent's bind pose world position to transform to parent space
615
+ let parentBindPoseWorldPos: Vec3
616
+ if (bone.parentIndex >= 0) {
617
+ parentBindPoseWorldPos = computeBindPoseWorldPosition(bone.parentIndex)
618
+ } else {
619
+ parentBindPoseWorldPos = Vec3.zeros()
620
+ }
621
+
622
+ // Transform target world position to parent's local space
623
+ // In bind pose, parent's world matrix is just a translation
624
+ const parentSpacePos = targetWorldPos.subtract(parentBindPoseWorldPos)
625
+
626
+ // Subtract bindTranslation to get position after bind translation
627
+ const afterBindTranslation = parentSpacePos.subtract(
628
+ new Vec3(bone.bindTranslation[0], bone.bindTranslation[1], bone.bindTranslation[2])
629
+ )
630
+
631
+ // Apply inverse rotation to get local translation
632
+ // Use provided rotation (animation rotation) or fall back to current localRotation
633
+ // Using animation rotation prevents conflicts when IK modifies the rotation
634
+ const localRotation = rotation ?? localRot[boneIdx]
635
+ // Clone to avoid mutating, then conjugate and normalize
636
+ const invRotation = localRotation.clone().conjugate().normalize()
637
+ const rotationMat = Mat4.fromQuat(invRotation.x, invRotation.y, invRotation.z, invRotation.w)
638
+ const rm = rotationMat.values
639
+ const localTranslation = new Vec3(
640
+ rm[0] * afterBindTranslation.x + rm[4] * afterBindTranslation.y + rm[8] * afterBindTranslation.z,
641
+ rm[1] * afterBindTranslation.x + rm[5] * afterBindTranslation.y + rm[9] * afterBindTranslation.z,
642
+ rm[2] * afterBindTranslation.x + rm[6] * afterBindTranslation.y + rm[10] * afterBindTranslation.z
643
+ )
644
+
645
+ return localTranslation
646
+ }
647
+
629
648
  getBoneWorldMatrices(): Float32Array {
630
649
  // Convert Mat4[] to Float32Array for WebGPU compatibility
631
650
  const boneCount = this.skeleton.bones.length
@@ -1003,8 +1022,13 @@ export class Model {
1003
1022
 
1004
1023
  if (!frameB) {
1005
1024
  // No interpolation needed - direct assignment
1006
- localRot.set(frameA.rotation)
1007
- localTrans.set(frameA.translation)
1025
+ // Use animation frame's rotation for translation conversion to ensure consistency
1026
+ // This prevents conflicts when IK later modifies the rotation
1027
+ const frameRotation = frameA.rotation
1028
+ localRot.set(frameRotation)
1029
+ // Convert VMD relative translation to local translation using animation rotation
1030
+ const localTranslation = this.convertVMDTranslationToLocal(boneIdx, frameA.translation, frameRotation)
1031
+ localTrans.set(localTranslation)
1008
1032
  } else {
1009
1033
  const timeA = keyFrames[idx].time
1010
1034
  const timeB = keyFrames[idx + 1].time
@@ -1024,7 +1048,7 @@ export class Model {
1024
1048
  // Use Quat.slerp to interpolate rotation
1025
1049
  const rotation = Quat.slerp(frameA.rotation, frameB.rotation, rotT)
1026
1050
 
1027
- // Interpolate translation using bezier for each component
1051
+ // Interpolate VMD translation using bezier for each component
1028
1052
  // Inline getWeight to avoid function call overhead
1029
1053
  const getWeight = (offset: number) =>
1030
1054
  bezierInterpolate(
@@ -1039,11 +1063,21 @@ export class Model {
1039
1063
  const tyWeight = getWeight(16)
1040
1064
  const tzWeight = getWeight(32)
1041
1065
 
1066
+ // Interpolate VMD relative translations (relative to bind pose world position)
1067
+ const interpolatedVMDTranslation = new Vec3(
1068
+ frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight,
1069
+ frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight,
1070
+ frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight
1071
+ )
1072
+
1073
+ // Convert interpolated VMD translation to local translation using animation rotation
1074
+ // This ensures translation is computed for the animation rotation, not the runtime rotation
1075
+ // that will be modified by IK, preventing conflicts
1076
+ const localTranslation = this.convertVMDTranslationToLocal(boneIdx, interpolatedVMDTranslation, rotation)
1077
+
1042
1078
  // Direct property writes to avoid object allocation
1043
1079
  localRot.set(rotation)
1044
- localTrans.x = frameA.translation.x + (frameB.translation.x - frameA.translation.x) * txWeight
1045
- localTrans.y = frameA.translation.y + (frameB.translation.y - frameA.translation.y) * tyWeight
1046
- localTrans.z = frameA.translation.z + (frameB.translation.z - frameA.translation.z) * tzWeight
1080
+ localTrans.set(localTranslation)
1047
1081
  }
1048
1082
  }
1049
1083
 
@@ -1138,14 +1172,136 @@ export class Model {
1138
1172
  const ikChainInfo = this.runtimeSkeleton.ikChainInfo
1139
1173
  if (!ikChainInfo) return
1140
1174
 
1141
- IKSolverSystem.solve(
1142
- ikSolvers,
1143
- this.skeleton.bones,
1144
- this.runtimeSkeleton.localRotations,
1145
- this.runtimeSkeleton.localTranslations,
1146
- this.runtimeSkeleton.worldMatrices,
1147
- ikChainInfo
1148
- )
1175
+ // Solve each IK solver sequentially, ensuring consistent state between solvers
1176
+ for (const solver of ikSolvers) {
1177
+ // Recompute ALL world matrices before each solver starts
1178
+ // This ensures each solver sees the effects of previous solvers on localRotations
1179
+ this.computeWorldMatrices()
1180
+
1181
+ // Clear computed set for this solver's pass
1182
+ this.ikComputedSet.clear()
1183
+
1184
+ // Solve this IK chain
1185
+ // Pass callback that uses model's world matrix computation (handles append correctly)
1186
+ IKSolverSystem.solve(
1187
+ [solver], // Solve one at a time
1188
+ this.skeleton.bones,
1189
+ this.runtimeSkeleton.localRotations,
1190
+ this.runtimeSkeleton.localTranslations,
1191
+ this.runtimeSkeleton.worldMatrices,
1192
+ ikChainInfo,
1193
+ (boneIndex, applyIK) => {
1194
+ // Clear computed set for each bone update to allow recomputation in same iteration
1195
+ this.ikComputedSet.delete(boneIndex)
1196
+ this.computeSingleBoneWorldMatrix(boneIndex, applyIK)
1197
+ }
1198
+ )
1199
+ }
1200
+ }
1201
+
1202
+ // Cached set to track which bones are being computed in current IK pass (to avoid infinite recursion)
1203
+ private ikComputedSet: Set<number> = new Set()
1204
+
1205
+ // Add this new method to compute a single bone's world matrix
1206
+ // Recursively ensures parents are computed first to avoid using stale parent matrices
1207
+ private computeSingleBoneWorldMatrix(boneIndex: number, applyIK: boolean): void {
1208
+ const bones = this.skeleton.bones
1209
+ const localRot = this.runtimeSkeleton.localRotations
1210
+ const localTrans = this.runtimeSkeleton.localTranslations
1211
+ const worldMats = this.runtimeSkeleton.worldMatrices
1212
+ const ikChainInfo = this.runtimeSkeleton.ikChainInfo
1213
+
1214
+ const b = bones[boneIndex]
1215
+
1216
+ // Prevent infinite recursion: if this bone is already being computed in this call chain, skip
1217
+ if (this.ikComputedSet.has(boneIndex)) {
1218
+ return
1219
+ }
1220
+
1221
+ // Mark this bone as being computed to prevent infinite recursion
1222
+ this.ikComputedSet.add(boneIndex)
1223
+
1224
+ // Recursively compute parent first if it exists (ensures parent matrix is up-to-date)
1225
+ if (b.parentIndex >= 0) {
1226
+ this.computeSingleBoneWorldMatrix(b.parentIndex, applyIK)
1227
+ }
1228
+
1229
+ // Get base rotation
1230
+ let boneRot = localRot[boneIndex]
1231
+
1232
+ // Apply IK rotation if requested
1233
+ if (applyIK && ikChainInfo) {
1234
+ const chainInfo = ikChainInfo[boneIndex]
1235
+ if (chainInfo?.ikRotation) {
1236
+ boneRot = chainInfo.ikRotation.multiply(boneRot).normalize()
1237
+ }
1238
+ }
1239
+
1240
+ let rotateM = Mat4.fromQuat(boneRot.x, boneRot.y, boneRot.z, boneRot.w)
1241
+ let addLocalTx = 0, addLocalTy = 0, addLocalTz = 0
1242
+
1243
+ // Handle append transformations (same logic as computeWorldMatrices)
1244
+ const appendParentIdx = b.appendParentIndex
1245
+ const hasAppend = b.appendRotate &&
1246
+ appendParentIdx !== undefined &&
1247
+ appendParentIdx >= 0 &&
1248
+ appendParentIdx < bones.length
1249
+
1250
+ if (hasAppend) {
1251
+ const ratio = b.appendRatio === undefined ? 1 : Math.max(-1, Math.min(1, b.appendRatio))
1252
+ const hasRatio = Math.abs(ratio) > 1e-6
1253
+
1254
+ if (hasRatio) {
1255
+ if (b.appendRotate) {
1256
+ // Get append parent's rotation
1257
+ // During IK solving, use only base local rotation (not IK rotations) to avoid
1258
+ // conflicts with IK rotations that are still being computed incrementally
1259
+ // IK rotations will be applied to localRotations after IK solving completes
1260
+ if (appendParentIdx >= 0) {
1261
+ // Compute append parent's world matrix for dependency order, but use base rotation for append
1262
+ this.computeSingleBoneWorldMatrix(appendParentIdx, applyIK)
1263
+ }
1264
+
1265
+ // Use append parent's base local rotation only (IK rotations are applied after solving)
1266
+ let appendRot = localRot[appendParentIdx]
1267
+
1268
+ let ax = appendRot.x, ay = appendRot.y, az = appendRot.z
1269
+ const aw = appendRot.w
1270
+ const absRatio = ratio < 0 ? -ratio : ratio
1271
+ if (ratio < 0) { ax = -ax; ay = -ay; az = -az }
1272
+
1273
+ const appendQuat = new Quat(ax, ay, az, aw)
1274
+ const result = Quat.slerp(Quat.identity(), appendQuat, absRatio)
1275
+ rotateM = Mat4.fromQuat(result.x, result.y, result.z, result.w).multiply(rotateM)
1276
+ }
1277
+
1278
+ if (b.appendMove) {
1279
+ const appendTrans = localTrans[appendParentIdx]
1280
+ addLocalTx = appendTrans.x * ratio
1281
+ addLocalTy = appendTrans.y * ratio
1282
+ addLocalTz = appendTrans.z * ratio
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ const boneTrans = localTrans[boneIndex]
1288
+ const localTx = boneTrans.x + addLocalTx
1289
+ const localTy = boneTrans.y + addLocalTy
1290
+ const localTz = boneTrans.z + addLocalTz
1291
+
1292
+ this.cachedIdentityMat1
1293
+ .setIdentity()
1294
+ .translateInPlace(b.bindTranslation[0], b.bindTranslation[1], b.bindTranslation[2])
1295
+ this.cachedIdentityMat2.setIdentity().translateInPlace(localTx, localTy, localTz)
1296
+ const localM = this.cachedIdentityMat1.multiply(rotateM).multiply(this.cachedIdentityMat2)
1297
+
1298
+ const worldMat = worldMats[boneIndex]
1299
+ if (b.parentIndex >= 0) {
1300
+ const parentMat = worldMats[b.parentIndex]
1301
+ Mat4.multiplyArrays(parentMat.values, 0, localM.values, 0, worldMat.values, 0)
1302
+ } else {
1303
+ worldMat.values.set(localM.values)
1304
+ }
1149
1305
  }
1150
1306
 
1151
1307
  private computeWorldMatrices(): void {
@@ -1238,4 +1394,4 @@ export class Model {
1238
1394
  // Process all bones (recursion handles dependencies automatically)
1239
1395
  for (let i = 0; i < boneCount; i++) computeWorld(i)
1240
1396
  }
1241
- }
1397
+ }
@@ -1,15 +0,0 @@
1
- /**
2
- * Bezier interpolation for VMD animations
3
- * Based on the reference implementation from babylon-mmd
4
- */
5
- /**
6
- * Bezier interpolation function
7
- * @param x1 First control point X (0-127, normalized to 0-1)
8
- * @param x2 Second control point X (0-127, normalized to 0-1)
9
- * @param y1 First control point Y (0-127, normalized to 0-1)
10
- * @param y2 Second control point Y (0-127, normalized to 0-1)
11
- * @param t Interpolation parameter (0-1)
12
- * @returns Interpolated value (0-1)
13
- */
14
- export declare function bezierInterpolate(x1: number, x2: number, y1: number, y2: number, t: number): number;
15
- //# sourceMappingURL=bezier-interpolate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bezier-interpolate.d.ts","sourceRoot":"","sources":["../src/bezier-interpolate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgCnG"}
@@ -1,40 +0,0 @@
1
- /**
2
- * Bezier interpolation for VMD animations
3
- * Based on the reference implementation from babylon-mmd
4
- */
5
- /**
6
- * Bezier interpolation function
7
- * @param x1 First control point X (0-127, normalized to 0-1)
8
- * @param x2 Second control point X (0-127, normalized to 0-1)
9
- * @param y1 First control point Y (0-127, normalized to 0-1)
10
- * @param y2 Second control point Y (0-127, normalized to 0-1)
11
- * @param t Interpolation parameter (0-1)
12
- * @returns Interpolated value (0-1)
13
- */
14
- export function bezierInterpolate(x1, x2, y1, y2, t) {
15
- // Clamp t to [0, 1]
16
- t = Math.max(0, Math.min(1, t));
17
- // Binary search for the t value that gives us the desired x
18
- // We're solving for t in the Bezier curve: x(t) = 3*(1-t)^2*t*x1 + 3*(1-t)*t^2*x2 + t^3
19
- let start = 0;
20
- let end = 1;
21
- let mid = 0.5;
22
- // Iterate until we find the t value that gives us the desired x
23
- for (let i = 0; i < 15; i++) {
24
- // Evaluate Bezier curve at mid point
25
- const x = 3 * (1 - mid) * (1 - mid) * mid * x1 + 3 * (1 - mid) * mid * mid * x2 + mid * mid * mid;
26
- if (Math.abs(x - t) < 0.0001) {
27
- break;
28
- }
29
- if (x < t) {
30
- start = mid;
31
- }
32
- else {
33
- end = mid;
34
- }
35
- mid = (start + end) / 2;
36
- }
37
- // Now evaluate the y value at this t
38
- const y = 3 * (1 - mid) * (1 - mid) * mid * y1 + 3 * (1 - mid) * mid * mid * y2 + mid * mid * mid;
39
- return y;
40
- }
@@ -1,143 +0,0 @@
1
- import { Quat, Vec3 } from "reze-mmd";
2
- export type EngineOptions = {
3
- ambientColor?: Vec3;
4
- bloomIntensity?: number;
5
- rimLightIntensity?: number;
6
- cameraDistance?: number;
7
- cameraTarget?: Vec3;
8
- };
9
- export interface EngineStats {
10
- fps: number;
11
- frameTime: number;
12
- }
13
- export declare class Engine {
14
- private canvas;
15
- private device;
16
- private context;
17
- private presentationFormat;
18
- private camera;
19
- private cameraUniformBuffer;
20
- private cameraMatrixData;
21
- private cameraDistance;
22
- private cameraTarget;
23
- private lightUniformBuffer;
24
- private lightData;
25
- private vertexBuffer;
26
- private indexBuffer?;
27
- private resizeObserver;
28
- private depthTexture;
29
- private modelPipeline;
30
- private eyePipeline;
31
- private hairPipelineOverEyes;
32
- private hairPipelineOverNonEyes;
33
- private hairDepthPipeline;
34
- private outlinePipeline;
35
- private hairOutlinePipeline;
36
- private mainBindGroupLayout;
37
- private outlineBindGroupLayout;
38
- private jointsBuffer;
39
- private weightsBuffer;
40
- private skinMatrixBuffer?;
41
- private multisampleTexture;
42
- private readonly sampleCount;
43
- private renderPassDescriptor;
44
- private readonly STENCIL_EYE_VALUE;
45
- private readonly BLOOM_DOWNSCALE_FACTOR;
46
- private static readonly DEFAULT_BLOOM_THRESHOLD;
47
- private static readonly DEFAULT_BLOOM_INTENSITY;
48
- private static readonly DEFAULT_RIM_LIGHT_INTENSITY;
49
- private static readonly DEFAULT_CAMERA_DISTANCE;
50
- private static readonly DEFAULT_CAMERA_TARGET;
51
- private static readonly TRANSPARENCY_EPSILON;
52
- private static readonly STATS_FPS_UPDATE_INTERVAL_MS;
53
- private static readonly STATS_FRAME_TIME_ROUNDING;
54
- private ambientColor;
55
- private sceneRenderTexture;
56
- private sceneRenderTextureView;
57
- private bloomExtractTexture;
58
- private bloomBlurTexture1;
59
- private bloomBlurTexture2;
60
- private bloomExtractPipeline;
61
- private bloomBlurPipeline;
62
- private bloomComposePipeline;
63
- private blurDirectionBuffer;
64
- private bloomIntensityBuffer;
65
- private bloomThresholdBuffer;
66
- private linearSampler;
67
- private bloomExtractBindGroup?;
68
- private bloomBlurHBindGroup?;
69
- private bloomBlurVBindGroup?;
70
- private bloomComposeBindGroup?;
71
- private bloomThreshold;
72
- private bloomIntensity;
73
- private rimLightIntensity;
74
- private currentModel;
75
- private modelDir;
76
- private physics;
77
- private materialSampler;
78
- private textureCache;
79
- private vertexBufferNeedsUpdate;
80
- private drawCalls;
81
- private lastFpsUpdate;
82
- private framesSinceLastUpdate;
83
- private lastFrameTime;
84
- private frameTimeSum;
85
- private frameTimeCount;
86
- private stats;
87
- private animationFrameId;
88
- private renderLoopCallback;
89
- private player;
90
- private hasAnimation;
91
- constructor(canvas: HTMLCanvasElement, options?: EngineOptions);
92
- init(): Promise<void>;
93
- private createRenderPipeline;
94
- private createPipelines;
95
- private createBloomPipelines;
96
- private setupBloom;
97
- private setupResize;
98
- private handleResize;
99
- private setupCamera;
100
- private setupLighting;
101
- private setAmbientColor;
102
- loadAnimation(url: string): Promise<void>;
103
- playAnimation(): void;
104
- stopAnimation(): void;
105
- pauseAnimation(): void;
106
- seekAnimation(time: number): void;
107
- getAnimationProgress(): import("./player").AnimationProgress;
108
- /**
109
- * Apply animation pose to model
110
- */
111
- private applyPose;
112
- /**
113
- * Reset bones and physics to match a given pose
114
- * @param pose The pose to apply
115
- * @param resetBonesWithoutKeyframes If true, reset bones that don't have keyframes in the pose to identity
116
- */
117
- private resetBonesAndPhysics;
118
- getStats(): EngineStats;
119
- runRenderLoop(callback?: () => void): void;
120
- stopRenderLoop(): void;
121
- dispose(): void;
122
- loadModel(path: string): Promise<void>;
123
- rotateBones(bones: string[], rotations: Quat[], durationMs?: number): void;
124
- moveBones(bones: string[], relativeTranslations: Vec3[], durationMs?: number): void;
125
- setMorphWeight(name: string, weight: number, durationMs?: number): void;
126
- private updateVertexBuffer;
127
- private setupModelBuffers;
128
- private setupMaterials;
129
- private createMaterialUniformBuffer;
130
- private createUniformBuffer;
131
- private createTextureFromPath;
132
- private renderEyes;
133
- private renderHair;
134
- render(): void;
135
- private applyBloom;
136
- private updateCameraUniforms;
137
- private updateRenderTarget;
138
- private updateModelPose;
139
- private computeSkinMatrices;
140
- private drawOutlines;
141
- private updateStats;
142
- }
143
- //# sourceMappingURL=engine_ts.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine_ts.d.ts","sourceRoot":"","sources":["../src/engine_ts.ts"],"names":[],"mappings":"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"}