198 lines
6.7 KiB
JavaScript
198 lines
6.7 KiB
JavaScript
import * as THREE from 'three'
|
|
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
|
|
|
|
class HelperBot{
|
|
|
|
constructor(canvasSelector){
|
|
this.renderer = null
|
|
this.canvasEl = document.querySelector(canvasSelector)
|
|
this.initScene()
|
|
window.bot=this
|
|
}
|
|
|
|
initScene(){
|
|
|
|
this.canvasEl .width = window.innerWidth
|
|
this.canvasEl .height = window.innerHeight
|
|
|
|
this.scene = new THREE.Scene()
|
|
|
|
this.camera = new THREE.PerspectiveCamera(30, this.canvasEl.clientWidth / this.canvasEl.clientHeight, 0.1, 1000)
|
|
this.camera.position.set(0, 1, 10)
|
|
this.camera.lookAt(0, 0, 0)
|
|
|
|
// Lights
|
|
const dLight1 = new THREE.DirectionalLight(0xffffff, 1.5)
|
|
dLight1.position.set(5, 5, 5)
|
|
this.scene.add(dLight1)
|
|
const dLight2 = new THREE.DirectionalLight(0xffffff, 1.5)
|
|
dLight2.position.set(-5, -5, 5)
|
|
this.scene.add(dLight2)
|
|
this.scene.add(new THREE.AmbientLight(0xffffff, 1))
|
|
|
|
|
|
|
|
|
|
this.renderer = new THREE.WebGLRenderer({
|
|
canvas: this.canvasEl ,
|
|
alpha: true
|
|
})
|
|
|
|
this.renderer.setSize(this.canvasEl .width, this.canvasEl .height)
|
|
this.renderer.setPixelRatio(window.devicePixelRatio)
|
|
this.renderer.setClearColor(0x000000, 0)
|
|
|
|
|
|
this.threeClock = new THREE.Clock()
|
|
const loader = new GLTFLoader().setPath('/app/assets/3dModels/')
|
|
loader.load('mecha2.glb', async gltf => {
|
|
this.mecha = new THREE.Group()
|
|
this.scene.add(this.mecha)
|
|
|
|
this.mechaModel = gltf.scene
|
|
this.mecha.add(this.mechaModel)
|
|
|
|
this.mechaModel.scale.set(5, 5, 5)
|
|
this.mechaModel.rotation.set(0, 0, 0)
|
|
this.mechaModel.position.set(0, -1.5, 0)
|
|
this.mechaModel.updateMatrix()
|
|
this.mechaModel.rotateY(-Math.PI) // 180deg so facing user
|
|
|
|
// GPU compilation (optimization)
|
|
await this.renderer.compileAsync(this.mechaModel, this.camera, this.scene)
|
|
|
|
this.mechaModel.traverse(obj => {
|
|
if (obj.isMesh && obj.material && obj.material.color) {
|
|
const c = obj.material.color.clone()
|
|
const boost = 0.1
|
|
obj.material.emissive = c
|
|
obj.material.emissiveIntensity = boost
|
|
}
|
|
})
|
|
|
|
if (gltf.animations && gltf.animations.length > 0) {
|
|
this.mixer = new THREE.AnimationMixer(this.mechaModel)
|
|
const action = this.mixer.clipAction(gltf.animations[0])
|
|
action.play()
|
|
}
|
|
|
|
|
|
this.leftArm = this.mechaModel.getObjectByName('ArmL1_01')
|
|
this.animate = true
|
|
})
|
|
|
|
|
|
this.resizeWindow()
|
|
window.addEventListener('resize', this.resizeWindow.bind(this))
|
|
this.render()
|
|
|
|
}
|
|
|
|
resizeWindow() {
|
|
const w = window.innerWidth
|
|
const h = window.innerHeight
|
|
|
|
this.canvasEl .width = w
|
|
this.canvasEl .height = h
|
|
|
|
this.renderer.setSize(w, h)
|
|
this.renderer.setPixelRatio(window.devicePixelRatio)
|
|
|
|
this.camera.aspect = w / h
|
|
this.camera.updateProjectionMatrix()
|
|
}
|
|
|
|
startRendering(){
|
|
this.addControls()
|
|
this.render()
|
|
}
|
|
|
|
render() {
|
|
TWEEN.update()
|
|
const resized = this.resizeRendererToDisplaySize()
|
|
|
|
const delta = this.threeClock.getDelta()
|
|
if(this.mixer && this.animate) this.mixer.update(delta)
|
|
|
|
this.renderer.render(this.scene, this.camera)
|
|
requestAnimationFrame(this.render.bind(this))
|
|
}
|
|
|
|
resizeRendererToDisplaySize() {
|
|
const width = this.canvasEl.clientWidth
|
|
const height = this.canvasEl.clientHeight
|
|
if (this.canvasEl.width !== width || this.canvasEl.height !== height) {
|
|
this.renderer.setSize(width, height, false)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
translateBot(position, delay=500, endcb, updatecb){
|
|
if(this.movingBotTween) this.movingBotTween.end()
|
|
this.movingBotTween = new TWEEN.Tween(this.mecha.position)
|
|
.to({ x: Number(position.x),
|
|
y: Number(position.z),
|
|
z: Number(position.y),
|
|
}, delay)
|
|
.easing(TWEEN.Easing.Sinusoidal.InOut)
|
|
if(typeof(endcb)=='function') this.movingBotTween.onComplete(endcb)
|
|
if(typeof(updatecb)=='function') this.movingBotTween.onUpdate(updatecb)
|
|
this.movingBotTween.start()
|
|
}
|
|
|
|
rotateBot(rotation, delay=500, endcb, updatecb){
|
|
//TODO : respect existing Z
|
|
|
|
if(this.rotatingBotTween) this.rotatingBotTween.end()
|
|
this.rotatingBotTween = new TWEEN.Tween(this.mecha.rotation)
|
|
.to({ x: Number(rotation.x*Math.PI/180),
|
|
y: Number(rotation.z*Math.PI/180),
|
|
z: Number(rotation.y*Math.PI/180),
|
|
}, delay)
|
|
.easing(TWEEN.Easing.Sinusoidal.InOut)
|
|
if(typeof(endcb)=='function') this.rotatingBotTween.onComplete(endcb)
|
|
if(typeof(updatecb)=='function') this.rotatingBotTween.onUpdate(updatecb)
|
|
this.rotatingBotTween.start()
|
|
}
|
|
|
|
propelMove(position){
|
|
//TODO : respect existing Z rotation
|
|
|
|
const moveDelay = 1000
|
|
const dx = this.mecha.position.x - position.x
|
|
const dy = this.mecha.position.z - position.y
|
|
const dz = this.mecha.position.y - position.z
|
|
const delay = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)) * moveDelay
|
|
const ax = (dy>0.5) ? -10 : ((dy<-0.5) ? 10 : 0 )
|
|
const ay = (dx>0.5) ? 10 : ((dx<-0.5) ? -10 : 0 )
|
|
|
|
console.log( this.mecha.position.x, this.mecha.position.y)
|
|
console.log(dx,dy,ax,ay)
|
|
|
|
const rotationDelay = Math.min(delay/4, 500)
|
|
this.rotateBot({x: ax, y: ay, z: 0}, rotationDelay)
|
|
|
|
let chkpt1 = true, chkpt2 = true, chkpt3 = true
|
|
const startTime = Date.now()
|
|
this.translateBot(position, delay, null, () => {
|
|
const t = (Date.now()-startTime)/delay
|
|
if(chkpt1 && (t >= 0.2)){ //Time to stop accelerating
|
|
this.rotateBot({x: 0, y: 0, z: 0}, rotationDelay)
|
|
chkpt1 = false
|
|
}
|
|
if(chkpt2 && (t >= 0.7)){ //Time to brake
|
|
this.rotateBot({x: -ax, y: -ay, z: 0}, rotationDelay)
|
|
chkpt2 = false
|
|
}
|
|
if(chkpt3 && (t >= 0.9)){ //Time to restore
|
|
this.rotateBot({x: 0, y: 0, z: 0}, rotationDelay)
|
|
chkpt3 = false
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
app.registerClass('HelperBot', HelperBot) |