UNPKG

@prisma-cms/mc.js-module

Version:
518 lines (476 loc) 16.5 kB
/* eslint-disable no-underscore-dangle, max-classes-per-file */ import Helpers from '../../utils/helpers' import Config from '../../config/config' import { loadSkinToCanvas, isSlimSkin } from './playerObjectUtils' import * as THREE from 'three' import TWEEN from '@tweenjs/tween.js' const DIMENSION = Config.block.dimension const EYE_2_TOE = Config.player.aabb.eye2toe const EYE_2_TOP = Config.player.aabb.eye2top // const HEAD_BODY_DEG = Config.player.headBodyDeg const HEIGHT = EYE_2_TOE + EYE_2_TOP function toFaceVertices(x1, y1, x2, y2, w, h) { return [ new THREE.Vector2(x1 / w, 1.0 - y2 / h), new THREE.Vector2(x2 / w, 1.0 - y2 / h), new THREE.Vector2(x2 / w, 1.0 - y1 / h), new THREE.Vector2(x1 / w, 1.0 - y1 / h) ] } function toSkinVertices(x1, y1, x2, y2) { return toFaceVertices(x1, y1, x2, y2, 64.0, 64.0) } function setVertices(box, top, bottom, left, front, right, back) { box.faceVertexUvs[0] = [] box.faceVertexUvs[0][0] = [right[3], right[0], right[2]] box.faceVertexUvs[0][1] = [right[0], right[1], right[2]] box.faceVertexUvs[0][2] = [left[3], left[0], left[2]] box.faceVertexUvs[0][3] = [left[0], left[1], left[2]] box.faceVertexUvs[0][4] = [top[3], top[0], top[2]] box.faceVertexUvs[0][5] = [top[0], top[1], top[2]] box.faceVertexUvs[0][6] = [bottom[0], bottom[3], bottom[1]] box.faceVertexUvs[0][7] = [bottom[3], bottom[2], bottom[1]] box.faceVertexUvs[0][8] = [front[3], front[0], front[2]] box.faceVertexUvs[0][9] = [front[0], front[1], front[2]] box.faceVertexUvs[0][10] = [back[3], back[0], back[2]] box.faceVertexUvs[0][11] = [back[0], back[1], back[2]] } const esp = 0.002 /** * Notice that innerLayer and outerLayer may NOT be the direct children of the Group. */ class BodyPart extends THREE.Group { constructor(innerLayer, outerLayer) { super() this.innerLayer = innerLayer this.outerLayer = outerLayer innerLayer.name = 'inner' outerLayer.name = 'outer' } } class SkinObject extends THREE.Group { constructor(layer1Material, layer2Material) { super() this.modelListeners = [] // called when model(slim property) is changed this._slim = false // Head const headBox = new THREE.BoxGeometry(8, 8, 8, 0, 0, 0) setVertices( headBox, toSkinVertices(8, 0, 16, 8), toSkinVertices(16, 0, 24, 8), toSkinVertices(0, 8, 8, 16), toSkinVertices(8, 8, 16, 16), toSkinVertices(16, 8, 24, 16), toSkinVertices(24, 8, 32, 16) ) headBox.rotateY(Math.PI) const headMesh = new THREE.Mesh(headBox, layer1Material) const head2Box = new THREE.BoxGeometry(9, 9, 9, 0, 0, 0) setVertices( head2Box, toSkinVertices(40, 0, 48, 8), toSkinVertices(48, 0, 56, 8), toSkinVertices(32, 8, 40, 16), toSkinVertices(40, 8, 48, 16), toSkinVertices(48, 8, 56, 16), toSkinVertices(56, 8, 64, 16) ) head2Box.rotateY(Math.PI) const head2Mesh = new THREE.Mesh(head2Box, layer2Material) head2Mesh.renderOrder = -1 headMesh.position.setY(3) this.head = new BodyPart(headMesh, head2Mesh) this.head.name = 'head' this.head.add(headMesh, head2Mesh) this.head.rotation.order = 'YXZ' this.head.position.setY(-3) this.add(this.head) // Body const bodyBox = new THREE.BoxGeometry(8, 12, 4, 0, 0, 0) setVertices( bodyBox, toSkinVertices(20, 16, 28, 20), toSkinVertices(28, 16, 36, 20), toSkinVertices(16, 20, 20, 32), toSkinVertices(20, 20, 28, 32), toSkinVertices(28, 20, 32, 32), toSkinVertices(32, 20, 40, 32) ) bodyBox.rotateY(Math.PI) const bodyMesh = new THREE.Mesh(bodyBox, layer1Material) const body2Box = new THREE.BoxGeometry(9, 13.5, 4.5, 0, 0, 0) setVertices( body2Box, toSkinVertices(20, 32, 28, 36), toSkinVertices(28, 32, 36, 36), toSkinVertices(16, 36, 20, 48), toSkinVertices(20, 36, 28, 48), toSkinVertices(28, 36, 32, 48), toSkinVertices(32, 36, 40, 48) ) body2Box.rotateY(Math.PI) const body2Mesh = new THREE.Mesh(body2Box, layer2Material) this.body = new BodyPart(bodyMesh, body2Mesh) this.body.name = 'body' this.body.add(bodyMesh, body2Mesh) this.body.position.y = -10 this.add(this.body) // Right Arm const rightArmBox = new THREE.BoxGeometry(1, 1, 1, 0, 0, 0) // w/d/h is model-related rightArmBox.rotateY(Math.PI) const rightArmMesh = new THREE.Mesh(rightArmBox, layer1Material) this.modelListeners.push(() => { rightArmMesh.scale.x = (this.slim ? 3 : 4) - esp rightArmMesh.scale.y = 12 - esp rightArmMesh.scale.z = 4 - esp if (this.slim) { setVertices( rightArmBox, toSkinVertices(44, 16, 47, 20), toSkinVertices(47, 16, 50, 20), toSkinVertices(40, 20, 44, 32), toSkinVertices(44, 20, 47, 32), toSkinVertices(47, 20, 51, 32), toSkinVertices(51, 20, 54, 32) ) } else { setVertices( rightArmBox, toSkinVertices(44, 16, 48, 20), toSkinVertices(48, 16, 52, 20), toSkinVertices(40, 20, 44, 32), toSkinVertices(44, 20, 48, 32), toSkinVertices(48, 20, 52, 32), toSkinVertices(52, 20, 56, 32) ) } rightArmBox.uvsNeedUpdate = true rightArmBox.elementsNeedUpdate = true }) const rightArm2Box = new THREE.BoxGeometry(1, 1, 1, 0, 0, 0) // w/d/h is model-related rightArm2Box.rotateY(Math.PI) const rightArm2Mesh = new THREE.Mesh(rightArm2Box, layer2Material) rightArm2Mesh.renderOrder = 1 this.modelListeners.push(() => { rightArm2Mesh.scale.x = (this.slim ? 3.375 : 4.5) - esp rightArm2Mesh.scale.y = 13.5 - esp rightArm2Mesh.scale.z = 4.5 - esp if (this.slim) { setVertices( rightArm2Box, toSkinVertices(44, 32, 47, 36), toSkinVertices(47, 32, 50, 36), toSkinVertices(40, 36, 44, 48), toSkinVertices(44, 36, 47, 48), toSkinVertices(47, 36, 51, 48), toSkinVertices(51, 36, 54, 48) ) } else { setVertices( rightArm2Box, toSkinVertices(44, 32, 48, 36), toSkinVertices(48, 32, 52, 36), toSkinVertices(40, 36, 44, 48), toSkinVertices(44, 36, 48, 48), toSkinVertices(48, 36, 52, 48), toSkinVertices(52, 36, 56, 48) ) } rightArm2Box.uvsNeedUpdate = true rightArm2Box.elementsNeedUpdate = true }) const rightArmPivot = new THREE.Group() rightArmPivot.add(rightArmMesh, rightArm2Mesh) rightArmPivot.position.y = -6 this.rightArm = new BodyPart(rightArmMesh, rightArm2Mesh) this.rightArm.name = 'rightArm' this.rightArm.add(rightArmPivot) this.rightArm.position.y = -4 this.modelListeners.push(() => { this.rightArm.position.x = this.slim ? -5.5 : -6 }) this.add(this.rightArm) // Left Arm const leftArmBox = new THREE.BoxGeometry(1, 1, 1, 0, 0, 0) // w/d/h is model-related leftArmBox.rotateY(Math.PI) const leftArmMesh = new THREE.Mesh(leftArmBox, layer1Material) this.modelListeners.push(() => { leftArmMesh.scale.x = (this.slim ? 3 : 4) - esp leftArmMesh.scale.y = 12 - esp leftArmMesh.scale.z = 4 - esp if (this.slim) { setVertices( leftArmBox, toSkinVertices(36, 48, 39, 52), toSkinVertices(39, 48, 42, 52), toSkinVertices(32, 52, 36, 64), toSkinVertices(36, 52, 39, 64), toSkinVertices(39, 52, 43, 64), toSkinVertices(43, 52, 46, 64) ) } else { setVertices( leftArmBox, toSkinVertices(36, 48, 40, 52), toSkinVertices(40, 48, 44, 52), toSkinVertices(32, 52, 36, 64), toSkinVertices(36, 52, 40, 64), toSkinVertices(40, 52, 44, 64), toSkinVertices(44, 52, 48, 64) ) } leftArmBox.uvsNeedUpdate = true leftArmBox.elementsNeedUpdate = true }) const leftArm2Box = new THREE.BoxGeometry(1, 1, 1, 0, 0, 0) // w/d/h is model-related leftArm2Box.rotateY(Math.PI) const leftArm2Mesh = new THREE.Mesh(leftArm2Box, layer2Material) leftArm2Mesh.renderOrder = 1 this.modelListeners.push(() => { leftArm2Mesh.scale.x = (this.slim ? 3.375 : 4.5) - esp leftArm2Mesh.scale.y = 13.5 - esp leftArm2Mesh.scale.z = 4.5 - esp if (this.slim) { setVertices( leftArm2Box, toSkinVertices(52, 48, 55, 52), toSkinVertices(55, 48, 58, 52), toSkinVertices(48, 52, 52, 64), toSkinVertices(52, 52, 55, 64), toSkinVertices(55, 52, 59, 64), toSkinVertices(59, 52, 62, 64) ) } else { setVertices( leftArm2Box, toSkinVertices(52, 48, 56, 52), toSkinVertices(56, 48, 60, 52), toSkinVertices(48, 52, 52, 64), toSkinVertices(52, 52, 56, 64), toSkinVertices(56, 52, 60, 64), toSkinVertices(60, 52, 64, 64) ) } leftArm2Box.uvsNeedUpdate = true leftArm2Box.elementsNeedUpdate = true }) const leftArmPivot = new THREE.Group() leftArmPivot.add(leftArmMesh, leftArm2Mesh) leftArmPivot.position.y = -6 this.leftArm = new BodyPart(leftArmMesh, leftArm2Mesh) this.leftArm.name = 'leftArm' this.leftArm.add(leftArmPivot) this.leftArm.position.y = -4 this.modelListeners.push(() => { this.leftArm.position.x = this.slim ? 5.5 : 6 }) this.add(this.leftArm) // Right Leg const rightLegBox = new THREE.BoxGeometry( 4 - esp, 12 - esp, 4 - esp, 0, 0, 0 ) setVertices( rightLegBox, toSkinVertices(4, 16, 8, 20), toSkinVertices(8, 16, 12, 20), toSkinVertices(0, 20, 4, 32), toSkinVertices(4, 20, 8, 32), toSkinVertices(8, 20, 12, 32), toSkinVertices(12, 20, 16, 32) ) rightLegBox.rotateY(Math.PI) const rightLegMesh = new THREE.Mesh(rightLegBox, layer1Material) const rightLeg2Box = new THREE.BoxGeometry( 4.5 - esp, 13.5 - esp, 4.5 - esp, 0, 0, 0 ) setVertices( rightLeg2Box, toSkinVertices(4, 32, 8, 36), toSkinVertices(8, 32, 12, 36), toSkinVertices(0, 36, 4, 48), toSkinVertices(4, 36, 8, 48), toSkinVertices(8, 36, 12, 48), toSkinVertices(12, 36, 16, 48) ) rightLeg2Box.rotateY(Math.PI) const rightLeg2Mesh = new THREE.Mesh(rightLeg2Box, layer2Material) rightLeg2Mesh.renderOrder = 1 const rightLegPivot = new THREE.Group() rightLegPivot.add(rightLegMesh, rightLeg2Mesh) rightLegPivot.position.y = -6 this.rightLeg = new BodyPart(rightLegMesh, rightLeg2Mesh) this.rightLeg.name = 'rightLeg' this.rightLeg.add(rightLegPivot) this.rightLeg.position.y = -16 this.rightLeg.position.x = -2 this.add(this.rightLeg) // Left Leg const leftLegBox = new THREE.BoxGeometry( 4 - esp, 12 - esp, 4 - esp, 0, 0, 0 ) setVertices( leftLegBox, toSkinVertices(20, 48, 24, 52), toSkinVertices(24, 48, 28, 52), toSkinVertices(16, 52, 20, 64), toSkinVertices(20, 52, 24, 64), toSkinVertices(24, 52, 28, 64), toSkinVertices(28, 52, 32, 64) ) leftLegBox.rotateY(Math.PI) const leftLegMesh = new THREE.Mesh(leftLegBox, layer1Material) const leftLeg2Box = new THREE.BoxGeometry( 4.5 - esp, 13.5 - esp, 4.5 - esp, 0, 0, 0 ) setVertices( leftLeg2Box, toSkinVertices(4, 48, 8, 52), toSkinVertices(8, 48, 12, 52), toSkinVertices(0, 52, 4, 64), toSkinVertices(4, 52, 8, 64), toSkinVertices(8, 52, 12, 64), toSkinVertices(12, 52, 16, 64) ) leftLeg2Box.rotateY(Math.PI) const leftLeg2Mesh = new THREE.Mesh(leftLeg2Box, layer2Material) leftLeg2Mesh.renderOrder = 1 const leftLegPivot = new THREE.Group() leftLegPivot.add(leftLegMesh, leftLeg2Mesh) leftLegPivot.position.y = -6 this.leftLeg = new BodyPart(leftLegMesh, leftLeg2Mesh) this.leftLeg.name = 'leftLeg' this.leftLeg.add(leftLegPivot) this.leftLeg.position.y = -16 this.leftLeg.position.x = 2 this.add(this.leftLeg) this.slim = false } get slim() { return this._slim } set slim(value) { this._slim = value this.modelListeners.forEach(listener => listener()) } getBodyParts() { return this.children.filter(it => it instanceof BodyPart) } setInnerLayerVisible(value) { this.getBodyParts().forEach(part => (part.innerLayer.visible = value)) } setOuterLayerVisible(value) { this.getBodyParts().forEach(part => (part.outerLayer.visible = value)) } } export default class PlayerObject extends THREE.Group { constructor(skinImg, pos, dir) { super() this.skinImg = new Image() this.skinCanvas = document.createElement('canvas') this.skinTexture = new THREE.Texture(this.skinCanvas) this.skinTexture.magFilter = THREE.NearestFilter this.skinTexture.minFilter = THREE.NearestFilter const layer1Material = new THREE.MeshLambertMaterial({ map: this.skinTexture, side: THREE.FrontSide }) const layer2Material = new THREE.MeshLambertMaterial({ map: this.skinTexture, transparent: true, opacity: 1, side: THREE.DoubleSide, alphaTest: 0.5 }) this.skin = new SkinObject(layer1Material, layer2Material) this.skin.name = 'skin' this.skin.visible = false this.add(this.skin) this.skinImg.crossOrigin = 'anonymous' this.skinImg.onerror = () => Helpers.error(`Failed loading ${this.skinImg.src}`, true) this.skinImg.onload = () => { loadSkinToCanvas(this.skinCanvas, this.skinImg) if (this.detectModel) { this.playerObject.skin.slim = isSlimSkin(this.skinCanvas) } this.skinTexture.needsUpdate = true layer1Material.needsUpdate = true layer2Material.needsUpdate = true this.skin.visible = true } this.skinImg.src = skinImg this.scale.set(HEIGHT, HEIGHT, HEIGHT) this.position.copy(pos.multiplyScalar(DIMENSION)) this.position.y += EYE_2_TOE * DIMENSION this.skin.head.rotation.x = dir.x this.rotation.y = dir.y this.oldDirY = dir.y } tweenPosition = (x, y, z) => { new TWEEN.Tween(this.position) .to( { x: x * DIMENSION, y: (y + EYE_2_TOE) * DIMENSION, z: z * DIMENSION }, 90 ) .start() } tweenDirection = (dirx, diry) => { const { head } = this.skin // HORIZONTAL DIRECTION CHANGE // const headY = head.rotation.y % (Math.PI * 2) // const deltaDirY = diry - this.oldDirY // if (deltaDirY >= 0) this.stuckRight = false // if (deltaDirY <= 0) this.stuckLeft = false // // if (deltaDirY !== 0) { // // if ((this.stuckRight && deltaDirY < 0) || (this.stuckLeft && deltaDirY > 0)) { // // new TWEEN.Tween(this.rotation).to({ y: this.rotation.y + deltaDirY }, 90).start() // // } else if (headY + deltaDirY > HEAD_BODY_DEG) { // // console.log('stuckLeft', headY) // // if (headY < HEAD_BODY_DEG) // // new TWEEN.Tween(head.rotation).to({ y: HEAD_BODY_DEG }, 90).start() // // new TWEEN.Tween(this.rotation) // // .to({ y: this.rotation.y + deltaDirY - HEAD_BODY_DEG }, 90) // // .start() // // this.stuckLeft = true // // } else if (headY + deltaDirY < -HEAD_BODY_DEG) { // // console.log('stuckRight', headY) // // if (headY > -HEAD_BODY_DEG) // // new TWEEN.Tween(head.rotation).to({ y: -HEAD_BODY_DEG }, 90).start() // // new TWEEN.Tween(this.rotation) // // .to({ y: this.rotation.y + deltaDirY - HEAD_BODY_DEG }, 90) // // .start() // // this.stuckRight = true // // } else { // // new TWEEN.Tween(head.rotation).to({ y: diry % (Math.PI * 2) }, 90).start() // // } // // } // VERTICAL DIRECTION CHANGE new TWEEN.Tween(head.rotation).to({ x: dirx }, 90).start() new TWEEN.Tween(this.rotation).to({ y: diry }, 90).start() } // setDirection = (x, y) => { // this.skin.head.rotation.x = x // this.skin.head.rotation.y = y // } }