UNPKG

@polygonjs/polygonjs

Version:

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

178 lines (157 loc) 5.78 kB
import {LineBasicMaterial, Color, Vector4, Vector3, BufferGeometry, BufferAttribute, LineSegments} from 'three'; import {QuadObject} from '../QuadObject'; import {QUADTesselationParams} from '../QuadCommon'; import {Attribute} from '../../../Attribute'; import {QuadPrimitive} from '../QuadPrimitive'; import {stringMatchMask} from '../../../../String'; import {ThreejsPrimitiveTriangle} from '../../three/ThreejsPrimitiveTriangle'; import {prepareObject} from './QuadToObject3DCommon'; import {NEIGHBOUR_INDICES, HalfEdgeIndices, quadHalfEdgeIndices} from '../graph/QuadGraphCommon'; import {NeighbourData, QuadGraph} from '../graph/QuadGraph'; const _v4 = new Vector4(); const _p0 = new Vector3(); const _p1 = new Vector3(); const _p2 = new Vector3(); const _p3 = new Vector3(); const _neighbourData: NeighbourData = { quadNode: null, neighbourIndex: null, }; const _indices: HalfEdgeIndices = { index0: -1, index1: -1, }; const _lineMaterialByColorStyle: Map<string, LineBasicMaterial> = new Map(); function _createOrFindLineMaterial(color: Color) { let material = _lineMaterialByColorStyle.get(color.getStyle()); if (!material) { material = new LineBasicMaterial({ color, linewidth: 1, }); _lineMaterialByColorStyle.set(color.getStyle(), material); } return material; } export function quadToLine(quadObject: QuadObject, graph:QuadGraph,options: QUADTesselationParams) { const {splitQuads, unsharedEdges, wireframeColor} = options; const quadGeometry = quadObject.geometry; const quadsCount = quadGeometry.quadsCount(); const indices = quadGeometry.index; const srcPositions = quadGeometry.attributes.position.array; const edges = new Map<number, number>(); const buildUnsharedEdges = () => { const newIndices: number[] = new Array(); const positions: number[] = []; for (let i = 0; i < quadsCount; i++) { const currentNode = graph.quadNode(i)!; for (const ni of NEIGHBOUR_INDICES) { graph.neighbourData(i, ni, _neighbourData); if (_neighbourData.quadNode == null) { quadHalfEdgeIndices(currentNode.indices, ni, _indices); const i0 = _indices.index0; const i1 = _indices.index1; _p0.fromArray(srcPositions, i0 * 3); _p1.fromArray(srcPositions, i1 * 3); positions.push(_p0.x, _p0.y, _p0.z); positions.push(_p1.x, _p1.y, _p1.z); newIndices.push(newIndices.length); newIndices.push(newIndices.length); } } } const geometry = new BufferGeometry(); geometry.setAttribute(Attribute.POSITION, new BufferAttribute(new Float32Array(positions), 3)); geometry.setIndex(newIndices); return geometry; }; const splitGeometry = () => { const newIndices: number[] = new Array(); const positions: number[] = []; for (let i = 0; i < quadsCount; i++) { _v4.fromArray(indices, i * 4); _p0.fromArray(srcPositions, _v4.x * 3); _p1.fromArray(srcPositions, _v4.y * 3); _p2.fromArray(srcPositions, _v4.z * 3); _p3.fromArray(srcPositions, _v4.w * 3); const j = i * 4 * 3; const k = i * 8; const m = i * 4; _p0.toArray(positions, j); _p1.toArray(positions, j + 3); _p2.toArray(positions, j + 6); _p3.toArray(positions, j + 9); newIndices[k] = m; newIndices[k + 1] = m + 1; newIndices[k + 2] = m + 1; newIndices[k + 3] = m + 2; newIndices[k + 4] = m + 2; newIndices[k + 5] = m + 3; newIndices[k + 6] = m + 3; newIndices[k + 7] = m; } const geometry = new BufferGeometry(); geometry.setAttribute(Attribute.POSITION, new BufferAttribute(new Float32Array(positions), 3)); geometry.setIndex(newIndices); return geometry; }; const unsplitGeometry = () => { const newIndices: number[] = new Array(); const addEdgeUnsplit = (a: number, b: number) => { if (edges.get(a) == b || edges.get(b) == a) { return; } edges.set(a, b); edges.set(b, a); newIndices.push(a, b); }; for (let i = 0; i < quadsCount; i++) { _v4.fromArray(indices, i * 4); addEdgeUnsplit(_v4.x, _v4.y); addEdgeUnsplit(_v4.y, _v4.z); addEdgeUnsplit(_v4.z, _v4.w); addEdgeUnsplit(_v4.w, _v4.x); } const positions = [...srcPositions]; const geometry = new BufferGeometry(); geometry.setAttribute(Attribute.POSITION, new BufferAttribute(new Float32Array(positions), 3)); geometry.setIndex(newIndices); return geometry; }; const geometry = unsharedEdges ? buildUnsharedEdges() : splitQuads ? splitGeometry() : unsplitGeometry(); const material = _createOrFindLineMaterial(wireframeColor); const lineSegments = new LineSegments(geometry, material); // primitive attributes if (splitQuads) { const primitiveAttributes = QuadPrimitive.attributesFromGeometry(quadGeometry); if (primitiveAttributes) { const primitiveAttributeNames = Object.keys(primitiveAttributes).filter((attributeName) => stringMatchMask(attributeName, options.primitiveAttributes) ); for (const primitiveAttributeName of primitiveAttributeNames) { const srcAttribute = primitiveAttributes[primitiveAttributeName]; const destPrimitivesCount = quadsCount * 4; const destAttribute = { itemSize: srcAttribute.itemSize, isString: srcAttribute.isString, array: new Array(destPrimitivesCount * srcAttribute.itemSize), }; ThreejsPrimitiveTriangle.addAttribute(lineSegments, primitiveAttributeName, destAttribute); const srcArray = srcAttribute.array; const destArray = destAttribute.array; const srcArraySize = srcArray.length; let j = 0; for (let i = 0; i < srcArraySize; i++) { // 1 quad -> 4 lines destArray[j] = srcArray[i]; destArray[j + 1] = srcArray[i]; destArray[j + 2] = srcArray[i]; destArray[j + 3] = srcArray[i]; j += 4; } } } } prepareObject(lineSegments, {shadow: false}); return lineSegments; }