use glTF animations
This commit is contained in:
+71
-38
@@ -23,20 +23,9 @@ camera.updateProjectionMatrix()
|
||||
camera.layers.enable(1)
|
||||
camera.layers.enable(2)
|
||||
|
||||
const gltfLoader = new GLTFLoader()
|
||||
const addTo = function addTo(scene) {
|
||||
return function addToScene(gltf) {
|
||||
const model = gltf.scene
|
||||
const scale = 2
|
||||
model.scale.set(scale, scale, scale)
|
||||
scene.add(model)
|
||||
}
|
||||
}
|
||||
|
||||
const projectileMaterial = new THREE.MeshToonMaterial({ color: 0xcccccc })
|
||||
const terrainMaterial = new THREE.MeshToonMaterial({ color: 0x5c4033 })
|
||||
const passableTerrainMaterial = new THREE.MeshToonMaterial({ color: 0x228822, transparent: true, opacity: 0.65 })
|
||||
// const bboxMaterial = new THREE.MeshToonMaterial({ color: 0xffd700, transparent: true, opacity: 0.2 })
|
||||
const opacity = 0.3
|
||||
const teamMaterials = {
|
||||
blue: new THREE.MeshToonMaterial({ color: 0x4444ff }),
|
||||
@@ -58,17 +47,50 @@ minimapRenderer.setSize(300, 300)
|
||||
minimapRenderer.setAnimationLoop(minimapRender)
|
||||
minimapCamera.position.set(10, 10, 10)
|
||||
|
||||
const animationActions = {}
|
||||
const entities = {}
|
||||
const projectiles = {}
|
||||
const gltf = {}
|
||||
const mixers = {}
|
||||
const positionTweens = {}
|
||||
const projectiles = {}
|
||||
const rotationTweens = {}
|
||||
const terrains = {}
|
||||
var state = { abilities: [], entities: [], terrains: [], projectiles: [] }
|
||||
|
||||
global.animationActions = animationActions
|
||||
global.entities = entities
|
||||
global.gltf = gltf
|
||||
global.mixers = mixers
|
||||
global.projectiles = projectiles
|
||||
global.terrains = terrains
|
||||
global.state = state
|
||||
global.terrains = terrains
|
||||
|
||||
const gltfLoader = new GLTFLoader()
|
||||
const preloadGLTF = function loadTemplate(path) {
|
||||
gltfLoader.load(path, (loadedGLTF) => gltf[path] = loadedGLTF)
|
||||
}
|
||||
|
||||
const addGLTF = function addGLTF(scene, path, id) {
|
||||
if (gltf[path] == null) {
|
||||
setTimeout(() => addGLTF(scene, path, id), 200)
|
||||
return
|
||||
}
|
||||
|
||||
const scale = 2
|
||||
const model = gltf[path].scene.clone()
|
||||
|
||||
const mixer = new THREE.AnimationMixer(model)
|
||||
mixers[id] = mixer
|
||||
|
||||
animationActions[id] = {}
|
||||
gltf[path].animations.forEach((it) => {
|
||||
const animation = mixer.clipAction(it)
|
||||
animationActions[id][it.name] = animation
|
||||
})
|
||||
|
||||
model.scale.set(scale, scale, scale)
|
||||
scene.add(model)
|
||||
}
|
||||
|
||||
const geometry = new THREE.PlaneGeometry(0, 0)
|
||||
const material = new THREE.MeshToonMaterial({ color: 0x115011 })
|
||||
@@ -102,6 +124,7 @@ function render() {
|
||||
cameraMovement()
|
||||
Object.values(positionTweens).forEach((tween) => tween.update())
|
||||
Object.values(rotationTweens).forEach((tween) => tween.update())
|
||||
Object.values(mixers).forEach((mixer) => mixer.update(delta))
|
||||
renderer.render(scene, camera)
|
||||
stats.end()
|
||||
}
|
||||
@@ -311,6 +334,7 @@ function connectWebSocket() {
|
||||
else {
|
||||
// const entityMaterial = teamMaterials[e.team]
|
||||
// entity = new THREE.Mesh(new THREE.CylinderGeometry(e.visualRadius / 100, e.visualRadius / 100, e.height / 50), entityMaterial)
|
||||
// TODO: change entity material
|
||||
entity = new THREE.Group()
|
||||
entity.rotation.x = Math.PI / 2
|
||||
entity.scale.set(e.visualRadius / 100, e.height / 100, e.visualRadius / 100)
|
||||
@@ -319,9 +343,9 @@ function connectWebSocket() {
|
||||
entity.position.set(e.position.x / 100, e.position.y / 100, 0)
|
||||
scene.add(entity)
|
||||
|
||||
const hpMargin = 0.4
|
||||
const hpMargin = 4.8
|
||||
const maxHp = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xd03333 }))
|
||||
maxHp.position.set(0, (e.visualRadius / 100) + hpMargin, 0)
|
||||
maxHp.position.set(0, hpMargin, 0)
|
||||
maxHp.scale.set(1.5, 0.2, 1)
|
||||
maxHp.layers.set(1)
|
||||
entity.add(maxHp)
|
||||
@@ -338,9 +362,9 @@ function connectWebSocket() {
|
||||
entity.add(teamMarker)
|
||||
|
||||
const buffMaterial = new THREE.MeshToonMaterial({ color: 0xffff00, transparent: true, opacity: 0.4 })
|
||||
const buffMarker = new THREE.Mesh(new THREE.CylinderGeometry((e.visualRadius + 10) / 100, (e.visualRadius + 10) / 100, 1), buffMaterial)
|
||||
const buffMarkerSize = 400
|
||||
buffMarker.scale.y = e.height / buffMarkerSize
|
||||
const buffMarkerRadius = 0.6
|
||||
const buffMarker = new THREE.Mesh(new THREE.CylinderGeometry(buffMarkerRadius, buffMarkerRadius, 0.3), buffMaterial)
|
||||
buffMarker.position.y = 2
|
||||
buffMarker.layers.set(1)
|
||||
buffMarker.visible = false
|
||||
entity.add(buffMarker)
|
||||
@@ -356,7 +380,7 @@ function connectWebSocket() {
|
||||
castingMarker.position.y = 1
|
||||
castingMarker.scale.y = e.height / castingMarkerSize
|
||||
castingMarker.layers.set(1)
|
||||
buffMarker.visible = false
|
||||
castingMarker.visible = false
|
||||
castingMarkerrotationBase.add(castingMarker)
|
||||
|
||||
const rangeMaterial = teamMaterials['range']
|
||||
@@ -369,10 +393,15 @@ function connectWebSocket() {
|
||||
entity.add(rangeMarker)
|
||||
|
||||
const modelRotationBase = new THREE.Object3D()
|
||||
modelRotationBase.rotation.y = e.rotation - (Math.PI / 2)
|
||||
modelRotationBase.layers.set(1)
|
||||
entity.add(modelRotationBase)
|
||||
|
||||
gltfLoader.load('models/generic-player-placeholder.gltf', addTo(modelRotationBase))
|
||||
addGLTF(modelRotationBase, 'models/generic-player-placeholder.gltf', e.id)
|
||||
const animations = animationActions[e.id] ?? {}
|
||||
if (e.dead) {
|
||||
animations.dead?.reset().play()
|
||||
}
|
||||
|
||||
entities[e.id] = entity
|
||||
}
|
||||
@@ -381,18 +410,30 @@ function connectWebSocket() {
|
||||
entity.children.at(1).visible = !e.dead
|
||||
entity.children.at(2).visible = e.buffs.some((it) => it.id == 'exposed') // TODO: only works for Exposed now
|
||||
|
||||
const animations = animationActions[e.id] ?? {}
|
||||
const fadeIn = 0.15
|
||||
|
||||
if (e.dead) {
|
||||
entity.rotation.x = 0
|
||||
entity.children.at(5).children.at(0).rotation.y = Math.PI / 2
|
||||
if (!animations.dead?.isRunning()) {
|
||||
Object.values(animations).forEach((it) => it.isRunning() && it.reset().fadeOut(fadeIn).play())
|
||||
animations.dead?.reset().fadeIn(fadeIn).play()
|
||||
}
|
||||
}
|
||||
else if (e.casting != null) {
|
||||
if (!animations.cast?.isRunning()) {
|
||||
Object.values(animations).forEach((it) => it.isRunning() && it.reset().fadeOut(fadeIn).play())
|
||||
animations.cast?.reset().fadeIn(fadeIn).play()
|
||||
}
|
||||
}
|
||||
else {
|
||||
entity.rotation.x = Math.PI / 2
|
||||
entity.children.at(5).children.at(0).rotation.y = 0
|
||||
if (!animations.default?.isRunning()) {
|
||||
Object.values(animations).forEach((it) => it.isRunning() && it.reset().fadeOut(fadeIn).play())
|
||||
animations.default?.reset().fadeIn(fadeIn).play()
|
||||
}
|
||||
}
|
||||
|
||||
entity.userData.flaggedForRemoval = false
|
||||
entity.children.at(3).rotation.y = e.rotation
|
||||
// entity.children.at(5).rotation.y = e.rotation - (Math.PI / 2)
|
||||
const oldRotationY = entity.children.at(5).rotation.y
|
||||
const newRotationY = e.rotation - (Math.PI / 2)
|
||||
if (Math.abs((oldRotationY - (2 * Math.PI)) - newRotationY) < Math.abs(oldRotationY - newRotationY)) {
|
||||
@@ -411,13 +452,15 @@ function connectWebSocket() {
|
||||
hp.position.x = -(1 - percentageHp) / 2
|
||||
|
||||
entity.children.at(4).visible = e.id == playerId
|
||||
entity.children.at(3).children.at(0).visible = e.casting != null
|
||||
// entity.children.at(3).children.at(0).visible = e.casting != null
|
||||
}
|
||||
|
||||
for (const e of Object.values(entities)) {
|
||||
if (e.userData.flaggedForRemoval) {
|
||||
scene.remove(e)
|
||||
delete animationActions[e.userData.id]
|
||||
delete entities[e.userData.id]
|
||||
delete mixers[e.userData.id]
|
||||
delete positionTweens[e.userData.id]
|
||||
delete rotationTweens[e.userData.id]
|
||||
}
|
||||
@@ -480,18 +523,6 @@ function connectWebSocket() {
|
||||
terrain.userData.id = t.id
|
||||
scene.add(terrain)
|
||||
terrains[t.id] = terrain
|
||||
|
||||
// // TODO: bboxes aren't tracked and can leak memory
|
||||
// const bboxValues = Object.values(t.bbox)
|
||||
// if (bboxValues.length >= 4) {
|
||||
// const width = (bboxValues[1] - bboxValues[3]) / 100
|
||||
// const height = (bboxValues[0] - bboxValues[2]) / 100
|
||||
|
||||
// const bbox = new THREE.Mesh(new THREE.BoxGeometry(width, height, 0.2), bboxMaterial)
|
||||
// bbox.position.set((bboxValues[3] / 100) + (width / 2), (bboxValues[2] / 100) + (height / 2), 0)
|
||||
// bbox.layers.set(1)
|
||||
// scene.add(bbox)
|
||||
// }
|
||||
}
|
||||
|
||||
terrain.position.set(t.position.x / 100, t.position.y / 100, 0)
|
||||
@@ -582,6 +613,8 @@ function connectWebSocket() {
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
preloadGLTF('models/generic-player-placeholder.gltf')
|
||||
|
||||
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries())
|
||||
playerId = params.id
|
||||
if (playerId == null) {
|
||||
|
||||
Reference in New Issue
Block a user