threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
136 lines • 6.53 kB
JavaScript
import { Rhino3dmLoader } from 'three/examples/jsm/loaders/3DMLoader.js';
import { Color, DoubleSide, InstancedMesh, MeshStandardMaterial, } from 'three';
export class Rhino3dmLoader2 extends Rhino3dmLoader {
constructor(manager) {
super(manager);
this.materials = [];
this.setLibraryPath(Rhino3dmLoader2.LIBRARY_PATH);
}
// eslint-disable-next-line @typescript-eslint/naming-convention
_createMaterial(material) {
if (!Rhino3dmLoader2.ImportMaterials)
return this.materials[0] || new MeshStandardMaterial({
color: new Color(1, 1, 1),
metalness: 0.8,
name: 'default',
side: DoubleSide,
});
return super._createMaterial(material);
}
async loadAsync(url, onProgress) {
const ret = await super.loadAsync(url, onProgress);
ret.rotateX(-Math.PI / 2); // since models are rotated
if (ret.userData.materials)
delete ret.userData.materials; // we don't want them saved in the file during export
// console.log(ret.userData)
const layers = ret.userData.layers;
ret.traverse((obj) => {
const castShadow = obj.userData.attributes?.castsShadows;
const receiveShadow = obj.userData.attributes?.receivesShadows;
obj.castShadow = castShadow;
obj.receiveShadow = receiveShadow;
const layerIndex = obj.userData.attributes?.layerIndex ?? obj.userData.defAttributes?.layerIndex;
const layer = layers[layerIndex];
if (layer)
obj.userData.rhinoLayer = layer;
obj.userData.rhino3dmRoot = ret.uuid;
if (!Rhino3dmLoader2.LoadUserDataStrings)
obj.userData.strings = [];
if (!Rhino3dmLoader2.LoadUserDataWarnings)
delete obj.userData.warnings;
this._hideLineMesh(obj);
this._useInstancedMesh(obj);
this._useMaterialSource(obj, layer);
});
this.materials = []; // so that next file load doesn't give the same materials.
return ret;
}
_useMaterialSource(obj, layer) {
if (!Rhino3dmLoader2.ImportMaterials)
return;
const mesh = obj;
if (mesh.material?.name === 'default' || Rhino3dmLoader2.ForceLayerMaterials) {
// https://developer.rhino3d.com/api/rhinoscript/object_methods/objectmaterialsource.htm
const materialSource = mesh.userData.attributes?.materialSource || mesh.userData.defAttributes?.materialSource;
const colorSource = mesh.userData.attributes?.colorSource || mesh.userData.defAttributes?.colorSource;
// const materialSource = mesh.userData.defAttributes?.materialSource
// console.log(materialSource, mesh.userData.attributes, mesh.userData.defAttributes)
if (!Rhino3dmLoader2.ForceLayerMaterials && !materialSource && !colorSource)
return;
if (Rhino3dmLoader2.ForceLayerMaterials ||
(materialSource?.value === 0 || materialSource?.value === 1 && colorSource?.value === 0)) { // material from layer
if (layer) {
mesh.material = this._compareMaterials(this._createMaterial({
diffuseColor: layer.color,
name: layer.name,
transparency: 0,
textures: [],
}));
}
}
else if (materialSource?.value === 3 || materialSource?.value === 1 && colorSource?.value === 3) { // material from parent
mesh.traverseAncestors((parent) => {
if (parent?.material)
mesh.material = parent.material;
});
}
else if (materialSource && materialSource.value !== 1) {
console.warn('Unknown material source', materialSource, mesh, mesh.userData.attributes);
}
}
}
_useInstancedMesh(obj) {
if (!Rhino3dmLoader2.ReplaceWithInstancedMesh)
return;
if (obj.children.length <= 0)
return;
const children = obj.children;
const geometries = children.map((c) => c.geometry);
const uniqueGeometries = geometries.filter((g, i) => geometries.indexOf(g) === i);
uniqueGeometries.forEach((g) => {
const instances = children.filter((c) => c.geometry === g);
const instances2 = instances.length > 0 ? instances.filter((c) => c.material === instances[0].material) : [];
if (instances2.length > 1) {
const instanced = new InstancedMesh(g, instances2[0].material, instances2.length);
instanced.userData = { ...instances2[0].userData };
instanced.userData.instanceUserData = [];
instanced.userData.attributes = instanced.userData.defAttributes || instanced.userData.attributes;
if (instanced.userData.defAttributes)
delete instanced.userData.defAttributes;
instanced.name = instanced.userData.attributes?.name || instances2[0].name;
instances2.forEach((c, i) => {
instanced.setMatrixAt(i, c.matrix);
obj.remove(c);
instanced.userData.instanceUserData.push(c.userData);
});
obj.add(instanced);
}
});
}
_hideLineMesh(obj) {
if (!Rhino3dmLoader2.HideLineMesh && !Rhino3dmLoader2.HidePointMesh)
return;
if (obj.children.length <= 0)
return;
const toHide = [];
obj.traverse((c) => {
if (c && (Rhino3dmLoader2.HideLineMesh && (c.isLine || c.isLineSegments))
||
Rhino3dmLoader2.HidePointMesh && c.isPoints)
toHide.push(c);
});
toHide.forEach((c) => {
c.userData.visible_3dm = c.visible;
c.visible = false;
});
}
}
Rhino3dmLoader2.LIBRARY_PATH = 'https://cdn.jsdelivr.net/npm/rhino3dm@7.15.0/';
Rhino3dmLoader2.ImportMaterials = true;
Rhino3dmLoader2.ForceLayerMaterials = false;
Rhino3dmLoader2.ReplaceWithInstancedMesh = false;
Rhino3dmLoader2.HideLineMesh = false;
Rhino3dmLoader2.HidePointMesh = false;
Rhino3dmLoader2.LoadUserDataStrings = true;
Rhino3dmLoader2.LoadUserDataWarnings = true;
//# sourceMappingURL=Rhino3dmLoader2.js.map