https://github.com/drohmer/velocity_skinning_replicability
Tip revision: 2f0c554555dabbde29a200f683a6d8e82de3b4e0 authored by Damien Rohmer on 14 April 2021, 18:58:00 UTC
update
update
Tip revision: 2f0c554
scene.js
"use strict";
let K0 = 1;
initEmptyScene(sceneElements);
load3DObjects(sceneElements.sceneGraph);
requestAnimationFrame( computeFrame );
// Set up resize window
window.addEventListener('resize', resizeWindow);
function changeWireframe(value)
{
if(value===true) {
for(let name in sceneElements.models)
{
sceneElements.models[name]["mesh"].material = sceneElements.param.materialWireframe;
}
}
else{
for(let name in sceneElements.models)
{
sceneElements.models[name]["mesh"].material = sceneElements.models[name]["material"];
}
}
}
function changeDisplaySkeleton(value)
{
for(let name in sceneElements.models)
{
for(let k in sceneElements.models[name]["mesh"]["children"]){
sceneElements.models[name]["mesh"]["children"][k].visible = value;
}
}
}
function changeTexture(value)
{
for(let name in sceneElements.models)
{
if(value==false) {
sceneElements.models[name]["mesh"].material.map = sceneElements.param.textureWhite;
sceneElements.models[name]["mesh"].material.color = new THREE.Color("rgb(220, 220, 220)");
}
else{
sceneElements.models[name]["mesh"].material.map = sceneElements.models[name].texture;
sceneElements.models[name]["mesh"].material.color = new THREE.Color("rgb(255, 255, 255)");
}
}
}
function changeProps(value)
{
for(let element in sceneElements.props)
{
const model = sceneElements.props[element];
if(value==false) {
model.visible = false;
}
else{
model.visible = true;
}
}
if(value==false) {
sceneElements.renderer.setClearColor('rgb(255, 255, 255)', 1.0);
}
else{
sceneElements.renderer.setClearColor('rgb(200, 200, 255)', 1.0);
}
}
function changeWeight(value)
{
for(let name in sceneElements.models)
{
const model = sceneElements.models[name]["mesh"];
model.material.vertexColors = false;
if(value==true) {
model.material.vertexColors = true;
}
else{
model.material.vertexColors = false;
}
model.material.needsUpdate = true
}
}
function quaternion_to_axis_angle(q, axis, angle)
{
axis.set(q.x, q.y, q.z);
const n = axis.length();
if(n<1e-6)
{
axis.set(1,0,0);
angle = 0.0;
}
else{
angle = 2 * Math.atan2(n, q.w);
axis.divideScalar(n);
}
}
function quaternion_to_angular_speed(q, W)
{
W.set(q.x, q.y, q.z);
const n = W.length();
if(n<1e-6)
{
W.set(0,0,0);
}
else{
const angle = 2 * Math.atan2(n, q.w);
W.divideScalar(n);
W.multiplyScalar(angle);
}
}
let avg_dt = 0.0;
let previous_time = 0;
gui.close();
function computeFrame( time ) {
const dt = time-previous_time;
previous_time = time;
if(time>2000)
{
avg_dt = 0.9*avg_dt + 0.1*dt;
}
else{
avg_dt = 1/60;
}
sceneElements.timer.current = sceneElements.timer.current + 1/50;
if(sceneElements.timer.current>sceneElements.timer.interval[1]){
sceneElements.timer.current = sceneElements.timer.interval[0];
}
for(let name in sceneElements.models)
{
const model = sceneElements.models[name];
let ready = true;
if(!model["mesh"] || !model["animation"] || !model["skeleton"] || !model["skeleton_current"] || !model["velocity_skinning"]) { ready=false; }
//if(model["rig"] && model["mesh"] && !model["rig"]["vertex_depending_on_joint"]) {build_hierarchy_dependency(model);}
if(ready)
{
if(model["visible"]===false){
model["mesh"].visible = false;
continue;
}
else{
model["mesh"].visible = true;
}
interpolate_skeleton_at_time(sceneElements.timer.current, model["animation"], model["skeleton_current"], model["skeleton"]["parent_id"]);
const N_joint = model["skeleton_current"]["position_local"].length;
const R_parent = new THREE.Quaternion(0,0,0,1);
for(let k=0; k<N_joint; ++k)
{
const p1 = model["skeleton_current"]["position_local"][k];
const p0 = model["velocity_skinning"]["speed_tracker"][k].last_position;
const new_speed = new THREE.Vector3();
new_speed.copy(p1);
new_speed.sub(p0);
new_speed.divideScalar(1/60.0);
model["velocity_skinning"]["speed_tracker"][k].avg_speed.multiplyScalar(0.75);
new_speed.multiplyScalar(1-0.75);
model["velocity_skinning"]["speed_tracker"][k].avg_speed.add(new_speed);
model["velocity_skinning"]["speed_tracker"][k].last_position.copy(p1);
const q1 = model["skeleton_current"]["rotation_local"][k];
const q0 = model["velocity_skinning"]["rotation_tracker"][k].last_position;
const new_rotation_speed = new THREE.Quaternion();
new_rotation_speed.copy(q0);
new_rotation_speed.conjugate();
new_rotation_speed.premultiply(q1);
const new_rotation_speed_vec = new THREE.Vector3();
quaternion_to_angular_speed(new_rotation_speed, new_rotation_speed_vec);
new_rotation_speed_vec.divideScalar(1/60.0);
new_rotation_speed_vec.multiplyScalar(1-0.75);
model["velocity_skinning"]["rotation_tracker"][k].avg_speed.multiplyScalar(0.75);
model["velocity_skinning"]["rotation_tracker"][k].avg_speed.add(new_rotation_speed_vec);
model["velocity_skinning"]["rotation_tracker"][k].last_position.copy(q1);
if(k>0)
{
const parent = model["skeleton"]["parent_id"][k];
R_parent.copy( model["skeleton_current"]["rotation_global"][parent] );
}
model["velocity_skinning"]["speed_tracker"][k].current_speed.copy(model["velocity_skinning"]["speed_tracker"][k].avg_speed);
model["velocity_skinning"]["speed_tracker"][k].current_speed.applyQuaternion(R_parent);
model["velocity_skinning"]["rotation_tracker"][k].current_speed.copy(model["velocity_skinning"]["rotation_tracker"][k].avg_speed);
model["velocity_skinning"]["rotation_tracker"][k].current_speed.applyQuaternion(R_parent);
}
// if(K0<150 && name==="cylinder"){
// console.log(model["rotation_tracker"][1].current_speed);
// }
computeSkinning(model["vertex_skinning"], model["rig"], model["vertex_rest_pose"], model["skeleton"], model["skeleton_current"]);
if(sceneElements.param['DeformOnly'])
{
computeSkinningInverse(model["vertex_skinning_inverse"], model["rig"], model["vertex_skinning"], model["skeleton"], model["skeleton_current"]);
for(let k=0, N=model["vertex_skinning"].length; k<N; ++k) {
model["vertex_skinning"][k].copy(model["vertex_skinning_inverse"][k]);
}
}
//computeSkinning(model["vertex_skinning_inverse"], model["rig"], model["vertex_skinning"], model["skeleton_current"], model["skeleton"]);
// computeSkinningInverse(model["vertex_skinning_inverse"], model["rig"], model["vertex_skinning"], model["skeleton"], model["skeleton_current"]);
for(let k=0; k<N_joint; ++k)
{
const p_joint = model["skeleton_current"]["position_global"][k];
model["skeleton"]["visual_sphere"][k].position.copy(p_joint);
}
for(let k=1; k<N_joint; ++k)
{
const parent = model["skeleton"]["parent_id"][k];
const p1 = model["skeleton_current"]["position_global"][k];
const p0 = model["skeleton_current"]["position_global"][parent];
model["skeletonSegments"][k].geometry.vertices[0].copy(p0);
model["skeletonSegments"][k].geometry.vertices[1].copy(p1);
}
const N_vertex = model["vertex_skinning"].length;
for(let k=0; k<N_vertex; ++k){
model["mesh"].geometry.vertices[k].copy( model["vertex_skinning"][k] );
}
velocitySkinning(model);
for(let k=0, N=model["vertex_skinning"].length; k<N; ++k) {
model["mesh"].geometry.vertices[k].copy(model["vertex_velocity_skinning"][k]);
}
model["mesh"].geometry.verticesNeedUpdate = true;
model["mesh"].geometry.computeVertexNormals()
model["mesh"].geometry.normalsNeedUpdate = true;
const N_children = model["mesh"]["children"].length;
for(let k=0; k<N_children; ++k) {
const child = model["mesh"]["children"][k];
if(child.type==="Line"){
child.geometry.verticesNeedUpdate = true;
}
}
}
}
K0 = K0+1;
composer
render( sceneElements );
sceneElements.control.update();
requestAnimationFrame( computeFrame );
}
// Update render image size and camera aspect when the window is resized
function resizeWindow(eventParam) {
const width = window.innerWidth;
const height = window.innerHeight;
sceneElements.camera.aspect = width / height;
sceneElements.camera.updateProjectionMatrix();
sceneElements.renderer.setSize(width, height);
}
function render(sceneElements) {
if(sceneElements.param.ssao===true){
composer.render();
}
else{
sceneElements.renderer.render(sceneElements.sceneGraph, sceneElements.camera);
}
}
// Create and insert in the scene graph the shapes of the 3D scene
function load3DObjects(sceneGraph) {
const loader = new THREE.TextureLoader();
sceneElements.param.textureWhite = loader.load("assets/texture_white.png");
gui.add(sceneElements.param, 'Activated').name("VelocitySkinning");
gui.add(sceneElements.param, 'DeformOnly').name("RestShape");
let obj = { Select:function(){viewAllScene()} };
gui.add(obj,'Select').name( 'Select All');
gui.add(sceneElements.param, 'Flappy', 0, 3.0);
gui.add(sceneElements.param, 'Squashy', 0, 3.0);
const Id = new THREE.Quaternion();
const R = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0),-3.14/2.0);
const R_cow = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),-5*3.14/6.0) . multiply(R);
loadAsset("cow", [-2,0.75,2], 1.0, R_cow, false);
sceneElements.models["cow"].param["translation"] = [-2,0.75,2];
sceneElements.models["cow"].param["squashy"] = 0.2;
//const R_cow_up = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),-7*3.14/6.0);
//loadAsset("cow_up", [3,1.3,2], 1.0, R_cow_up);
const R_dragon = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),-3.14/4.0) . multiply(R);
loadAsset("dragon", [2.0,1.05,-0.5], 0.8, R_dragon, false);
sceneElements.models["dragon"].param["translation"] = [2.0,0.5,-0.5];
sceneElements.models["dragon"].param["squashy"] = 0;
//const R_dragon2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),-3.14/4.0) . multiply(R);
//loadAsset("dragon2", [2.5,0.9,-0.5], 0.7, R_dragon);
// loadAsset("cylinder", [0,0,0], R);
loadAsset("giraffe", [1,0,3], 1.3, Id, true);
sceneElements.models["giraffe"]["param"]["flappy"] = 0.7;
sceneElements.models["giraffe"].param["translation"] = [1,1.3,3];
sceneElements.models["giraffe"].param["squashy"] = 0.1;
const R_bird = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),-5*3.14/7.0).multiply(R);
loadAsset("bird", [1.5,2.5,0], 1.0, R_bird);
sceneElements.models["bird"]["param"]["squashy"] = 0.5;
sceneElements.models["bird"]["param"]["flappy"] = 0.5;
sceneElements.models["bird"].param["translation"] = [1.5,2.5,0];
const R_sphere = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),3.14/2.0);
loadAsset("sphere", [-0.5,0.35,0], 1.6, R_sphere, false);
sceneElements.models["sphere"]["param"]["flappy"] = 0.0;
sceneElements.models["sphere"]["param"]["squashy"] = 1.25;
sceneElements.models["sphere"].param["translation"] = [-0.5,0.35,0];
//loadAsset("snail", [2,0.75,2], Id);
let fbx_loader = new THREE.FBXLoader();
fbx_loader.load( 'assets/flower/sunflower.fbx', function ( object ) {
object.scale.copy(new THREE.Vector3(0.3,0.3,0.3));
object.translateY(-1.0).translateZ(-2.5).translateX(-2.25);
sceneGraph.add( object );
sceneElements.props["flower"]=object;
} );
fbx_loader.load( 'assets/tree/Boom_Final.fbx', function ( object ) {
object.scale.copy(new THREE.Vector3(0.004,0.004,0.004));
object.translateY(-1.0).translateZ(-3.5).translateX(1.5);
sceneGraph.add( object );
const loader = new THREE.TextureLoader();
const texture = loader.load('assets/tree/Boom_Final_UV.png');
const R = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0),3.14/2.0+3.14/8.0);
object.children[0].material.map = texture;
object.children[0].castShadow = true;
object.setRotationFromQuaternion(R);
sceneElements.props["tree"]=object;
} );
gui.add(sceneElements.timer, "current", 0, sceneElements.timer["interval"][1]).listen().name("Time Cycle");
const folderDisplay = gui.addFolder(`Display`)
const materialWireframe = new THREE.MeshBasicMaterial( {color:'rgb(255, 255, 255)', side:THREE.DoubleSide, wireframe:true} );
sceneElements.param["materialWireframe"] = materialWireframe;
folderDisplay.add(sceneElements.param, "Wireframe").listen().onChange(changeWireframe);
folderDisplay.add(sceneElements.param, "displaySkeleton").name("Skeleton").listen().onChange(changeDisplaySkeleton);
folderDisplay.add(sceneElements.param, "texture").listen().onChange(changeTexture);
folderDisplay.add(sceneElements.param, "background").listen().onChange(changeProps);
folderDisplay.add(sceneElements.param, "weights").name("Floppy weights").listen().onChange(changeWeight);
folderDisplay.add(sceneElements.param, "ssao").name("SSAO");
console.log(sceneElements);
sceneElements.timer.current = sceneElements.timer.interval[0];
loadVisualElementHelper(sceneGraph);
createBasicElements(sceneGraph);
}