@@ -29,7 +29,7 @@ import { CopyShader } from "./jsm/shaders/CopyShader"
2929import { VerticalBlurShader } from "./jsm/shaders/VerticalBlurShader"
3030import { BufferGeometry } from "./core/BufferGeometry"
3131import { MeshStandardMaterial } from "./materials/MeshStandardMaterial"
32- import { BufferAttribute } from "./core/BufferAttribute"
32+ import { BufferAttribute , Float32BufferAttribute , Uint16BufferAttribute } from "./core/BufferAttribute"
3333import { SphereGeometry } from "./geometries/SphereGeometry"
3434import { MeshPhysicalMaterial } from "./materials/MeshPhysicalMaterial"
3535import { TextureLoader } from "./loaders/TextureLoader"
@@ -56,6 +56,11 @@ import { RectAreaLight } from "./lights/RectAreaLight"
5656
5757import { environments } from "./environments.js"
5858import { BokehPass } from "./jsm/postprocessing/BokehPass"
59+ import { MeshBasicMaterial } from "./materials/MeshBasicMaterial"
60+ import { CylinderGeometry } from "./geometries/CylinderGeometry"
61+ import { Bone } from "./objects/Bone"
62+ import { SkinnedMesh } from "./objects/SkinnedMesh"
63+ import { Skeleton } from "./objects/Skeleton"
5964
6065const loadingManager = new LoadingManager ( )
6166loadingManager . onProgress = ( url , loaded , total ) => {
@@ -276,6 +281,13 @@ export class App {
276281 vnh
277282 vth
278283
284+ sizing = {
285+ halfHeight : 2.5 ,
286+ segmentHeight : 1.0 ,
287+ segments : 5 ,
288+ }
289+ skeleton
290+
279291 constructor ( ) {
280292 this . createRenderer ( )
281293 this . createScene ( )
@@ -284,10 +296,11 @@ export class App {
284296 this . createClock ( )
285297 this . createControls ( )
286298 this . createLights ( )
287- // this.setupCube()
288- this . loadTexure ( )
289- this . loadMaterial ( )
290- this . loadModel ( )
299+ // this.createCube()
300+ this . createCylinder ( )
301+ // this.loadTexure()
302+ // this.loadMaterial()
303+ // this.loadModel()
291304 // this.loadGLTF()
292305 // this.loadHair()
293306 // this.loadJSON()
@@ -410,13 +423,72 @@ export class App {
410423 this . controls . screenSpacePanning = true
411424 }
412425
413- setupCube ( ) {
426+ createCube ( ) {
414427 const geometry = new BoxGeometry ( 1 , 1 , 1 )
415428 const material = new MeshPhongMaterial ( { color : 0x00ff00 } )
416429 this . cube = new Mesh ( geometry , material )
417430 this . scene . add ( this . cube )
418431 }
419432
433+ createCylinder ( ) {
434+ const geometry = new CylinderGeometry ( 5 , 5 , 5 , 5 , this . sizing . segments , true )
435+ const material = new MeshBasicMaterial ( {
436+ color : 0x156289 ,
437+ wireframe : true ,
438+ skinning : true ,
439+ } )
440+
441+ // 3. Tạo xương (bones) theo segment
442+ const bones = [ ]
443+ let prevBone = null
444+
445+ for ( let i = 0 ; i <= this . sizing . segments ; i ++ ) {
446+ const bone = new Bone ( )
447+ bone . position . y = i * this . sizing . segmentHeight
448+ if ( prevBone ) {
449+ prevBone . add ( bone )
450+ }
451+ bones . push ( bone )
452+ prevBone = bone
453+ }
454+
455+ // 4. Tạo skin indices và skin weights thủ công
456+ const position = geometry . attributes . position
457+ const vertex = new Vector3 ( )
458+
459+ const skinIndices = [ ]
460+ const skinWeights = [ ]
461+
462+ for ( let i = 0 ; i < position . count ; i ++ ) {
463+ vertex . fromBufferAttribute ( position , i )
464+
465+ // Chuyển vertex.y về hệ tọa độ 0 -> chiều cao hình trụ
466+ const y = vertex . y + this . sizing . halfHeight
467+
468+ const skinIndex = Math . floor ( y / this . sizing . segmentHeight )
469+ const skinWeight = ( y % this . sizing . segmentHeight ) / this . sizing . segmentHeight
470+
471+ // Mỗi vertex chịu ảnh hưởng bởi 2 bone liền kề
472+ skinIndices . push ( skinIndex , skinIndex + 1 , 0 , 0 )
473+ skinWeights . push ( 1 - skinWeight , skinWeight , 0 , 0 )
474+ }
475+
476+ geometry . setAttribute ( "skinIndex" , new Uint16BufferAttribute ( skinIndices , 4 ) )
477+ geometry . setAttribute ( "skinWeight" , new Float32BufferAttribute ( skinWeights , 4 ) )
478+
479+ // 5. Tạo SkinnedMesh và Skeleton
480+ const mesh = new SkinnedMesh ( geometry , material )
481+ this . skeleton = new Skeleton ( bones )
482+
483+ // Thêm xương gốc vào mesh
484+ mesh . add ( bones [ 0 ] )
485+
486+ // Gắn skeleton vào mesh
487+ mesh . bind ( this . skeleton )
488+
489+ this . scene . add ( mesh )
490+ }
491+
420492 createLights ( ) {
421493 // const ambientLight = new AmbientLight(0xffffff, 0.63)
422494 // this.scene.add(ambientLight)
@@ -1418,6 +1490,12 @@ export class App {
14181490 this . meshFolder = this . gui . addFolder ( "Mesh" )
14191491 this . meshFolder . close ( )
14201492 // this.gui.close()
1493+
1494+ const skeletonFolder = this . gui . addFolder ( "Skeleton" )
1495+ skeletonFolder . close ( )
1496+ skeletonFolder . add ( this . sizing , "halfHeight" , - 10 , 10 , 0.01 ) . name ( "halfHeight" )
1497+ skeletonFolder . add ( this . sizing , "segmentHeight" , - 10 , 10 , 0.01 ) . name ( "segmentHeight" )
1498+ skeletonFolder . add ( this . sizing , "segments" , - 10 , 10 , 0.01 ) . name ( "segments" )
14211499 }
14221500
14231501 addHelpers ( ) {
@@ -1525,7 +1603,7 @@ export class App {
15251603
15261604 getEnvironmentTexture ( environment ) {
15271605 const { id, path } = environment
1528- // neutral (THREE. RoomEnvironment)
1606+ // neutral (RoomEnvironment)
15291607 // if (id === "neutral") {
15301608 // return Promise.resolve({ envMap: this.neutralEnvironment })
15311609 // }
@@ -1617,9 +1695,13 @@ export class App {
16171695 // // Rotate the cube
16181696 // this.cube.rotation.x += 0.01
16191697 // this.cube.rotation.y += 0.01
1698+ if ( this . skeleton ) {
1699+ this . skeleton . bones [ 0 ] . rotation . x = Math . sin ( Date . now ( ) * 0.001 ) * 0.3
1700+ this . skeleton . bones [ 1 ] . rotation . x = Math . sin ( Date . now ( ) * 0.001 + 1 ) * 0.5
1701+ }
16201702
1703+ this . controls . update ( )
16211704 if ( this . controls ) {
1622- this . controls . update ( )
16231705 }
16241706
16251707 if ( this . state . postProcessing ) {
0 commit comments