UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

209 lines (208 loc) 7.19 kB
import { Loader, FileLoader, Skeleton, Vector3, Quaternion, Bone, VectorKeyframeTrack, QuaternionKeyframeTrack, AnimationClip } from "three"; class BVHLoader extends Loader { constructor(manager) { super(manager); this.animateBonePositions = true; this.animateBoneRotations = true; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load( url, function(text) { try { onLoad(scope.parse(text)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError ); } parse(text) { function readBvh(lines2) { if (nextLine(lines2) !== "HIERARCHY") { console.error("THREE.BVHLoader: HIERARCHY expected."); } const list = []; const root = readNode(lines2, nextLine(lines2), list); if (nextLine(lines2) !== "MOTION") { console.error("THREE.BVHLoader: MOTION expected."); } let tokens = nextLine(lines2).split(/[\s]+/); const numFrames = parseInt(tokens[1]); if (isNaN(numFrames)) { console.error("THREE.BVHLoader: Failed to read number of frames."); } tokens = nextLine(lines2).split(/[\s]+/); const frameTime = parseFloat(tokens[2]); if (isNaN(frameTime)) { console.error("THREE.BVHLoader: Failed to read frame time."); } for (let i = 0; i < numFrames; i++) { tokens = nextLine(lines2).split(/[\s]+/); readFrameData(tokens, i * frameTime, root); } return list; } function readFrameData(data, frameTime, bone) { if (bone.type === "ENDSITE") return; const keyframe = { time: frameTime, position: new Vector3(), rotation: new Quaternion() }; bone.frames.push(keyframe); const quat = new Quaternion(); const vx = new Vector3(1, 0, 0); const vy = new Vector3(0, 1, 0); const vz = new Vector3(0, 0, 1); for (let i = 0; i < bone.channels.length; i++) { switch (bone.channels[i]) { case "Xposition": keyframe.position.x = parseFloat(data.shift().trim()); break; case "Yposition": keyframe.position.y = parseFloat(data.shift().trim()); break; case "Zposition": keyframe.position.z = parseFloat(data.shift().trim()); break; case "Xrotation": quat.setFromAxisAngle(vx, parseFloat(data.shift().trim()) * Math.PI / 180); keyframe.rotation.multiply(quat); break; case "Yrotation": quat.setFromAxisAngle(vy, parseFloat(data.shift().trim()) * Math.PI / 180); keyframe.rotation.multiply(quat); break; case "Zrotation": quat.setFromAxisAngle(vz, parseFloat(data.shift().trim()) * Math.PI / 180); keyframe.rotation.multiply(quat); break; default: console.warn("THREE.BVHLoader: Invalid channel type."); } } for (let i = 0; i < bone.children.length; i++) { readFrameData(data, frameTime, bone.children[i]); } } function readNode(lines2, firstline, list) { const node = { name: "", type: "", frames: [] }; list.push(node); let tokens = firstline.split(/[\s]+/); if (tokens[0].toUpperCase() === "END" && tokens[1].toUpperCase() === "SITE") { node.type = "ENDSITE"; node.name = "ENDSITE"; } else { node.name = tokens[1]; node.type = tokens[0].toUpperCase(); } if (nextLine(lines2) !== "{") { console.error("THREE.BVHLoader: Expected opening { after type & name"); } tokens = nextLine(lines2).split(/[\s]+/); if (tokens[0] !== "OFFSET") { console.error("THREE.BVHLoader: Expected OFFSET but got: " + tokens[0]); } if (tokens.length !== 4) { console.error("THREE.BVHLoader: Invalid number of values for OFFSET."); } const offset = new Vector3(parseFloat(tokens[1]), parseFloat(tokens[2]), parseFloat(tokens[3])); if (isNaN(offset.x) || isNaN(offset.y) || isNaN(offset.z)) { console.error("THREE.BVHLoader: Invalid values of OFFSET."); } node.offset = offset; if (node.type !== "ENDSITE") { tokens = nextLine(lines2).split(/[\s]+/); if (tokens[0] !== "CHANNELS") { console.error("THREE.BVHLoader: Expected CHANNELS definition."); } const numChannels = parseInt(tokens[1]); node.channels = tokens.splice(2, numChannels); node.children = []; } while (true) { const line = nextLine(lines2); if (line === "}") { return node; } else { node.children.push(readNode(lines2, line, list)); } } } function toTHREEBone(source, list) { const bone = new Bone(); list.push(bone); bone.position.add(source.offset); bone.name = source.name; if (source.type !== "ENDSITE") { for (let i = 0; i < source.children.length; i++) { bone.add(toTHREEBone(source.children[i], list)); } } return bone; } function toTHREEAnimation(bones2) { const tracks = []; for (let i = 0; i < bones2.length; i++) { const bone = bones2[i]; if (bone.type === "ENDSITE") continue; const times = []; const positions = []; const rotations = []; for (let j = 0; j < bone.frames.length; j++) { const frame = bone.frames[j]; times.push(frame.time); positions.push(frame.position.x + bone.offset.x); positions.push(frame.position.y + bone.offset.y); positions.push(frame.position.z + bone.offset.z); rotations.push(frame.rotation.x); rotations.push(frame.rotation.y); rotations.push(frame.rotation.z); rotations.push(frame.rotation.w); } if (scope.animateBonePositions) { tracks.push(new VectorKeyframeTrack(".bones[" + bone.name + "].position", times, positions)); } if (scope.animateBoneRotations) { tracks.push(new QuaternionKeyframeTrack(".bones[" + bone.name + "].quaternion", times, rotations)); } } return new AnimationClip("animation", -1, tracks); } function nextLine(lines2) { let line; while ((line = lines2.shift().trim()).length === 0) { } return line; } const scope = this; const lines = text.split(/[\r\n]+/g); const bones = readBvh(lines); const threeBones = []; toTHREEBone(bones[0], threeBones); const threeClip = toTHREEAnimation(bones); return { skeleton: new Skeleton(threeBones), clip: threeClip }; } } export { BVHLoader }; //# sourceMappingURL=BVHLoader.js.map