GLTF Animations
Video Lecture
Description
In this lesson, we will create the GLTF equivalent of the project created in the FBX animations lesson.
We use Blender to convert the main FBX model and it's related animation files, into GLB files.
Start Code
./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/libs/dat.gui.module', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/libs/dat.gui.module.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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | 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 { GUI } from '/jsm/libs/dat.gui.module' 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) scene.add(light1); var light2 = new THREE.PointLight(); light2.position.set(-2.5, 2.5, 2.5) 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) document.body.appendChild(renderer.domElement) const controls = new OrbitControls(camera, renderer.domElement) controls.screenSpacePanning = true controls.target.set(0, 1, 0) let mixer: THREE.AnimationMixer let modelReady = false; let animationActions: THREE.AnimationAction[] = new Array() let activeAction: THREE.AnimationAction let lastAction: THREE.AnimationAction const gltfLoader: GLTFLoader = new GLTFLoader(); gltfLoader.load( 'models/vanguard.glb', (gltf) => { // gltf.scene.scale.set(.01, .01, .01) mixer = new THREE.AnimationMixer(gltf.scene); let animationAction = mixer.clipAction((gltf as any).animations[0]); animationActions.push(animationAction) animationsFolder.add(animations, "default") activeAction = animationActions[0] scene.add(gltf.scene); //add an animation from another file gltfLoader.load('models/vanguard@samba.glb', (gltf) => { console.log("loaded samba") let animationAction = mixer.clipAction((gltf as any).animations[0]); animationActions.push(animationAction) animationsFolder.add(animations, "samba") //add an animation from another file gltfLoader.load('models/vanguard@bellydance.glb', (gltf) => { console.log("loaded bellydance") let animationAction = mixer.clipAction((gltf as any).animations[0]); animationActions.push(animationAction) animationsFolder.add(animations, "bellydance") //add an animation from another file gltfLoader.load('models/vanguard@goofyrunning.glb', (gltf) => { console.log("loaded goofyrunning"); (gltf as any).animations[0].tracks.shift() //delete the specific track that moves the object forward while running let animationAction = mixer.clipAction((gltf as any).animations[0]); animationActions.push(animationAction) animationsFolder.add(animations, "goofyrunning") modelReady = true }, (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded') }, (error) => { console.log(error); } ) }, (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded') }, (error) => { console.log(error); } ) }, (xhr) => { console.log((xhr.loaded / xhr.total * 100) + '% loaded') }, (error) => { console.log(error); } ) }, (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) var animations = { default: function () { setAction(animationActions[0]) }, samba: function () { setAction(animationActions[1]) }, bellydance: function () { setAction(animationActions[2]) }, goofyrunning: function () { setAction(animationActions[3]) }, } const setAction = (toAction: THREE.AnimationAction) => { if (toAction != activeAction) { lastAction = activeAction activeAction = toAction //lastAction.stop() lastAction.fadeOut(1) activeAction.reset() activeAction.fadeIn(1) activeAction.play() } } const gui = new GUI() const animationsFolder = gui.addFolder("Animations") animationsFolder.open() const clock: THREE.Clock = new THREE.Clock() var animate = function () { requestAnimationFrame(animate) controls.update() if (modelReady) mixer.update(clock.getDelta()); render() stats.update() }; function render() { renderer.render(scene, camera) } animate(); |