239 lines
6.4 KiB
JavaScript
239 lines
6.4 KiB
JavaScript
/* global THREE */
|
|
|
|
const entities = require('./entities.json')
|
|
const { loadTexture } = globalThis.isElectron ? require('../utils.electron.js') : require('../utils')
|
|
|
|
const elemFaces = {
|
|
up: {
|
|
dir: [0, 1, 0],
|
|
u0: [0, 0, 1],
|
|
v0: [0, 0, 0],
|
|
u1: [1, 0, 1],
|
|
v1: [0, 0, 1],
|
|
corners: [
|
|
[0, 1, 1, 0, 0],
|
|
[1, 1, 1, 1, 0],
|
|
[0, 1, 0, 0, 1],
|
|
[1, 1, 0, 1, 1]
|
|
]
|
|
},
|
|
down: {
|
|
dir: [0, -1, 0],
|
|
u0: [1, 0, 1],
|
|
v0: [0, 0, 0],
|
|
u1: [2, 0, 1],
|
|
v1: [0, 0, 1],
|
|
corners: [
|
|
[1, 0, 1, 0, 0],
|
|
[0, 0, 1, 1, 0],
|
|
[1, 0, 0, 0, 1],
|
|
[0, 0, 0, 1, 1]
|
|
]
|
|
},
|
|
east: {
|
|
dir: [1, 0, 0],
|
|
u0: [0, 0, 0],
|
|
v0: [0, 0, 1],
|
|
u1: [0, 0, 1],
|
|
v1: [0, 1, 1],
|
|
corners: [
|
|
[1, 1, 1, 0, 0],
|
|
[1, 0, 1, 0, 1],
|
|
[1, 1, 0, 1, 0],
|
|
[1, 0, 0, 1, 1]
|
|
]
|
|
},
|
|
west: {
|
|
dir: [-1, 0, 0],
|
|
u0: [1, 0, 1],
|
|
v0: [0, 0, 1],
|
|
u1: [1, 0, 2],
|
|
v1: [0, 1, 1],
|
|
corners: [
|
|
[0, 1, 0, 0, 0],
|
|
[0, 0, 0, 0, 1],
|
|
[0, 1, 1, 1, 0],
|
|
[0, 0, 1, 1, 1]
|
|
]
|
|
},
|
|
north: {
|
|
dir: [0, 0, -1],
|
|
u0: [0, 0, 1],
|
|
v0: [0, 0, 1],
|
|
u1: [1, 0, 1],
|
|
v1: [0, 1, 1],
|
|
corners: [
|
|
[1, 0, 0, 0, 1],
|
|
[0, 0, 0, 1, 1],
|
|
[1, 1, 0, 0, 0],
|
|
[0, 1, 0, 1, 0]
|
|
]
|
|
},
|
|
south: {
|
|
dir: [0, 0, 1],
|
|
u0: [1, 0, 2],
|
|
v0: [0, 0, 1],
|
|
u1: [2, 0, 2],
|
|
v1: [0, 1, 1],
|
|
corners: [
|
|
[0, 0, 1, 0, 1],
|
|
[1, 0, 1, 1, 1],
|
|
[0, 1, 1, 0, 0],
|
|
[1, 1, 1, 1, 0]
|
|
]
|
|
}
|
|
}
|
|
|
|
function dot (a, b) {
|
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
|
|
}
|
|
|
|
function addCube (attr, boneId, bone, cube, texWidth = 64, texHeight = 64) {
|
|
const cubeRotation = new THREE.Euler(0, 0, 0)
|
|
if (cube.rotation) {
|
|
cubeRotation.x = -cube.rotation[0] * Math.PI / 180
|
|
cubeRotation.y = -cube.rotation[1] * Math.PI / 180
|
|
cubeRotation.z = -cube.rotation[2] * Math.PI / 180
|
|
}
|
|
for (const { dir, corners, u0, v0, u1, v1 } of Object.values(elemFaces)) {
|
|
const ndx = Math.floor(attr.positions.length / 3)
|
|
|
|
for (const pos of corners) {
|
|
const u = (cube.uv[0] + dot(pos[3] ? u1 : u0, cube.size)) / texWidth
|
|
const v = (cube.uv[1] + dot(pos[4] ? v1 : v0, cube.size)) / texHeight
|
|
|
|
const inflate = cube.inflate ? cube.inflate : 0
|
|
let vecPos = new THREE.Vector3(
|
|
cube.origin[0] + pos[0] * cube.size[0] + (pos[0] ? inflate : -inflate),
|
|
cube.origin[1] + pos[1] * cube.size[1] + (pos[1] ? inflate : -inflate),
|
|
cube.origin[2] + pos[2] * cube.size[2] + (pos[2] ? inflate : -inflate)
|
|
)
|
|
|
|
vecPos = vecPos.applyEuler(cubeRotation)
|
|
vecPos = vecPos.sub(bone.position)
|
|
vecPos = vecPos.applyEuler(bone.rotation)
|
|
vecPos = vecPos.add(bone.position)
|
|
|
|
attr.positions.push(vecPos.x, vecPos.y, vecPos.z)
|
|
attr.normals.push(...dir)
|
|
attr.uvs.push(u, v)
|
|
attr.skinIndices.push(boneId, 0, 0, 0)
|
|
attr.skinWeights.push(1, 0, 0, 0)
|
|
}
|
|
|
|
attr.indices.push(
|
|
ndx, ndx + 1, ndx + 2,
|
|
ndx + 2, ndx + 1, ndx + 3
|
|
)
|
|
}
|
|
}
|
|
|
|
function getMesh (texture, jsonModel) {
|
|
const bones = {}
|
|
|
|
const geoData = {
|
|
positions: [],
|
|
normals: [],
|
|
uvs: [],
|
|
indices: [],
|
|
skinIndices: [],
|
|
skinWeights: []
|
|
}
|
|
let i = 0
|
|
for (const jsonBone of jsonModel.bones) {
|
|
const bone = new THREE.Bone()
|
|
if (jsonBone.pivot) {
|
|
bone.position.x = jsonBone.pivot[0]
|
|
bone.position.y = jsonBone.pivot[1]
|
|
bone.position.z = jsonBone.pivot[2]
|
|
}
|
|
if (jsonBone.bind_pose_rotation) {
|
|
bone.rotation.x = -jsonBone.bind_pose_rotation[0] * Math.PI / 180
|
|
bone.rotation.y = -jsonBone.bind_pose_rotation[1] * Math.PI / 180
|
|
bone.rotation.z = -jsonBone.bind_pose_rotation[2] * Math.PI / 180
|
|
} else if (jsonBone.rotation) {
|
|
bone.rotation.x = -jsonBone.rotation[0] * Math.PI / 180
|
|
bone.rotation.y = -jsonBone.rotation[1] * Math.PI / 180
|
|
bone.rotation.z = -jsonBone.rotation[2] * Math.PI / 180
|
|
}
|
|
bones[jsonBone.name] = bone
|
|
|
|
if (jsonBone.cubes) {
|
|
for (const cube of jsonBone.cubes) {
|
|
addCube(geoData, i, bone, cube, jsonModel.texturewidth, jsonModel.textureheight)
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
|
|
const rootBones = []
|
|
for (const jsonBone of jsonModel.bones) {
|
|
if (jsonBone.parent) bones[jsonBone.parent].add(bones[jsonBone.name])
|
|
else rootBones.push(bones[jsonBone.name])
|
|
}
|
|
|
|
const skeleton = new THREE.Skeleton(Object.values(bones))
|
|
|
|
const geometry = new THREE.BufferGeometry()
|
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(geoData.positions, 3))
|
|
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(geoData.normals, 3))
|
|
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(geoData.uvs, 2))
|
|
geometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(geoData.skinIndices, 4))
|
|
geometry.setAttribute('skinWeight', new THREE.Float32BufferAttribute(geoData.skinWeights, 4))
|
|
geometry.setIndex(geoData.indices)
|
|
|
|
const material = new THREE.MeshLambertMaterial({ transparent: true, skinning: true, alphaTest: 0.1 })
|
|
const mesh = new THREE.SkinnedMesh(geometry, material)
|
|
mesh.add(...rootBones)
|
|
mesh.bind(skeleton)
|
|
mesh.scale.set(1 / 16, 1 / 16, 1 / 16)
|
|
|
|
loadTexture(texture, texture => {
|
|
texture.magFilter = THREE.NearestFilter
|
|
texture.minFilter = THREE.NearestFilter
|
|
texture.flipY = false
|
|
texture.wrapS = THREE.RepeatWrapping
|
|
texture.wrapT = THREE.RepeatWrapping
|
|
material.map = texture
|
|
})
|
|
|
|
return mesh
|
|
}
|
|
|
|
// TODO!
|
|
const entitiesMap = {
|
|
// item: null,
|
|
'glow_squid': 'squid'
|
|
}
|
|
|
|
// const unknownEntitiesSet = new Set()
|
|
|
|
class Entity {
|
|
constructor (version, type, scene) {
|
|
let mappedValue = entitiesMap[type]
|
|
// todo is it okay?
|
|
if (mappedValue === null) return
|
|
else if (mappedValue) type = mappedValue
|
|
|
|
const e = entities[type]
|
|
if (!e) {
|
|
// if (unknownEntitiesSet.has(type)) throw new Error('ignore...')
|
|
// unknownEntitiesSet.add(type)
|
|
throw new Error(`Unknown entity ${type}`)
|
|
}
|
|
|
|
this.mesh = new THREE.Object3D()
|
|
for (const [name, jsonModel] of Object.entries(e.geometry)) {
|
|
const texture = e.textures[name]
|
|
if (!texture) continue
|
|
// console.log(JSON.stringify(jsonModel, null, 2))
|
|
const mesh = getMesh(texture.replace('textures', 'textures/' + version) + '.png', jsonModel)
|
|
/* const skeletonHelper = new THREE.SkeletonHelper( mesh )
|
|
skeletonHelper.material.linewidth = 2
|
|
scene.add( skeletonHelper ) */
|
|
this.mesh.add(mesh)
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = Entity
|