import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils'; import modelLoaderInstance from './pony-loader'; import { CharacterPacket, FullStatePacket } from '../../common/types/packet'; import { NameTag } from './nametag'; import { CanvasUtils } from './canvas-utils'; import { Mesh, MeshStandardMaterial, Color, AnimationAction, AnimationMixer, Object3D, Vector3, } from 'three'; const nameTagBuilder = new CanvasUtils({ fill: false, textBorderColor: '#000', foregroundColor: '#fff', textShadowBlur: 2, textBorderSize: 1, }); export class PonyEntity { public velocity = new Vector3(0, 0, 0); public angularVelocity = new Vector3(0, 0, 0); public mixer!: AnimationMixer; public container!: Object3D; public model!: Object3D; public material!: MeshStandardMaterial; public walkAnimationState = 0; public idleAction: AnimationAction; public walkAction: AnimationAction; public nameTag?: NameTag; public changes: FullStatePacket = {}; initialize() { this.model = (SkeletonUtils as any).clone(modelLoaderInstance.ponyModel); this.material = ( (this.model.children[0].children[1] as Mesh) .material as MeshStandardMaterial ).clone(); (this.model.children[0].children[1] as Mesh).material = this.material; this.mixer = new AnimationMixer(this.model); this.idleAction = this.mixer.clipAction(modelLoaderInstance.animations[0]); this.walkAction = this.mixer.clipAction(modelLoaderInstance.animations[2]); this.idleAction.play(); this.container = new Object3D(); this.container.add(this.model); } update(dt: number) { this.container.position.add(this.velocity.clone().multiplyScalar(dt)); this.container.rotation.setFromVector3( new Vector3( this.container.rotation.x, this.container.rotation.y, this.container.rotation.z, ).add(this.angularVelocity.clone().multiplyScalar(dt)), ); this.mixer.update(dt); } dispose() { this.model = null; this.material.dispose(); this.nameTag?.dispose(); } public addNameTag(name: string) { this.nameTag = new NameTag(nameTagBuilder, name); this.nameTag.tag.position.set(0, 1.8, 0.5); this.container.add(this.nameTag.tag); } public setWalkAnimationState(index: number) { const previousState = this.walkAnimationState; this.walkAnimationState = index; if (previousState === this.walkAnimationState) { return; } this.changes.animState = index; if (index === 1) { this.walkAction.reset().crossFadeFrom(this.idleAction, 0.5, false).play(); } else { this.walkAction.crossFadeTo(this.idleAction.reset(), 0.5, false).play(); } } public setColor(color: number | string) { this.material.color = new Color(color); } public setCharacter(packet: CharacterPacket) { if (packet.color) { this.setColor(packet.color); } } }