UNPKG

@threepipe/plugins-extra-importers

Version:

Extra Threepipe plugins for importing several file types.

1,558 lines 575 kB
/** * @license * @threepipe/plugins-extra-importers v0.2.3 * Copyright 2022-2025 repalash <palash@shaders.app> * Apache-2.0 License * See ./dependencies.txt for any bundled third-party dependencies and licenses. */ import { Loader, LoaderUtils, FileLoader, Group, MeshPhongMaterial, DoubleSide, AdditiveBlending, BufferGeometry, Mesh, Float32BufferAttribute, Matrix4, TextureLoader, Color, SRGBColorSpace, BufferAttribute, MeshStandardMaterial, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, LinearFilter, LinearMipmapLinearFilter, NearestFilter, DataTextureLoader, Vector3, Quaternion, MeshBasicMaterial, Scene, MathUtils, AnimationClip, VectorKeyframeTrack, QuaternionKeyframeTrack, MeshLambertMaterial, Vector2, FrontSide, PerspectiveCamera, OrthographicCamera, AmbientLight, SpotLight, PointLight, DirectionalLight, Bone, LineBasicMaterial, SkinnedMesh, Line, LineSegments, Skeleton, NumberKeyframeTrack, Int32BufferAttribute, PointsMaterial, Points, RawShaderMaterial, Object3D, SphereGeometry, BackSide, DataTexture, BoxGeometry, ConeGeometry, CylinderGeometry, ShapeUtils, Ray, ShaderMaterial, UniformsUtils, UniformsLib, BaseImporterPlugin, Importer, SkeletonHelper, PhysicalMaterial } from "threepipe"; class TDSLoader extends Loader { constructor(manager) { super(manager); this.debug = false; this.group = null; this.materials = []; this.meshes = []; } /** * Load 3ds file from url. * * @method load * @param {[type]} url URL for the file. * @param {Function} onLoad onLoad callback, receives group Object3D as argument. * @param {Function} onProgress onProgress callback. * @param {Function} onError onError callback. */ load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === "" ? LoaderUtils.extractUrlBase(url) : this.path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function(data) { try { onLoad(scope.parse(data, path)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } /** * Parse arraybuffer data and load 3ds file. * * @method parse * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. * @param {String} path Path for external resources. * @return {Group} Group loaded from 3ds file. */ parse(arraybuffer, path) { this.group = new Group(); this.materials = []; this.meshes = []; this.readFile(arraybuffer, path); for (let i = 0; i < this.meshes.length; i++) { this.group.add(this.meshes[i]); } return this.group; } /** * Decode file content to read 3ds data. * * @method readFile * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded. * @param {String} path Path for external resources. */ readFile(arraybuffer, path) { const data = new DataView(arraybuffer); const chunk = new Chunk(data, 0, this.debugMessage); if (chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC) { let next = chunk.readChunk(); while (next) { if (next.id === M3D_VERSION) { const version = next.readDWord(); this.debugMessage("3DS file version: " + version); } else if (next.id === MDATA) { this.readMeshData(next, path); } else { this.debugMessage("Unknown main chunk: " + next.hexId); } next = chunk.readChunk(); } } this.debugMessage("Parsed " + this.meshes.length + " meshes"); } /** * Read mesh data chunk. * * @method readMeshData * @param {Chunk} chunk to read mesh from * @param {String} path Path for external resources. */ readMeshData(chunk, path) { let next = chunk.readChunk(); while (next) { if (next.id === MESH_VERSION) { const version = +next.readDWord(); this.debugMessage("Mesh Version: " + version); } else if (next.id === MASTER_SCALE) { const scale = next.readFloat(); this.debugMessage("Master scale: " + scale); this.group.scale.set(scale, scale, scale); } else if (next.id === NAMED_OBJECT) { this.debugMessage("Named Object"); this.readNamedObject(next); } else if (next.id === MAT_ENTRY) { this.debugMessage("Material"); this.readMaterialEntry(next, path); } else { this.debugMessage("Unknown MDATA chunk: " + next.hexId); } next = chunk.readChunk(); } } /** * Read named object chunk. * * @method readNamedObject * @param {Chunk} chunk Chunk in use. */ readNamedObject(chunk) { const name = chunk.readString(); let next = chunk.readChunk(); while (next) { if (next.id === N_TRI_OBJECT) { const mesh = this.readMesh(next); mesh.name = name; this.meshes.push(mesh); } else { this.debugMessage("Unknown named object chunk: " + next.hexId); } next = chunk.readChunk(); } } /** * Read material data chunk and add it to the material list. * * @method readMaterialEntry * @param {Chunk} chunk Chunk in use. * @param {String} path Path for external resources. */ readMaterialEntry(chunk, path) { let next = chunk.readChunk(); const material = new MeshPhongMaterial(); while (next) { if (next.id === MAT_NAME) { material.name = next.readString(); this.debugMessage(" Name: " + material.name); } else if (next.id === MAT_WIRE) { this.debugMessage(" Wireframe"); material.wireframe = true; } else if (next.id === MAT_WIRE_SIZE) { const value = next.readByte(); material.wireframeLinewidth = value; this.debugMessage(" Wireframe Thickness: " + value); } else if (next.id === MAT_TWO_SIDE) { material.side = DoubleSide; this.debugMessage(" DoubleSided"); } else if (next.id === MAT_ADDITIVE) { this.debugMessage(" Additive Blending"); material.blending = AdditiveBlending; } else if (next.id === MAT_DIFFUSE) { this.debugMessage(" Diffuse Color"); material.color = this.readColor(next); } else if (next.id === MAT_SPECULAR) { this.debugMessage(" Specular Color"); material.specular = this.readColor(next); } else if (next.id === MAT_AMBIENT) { this.debugMessage(" Ambient color"); material.color = this.readColor(next); } else if (next.id === MAT_SHININESS) { const shininess = this.readPercentage(next); material.shininess = shininess * 100; this.debugMessage(" Shininess : " + shininess); } else if (next.id === MAT_TRANSPARENCY) { const transparency = this.readPercentage(next); material.opacity = 1 - transparency; this.debugMessage(" Transparency : " + transparency); material.transparent = material.opacity < 1 ? true : false; } else if (next.id === MAT_TEXMAP) { this.debugMessage(" ColorMap"); material.map = this.readMap(next, path); } else if (next.id === MAT_BUMPMAP) { this.debugMessage(" BumpMap"); material.bumpMap = this.readMap(next, path); } else if (next.id === MAT_OPACMAP) { this.debugMessage(" OpacityMap"); material.alphaMap = this.readMap(next, path); } else if (next.id === MAT_SPECMAP) { this.debugMessage(" SpecularMap"); material.specularMap = this.readMap(next, path); } else { this.debugMessage(" Unknown material chunk: " + next.hexId); } next = chunk.readChunk(); } this.materials[material.name] = material; } /** * Read mesh data chunk. * * @method readMesh * @param {Chunk} chunk Chunk in use. * @return {Mesh} The parsed mesh. */ readMesh(chunk) { let next = chunk.readChunk(); const geometry = new BufferGeometry(); const material = new MeshPhongMaterial(); const mesh = new Mesh(geometry, material); mesh.name = "mesh"; while (next) { if (next.id === POINT_ARRAY) { const points = next.readWord(); this.debugMessage(" Vertex: " + points); const vertices = []; for (let i = 0; i < points; i++) { vertices.push(next.readFloat()); vertices.push(next.readFloat()); vertices.push(next.readFloat()); } geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); } else if (next.id === FACE_ARRAY) { this.readFaceArray(next, mesh); } else if (next.id === TEX_VERTS) { const texels = next.readWord(); this.debugMessage(" UV: " + texels); const uvs = []; for (let i = 0; i < texels; i++) { uvs.push(next.readFloat()); uvs.push(next.readFloat()); } geometry.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } else if (next.id === MESH_MATRIX) { this.debugMessage(" Tranformation Matrix (TODO)"); const values = []; for (let i = 0; i < 12; i++) { values[i] = next.readFloat(); } const matrix = new Matrix4(); matrix.elements[0] = values[0]; matrix.elements[1] = values[6]; matrix.elements[2] = values[3]; matrix.elements[3] = values[9]; matrix.elements[4] = values[2]; matrix.elements[5] = values[8]; matrix.elements[6] = values[5]; matrix.elements[7] = values[11]; matrix.elements[8] = values[1]; matrix.elements[9] = values[7]; matrix.elements[10] = values[4]; matrix.elements[11] = values[10]; matrix.elements[12] = 0; matrix.elements[13] = 0; matrix.elements[14] = 0; matrix.elements[15] = 1; matrix.transpose(); const inverse = new Matrix4(); inverse.copy(matrix).invert(); geometry.applyMatrix4(inverse); matrix.decompose(mesh.position, mesh.quaternion, mesh.scale); } else { this.debugMessage(" Unknown mesh chunk: " + next.hexId); } next = chunk.readChunk(); } geometry.computeVertexNormals(); return mesh; } /** * Read face array data chunk. * * @method readFaceArray * @param {Chunk} chunk Chunk in use. * @param {Mesh} mesh Mesh to be filled with the data read. */ readFaceArray(chunk, mesh) { const faces = chunk.readWord(); this.debugMessage(" Faces: " + faces); const index = []; for (let i = 0; i < faces; ++i) { index.push(chunk.readWord(), chunk.readWord(), chunk.readWord()); chunk.readWord(); } mesh.geometry.setIndex(index); let materialIndex = 0; let start = 0; while (!chunk.endOfChunk) { const subchunk = chunk.readChunk(); if (subchunk.id === MSH_MAT_GROUP) { this.debugMessage(" Material Group"); const group = this.readMaterialGroup(subchunk); const count = group.index.length * 3; mesh.geometry.addGroup(start, count, materialIndex); start += count; materialIndex++; const material = this.materials[group.name]; if (Array.isArray(mesh.material) === false) mesh.material = []; if (material !== void 0) { mesh.material.push(material); } } else { this.debugMessage(" Unknown face array chunk: " + subchunk.hexId); } } if (mesh.material.length === 1) mesh.material = mesh.material[0]; } /** * Read texture map data chunk. * * @method readMap * @param {Chunk} chunk Chunk in use. * @param {String} path Path for external resources. * @return {Texture} Texture read from this data chunk. */ readMap(chunk, path) { let next = chunk.readChunk(); let texture = {}; const loader = new TextureLoader(this.manager); loader.setPath(this.resourcePath || path).setCrossOrigin(this.crossOrigin); while (next) { if (next.id === MAT_MAPNAME) { const name = next.readString(); texture = loader.load(name); this.debugMessage(" File: " + path + name); } else if (next.id === MAT_MAP_UOFFSET) { texture.offset.x = next.readFloat(); this.debugMessage(" OffsetX: " + texture.offset.x); } else if (next.id === MAT_MAP_VOFFSET) { texture.offset.y = next.readFloat(); this.debugMessage(" OffsetY: " + texture.offset.y); } else if (next.id === MAT_MAP_USCALE) { texture.repeat.x = next.readFloat(); this.debugMessage(" RepeatX: " + texture.repeat.x); } else if (next.id === MAT_MAP_VSCALE) { texture.repeat.y = next.readFloat(); this.debugMessage(" RepeatY: " + texture.repeat.y); } else { this.debugMessage(" Unknown map chunk: " + next.hexId); } next = chunk.readChunk(); } return texture; } /** * Read material group data chunk. * * @method readMaterialGroup * @param {Chunk} chunk Chunk in use. * @return {Object} Object with name and index of the object. */ readMaterialGroup(chunk) { const name = chunk.readString(); const numFaces = chunk.readWord(); this.debugMessage(" Name: " + name); this.debugMessage(" Faces: " + numFaces); const index = []; for (let i = 0; i < numFaces; ++i) { index.push(chunk.readWord()); } return { name, index }; } /** * Read a color value. * * @method readColor * @param {Chunk} chunk Chunk. * @return {Color} Color value read.. */ readColor(chunk) { const subChunk = chunk.readChunk(); const color = new Color(); if (subChunk.id === COLOR_24 || subChunk.id === LIN_COLOR_24) { const r = subChunk.readByte(); const g = subChunk.readByte(); const b = subChunk.readByte(); color.setRGB(r / 255, g / 255, b / 255); this.debugMessage(" Color: " + color.r + ", " + color.g + ", " + color.b); } else if (subChunk.id === COLOR_F || subChunk.id === LIN_COLOR_F) { const r = subChunk.readFloat(); const g = subChunk.readFloat(); const b = subChunk.readFloat(); color.setRGB(r, g, b); this.debugMessage(" Color: " + color.r + ", " + color.g + ", " + color.b); } else { this.debugMessage(" Unknown color chunk: " + subChunk.hexId); } return color; } /** * Read percentage value. * * @method readPercentage * @param {Chunk} chunk Chunk to read data from. * @return {Number} Data read from the dataview. */ readPercentage(chunk) { const subChunk = chunk.readChunk(); switch (subChunk.id) { case INT_PERCENTAGE: return subChunk.readShort() / 100; case FLOAT_PERCENTAGE: return subChunk.readFloat(); default: this.debugMessage(" Unknown percentage chunk: " + subChunk.hexId); return 0; } } /** * Print debug message to the console. * * Is controlled by a flag to show or hide debug messages. * * @method debugMessage * @param {Object} message Debug message to print to the console. */ debugMessage(message) { if (this.debug) { console.log(message); } } } class Chunk { /** * Create a new chunk * * @class Chunk * @param {DataView} data DataView to read from. * @param {Number} position in data. * @param {Function} debugMessage logging callback. */ constructor(data, position, debugMessage) { this.data = data; this.offset = position; this.position = position; this.debugMessage = debugMessage; if (this.debugMessage instanceof Function) { this.debugMessage = function() { }; } this.id = this.readWord(); this.size = this.readDWord(); this.end = this.offset + this.size; if (this.end > data.byteLength) { this.debugMessage("Bad chunk size for chunk at " + position); } } /** * read a sub cchunk. * * @method readChunk * @return {Chunk | null} next sub chunk */ readChunk() { if (this.endOfChunk) { return null; } try { const next = new Chunk(this.data, this.position, this.debugMessage); this.position += next.size; return next; } catch (e) { this.debugMessage("Unable to read chunk at " + this.position); return null; } } /** * return the ID of this chunk as Hex * * @method idToString * @return {String} hex-string of id */ get hexId() { return this.id.toString(16); } get endOfChunk() { return this.position >= this.end; } /** * Read byte value. * * @method readByte * @return {Number} Data read from the dataview. */ readByte() { const v = this.data.getUint8(this.position, true); this.position += 1; return v; } /** * Read 32 bit float value. * * @method readFloat * @return {Number} Data read from the dataview. */ readFloat() { try { const v = this.data.getFloat32(this.position, true); this.position += 4; return v; } catch (e) { this.debugMessage(e + " " + this.position + " " + this.data.byteLength); return 0; } } /** * Read 32 bit signed integer value. * * @method readInt * @return {Number} Data read from the dataview. */ readInt() { const v = this.data.getInt32(this.position, true); this.position += 4; return v; } /** * Read 16 bit signed integer value. * * @method readShort * @return {Number} Data read from the dataview. */ readShort() { const v = this.data.getInt16(this.position, true); this.position += 2; return v; } /** * Read 64 bit unsigned integer value. * * @method readDWord * @return {Number} Data read from the dataview. */ readDWord() { const v = this.data.getUint32(this.position, true); this.position += 4; return v; } /** * Read 32 bit unsigned integer value. * * @method readWord * @return {Number} Data read from the dataview. */ readWord() { const v = this.data.getUint16(this.position, true); this.position += 2; return v; } /** * Read NULL terminated ASCII string value from chunk-pos. * * @method readString * @return {String} Data read from the dataview. */ readString() { let s = ""; let c = this.readByte(); while (c) { s += String.fromCharCode(c); c = this.readByte(); } return s; } } const M3DMAGIC = 19789; const MLIBMAGIC = 15786; const CMAGIC = 49725; const M3D_VERSION = 2; const COLOR_F = 16; const COLOR_24 = 17; const LIN_COLOR_24 = 18; const LIN_COLOR_F = 19; const INT_PERCENTAGE = 48; const FLOAT_PERCENTAGE = 49; const MDATA = 15677; const MESH_VERSION = 15678; const MASTER_SCALE = 256; const MAT_ENTRY = 45055; const MAT_NAME = 40960; const MAT_AMBIENT = 40976; const MAT_DIFFUSE = 40992; const MAT_SPECULAR = 41008; const MAT_SHININESS = 41024; const MAT_TRANSPARENCY = 41040; const MAT_TWO_SIDE = 41089; const MAT_ADDITIVE = 41091; const MAT_WIRE = 41093; const MAT_WIRE_SIZE = 41095; const MAT_TEXMAP = 41472; const MAT_OPACMAP = 41488; const MAT_BUMPMAP = 41520; const MAT_SPECMAP = 41476; const MAT_MAPNAME = 41728; const MAT_MAP_USCALE = 41812; const MAT_MAP_VSCALE = 41814; const MAT_MAP_UOFFSET = 41816; const MAT_MAP_VOFFSET = 41818; const NAMED_OBJECT = 16384; const N_TRI_OBJECT = 16640; const POINT_ARRAY = 16656; const FACE_ARRAY = 16672; const MSH_MAT_GROUP = 16688; const TEX_VERTS = 16704; const MESH_MATRIX = 16736; /*! fflate - fast JavaScript compression/decompression <https://101arrowz.github.io/fflate> Licensed under MIT. https://github.com/101arrowz/fflate/blob/master/LICENSE version 0.6.9 */ var durl = function(c) { return URL.createObjectURL(new Blob([c], { type: "text/javascript" })); }; try { URL.revokeObjectURL(durl("")); } catch (e) { durl = function(c) { return "data:application/javascript;charset=UTF-8," + encodeURI(c); }; } var u8 = Uint8Array, u16 = Uint16Array, u32 = Uint32Array; var fleb = new u8([ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0 ]); var fdeb = new u8([ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0 ]); var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); var freb = function(eb, start) { var b = new u16(31); for (var i = 0; i < 31; ++i) { b[i] = start += 1 << eb[i - 1]; } var r = new u32(b[30]); for (var i = 1; i < 30; ++i) { for (var j = b[i]; j < b[i + 1]; ++j) { r[j] = j - b[i] << 5 | i; } } return [b, r]; }; var _a = freb(fleb, 2), fl = _a[0], revfl = _a[1]; fl[28] = 258, revfl[258] = 28; var _b = freb(fdeb, 0), fd = _b[0]; var rev = new u16(32768); for (var i = 0; i < 32768; ++i) { var x = (i & 43690) >>> 1 | (i & 21845) << 1; x = (x & 52428) >>> 2 | (x & 13107) << 2; x = (x & 61680) >>> 4 | (x & 3855) << 4; rev[i] = ((x & 65280) >>> 8 | (x & 255) << 8) >>> 1; } var hMap = function(cd, mb, r) { var s = cd.length; var i = 0; var l = new u16(mb); for (; i < s; ++i) ++l[cd[i] - 1]; var le = new u16(mb); for (i = 0; i < mb; ++i) { le[i] = le[i - 1] + l[i - 1] << 1; } var co; if (r) { co = new u16(1 << mb); var rvb = 15 - mb; for (i = 0; i < s; ++i) { if (cd[i]) { var sv = i << 4 | cd[i]; var r_1 = mb - cd[i]; var v = le[cd[i] - 1]++ << r_1; for (var m = v | (1 << r_1) - 1; v <= m; ++v) { co[rev[v] >>> rvb] = sv; } } } } else { co = new u16(s); for (i = 0; i < s; ++i) { if (cd[i]) { co[i] = rev[le[cd[i] - 1]++] >>> 15 - cd[i]; } } } return co; }; var flt = new u8(288); for (var i = 0; i < 144; ++i) flt[i] = 8; for (var i = 144; i < 256; ++i) flt[i] = 9; for (var i = 256; i < 280; ++i) flt[i] = 7; for (var i = 280; i < 288; ++i) flt[i] = 8; var fdt = new u8(32); for (var i = 0; i < 32; ++i) fdt[i] = 5; var flrm = /* @__PURE__ */ hMap(flt, 9, 1); var fdrm = /* @__PURE__ */ hMap(fdt, 5, 1); var max = function(a) { var m = a[0]; for (var i = 1; i < a.length; ++i) { if (a[i] > m) m = a[i]; } return m; }; var bits = function(d, p, m) { var o = p / 8 | 0; return (d[o] | d[o + 1] << 8) >> (p & 7) & m; }; var bits16 = function(d, p) { var o = p / 8 | 0; return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7); }; var shft = function(p) { return (p / 8 | 0) + (p & 7 && 1); }; var slc = function(v, s, e) { if (s == null || s < 0) s = 0; if (e == null || e > v.length) e = v.length; var n = new (v instanceof u16 ? u16 : v instanceof u32 ? u32 : u8)(e - s); n.set(v.subarray(s, e)); return n; }; var inflt = function(dat, buf, st) { var sl = dat.length; if (!sl || st && !st.l && sl < 5) return buf || new u8(0); var noBuf = !buf || st; var noSt = !st || st.i; if (!st) st = {}; if (!buf) buf = new u8(sl * 3); var cbuf = function(l2) { var bl = buf.length; if (l2 > bl) { var nbuf = new u8(Math.max(bl * 2, l2)); nbuf.set(buf); buf = nbuf; } }; var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n; var tbts = sl * 8; do { if (!lm) { st.f = final = bits(dat, pos, 1); var type = bits(dat, pos + 1, 3); pos += 3; if (!type) { var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l; if (t > sl) { if (noSt) throw "unexpected EOF"; break; } if (noBuf) cbuf(bt + l); buf.set(dat.subarray(s, t), bt); st.b = bt += l, st.p = pos = t * 8; continue; } else if (type == 1) lm = flrm, dm = fdrm, lbt = 9, dbt = 5; else if (type == 2) { var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4; var tl = hLit + bits(dat, pos + 5, 31) + 1; pos += 14; var ldt = new u8(tl); var clt = new u8(19); for (var i = 0; i < hcLen; ++i) { clt[clim[i]] = bits(dat, pos + i * 3, 7); } pos += hcLen * 3; var clb = max(clt), clbmsk = (1 << clb) - 1; var clm = hMap(clt, clb, 1); for (var i = 0; i < tl; ) { var r = clm[bits(dat, pos, clbmsk)]; pos += r & 15; var s = r >>> 4; if (s < 16) { ldt[i++] = s; } else { var c = 0, n = 0; if (s == 16) n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1]; else if (s == 17) n = 3 + bits(dat, pos, 7), pos += 3; else if (s == 18) n = 11 + bits(dat, pos, 127), pos += 7; while (n--) ldt[i++] = c; } } var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit); lbt = max(lt); dbt = max(dt); lm = hMap(lt, lbt, 1); dm = hMap(dt, dbt, 1); } else throw "invalid block type"; if (pos > tbts) { if (noSt) throw "unexpected EOF"; break; } } if (noBuf) cbuf(bt + 131072); var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1; var lpos = pos; for (; ; lpos = pos) { var c = lm[bits16(dat, pos) & lms], sym = c >>> 4; pos += c & 15; if (pos > tbts) { if (noSt) throw "unexpected EOF"; break; } if (!c) throw "invalid length/literal"; if (sym < 256) buf[bt++] = sym; else if (sym == 256) { lpos = pos, lm = null; break; } else { var add = sym - 254; if (sym > 264) { var i = sym - 257, b = fleb[i]; add = bits(dat, pos, (1 << b) - 1) + fl[i]; pos += b; } var d = dm[bits16(dat, pos) & dms], dsym = d >>> 4; if (!d) throw "invalid distance"; pos += d & 15; var dt = fd[dsym]; if (dsym > 3) { var b = fdeb[dsym]; dt += bits16(dat, pos) & (1 << b) - 1, pos += b; } if (pos > tbts) { if (noSt) throw "unexpected EOF"; break; } if (noBuf) cbuf(bt + 131072); var end = bt + add; for (; bt < end; bt += 4) { buf[bt] = buf[bt - dt]; buf[bt + 1] = buf[bt + 1 - dt]; buf[bt + 2] = buf[bt + 2 - dt]; buf[bt + 3] = buf[bt + 3 - dt]; } bt = end; } } st.l = lm, st.p = lpos, st.b = bt; if (lm) final = 1, st.m = lbt, st.d = dm, st.n = dbt; } while (!final); return bt == buf.length ? buf : slc(buf, 0, bt); }; var et$1 = /* @__PURE__ */ new u8(0); var b2 = function(d, b) { return d[b] | d[b + 1] << 8; }; var b4 = function(d, b) { return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0; }; var b8 = function(d, b) { return b4(d, b) + b4(d, b + 4) * 4294967296; }; var zlv = function(d) { if ((d[0] & 15) != 8 || d[0] >>> 4 > 7 || (d[0] << 8 | d[1]) % 31) throw "invalid zlib data"; if (d[1] & 32) throw "invalid zlib data: preset dictionaries not supported"; }; function inflateSync(data, out) { return inflt(data, out); } function unzlibSync(data, out) { return inflt((zlv(data), data.subarray(2, -4)), out); } var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder(); var tds = 0; try { td.decode(et$1, { stream: true }); tds = 1; } catch (e) { } var dutf8 = function(d) { for (var r = "", i = 0; ; ) { var c = d[i++]; var eb = (c > 127) + (c > 223) + (c > 239); if (i + eb > d.length) return [r, slc(d, i - 1)]; if (!eb) r += String.fromCharCode(c); else if (eb == 3) { c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | d[i++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023); } else if (eb & 1) r += String.fromCharCode((c & 31) << 6 | d[i++] & 63); else r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63); } }; function strFromU8(dat, latin1) { if (latin1) { var r = ""; for (var i = 0; i < dat.length; i += 16384) r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384)); return r; } else if (td) return td.decode(dat); else { var _a2 = dutf8(dat), out = _a2[0], ext = _a2[1]; if (ext.length) throw "invalid utf-8 data"; return out; } } var slzh = function(d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); }; var zh = function(d, b, z) { var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20); var _a2 = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a2[0], su = _a2[1], off = _a2[2]; return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off]; }; var z64e = function(d, b) { for (; b2(d, b) != 1; b += 4 + b2(d, b + 2)) ; return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)]; }; function unzipSync(data) { var files = {}; var e = data.length - 22; for (; b4(data, e) != 101010256; --e) { if (!e || data.length - e > 65558) throw "invalid zip file"; } var c = b2(data, e + 8); if (!c) return {}; var o = b4(data, e + 16); var z = o == 4294967295; if (z) { e = b4(data, e - 12); if (b4(data, e) != 101075792) throw "invalid zip file"; c = b4(data, e + 32); o = b4(data, e + 48); } for (var i = 0; i < c; ++i) { var _a2 = zh(data, o, z), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off); o = no; if (!c_2) files[fn] = slc(data, b, b + sc); else if (c_2 == 8) files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su)); else throw "unknown compression type " + c_2; } return files; } const COLOR_SPACE_3MF = SRGBColorSpace; class ThreeMFLoader extends Loader { constructor(manager) { super(manager); this.availableExtensions = []; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function(buffer) { try { onLoad(scope.parse(buffer)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(data) { const scope = this; const textureLoader = new TextureLoader(this.manager); function loadDocument(data2) { let zip = null; let file = null; let relsName; let modelRelsName; const modelPartNames = []; const texturesPartNames = []; let modelRels; const modelParts = {}; const printTicketParts = {}; const texturesParts = {}; const textDecoder = new TextDecoder(); try { zip = unzipSync(new Uint8Array(data2)); } catch (e) { if (e instanceof ReferenceError) { console.error("THREE.3MFLoader: fflate missing and file is compressed."); return null; } } for (file in zip) { if (file.match(/\_rels\/.rels$/)) { relsName = file; } else if (file.match(/3D\/_rels\/.*\.model\.rels$/)) { modelRelsName = file; } else if (file.match(/^3D\/.*\.model$/)) { modelPartNames.push(file); } else if (file.match(/^3D\/Textures?\/.*/)) { texturesPartNames.push(file); } } const relsView = zip[relsName]; const relsFileText = textDecoder.decode(relsView); const rels = parseRelsXml(relsFileText); if (modelRelsName) { const relsView2 = zip[modelRelsName]; const relsFileText2 = textDecoder.decode(relsView2); modelRels = parseRelsXml(relsFileText2); } for (let i = 0; i < modelPartNames.length; i++) { const modelPart = modelPartNames[i]; const view = zip[modelPart]; const fileText = textDecoder.decode(view); const xmlData = new DOMParser().parseFromString(fileText, "application/xml"); if (xmlData.documentElement.nodeName.toLowerCase() !== "model") { console.error("THREE.3MFLoader: Error loading 3MF - no 3MF document found: ", modelPart); } const modelNode = xmlData.querySelector("model"); const extensions = {}; for (let i2 = 0; i2 < modelNode.attributes.length; i2++) { const attr = modelNode.attributes[i2]; if (attr.name.match(/^xmlns:(.+)$/)) { extensions[attr.value] = RegExp.$1; } } const modelData = parseModelNode(modelNode); modelData["xml"] = modelNode; if (0 < Object.keys(extensions).length) { modelData["extensions"] = extensions; } modelParts[modelPart] = modelData; } for (let i = 0; i < texturesPartNames.length; i++) { const texturesPartName = texturesPartNames[i]; texturesParts[texturesPartName] = zip[texturesPartName].buffer; } return { rels, modelRels, model: modelParts, printTicket: printTicketParts, texture: texturesParts }; } function parseRelsXml(relsFileText) { const relationships = []; const relsXmlData = new DOMParser().parseFromString(relsFileText, "application/xml"); const relsNodes = relsXmlData.querySelectorAll("Relationship"); for (let i = 0; i < relsNodes.length; i++) { const relsNode = relsNodes[i]; const relationship = { target: relsNode.getAttribute("Target"), //required id: relsNode.getAttribute("Id"), //required type: relsNode.getAttribute("Type") //required }; relationships.push(relationship); } return relationships; } function parseMetadataNodes(metadataNodes) { const metadataData = {}; for (let i = 0; i < metadataNodes.length; i++) { const metadataNode = metadataNodes[i]; const name = metadataNode.getAttribute("name"); const validNames = [ "Title", "Designer", "Description", "Copyright", "LicenseTerms", "Rating", "CreationDate", "ModificationDate" ]; if (0 <= validNames.indexOf(name)) { metadataData[name] = metadataNode.textContent; } } return metadataData; } function parseBasematerialsNode(basematerialsNode) { const basematerialsData = { id: basematerialsNode.getAttribute("id"), // required basematerials: [] }; const basematerialNodes = basematerialsNode.querySelectorAll("base"); for (let i = 0; i < basematerialNodes.length; i++) { const basematerialNode = basematerialNodes[i]; const basematerialData = parseBasematerialNode(basematerialNode); basematerialData.index = i; basematerialsData.basematerials.push(basematerialData); } return basematerialsData; } function parseTexture2DNode(texture2DNode) { const texture2dData = { id: texture2DNode.getAttribute("id"), // required path: texture2DNode.getAttribute("path"), // required contenttype: texture2DNode.getAttribute("contenttype"), // required tilestyleu: texture2DNode.getAttribute("tilestyleu"), tilestylev: texture2DNode.getAttribute("tilestylev"), filter: texture2DNode.getAttribute("filter") }; return texture2dData; } function parseTextures2DGroupNode(texture2DGroupNode) { const texture2DGroupData = { id: texture2DGroupNode.getAttribute("id"), // required texid: texture2DGroupNode.getAttribute("texid"), // required displaypropertiesid: texture2DGroupNode.getAttribute("displaypropertiesid") }; const tex2coordNodes = texture2DGroupNode.querySelectorAll("tex2coord"); const uvs = []; for (let i = 0; i < tex2coordNodes.length; i++) { const tex2coordNode = tex2coordNodes[i]; const u = tex2coordNode.getAttribute("u"); const v = tex2coordNode.getAttribute("v"); uvs.push(parseFloat(u), parseFloat(v)); } texture2DGroupData["uvs"] = new Float32Array(uvs); return texture2DGroupData; } function parseColorGroupNode(colorGroupNode) { const colorGroupData = { id: colorGroupNode.getAttribute("id"), // required displaypropertiesid: colorGroupNode.getAttribute("displaypropertiesid") }; const colorNodes = colorGroupNode.querySelectorAll("color"); const colors = []; const colorObject = new Color(); for (let i = 0; i < colorNodes.length; i++) { const colorNode = colorNodes[i]; const color = colorNode.getAttribute("color"); colorObject.setStyle(color.substring(0, 7), COLOR_SPACE_3MF); colors.push(colorObject.r, colorObject.g, colorObject.b); } colorGroupData["colors"] = new Float32Array(colors); return colorGroupData; } function parseMetallicDisplaypropertiesNode(metallicDisplaypropetiesNode) { const metallicDisplaypropertiesData = { id: metallicDisplaypropetiesNode.getAttribute("id") // required }; const metallicNodes = metallicDisplaypropetiesNode.querySelectorAll("pbmetallic"); const metallicData = []; for (let i = 0; i < metallicNodes.length; i++) { const metallicNode = metallicNodes[i]; metallicData.push({ name: metallicNode.getAttribute("name"), // required metallicness: parseFloat(metallicNode.getAttribute("metallicness")), // required roughness: parseFloat(metallicNode.getAttribute("roughness")) // required }); } metallicDisplaypropertiesData.data = metallicData; return metallicDisplaypropertiesData; } function parseBasematerialNode(basematerialNode) { const basematerialData = {}; basematerialData["name"] = basematerialNode.getAttribute("name"); basematerialData["displaycolor"] = basematerialNode.getAttribute("displaycolor"); basematerialData["displaypropertiesid"] = basematerialNode.getAttribute("displaypropertiesid"); return basematerialData; } function parseMeshNode(meshNode) { const meshData = {}; const vertices = []; const vertexNodes = meshNode.querySelectorAll("vertices vertex"); for (let i = 0; i < vertexNodes.length; i++) { const vertexNode = vertexNodes[i]; const x = vertexNode.getAttribute("x"); const y = vertexNode.getAttribute("y"); const z = vertexNode.getAttribute("z"); vertices.push(parseFloat(x), parseFloat(y), parseFloat(z)); } meshData["vertices"] = new Float32Array(vertices); const triangleProperties = []; const triangles = []; const triangleNodes = meshNode.querySelectorAll("triangles triangle"); for (let i = 0; i < triangleNodes.length; i++) { const triangleNode = triangleNodes[i]; const v1 = triangleNode.getAttribute("v1"); const v2 = triangleNode.getAttribute("v2"); const v3 = triangleNode.getAttribute("v3"); const p1 = triangleNode.getAttribute("p1"); const p2 = triangleNode.getAttribute("p2"); const p3 = triangleNode.getAttribute("p3"); const pid = triangleNode.getAttribute("pid"); const triangleProperty = {}; triangleProperty["v1"] = parseInt(v1, 10); triangleProperty["v2"] = parseInt(v2, 10); triangleProperty["v3"] = parseInt(v3, 10); triangles.push(triangleProperty["v1"], triangleProperty["v2"], triangleProperty["v3"]); if (p1) { triangleProperty["p1"] = parseInt(p1, 10); } if (p2) { triangleProperty["p2"] = parseInt(p2, 10); } if (p3) { triangleProperty["p3"] = parseInt(p3, 10); } if (pid) { triangleProperty["pid"] = pid; } if (0 < Object.keys(triangleProperty).length) { triangleProperties.push(triangleProperty); } } meshData["triangleProperties"] = triangleProperties; meshData["triangles"] = new Uint32Array(triangles); return meshData; } function parseComponentsNode(componentsNode) { const components = []; const componentNodes = componentsNode.querySelectorAll("component"); for (let i = 0; i < componentNodes.length; i++) { const componentNode = componentNodes[i]; const componentData = parseComponentNode(componentNode); components.push(componentData); } return components; } function parseComponentNode(componentNode) { const componentData = {}; componentData["objectId"] = componentNode.getAttribute("objectid"); const transform = componentNode.getAttribute("transform"); if (transform) { componentData["transform"] = parseTransform(transform); } return componentData; } function parseTransform(transform) { const t = []; transform.split(" ").forEach(function(s) { t.push(parseFloat(s)); }); const matrix = new Matrix4(); matrix.set( t[0], t[3], t[6], t[9], t[1], t[4], t[7], t[10], t[2], t[5], t[8], t[11], 0, 0, 0, 1 ); return matrix; } function parseObjectNode(objectNode) { const objectData = { type: objectNode.getAttribute("type") }; const id = objectNode.getAttribute("id"); if (id) { objectData["id"] = id; } const pid = objectNode.getAttribute("pid"); if (pid) { objectData["pid"] = pid; } const pindex = objectNode.getAttribute("pindex"); if (pindex) { objectData["pindex"] = pindex; } const thumbnail = objectNode.getAttribute("thumbnail"); if (thumbnail) { objectData["thumbnail"] = thumbnail; } const partnumber = objectNode.getAttribute("partnumber"); if (partnumber) { objectData["partnumber"] = partnumber; } const name = objectNode.getAttribute("name"); if (name) { objectData["name"] = name; } const meshNode = objectNode.querySelector("mesh"); if (meshNode) { objectData["mesh"] = parseMeshNode(meshNode); } const componentsNode = objectNode.querySelector("components"); if (componentsNode) { objectData["components"] = parseComponentsNode(componentsNode); } return objectData; } function parseResourcesNode(resourcesNode) { const resourcesData = {}; resourcesData["basematerials"] = {}; const basematerialsNodes = resourcesNode.querySelectorAll("basematerials"); for (let i = 0; i < basematerialsNodes.length; i++) { const basematerialsNode = basematerialsNodes[i]; const basematerialsData = parseBasematerialsNode(basematerialsNode); resourcesData["basematerials"][basematerialsData["id"]] = basematerialsData; } resourcesData["texture2d"] = {}; const textures2DNodes = resourcesNode.querySelectorAll("texture2d"); for (let i = 0; i < textures2DNodes.length; i++) { const textures2DNode = textures2DNodes[i]; const texture2DData = parseTexture2DNode(textures2DNode); resourcesData["texture2d"][texture2DData["id"]] = texture2DData; } resourcesData["colorgroup"] = {}; const colorGroupNodes = resourcesNode.querySelectorAll("colorgroup"); for (let i = 0; i < colorGroupNodes.length; i++) { const colorGroupNode = colorGroupNodes[i]; const colorGroupData = parseColorGroupNode(colorGroupNode); resourcesData["colorgroup"][colorGroupData["id"]] = colorGroupData; } resourcesData["pbmetallicdisplayproperties"] = {}; const pbmetallicdisplaypropertiesNodes = resourcesNode.querySelectorAll("pbmetallicdisplayproperties"); for (let i = 0; i < pbmetallicdisplaypropertiesNodes.length; i++) { const pbmetallicdisplaypropertiesNode = pbmetallicdisplaypropertiesNodes[i]; const pbmetallicdisplaypropertiesData = parseMetallicDisplaypropertiesNode(pbmetallicdisplaypropertiesNode); resourcesData["pbmetallicdisplayproperties"][pbmetallicdisplaypropertiesData["id"]] = pbmetallicdisplaypropertiesData; } resourcesData["texture2dgroup"] = {}; const textures2DGroupNodes = resourcesNode.querySelectorAll("texture2dgroup"); for (let i = 0; i < textures2DGroupNodes.length; i++) { const textures2DGroupNode = textures2DGroupNodes[i]; const textures2DGroupData = parseTextures2DGroupNode(textures2DGroupNode); resourcesData["texture2dgroup"][textures2DGroupData["id"]] = textures2DGroupData; } resourcesData["object"] = {}; const objectNodes = resourcesNode.querySelectorAll("object"); for (let i = 0; i < objectNodes.length; i++) { const objectNode = objectNodes[i]; const objectData = parseObjectNode(objectNode); resourcesData["object"][objectData["id"]] = objectData; } return resourcesData; } function parseBuildNode(buildNode) { const buildData = []; const itemNodes = buildNode.querySelectorAll("item"); for (let i = 0; i < itemNodes.length; i++) { const itemNode = itemNodes[i]; const buildItem = { objectId: itemNode.getAttribute("objectid") }; const transform = itemNode.getAttribute("transform"); if (transform) { buildItem["transform"] = parseTransform(transform); } buildData.push(buildItem); } return buildData; } function parseModelNode(modelNode) { const modelData = { unit: modelNode.getAttribute("unit") || "millimeter" }; const metadataNodes = modelNode.querySelectorAll("metadata"); if (metadataNodes) { modelData["metadata"] = parseMetadataNodes(metadataNodes); } const resourcesNode = modelNode.querySelector("resources"); if (resourcesNode) { modelData["resources"] = parseResourcesNode(resourcesNode); } const buildNode = modelNode.querySelector("build"); if (buildNode) { modelData["build"] = parseBuildNode(buildNode); } return modelData; } function buildTexture(texture2dgroup, objects2, modelData, textureData) { const texid = texture2dgroup.texid; const texture2ds = modelData.resources.texture2d; const texture2d = texture2ds[texid]; if (texture2d) { const data2 = textureData[texture2d.path]; const type = texture2d.contenttype; const blob = new Blob([data2], { type }); const sourceURI = URL.createObjectURL(blob); const texture = textureLoader.load(sourceURI, function() { URL.revokeObjectURL(sourceURI); }); texture.colorSpace = COLOR_SPACE_3MF; switch (texture2d.tilestyleu) { case "wrap": texture.wrapS = RepeatWrapping; break; case "mirror": texture.wrapS = MirroredRepeatWrapping; break; case "none": case "clamp": texture.wrapS = ClampToEdgeWrapping; break; default: texture.wrapS = RepeatWrapping; } switch (texture2d.tilestylev) { case "wrap": texture.wrapT = RepeatWrapping; break; case "mirror": texture.wrapT = MirroredRepeatWrapping; break; case "none": case "clamp": texture.wrapT = ClampToEdgeWrapping; break; default: texture.wrapT = RepeatWrapping; } switch (texture2d.filter) { case "auto": texture.magFilter = LinearFilter; texture.minFilter = LinearMipmapLinearFilter; break; case "linear": texture.magFilter = LinearFilter; texture.minFilter = LinearFilter; break; case "nearest": texture.magFilter = NearestFilter; texture.minFilter = NearestFilter; break; default: texture.magFilter = LinearFilter; texture.minFilter = LinearMipmapLinearFilter; } return texture; } else { return null; } } function buildBasematerialsMeshes(basematerials, triangleProperties, meshData, objects2, modelData, textureData, objectData) { const objectPindex = objectData.pindex; const materialMap = {}; for (let i = 0, l = triangleProperties.length; i < l; i++) { const triangleProperty = triangleProperties[i]; const pindex = triangleProperty.p1 !== void 0 ? triangleProperty.p1 : objectPindex; if (m