Hi All,
After dabbling with R3F to load a GLTF animation I have come round full circle and back to Three.js and finally got my GLTF Camera animation to play.
Question 1:
I am now trying to make the animation play once and resume Orbit controls after single loop. Is this possible?
Question 2:
Is it possible to slow animations down or would it be better to edit the Blender file before exporting to GLTF?
Here is my code, any help is much appreciated:
<script type="module"> import * as THREE from "three"; import Stats from 'three/addons/libs/stats.js'; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { RGBELoader } from "three/addons/loaders/RGBELoader.js"; import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; let camera, scene, renderer; let raycaster, mouse, mixer, clock; init(); animate(); function init() { const container = document.createElement("div"); document.body.appendChild(container); // Camera camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 150 ); camera.position.set(-18, 1.8, 20); scene = new THREE.Scene(); clock = new THREE.Clock(); // setting up the onclick function raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2(); new RGBELoader() .setPath("textures/equirectangular/") .load("forest_cave_1k.hdr", function (texture) { texture.mapping = THREE.EquirectangularReflectionMapping; scene.background = texture; scene.environment = texture; // Loads GLTF model // Configure and create Draco decoder. const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath( 'jsm/libs/draco/' ); const loader = new GLTFLoader() loader.setDRACOLoader(dracoLoader) loader.load("models/gltf/MakerVerdeSchool/MakerVerde-Gallery-anitest.gltf", function (gltf) { gltf.scene.traverse(function (child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } scene.add(gltf.scene); camera = gltf.cameras[ 0 ]; scene.add( gltf.scene ); mixer = new THREE.AnimationMixer( gltf.scene ); gltf.animations.forEach( ( clip ) => { mixer.clipAction( clip ).play(); }); }); }); }); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.shadowMap.enabled = true; renderer.shadowMap.type = true; renderer.toneMappingExposure = 0.5; renderer.outputEncoding = THREE.sRGBEncoding; container.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); // controls.addEventListener("change", render); // use if there is no animation loop controls.minDistance = 1; controls.maxDistance = 1000; controls.target.set(0, 0, 0); controls.update(); window.addEventListener("resize", onWindowResize); renderer.domElement.addEventListener("click", onClick, false); } function onClick() { event.preventDefault(); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(scene.children, false); // if (intersects.length > 0) { // // console.log('intersection:', intersects[0]); // window.open(intersects[0].object.userData.name); // } if (!intersects[0].object.userData.name.includes("Object")) { window.open(intersects[0].object.userData.name); } } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // function animate() { requestAnimationFrame( animate ); var delta = clock.getDelta(); if ( mixer ) mixer.update( delta ); renderer.render( scene, camera ); } </script>