bones-browser.html 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Three.js Bones Browser</title>
  6. <link rel="shortcut icon" href="../../files/favicon.ico" />
  7. <link rel="stylesheet" type="text/css" href="../../files/main.css">
  8. <style>
  9. canvas {
  10. display: block;
  11. width: 100%;
  12. height: 100%;
  13. }
  14. #newWindow {
  15. display: block;
  16. position: absolute;
  17. bottom: 0.3em;
  18. left: 0.5em;
  19. color: #fff;
  20. }
  21. </style>
  22. </head>
  23. <body>
  24. <a id='newWindow' href='./bones-browser.html' target='_blank'>Open in New Window</a>
  25. <script type="module">
  26. import {
  27. Bone,
  28. Color,
  29. CylinderBufferGeometry,
  30. DoubleSide,
  31. Float32BufferAttribute,
  32. MeshPhongMaterial,
  33. PerspectiveCamera,
  34. PointLight,
  35. Scene,
  36. SkinnedMesh,
  37. Skeleton,
  38. SkeletonHelper,
  39. Vector3,
  40. Uint16BufferAttribute,
  41. WebGLRenderer
  42. } from "../../build/three.module.js";
  43. import { GUI } from '../../examples/jsm/libs/dat.gui.module.js';
  44. import { OrbitControls } from '../../examples/jsm/controls/OrbitControls.js';
  45. var gui, scene, camera, renderer, orbit, lights, mesh, bones, skeletonHelper;
  46. var state = {
  47. animateBones: false
  48. };
  49. function initScene() {
  50. gui = new GUI();
  51. scene = new Scene();
  52. scene.background = new Color( 0x444444 );
  53. camera = new PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 200 );
  54. camera.position.z = 30;
  55. camera.position.y = 30;
  56. renderer = new WebGLRenderer( { antialias: true } );
  57. renderer.setPixelRatio( window.devicePixelRatio );
  58. renderer.setSize( window.innerWidth, window.innerHeight );
  59. document.body.appendChild( renderer.domElement );
  60. orbit = new OrbitControls( camera, renderer.domElement );
  61. orbit.enableZoom = false;
  62. lights = [];
  63. lights[ 0 ] = new PointLight( 0xffffff, 1, 0 );
  64. lights[ 1 ] = new PointLight( 0xffffff, 1, 0 );
  65. lights[ 2 ] = new PointLight( 0xffffff, 1, 0 );
  66. lights[ 0 ].position.set( 0, 200, 0 );
  67. lights[ 1 ].position.set( 100, 200, 100 );
  68. lights[ 2 ].position.set( - 100, - 200, - 100 );
  69. scene.add( lights[ 0 ] );
  70. scene.add( lights[ 1 ] );
  71. scene.add( lights[ 2 ] );
  72. window.addEventListener( 'resize', function () {
  73. camera.aspect = window.innerWidth / window.innerHeight;
  74. camera.updateProjectionMatrix();
  75. renderer.setSize( window.innerWidth, window.innerHeight );
  76. }, false );
  77. initBones();
  78. setupDatGui();
  79. }
  80. function createGeometry( sizing ) {
  81. var geometry = new CylinderBufferGeometry(
  82. 5, // radiusTop
  83. 5, // radiusBottom
  84. sizing.height, // height
  85. 8, // radiusSegments
  86. sizing.segmentCount * 3, // heightSegments
  87. true // openEnded
  88. );
  89. var position = geometry.attributes.position;
  90. var vertex = new Vector3();
  91. var skinIndices = [];
  92. var skinWeights = [];
  93. for ( var i = 0; i < position.count; i ++ ) {
  94. vertex.fromBufferAttribute( position, i );
  95. var y = ( vertex.y + sizing.halfHeight );
  96. var skinIndex = Math.floor( y / sizing.segmentHeight );
  97. var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;
  98. skinIndices.push( skinIndex, skinIndex + 1, 0, 0 );
  99. skinWeights.push( 1 - skinWeight, skinWeight, 0, 0 );
  100. }
  101. geometry.setAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) );
  102. geometry.setAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) );
  103. return geometry;
  104. }
  105. function createBones( sizing ) {
  106. bones = [];
  107. var prevBone = new Bone();
  108. bones.push( prevBone );
  109. prevBone.position.y = - sizing.halfHeight;
  110. for ( var i = 0; i < sizing.segmentCount; i ++ ) {
  111. var bone = new Bone();
  112. bone.position.y = sizing.segmentHeight;
  113. bones.push( bone );
  114. prevBone.add( bone );
  115. prevBone = bone;
  116. }
  117. return bones;
  118. }
  119. function createMesh( geometry, bones ) {
  120. var material = new MeshPhongMaterial( {
  121. skinning: true,
  122. color: 0x156289,
  123. emissive: 0x072534,
  124. side: DoubleSide,
  125. flatShading: true
  126. } );
  127. var mesh = new SkinnedMesh( geometry, material );
  128. var skeleton = new Skeleton( bones );
  129. mesh.add( bones[ 0 ] );
  130. mesh.bind( skeleton );
  131. skeletonHelper = new SkeletonHelper( mesh );
  132. skeletonHelper.material.linewidth = 2;
  133. scene.add( skeletonHelper );
  134. return mesh;
  135. }
  136. function setupDatGui() {
  137. var folder = gui.addFolder( "General Options" );
  138. folder.add( state, "animateBones" );
  139. folder.__controllers[ 0 ].name( "Animate Bones" );
  140. folder.add( mesh, "pose" );
  141. folder.__controllers[ 1 ].name( ".pose()" );
  142. var bones = mesh.skeleton.bones;
  143. for ( var i = 0; i < bones.length; i ++ ) {
  144. var bone = bones[ i ];
  145. folder = gui.addFolder( "Bone " + i );
  146. folder.add( bone.position, 'x', - 10 + bone.position.x, 10 + bone.position.x );
  147. folder.add( bone.position, 'y', - 10 + bone.position.y, 10 + bone.position.y );
  148. folder.add( bone.position, 'z', - 10 + bone.position.z, 10 + bone.position.z );
  149. folder.add( bone.rotation, 'x', - Math.PI * 0.5, Math.PI * 0.5 );
  150. folder.add( bone.rotation, 'y', - Math.PI * 0.5, Math.PI * 0.5 );
  151. folder.add( bone.rotation, 'z', - Math.PI * 0.5, Math.PI * 0.5 );
  152. folder.add( bone.scale, 'x', 0, 2 );
  153. folder.add( bone.scale, 'y', 0, 2 );
  154. folder.add( bone.scale, 'z', 0, 2 );
  155. folder.__controllers[ 0 ].name( "position.x" );
  156. folder.__controllers[ 1 ].name( "position.y" );
  157. folder.__controllers[ 2 ].name( "position.z" );
  158. folder.__controllers[ 3 ].name( "rotation.x" );
  159. folder.__controllers[ 4 ].name( "rotation.y" );
  160. folder.__controllers[ 5 ].name( "rotation.z" );
  161. folder.__controllers[ 6 ].name( "scale.x" );
  162. folder.__controllers[ 7 ].name( "scale.y" );
  163. folder.__controllers[ 8 ].name( "scale.z" );
  164. }
  165. }
  166. function initBones() {
  167. var segmentHeight = 8;
  168. var segmentCount = 4;
  169. var height = segmentHeight * segmentCount;
  170. var halfHeight = height * 0.5;
  171. var sizing = {
  172. segmentHeight: segmentHeight,
  173. segmentCount: segmentCount,
  174. height: height,
  175. halfHeight: halfHeight
  176. };
  177. var geometry = createGeometry( sizing );
  178. var bones = createBones( sizing );
  179. mesh = createMesh( geometry, bones );
  180. mesh.scale.multiplyScalar( 1 );
  181. scene.add( mesh );
  182. }
  183. function render() {
  184. requestAnimationFrame( render );
  185. var time = Date.now() * 0.001;
  186. //Wiggle the bones
  187. if ( state.animateBones ) {
  188. for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
  189. mesh.skeleton.bones[ i ].rotation.z = Math.sin( time ) * 2 / mesh.skeleton.bones.length;
  190. }
  191. }
  192. renderer.render( scene, camera );
  193. }
  194. initScene();
  195. render();
  196. </script>
  197. </body>
  198. </html>