<!DOCTYPE html>
<html>
<head>
<title>BVH to JSON Data Inspector (Final)</title>
<meta charset="utf-8">
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="./js/aframe-vrm.js"></script>
<script>
(() => {
const t = {
Quaternion: THREE.Quaternion,
Vector3: THREE.Vector3,
Matrix4: THREE.Matrix4,
Skeleton: THREE.Skeleton,
Bone: THREE.Bone,
AnimationClip: THREE.AnimationClip,
QuaternionKeyframeTrack: THREE.QuaternionKeyframeTrack,
VectorKeyframeTrack: THREE.VectorKeyframeTrack,
Loader: THREE.Loader,
FileLoader: THREE.FileLoader
},
e = new t.Quaternion,
s = new t.Vector3,
o = new t.Matrix4;
class n extends t.Loader {
constructor(r) {
super(r), this.animateBonePositions = !0, this.animateBoneRotations = !0
}
load(r, i, h, a) {
const l = this,
c = new t.FileLoader(l.manager);
c.setPath(l.path), c.setRequestHeader(l.requestHeader), c.setWithCredentials(l.withCredentials), c.load(r, function(d) {
try {
i(l.parse(d))
} catch (p) {
a ? a(p) : console.error(p)
}
}, h, a)
}
parse(r) {
const i = r.split(/[\s,]+/),
h = [];
for (let e = 0; e < i.length; e++) {
const t = i[e];
t.length > 0 && h.push(t)
}
if ("HIERARCHY" !== h.shift()) throw new Error("Unexpected token: " + h.shift() + ". Expected: HIERARCHY");
if ("ROOT" !== h.shift()) throw new Error("Unexpected token: " + h.shift() + ". Expected: ROOT");
const a = this._readNode(h);
if ("MOTION" !== h.shift()) throw new Error("Unexpected token: " + h.shift() + ". Expected: MOTION");
if ("Frames:" !== h.shift()) throw new Error("Unexpected token: " + h.shift() + ". Expected: Frames:");
const l = parseInt(h.shift());
if (isNaN(l)) throw new Error("Unexpected token: " + h.shift() + ". Expected: A number.");
if ("Frame" !== h.shift()) throw new Error("Unexpected token: ".concat(h.shift(), ". Expected: Frame"));
if ("Time:" !== h.shift()) throw new Error("Unexpected token: ".concat(h.shift(), ". Expected: Time:"));
const c = parseFloat(h.shift());
if (isNaN(c)) throw new Error("Unexpected token: " + h.shift() + ". Expected: A number.");
const d = l * c,
p = [];
let u = 0;
for (let e = 0; e < l; e++) {
const t = [];
for (let s = 0; s < a.channels.length; s++) {
const e = parseFloat(h.shift());
t.push(e)
}
p.push(t), u += c
}
const m = [],
f = [];
a.traverse(function(r) {
l.animateBonePositions && r.position.length > 0 && f.push(new t.VectorKeyframeTrack(r.name + ".position", r.position, r.position.length / 3)), l.animateBoneRotations && r.quaternion.length > 0 && m.push(new t.QuaternionKeyframeTrack(r.name + ".quaternion", r.quaternion, r.quaternion.length / 4))
});
const g = function(t) {
for (let r = 0, i = 0, h = 1 / c; r < p.length; r++) {
const a = p[r];
i = 0;
for (let l = 0; l < t.channels.length; l++) {
const c = t.channels[l];
switch (c) {
case "Xposition":
t.position[3 * r] = a[i++];
break;
case "Yposition":
t.position[3 * r + 1] = a[i++];
break;
case "Zposition":
t.position[3 * r + 2] = a[i++];
break;
case "Xrotation":
e.x = a[i++] * Math.PI / 180;
break;
case "Yrotation":
e.y = a[i++] * Math.PI / 180;
break;
case "Zrotation":
e.z = a[i++] * Math.PI / 180;
break;
default:
throw new Error("Unexpected channel type: " + c)
}
}
s.set(e.x, e.y, e.z).normalize(), t.quaternion.push(new t.Quaternion().setFromEuler(s).x), t.quaternion.push(new t.Quaternion().setFromEuler(s).y), t.quaternion.push(new t.Quaternion().setFromEuler(s).z), t.quaternion.push(new t.Quaternion().setFromEuler(s).w)
}
t.children.forEach(g)
};
g(a);
const k = new t.Bone;
a.traverse(function(e) {
const s = new t.Bone;
s.position.fromArray(e.offset), s.name = e.name, k.add(s)
});
const _ = new t.Skeleton(k.children);
return {
skeleton: _,
clip: new t.AnimationClip("animation", d, [...f, ...m])
}
}
_readNode(e) {
const s = e.shift(),
o = {
name: s,
offset: [],
channels: [],
children: [],
position: [],
quaternion: []
};
if ("{" !== e.shift()) throw new Error("Unexpected token: " + e.shift() + ". Expected: {");
for (;
"}" !== e[0];) {
const t = e.shift();
if ("OFFSET" === t) {
for (let t = 0; t < 3; t++) {
const s = parseFloat(e.shift());
if (isNaN(s)) throw new Error("Unexpected token: " + e.shift() + ". Expected: A number.");
o.offset.push(s)
}
} else if ("CHANNELS" === t) {
const t = parseInt(e.shift());
if (isNaN(t)) throw new Error("Unexpected token: " + e.shift() + ". Expected: A number.");
for (let s = 0; s < t; s++) o.channels.push(e.shift())
} else {
if ("JOINT" !== t && "End" !== t) throw new Error("Unexpected token: " + t);
o.children.push(this._readNode(e))
}
}
return e.shift(), o
}
}
THREE.BVHLoader = n;
})();
</script>
</head>
<body>
<a-scene>
<a-entity
id="avatar"
vrm="src: ./vrm/tesA1_V0a.vrm"
position="0 0 0"
rotation="0 180 0">
</a-entity>
<a-camera position="0 1.2 2.5"></a-camera>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
<script>
window.addEventListener('load', function() {
console.log('✅ ページ読み込み完了。BVHをJSONに変換し、結果を出力します。');
const BVH_URL = 'https://p-bookmark.sakura.ne.jp/junkerstock/vrm/8.bvh';
const loader = new THREE.BVHLoader();
loader.load(BVH_URL, function(bvh) {
console.log('✅ BVH読み込み成功!');
const animationClip = bvh.clip;
console.log('%c--- Raw AnimationClip Data (Start) ---', 'color: green; font-weight: bold;');
const clipJSON = THREE.AnimationClip.toJSON(animationClip);
console.log(JSON.stringify(clipJSON, null, 2));
console.log('Interactive Motion Object:', animationClip);
console.log('%c--- Raw AnimationClip Data (End) ---', 'color: green; font-weight: bold;');
console.log('✅ データ出力完了。');
});
});
</script>
</body>
</html>
使用変数
) { console.log -------( Function ) | |
a | |
animationClip | |
bvh) { console.log -------( Function ) | |
BVHLoader | |
BVH_URL | |
c | |
charset | |
clipJSON | |
color | |
d) { try { i -------( Function ) | |
d | |
e) { const s = new t.Bone; s.position.fromArray -------( Function ) | |
e | |
f | |
g | |
h | |
i | |
id | |
imateBonePositions | |
imateBoneRotations | |
k | |
l | |
loader | |
m | |
name | |
o | |
p | |
position | |
r) { l.animateBonePositions && r.position.length > 0 && f.push -------( Function ) | |
r | |
rotation | |
s | |
src | |
t | |
u | |
vrm | |
x | |
y | |
z | |
_ |