123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <title>Three.js CCDIKSolver Browser</title>
- <link rel="shortcut icon" href="../../files/favicon.ico" />
- <link rel="stylesheet" type="text/css" href="../../files/main.css">
- <style>
- canvas {
- display: block;
- width: 100%;
- height: 100%;
- }
- #newWindow {
- display: block;
- position: absolute;
- bottom: 0.3em;
- left: 0.5em;
- color: #fff;
- }
- </style>
- </head>
- <body>
- <script type="importmap">
- {
- "imports": {
- "three": "../../build/three.module.js",
- "three/addons/": "../../examples/jsm/"
- }
- }
- </script>
- <a id='newWindow' href='./ccdiksolver-browser.html' target='_blank'>Open in New Window</a>
- <script type="module">
- //
- // Forked from /docs/api/en/objects/SkinnedMesh example
- //
- import {
- Bone,
- Color,
- CylinderGeometry,
- DoubleSide,
- Float32BufferAttribute,
- MeshPhongMaterial,
- PerspectiveCamera,
- Scene,
- SkinnedMesh,
- Skeleton,
- SkeletonHelper,
- Vector3,
- Uint16BufferAttribute,
- WebGLRenderer
- } from 'three';
- import { CCDIKSolver, CCDIKHelper } from 'three/addons/animation/CCDIKSolver.js';
- import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
- import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
- let gui, scene, camera, renderer, orbit, mesh, bones, skeletonHelper, ikSolver;
- const state = {
- ikSolverAutoUpdate: true
- };
- function initScene() {
- gui = new GUI();
- scene = new Scene();
- scene.background = new Color( 0x444444 );
- camera = new PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 200 );
- camera.position.z = 30;
- camera.position.y = 30;
- renderer = new WebGLRenderer( { antialias: true } );
- renderer.setPixelRatio( window.devicePixelRatio );
- renderer.setSize( window.innerWidth, window.innerHeight );
- document.body.appendChild( renderer.domElement );
- orbit = new OrbitControls( camera, renderer.domElement );
- orbit.enableZoom = false;
- window.addEventListener( 'resize', function () {
- camera.aspect = window.innerWidth / window.innerHeight;
- camera.updateProjectionMatrix();
- renderer.setSize( window.innerWidth, window.innerHeight );
- }, false );
- initBones();
- setupDatGui();
- }
- function createGeometry( sizing ) {
- const geometry = new CylinderGeometry(
- 5, // radiusTop
- 5, // radiusBottom
- sizing.height, // height
- 8, // radiusSegments
- sizing.segmentCount * 1, // heightSegments
- true // openEnded
- );
- const position = geometry.attributes.position;
- const vertex = new Vector3();
- const skinIndices = [];
- const skinWeights = [];
- for ( let i = 0; i < position.count; i ++ ) {
- vertex.fromBufferAttribute( position, i );
- const y = ( vertex.y + sizing.halfHeight );
- const skinIndex = Math.floor( y / sizing.segmentHeight );
- const skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;
- skinIndices.push( skinIndex, skinIndex + 1, 0, 0 );
- skinWeights.push( 1 - skinWeight, skinWeight, 0, 0 );
- }
- geometry.setAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) );
- geometry.setAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) );
- return geometry;
- }
- function createBones( sizing ) {
- bones = [];
- // "root bone"
- const rootBone = new Bone();
- rootBone.name = 'root';
- rootBone.position.y = - sizing.halfHeight;
- bones.push( rootBone );
- //
- // "bone0", "bone1", "bone2", "bone3"
- //
- // "bone0"
- let prevBone = new Bone();
- prevBone.position.y = 0;
- rootBone.add( prevBone );
- bones.push( prevBone );
- // "bone1", "bone2", "bone3"
- for ( let i = 1; i <= sizing.segmentCount; i ++ ) {
- const bone = new Bone();
- bone.position.y = sizing.segmentHeight;
- bones.push( bone );
- bone.name = `bone${i}`;
- prevBone.add( bone );
- prevBone = bone;
- }
- // "target"
- const targetBone = new Bone();
- targetBone.name = 'target';
- targetBone.position.y = sizing.height + sizing.segmentHeight; // relative to parent: rootBone
- rootBone.add( targetBone );
- bones.push( targetBone );
- return bones;
- }
- function createMesh( geometry, bones ) {
- const material = new MeshPhongMaterial( {
- color: 0x156289,
- emissive: 0x072534,
- side: DoubleSide,
- flatShading: true,
- wireframe: true
- } );
- const mesh = new SkinnedMesh( geometry, material );
- const skeleton = new Skeleton( bones );
- mesh.add( bones[ 0 ] );
- mesh.bind( skeleton );
- skeletonHelper = new SkeletonHelper( mesh );
- skeletonHelper.material.linewidth = 2;
- scene.add( skeletonHelper );
- return mesh;
- }
- function setupDatGui() {
- gui.add( mesh, 'pose' ).name( 'mesh.pose()' );
- mesh.skeleton.bones
- .filter( ( bone ) => bone.name === 'target' )
- .forEach( function ( bone ) {
- const folder = gui.addFolder( bone.name );
- const delta = 20;
- folder.add( bone.position, 'x', - delta + bone.position.x, delta + bone.position.x );
- folder.add( bone.position, 'y', - bone.position.y, bone.position.y );
- folder.add( bone.position, 'z', - delta + bone.position.z, delta + bone.position.z );
- } );
- gui.add( ikSolver, 'update' ).name( 'ikSolver.update()' );
- gui.add( state, 'ikSolverAutoUpdate' );
- }
- function initBones() {
- const segmentHeight = 8;
- const segmentCount = 3;
- const height = segmentHeight * segmentCount;
- const halfHeight = height * 0.5;
- const sizing = {
- segmentHeight,
- segmentCount,
- height,
- halfHeight
- };
- const geometry = createGeometry( sizing );
- const bones = createBones( sizing );
- mesh = createMesh( geometry, bones );
- scene.add( mesh );
- //
- // ikSolver
- //
- const iks = [
- {
- target: 5,
- effector: 4,
- links: [ { index: 3 }, { index: 2 }, { index: 1 } ]
- }
- ];
- ikSolver = new CCDIKSolver( mesh, iks );
- scene.add( new CCDIKHelper( mesh, iks ) );
- }
- function render() {
- requestAnimationFrame( render );
- if ( state.ikSolverAutoUpdate ) {
- ikSolver?.update();
- }
- renderer.render( scene, camera );
- }
- initScene();
- render();
- </script>
- </body>
- </html>
|