Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New animation / skinning / ik example #24652

Merged
merged 26 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/examples/en/animations/CCDIKSolver.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ <h2>Code Example</h2>
<h2>Examples</h2>

<p>
[example:webgl_animation_skinning_ik]<br />
Mugen87 marked this conversation as resolved.
Show resolved Hide resolved
[example:webgl_loader_mmd]<br />
[example:webgl_loader_mmd_pose]<br />
[example:webgl_loader_mmd_audio]
Expand Down
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"webgl_animation_keyframes",
"webgl_animation_skinning_blending",
"webgl_animation_skinning_additive_blending",
"webgl_animation_skinning_ik",
"webgl_animation_skinning_morph",
"webgl_animation_multiple",
"webgl_camera",
Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/animation/CCDIKSolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv )
*/
class CCDIKHelper extends Object3D {

constructor( mesh, iks = [] ) {
constructor( mesh, iks = [], sphereSize = 0.25 ) {

super();

Expand All @@ -293,7 +293,7 @@ class CCDIKHelper extends Object3D {
this.matrix.copy( mesh.matrixWorld );
this.matrixAutoUpdate = false;

this.sphereGeometry = new SphereGeometry( 0.25, 16, 8 );
this.sphereGeometry = new SphereGeometry( sphereSize, 16, 8 );

this.targetSphereMaterial = new MeshBasicMaterial( {
color: new Color( 0xff8888 ),
Expand Down
Binary file added examples/models/gltf/kira.glb
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
220 changes: 220 additions & 0 deletions examples/webgl_animation_skinning_ik.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - animation - skinning - ik</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="author" content="Antoine BERNIER (abernier)" />
<link type="text/css" rel="stylesheet" href="main.css">
<style>
body {color:white;}
#info a {
color:inherit;
}
</style>
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - <a href="https://assetstore.unity.com/packages/3d/characters/humanoids/humans/kira-lowpoly-character-100303" target="_blank" rel="noopener">Kira</a>'s "<a href="https://en.wikipedia.org/wiki/Hand_with_Reflecting_Sphere" target="_blank" rel="noopener">hand with Reflecting Sphere</a>"<br />
(see <a href="https://abernier.name/three.js/examples/webgl_esher.html" target="_blank" rel="noopener">full-experiment</a>)
</div>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">
import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { CCDIKSolver, CCDIKHelper } from './jsm/animation/CCDIKSolver.js';
import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

let scene, camera, renderer, orbitControls, transformControls;
let mirrorSphereCamera;

const OOI = {};
let IKSolver;

let stats, gui, conf;
const v0 = new THREE.Vector3();

init().then( animate );

async function init() {

conf = {
followSphere: false,
turnHead: true,
ik_solver: true,
};

scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xffffff, .17 );
scene.background = new THREE.Color( 0xdddddd );

camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.001, 5000 );
camera.position.set( 0.9728517749133652, 1.1044765132727201, 0.7316689528482836 );
camera.lookAt( scene.position );

const ambientLight = new THREE.AmbientLight( 0xffffff, 8 ); // soft white light
scene.add( ambientLight );

renderer = new THREE.WebGLRenderer( { antialias: true, logarithmicDepthBuffer: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.physicallyCorrectLights = true;
document.body.appendChild( renderer.domElement );

stats = new Stats();
document.body.appendChild( stats.dom );

orbitControls = new OrbitControls( camera, renderer.domElement );
orbitControls.minDistance = .2;
orbitControls.maxDistance = 1.5;
orbitControls.enableDamping = true;

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'js/libs/draco/' );
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader( dracoLoader );

const gltf = await gltfLoader.loadAsync( 'models/gltf/kira.glb' );
gltf.scene.traverse( n => {

if ( n.name === 'head' ) OOI.head = n;
if ( n.name === 'lowerarm_l' ) OOI.lowerarm_l = n;
if ( n.name === 'Upperarm_l' ) OOI.Upperarm_l = n;
if ( n.name === 'hand_l' ) OOI.hand_l = n;
if ( n.name === 'target_hand_l' ) OOI.target_hand_l = n;

if ( n.name === 'boule' ) OOI.sphere = n;
if ( n.name === 'Kira_Shirt' ) OOI.kira = n;

if ( n.isMesh ) n.frustumCulled = false;

} );
scene.add( gltf.scene );

orbitControls.target.copy( OOI.sphere.position ); // orbit controls lookAt the sphere
OOI.hand_l.attach( OOI.sphere );

// mirror sphere cube-camera
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 1024 );
mirrorSphereCamera = new THREE.CubeCamera( 0.05, 50, cubeRenderTarget );
scene.add( mirrorSphereCamera );
const mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: cubeRenderTarget.texture } );
OOI.sphere.material = mirrorSphereMaterial;

transformControls = new TransformControls( camera, renderer.domElement );
transformControls.size = .75;
transformControls.showX = false;
transformControls.space = 'world';
transformControls.attach( OOI.target_hand_l );
scene.add( transformControls );
// disable orbitControls while using transformControls
transformControls.addEventListener( 'mouseDown', () => orbitControls.enabled = false );
transformControls.addEventListener( 'mouseUp', () => orbitControls.enabled = true );

OOI.kira.add( OOI.kira.skeleton.bones[ 0 ] );
const iks = [
{
target: 25, // "target_hand_l"
effector: 9, // "hand_l"
links: [
{
index: 8, // "lowerarm_l"
rotationMin: new THREE.Vector3( 1.2, - 1.8, - .4 ),
rotationMax: new THREE.Vector3( 1.7, - 1.1, .3 )
},
{
index: 7, // "Upperarm_l"
rotationMin: new THREE.Vector3( 0.1, - 0.7, - 1.8 ),
rotationMax: new THREE.Vector3( 1.1, 0, - 1.4 )
},
],
}
];
IKSolver = new CCDIKSolver( OOI.kira, iks );
const ccdikhelper = new CCDIKHelper( OOI.kira, iks, 0.01 );
scene.add( ccdikhelper );

gui = new GUI();
gui.add( conf, 'followSphere' ).name( 'follow sphere' );
gui.add( conf, 'turnHead' ).name( 'turn head' );
gui.add( conf, 'ik_solver' ).name( 'IK auto update' );
gui.add( IKSolver, 'update' ).name( 'IK manual update()' );
gui.open();

window.addEventListener( 'resize', onWindowResize, false );

}

function animate( ) {

if ( OOI.sphere && mirrorSphereCamera ) {

OOI.sphere.visible = false;
OOI.sphere.getWorldPosition( mirrorSphereCamera.position );
mirrorSphereCamera.update( renderer, scene );
OOI.sphere.visible = true;

}

if ( OOI.sphere && conf.followSphere ) {

// orbitControls follows the sphere
OOI.sphere.getWorldPosition( v0 );
Mugen87 marked this conversation as resolved.
Show resolved Hide resolved
orbitControls.target.lerp( v0, .1 );

}

if ( OOI.head && OOI.sphere && conf.turnHead ) {

// turn head
OOI.sphere.getWorldPosition( v0 );
OOI.head.lookAt( v0 );
OOI.head.rotation.set( OOI.head.rotation.x, OOI.head.rotation.y + Math.PI, OOI.head.rotation.z );

}

if ( conf.ik_solver ) {

IKSolver?.update();

}

orbitControls.update();
renderer.render( scene, camera );

stats.update(); // fps stats

requestAnimationFrame( animate );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}
</script>
</body>
</html>