import * as path from 'path'; import * as THREE from 'three'; import { MeshBuilder } from '../lib/vox-mesh-builder'; import { IEntity } from '../../../../common/engine/state'; import { VoxelData, IVoxel } from '../../../../common/engine/vox'; import { Asset } from '../../../../common/engine/asset'; const VoxCache: {[key: string]: Promise} = {}; const BuildVoxMesh = (voxelBin, data) => { const builder = new MeshBuilder(voxelBin, { voxelSize: data.size, vertexColor: true, optimizeFaces: false, jitter: data.jitter || 0 }); const mesh = builder.createMesh(); mesh.castShadow = true; mesh.receiveShadow = true; return mesh; }; const Get = async (file: string) => { if(VoxCache[file]) { return VoxCache[file]; } let resolved = false; return VoxCache[file] = new Promise(resolve => { // setup watch Asset.watch(file, (payload) => { const mesh = BuildVoxMesh(payload, { size: 0.5, jitter: 0 }); VoxCache[file] = Promise.resolve(mesh); if(resolved) { return; } resolve(mesh); resolved = true; }); }); }; export interface ITileComponent { vox: string; atlas: {[key: number]: string}; size: number; } export class TileMesh extends THREE.Object3D { data: ITileComponent; bodyUpdate: (newBody: any) => void; constructor(data: ITileComponent | Promise, fn: (newBody: any) => void) { super(); // Add support for watching an image file for the tilemap this.update_vox = this.update_vox.bind(this); this.update(data); this.bodyUpdate = fn; } async update_vox(voxData: VoxelData) { if(this.children[0]) { this.remove(this.children[0]); } const parent = new THREE.Object3D(); this.add(parent); const body = {bodies:[]}; // could get garbled if mesh resolves slow await Promise.all(voxData.voxels.map(async voxel => { const mesh = this.data.atlas[voxel.colorIndex] ? this.data.atlas[voxel.colorIndex] : this.data.atlas[0]; const o3d = (await Get(mesh)).clone(); o3d.position.set(voxel.x, voxel.z, voxel.y).multiplyScalar(2.5); body.bodies.push({ pos: o3d.position.toArray(), size: [2.5, 2.5, 2.5], mass: 0 }); o3d.rotateY(THREE.Math.degToRad([90, 180, 270, 0][Math.floor(Math.random() * 4)])); parent.add(o3d); })); this.bodyUpdate(body); } async update(data: ITileComponent | Promise) { if(this.data) { Asset.off(this.data.vox, this.update_vox); } this.data = await data; console.log('tilemap watching', this.data.vox); Asset.watch(this.data.vox, this.update_vox); // Rip through the atlas Object.keys(this.data.atlas).forEach(key => { Get(this.data.atlas[key]); }); } }