import { Triangle, AABB, getworldcoord, clipTriangleOutside, clipTriangleInside, getTriangleAABB, } from './Geometry'; import { icosphere, monkey } from './obj'; import { triangle_create } from './Polygon'; const instance_center = Scene.getItem("6hfQXGTm") as Cuboid; instance_center.width = 0; instance_center.height = 0; instance_center.length = 0; const Default_color = null;//new Color(255, 255, 255); const Default_size = new Vector3( 1, 1, 1); const Default_pos = new Vector3( 0, 0, 0); export interface ModelData { center: Cuboid; vertices: Vector3[]; triangles: Triangle[]; polygons: any[][]; bounds: AABB; } export function load_model( obj: string, size: Vector3 = Default_size, position: Vector3 = Default_pos, color: Color = Default_color, render: boolean, ): ModelData { const vertices: Vector3[] = []; const triangles: Triangle[] = []; const polygons: any[][] = []; const scale = new Vector3( size?.x ?? Default_size.x, size?.y ?? Default_size.y, size?.z ?? Default_size.z ); let minX = Infinity; let maxX = -Infinity; let minY = Infinity; let maxY = -Infinity; let minZ = Infinity; let maxZ = -Infinity; const lines = obj.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (!line || line.startsWith("#")) continue; const parts = line.split(/\s+/); const type = parts[0]; if (type === "v") { const vx = Number(parts[1]) * scale.x; const vy = Number(parts[2]) * scale.y; const vz = Number(parts[3]) * scale.z; const vertex = new Vector3(vx, vy, vz); vertices.push(vertex); if (vx < minX) minX = vx; if (vx > maxX) maxX = vx; if (vy < minY) minY = vy; if (vy > maxY) maxY = vy; if (vz < minZ) minZ = vz; if (vz > maxZ) maxZ = vz; } else if (type === "f") { const faceIndices: number[] = []; for (let j = 1; j < parts.length; j++) { faceIndices.push( parseInt(parts[j].split("/")[0], 10) - 1 ); } // Triangulate polygon face using a fan for (let j = 1; j < faceIndices.length - 1; j++) { const pa = vertices[faceIndices[0]]; const pb = vertices[faceIndices[j]]; const pc = vertices[faceIndices[j + 1]]; if (!pa || !pb || !pc) continue; const boundtri:AABB = { minX: Math.min(pa.x, pb.x, pc.x), maxX: Math.max(pa.x, pb.x, pc.x), minY: Math.min(pa.y, pb.y, pc.y), maxY: Math.max(pa.y, pb.y, pc.y), minZ: Math.min(pa.z, pb.z, pc.z), maxZ: Math.max(pa.z, pb.z, pc.z) } // Mathematical triangle const triangle: Triangle = { a: pa, b: pb, c: pc, bounds: boundtri, }; triangles.push(triangle); if (render){ // Rendered polygon const polygon = triangle_create( pa, pb, pc, color ?? Default_color ); polygons.push(polygon); } } } } const centerPos = vertices.length > 0 ? new Vector3( (minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2 ) : new Vector3(0, 0, 0); const center = instance_center.copy(); center.active = true; center.transform.position = centerPos; for (let i = 0; i < polygons.length; i++) { for (let j = 0; j < polygons[i].length; j++) { center.add(polygons[i][j]); } } center.transform.position = position ?? Default_pos; const bounds: AABB = { minX, maxX, minY, maxY, minZ, maxZ }; return { center, vertices, triangles, polygons, bounds }; } export function sub_model(cutter:Triangle[],cut_object:Triangle[],color:Color = Default_color) : ModelData{ let SubtractModelAABB:AABB = { minX:1e100, minY:1e100, minZ:1e100, maxX:-1e100, maxY:-1e100, maxZ:-1e100, } let Polygons = []; let Triangles = []; let Vertices = []; // A's surface, minus whatever's inside B cut_object.forEach(tria => { clipTriangleOutside(tria, cutter).forEach(t => { SubtractModelAABB = { minX:Math.min(SubtractModelAABB.minX,t.a.x,t.b.x,t.c.x), minY:Math.min(SubtractModelAABB.minY,t.a.y,t.b.y,t.c.y), minZ:Math.min(SubtractModelAABB.minZ,t.a.z,t.b.z,t.c.z), maxX:Math.max(SubtractModelAABB.maxX,t.a.x,t.b.x,t.c.x), maxY:Math.max(SubtractModelAABB.maxY,t.a.y,t.b.y,t.c.y), maxZ:Math.max(SubtractModelAABB.maxZ,t.a.z,t.b.z,t.c.z), } const tri:Triangle = { a:t.a, b:t.b, c:t.c, bounds:getTriangleAABB(t.a,t.b,t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); // B's surface, only the part that's inside A cutter.forEach(trib => { clipTriangleInside(trib, cut_object).forEach(t => { SubtractModelAABB = { minX:Math.min(SubtractModelAABB.minX,t.a.x,t.b.x,t.c.x), minY:Math.min(SubtractModelAABB.minY,t.a.y,t.b.y,t.c.y), minZ:Math.min(SubtractModelAABB.minZ,t.a.z,t.b.z,t.c.z), maxX:Math.max(SubtractModelAABB.maxX,t.a.x,t.b.x,t.c.x), maxY:Math.max(SubtractModelAABB.maxY,t.a.y,t.b.y,t.c.y), maxZ:Math.max(SubtractModelAABB.maxZ,t.a.z,t.b.z,t.c.z), } const tri:Triangle = { a:t.a, b:t.b, c:t.c, bounds:getTriangleAABB(t.a,t.b,t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); const CenterPos = new Vector3( (SubtractModelAABB.maxX + SubtractModelAABB.minX)/2, (SubtractModelAABB.maxY + SubtractModelAABB.minY)/2, (SubtractModelAABB.maxZ + SubtractModelAABB.minZ)/2, ) const center = instance_center.copy(); center.transform.position = CenterPos; center.active = true; Triangles.forEach(t => { const poly = triangle_create(t.a,t.b,t.c,color ?? Default_color); Polygons.push(poly); poly.forEach(isotri => { center.add(isotri); }); }); const subdata:ModelData = { center: center, vertices: Vertices, triangles: Triangles, polygons: Polygons, bounds: SubtractModelAABB, } return subdata } export function mask_model(cutter: Triangle[], cut_object: Triangle[], color:Color = Default_color): ModelData { let MaskModelAABB: AABB = { minX: 1e100, minY: 1e100, minZ: 1e100, maxX: -1e100, maxY: -1e100, maxZ: -1e100, }; let Polygons = []; let Triangles = []; let Vertices = []; // 1. A's surface, only the part that is INSIDE B cut_object.forEach(tria => { clipTriangleInside(tria, cutter).forEach(t => { MaskModelAABB = { minX: Math.min(MaskModelAABB.minX, t.a.x, t.b.x, t.c.x), minY: Math.min(MaskModelAABB.minY, t.a.y, t.b.y, t.c.y), minZ: Math.min(MaskModelAABB.minZ, t.a.z, t.b.z, t.c.z), maxX: Math.max(MaskModelAABB.maxX, t.a.x, t.b.x, t.c.x), maxY: Math.max(MaskModelAABB.maxY, t.a.y, t.b.y, t.c.y), maxZ: Math.max(MaskModelAABB.maxZ, t.a.z, t.b.z, t.c.z), }; const tri: Triangle = { a: t.a, b: t.b, c: t.c, bounds: getTriangleAABB(t.a, t.b, t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); // 2. B's surface, only the part that is INSIDE A cutter.forEach(trib => { clipTriangleInside(trib, cut_object).forEach(t => { MaskModelAABB = { minX: Math.min(MaskModelAABB.minX, t.a.x, t.b.x, t.c.x), minY: Math.min(MaskModelAABB.minY, t.a.y, t.b.y, t.c.y), minZ: Math.min(MaskModelAABB.minZ, t.a.z, t.b.z, t.c.z), maxX: Math.max(MaskModelAABB.maxX, t.a.x, t.b.x, t.c.x), maxY: Math.max(MaskModelAABB.maxY, t.a.y, t.b.y, t.c.y), maxZ: Math.max(MaskModelAABB.maxZ, t.a.z, t.b.z, t.c.z), }; const tri: Triangle = { a: t.a, b: t.b, c: t.c, bounds: getTriangleAABB(t.a, t.b, t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); const CenterPos = new Vector3( (MaskModelAABB.maxX + MaskModelAABB.minX) / 2, (MaskModelAABB.maxY + MaskModelAABB.minY) / 2, (MaskModelAABB.maxZ + MaskModelAABB.minZ) / 2, ); const center = instance_center.copy(); center.transform.position = CenterPos; center.active = true; Triangles.forEach(t => { const poly = triangle_create(t.a, t.b, t.c, color ?? Default_color); Polygons.push(poly); poly.forEach(isotri => { center.add(isotri); }); }); const maskdata: ModelData = { center: center, vertices: Vertices, triangles: Triangles, polygons: Polygons, bounds: MaskModelAABB, }; return maskdata; } export function union_model(cutter: Triangle[], cut_object: Triangle[] , color:Color = Default_color): ModelData { let UnionModelAABB: AABB = { minX: 1e100, minY: 1e100, minZ: 1e100, maxX: -1e100, maxY: -1e100, maxZ: -1e100, }; let Polygons = []; let Triangles = []; let Vertices = []; // 1. A's surface, only the part that is OUTSIDE B cut_object.forEach(tria => { clipTriangleOutside(tria, cutter).forEach(t => { UnionModelAABB = { minX: Math.min(UnionModelAABB.minX, t.a.x, t.b.x, t.c.x), minY: Math.min(UnionModelAABB.minY, t.a.y, t.b.y, t.c.y), minZ: Math.min(UnionModelAABB.minZ, t.a.z, t.b.z, t.c.z), maxX: Math.max(UnionModelAABB.maxX, t.a.x, t.b.x, t.c.x), maxY: Math.max(UnionModelAABB.maxY, t.a.y, t.b.y, t.c.y), maxZ: Math.max(UnionModelAABB.maxZ, t.a.z, t.b.z, t.c.z), }; const tri: Triangle = { a: t.a, b: t.b, c: t.c, bounds: getTriangleAABB(t.a, t.b, t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); // 2. B's surface, only the part that is OUTSIDE A cutter.forEach(trib => { clipTriangleOutside(trib, cut_object).forEach(t => { UnionModelAABB = { minX: Math.min(UnionModelAABB.minX, t.a.x, t.b.x, t.c.x), minY: Math.min(UnionModelAABB.minY, t.a.y, t.b.y, t.c.y), minZ: Math.min(UnionModelAABB.minZ, t.a.z, t.b.z, t.c.z), maxX: Math.max(UnionModelAABB.maxX, t.a.x, t.b.x, t.c.x), maxY: Math.max(UnionModelAABB.maxY, t.a.y, t.b.y, t.c.y), maxZ: Math.max(UnionModelAABB.maxZ, t.a.z, t.b.z, t.c.z), }; const tri: Triangle = { a: t.a, b: t.b, c: t.c, bounds: getTriangleAABB(t.a, t.b, t.c), }; Triangles.push(tri); Vertices.push(t.a); Vertices.push(t.b); Vertices.push(t.c); }); }); const CenterPos = new Vector3( (UnionModelAABB.maxX + UnionModelAABB.minX) / 2, (UnionModelAABB.maxY + UnionModelAABB.minY) / 2, (UnionModelAABB.maxZ + UnionModelAABB.minZ) / 2, ); const center = instance_center.copy(); center.transform.position = CenterPos; center.active = true; Triangles.forEach(t => { const poly = triangle_create(t.a, t.b, t.c, color ?? Default_color); Polygons.push(poly); poly.forEach(isotri => { center.add(isotri); }); }); const uniondata: ModelData = { center: center, vertices: Vertices, triangles: Triangles, polygons: Polygons, bounds: UnionModelAABB, }; return uniondata; } export function scale(modelTriangles: any[][], factor: number): void { for (let i = 0; i < modelTriangles.length; i++) { const face = modelTriangles[i]; for (let j = 0; j < face.length; j++) { const tri = face[j]; // 1. Scale geometric dimensions tri.height *= factor; tri.bottomLength *= factor; // 2. Scale local position relative to the center pivot const pos = tri.transform.position; tri.transform.position = new Vector3( pos.x * factor, pos.y * factor, pos.z * factor ); } } }