UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

120 lines (113 loc) 4.12 kB
import {BaseSopOperation} from './_Base'; import {DefaultOperationParams} from '../_Base'; import {CoreGroup, Object3DWithGeometry} from '../../geometry/Group'; import {ObjectType, objectTypeFromConstructor} from '../../geometry/Constant'; import {Material} from 'three/src/materials/Material'; import {MapUtils} from '../../MapUtils'; import {CoreGeometry} from '../../geometry/Geometry'; import {Object3D} from 'three/src/core/Object3D'; import {Group} from 'three/src/objects/Group'; import {Mesh} from 'three/src/objects/Mesh'; import {InputCloneMode} from '../../../engine/poly/InputCloneMode'; interface MergeSopParams extends DefaultOperationParams { compact: boolean; } export class MergeSopOperation extends BaseSopOperation { static readonly DEFAULT_PARAMS: MergeSopParams = { compact: true, }; static readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE; static type(): Readonly<'merge'> { return 'merge'; } // TODO: improvement: // for compact, I should really keep track of geometry ids, // to make sure I am not including a geometry twice, if there is a hierarchy cook(input_contents: CoreGroup[], params: MergeSopParams) { let all_objects: Object3D[] = []; for (let input_core_group of input_contents) { if (input_core_group) { const objects = input_core_group.objects(); if (params.compact) { for (let object of objects) { object.traverse((child) => { all_objects.push(child as Object3DWithGeometry); }); } } else { // if we are not compact, // we only use the current level, not children for (let object of input_core_group.objects()) { all_objects.push(object); } } } } if (params.compact) { all_objects = this._make_compact(all_objects); } for (let object of all_objects) { object.traverse((o) => { o.matrixAutoUpdate = false; }); } return this.create_core_group_from_objects(all_objects); } _make_compact(all_objects: Object3D[]): Object3DWithGeometry[] { const materials_by_object_type: Map<ObjectType, Material> = new Map(); const objects_by_type: Map<ObjectType, Object3DWithGeometry[]> = new Map(); // objects_by_type.set(ObjectType.MESH, []); // objects_by_type.set(ObjectType.POINTS, []); // objects_by_type.set(ObjectType.LINE_SEGMENTS, []); const ordered_object_types: ObjectType[] = []; for (let object of all_objects) { object.traverse((object3d: Object3D) => { if (object3d instanceof Group) { // we do not want groups, // as their children will end up being duplicated return; } const object = object3d as Object3DWithGeometry; if (object.geometry) { const object_type = objectTypeFromConstructor(object.constructor); if (!ordered_object_types.includes(object_type)) { ordered_object_types.push(object_type); } if (object_type) { const found_mat = materials_by_object_type.get(object_type); if (!found_mat) { materials_by_object_type.set(object_type, (object as Mesh).material as Material); } MapUtils.push_on_array_at_entry(objects_by_type, object_type, object); } } }); } const merged_objects: Object3DWithGeometry[] = []; ordered_object_types.forEach((object_type) => { const objects = objects_by_type.get(object_type); if (objects) { const geometries = []; for (let object of objects) { const geometry = object.geometry; geometry.applyMatrix4(object.matrix); geometries.push(geometry); } // TODO: test that this works with geometries with same attributes try { const merged_geometry = CoreGeometry.merge_geometries(geometries); if (merged_geometry) { const material = materials_by_object_type.get(object_type); const object = this.create_object(merged_geometry, object_type, material); merged_objects.push(object as Object3DWithGeometry); } else { this.states?.error.set('merge failed, check that input geometries have the same attributes'); } } catch (e) { this.states?.error.set(e); } } }); return merged_objects; } }