/// import {isObjectManifold} from "./validation"; declare const THREE: typeof import('three'); const contentTypes = `` const rels = `` const colourHexes = [ { hex: "#6EC9F4", name: "Light Blue" }, { hex: "#F7D84A", name: "Yellow" }, { hex: "#F4A428", name: "Orange" }, { hex: "#E64545", name: "Red" }, { hex: "#AD4ED2", name: "Purple" }, { hex: "#5B89E5", name: "Blue" }, { hex: "#30B75B", name: "Green" }, { hex: "#7CF38C", name: "Lime" }, { hex: "#F874D6", name: "Pink" }, { hex: "#D8D8D8", name: "Silver" }, ]; export type MFObject = {name: string, vertices: Vertex[], triangles: IndexedTriangle[]} export type Vertex = [number, number, number] export type RawTriangle = {vertices: Vertex[], colour: number, object: number} export type IndexedTriangle = {indexes: [number, number, number]} export async function compile(units: string, splitGroups: undefined|"group"|"marker"): Promise { // @ts-ignore const zip = new JSZip() // Basic stuff zip.file("[Content_Types].xml", contentTypes) zip.folder("_rels")?.file(".rels", rels) // Geometry const trianglesRaw: RawTriangle[] = [] const objects: {[key: number]: MFObject} = {} let objectIndex = 1000 function addGeometry(geometry: THREE.BufferGeometry, mesh: {matrixWorld: THREE.Matrix4}, cube: {color: number}, object: number) { const position = geometry.attributes.position const index = geometry.index! const positions = position.array const indices = index.array const matrix = mesh.matrixWorld for (let i = 0; i < indices.length; i += 3) { const v: [number, number, number][] = []; for (let j = 0; j < 3; j++) { const idx = indices[i + j] * 3; const x = positions[idx]; const y = positions[idx + 1]; const z = positions[idx + 2]; const vec = new THREE.Vector3(x, y, z).applyMatrix4(matrix); v.push([vec.x, vec.z, vec.y]); } trianglesRaw.push({vertices: v, colour: +cube.color - 1, object}); } } function addCube(cube: Cube, object: number) { const mesh = cube.mesh mesh.updateMatrixWorld() const geometry = mesh.geometry addGeometry(geometry, mesh, cube, object); } function addMesh(meshObj: Mesh, object: number) { const mesh = meshObj.mesh mesh.updateMatrixWorld() mesh.traverse(child => { if (!(child as THREE.Mesh).isMesh) { return } const childMesh = child as THREE.Mesh const geometry = childMesh.geometry addGeometry(geometry, mesh, meshObj, object); }) } function addGroup(group: Group) { objects[++objectIndex] ??= {name: group.name ?? "Group", vertices: [], triangles: []} addChildren(group.children, objectIndex) } function addChildren(children: OutlinerNode[], objectIndex: number) { for (const child of children) { if (child instanceof Cube) addCube(child, objectIndex) else if (child instanceof Mesh) addMesh(child, objectIndex) else if (child instanceof Group) addGroup(child) } } if (splitGroups !== "marker") objects[objectIndex] ??= {name: Project?.name && Project.name.length > 0 ? Project.name : "BBRoot", vertices: [], triangles: []} addChildren(splitGroups === "group" ? Outliner.root : [...Cube.all, ...Mesh.all], objectIndex) for (const triangle of trianglesRaw) { const indexes = [] if (splitGroups === "marker") objects[triangle.colour] ??= {name: colourHexes[triangle.colour].name, vertices: [], triangles: []} const object = objects[splitGroups === "marker" ? triangle.colour : triangle.object] for (const vertex of triangle.vertices) { const [x, y, z] = vertex let index = object.vertices.findIndex(d => d[0] === x && d[1] === y && d[2] === z) if (index === -1) { index = object.vertices.length object.vertices.push(vertex) } indexes.push(index) } object.triangles.push({indexes} as IndexedTriangle) } const filteredObjects = Object.values(objects).filter(({vertices}) => vertices.length > 0) const model = ` ${filteredObjects.map(({name, vertices, triangles}, index) => ` ${vertices.map(([x, y, z]) => ``).join("")} ${triangles.map(({indexes}) => ``)} `).join("\n")} ${filteredObjects.map(({vertices}, index) => vertices.length > 0 ? `` : "").join("\n")} ` for (const object of filteredObjects) { if (isObjectManifold(object)) continue console.error(object, "Object failed validation") } zip.folder("3D")?.file("3dmodel.model", model) return zip.generateAsync({type: "arraybuffer", compression: "DEFLATE"}) }