588 lines
23 KiB
JavaScript
588 lines
23 KiB
JavaScript
const Cesium = require('cesium/Cesium');
|
|
|
|
export const LOOP_TYPE = Object.freeze({"CLAMP":1, "LOOP":2});
|
|
export const PLAY_STATE = Object.freeze({"PLAY":1, "STOP":2, "PAUSE":3});
|
|
|
|
export class AnimationKey {
|
|
constructor(time, value) {
|
|
this.time = time;
|
|
this.value = value;
|
|
}
|
|
};
|
|
|
|
export class AnimationTrack {
|
|
constructor() {
|
|
this.translation_keys = [];
|
|
this.rotation_keys = [];
|
|
this.scale_keys = [];
|
|
}
|
|
};
|
|
|
|
export class Animation {
|
|
constructor(name) {
|
|
this.name = name;
|
|
this.duration = 0;
|
|
this.tracks = {}; // a dictionary whose keys are node names
|
|
}
|
|
};
|
|
|
|
export class AnimationSet {
|
|
constructor(animations, nodes) {
|
|
this.animations = animations;
|
|
this.nodes = nodes;
|
|
}
|
|
};
|
|
|
|
export class AnimationPlayer {
|
|
constructor(animation_set, entity, fps) {
|
|
this.loop_type = LOOP_TYPE.CLAMP;
|
|
this.play_state = PLAY_STATE.STOP;
|
|
this.animation_set = animation_set;
|
|
this.entity = entity;
|
|
if(this.animation_set.animations.length > 0) {
|
|
this.current_animation = this.animation_set.animations[0];
|
|
}
|
|
else {
|
|
this.current_animation = "";
|
|
}
|
|
|
|
// set initial node positions for Cesium entity
|
|
let cesium_nodes = {};
|
|
|
|
for(var node_name in this.animation_set.nodes) {
|
|
if(typeof this.entity.model.nodeTransformations != "undefined" &&
|
|
typeof this.entity.model.nodeTransformations[node_name] != "undefined"){
|
|
cesium_nodes[node_name] = this.entity.model.nodeTransformations[node_name];
|
|
} else {
|
|
cesium_nodes[node_name] = {
|
|
translation: new Cesium.Cartesian3(0, 0, 0),
|
|
rotation: new Cesium.Cartesian4(0, 0, 0, 1),
|
|
scale: new Cesium.Cartesian3(1, 1, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
this.entity.model.nodeTransformations = cesium_nodes;
|
|
this.interval_id = -1;
|
|
this.current_time = 0;
|
|
this.speed = 1;
|
|
this._frame_duration = 1.0/fps;
|
|
}
|
|
|
|
setAnimation(animation_name) {
|
|
for(var i = 0; i < this.animation_set.animations.length; i++) {
|
|
if(animation_name === this.animation_set.animations[i].name) {
|
|
this.current_animation = this.animation_set.animations[i];
|
|
return;
|
|
}
|
|
}
|
|
console.error("Can't set current animation: " + animation_name + " does not exist");
|
|
}
|
|
|
|
setFPS(fps) {
|
|
this._frame_duration = 1.0/fps;
|
|
}
|
|
|
|
play(animation_name) {
|
|
if(typeof animation_name === 'undefined') {
|
|
if(this.play_state === PLAY_STATE.PLAY) {
|
|
return;
|
|
} else if(this.play_state === PLAY_STATE.PAUSE) {
|
|
this.play_state = PLAY_STATE.PLAY;
|
|
} else if(this.play_state === PLAY_STATE.STOP) {
|
|
this.play_state = PLAY_STATE.PLAY;
|
|
this.interval_id = window.setInterval(() => this._update(), this._frame_duration * 1000);
|
|
}
|
|
return;
|
|
}
|
|
|
|
let animations = this.animation_set.animations;
|
|
for(var i = 0; i < animations.length; i++) {
|
|
if(animations[i].name === animation_name) {
|
|
this.current_animation = animations[i];
|
|
if(this.play_state === PLAY_STATE.PLAY) {
|
|
return;
|
|
} else if(this.play_state === PLAY_STATE.PAUSE) {
|
|
this.play_state = PLAY_STATE.PLAY;
|
|
} else if(this.play_state === PLAY_STATE.STOP) {
|
|
this.play_state = PLAY_STATE.PLAY;
|
|
this.interval_id = window.setInterval(() => this._update(), this._frame_duration * 1000);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
console.error("Can't play animation: " + animation_name + " does not exist");
|
|
}
|
|
|
|
_clearUpdateInterval() {
|
|
clearInterval(this.interval_id);
|
|
this.interval_id = -1;
|
|
}
|
|
|
|
_update() {
|
|
if(this.play_state === PLAY_STATE.PLAY)
|
|
this.setTime(this.current_time + this._frame_duration * this.speed);
|
|
}
|
|
|
|
setPercent(percent) {
|
|
if(percent < 0.0) {
|
|
percent = 0.0;
|
|
}
|
|
else if(percent > 1.0) {
|
|
percent = 1.0;
|
|
}
|
|
let time = this.current_animation.duration * percent;
|
|
this.setTime(time);
|
|
}
|
|
|
|
setTime(current_time) {
|
|
this.current_time = current_time;
|
|
if(this.speed > 0) {
|
|
if(this.current_time > this.current_animation.duration) {
|
|
if(this.loop_type === LOOP_TYPE.CLAMP) {
|
|
this.current_time = this.current_animation.duration;
|
|
} else if(this.loop_type === LOOP_TYPE.LOOP) {
|
|
this.current_time = 0;
|
|
}
|
|
}
|
|
} else if(this.speed < 0) {
|
|
if(this.current_time < 0) {
|
|
if(this.loop_type === LOOP_TYPE.CLAMP) {
|
|
this.current_time = 0;
|
|
} else if(this.loop_type === LOOP_TYPE.LOOP) {
|
|
this.current_time = this.current_animation.duration;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for(var track_name in this.current_animation.tracks) {
|
|
let track = this.current_animation.tracks[track_name];
|
|
let node = this.animation_set.nodes[track_name];
|
|
let curr_trans_keys = this.getKeysAtTime(track.translation_keys, this.current_time);
|
|
let curr_rot_keys = this.getKeysAtTime(track.rotation_keys, this.current_time);
|
|
let curr_scale_keys = this.getKeysAtTime(track.scale_keys, this.current_time);
|
|
|
|
//--------------------------
|
|
// Translation
|
|
//--------------------------
|
|
if(typeof curr_trans_keys !== 'undefined' && curr_trans_keys.length > 0) {
|
|
let orig_trans = node.translation;
|
|
let invMat = node.inv_rotation_matrix;
|
|
|
|
if(curr_trans_keys[0].time == curr_trans_keys[1].time) {
|
|
let result = new Cesium.Cartesian3(curr_trans_keys[0].value[0] - orig_trans[0], curr_trans_keys[0].value[1] - orig_trans[1], curr_trans_keys[0].value[2] - orig_trans[2]);
|
|
//get the result expressed in local node space
|
|
Cesium.Matrix3.multiplyByVector(invMat, result, result);
|
|
this.entity.model.nodeTransformations[track_name].translation = result;
|
|
} else {
|
|
let keyDelta = curr_trans_keys[1].time - curr_trans_keys[0].time;
|
|
let timeDelta = this.current_time - curr_trans_keys[0].time;
|
|
let t = timeDelta/keyDelta;
|
|
let start = new Cesium.Cartesian3(curr_trans_keys[0].value[0], curr_trans_keys[0].value[1], curr_trans_keys[0].value[2]);
|
|
let end = new Cesium.Cartesian3(curr_trans_keys[1].value[0], curr_trans_keys[1].value[1], curr_trans_keys[1].value[2]);
|
|
|
|
//interpolate the translation keys
|
|
let result = new Cesium.Cartesian3();
|
|
Cesium.Cartesian3.lerp(start, end, t, result);
|
|
|
|
//account for delta / relative offset from original translation
|
|
result.x -= orig_trans[0];
|
|
result.y -= orig_trans[1];
|
|
result.z -= orig_trans[2];
|
|
|
|
//get the result expressed in local node space
|
|
Cesium.Matrix3.multiplyByVector(invMat, result, result);
|
|
|
|
this.entity.model.nodeTransformations[track_name].translation = result;
|
|
}
|
|
}
|
|
|
|
//--------------------------
|
|
// Rotation
|
|
//--------------------------
|
|
if(typeof curr_rot_keys !== 'undefined' && curr_rot_keys.length > 0) {
|
|
|
|
let orig_inv = node.inv_rotation;
|
|
let invMat = node.inv_rotation_matrix;
|
|
|
|
if(curr_rot_keys[0].time == curr_rot_keys[1].time) {
|
|
let result = new Cesium.Quaternion(curr_rot_keys[0].value[0], curr_rot_keys[0].value[1], curr_rot_keys[0].value[2], curr_rot_keys[0].value[3]);
|
|
|
|
//isolate the axis
|
|
let resultAxis = new Cesium.Cartesian3(1,0,0);
|
|
let resultAngle = Cesium.Quaternion.computeAngle(result);
|
|
if(Math.abs(resultAngle) > Cesium.Math.EPSILON5)
|
|
Cesium.Quaternion.computeAxis(result, resultAxis);
|
|
|
|
//transform to local node space
|
|
Cesium.Matrix3.multiplyByVector(invMat, resultAxis, resultAxis);
|
|
|
|
//get the new quaternion expressed in local node space
|
|
Cesium.Quaternion.fromAxisAngle(resultAxis, resultAngle, result);
|
|
//calc the rotation delta/difference
|
|
Cesium.Quaternion.multiply(result, orig_inv, result);
|
|
this.entity.model.nodeTransformations[track_name].rotation = result;
|
|
} else {
|
|
let keyDelta = curr_rot_keys[1].time - curr_rot_keys[0].time;
|
|
let timeDelta = this.current_time - curr_rot_keys[0].time;
|
|
let t = timeDelta/keyDelta;
|
|
let start = new Cesium.Quaternion(curr_rot_keys[0].value[0], curr_rot_keys[0].value[1], curr_rot_keys[0].value[2], curr_rot_keys[0].value[3]);
|
|
let end = new Cesium.Quaternion(curr_rot_keys[1].value[0], curr_rot_keys[1].value[1], curr_rot_keys[1].value[2], curr_rot_keys[1].value[3]);
|
|
|
|
//slerp the rotation keys
|
|
let result = new Cesium.Quaternion();
|
|
Cesium.Quaternion.slerp(start, end, t, result);
|
|
|
|
//isolate the axis
|
|
let resultAxis = new Cesium.Cartesian3(1,0,0);
|
|
let resultAngle = Cesium.Quaternion.computeAngle(result);
|
|
if(Math.abs(resultAngle) > Cesium.Math.EPSILON5)
|
|
Cesium.Quaternion.computeAxis(result, resultAxis);
|
|
|
|
//transform to local node space
|
|
Cesium.Matrix3.multiplyByVector(invMat, resultAxis, resultAxis);
|
|
|
|
//get the new quaternion expressed in local node space
|
|
Cesium.Quaternion.fromAxisAngle(resultAxis, resultAngle, result);
|
|
|
|
//calc the rotation delta/difference
|
|
Cesium.Quaternion.multiply(result, orig_inv, result);
|
|
|
|
this.entity.model.nodeTransformations[track_name].rotation = result;
|
|
}
|
|
}
|
|
|
|
//--------------------------
|
|
// Scale
|
|
//--------------------------
|
|
if(typeof curr_scale_keys !== 'undefined' && curr_scale_keys.length > 0) {
|
|
let orig_scale = this.animation_set.nodes[track_name].scale;
|
|
|
|
if(curr_scale_keys[0].time == curr_scale_keys[1].time) {
|
|
let result = new Cesium.Cartesian3(curr_scale_keys[0].value[0] / orig_scale[0], curr_scale_keys[0].value[1] / orig_scale[1], curr_scale_keys[0].value[2] / orig_scale[2]);
|
|
this.entity.model.nodeTransformations[track_name].scale = result;
|
|
} else {
|
|
let keyDelta = curr_scale_keys[1].time - curr_scale_keys[0].time;
|
|
let timeDelta = this.current_time - curr_scale_keys[0].time;
|
|
let t = timeDelta/keyDelta;
|
|
let start = new Cesium.Cartesian3(curr_scale_keys[0].value[0], curr_scale_keys[0].value[1], curr_scale_keys[0].value[2]);
|
|
let end = new Cesium.Cartesian3(curr_scale_keys[1].value[0], curr_scale_keys[1].value[1], curr_scale_keys[1].value[2]);
|
|
let result = new Cesium.Cartesian3();
|
|
Cesium.Cartesian3.lerp(start, end, t, result);
|
|
|
|
//account for delta / relative offset from original scale
|
|
result.x /= orig_scale[0];
|
|
result.y /= orig_scale[1];
|
|
result.z /= orig_scale[2];
|
|
this.entity.model.nodeTransformations[track_name].scale = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getKeysAtTime(keys, time) {
|
|
let result = [];
|
|
if(keys.length == 0)
|
|
return result;
|
|
|
|
//we need to return some value even if the first key for this track isn't reached quite yet
|
|
if(keys[0].time > time) {
|
|
result.push(keys[0]);
|
|
result.push(keys[0]);
|
|
return result;
|
|
}
|
|
|
|
//just clamp the last key if we are at the end
|
|
if(time > keys[keys.length-1].time) {
|
|
result.push(keys[keys.length-1]);
|
|
result.push(keys[keys.length-1]);
|
|
return result;
|
|
}
|
|
|
|
for(var i = 0; i < keys.length-1; i++) {
|
|
if(keys[i].time <= time && keys[i+1].time >= time) {
|
|
result.push(keys[i]);
|
|
result.push(keys[i+1]);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
stop() {
|
|
this.play_state = PLAY_STATE.STOP;
|
|
this.current_time = 0;
|
|
//reset the node transforms on the entity to the default pose
|
|
let cesium_nodes = {};
|
|
for(var node_name in this.animation_set.nodes) {
|
|
cesium_nodes[node_name] = {
|
|
translation: new Cesium.Cartesian3(0, 0, 0),
|
|
rotation: new Cesium.Cartesian4(0, 0, 0, 1),
|
|
scale: new Cesium.Cartesian3(1, 1, 1)
|
|
}
|
|
}
|
|
this.entity.model.nodeTransformations = cesium_nodes;
|
|
this._clearUpdateInterval();
|
|
|
|
}
|
|
|
|
pause() {
|
|
//no need to pause if we are not playing
|
|
if(this.play_state === PLAY_STATE.PLAY)
|
|
this.play_state = PLAY_STATE.PAUSE;
|
|
this._clearUpdateInterval();
|
|
}
|
|
}
|
|
|
|
export class AnimationParser {
|
|
static _readFileAsync(file) {
|
|
return new Promise((resolve, reject) => {
|
|
let reader = new FileReader();
|
|
|
|
reader.onload = () => {
|
|
resolve(reader.result);
|
|
};
|
|
|
|
reader.onerror = reject;
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
});
|
|
}
|
|
|
|
static _getResourceAsync(uri) {
|
|
return new Promise((resolve, reject) => {
|
|
var req = new Request(uri);
|
|
|
|
fetch(req).then(function(response) {
|
|
if (!response.ok) {
|
|
reject(new Error(response.statusText));
|
|
}
|
|
return response;
|
|
}).then(function(response) {
|
|
resolve(response.arrayBuffer());
|
|
});
|
|
});
|
|
}
|
|
|
|
static parseAnimationNodesFromArrayBuffer(array_buffer) {
|
|
// get the length of the JSON data starting at 12 byte offset according to gltf standard
|
|
let dv = new DataView(array_buffer, 12, 4);
|
|
// don't forget to set little-endian = true when parsing from data view (gltf standard!)
|
|
let json_chunk_length = dv.getUint32(0, true);
|
|
console.log("gltf JSON length: " + json_chunk_length + " bytes");
|
|
|
|
// get the actual JSON data itself
|
|
let json_data_chunk = array_buffer.slice(20, 20+json_chunk_length);
|
|
let decoder = new TextDecoder('UTF-8');
|
|
let json_text = decoder.decode(json_data_chunk);
|
|
let gltf_json = JSON.parse(json_text);
|
|
console.log("gltf JSON loaded successfully:");
|
|
|
|
// store links to parent nodes
|
|
for(var i = 0; i < gltf_json.nodes.length; i++) {
|
|
if(typeof gltf_json.nodes[i].children != 'undefined') {
|
|
for(var k = 0; k < gltf_json.nodes[i].children.length; k++) {
|
|
gltf_json.nodes[gltf_json.nodes[i].children[k]].parent = gltf_json.nodes[i].name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return gltf_json.nodes;
|
|
}
|
|
|
|
static parseAnimationsFromArrayBuffer(array_buffer) {
|
|
let animations = [];
|
|
|
|
// get the length of the JSON data starting at 12 byte offset according to gltf standard
|
|
let dv = new DataView(array_buffer, 12, 4);
|
|
// don't forget to set little-endian = true when parsing from data view (gltf tandard!)
|
|
let json_chunk_length = dv.getUint32(0, true);
|
|
console.log("gltf JSON length: " + json_chunk_length + " bytes");
|
|
|
|
// get the actual JSON data itself
|
|
let json_data_chunk = array_buffer.slice(20, 20+json_chunk_length);
|
|
let decoder = new TextDecoder('UTF-8');
|
|
let json_text = decoder.decode(json_data_chunk);
|
|
let gltf_json = JSON.parse(json_text);
|
|
console.log("gltf JSON loaded successfully:");
|
|
console.log(gltf_json);
|
|
|
|
// get the length of the gltf embedded binary data
|
|
let bin_offset = 20+json_chunk_length;
|
|
dv = new DataView(array_buffer, bin_offset, 4);
|
|
let bin_chunk_length = dv.getUint32(0, true);
|
|
console.log("gltf bin length: " + bin_chunk_length + " bytes");
|
|
|
|
// get the actual binary data, we add 8 to get past the "type" and "chunk length" headers
|
|
let bin_data_chunk = array_buffer.slice(bin_offset + 8, bin_offset + 8 + bin_chunk_length);
|
|
|
|
//--------------------------------------------------
|
|
// get and process all animations
|
|
//--------------------------------------------------
|
|
if(typeof gltf_json.animations === 'undefined')
|
|
return [];
|
|
for(var i = 0; i < gltf_json.animations.length; i++) {
|
|
let anim_name = gltf_json.animations[i].name;
|
|
if(typeof anim_name == 'undefined' || anim_name == "")
|
|
anim_name = "animation_" + i;
|
|
let curr_animation = new Animation(anim_name);
|
|
console.log("processing animation: " + anim_name);
|
|
|
|
for(var k = 0; k < gltf_json.animations[i].channels.length; k++) {
|
|
let channel = gltf_json.animations[i].channels[k];
|
|
|
|
// the following will be either "translation, rotation, or scale"
|
|
let dof_type = channel.target.path;
|
|
|
|
let node = gltf_json.nodes[channel.target.node];
|
|
if(typeof node == 'undefined') {
|
|
console.warn("node is undefined for channel " + k);
|
|
continue;
|
|
}
|
|
|
|
let node_name = node.name;
|
|
if(typeof node_name == 'undefined' || node.name == "") {
|
|
node_name = "node_" + channel.target.node;
|
|
}
|
|
|
|
// add a new track to this animation for the node if it does not exist already
|
|
if(typeof curr_animation.tracks[node_name] == 'undefined')
|
|
curr_animation.tracks[node_name] = new AnimationTrack();
|
|
|
|
let sampler = gltf_json.animations[i].samplers[channel.sampler];
|
|
|
|
//--------------------------------------------------
|
|
// get input accessor (keyframe times for this channel's sampler) and process the data for it
|
|
//--------------------------------------------------
|
|
let input = gltf_json.accessors[sampler.input];
|
|
//console.log("min: " + input.min + " max: " + input.max);
|
|
|
|
let input_accessor_byte_offset = (typeof input.byteOffset == 'undefined' ? 0 : input.byteOffset);
|
|
if(input.componentType != 5126)
|
|
console.warn("input component type is not a float!");
|
|
|
|
// each element (keyframe timestamp) is a 4 byte float
|
|
let input_element_size = 4;
|
|
|
|
//use the buffer view and accessor to offset into the binary buffer to retrieve our data
|
|
let input_bufferView = gltf_json.bufferViews[input.bufferView];
|
|
let input_accessor_data_offset = input_bufferView.byteOffset + input_accessor_byte_offset;
|
|
let input_bin = bin_data_chunk.slice(input_accessor_data_offset, input_accessor_data_offset + input_element_size * input.count);
|
|
let input_dv = new DataView(input_bin);
|
|
|
|
// parse and store each timestamp out of the buffer
|
|
let timestamps = [];
|
|
for(var j = 0; j < input.count; j++) {
|
|
let timestamp = input_dv.getFloat32(j*4, true);
|
|
if(timestamp > curr_animation.duration) {
|
|
curr_animation.duration = timestamp;
|
|
}
|
|
timestamps.push(timestamp);
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// get output accessor (keyframe values for this channel's sampler) and process the data for it
|
|
//--------------------------------------------------
|
|
let output = gltf_json.accessors[sampler.output];
|
|
|
|
let output_accessor_byte_offset = (typeof output.byteOffset == 'undefined' ? 0 : output.byteOffset);
|
|
|
|
// we only care about VEC3 and VEC4 since we are only dealing with rotation, scale, and translation,
|
|
// and we are going to assume they are floating point (componentType = 5126 according to gltf spec)
|
|
if(output.componentType != 5126)
|
|
console.warn("output component type is not a float!");
|
|
|
|
let output_component_count = (output.type == "VEC3" ? 3 : 4);
|
|
// 4 byte floats in according to gltf spec
|
|
let output_element_size = output_component_count * 4;
|
|
|
|
//use the buffer view and accessor to offset into the binary buffer to retrieve our value data
|
|
let output_bufferView = gltf_json.bufferViews[output.bufferView];
|
|
let output_accessor_data_offset = output_bufferView.byteOffset + output_accessor_byte_offset;
|
|
let output_bin = bin_data_chunk.slice(output_accessor_data_offset, output_accessor_data_offset + output_element_size * output.count);
|
|
let output_dv = new DataView(output_bin);
|
|
|
|
// parse and store each value
|
|
let values = [];
|
|
for(var j = 0; j < output.count * output_component_count; j += output_component_count) {
|
|
let value = [];
|
|
for(var l = 0; l < output_component_count; l++) {
|
|
value.push(output_dv.getFloat32(j*4 + l*4, true));
|
|
}
|
|
values.push(value);
|
|
}
|
|
|
|
if(dof_type == "translation") {
|
|
for(var j = 0; j < output.count; j++) {
|
|
curr_animation.tracks[node_name].translation_keys.push(new AnimationKey(timestamps[j], values[j]));
|
|
}
|
|
} else if(dof_type == "rotation") {
|
|
for(var j = 0; j < output.count; j++) {
|
|
curr_animation.tracks[node_name].rotation_keys.push(new AnimationKey(timestamps[j], values[j]));
|
|
}
|
|
} else if(dof_type == "scale") {
|
|
for(var j = 0; j < output.count; j++) {
|
|
curr_animation.tracks[node_name].scale_keys.push(new AnimationKey(timestamps[j], values[j]));
|
|
}
|
|
}
|
|
}
|
|
animations.push(curr_animation);
|
|
}
|
|
return animations;
|
|
}
|
|
|
|
static async parseAnimationSetFromUri(glb_uri) {
|
|
let array_buffer = await this._getResourceAsync(glb_uri);
|
|
return this._parseAnimationSetFromArrayBuffer(array_buffer);
|
|
}
|
|
|
|
static async parseAnimationSetFromFile(glb_file) {
|
|
let array_buffer = await this._readFileAsync(glb_file);
|
|
return this._parseAnimationSetFromArrayBuffer(array_buffer);
|
|
}
|
|
|
|
static _parseAnimationSetFromArrayBuffer(array_buffer) {
|
|
let animation_nodes = AnimationParser.parseAnimationNodesFromArrayBuffer(array_buffer);
|
|
// convert nodes to dictionary format
|
|
let nodes_dict = {};
|
|
for(var i = 0; i < animation_nodes.length; i++) {
|
|
nodes_dict[animation_nodes[i].name] = animation_nodes[i];
|
|
|
|
//if the node defines its TRS info as a matrix, we need to capture that (see glTF 2.0 spec)
|
|
if(typeof animation_nodes[i].matrix !== 'undefined') {
|
|
let mat = new Cesium.Matrix4();
|
|
Cesium.Matrix4.fromColumnMajorArray(animation_nodes[i].matrix, mat);
|
|
nodes_dict[animation_nodes[i].name].matrix = mat;
|
|
}
|
|
|
|
//set default values for translation rotation and scale if they do not exist
|
|
if(typeof nodes_dict[animation_nodes[i].name].translation === 'undefined')
|
|
nodes_dict[animation_nodes[i].name].translation = [0,0,0];
|
|
|
|
if(typeof nodes_dict[animation_nodes[i].name].rotation === 'undefined') {
|
|
nodes_dict[animation_nodes[i].name].rotation = [0,0,0,1];
|
|
nodes_dict[animation_nodes[i].name].inv_rotation_matrix = Cesium.Matrix3.IDENTITY;
|
|
nodes_dict[animation_nodes[i].name].inv_rotation = new Cesium.Quaternion(0,0,0,1);
|
|
}
|
|
else {
|
|
//compute and store the inverse rotation matrix and quaternion for future calculations
|
|
let orig_rot = nodes_dict[animation_nodes[i].name].rotation;
|
|
let orig_quat = new Cesium.Quaternion(orig_rot[0], orig_rot[1], orig_rot[2], orig_rot[3]);
|
|
let orig_quat_inv = new Cesium.Quaternion();
|
|
Cesium.Quaternion.inverse(orig_quat, orig_quat_inv);
|
|
let invMat = new Cesium.Matrix3();
|
|
Cesium.Matrix3.fromQuaternion(orig_quat_inv, invMat);
|
|
nodes_dict[animation_nodes[i].name].inv_rotation_matrix = invMat;
|
|
nodes_dict[animation_nodes[i].name].inv_rotation = orig_quat_inv;
|
|
}
|
|
|
|
if(typeof nodes_dict[animation_nodes[i].name].scale === 'undefined')
|
|
nodes_dict[animation_nodes[i].name].scale = [0,0,0];
|
|
}
|
|
|
|
let animations = AnimationParser.parseAnimationsFromArrayBuffer(array_buffer);
|
|
console.log(nodes_dict);
|
|
return new AnimationSet(animations, nodes_dict);
|
|
}
|
|
};
|