diff --git a/packages/viewer/src/modules/Batch.ts b/packages/viewer/src/modules/Batch.ts index 72ce5b150..e8530610a 100644 --- a/packages/viewer/src/modules/Batch.ts +++ b/packages/viewer/src/modules/Batch.ts @@ -1,15 +1,75 @@ -import { BufferGeometry } from 'three' +import { + BufferGeometry, + Float32BufferAttribute, + Material, + Mesh, + Uint32BufferAttribute +} from 'three' import { NodeRenderView } from './NodeRenderView' export default class Batch { + private id: string private renderViews: NodeRenderView[] private bufferGeometry: BufferGeometry + private material: Material + public mesh: Mesh - public constructor() { + public constructor(id: string, renderViews: NodeRenderView[]) { + this.id = id + this.renderViews = renderViews this.bufferGeometry = new BufferGeometry() } - public addRenderView(renderView: NodeRenderView) { - this.renderViews.push(renderView) + public setMaterial(material: Material) { + this.material = material + + this.mesh + this.material + this.renderViews + this.bufferGeometry + } + + public buildBatch() { + const indicesCount = this.renderViews.flatMap( + (val: NodeRenderView) => val.renderData.geometry.attributes.INDEX + ).length + const attributeCount = this.renderViews.flatMap( + (val: NodeRenderView) => val.renderData.geometry.attributes.POSITION + ).length + const indices = new Int32Array(indicesCount) + const position = new Float32Array(attributeCount) + let offset = 0 + let arrayOffset = 0 + for (let k = 0; k < this.renderViews.length; k++) { + const geometry = this.renderViews[k].renderData.geometry + indices.set( + geometry.attributes.INDEX.map((val) => val + offset), + arrayOffset + ) + position.set(geometry.attributes.POSITION, offset) + this.renderViews[k].setBatchData( + this.id, + arrayOffset, + geometry.attributes.INDEX.length + ) + + offset += geometry.attributes.POSITION.length + arrayOffset += geometry.attributes.INDEX.length + } + + this.bufferGeometry.setIndex(new Uint32BufferAttribute(indices, 1)) + + this.bufferGeometry.setAttribute( + 'position', + new Float32BufferAttribute(position, 3) + ) + + this.bufferGeometry.computeVertexNormals() + this.bufferGeometry.computeBoundingSphere() + this.bufferGeometry.computeBoundingBox() + + // Geometry.updateRTEGeometry(this.bufferGeometry) + + this.mesh = new Mesh(this.bufferGeometry, this.material) } } diff --git a/packages/viewer/src/modules/Batcher.ts b/packages/viewer/src/modules/Batcher.ts index e6c0f591d..716b9ce0b 100644 --- a/packages/viewer/src/modules/Batcher.ts +++ b/packages/viewer/src/modules/Batcher.ts @@ -1,9 +1,12 @@ +import { generateUUID } from 'three/src/math/MathUtils' +import Batch from './Batch' import { SpeckleType } from './converter/GeometryConverter' import { WorldTree } from './converter/WorldTree' import Materials from './materials/Materials' export default class Batcher { private materials: Materials + public batches: { [id: string]: Batch } = {} public constructor() { this.materials = new Materials() @@ -30,6 +33,15 @@ export default class Batcher { const batch = rendeViews.slice(batchStart, m) batches.push(batch) batchStart += batch.length + + const material = this.materials.updateMaterialMap( + materialHashes[k], + batch[0].renderData.renderMaterial + ) + const batchID = generateUUID() + this.batches[batchID] = new Batch(batchID, batch) + this.batches[batchID].setMaterial(material) + this.batches[batchID].buildBatch() break } } diff --git a/packages/viewer/src/modules/NodeRenderView.ts b/packages/viewer/src/modules/NodeRenderView.ts index 1b5c18541..c3f8e78ba 100644 --- a/packages/viewer/src/modules/NodeRenderView.ts +++ b/packages/viewer/src/modules/NodeRenderView.ts @@ -17,12 +17,13 @@ export interface NodeRenderData { geometry: GeometryData renderMaterial: RenderMaterial displayStyle: DisplayStyle - batchId: string - batchIndexStart: number - batchIndexCount: number } export class NodeRenderView { + private _batchId: string + private _batchIndexStart: number + private _batchIndexCount: number + private readonly _renderData: NodeRenderData private _materialHash: number @@ -34,9 +35,23 @@ export class NodeRenderView { return this._materialHash } + public get hasGeometry() { + return this._renderData.geometry && this._renderData.geometry.attributes + } + public constructor(data: NodeRenderData) { this._renderData = data this._materialHash = this.getMaterialHash(data.renderMaterial) + + this._batchId + this._batchIndexCount + this._batchIndexStart + } + + public setBatchData(id: string, start: number, count: number) { + this._batchId = id + this._batchIndexStart = start + this._batchIndexCount = count } private getMaterialHash(material: RenderMaterial) { diff --git a/packages/viewer/src/modules/RenderTree.ts b/packages/viewer/src/modules/RenderTree.ts index 8f7cfc2a6..ba5212342 100644 --- a/packages/viewer/src/modules/RenderTree.ts +++ b/packages/viewer/src/modules/RenderTree.ts @@ -16,6 +16,8 @@ export class RenderTree { this.root.walk((node: TreeNode): boolean => { const rendeNode = this.buildRenderNode(node) node.model.renderView = rendeNode ? new NodeRenderView(rendeNode) : null + if (node.model.renderView && node.model.renderView.hasGeometry) + Geometry.transformGeometryData(rendeNode.geometry, this.computeTransform(node)) return true }) } @@ -33,10 +35,7 @@ export class RenderTree { Materials.renderMaterialFromNode(node.parent), displayStyle: Materials.displayStyleFromNode(node) || - Materials.displayStyleFromNode(node.parent), - batchId: 'n/a', - batchIndexStart: 0, - batchIndexCount: 0 + Materials.displayStyleFromNode(node.parent) } } return ret @@ -77,7 +76,7 @@ export class RenderTree { if (renderView) { const renderData = renderView.renderData if (renderData.speckleType === SpeckleType.BlockInstance) return true - Geometry.transformGeometryData(renderData.geometry, this.computeTransform(node)) + // Geometry.transformGeometryData(renderData.geometry, this.computeTransform(node)) let geometry = null let wrapperType = '' /** ULTA-TEMPORARY */ diff --git a/packages/viewer/src/modules/ViewerObjectLoader.js b/packages/viewer/src/modules/ViewerObjectLoader.js index d4100b66a..109058d05 100644 --- a/packages/viewer/src/modules/ViewerObjectLoader.js +++ b/packages/viewer/src/modules/ViewerObjectLoader.js @@ -107,12 +107,17 @@ export default class ViewerObjectLoader { WorldTree.getRenderTree().buildRenderTree() batcher.makeBatches() + for (const k in batcher.batches) { + this.viewer.scene.add(batcher.batches[k].mesh) + } + parsedObjects = WorldTree.getRenderTree().getObjectWrappers() for (let k = 0; k < parsedObjects.length; k++) { await this.converter.asyncPause() this.viewer.sceneManager.addObject(parsedObjects[k]) } + await this.viewer.sceneManager.postLoadFunction() this.viewer.emit('load-complete') diff --git a/packages/viewer/src/modules/materials/Materials.ts b/packages/viewer/src/modules/materials/Materials.ts index b9585fd24..30b03e8c7 100644 --- a/packages/viewer/src/modules/materials/Materials.ts +++ b/packages/viewer/src/modules/materials/Materials.ts @@ -1,7 +1,6 @@ -import { DoubleSide, Material } from 'three' +import { DoubleSide, Material, MeshStandardMaterial } from 'three' import { TreeNode } from '../converter/WorldTree' import { DisplayStyle, RenderMaterial } from '../NodeRenderView' -import SpeckleStandardMaterial from './SpeckleStandardMaterial' export default class Materials { private readonly materialMap: { [hash: number]: Material } = {} @@ -30,18 +29,30 @@ export default class Materials { return displayStyle } - public updateMaterialMap(hash: number, renderMaterial: RenderMaterial) { + public updateMaterialMap(hash: number, renderMaterial: RenderMaterial): Material { if (this.materialMap[hash]) { console.warn(`Duplicate material hash found: ${hash}, overwritting`) } - this.materialMap[hash] = new SpeckleStandardMaterial({ - color: renderMaterial.color, - emissive: 0x0, - roughness: 1, - metalness: 0, - side: DoubleSide // TBD - // clippingPlanes: this.viewer.sectionBox.planes - }) + if (renderMaterial) { + this.materialMap[hash] = new MeshStandardMaterial({ + color: renderMaterial.color, + emissive: 0x0, + roughness: 1, + metalness: 0, + side: DoubleSide // TBD + // clippingPlanes: this.viewer.sectionBox.planes + }) + } else { + this.materialMap[hash] = new MeshStandardMaterial({ + color: 0xff00ff, + emissive: 0x0, + roughness: 1, + metalness: 0, + side: DoubleSide // TBD + // clippingPlanes: this.viewer.sectionBox.planes + }) + } + return this.materialMap[hash] } }