"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const THREE = require("three"); const _taskCache = /* @__PURE__ */ new WeakMap(); class Rhino3dmLoader extends THREE.Loader { constructor(manager) { super(manager); this.libraryPath = ""; this.libraryPending = null; this.libraryBinary = null; this.libraryConfig = {}; this.url = ""; this.workerLimit = 4; this.workerPool = []; this.workerNextTaskID = 1; this.workerSourceURL = ""; this.workerConfig = {}; this.materials = []; } setLibraryPath(path) { this.libraryPath = path; return this; } setWorkerLimit(workerLimit) { this.workerLimit = workerLimit; return this; } load(url, onLoad, onProgress, onError) { const loader = new THREE.FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType("arraybuffer"); loader.setRequestHeader(this.requestHeader); this.url = url; loader.load( url, (buffer) => { if (_taskCache.has(buffer)) { const cachedTask = _taskCache.get(buffer); return cachedTask.promise.then(onLoad).catch(onError); } this.decodeObjects(buffer, url).then(onLoad).catch(onError); }, onProgress, onError ); } debug() { console.log( "Task load: ", this.workerPool.map((worker) => worker._taskLoad) ); } decodeObjects(buffer, url) { let worker; let taskID; const taskCost = buffer.byteLength; const objectPending = this._getWorker(taskCost).then((_worker) => { worker = _worker; taskID = this.workerNextTaskID++; return new Promise((resolve, reject) => { worker._callbacks[taskID] = { resolve, reject }; worker.postMessage({ type: "decode", id: taskID, buffer }, [buffer]); }); }).then((message) => this._createGeometry(message.data)); objectPending.catch(() => true).then(() => { if (worker && taskID) { this._releaseTask(worker, taskID); } }); _taskCache.set(buffer, { url, promise: objectPending }); return objectPending; } parse(data, onLoad, onError) { this.decodeObjects(data, "").then(onLoad).catch(onError); } _compareMaterials(material) { const mat = {}; mat.name = material.name; mat.color = {}; mat.color.r = material.color.r; mat.color.g = material.color.g; mat.color.b = material.color.b; mat.type = material.type; for (let i = 0; i < this.materials.length; i++) { const m = this.materials[i]; const _mat = {}; _mat.name = m.name; _mat.color = {}; _mat.color.r = m.color.r; _mat.color.g = m.color.g; _mat.color.b = m.color.b; _mat.type = m.type; if (JSON.stringify(mat) === JSON.stringify(_mat)) { return m; } } this.materials.push(material); return material; } _createMaterial(material) { if (material === void 0) { return new THREE.MeshStandardMaterial({ color: new THREE.Color(1, 1, 1), metalness: 0.8, name: "default", side: 2 }); } const _diffuseColor = material.diffuseColor; const diffusecolor = new THREE.Color(_diffuseColor.r / 255, _diffuseColor.g / 255, _diffuseColor.b / 255); if (_diffuseColor.r === 0 && _diffuseColor.g === 0 && _diffuseColor.b === 0) { diffusecolor.r = 1; diffusecolor.g = 1; diffusecolor.b = 1; } const mat = new THREE.MeshStandardMaterial({ color: diffusecolor, name: material.name, side: 2, transparent: material.transparency > 0 ? true : false, opacity: 1 - material.transparency }); const textureLoader = new THREE.TextureLoader(); for (let i = 0; i < material.textures.length; i++) { const texture = material.textures[i]; if (texture.image !== null) { const map = textureLoader.load(texture.image); switch (texture.type) { case "Diffuse": mat.map = map; break; case "Bump": mat.bumpMap = map; break; case "Transparency": mat.alphaMap = map; mat.transparent = true; break; case "Emap": mat.envMap = map; break; } } } return mat; } _createGeometry(data) { const object = new THREE.Object3D(); const instanceDefinitionObjects = []; const instanceDefinitions = []; const instanceReferences = []; object.userData["layers"] = data.layers; object.userData["groups"] = data.groups; object.userData["settings"] = data.settings; object.userData["objectType"] = "File3dm"; object.userData["materials"] = null; object.name = this.url; let objects = data.objects; const materials = data.materials; for (let i = 0; i < objects.length; i++) { const obj = objects[i]; const attributes = obj.attributes; switch (obj.objectType) { case "InstanceDefinition": instanceDefinitions.push(obj); break; case "InstanceReference": instanceReferences.push(obj); break; default: let _object; if (attributes.materialIndex >= 0) { const rMaterial = materials[attributes.materialIndex]; let material = this._createMaterial(rMaterial); material = this._compareMaterials(material); _object = this._createObject(obj, material); } else { const material = this._createMaterial(); _object = this._createObject(obj, material); } if (_object === void 0) { continue; } const layer = data.layers[attributes.layerIndex]; _object.visible = layer ? data.layers[attributes.layerIndex].visible : true; if (attributes.isInstanceDefinitionObject) { instanceDefinitionObjects.push(_object); } else { object.add(_object); } break; } } for (let i = 0; i < instanceDefinitions.length; i++) { const iDef = instanceDefinitions[i]; objects = []; for (let j = 0; j < iDef.attributes.objectIds.length; j++) { const objId = iDef.attributes.objectIds[j]; for (let p = 0; p < instanceDefinitionObjects.length; p++) { const idoId = instanceDefinitionObjects[p].userData.attributes.id; if (objId === idoId) { objects.push(instanceDefinitionObjects[p]); } } } for (let j = 0; j < instanceReferences.length; j++) { const iRef = instanceReferences[j]; if (iRef.geometry.parentIdefId === iDef.attributes.id) { const iRefObject = new THREE.Object3D(); const xf = iRef.geometry.xform.array; const matrix = new THREE.Matrix4(); matrix.set( xf[0], xf[1], xf[2], xf[3], xf[4], xf[5], xf[6], xf[7], xf[8], xf[9], xf[10], xf[11], xf[12], xf[13], xf[14], xf[15] ); iRefObject.applyMatrix4(matrix); for (let p = 0; p < objects.length; p++) { iRefObject.add(objects[p].clone(true)); } object.add(iRefObject); } } } object.userData["materials"] = this.materials; return object; } _createObject(obj, mat) { const loader = new THREE.BufferGeometryLoader(); const attributes = obj.attributes; let geometry, material, _color, color; switch (obj.objectType) { case "Point": case "PointSet": geometry = loader.parse(obj.geometry); if (geometry.attributes.hasOwnProperty("color")) { material = new THREE.PointsMaterial({ vertexColors: true, sizeAttenuation: false, size: 2 }); } else { _color = attributes.drawColor; color = new THREE.Color(_color.r / 255, _color.g / 255, _color.b / 255); material = new THREE.PointsMaterial({ color, sizeAttenuation: false, size: 2 }); } material = this._compareMaterials(material); const points = new THREE.Points(geometry, material); points.userData["attributes"] = attributes; points.userData["objectType"] = obj.objectType; if (attributes.name) { points.name = attributes.name; } return points; case "Mesh": case "Extrusion": case "SubD": case "Brep": if (obj.geometry === null) return; geometry = loader.parse(obj.geometry); if (geometry.attributes.hasOwnProperty("color")) { mat.vertexColors = true; } if (mat === null) { mat = this._createMaterial(); mat = this._compareMaterials(mat); } const mesh = new THREE.Mesh(geometry, mat); mesh.castShadow = attributes.castsShadows; mesh.receiveShadow = attributes.receivesShadows; mesh.userData["attributes"] = attributes; mesh.userData["objectType"] = obj.objectType; if (attributes.name) { mesh.name = attributes.name; } return mesh; case "Curve": geometry = loader.parse(obj.geometry); _color = attributes.drawColor; color = new THREE.Color(_color.r / 255, _color.g / 255, _color.b / 255); material = new THREE.LineBasicMaterial({ color }); material = this._compareMaterials(material); const lines = new THREE.Line(geometry, material); lines.userData["attributes"] = attributes; lines.userData["objectType"] = obj.objectType; if (attributes.name) { lines.name = attributes.name; } return lines; case "TextDot": geometry = obj.geometry; const ctx = document.createElement("canvas").getContext("2d"); const font = `${geometry.fontHeight}px ${geometry.fontFace}`; ctx.font = font; const width = ctx.measureText(geometry.text).width + 10; const height = geometry.fontHeight + 10; const r = window.devicePixelRatio; ctx.canvas.width = width * r; ctx.canvas.height = height * r; ctx.canvas.style.width = width + "px"; ctx.canvas.style.height = height + "px"; ctx.setTransform(r, 0, 0, r, 0, 0); ctx.font = font; ctx.textBaseline = "middle"; ctx.textAlign = "center"; color = attributes.drawColor; ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${color.a})`; ctx.fillRect(0, 0, width, height); ctx.fillStyle = "white"; ctx.fillText(geometry.text, width / 2, height / 2); const texture = new THREE.CanvasTexture(ctx.canvas); texture.minFilter = THREE.LinearFilter; texture.wrapS = THREE.ClampToEdgeWrapping; texture.wrapT = THREE.ClampToEdgeWrapping; material = new THREE.SpriteMaterial({ map: texture, depthTest: false }); const sprite = new THREE.Sprite(material); sprite.position.set(geometry.point[0], geometry.point[1], geometry.point[2]); sprite.scale.set(width / 10, height / 10, 1); sprite.userData["attributes"] = attributes; sprite.userData["objectType"] = obj.objectType; if (attributes.name) { sprite.name = attributes.name; } return sprite; case "Light": geometry = obj.geometry; let light; if (geometry.isDirectionalLight) { light = new THREE.DirectionalLight(); light.castShadow = attributes.castsShadows; light.position.set(geometry.location[0], geometry.location[1], geometry.location[2]); light.target.position.set(geometry.direction[0], geometry.direction[1], geometry.direction[2]); light.shadow.normalBias = 0.1; } else if (geometry.isPointLight) { light = new THREE.PointLight(); light.castShadow = attributes.castsShadows; light.position.set(geometry.location[0], geometry.location[1], geometry.location[2]); light.shadow.normalBias = 0.1; } else if (geometry.isRectangularLight) { light = new THREE.RectAreaLight(); const width2 = Math.abs(geometry.width[2]); const height2 = Math.abs(geometry.length[0]); light.position.set(geometry.location[0] - height2 / 2, geometry.location[1], geometry.location[2] - width2 / 2); light.height = height2; light.width = width2; light.lookAt(new THREE.Vector3(geometry.direction[0], geometry.direction[1], geometry.direction[2])); } else if (geometry.isSpotLight) { light = new THREE.SpotLight(); light.castShadow = attributes.castsShadows; light.position.set(geometry.location[0], geometry.location[1], geometry.location[2]); light.target.position.set(geometry.direction[0], geometry.direction[1], geometry.direction[2]); light.angle = geometry.spotAngleRadians; light.shadow.normalBias = 0.1; } else if (geometry.isLinearLight) { console.warn("THREE.3DMLoader: No conversion exists for linear lights."); return; } if (light) { light.intensity = geometry.intensity; _color = geometry.diffuse; color = new THREE.Color(_color.r / 255, _color.g / 255, _color.b / 255); light.color = color; light.userData["attributes"] = attributes; light.userData["objectType"] = obj.objectType; } return light; } } _initLibrary() { if (!this.libraryPending) { const jsLoader = new THREE.FileLoader(this.manager); jsLoader.setPath(this.libraryPath); const jsContent = new Promise((resolve, reject) => { jsLoader.load("rhino3dm.js", resolve, void 0, reject); }); const binaryLoader = new THREE.FileLoader(this.manager); binaryLoader.setPath(this.libraryPath); binaryLoader.setResponseType("arraybuffer"); const binaryContent = new Promise((resolve, reject) => { binaryLoader.load("rhino3dm.wasm", resolve, void 0, reject); }); this.libraryPending = Promise.all([jsContent, binaryContent]).then(([jsContent2, binaryContent2]) => { this.libraryConfig.wasmBinary = binaryContent2; const fn = Rhino3dmWorker.toString(); const body = [ "/* rhino3dm.js */", jsContent2, "/* worker */", fn.substring(fn.indexOf("{") + 1, fn.lastIndexOf("}")) ].join("\n"); this.workerSourceURL = URL.createObjectURL(new Blob([body])); }); } return this.libraryPending; } _getWorker(taskCost) { return this._initLibrary().then(() => { if (this.workerPool.length < this.workerLimit) { const worker2 = new Worker(this.workerSourceURL); worker2._callbacks = {}; worker2._taskCosts = {}; worker2._taskLoad = 0; worker2.postMessage({ type: "init", libraryConfig: this.libraryConfig }); worker2.onmessage = function(e) { const message = e.data; switch (message.type) { case "decode": worker2._callbacks[message.id].resolve(message); break; case "error": worker2._callbacks[message.id].reject(message); break; default: console.error('THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"'); } }; this.workerPool.push(worker2); } else { this.workerPool.sort(function(a, b) { return a._taskLoad > b._taskLoad ? -1 : 1; }); } const worker = this.workerPool[this.workerPool.length - 1]; worker._taskLoad += taskCost; return worker; }); } _releaseTask(worker, taskID) { worker._taskLoad -= worker._taskCosts[taskID]; delete worker._callbacks[taskID]; delete worker._taskCosts[taskID]; } dispose() { for (let i = 0; i < this.workerPool.length; ++i) { this.workerPool[i].terminate(); } this.workerPool.length = 0; return this; } } function Rhino3dmWorker() { let libraryPending; let libraryConfig; let rhino; onmessage = function(e) { const message = e.data; switch (message.type) { case "init": libraryConfig = message.libraryConfig; const wasmBinary = libraryConfig.wasmBinary; let RhinoModule; libraryPending = new Promise(function(resolve) { RhinoModule = { wasmBinary, onRuntimeInitialized: resolve }; rhino3dm(RhinoModule); }).then(() => { rhino = RhinoModule; }); break; case "decode": const buffer = message.buffer; libraryPending.then(() => { const data = decodeObjects(rhino, buffer); self.postMessage({ type: "decode", id: message.id, data }); }); break; } }; function decodeObjects(rhino2, buffer) { const arr = new Uint8Array(buffer); const doc = rhino2.File3dm.fromByteArray(arr); const objects = []; const materials = []; const layers = []; const views = []; const namedViews = []; const groups = []; const objs = doc.objects(); const cnt = objs.count; for (let i = 0; i < cnt; i++) { const _object = objs.get(i); const object = extractObjectData(_object, doc); _object.delete(); if (object) { objects.push(object); } } for (let i = 0; i < doc.instanceDefinitions().count(); i++) { const idef = doc.instanceDefinitions().get(i); const idefAttributes = extractProperties(idef); idefAttributes.objectIds = idef.getObjectIds(); objects.push({ geometry: null, attributes: idefAttributes, objectType: "InstanceDefinition" }); } const textureTypes = [ // rhino.TextureType.Bitmap, rhino2.TextureType.Diffuse, rhino2.TextureType.Bump, rhino2.TextureType.Transparency, rhino2.TextureType.Opacity, rhino2.TextureType.Emap ]; const pbrTextureTypes = [ rhino2.TextureType.PBR_BaseColor, rhino2.TextureType.PBR_Subsurface, rhino2.TextureType.PBR_SubsurfaceScattering, rhino2.TextureType.PBR_SubsurfaceScatteringRadius, rhino2.TextureType.PBR_Metallic, rhino2.TextureType.PBR_Specular, rhino2.TextureType.PBR_SpecularTint, rhino2.TextureType.PBR_Roughness, rhino2.TextureType.PBR_Anisotropic, rhino2.TextureType.PBR_Anisotropic_Rotation, rhino2.TextureType.PBR_Sheen, rhino2.TextureType.PBR_SheenTint, rhino2.TextureType.PBR_Clearcoat, rhino2.TextureType.PBR_ClearcoatBump, rhino2.TextureType.PBR_ClearcoatRoughness, rhino2.TextureType.PBR_OpacityIor, rhino2.TextureType.PBR_OpacityRoughness, rhino2.TextureType.PBR_Emission, rhino2.TextureType.PBR_AmbientOcclusion, rhino2.TextureType.PBR_Displacement ]; for (let i = 0; i < doc.materials().count(); i++) { const _material = doc.materials().get(i); const _pbrMaterial = _material.physicallyBased(); let material = extractProperties(_material); const textures = []; for (let j = 0; j < textureTypes.length; j++) { const _texture = _material.getTexture(textureTypes[j]); if (_texture) { let textureType = textureTypes[j].constructor.name; textureType = textureType.substring(12, textureType.length); const texture = { type: textureType }; const image = doc.getEmbeddedFileAsBase64(_texture.fileName); if (image) { texture.image = "data:image/png;base64," + image; } else { console.warn(`THREE.3DMLoader: Image for ${textureType} texture not embedded in file.`); texture.image = null; } textures.push(texture); _texture.delete(); } } material.textures = textures; if (_pbrMaterial.supported) { console.log("pbr true"); for (let j = 0; j < pbrTextureTypes.length; j++) { const _texture = _material.getTexture(textureTypes[j]); if (_texture) { const image = doc.getEmbeddedFileAsBase64(_texture.fileName); let textureType = textureTypes[j].constructor.name; textureType = textureType.substring(12, textureType.length); const texture = { type: textureType, image: "data:image/png;base64," + image }; textures.push(texture); _texture.delete(); } } const pbMaterialProperties = extractProperties(_material.physicallyBased()); material = Object.assign(pbMaterialProperties, material); } materials.push(material); _material.delete(); _pbrMaterial.delete(); } for (let i = 0; i < doc.layers().count(); i++) { const _layer = doc.layers().get(i); const layer = extractProperties(_layer); layers.push(layer); _layer.delete(); } for (let i = 0; i < doc.views().count(); i++) { const _view = doc.views().get(i); const view = extractProperties(_view); views.push(view); _view.delete(); } for (let i = 0; i < doc.namedViews().count(); i++) { const _namedView = doc.namedViews().get(i); const namedView = extractProperties(_namedView); namedViews.push(namedView); _namedView.delete(); } for (let i = 0; i < doc.groups().count(); i++) { const _group = doc.groups().get(i); const group = extractProperties(_group); groups.push(group); _group.delete(); } const settings = extractProperties(doc.settings()); doc.delete(); return { objects, materials, layers, views, namedViews, groups, settings }; } function extractObjectData(object, doc) { const _geometry = object.geometry(); const _attributes = object.attributes(); let objectType = _geometry.objectType; let geometry, attributes, position, data, mesh; switch (objectType) { case rhino.ObjectType.Curve: const pts = curveToPoints(_geometry, 100); position = {}; attributes = {}; data = {}; position.itemSize = 3; position.type = "Float32Array"; position.array = []; for (let j = 0; j < pts.length; j++) { position.array.push(pts[j][0]); position.array.push(pts[j][1]); position.array.push(pts[j][2]); } attributes.position = position; data.attributes = attributes; geometry = { data }; break; case rhino.ObjectType.Point: const pt = _geometry.location; position = {}; const color = {}; attributes = {}; data = {}; position.itemSize = 3; position.type = "Float32Array"; position.array = [pt[0], pt[1], pt[2]]; const _color = _attributes.drawColor(doc); color.itemSize = 3; color.type = "Float32Array"; color.array = [_color.r / 255, _color.g / 255, _color.b / 255]; attributes.position = position; attributes.color = color; data.attributes = attributes; geometry = { data }; break; case rhino.ObjectType.PointSet: case rhino.ObjectType.Mesh: geometry = _geometry.toThreejsJSON(); break; case rhino.ObjectType.Brep: const faces = _geometry.faces(); mesh = new rhino.Mesh(); for (let faceIndex = 0; faceIndex < faces.count; faceIndex++) { const face = faces.get(faceIndex); const _mesh = face.getMesh(rhino.MeshType.Any); if (_mesh) { mesh.append(_mesh); _mesh.delete(); } face.delete(); } if (mesh.faces().count > 0) { mesh.compact(); geometry = mesh.toThreejsJSON(); faces.delete(); } mesh.delete(); break; case rhino.ObjectType.Extrusion: mesh = _geometry.getMesh(rhino.MeshType.Any); if (mesh) { geometry = mesh.toThreejsJSON(); mesh.delete(); } break; case rhino.ObjectType.TextDot: geometry = extractProperties(_geometry); break; case rhino.ObjectType.Light: geometry = extractProperties(_geometry); break; case rhino.ObjectType.InstanceReference: geometry = extractProperties(_geometry); geometry.xform = extractProperties(_geometry.xform); geometry.xform.array = _geometry.xform.toFloatArray(true); break; case rhino.ObjectType.SubD: _geometry.subdivide(3); mesh = rhino.Mesh.createFromSubDControlNet(_geometry); if (mesh) { geometry = mesh.toThreejsJSON(); mesh.delete(); } break; default: console.warn(`THREE.3DMLoader: TODO: Implement ${objectType.constructor.name}`); break; } if (geometry) { attributes = extractProperties(_attributes); attributes.geometry = extractProperties(_geometry); if (_attributes.groupCount > 0) { attributes.groupIds = _attributes.getGroupList(); } if (_attributes.userStringCount > 0) { attributes.userStrings = _attributes.getUserStrings(); } if (_geometry.userStringCount > 0) { attributes.geometry.userStrings = _geometry.getUserStrings(); } attributes.drawColor = _attributes.drawColor(doc); objectType = objectType.constructor.name; objectType = objectType.substring(11, objectType.length); return { geometry, attributes, objectType }; } else { console.warn(`THREE.3DMLoader: ${objectType.constructor.name} has no associated mesh geometry.`); } } function extractProperties(object) { const result = {}; for (const property in object) { const value = object[property]; if (typeof value !== "function") { if (typeof value === "object" && value !== null && value.hasOwnProperty("constructor")) { result[property] = { name: value.constructor.name, value: value.value }; } else { result[property] = value; } } } return result; } function curveToPoints(curve, pointLimit) { let pointCount = pointLimit; let rc = []; const ts = []; if (curve instanceof rhino.LineCurve) { return [curve.pointAtStart, curve.pointAtEnd]; } if (curve instanceof rhino.PolylineCurve) { pointCount = curve.pointCount; for (let i = 0; i < pointCount; i++) { rc.push(curve.point(i)); } return rc; } if (curve instanceof rhino.PolyCurve) { const segmentCount = curve.segmentCount; for (let i = 0; i < segmentCount; i++) { const segment = curve.segmentCurve(i); const segmentArray = curveToPoints(segment, pointCount); rc = rc.concat(segmentArray); segment.delete(); } return rc; } if (curve instanceof rhino.ArcCurve) { pointCount = Math.floor(curve.angleDegrees / 5); pointCount = pointCount < 2 ? 2 : pointCount; } if (curve instanceof rhino.NurbsCurve && curve.degree === 1) { const pLine = curve.tryGetPolyline(); for (let i = 0; i < pLine.count; i++) { rc.push(pLine.get(i)); } pLine.delete(); return rc; } const domain = curve.domain; const divisions = pointCount - 1; for (let j = 0; j < pointCount; j++) { const t = domain[0] + j / divisions * (domain[1] - domain[0]); if (t === domain[0] || t === domain[1]) { ts.push(t); continue; } const tan = curve.tangentAt(t); const prevTan = curve.tangentAt(ts.slice(-1)[0]); const tS = tan[0] * tan[0] + tan[1] * tan[1] + tan[2] * tan[2]; const ptS = prevTan[0] * prevTan[0] + prevTan[1] * prevTan[1] + prevTan[2] * prevTan[2]; const denominator = Math.sqrt(tS * ptS); let angle; if (denominator === 0) { angle = Math.PI / 2; } else { const theta = (tan.x * prevTan.x + tan.y * prevTan.y + tan.z * prevTan.z) / denominator; angle = Math.acos(Math.max(-1, Math.min(1, theta))); } if (angle < 0.1) continue; ts.push(t); } rc = ts.map((t) => curve.pointAt(t)); return rc; } } exports.Rhino3dmLoader = Rhino3dmLoader; //# sourceMappingURL=3DMLoader.cjs.map