threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
177 lines • 7.07 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { AViewerPluginSync } from '../../viewer';
import { PickingPlugin } from '../interaction/PickingPlugin';
import { uiButton, uiSlider } from 'uiconfig.js';
import { Vector3 } from 'three';
/**
* Boilerplate for implementing a plugin for simplifying geometries.
* This is a base class and cannot be used directly.
* See {@link MeshOptSimplifyModifierPlugin} the [simplify-modifier-plugin](https://threepipe.org/examples/#simplify-modifier-plugin) example for a sample implementation.
*/
export class SimplifyModifierPlugin extends AViewerPluginSync {
constructor() {
super();
this.enabled = true;
this.toJSON = undefined;
/**
* Factor of vertices to remove. eg 0.5 will remove half of the vertices.
* Default is 0.5
* This is used when no factor or count is provided in the options to simplifyGeometry or simplifyGeometries.
*/
this.simplifyFactor = 0.5;
}
get initialized() { return true; }
async initialize() { return; }
onAdded(viewer) {
super.onAdded(viewer);
this._pickingPlugin = viewer.getPlugin(PickingPlugin);
}
simplifyGeometries(geometry, options) {
if (!geometry) {
const selected = this._pickingPlugin?.getSelectedObject();
if (!selected?.isObject3D)
return;
const geom = [];
selected?.traverse((o) => {
if (o.geometry && !geom.includes(o.geometry))
geom.push(o.geometry);
});
geometry = geom;
if (!geometry || !geometry.length)
return;
}
if (!Array.isArray(geometry))
geometry = [geometry];
const res = [];
for (const g of geometry) {
res.push(this.simplifyGeometry(g, options));
}
return res;
}
simplifyGeometry(geometry, { factor, count, replace = true, disposeOnReplace = false, } = {}) {
if (!geometry) {
const selected = this._pickingPlugin?.getSelectedObject();
geometry = selected?.geometry;
if (!geometry)
return undefined;
}
if (!geometry.attributes.position) {
this._viewer?.console.error('SimplifyModifierPlugin: Geometry does not have position attribute', geometry);
return geometry;
}
factor = factor || this.simplifyFactor;
count = count || geometry.attributes.position.count * factor;
if (!geometry.boundingBox)
geometry.computeBoundingBox();
const simplified = this._simplify(geometry, count);
simplified.computeBoundingBox();
simplified.computeBoundingSphere();
simplified.computeVertexNormals();
const bbox = simplified.boundingBox;
const size = bbox.getSize(new Vector3());
if (!isFinite(size.x) || !isFinite(size.y) || !isFinite(size.z)) {
this._viewer?.console.error('SimplifyModifierPlugin: Unable to simplify', geometry, simplified, size);
return geometry;
}
const oldBB = geometry.boundingBox;
const oldSize = oldBB.getSize(new Vector3());
const diff = size.clone().sub(oldSize);
const diffPerc = diff.clone().divide(oldSize);
if (diffPerc.lengthSq() > 0.001) {
// todo: add option to skip this
console.warn('Simplify', geometry, simplified, bbox, oldBB, size, oldSize, diff, diffPerc);
}
// simplified.setDirty()
if (!replace)
return simplified;
// not working?
// geometry.copy(simplified)
// geometry.setDirty()
// simplified.dispose()
const meshes = geometry.appliedMeshes;
if (!meshes) {
console.error('No meshes found for geometry, cannot replace', geometry);
return simplified;
}
for (const mesh of meshes) {
mesh.geometry = simplified;
}
if (disposeOnReplace) {
geometry.dispose(true);
}
return simplified;
}
async simplifyAll(root, options) {
if (!root && this._viewer)
root = this._viewer.scene.modelRoot;
if (!root) {
console.error('SimplifyModifierPlugin: No root found');
return;
}
if (!this.initialized) {
await this.initialize();
if (!this.initialized) {
this._viewer?.console.error('SimplifyModifierPlugin cannot be initialized');
return;
}
}
const geometries = [];
root.traverse((o) => {
if (o.geometry && !geometries.includes(o.geometry))
geometries.push(o.geometry);
});
if (!geometries.length) {
console.error('SimplifyModifierPlugin: No geometries found');
return;
}
return this.simplifyGeometries(geometries, options);
}
async simplifySelected() {
if (!this._viewer)
return;
if (!this.initialized) {
await this.initialize();
if (!this.initialized) {
await this._viewer.dialog.alert('Simplify: SimplifyModifierPlugin cannot be initialized');
return;
}
}
const selected = this._pickingPlugin?.getSelectedObject();
if (!selected?.isObject3D) {
await this._viewer.dialog.alert('Simplify: No Object Selected');
return;
}
let doAll = false;
if (!selected.geometry)
doAll = true;
else if (selected.children.length === 0)
doAll = true;
if (!doAll) {
const resp = await this._viewer.dialog.confirm('Simplify: Simplify all in hierarchy?');
if (resp)
doAll = true;
}
if (doAll) {
this.simplifyGeometries();
}
else {
this.simplifyGeometry(selected.geometry);
}
}
}
SimplifyModifierPlugin.PluginType = 'SimplifyModifierPlugin';
__decorate([
uiSlider('Simplify Factor', [0, 1])
], SimplifyModifierPlugin.prototype, "simplifyFactor", void 0);
__decorate([
uiButton('Simplify All', { sendArgs: false })
], SimplifyModifierPlugin.prototype, "simplifyAll", null);
__decorate([
uiButton('Simplify Selected')
], SimplifyModifierPlugin.prototype, "simplifySelected", null);
//# sourceMappingURL=SimplifyModifierPlugin.js.map