DragControls with GLTF Animations
GLTF models exported with animations, often don't work with the DragControls, so one approach to solve the problem, is to create a secondary object, such as a cube, and bind the DragControls to that instead. Then adjust the position of your model group to the new position of the cube being dragged.
In this example, you can drag the animated GLTF model using the DragControls.
Start Scripts
./src/server/server.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import express from "express" import path from "path" import http from "http" const port: number = 3000 class App { private server: http.Server private port: number constructor(port: number) { this.port = port const app = express() app.use(express.static(path.join(__dirname, '../client'))) app.use('/build/three.module.js', express.static(path.join(__dirname, '../../node_modules/three/build/three.module.js'))) app.use('/jsm/controls/OrbitControls', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/controls/OrbitControls.js'))) app.use('/jsm/loaders/GLTFLoader', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/loaders/GLTFLoader.js'))) app.use('/jsm/libs/stats.module', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/libs/stats.module.js'))) app.use('/jsm/controls/DragControls', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/controls/DragControls.js'))) this.server = new http.Server(app); } public Start() { this.server.listen(this.port, () => { console.log( `Server listening on port ${this.port}.` ) }) } } new App(port).Start() |
./src/client/client.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import * as THREE from '/build/three.module.js' import { OrbitControls } from '/jsm/controls/OrbitControls' import { GLTFLoader } from '/jsm/loaders/GLTFLoader' import Stats from '/jsm/libs/stats.module' import { DragControls } from '/jsm/controls/DragControls' const scene: THREE.Scene = new THREE.Scene() const axesHelper = new THREE.AxesHelper(5) scene.add(axesHelper) var light1 = new THREE.PointLight(); light1.position.set(2.5, 2.5, 2.5) light1.castShadow = true scene.add(light1); var light2 = new THREE.PointLight(); light2.position.set(-2.5, 2.5, 2.5) light2.castShadow = true scene.add(light2); const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000) camera.position.set(0.8, 1.4, 1.0) const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer() renderer.setSize(window.innerWidth, window.innerHeight) renderer.shadowMap.enabled = true document.body.appendChild(renderer.domElement) const orbitControls = new OrbitControls(camera, renderer.domElement) orbitControls.screenSpacePanning = true orbitControls.target.set(0, 1, 0) const sceneMeshes: THREE.Mesh[] = new Array() let boxHelper: THREE.BoxHelper const dragControls = new DragControls(sceneMeshes, camera, renderer.domElement) dragControls.addEventListener("hoveron", function (event) { boxHelper.visible = true orbitControls.enabled = false }); dragControls.addEventListener("hoveroff", function (event) { boxHelper.visible = false orbitControls.enabled = true }); dragControls.addEventListener("drag", function (event) { event.object.position.y = 0 }); dragControls.addEventListener('dragstart', function (event) { boxHelper.visible = true orbitControls.enabled = false }) dragControls.addEventListener('dragend', function (event) { boxHelper.visible = false orbitControls.enabled = true }) const planeGeometry: THREE.PlaneGeometry = new THREE.PlaneGeometry(25, 25) const texture = new THREE.TextureLoader().load("img/grid.png") const plane: THREE.Mesh = new THREE.Mesh(planeGeometry, new THREE.MeshPhongMaterial({ map: texture })) plane.rotateX(-Math.PI / 2) plane.receiveShadow = true scene.add(plane) let mixer: THREE.AnimationMixer let modelReady = false; const gltfLoader: GLTFLoader = new GLTFLoader(); let modelGroup: THREE.Group let modelDragBox: THREE.Mesh gltfLoader.load( 'models/eve@punching.glb', (gltf) => { gltf.scene.traverse(function (child) { if (child instanceof THREE.Group) { modelGroup = child } if ((<THREE.Mesh>child).isMesh) { child.castShadow = true child.frustumCulled = false; (child as THREE.Mesh).geometry.computeVertexNormals() } }); mixer = new THREE.AnimationMixer(gltf.scene); mixer.clipAction((gltf as any).animations[0]).play() modelDragBox = new THREE.Mesh(new THREE.BoxGeometry(.5, 1.3, .5), new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })) modelDragBox.geometry.translate(0, .65, 0) scene.add(modelDragBox) sceneMeshes.push(modelDragBox); boxHelper = new THREE.BoxHelper(modelDragBox, 0xffff00 ) boxHelper.visible = false scene.add(boxHelper) scene.add(gltf.scene) modelReady = true }, (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded') }, (error) => { console.log(error); } ) window.addEventListener('resize', onWindowResize, false) function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) render() } const stats = Stats() document.body.appendChild(stats.dom) const clock: THREE.Clock = new THREE.Clock() var animate = function () { requestAnimationFrame(animate) orbitControls.update() if (modelReady) { mixer.update(clock.getDelta()); modelGroup.position.copy(modelDragBox.position) boxHelper.update() } render() stats.update() }; function render() { renderer.render(scene, camera) } animate(); |