UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

334 lines (333 loc) 12 kB
import { Object3D, Box3, MathUtils, MeshLambertMaterial, TextureLoader, UVMapping } from "three"; import { MD2Loader } from "../loaders/MD2Loader.js"; import { MorphBlendMesh } from "./MorphBlendMesh.js"; class MD2CharacterComplex { constructor() { this.scale = 1; this.animationFPS = 6; this.transitionFrames = 15; this.maxSpeed = 275; this.maxReverseSpeed = -275; this.frontAcceleration = 600; this.backAcceleration = 600; this.frontDecceleration = 600; this.angularSpeed = 2.5; this.root = new Object3D(); this.meshBody = null; this.meshWeapon = null; this.controls = null; this.skinsBody = []; this.skinsWeapon = []; this.weapons = []; this.currentSkin = void 0; this.onLoadComplete = function() { }; this.meshes = []; this.animations = {}; this.loadCounter = 0; this.speed = 0; this.bodyOrientation = 0; this.walkSpeed = this.maxSpeed; this.crouchSpeed = this.maxSpeed * 0.5; this.activeAnimation = null; this.oldAnimation = null; } enableShadows(enable) { for (let i = 0; i < this.meshes.length; i++) { this.meshes[i].castShadow = enable; this.meshes[i].receiveShadow = enable; } } setVisible(enable) { for (let i = 0; i < this.meshes.length; i++) { this.meshes[i].visible = enable; this.meshes[i].visible = enable; } } shareParts(original) { this.animations = original.animations; this.walkSpeed = original.walkSpeed; this.crouchSpeed = original.crouchSpeed; this.skinsBody = original.skinsBody; this.skinsWeapon = original.skinsWeapon; const mesh = this._createPart(original.meshBody.geometry, this.skinsBody[0]); mesh.scale.set(this.scale, this.scale, this.scale); this.root.position.y = original.root.position.y; this.root.add(mesh); this.meshBody = mesh; this.meshes.push(mesh); for (let i = 0; i < original.weapons.length; i++) { const meshWeapon = this._createPart(original.weapons[i].geometry, this.skinsWeapon[i]); meshWeapon.scale.set(this.scale, this.scale, this.scale); meshWeapon.visible = false; meshWeapon.name = original.weapons[i].name; this.root.add(meshWeapon); this.weapons[i] = meshWeapon; this.meshWeapon = meshWeapon; this.meshes.push(meshWeapon); } } loadParts(config) { const scope = this; function loadTextures(baseUrl, textureUrls) { const textureLoader = new TextureLoader(); const textures = []; for (let i = 0; i < textureUrls.length; i++) { textures[i] = textureLoader.load(baseUrl + textureUrls[i], checkLoadingComplete); textures[i].mapping = UVMapping; textures[i].name = textureUrls[i]; if ("colorSpace" in textures[i]) textures[i].colorSpace = "srgb"; else textures[i].encoding = 3001; } return textures; } function checkLoadingComplete() { scope.loadCounter -= 1; if (scope.loadCounter === 0) scope.onLoadComplete(); } this.animations = config.animations; this.walkSpeed = config.walkSpeed; this.crouchSpeed = config.crouchSpeed; this.loadCounter = config.weapons.length * 2 + config.skins.length + 1; const weaponsTextures = []; for (let i = 0; i < config.weapons.length; i++) weaponsTextures[i] = config.weapons[i][1]; this.skinsBody = loadTextures(config.baseUrl + "skins/", config.skins); this.skinsWeapon = loadTextures(config.baseUrl + "skins/", weaponsTextures); const loader = new MD2Loader(); loader.load(config.baseUrl + config.body, function(geo) { const boundingBox = new Box3(); boundingBox.setFromBufferAttribute(geo.attributes.position); scope.root.position.y = -scope.scale * boundingBox.min.y; const mesh = scope._createPart(geo, scope.skinsBody[0]); mesh.scale.set(scope.scale, scope.scale, scope.scale); scope.root.add(mesh); scope.meshBody = mesh; scope.meshes.push(mesh); checkLoadingComplete(); }); const generateCallback = function(index, name) { return function(geo) { const mesh = scope._createPart(geo, scope.skinsWeapon[index]); mesh.scale.set(scope.scale, scope.scale, scope.scale); mesh.visible = false; mesh.name = name; scope.root.add(mesh); scope.weapons[index] = mesh; scope.meshWeapon = mesh; scope.meshes.push(mesh); checkLoadingComplete(); }; }; for (let i = 0; i < config.weapons.length; i++) { loader.load(config.baseUrl + config.weapons[i][0], generateCallback(i, config.weapons[i][0])); } } setPlaybackRate(rate) { if (this.meshBody) this.meshBody.duration = this.meshBody.baseDuration / rate; if (this.meshWeapon) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate; } setWireframe(wireframeEnabled) { if (wireframeEnabled) { if (this.meshBody) this.meshBody.material = this.meshBody.materialWireframe; if (this.meshWeapon) this.meshWeapon.material = this.meshWeapon.materialWireframe; } else { if (this.meshBody) this.meshBody.material = this.meshBody.materialTexture; if (this.meshWeapon) this.meshWeapon.material = this.meshWeapon.materialTexture; } } setSkin(index) { if (this.meshBody && this.meshBody.material.wireframe === false) { this.meshBody.material.map = this.skinsBody[index]; this.currentSkin = index; } } setWeapon(index) { for (let i = 0; i < this.weapons.length; i++) this.weapons[i].visible = false; const activeWeapon = this.weapons[index]; if (activeWeapon) { activeWeapon.visible = true; this.meshWeapon = activeWeapon; if (this.activeAnimation) { activeWeapon.playAnimation(this.activeAnimation); this.meshWeapon.setAnimationTime(this.activeAnimation, this.meshBody.getAnimationTime(this.activeAnimation)); } } } setAnimation(animationName) { if (animationName === this.activeAnimation || !animationName) return; if (this.meshBody) { this.meshBody.setAnimationWeight(animationName, 0); this.meshBody.playAnimation(animationName); this.oldAnimation = this.activeAnimation; this.activeAnimation = animationName; this.blendCounter = this.transitionFrames; } if (this.meshWeapon) { this.meshWeapon.setAnimationWeight(animationName, 0); this.meshWeapon.playAnimation(animationName); } } update(delta) { if (this.controls) this.updateMovementModel(delta); if (this.animations) { this.updateBehaviors(); this.updateAnimations(delta); } } updateAnimations(delta) { let mix = 1; if (this.blendCounter > 0) { mix = (this.transitionFrames - this.blendCounter) / this.transitionFrames; this.blendCounter -= 1; } if (this.meshBody) { this.meshBody.update(delta); this.meshBody.setAnimationWeight(this.activeAnimation, mix); this.meshBody.setAnimationWeight(this.oldAnimation, 1 - mix); } if (this.meshWeapon) { this.meshWeapon.update(delta); this.meshWeapon.setAnimationWeight(this.activeAnimation, mix); this.meshWeapon.setAnimationWeight(this.oldAnimation, 1 - mix); } } updateBehaviors() { const controls = this.controls; const animations = this.animations; let moveAnimation, idleAnimation; if (controls.crouch) { moveAnimation = animations["crouchMove"]; idleAnimation = animations["crouchIdle"]; } else { moveAnimation = animations["move"]; idleAnimation = animations["idle"]; } if (controls.jump) { moveAnimation = animations["jump"]; idleAnimation = animations["jump"]; } if (controls.attack) { if (controls.crouch) { moveAnimation = animations["crouchAttack"]; idleAnimation = animations["crouchAttack"]; } else { moveAnimation = animations["attack"]; idleAnimation = animations["attack"]; } } if (controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight) { if (this.activeAnimation !== moveAnimation) { this.setAnimation(moveAnimation); } } if (Math.abs(this.speed) < 0.2 * this.maxSpeed && !(controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward)) { if (this.activeAnimation !== idleAnimation) { this.setAnimation(idleAnimation); } } if (controls.moveForward) { if (this.meshBody) { this.meshBody.setAnimationDirectionForward(this.activeAnimation); this.meshBody.setAnimationDirectionForward(this.oldAnimation); } if (this.meshWeapon) { this.meshWeapon.setAnimationDirectionForward(this.activeAnimation); this.meshWeapon.setAnimationDirectionForward(this.oldAnimation); } } if (controls.moveBackward) { if (this.meshBody) { this.meshBody.setAnimationDirectionBackward(this.activeAnimation); this.meshBody.setAnimationDirectionBackward(this.oldAnimation); } if (this.meshWeapon) { this.meshWeapon.setAnimationDirectionBackward(this.activeAnimation); this.meshWeapon.setAnimationDirectionBackward(this.oldAnimation); } } } updateMovementModel(delta) { function exponentialEaseOut(k) { return k === 1 ? 1 : -Math.pow(2, -10 * k) + 1; } const controls = this.controls; if (controls.crouch) this.maxSpeed = this.crouchSpeed; else this.maxSpeed = this.walkSpeed; this.maxReverseSpeed = -this.maxSpeed; if (controls.moveForward) this.speed = MathUtils.clamp(this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed); if (controls.moveBackward) this.speed = MathUtils.clamp(this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed); const dir = 1; if (controls.moveLeft) { this.bodyOrientation += delta * this.angularSpeed; this.speed = MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed ); } if (controls.moveRight) { this.bodyOrientation -= delta * this.angularSpeed; this.speed = MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed ); } if (!(controls.moveForward || controls.moveBackward)) { if (this.speed > 0) { const k = exponentialEaseOut(this.speed / this.maxSpeed); this.speed = MathUtils.clamp(this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed); } else { const k = exponentialEaseOut(this.speed / this.maxReverseSpeed); this.speed = MathUtils.clamp(this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0); } } const forwardDelta = this.speed * delta; this.root.position.x += Math.sin(this.bodyOrientation) * forwardDelta; this.root.position.z += Math.cos(this.bodyOrientation) * forwardDelta; this.root.rotation.y = this.bodyOrientation; } // internal _createPart(geometry, skinMap) { const materialWireframe = new MeshLambertMaterial({ color: 16755200, wireframe: true, morphTargets: true, morphNormals: true }); const materialTexture = new MeshLambertMaterial({ color: 16777215, wireframe: false, map: skinMap, morphTargets: true, morphNormals: true }); const mesh = new MorphBlendMesh(geometry, materialTexture); mesh.rotation.y = -Math.PI / 2; mesh.materialTexture = materialTexture; mesh.materialWireframe = materialWireframe; mesh.autoCreateAnimations(this.animationFPS); return mesh; } } export { MD2CharacterComplex }; //# sourceMappingURL=MD2CharacterComplex.js.map