UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

242 lines 9.71 kB
import { autoGPUInstanceMeshes, isInScene, toIndexedGeometry } from '../../three/utils'; import { Vector3 } from 'three'; import { ThreeViewer } from '../../viewer'; export const iGeometryCommons = { setDirty: function (options) { this.dispatchEvent({ bubbleToObject: true, ...options, type: 'geometryUpdate', geometry: this }); // this sets sceneUpdate in root scene this.refreshUi(); }, refreshUi: function () { this.uiConfig?.uiRefresh?.(true, 'postFrame', 1); }, dispose: (superDispose) => function (force = true) { if (!force && (this.userData.disposeOnIdle === false || isInScene(this))) return; superDispose.call(this); }, clone: (superClone) => function () { return iGeometryCommons.upgradeGeometry.call(superClone.call(this)); }, upgradeGeometry: upgradeGeometry, center: (superCenter) => function (offset, keepWorldPosition = false, setDirty = true) { if (keepWorldPosition) { offset = offset ? offset.clone() : new Vector3(); superCenter.call(this, offset); offset.negate(); const meshes = this.appliedMeshes; for (const m of meshes) { m.updateMatrix(); m.position.copy(offset).applyMatrix4(m.matrix); if (setDirty) m.setDirty(); } } else { superCenter.call(this, offset); } if (setDirty) this.setDirty(); return this; }, center2: function (offset, keepWorldPosition = false, setDirty = true) { const offset1 = offset ? offset : new Vector3(); if (keepWorldPosition) { this.center(offset1, false, false); const meshes = this.appliedMeshes; const positions = new WeakMap(); for (const m of meshes) { m.updateMatrix(); positions.set(m, m.position.clone()); m.position.set(-offset1.x, -offset1.y, -offset1.z).applyMatrix4(m.matrix); if (setDirty) m.setDirty(); } if (setDirty) this.setDirty(); return () => { // undo for (const m of meshes) { const pos = positions.get(m); if (!pos) { console.warn('GeometryCommons: No position found for mesh', m); continue; } m.position.copy(pos); if (setDirty) m.setDirty(); } if (setDirty) this.setDirty(); }; } else { this.center(offset1, false, false); if (setDirty) this.setDirty(); return () => { // undo this.translate(-offset1.x, -offset1.y, -offset1.z); if (setDirty) this.setDirty(); }; } }, makeUiConfig: function () { if (this.uiConfig) return this.uiConfig; return { label: 'Geometry', type: 'folder', children: [ { type: 'input', property: [this, 'uuid'], disabled: true, }, { type: 'input', property: [this, 'name'], }, { type: 'button', label: 'Center Geometry', value: async () => { if (!await ThreeViewer.Dialog.confirm('This will move the objects based on the geometry center, do you want to continue?')) return; return this.center2(); }, }, { type: 'button', label: 'Center Geometry (keep position)', value: async () => { if (!await ThreeViewer.Dialog.confirm('This will move the geometry center keeping the object position, do you want to continue?')) return; return this.center2(undefined, true); }, }, { type: 'button', label: 'Compute vertex normals', value: async () => { if (this.hasAttribute('normal') && !await ThreeViewer.Dialog.confirm('Normals already exist, replace with computed normals?\nThis action cannot be undone.')) return; this.computeVertexNormals(); this.setDirty(); }, }, { type: 'button', label: 'Compute vertex tangents', value: async () => { if (this.hasAttribute('tangent') && !await ThreeViewer.Dialog.confirm('Tangents already exist, replace with computed tangents?\nThis action cannot be undone.')) return; this.computeTangents(); this.setDirty(); }, }, { type: 'button', label: 'Normalize normals', value: () => { this.normalizeNormals(); this.setDirty(); }, }, { type: 'button', label: 'Convert to indexed', hidden: () => !!this.index, value: async () => { if (this.attributes.index) return; const tolerance = parseFloat(await ThreeViewer.Dialog.prompt('Convert to Indexed: Tolerance?', '-1') ?? '-1'); toIndexedGeometry(this, tolerance); this.setDirty(); }, }, { type: 'button', label: 'Convert to non-indexed', hidden: () => !this.index, value: () => { if (!this.attributes.index) return; this.toNonIndexed(); this.setDirty(); }, }, { type: 'button', label: 'Create uv1 from uv', value: async () => { if (this.hasAttribute('uv1')) { if (!await ThreeViewer.Dialog.confirm('uv1 already exists, replace with uv data?\nThis action cannot be undone.')) return; } this.setAttribute('uv1', this.getAttribute('uv')); this.setDirty(); }, }, { type: 'button', label: 'Remove vertex color attribute', hidden: () => !this.hasAttribute('color'), value: async () => { if (!this.hasAttribute('color')) { await ThreeViewer.Dialog.prompt('No color attribute found'); return; } if (!await ThreeViewer.Dialog.confirm('Remove color attribute?')) return; this.deleteAttribute('color'); this.setDirty(); }, }, { type: 'button', label: 'Auto GPU Instances', hidden: () => !this.appliedMeshes || this.appliedMeshes.size < 2, value: async () => { if (!await ThreeViewer.Dialog.confirm('This will automatically create Instanced Mesh from geometry instances. This action is irreversible, do you want to continue?')) return; autoGPUInstanceMeshes(this); }, }, { type: 'input', label: 'Mesh count', getValue: () => this.appliedMeshes?.size ?? 0, disabled: true, }, ], }; }, }; function upgradeGeometry() { if (this.assetType === 'geometry') return this; // already upgraded if (!this.isBufferGeometry) { console.error('Geometry is not a BufferGeometry', this); return this; } this.assetType = 'geometry'; this.dispose = iGeometryCommons.dispose(this.dispose); this.center = iGeometryCommons.center(this.center); this.clone = iGeometryCommons.clone(this.clone); if (!this.center2) this.center2 = iGeometryCommons.center2; if (!this.setDirty) this.setDirty = iGeometryCommons.setDirty; if (!this.refreshUi) this.refreshUi = iGeometryCommons.refreshUi; if (!this.appliedMeshes) this.appliedMeshes = new Set(); if (!this.userData) this.userData = {}; this.uiConfig = iGeometryCommons.makeUiConfig.call(this); // todo: dispose uiconfig on geometry dispose // todo: add serialization? return this; } //# sourceMappingURL=iGeometryCommons.js.map