@babylonjs/serializers 8.25.2 → 8.26.1
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/BVH/bvhSerializer.d.ts +3 -2
- package/BVH/bvhSerializer.js +89 -86
- package/BVH/bvhSerializer.js.map +1 -1
- package/package.json +3 -3
package/BVH/bvhSerializer.d.ts
CHANGED
@@ -3,10 +3,11 @@ export declare class BVHExporter {
|
|
3
3
|
static Export(skeleton: Skeleton, animationNames?: string[], frameRate?: number): string;
|
4
4
|
private static _BuildBoneHierarchy;
|
5
5
|
private static _ExportHierarchy;
|
6
|
+
private static _IsEndSite;
|
6
7
|
private static _GetBoneOffset;
|
7
8
|
private static _ExportMotionData;
|
8
9
|
private static _CollectFrameValues;
|
9
|
-
private static
|
10
|
-
private static
|
10
|
+
private static _GetPositionAtFrameIndex;
|
11
|
+
private static _GetRotationAtFrameIndex;
|
11
12
|
private static _QuaternionToEuler;
|
12
13
|
}
|
package/BVH/bvhSerializer.js
CHANGED
@@ -1,31 +1,53 @@
|
|
1
|
+
import { AnimationRange } from "@babylonjs/core/Animations/animationRange.js";
|
1
2
|
import { Vector3, Quaternion, Matrix } from "@babylonjs/core/Maths/math.vector.js";
|
2
3
|
import { Tools } from "@babylonjs/core/Misc/tools.js";
|
3
4
|
import { Epsilon } from "@babylonjs/core/Maths/math.constants.js";
|
4
5
|
export class BVHExporter {
|
5
|
-
static Export(skeleton, animationNames = [], frameRate
|
6
|
+
static Export(skeleton, animationNames = [], frameRate) {
|
7
|
+
// Validate skeleton
|
8
|
+
if (!skeleton || skeleton.bones.length === 0) {
|
9
|
+
throw new Error("Invalid or empty skeleton provided");
|
10
|
+
}
|
6
11
|
// If no animation names provided, use all available animations
|
7
12
|
let animationsToExport = animationNames;
|
8
13
|
if (!animationNames || animationNames.length === 0) {
|
9
14
|
animationsToExport = skeleton.animations.map((anim) => anim.name);
|
10
15
|
}
|
11
|
-
//
|
12
|
-
let
|
13
|
-
|
14
|
-
|
16
|
+
// Calculate overall animation range from all specified animations
|
17
|
+
let overallRange = null;
|
18
|
+
for (const animName of animationsToExport) {
|
19
|
+
const range = skeleton.getAnimationRange(animName);
|
20
|
+
if (range) {
|
21
|
+
overallRange = overallRange ? new AnimationRange("animation-range", Math.min(overallRange.from, range.from), Math.max(overallRange.to, range.to)) : range;
|
22
|
+
}
|
15
23
|
}
|
16
|
-
if (!
|
17
|
-
// If no animation range found,
|
24
|
+
if (!overallRange) {
|
25
|
+
// If no animation range found, try to get from any animation
|
18
26
|
if (skeleton.animations.length > 0) {
|
19
|
-
|
20
|
-
animationRange = skeleton.getAnimationRange(skeleton.animations[0].name);
|
27
|
+
overallRange = skeleton.getAnimationRange(skeleton.animations[0].name);
|
21
28
|
}
|
22
|
-
if (!
|
29
|
+
if (!overallRange) {
|
23
30
|
throw new Error("No animation range found in skeleton");
|
24
31
|
}
|
25
32
|
}
|
33
|
+
// Calculate frame rate from animation data if not provided
|
34
|
+
const actualFrameRate = frameRate || 1 / (skeleton.animations[0]?.framePerSecond || 30);
|
26
35
|
// Build bone hierarchy and collect animation data
|
27
36
|
const boneHierarchy = this._BuildBoneHierarchy(skeleton, animationsToExport);
|
28
|
-
|
37
|
+
// Calculate frame count from actual animation keyframes
|
38
|
+
let frameCount = 0;
|
39
|
+
for (const boneData of boneHierarchy) {
|
40
|
+
if (boneData.positionKeys.length > 0) {
|
41
|
+
frameCount = Math.max(frameCount, boneData.positionKeys.length);
|
42
|
+
}
|
43
|
+
if (boneData.rotationKeys.length > 0) {
|
44
|
+
frameCount = Math.max(frameCount, boneData.rotationKeys.length);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
// Fallback: if no keyframes found, calculate from time range
|
48
|
+
if (frameCount === 0) {
|
49
|
+
frameCount = Math.floor((overallRange.to - overallRange.from) / actualFrameRate) + 1;
|
50
|
+
}
|
29
51
|
let exportString = "";
|
30
52
|
exportString += `HIERARCHY\n`;
|
31
53
|
// Export hierarchy recursively
|
@@ -33,10 +55,9 @@ export class BVHExporter {
|
|
33
55
|
// Export motion data
|
34
56
|
exportString += `MOTION\n`;
|
35
57
|
exportString += `Frames: ${frameCount}\n`;
|
36
|
-
exportString += `Frame Time: ${
|
37
|
-
exportString += `\n`;
|
58
|
+
exportString += `Frame Time: ${actualFrameRate.toFixed(6)}\n`;
|
38
59
|
// Export frame data
|
39
|
-
exportString += this._ExportMotionData(boneHierarchy, frameCount,
|
60
|
+
exportString += this._ExportMotionData(boneHierarchy, frameCount, 0, animationsToExport);
|
40
61
|
return exportString;
|
41
62
|
}
|
42
63
|
static _BuildBoneHierarchy(skeleton, animationNames) {
|
@@ -64,11 +85,11 @@ export class BVHExporter {
|
|
64
85
|
if (animationNames.includes(animation.name)) {
|
65
86
|
if (animation.targetProperty === "position") {
|
66
87
|
boneData.hasPositionChannels = true;
|
67
|
-
boneData.positionKeys
|
88
|
+
boneData.positionKeys.push(...animation.getKeys());
|
68
89
|
}
|
69
90
|
else if (animation.targetProperty === "rotationQuaternion") {
|
70
91
|
boneData.hasRotationChannels = true;
|
71
|
-
boneData.rotationKeys
|
92
|
+
boneData.rotationKeys.push(...animation.getKeys());
|
72
93
|
}
|
73
94
|
}
|
74
95
|
}
|
@@ -88,12 +109,11 @@ export class BVHExporter {
|
|
88
109
|
}
|
89
110
|
static _ExportHierarchy(boneData, indentLevel) {
|
90
111
|
let result = "";
|
91
|
-
// 4 spaces identation for each level
|
92
112
|
const indent = " ".repeat(indentLevel);
|
93
113
|
for (const data of boneData) {
|
94
114
|
const bone = data.bone;
|
95
|
-
|
96
|
-
if (
|
115
|
+
const isEndSite = this._IsEndSite(data);
|
116
|
+
if (isEndSite) {
|
97
117
|
result += `${indent}End Site\n`;
|
98
118
|
result += `${indent}{\n`;
|
99
119
|
const offset = this._GetBoneOffset(bone);
|
@@ -101,7 +121,7 @@ export class BVHExporter {
|
|
101
121
|
result += `${indent}}\n`;
|
102
122
|
}
|
103
123
|
else {
|
104
|
-
result += `${indent}JOINT ${bone.name}\n`;
|
124
|
+
result += `${indentLevel === 0 ? "ROOT" : `${indent}JOINT`} ${bone.name}\n`;
|
105
125
|
result += `${indent}{\n`;
|
106
126
|
const offset = this._GetBoneOffset(bone);
|
107
127
|
result += `${indent} OFFSET ${offset.x.toFixed(6)} ${offset.y.toFixed(6)} ${offset.z.toFixed(6)}\n`;
|
@@ -111,37 +131,40 @@ export class BVHExporter {
|
|
111
131
|
channels.push("Xposition", "Yposition", "Zposition");
|
112
132
|
}
|
113
133
|
if (data.hasRotationChannels) {
|
114
|
-
// BVH uses
|
134
|
+
// BVH typically uses ZXY rotation order
|
115
135
|
channels.push("Zrotation", "Xrotation", "Yrotation");
|
116
136
|
}
|
117
137
|
if (channels.length > 0) {
|
118
138
|
result += `${indent} CHANNELS ${channels.length} ${channels.join(" ")}\n`;
|
119
139
|
}
|
140
|
+
// Export children recursively
|
141
|
+
if (data.children.length > 0) {
|
142
|
+
result += this._ExportHierarchy(data.children, indentLevel + 1);
|
143
|
+
}
|
120
144
|
result += `${indent}}\n`;
|
121
145
|
}
|
122
|
-
// Export children recursively
|
123
|
-
if (data.children.length > 0) {
|
124
|
-
result += this._ExportHierarchy(data.children, indentLevel + 1);
|
125
|
-
}
|
126
146
|
}
|
127
147
|
return result;
|
128
148
|
}
|
149
|
+
static _IsEndSite(data) {
|
150
|
+
// An end site is a bone with no children (regardless of animation channels)
|
151
|
+
return data.children.length === 0;
|
152
|
+
}
|
129
153
|
static _GetBoneOffset(bone) {
|
130
|
-
//
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
return relativeOffset;
|
154
|
+
// Use the bone's rest matrix or local matrix for correct offset
|
155
|
+
try {
|
156
|
+
if (!bone.getParent()) {
|
157
|
+
// Root bone - use rest matrix translation
|
158
|
+
return bone.getRestMatrix().getTranslation();
|
159
|
+
}
|
160
|
+
// For child bones, use local matrix translation
|
161
|
+
const localMatrix = bone.getLocalMatrix();
|
162
|
+
return localMatrix.getTranslation();
|
163
|
+
}
|
164
|
+
catch (error) {
|
165
|
+
// Fallback to zero offset if matrix operations fail
|
166
|
+
return Vector3.Zero();
|
167
|
+
}
|
145
168
|
}
|
146
169
|
static _ExportMotionData(boneData, frameCount, startFrame, animationNames) {
|
147
170
|
let result = "";
|
@@ -153,82 +176,62 @@ export class BVHExporter {
|
|
153
176
|
}
|
154
177
|
return result;
|
155
178
|
}
|
156
|
-
static _CollectFrameValues(boneData,
|
179
|
+
static _CollectFrameValues(boneData, frameIndex, values, animationNames) {
|
157
180
|
for (const data of boneData) {
|
158
181
|
// Skip end sites
|
159
|
-
if (
|
182
|
+
if (this._IsEndSite(data)) {
|
160
183
|
continue;
|
161
184
|
}
|
162
185
|
// Add position values if available
|
163
186
|
if (data.hasPositionChannels) {
|
164
|
-
const position = this.
|
187
|
+
const position = this._GetPositionAtFrameIndex(data.positionKeys, frameIndex);
|
165
188
|
values.push(position.x, position.y, position.z);
|
166
189
|
}
|
167
190
|
// Add rotation values if available
|
168
191
|
if (data.hasRotationChannels) {
|
169
|
-
const rotation = this.
|
170
|
-
// Convert to Euler angles in
|
192
|
+
const rotation = this._GetRotationAtFrameIndex(data.rotationKeys, frameIndex);
|
193
|
+
// Convert to Euler angles in ZXY order
|
171
194
|
const euler = this._QuaternionToEuler(rotation);
|
172
195
|
values.push(euler.z, euler.x, euler.y);
|
173
196
|
}
|
174
197
|
// Process children recursively
|
175
198
|
if (data.children.length > 0) {
|
176
|
-
this._CollectFrameValues(data.children,
|
199
|
+
this._CollectFrameValues(data.children, frameIndex, values, animationNames);
|
177
200
|
}
|
178
201
|
}
|
179
202
|
}
|
180
|
-
static
|
203
|
+
static _GetPositionAtFrameIndex(keys, frameIndex) {
|
181
204
|
if (keys.length === 0) {
|
182
205
|
return Vector3.Zero();
|
183
206
|
}
|
184
|
-
//
|
185
|
-
|
186
|
-
|
187
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
188
|
-
if (keys[i].frame <= frame && keys[i + 1].frame >= frame) {
|
189
|
-
key1 = keys[i];
|
190
|
-
key2 = keys[i + 1];
|
191
|
-
break;
|
192
|
-
}
|
193
|
-
}
|
194
|
-
if (key1.frame === key2.frame) {
|
195
|
-
return key1.value.clone();
|
196
|
-
}
|
197
|
-
const t = (frame - key1.frame) / (key2.frame - key1.frame);
|
198
|
-
return Vector3.Lerp(key1.value, key2.value, t);
|
207
|
+
// Clamp frame index to valid range
|
208
|
+
const clampedIndex = Math.max(0, Math.min(frameIndex, keys.length - 1));
|
209
|
+
return keys[clampedIndex].value.clone();
|
199
210
|
}
|
200
|
-
static
|
211
|
+
static _GetRotationAtFrameIndex(keys, frameIndex) {
|
201
212
|
if (keys.length === 0) {
|
202
213
|
return Quaternion.Identity();
|
203
214
|
}
|
204
|
-
//
|
205
|
-
|
206
|
-
|
207
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
208
|
-
if (keys[i].frame <= frame && keys[i + 1].frame >= frame) {
|
209
|
-
key1 = keys[i];
|
210
|
-
key2 = keys[i + 1];
|
211
|
-
break;
|
212
|
-
}
|
213
|
-
}
|
214
|
-
if (key1.frame === key2.frame) {
|
215
|
-
return key1.value.clone();
|
216
|
-
}
|
217
|
-
const t = (frame - key1.frame) / (key2.frame - key1.frame);
|
218
|
-
return Quaternion.Slerp(key1.value, key2.value, t);
|
215
|
+
// Clamp frame index to valid range
|
216
|
+
const clampedIndex = Math.max(0, Math.min(frameIndex, keys.length - 1));
|
217
|
+
return keys[clampedIndex].value.clone();
|
219
218
|
}
|
220
219
|
static _QuaternionToEuler(quaternion) {
|
221
|
-
// Convert quaternion to Euler angles in
|
222
|
-
const matrix =
|
220
|
+
// Convert quaternion to Euler angles in ZXY order for BVH
|
221
|
+
const matrix = new Matrix();
|
222
|
+
quaternion.toRotationMatrix(matrix);
|
223
|
+
const m = matrix.m;
|
223
224
|
let x, y, z;
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
225
|
+
// ZXY rotation order extraction
|
226
|
+
const sy = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
|
227
|
+
if (sy > Epsilon) {
|
228
|
+
x = Math.atan2(m[6], m[10]);
|
229
|
+
y = Math.atan2(-m[2], sy);
|
230
|
+
z = Math.atan2(m[1], m[0]);
|
228
231
|
}
|
229
232
|
else {
|
230
|
-
x = Math.atan2(
|
231
|
-
y = Math.
|
233
|
+
x = Math.atan2(-m[9], m[5]);
|
234
|
+
y = Math.atan2(-m[2], sy);
|
232
235
|
z = 0;
|
233
236
|
}
|
234
237
|
return new Vector3(Tools.ToDegrees(x), Tools.ToDegrees(y), Tools.ToDegrees(z));
|
package/BVH/bvhSerializer.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"bvhSerializer.js","sourceRoot":"","sources":["../../../../dev/serializers/src/BVH/bvhSerializer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,6CAA+B;AACrE,OAAO,EAAE,KAAK,EAAE,sCAAwB;AACxC,OAAO,EAAE,OAAO,EAAE,gDAAkC;AAWpD,MAAM,OAAO,WAAW;IACb,MAAM,CAAC,MAAM,CAAC,QAAkB,EAAE,iBAA2B,EAAE,EAAE,YAAoB,OAAO;QAC/F,+DAA+D;QAC/D,IAAI,kBAAkB,GAAG,cAAc,CAAC;QACxC,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,kBAAkB,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,CAAC;QAED,yEAAyE;QACzE,IAAI,cAAc,GAAG,IAAI,CAAC;QAC1B,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,cAAc,GAAG,QAAQ,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,mEAAmE;YACnE,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,oCAAoC;gBACpC,cAAc,GAAG,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7E,CAAC;YAED,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAC5D,CAAC;QACL,CAAC;QAED,kDAAkD;QAClD,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC7E,MAAM,UAAU,GAAG,cAAc,CAAC,EAAE,GAAG,cAAc,CAAC,IAAI,GAAG,CAAC,CAAC;QAE/D,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,YAAY,IAAI,aAAa,CAAC;QAE9B,+BAA+B;QAC/B,YAAY,IAAI,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAExD,qBAAqB;QACrB,YAAY,IAAI,UAAU,CAAC;QAC3B,YAAY,IAAI,WAAW,UAAU,IAAI,CAAC;QAC1C,YAAY,IAAI,eAAe,SAAS,IAAI,CAAC;QAC7C,YAAY,IAAI,IAAI,CAAC;QAErB,oBAAoB;QACpB,YAAY,IAAI,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,UAAU,EAAE,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAE3G,OAAO,YAAY,CAAC;IACxB,CAAC;IAEO,MAAM,CAAC,mBAAmB,CAAC,QAAkB,EAAE,cAAwB;QAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC9C,MAAM,SAAS,GAAmB,EAAE,CAAC;QAErC,uCAAuC;QACvC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAiB;gBAC3B,IAAI;gBACJ,QAAQ,EAAE,EAAE;gBACZ,mBAAmB,EAAE,KAAK;gBAC1B,mBAAmB,EAAE,KAAK;gBAC1B,YAAY,EAAE,EAAE;gBAChB,YAAY,EAAE,EAAE;aACnB,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;QAED,0DAA0D;QAC1D,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;YAEpC,kEAAkE;YAClE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACtC,oEAAoE;oBACpE,IAAI,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC1C,IAAI,SAAS,CAAC,cAAc,KAAK,UAAU,EAAE,CAAC;4BAC1C,QAAQ,CAAC,mBAAmB,GAAG,IAAI,CAAC;4BACpC,QAAQ,CAAC,YAAY,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;wBAChD,CAAC;6BAAM,IAAI,SAAS,CAAC,cAAc,KAAK,oBAAoB,EAAE,CAAC;4BAC3D,QAAQ,CAAC,mBAAmB,GAAG,IAAI,CAAC;4BACpC,QAAQ,CAAC,YAAY,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;wBAChD,CAAC;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;YAED,kBAAkB;YAClB,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;gBACnB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAG,CAAC,CAAC;gBAClD,IAAI,UAAU,EAAE,CAAC;oBACb,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;IAEO,MAAM,CAAC,gBAAgB,CAAC,QAAwB,EAAE,WAAmB;QACzE,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,qCAAqC;QACrC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAE1C,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YAEvB,6EAA6E;YAC7E,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBACvF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC;gBACzB,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBACzC,MAAM,IAAI,GAAG,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvG,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,GAAG,MAAM,SAAS,IAAI,CAAC,IAAI,IAAI,CAAC;gBAC1C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC;gBACzB,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBACzC,MAAM,IAAI,GAAG,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBAEvG,qBAAqB;gBACrB,MAAM,QAAQ,GAAa,EAAE,CAAC;gBAC9B,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBAC3B,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;gBACzD,CAAC;gBACD,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBAC3B,8BAA8B;oBAC9B,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;gBACzD,CAAC;gBAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,GAAG,MAAM,gBAAgB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjF,CAAC;gBAED,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC;YAC7B,CAAC;YAED,8BAA8B;YAC9B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,cAAc,CAAC,IAAU;QACpC,mDAAmD;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,YAAY;QACvC,CAAC;QAED,4DAA4D;QAC5D,0DAA0D;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QAE5C,kCAAkC;QAClC,MAAM,YAAY,GAAG,UAAU,CAAC,cAAc,EAAE,CAAC;QACjD,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,EAAE,CAAC;QACrD,MAAM,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;QAE7D,4BAA4B;QAC5B,OAAO,cAAc,CAAC;IAC1B,CAAC;IAEO,MAAM,CAAC,iBAAiB,CAAC,QAAwB,EAAE,UAAkB,EAAE,UAAkB,EAAE,cAAwB;QACvH,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAa,EAAE,CAAC;YAEjC,kDAAkD;YAClD,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,KAAK,GAAG,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;YAEpF,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACpE,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAEO,MAAM,CAAC,mBAAmB,CAAC,QAAwB,EAAE,KAAa,EAAE,MAAgB,EAAE,cAAwB;QAClH,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC1B,iBAAiB;YACjB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBACvF,SAAS;YACb,CAAC;YAED,mCAAmC;YACnC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;gBACpE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YACpD,CAAC;YAED,mCAAmC;YACnC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;gBACpE,uCAAuC;gBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBAChD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3C,CAAC;YAED,+BAA+B;YAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;YAC3E,CAAC;QACL,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,mBAAmB,CAAC,IAAqB,EAAE,KAAa;QACnE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,0CAA0C;QAC1C,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBACvD,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACf,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnB,MAAM;YACV,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3D,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC;IAEO,MAAM,CAAC,mBAAmB,CAAC,IAAqB,EAAE,KAAa;QACnE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAC;QACjC,CAAC;QAED,0CAA0C;QAC1C,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBACvD,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACf,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnB,MAAM;YACV,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3D,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC;IAEO,MAAM,CAAC,kBAAkB,CAAC,UAAsB;QACpD,kDAAkD;QAClD,MAAM,MAAM,GAAG,UAAU,CAAC,gBAAgB,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;QAEzD,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAEZ,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,EAAE,CAAC;YACtC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1C,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACJ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC,GAAG,CAAC,CAAC;QACV,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;CACJ","sourcesContent":["import type { Skeleton } from \"core/Bones/skeleton\";\nimport type { Bone } from \"core/Bones/bone\";\nimport type { IAnimationKey } from \"core/Animations\";\nimport { Vector3, Quaternion, Matrix } from \"core/Maths/math.vector\";\nimport { Tools } from \"core/Misc/tools\";\nimport { Epsilon } from \"core/Maths/math.constants\";\n\ninterface IBVHBoneData {\n bone: Bone;\n children: IBVHBoneData[];\n hasPositionChannels: boolean;\n hasRotationChannels: boolean;\n positionKeys: IAnimationKey[];\n rotationKeys: IAnimationKey[];\n}\n\nexport class BVHExporter {\n public static Export(skeleton: Skeleton, animationNames: string[] = [], frameRate: number = 0.03333): string {\n // If no animation names provided, use all available animations\n let animationsToExport = animationNames;\n if (!animationNames || animationNames.length === 0) {\n animationsToExport = skeleton.animations.map((anim) => anim.name);\n }\n\n // Get animation range from the first animation (or create a default one)\n let animationRange = null;\n if (animationsToExport.length > 0) {\n animationRange = skeleton.getAnimationRange(animationsToExport[0]);\n }\n\n if (!animationRange) {\n // If no animation range found, create a default one or throw error\n if (skeleton.animations.length > 0) {\n // Use the first available animation\n animationRange = skeleton.getAnimationRange(skeleton.animations[0].name);\n }\n\n if (!animationRange) {\n throw new Error(\"No animation range found in skeleton\");\n }\n }\n\n // Build bone hierarchy and collect animation data\n const boneHierarchy = this._BuildBoneHierarchy(skeleton, animationsToExport);\n const frameCount = animationRange.to - animationRange.from + 1;\n\n let exportString = \"\";\n exportString += `HIERARCHY\\n`;\n\n // Export hierarchy recursively\n exportString += this._ExportHierarchy(boneHierarchy, 0);\n\n // Export motion data\n exportString += `MOTION\\n`;\n exportString += `Frames: ${frameCount}\\n`;\n exportString += `Frame Time: ${frameRate}\\n`;\n exportString += `\\n`;\n\n // Export frame data\n exportString += this._ExportMotionData(boneHierarchy, frameCount, animationRange.from, animationsToExport);\n\n return exportString;\n }\n\n private static _BuildBoneHierarchy(skeleton: Skeleton, animationNames: string[]): IBVHBoneData[] {\n const boneMap = new Map<Bone, IBVHBoneData>();\n const rootBones: IBVHBoneData[] = [];\n\n // First pass: create bone data objects\n for (const bone of skeleton.bones) {\n const boneData: IBVHBoneData = {\n bone,\n children: [],\n hasPositionChannels: false,\n hasRotationChannels: false,\n positionKeys: [],\n rotationKeys: [],\n };\n boneMap.set(bone, boneData);\n }\n\n // Second pass: build hierarchy and collect animation data\n for (const bone of skeleton.bones) {\n const boneData = boneMap.get(bone)!;\n\n // Check if bone has animations from the specified animation names\n if (bone.animations.length > 0) {\n for (const animation of bone.animations) {\n // Only include animations that are in the specified animation names\n if (animationNames.includes(animation.name)) {\n if (animation.targetProperty === \"position\") {\n boneData.hasPositionChannels = true;\n boneData.positionKeys = animation.getKeys();\n } else if (animation.targetProperty === \"rotationQuaternion\") {\n boneData.hasRotationChannels = true;\n boneData.rotationKeys = animation.getKeys();\n }\n }\n }\n }\n\n // Build hierarchy\n if (bone.getParent()) {\n const parentData = boneMap.get(bone.getParent()!);\n if (parentData) {\n parentData.children.push(boneData);\n }\n } else {\n rootBones.push(boneData);\n }\n }\n\n return rootBones;\n }\n\n private static _ExportHierarchy(boneData: IBVHBoneData[], indentLevel: number): string {\n let result = \"\";\n // 4 spaces identation for each level\n const indent = \" \".repeat(indentLevel);\n\n for (const data of boneData) {\n const bone = data.bone;\n\n // Determine if this is an end site (bone with no children and no animations)\n if (data.children.length === 0 && !data.hasPositionChannels && !data.hasRotationChannels) {\n result += `${indent}End Site\\n`;\n result += `${indent}{\\n`;\n const offset = this._GetBoneOffset(bone);\n result += `${indent} OFFSET ${offset.x.toFixed(6)} ${offset.y.toFixed(6)} ${offset.z.toFixed(6)}\\n`;\n result += `${indent}}\\n`;\n } else {\n result += `${indent}JOINT ${bone.name}\\n`;\n result += `${indent}{\\n`;\n const offset = this._GetBoneOffset(bone);\n result += `${indent} OFFSET ${offset.x.toFixed(6)} ${offset.y.toFixed(6)} ${offset.z.toFixed(6)}\\n`;\n\n // Determine channels\n const channels: string[] = [];\n if (data.hasPositionChannels) {\n channels.push(\"Xposition\", \"Yposition\", \"Zposition\");\n }\n if (data.hasRotationChannels) {\n // BVH uses ZYX rotation order\n channels.push(\"Zrotation\", \"Xrotation\", \"Yrotation\");\n }\n\n if (channels.length > 0) {\n result += `${indent} CHANNELS ${channels.length} ${channels.join(\" \")}\\n`;\n }\n\n result += `${indent}}\\n`;\n }\n\n // Export children recursively\n if (data.children.length > 0) {\n result += this._ExportHierarchy(data.children, indentLevel + 1);\n }\n }\n\n return result;\n }\n\n private static _GetBoneOffset(bone: Bone): Vector3 {\n // Get the local offset of the bone from its parent\n const parent = bone.getParent();\n if (!parent) {\n return Vector3.Zero(); // Root bone\n }\n\n // For BVH, we need to get the bone's offset from its parent\n // This should match the original BVH file's OFFSET values\n const boneMatrix = bone.getBindMatrix();\n const parentMatrix = parent.getBindMatrix();\n\n // Calculate the relative position\n const bonePosition = boneMatrix.getTranslation();\n const parentPosition = parentMatrix.getTranslation();\n const relativeOffset = bonePosition.subtract(parentPosition);\n\n // Return the full 3D offset\n return relativeOffset;\n }\n\n private static _ExportMotionData(boneData: IBVHBoneData[], frameCount: number, startFrame: number, animationNames: string[]): string {\n let result = \"\";\n\n for (let frame = 0; frame < frameCount; frame++) {\n const frameValues: number[] = [];\n\n // Collect values for all bones in hierarchy order\n this._CollectFrameValues(boneData, frame + startFrame, frameValues, animationNames);\n\n result += frameValues.map((v) => v.toFixed(6)).join(\" \") + \"\\n\";\n }\n\n return result;\n }\n\n private static _CollectFrameValues(boneData: IBVHBoneData[], frame: number, values: number[], animationNames: string[]): void {\n for (const data of boneData) {\n // Skip end sites\n if (data.children.length === 0 && !data.hasPositionChannels && !data.hasRotationChannels) {\n continue;\n }\n\n // Add position values if available\n if (data.hasPositionChannels) {\n const position = this._GetPositionAtFrame(data.positionKeys, frame);\n values.push(position.x, position.y, position.z);\n }\n\n // Add rotation values if available\n if (data.hasRotationChannels) {\n const rotation = this._GetRotationAtFrame(data.rotationKeys, frame);\n // Convert to Euler angles in ZYX order\n const euler = this._QuaternionToEuler(rotation);\n values.push(euler.z, euler.x, euler.y);\n }\n\n // Process children recursively\n if (data.children.length > 0) {\n this._CollectFrameValues(data.children, frame, values, animationNames);\n }\n }\n }\n\n private static _GetPositionAtFrame(keys: IAnimationKey[], frame: number): Vector3 {\n if (keys.length === 0) {\n return Vector3.Zero();\n }\n\n // Find the appropriate key or interpolate\n let key1 = keys[0];\n let key2 = keys[keys.length - 1];\n\n for (let i = 0; i < keys.length - 1; i++) {\n if (keys[i].frame <= frame && keys[i + 1].frame >= frame) {\n key1 = keys[i];\n key2 = keys[i + 1];\n break;\n }\n }\n\n if (key1.frame === key2.frame) {\n return key1.value.clone();\n }\n\n const t = (frame - key1.frame) / (key2.frame - key1.frame);\n return Vector3.Lerp(key1.value, key2.value, t);\n }\n\n private static _GetRotationAtFrame(keys: IAnimationKey[], frame: number): Quaternion {\n if (keys.length === 0) {\n return Quaternion.Identity();\n }\n\n // Find the appropriate key or interpolate\n let key1 = keys[0];\n let key2 = keys[keys.length - 1];\n\n for (let i = 0; i < keys.length - 1; i++) {\n if (keys[i].frame <= frame && keys[i + 1].frame >= frame) {\n key1 = keys[i];\n key2 = keys[i + 1];\n break;\n }\n }\n\n if (key1.frame === key2.frame) {\n return key1.value.clone();\n }\n\n const t = (frame - key1.frame) / (key2.frame - key1.frame);\n return Quaternion.Slerp(key1.value, key2.value, t);\n }\n\n private static _QuaternionToEuler(quaternion: Quaternion): Vector3 {\n // Convert quaternion to Euler angles in ZYX order\n const matrix = quaternion.toRotationMatrix(new Matrix());\n\n let x, y, z;\n\n if (Math.abs(matrix.m[6]) < 1 - Epsilon) {\n x = Math.atan2(-matrix.m[7], matrix.m[8]);\n y = Math.asin(matrix.m[6]);\n z = Math.atan2(-matrix.m[3], matrix.m[0]);\n } else {\n x = Math.atan2(matrix.m[5], matrix.m[4]);\n y = Math.asin(matrix.m[6]);\n z = 0;\n }\n\n return new Vector3(Tools.ToDegrees(x), Tools.ToDegrees(y), Tools.ToDegrees(z));\n }\n}\n"]}
|
1
|
+
{"version":3,"file":"bvhSerializer.js","sourceRoot":"","sources":["../../../../dev/serializers/src/BVH/bvhSerializer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Skeleton } from \"core/Bones/skeleton\";\nimport type { Bone } from \"core/Bones/bone\";\nimport type { IAnimationKey } from \"core/Animations/animationKey\";\nimport { AnimationRange } from \"core/Animations/animationRange\";\nimport { Vector3, Quaternion, Matrix } from \"core/Maths/math.vector\";\nimport { Tools } from \"core/Misc/tools\";\nimport { Epsilon } from \"core/Maths/math.constants\";\nimport type { Nullable } from \"core/types\";\n\ninterface IBVHBoneData {\n bone: Bone;\n children: IBVHBoneData[];\n hasPositionChannels: boolean;\n hasRotationChannels: boolean;\n positionKeys: IAnimationKey[];\n rotationKeys: IAnimationKey[];\n}\n\nexport class BVHExporter {\n public static Export(skeleton: Skeleton, animationNames: string[] = [], frameRate?: number): string {\n // Validate skeleton\n if (!skeleton || skeleton.bones.length === 0) {\n throw new Error(\"Invalid or empty skeleton provided\");\n }\n\n // If no animation names provided, use all available animations\n let animationsToExport = animationNames;\n if (!animationNames || animationNames.length === 0) {\n animationsToExport = skeleton.animations.map((anim) => anim.name);\n }\n\n // Calculate overall animation range from all specified animations\n let overallRange: Nullable<AnimationRange> = null;\n for (const animName of animationsToExport) {\n const range = skeleton.getAnimationRange(animName);\n if (range) {\n overallRange = overallRange ? new AnimationRange(\"animation-range\", Math.min(overallRange.from, range.from), Math.max(overallRange.to, range.to)) : range;\n }\n }\n\n if (!overallRange) {\n // If no animation range found, try to get from any animation\n if (skeleton.animations.length > 0) {\n overallRange = skeleton.getAnimationRange(skeleton.animations[0].name);\n }\n\n if (!overallRange) {\n throw new Error(\"No animation range found in skeleton\");\n }\n }\n\n // Calculate frame rate from animation data if not provided\n const actualFrameRate = frameRate || 1 / (skeleton.animations[0]?.framePerSecond || 30);\n\n // Build bone hierarchy and collect animation data\n const boneHierarchy = this._BuildBoneHierarchy(skeleton, animationsToExport);\n\n // Calculate frame count from actual animation keyframes\n let frameCount = 0;\n for (const boneData of boneHierarchy) {\n if (boneData.positionKeys.length > 0) {\n frameCount = Math.max(frameCount, boneData.positionKeys.length);\n }\n if (boneData.rotationKeys.length > 0) {\n frameCount = Math.max(frameCount, boneData.rotationKeys.length);\n }\n }\n\n // Fallback: if no keyframes found, calculate from time range\n if (frameCount === 0) {\n frameCount = Math.floor((overallRange.to - overallRange.from) / actualFrameRate) + 1;\n }\n\n let exportString = \"\";\n exportString += `HIERARCHY\\n`;\n\n // Export hierarchy recursively\n exportString += this._ExportHierarchy(boneHierarchy, 0);\n\n // Export motion data\n exportString += `MOTION\\n`;\n exportString += `Frames: ${frameCount}\\n`;\n exportString += `Frame Time: ${actualFrameRate.toFixed(6)}\\n`;\n\n // Export frame data\n exportString += this._ExportMotionData(boneHierarchy, frameCount, 0, animationsToExport);\n\n return exportString;\n }\n\n private static _BuildBoneHierarchy(skeleton: Skeleton, animationNames: string[]): IBVHBoneData[] {\n const boneMap = new Map<Bone, IBVHBoneData>();\n const rootBones: IBVHBoneData[] = [];\n\n // First pass: create bone data objects\n for (const bone of skeleton.bones) {\n const boneData: IBVHBoneData = {\n bone,\n children: [],\n hasPositionChannels: false,\n hasRotationChannels: false,\n positionKeys: [],\n rotationKeys: [],\n };\n boneMap.set(bone, boneData);\n }\n\n // Second pass: build hierarchy and collect animation data\n for (const bone of skeleton.bones) {\n const boneData = boneMap.get(bone)!;\n\n // Check if bone has animations from the specified animation names\n if (bone.animations.length > 0) {\n for (const animation of bone.animations) {\n // Only include animations that are in the specified animation names\n if (animationNames.includes(animation.name)) {\n if (animation.targetProperty === \"position\") {\n boneData.hasPositionChannels = true;\n boneData.positionKeys.push(...animation.getKeys());\n } else if (animation.targetProperty === \"rotationQuaternion\") {\n boneData.hasRotationChannels = true;\n boneData.rotationKeys.push(...animation.getKeys());\n }\n }\n }\n }\n\n // Build hierarchy\n if (bone.getParent()) {\n const parentData = boneMap.get(bone.getParent()!);\n if (parentData) {\n parentData.children.push(boneData);\n }\n } else {\n rootBones.push(boneData);\n }\n }\n\n return rootBones;\n }\n\n private static _ExportHierarchy(boneData: IBVHBoneData[], indentLevel: number): string {\n let result = \"\";\n const indent = \" \".repeat(indentLevel);\n\n for (const data of boneData) {\n const bone = data.bone;\n const isEndSite = this._IsEndSite(data);\n\n if (isEndSite) {\n result += `${indent}End Site\\n`;\n result += `${indent}{\\n`;\n const offset = this._GetBoneOffset(bone);\n result += `${indent} OFFSET ${offset.x.toFixed(6)} ${offset.y.toFixed(6)} ${offset.z.toFixed(6)}\\n`;\n result += `${indent}}\\n`;\n } else {\n result += `${indentLevel === 0 ? \"ROOT\" : `${indent}JOINT`} ${bone.name}\\n`;\n result += `${indent}{\\n`;\n const offset = this._GetBoneOffset(bone);\n result += `${indent} OFFSET ${offset.x.toFixed(6)} ${offset.y.toFixed(6)} ${offset.z.toFixed(6)}\\n`;\n\n // Determine channels\n const channels: string[] = [];\n if (data.hasPositionChannels) {\n channels.push(\"Xposition\", \"Yposition\", \"Zposition\");\n }\n if (data.hasRotationChannels) {\n // BVH typically uses ZXY rotation order\n channels.push(\"Zrotation\", \"Xrotation\", \"Yrotation\");\n }\n\n if (channels.length > 0) {\n result += `${indent} CHANNELS ${channels.length} ${channels.join(\" \")}\\n`;\n }\n\n // Export children recursively\n if (data.children.length > 0) {\n result += this._ExportHierarchy(data.children, indentLevel + 1);\n }\n\n result += `${indent}}\\n`;\n }\n }\n\n return result;\n }\n\n private static _IsEndSite(data: IBVHBoneData): boolean {\n // An end site is a bone with no children (regardless of animation channels)\n return data.children.length === 0;\n }\n\n private static _GetBoneOffset(bone: Bone): Vector3 {\n // Use the bone's rest matrix or local matrix for correct offset\n try {\n if (!bone.getParent()) {\n // Root bone - use rest matrix translation\n return bone.getRestMatrix().getTranslation();\n }\n\n // For child bones, use local matrix translation\n const localMatrix = bone.getLocalMatrix();\n return localMatrix.getTranslation();\n } catch (error) {\n // Fallback to zero offset if matrix operations fail\n return Vector3.Zero();\n }\n }\n\n private static _ExportMotionData(boneData: IBVHBoneData[], frameCount: number, startFrame: number, animationNames: string[]): string {\n let result = \"\";\n\n for (let frame = 0; frame < frameCount; frame++) {\n const frameValues: number[] = [];\n\n // Collect values for all bones in hierarchy order\n this._CollectFrameValues(boneData, frame + startFrame, frameValues, animationNames);\n\n result += frameValues.map((v) => v.toFixed(6)).join(\" \") + \"\\n\";\n }\n\n return result;\n }\n\n private static _CollectFrameValues(boneData: IBVHBoneData[], frameIndex: number, values: number[], animationNames: string[]): void {\n for (const data of boneData) {\n // Skip end sites\n if (this._IsEndSite(data)) {\n continue;\n }\n\n // Add position values if available\n if (data.hasPositionChannels) {\n const position = this._GetPositionAtFrameIndex(data.positionKeys, frameIndex);\n values.push(position.x, position.y, position.z);\n }\n\n // Add rotation values if available\n if (data.hasRotationChannels) {\n const rotation = this._GetRotationAtFrameIndex(data.rotationKeys, frameIndex);\n // Convert to Euler angles in ZXY order\n const euler = this._QuaternionToEuler(rotation);\n values.push(euler.z, euler.x, euler.y);\n }\n\n // Process children recursively\n if (data.children.length > 0) {\n this._CollectFrameValues(data.children, frameIndex, values, animationNames);\n }\n }\n }\n\n private static _GetPositionAtFrameIndex(keys: IAnimationKey[], frameIndex: number): Vector3 {\n if (keys.length === 0) {\n return Vector3.Zero();\n }\n\n // Clamp frame index to valid range\n const clampedIndex = Math.max(0, Math.min(frameIndex, keys.length - 1));\n return keys[clampedIndex].value.clone();\n }\n\n private static _GetRotationAtFrameIndex(keys: IAnimationKey[], frameIndex: number): Quaternion {\n if (keys.length === 0) {\n return Quaternion.Identity();\n }\n\n // Clamp frame index to valid range\n const clampedIndex = Math.max(0, Math.min(frameIndex, keys.length - 1));\n return keys[clampedIndex].value.clone();\n }\n\n private static _QuaternionToEuler(quaternion: Quaternion): Vector3 {\n // Convert quaternion to Euler angles in ZXY order for BVH\n const matrix = new Matrix();\n quaternion.toRotationMatrix(matrix);\n\n const m = matrix.m;\n let x, y, z;\n\n // ZXY rotation order extraction\n const sy = Math.sqrt(m[0] * m[0] + m[1] * m[1]);\n\n if (sy > Epsilon) {\n x = Math.atan2(m[6], m[10]);\n y = Math.atan2(-m[2], sy);\n z = Math.atan2(m[1], m[0]);\n } else {\n x = Math.atan2(-m[9], m[5]);\n y = Math.atan2(-m[2], sy);\n z = 0;\n }\n\n return new Vector3(Tools.ToDegrees(x), Tools.ToDegrees(y), Tools.ToDegrees(z));\n }\n}\n"]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@babylonjs/serializers",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.26.1",
|
4
4
|
"main": "index.js",
|
5
5
|
"module": "index.js",
|
6
6
|
"types": "index.d.ts",
|
@@ -18,10 +18,10 @@
|
|
18
18
|
"postcompile": "build-tools -c add-js-to-es6"
|
19
19
|
},
|
20
20
|
"devDependencies": {
|
21
|
-
"@babylonjs/core": "^8.
|
21
|
+
"@babylonjs/core": "^8.26.1",
|
22
22
|
"@dev/build-tools": "^1.0.0",
|
23
23
|
"@lts/serializers": "^1.0.0",
|
24
|
-
"babylonjs-gltf2interface": "^8.
|
24
|
+
"babylonjs-gltf2interface": "^8.26.1"
|
25
25
|
},
|
26
26
|
"peerDependencies": {
|
27
27
|
"@babylonjs/core": "^8.0.0",
|