You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							295 lines
						
					
					
						
							7.0 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							295 lines
						
					
					
						
							7.0 KiB
						
					
					
				
								<!DOCTYPE html>
							 | 
						|
								<html lang="en">
							 | 
						|
									<head>
							 | 
						|
										<meta charset="utf-8">
							 | 
						|
										<title>Three.js Bones 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>
							 | 
						|
								
							 | 
						|
										<!-- 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/": "../../examples/jsm/"
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										</script>
							 | 
						|
								
							 | 
						|
										<a id='newWindow' href='./bones-browser.html' target='_blank'>Open in New Window</a>
							 | 
						|
								
							 | 
						|
										<script type="module">
							 | 
						|
											import {
							 | 
						|
												Bone,
							 | 
						|
												Color,
							 | 
						|
												CylinderGeometry,
							 | 
						|
												DoubleSide,
							 | 
						|
												Float32BufferAttribute,
							 | 
						|
												MeshPhongMaterial,
							 | 
						|
												PerspectiveCamera,
							 | 
						|
												PointLight,
							 | 
						|
												Scene,
							 | 
						|
												SkinnedMesh,
							 | 
						|
												Skeleton,
							 | 
						|
												SkeletonHelper,
							 | 
						|
												Vector3,
							 | 
						|
												Uint16BufferAttribute,
							 | 
						|
												WebGLRenderer
							 | 
						|
											} from 'three';
							 | 
						|
								
							 | 
						|
											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, lights, mesh, bones, skeletonHelper;
							 | 
						|
								
							 | 
						|
											const state = {
							 | 
						|
												animateBones: false
							 | 
						|
											};
							 | 
						|
								
							 | 
						|
											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;
							 | 
						|
								
							 | 
						|
												lights = [];
							 | 
						|
												lights[ 0 ] = new PointLight( 0xffffff, 1, 0 );
							 | 
						|
												lights[ 1 ] = new PointLight( 0xffffff, 1, 0 );
							 | 
						|
												lights[ 2 ] = new PointLight( 0xffffff, 1, 0 );
							 | 
						|
								
							 | 
						|
												lights[ 0 ].position.set( 0, 200, 0 );
							 | 
						|
												lights[ 1 ].position.set( 100, 200, 100 );
							 | 
						|
												lights[ 2 ].position.set( - 100, - 200, - 100 );
							 | 
						|
								
							 | 
						|
												scene.add( lights[ 0 ] );
							 | 
						|
												scene.add( lights[ 1 ] );
							 | 
						|
												scene.add( lights[ 2 ] );
							 | 
						|
								
							 | 
						|
												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 * 3, // 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 = [];
							 | 
						|
								
							 | 
						|
												let prevBone = new Bone();
							 | 
						|
												bones.push( prevBone );
							 | 
						|
												prevBone.position.y = - sizing.halfHeight;
							 | 
						|
								
							 | 
						|
												for ( let i = 0; i < sizing.segmentCount; i ++ ) {
							 | 
						|
								
							 | 
						|
													const bone = new Bone();
							 | 
						|
													bone.position.y = sizing.segmentHeight;
							 | 
						|
													bones.push( bone );
							 | 
						|
													prevBone.add( bone );
							 | 
						|
													prevBone = bone;
							 | 
						|
								
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												return bones;
							 | 
						|
								
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											function createMesh( geometry, bones ) {
							 | 
						|
								
							 | 
						|
												const material = new MeshPhongMaterial( {
							 | 
						|
													color: 0x156289,
							 | 
						|
													emissive: 0x072534,
							 | 
						|
													side: DoubleSide,
							 | 
						|
													flatShading: 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() {
							 | 
						|
								
							 | 
						|
												let folder = gui.addFolder( 'General Options' );
							 | 
						|
								
							 | 
						|
												folder.add( state, 'animateBones' );
							 | 
						|
												folder.controllers[ 0 ].name( 'Animate Bones' );
							 | 
						|
								
							 | 
						|
												folder.add( mesh, 'pose' );
							 | 
						|
												folder.controllers[ 1 ].name( '.pose()' );
							 | 
						|
								
							 | 
						|
												const bones = mesh.skeleton.bones;
							 | 
						|
								
							 | 
						|
												for ( let i = 0; i < bones.length; i ++ ) {
							 | 
						|
								
							 | 
						|
													const bone = bones[ i ];
							 | 
						|
								
							 | 
						|
													folder = gui.addFolder( 'Bone ' + i );
							 | 
						|
								
							 | 
						|
													folder.add( bone.position, 'x', - 10 + bone.position.x, 10 + bone.position.x );
							 | 
						|
													folder.add( bone.position, 'y', - 10 + bone.position.y, 10 + bone.position.y );
							 | 
						|
													folder.add( bone.position, 'z', - 10 + bone.position.z, 10 + bone.position.z );
							 | 
						|
								
							 | 
						|
													folder.add( bone.rotation, 'x', - Math.PI * 0.5, Math.PI * 0.5 );
							 | 
						|
													folder.add( bone.rotation, 'y', - Math.PI * 0.5, Math.PI * 0.5 );
							 | 
						|
													folder.add( bone.rotation, 'z', - Math.PI * 0.5, Math.PI * 0.5 );
							 | 
						|
								
							 | 
						|
													folder.add( bone.scale, 'x', 0, 2 );
							 | 
						|
													folder.add( bone.scale, 'y', 0, 2 );
							 | 
						|
													folder.add( bone.scale, 'z', 0, 2 );
							 | 
						|
								
							 | 
						|
													folder.controllers[ 0 ].name( 'position.x' );
							 | 
						|
													folder.controllers[ 1 ].name( 'position.y' );
							 | 
						|
													folder.controllers[ 2 ].name( 'position.z' );
							 | 
						|
								
							 | 
						|
													folder.controllers[ 3 ].name( 'rotation.x' );
							 | 
						|
													folder.controllers[ 4 ].name( 'rotation.y' );
							 | 
						|
													folder.controllers[ 5 ].name( 'rotation.z' );
							 | 
						|
								
							 | 
						|
													folder.controllers[ 6 ].name( 'scale.x' );
							 | 
						|
													folder.controllers[ 7 ].name( 'scale.y' );
							 | 
						|
													folder.controllers[ 8 ].name( 'scale.z' );
							 | 
						|
								
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											function initBones() {
							 | 
						|
								
							 | 
						|
												const segmentHeight = 8;
							 | 
						|
												const segmentCount = 4;
							 | 
						|
												const height = segmentHeight * segmentCount;
							 | 
						|
												const halfHeight = height * 0.5;
							 | 
						|
								
							 | 
						|
												const sizing = {
							 | 
						|
													segmentHeight: segmentHeight,
							 | 
						|
													segmentCount: segmentCount,
							 | 
						|
													height: height,
							 | 
						|
													halfHeight: halfHeight
							 | 
						|
												};
							 | 
						|
								
							 | 
						|
												const geometry = createGeometry( sizing );
							 | 
						|
												const bones = createBones( sizing );
							 | 
						|
												mesh = createMesh( geometry, bones );
							 | 
						|
								
							 | 
						|
												mesh.scale.multiplyScalar( 1 );
							 | 
						|
												scene.add( mesh );
							 | 
						|
								
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											function render() {
							 | 
						|
								
							 | 
						|
												requestAnimationFrame( render );
							 | 
						|
								
							 | 
						|
												const time = Date.now() * 0.001;
							 | 
						|
								
							 | 
						|
												//Wiggle the bones
							 | 
						|
												if ( state.animateBones ) {
							 | 
						|
								
							 | 
						|
													for ( let i = 0; i < mesh.skeleton.bones.length; i ++ ) {
							 | 
						|
								
							 | 
						|
														mesh.skeleton.bones[ i ].rotation.z = Math.sin( time ) * 2 / mesh.skeleton.bones.length;
							 | 
						|
								
							 | 
						|
													}
							 | 
						|
								
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												renderer.render( scene, camera );
							 | 
						|
								
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											initScene();
							 | 
						|
											render();
							 | 
						|
								
							 | 
						|
										</script>
							 | 
						|
									</body>
							 | 
						|
								</html>
							 | 
						|
								
							 |