Multi Viewer (#1518)

* Fixed an issue with HDRIs not generating proper PMREMs over multiple viewer instances

* WIP on makign the world tree multiple instanced

* The WorldTree is no longer static. Each viewer has it's own instance and it hands it over to whoever needs it.

* Fixed an issue with filtering and the new non-static tree. Also removed the 'root' key from the NodeData structure since it's not needed

* Added an guard when building batches for situations where all render views have invalid geometries. This generally means there is somethign wrong with the stream itself

* multi-viewer css fixes

---------

Co-authored-by: Dimitrie Stefanescu <didimitrie@gmail.com>
This commit is contained in:
Alexandru Popovici
2023-04-21 13:12:15 +03:00
committed by GitHub
parent 90439d7d41
commit 4aef572a44
23 changed files with 888 additions and 648 deletions
+24 -1
View File
@@ -8,7 +8,30 @@
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="renderer"></div>
<div class="w-screen h-screen">
<div id="renderer" class="absolute w-full h-full"></div>
<div
id="renderer-controls"
class="relative overflow-y-scroll h-full pointer-events-none w-auto"
></div>
</div>
<script type="module" src="/src/main.ts"></script>
<!-- <div class="w-screen h-screen flex flex-col" id="multi-root">
<div class="h-1/2">
<div class="absolute w-full h-1/2" id="renderer0"></div>
<div
id="renderer0-controls"
class="relative overflow-y-scroll h-full pointer-events-none w-auto"
></div>
</div>
<div class="border-t-2 border-blue-500 h-1/2">
<div class="absolute w-full h-1/2" id="renderer1"></div>
<div
id="renderer1-controls"
class="relative overflow-y-scroll h-full pointer-events-none w-auto"
></div>
</div>
</div>
<script type="module" src="/src/main-multi.ts"></script> -->
</body>
</html>
+104 -105
View File
@@ -1,4 +1,4 @@
import { Box3, Viewer, WorldTree } from '@speckle/viewer'
import { Box3, WorldTree } from '@speckle/viewer'
import { Vector3 } from '@speckle/viewer'
import {
CanonicalView,
@@ -23,11 +23,11 @@ export default class Sandbox {
private objectControls
private batchesFolder
public static urlParams = {
public urlParams = {
url: 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb'
}
public static sceneParams = {
public sceneParams = {
worldSize: { x: 0, y: 0, z: 0 },
worldOrigin: { x: 0, y: 0, z: 0 },
pixelThreshold: 0.5,
@@ -35,7 +35,7 @@ export default class Sandbox {
tonemapping: 4 //'ACESFilmicToneMapping'
}
public static pipelineParams = {
public pipelineParams = {
pipelineOutput: 8,
accumulationFrames: 16,
dynamicAoEnabled: true,
@@ -61,7 +61,7 @@ export default class Sandbox {
}
}
public static lightParams: SunLightConfiguration = {
public lightParams: SunLightConfiguration = {
enabled: true,
castShadow: true,
intensity: 5,
@@ -73,7 +73,7 @@ export default class Sandbox {
shadowcatcher: true
}
public static batchesParams = {
public batchesParams = {
showBvh: false,
totalBvhSize: 0,
explode: 0,
@@ -81,11 +81,11 @@ export default class Sandbox {
culling: true
}
public static filterParams = {
public filterParams = {
filterBy: 'Volume'
}
public static shadowCatcherParams = {
public shadowCatcherParams = {
textureSize: 512,
weights: { x: 1, y: 1, z: 0, w: 1 },
blurRadius: 16,
@@ -94,12 +94,17 @@ export default class Sandbox {
sigmoidStrength: 2
}
public constructor(viewer: DebugViewer, selectionList: SelectionEvent[]) {
public constructor(
container: HTMLElement,
viewer: DebugViewer,
selectionList: SelectionEvent[]
) {
this.viewer = viewer
this.selectionList = selectionList
this.pane = new Pane({ title: 'Speckle Sandbox', expanded: true })
this.pane['containerElem_'].style =
'position:fixed; top: 5px; right: 5px; width: 300px;'
// Mad HTML/CSS skills
container.appendChild(this.pane['containerElem_'])
this.pane['containerElem_'].style = 'pointer-events:auto;'
this.tabs = this.pane.addTab({
pages: [
@@ -116,20 +121,20 @@ export default class Sandbox {
this.addViewControls()
this.addBatches()
this.properties = this.viewer.getObjectProperties()
Sandbox.batchesParams.totalBvhSize = this.getBVHSize()
this.batchesParams.totalBvhSize = this.getBVHSize()
this.refresh()
})
viewer.on(ViewerEvent.UnloadComplete, (url: string) => {
this.removeViewControls()
this.addViewControls()
this.properties = this.viewer.getObjectProperties()
Viewer.World.reduceWorld(WorldTree.getRenderTree(url).treeBounds)
viewer.World.reduceWorld(WorldTree.getRenderTree(url).treeBounds)
})
viewer.on(ViewerEvent.UnloadAllComplete, (url: string) => {
this.removeViewControls()
this.addViewControls()
this.properties = this.viewer.getObjectProperties()
Viewer.World.resetWorld()
viewer.World.resetWorld()
url
})
viewer.on(ViewerEvent.ObjectClicked, (selectionEvent: SelectionEvent) => {
@@ -284,7 +289,7 @@ export default class Sandbox {
}
public makeGenericUI() {
this.tabs.pages[0].addInput(Sandbox.urlParams, 'url', {
this.tabs.pages[0].addInput(this.urlParams, 'url', {
title: 'url'
})
@@ -293,7 +298,7 @@ export default class Sandbox {
})
loadButton.on('click', () => {
this.loadUrl(Sandbox.urlParams.url)
this.loadUrl(this.urlParams.url)
})
const clearButton = this.tabs.pages[0].addButton({
@@ -343,13 +348,19 @@ export default class Sandbox {
})
const dark = localStorage.getItem('dark') === 'dark'
if (dark) {
console.log(document.getElementById('multi-root'))
document.getElementById('multi-root')?.classList.toggle('background-dark')
document.getElementById('renderer')?.classList.toggle('background-dark')
}
darkModeToggle.on('click', () => {
const dark = document
.getElementById('renderer')
?.classList.toggle('background-dark')
let dark = false
if (document.getElementById('renderer'))
dark = document.getElementById('renderer')?.classList.toggle('background-dark')
else
dark = document
.getElementById('multi-root')
?.classList.toggle('background-dark')
localStorage.setItem('dark', dark ? `dark` : `light`)
})
@@ -397,31 +408,31 @@ export default class Sandbox {
title: 'World',
expanded: true
})
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'x', {
worldFolder.addInput(this.sceneParams.worldSize, 'x', {
disabled: true,
label: 'Size-x',
step: 0.00000001
})
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'y', {
worldFolder.addInput(this.sceneParams.worldSize, 'y', {
disabled: true,
label: 'Size-y',
step: 0.00000001
})
worldFolder.addInput(Sandbox.sceneParams.worldSize, 'z', {
worldFolder.addInput(this.sceneParams.worldSize, 'z', {
disabled: true,
label: 'Size-z',
step: 0.00000001
})
worldFolder.addSeparator()
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'x', {
worldFolder.addInput(this.sceneParams.worldOrigin, 'x', {
disabled: true,
label: 'Origin-x'
})
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'y', {
worldFolder.addInput(this.sceneParams.worldOrigin, 'y', {
disabled: true,
label: 'Origin-y'
})
worldFolder.addInput(Sandbox.sceneParams.worldOrigin, 'z', {
worldFolder.addInput(this.sceneParams.worldOrigin, 'z', {
disabled: true,
label: 'Origin-z'
})
@@ -433,13 +444,13 @@ export default class Sandbox {
})
postFolder
.addInput(Sandbox.sceneParams, 'exposure', {
.addInput(this.sceneParams, 'exposure', {
min: 0,
max: 1
})
.on('change', () => {
this.viewer.getRenderer().renderer.toneMappingExposure =
Sandbox.sceneParams.exposure
this.sceneParams.exposure
this.viewer.requestRender()
})
@@ -468,14 +479,14 @@ export default class Sandbox {
})
postFolder
.addInput(Sandbox.sceneParams, 'tonemapping', {
.addInput(this.sceneParams, 'tonemapping', {
options: {
Linear: 1,
ACES: 4
}
})
.on('change', () => {
this.viewer.getRenderer().renderer.toneMapping = Sandbox.sceneParams.tonemapping
this.viewer.getRenderer().renderer.toneMapping = this.sceneParams.tonemapping
this.viewer.requestRender()
})
@@ -484,7 +495,7 @@ export default class Sandbox {
expanded: true
})
pipelineFolder
.addInput(Sandbox.pipelineParams, 'pipelineOutput', {
.addInput(this.pipelineParams, 'pipelineOutput', {
options: {
DEPTH_RGBA: 0,
DEPTH: 1,
@@ -498,18 +509,18 @@ export default class Sandbox {
}
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
pipelineFolder
.addInput(Sandbox.pipelineParams, 'accumulationFrames', {
.addInput(this.pipelineParams, 'accumulationFrames', {
min: 1,
max: 128,
step: 1
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
@@ -519,33 +530,33 @@ export default class Sandbox {
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'intensity', { min: 0, max: 5 })
.addInput(this.pipelineParams.dynamicAoParams, 'intensity', { min: 0, max: 5 })
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'kernelRadius', {
.addInput(this.pipelineParams.dynamicAoParams, 'kernelRadius', {
min: 0,
max: 500
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'bias', {
.addInput(this.pipelineParams.dynamicAoParams, 'bias', {
min: -1,
max: 1
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'normalsType', {
.addInput(this.pipelineParams.dynamicAoParams, 'normalsType', {
options: {
DEFAULT: 0,
ADVANCED: 1,
@@ -553,35 +564,35 @@ export default class Sandbox {
}
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'blurEnabled', {})
.addInput(this.pipelineParams.dynamicAoParams, 'blurEnabled', {})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'blurRadius', {
.addInput(this.pipelineParams.dynamicAoParams, 'blurRadius', {
min: 0,
max: 10
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
dynamicAoFolder
.addInput(Sandbox.pipelineParams.dynamicAoParams, 'blurDepthCutoff', {
.addInput(this.pipelineParams.dynamicAoParams, 'blurDepthCutoff', {
min: 0,
max: 1,
step: 0.00001
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
@@ -596,13 +607,13 @@ export default class Sandbox {
// this.viewer.requestRender()
// })
staticAoFolder
.addInput(Sandbox.pipelineParams.staticAoParams, 'intensity', {
.addInput(this.pipelineParams.staticAoParams, 'intensity', {
min: 0,
max: 5,
step: 0.01
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
// staticAoFolder
@@ -627,34 +638,34 @@ export default class Sandbox {
// this.viewer.requestRender()
// })
staticAoFolder
.addInput(Sandbox.pipelineParams.staticAoParams, 'kernelRadius', {
.addInput(this.pipelineParams.staticAoParams, 'kernelRadius', {
min: 0,
max: 1000
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
staticAoFolder
.addInput(Sandbox.pipelineParams.staticAoParams, 'bias', {
.addInput(this.pipelineParams.staticAoParams, 'bias', {
min: -1,
max: 1,
step: 0.0001
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
staticAoFolder
.addInput(Sandbox.pipelineParams.staticAoParams, 'kernelSize', {
.addInput(this.pipelineParams.staticAoParams, 'kernelSize', {
min: 1,
max: 128,
step: 1
})
.on('change', () => {
this.viewer.getRenderer().pipelineOptions = Sandbox.pipelineParams
this.viewer.getRenderer().pipelineOptions = this.pipelineParams
this.viewer.requestRender()
})
@@ -667,62 +678,62 @@ export default class Sandbox {
expanded: true
})
directLightFolder
.addInput(Sandbox.lightParams, 'enabled', {
.addInput(this.lightParams, 'enabled', {
label: 'Sun Enabled'
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'castShadow', {
.addInput(this.lightParams, 'castShadow', {
label: 'Sun Shadows'
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'intensity', {
.addInput(this.lightParams, 'intensity', {
label: 'Sun Intensity',
min: 0,
max: 10
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'color', {
.addInput(this.lightParams, 'color', {
view: 'color',
label: 'Sun Color'
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'elevation', {
.addInput(this.lightParams, 'elevation', {
label: 'Sun Elevation',
min: 0,
max: Math.PI
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'azimuth', {
.addInput(this.lightParams, 'azimuth', {
label: 'Sun Azimuth',
min: -Math.PI * 0.5,
max: Math.PI * 0.5
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
.addInput(Sandbox.lightParams, 'radius', {
.addInput(this.lightParams, 'radius', {
label: 'Sun Radius',
min: 0,
max: 1000
})
.on('change', () => {
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
directLightFolder
@@ -757,14 +768,14 @@ export default class Sandbox {
})
indirectLightsFolder
.addInput(Sandbox.lightParams, 'indirectLightIntensity', {
.addInput(this.lightParams, 'indirectLightIntensity', {
label: 'Probe Intensity',
min: 0,
max: 10
})
.on('change', (value) => {
value
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
const shadowcatcherFolder = this.tabs.pages[1].addFolder({
@@ -773,14 +784,14 @@ export default class Sandbox {
})
shadowcatcherFolder
.addInput(Sandbox.lightParams, 'shadowcatcher', { label: 'Enabled' })
.addInput(this.lightParams, 'shadowcatcher', { label: 'Enabled' })
.on('change', (value) => {
value
this.viewer.setLightConfiguration(Sandbox.lightParams)
this.viewer.setLightConfiguration(this.lightParams)
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'textureSize', {
.addInput(this.shadowCatcherParams, 'textureSize', {
label: 'Texture Size',
min: 1,
max: 1024,
@@ -788,12 +799,11 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'weights', {
.addInput(this.shadowCatcherParams, 'weights', {
label: 'weights',
x: { min: 0, max: 100 },
y: { min: 0, max: 100 },
@@ -802,12 +812,11 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'blurRadius', {
.addInput(this.shadowCatcherParams, 'blurRadius', {
label: 'Blur Radius',
min: 1,
max: 128,
@@ -815,12 +824,11 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'stdDeviation', {
.addInput(this.shadowCatcherParams, 'stdDeviation', {
label: 'Blur Std Deviation',
min: 1,
max: 128,
@@ -828,12 +836,11 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'sigmoidRange', {
.addInput(this.shadowCatcherParams, 'sigmoidRange', {
label: 'Sigmoid Range',
min: -10,
max: 10,
@@ -841,12 +848,11 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
shadowcatcherFolder
.addInput(Sandbox.shadowCatcherParams, 'sigmoidStrength', {
.addInput(this.shadowCatcherParams, 'sigmoidStrength', {
label: 'Sigmoid Strength',
min: -10,
max: 10,
@@ -854,8 +860,7 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.getRenderer().shadowcatcher.configuration =
Sandbox.shadowCatcherParams
this.viewer.getRenderer().shadowcatcher.configuration = this.shadowCatcherParams
this.viewer.getRenderer().updateShadowCatcher()
})
}
@@ -866,7 +871,7 @@ export default class Sandbox {
expanded: true
})
filteringFolder.addInput(Sandbox.filterParams, 'filterBy', {
filteringFolder.addInput(this.filterParams, 'filterBy', {
options: {
Volume: 'parameters.HOST_VOLUME_COMPUTED.value',
Area: 'parameters.HOST_AREA_COMPUTED.value',
@@ -883,7 +888,7 @@ export default class Sandbox {
})
.on('click', () => {
const data = this.properties.find((value) => {
return value.key === Sandbox.filterParams.filterBy
return value.key === this.filterParams.filterBy
}) as PropertyInfo
this.viewer.setColorFilter(data)
this.pane.refresh()
@@ -909,19 +914,19 @@ export default class Sandbox {
})
container
.addInput(Sandbox.batchesParams, 'showBvh', {
.addInput(this.batchesParams, 'showBvh', {
label: 'Show BVH'
})
.on('change', (ev) => {
this.viewer.getRenderer().showBVH = ev.value
this.viewer.requestRender()
})
container.addInput(Sandbox.batchesParams, 'totalBvhSize', {
container.addInput(this.batchesParams, 'totalBvhSize', {
label: 'BVH Size(MB)',
disabled: true
})
container
.addInput(Sandbox.batchesParams, 'explode', {
.addInput(this.batchesParams, 'explode', {
label: 'Explode',
min: 0,
max: 0.25,
@@ -929,13 +934,10 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.explode(
Sandbox.batchesParams.explode,
Sandbox.batchesParams.explodeRange
)
this.viewer.explode(this.batchesParams.explode, this.batchesParams.explodeRange)
})
container
.addInput(Sandbox.batchesParams, 'explodeRange', {
.addInput(this.batchesParams, 'explodeRange', {
label: 'Explode Range',
min: 1,
max: 1000,
@@ -943,10 +945,7 @@ export default class Sandbox {
})
.on('change', (value) => {
value
this.viewer.explode(
Sandbox.batchesParams.explode,
Sandbox.batchesParams.explodeRange
)
this.viewer.explode(this.batchesParams.explode, this.batchesParams.explodeRange)
})
// container
// .addInput(Sandbox.batchesParams, 'culling', {
+261
View File
@@ -0,0 +1,261 @@
import {
DefaultViewerParams,
SelectionEvent,
ViewerEvent,
DebugViewer,
Viewer
} from '@speckle/viewer'
import './style.css'
import Sandbox from './Sandbox'
// const container0 = document.querySelector<HTMLElement>('#renderer0')
// if (!container0) {
// throw new Error("Couldn't find #app container!")
// }
// const container1 = document.querySelector<HTMLElement>('#renderer1')
// if (!container1) {
// throw new Error("Couldn't find #app container!")
// }
const createViewer = async (containerName: string, stream: string) => {
const container = document.querySelector<HTMLElement>(containerName)
const controlsContainer = document.querySelector<HTMLElement>(
`${containerName}-controls`
)
if (!container) {
throw new Error("Couldn't find #app container!")
}
if (!controlsContainer) {
throw new Error("Couldn't find #app controls container!")
}
// Viewer setup
const params = DefaultViewerParams
params.showStats = true
params.verbose = true
const multiSelectList: SelectionEvent[] = []
const viewer: Viewer = new DebugViewer(container, params)
await viewer.init()
const sandbox = new Sandbox(controlsContainer, viewer as DebugViewer, multiSelectList)
window.addEventListener('load', () => {
viewer.resize()
})
viewer.on(
ViewerEvent.LoadProgress,
(a: { progress: number; id: string; url: string }) => {
if (a.progress >= 1) {
viewer.resize()
}
}
)
viewer.on(ViewerEvent.LoadComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadAllComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.ObjectClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
multiSelectList.length = 0
await viewer.resetSelection()
viewer.setSectionBox()
return
}
if (!selectionInfo.multiple) multiSelectList.length = 0
const guids = multiSelectList.map((val) => val.hits[0].guid)
if (
(selectionInfo.multiple && !guids.includes(selectionInfo.hits[0].guid)) ||
multiSelectList.length === 0
) {
multiSelectList.push(selectionInfo)
}
const ids = multiSelectList.map((val) => val.hits[0].object.id)
console.log(selectionInfo)
await viewer.selectObjects(ids as string[])
})
viewer.on(ViewerEvent.ObjectDoubleClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
viewer.zoom()
return
}
viewer.zoom([selectionInfo.hits[0].object.id as string])
})
sandbox.makeGenericUI()
sandbox.makeSceneUI()
sandbox.makeFilteringUI()
sandbox.makeBatchesUI()
await sandbox.loadUrl(stream)
}
createViewer(
'#renderer0',
'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec'
)
createViewer('#renderer1', 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getStream = () => {
return (
// prettier-ignore
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
// Revit sample house (good for bim-like stuff with many display meshes)
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'Super' heavy revit shit
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
// IFC building (good for a tree based structure)
// 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223'
// IFC story, a subtree of the above
// 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216'
// Small scale lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/e22f696b08'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/af6098915b?c=%5B0.02144,-0.0377,0.05554,0.00566,0.00236,0,0,1%5D'
// AutoCAD
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
//Blizzard world
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec'
//Car
// 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e'
// Jonathon's
// 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011'
// Alex's cube
// 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D'
// Groups of groups
// 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f'
// Arc flowers
// 'https://latest.speckle.dev/streams/9e6c4343ba/commits/037e382aa2'
// Car lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// Arc and lines
// ' https://speckle.xyz/streams/99abc74dd4/commits/b32fdcf171?c=%5B198440.6051,6522070.21462,19199.49584,176653.24219,6523663.5,0,0,1%5D'
// AUTOCAD test stream
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/b49bfc73ea'
// REVIT test stream
// 'https://latest.speckle.dev/streams/c544db35f5/commits/7c29374369'
// Arcs
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/912d83412e'
// Freezers
// 'https://speckle.xyz/streams/f0532359ac/commits/98678e2a3d?c=%5B2455.15367,2689.87156,4366.68444,205.422,-149.41199,148.749,0,1%5D'
//Gergo's house
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/78bdd8eb76'
// Point cloud
// 'https://latest.speckle.dev/streams/2d19273d31/commits/9ceb423feb'
// 'https://latest.speckle.dev/streams/7707df6cae/commits/02bdf09092'
// 'https://latest.speckle.dev/streams/ca0378725b/commits/fbae00db5a'
// Luis sphere
// 'https://speckle.xyz/streams/b85d53c3b4/commits/b47f21b707'
// Crankshaft
// 'https://speckle.xyz/streams/c239718aff/commits/b3a8cfb97d'
// Building AO params
// 'https://latest.speckle.dev/streams/0dd74866d0/commits/317e210afa'
// Murder Cube
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/7f0c4d2fc1/'
// Classroom
// 'https://speckle.xyz/streams/0208ffb67b/commits/a980292728'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/328bd99997'
// 'https://latest.speckle.dev/streams/83e18d886f/commits/532bd6be3e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/f12861736e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/1015d417ea'
// Jedd's views
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/e6632fe057'
// 'https://latest.speckle.dev/streams/7d051a6449/commits/7632757a33'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// MEPs (whatever they are)
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/8575fe2978'
// Alex cubes
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// Tekla
// 'https://latest.speckle.dev/streams/caec6d6676/commits/588c731104'
// Purple market square
// 'https://latest.speckle.dev/streams/4ed51ed832/commits/5a313ac116'
// Sum building
// 'https://latest.speckle.dev/streams/92b620fb17/commits/4ea2759162'
// Boat
// 'https://latest.speckle.dev/streams/92b620fb17/commits/ba5df427db'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/c9ebe49824'
// Dim's dome
// 'https://latest.speckle.dev/streams/92b620fb17/commits/158d4e8bec'
// Engines 'n Shit
// 'https://latest.speckle.dev/streams/92b620fb17/commits/80b25e6e6c'
// Dim's tower
// 'https://latest.speckle.dev/streams/92b620fb17/commits/7fd3ec04c0'
//COD
// 'https://latest.speckle.dev/streams/d3c83b47bf/commits/5f76b7ef3d?overlay=34577a1a92,571d460754,4c39b56c32,a62dd3a5da&c=%5B2046.38919,1074.97765,125.18054,2088.91862,1025.71927,94.66317,0,1%5D'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/0feb23d263'
// Jonathon's not loading
// 'https://speckle.xyz/streams/ca99defd4b/commits/589b265c99'
// Jonathon's 3070
// 'https://speckle.xyz/streams/7ce9010d71/commits/d29e56fe75'
// Filter issue
// 'https://speckle.xyz/streams/f95d8deb90/commits/30f31becb6'
// Transparent
// 'https://latest.speckle.dev/streams/b5cc4e967c/objects/20343e0e8d469613a9d407499a6c38b1'
// dark
// 'https://latest.speckle.dev/streams/b5cc4e967c/commits/efdf3e2728?c=%5B-59.16128,-41.76491,-4.77376,-4.08052,-12.63558,-4.77376,0,1%5D'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?filter=%7B%7D&c=%5B-31.02357,37.60008,96.58899,11.01564,7.40652,66.0411,0,1%5D)'
// double
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?overlay=c009dbe144&filter=%7B%7D&c=%5B-104.70053,-98.80617,67.44669,6.53096,1.8739,38.584,0,1%5D'
// 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb',
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// tekla 2
// 'https://speckle.xyz/streams/be4813ccd2/commits/da85000921?c=%5B-1.12295,-2.60901,6.12402,4.77979,0.555,3.63346,0,1%5D'
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/bb7b718a1a'
// large meshes
// 'https://speckle.xyz/streams/48e6e33aa6/commits/2cf892f1b0'
// large lines
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/8f297ad0cd'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/6b1b1195c4'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/cef1e7527b'
// large lines
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// Instances Rhino
// 'https://latest.speckle.dev/streams/f92e060177/commits/1fff853107'
// Instances Revit
// 'https://latest.speckle.dev/streams/f92e060177/commits/92858681b7'
// 'https://latest.speckle.dev/streams/f92e060177/commits/655771674e'
// 'https://latest.speckle.dev/streams/f92e060177/commits/00dbbf4509'
// 'https://latest.speckle.dev/streams/f92e060177/commits/46fd255010'
// 'https://latest.speckle.dev/streams/f92e060177/commits/038a587267'
// 'https://latest.speckle.dev/streams/3f895e614f/commits/8a3e424997'
// Big curves
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// 'https://speckle.xyz/streams/7ce9010d71/commits/afda4ffdf8'
// Jonathon's lines
// 'https://speckle.xyz/streams/7ce9010d71/commits/8cd9e7e4fc'
// 'https://speckle.xyz/streams/7ce9010d71/objects/f46f95746975591c18b0b854dab5b570 '
// 'https://speckle.xyz/streams/813b728084/commits/e2f5ac9775'
// Overlayhs
// 'https://latest.speckle.dev/streams/85b9f0b9f5/commits/cdfbf3e036?overlay=71f61af444,00fe449457,53a6692b79'
//'Rafinery'
// 'https://speckle.xyz/streams/b7cac6a6df/commits/2e42381302'
// 'https://speckle.xyz/streams/7ce9010d71/commits/b8bbfd0c05?c=%5B-4.50925,11.1348,5.38124,-0.23829,0.68512,-0.09006,0,1%5D'
)
}
+236 -270
View File
@@ -9,279 +9,245 @@ import {
import './style.css'
import Sandbox from './Sandbox'
const container = document.querySelector<HTMLElement>('#renderer')
if (!container) {
throw new Error("Couldn't find #app container!")
const createViewer = async (containerName: string, stream: string) => {
const container = document.querySelector<HTMLElement>(containerName)
const controlsContainer = document.querySelector<HTMLElement>(
`${containerName}-controls`
)
if (!container) {
throw new Error("Couldn't find #app container!")
}
if (!controlsContainer) {
throw new Error("Couldn't find #app controls container!")
}
// Viewer setup
const params = DefaultViewerParams
params.showStats = true
params.verbose = true
const multiSelectList: SelectionEvent[] = []
const viewer: Viewer = new DebugViewer(container, params)
await viewer.init()
const sandbox = new Sandbox(controlsContainer, viewer as DebugViewer, multiSelectList)
window.addEventListener('load', () => {
viewer.resize()
})
viewer.on(
ViewerEvent.LoadProgress,
(a: { progress: number; id: string; url: string }) => {
if (a.progress >= 1) {
viewer.resize()
}
}
)
viewer.on(ViewerEvent.LoadComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadAllComplete, () => {
Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize)
Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.ObjectClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
multiSelectList.length = 0
await viewer.resetSelection()
viewer.setSectionBox()
return
}
if (!selectionInfo.multiple) multiSelectList.length = 0
const guids = multiSelectList.map((val) => val.hits[0].guid)
if (
(selectionInfo.multiple && !guids.includes(selectionInfo.hits[0].guid)) ||
multiSelectList.length === 0
) {
multiSelectList.push(selectionInfo)
}
const ids = multiSelectList.map((val) => val.hits[0].object.id)
console.log(selectionInfo)
await viewer.selectObjects(ids as string[])
})
viewer.on(ViewerEvent.ObjectDoubleClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
viewer.zoom()
return
}
viewer.zoom([selectionInfo.hits[0].object.id as string])
})
sandbox.makeGenericUI()
sandbox.makeSceneUI()
sandbox.makeFilteringUI()
sandbox.makeBatchesUI()
await sandbox.loadUrl(stream)
}
// Viewer setup
const params = DefaultViewerParams
params.showStats = true
params.verbose = true
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getStream = () => {
return (
// prettier-ignore
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
// Revit sample house (good for bim-like stuff with many display meshes)
const multiSelectList: SelectionEvent[] = []
const viewer: Viewer = new DebugViewer(container, params)
await viewer.init()
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'Super' heavy revit shit
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
// IFC building (good for a tree based structure)
// 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223'
// IFC story, a subtree of the above
// 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216'
// Small scale lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/e22f696b08'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/af6098915b?c=%5B0.02144,-0.0377,0.05554,0.00566,0.00236,0,0,1%5D'
// AutoCAD
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
//Blizzard world
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec'
//Car
// 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e'
// Jonathon's
// 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011'
// Alex's cube
// 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D'
// Groups of groups
// 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f'
// Arc flowers
// 'https://latest.speckle.dev/streams/9e6c4343ba/commits/037e382aa2'
// Car lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// Arc and lines
// ' https://speckle.xyz/streams/99abc74dd4/commits/b32fdcf171?c=%5B198440.6051,6522070.21462,19199.49584,176653.24219,6523663.5,0,0,1%5D'
// AUTOCAD test stream
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/b49bfc73ea'
// REVIT test stream
// 'https://latest.speckle.dev/streams/c544db35f5/commits/7c29374369'
// Arcs
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/912d83412e'
// Freezers
// 'https://speckle.xyz/streams/f0532359ac/commits/98678e2a3d?c=%5B2455.15367,2689.87156,4366.68444,205.422,-149.41199,148.749,0,1%5D'
//Gergo's house
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/78bdd8eb76'
// Point cloud
// 'https://latest.speckle.dev/streams/2d19273d31/commits/9ceb423feb'
// 'https://latest.speckle.dev/streams/7707df6cae/commits/02bdf09092'
// 'https://latest.speckle.dev/streams/ca0378725b/commits/fbae00db5a'
// Luis sphere
// 'https://speckle.xyz/streams/b85d53c3b4/commits/b47f21b707'
// Crankshaft
// 'https://speckle.xyz/streams/c239718aff/commits/b3a8cfb97d'
// Building AO params
// 'https://latest.speckle.dev/streams/0dd74866d0/commits/317e210afa'
// Murder Cube
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/7f0c4d2fc1/'
// Classroom
// 'https://speckle.xyz/streams/0208ffb67b/commits/a980292728'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/328bd99997'
// 'https://latest.speckle.dev/streams/83e18d886f/commits/532bd6be3e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/f12861736e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/1015d417ea'
// Jedd's views
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/e6632fe057'
// 'https://latest.speckle.dev/streams/7d051a6449/commits/7632757a33'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// MEPs (whatever they are)
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/8575fe2978'
// Alex cubes
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// Tekla
// 'https://latest.speckle.dev/streams/caec6d6676/commits/588c731104'
// Purple market square
// 'https://latest.speckle.dev/streams/4ed51ed832/commits/5a313ac116'
// Sum building
// 'https://latest.speckle.dev/streams/92b620fb17/commits/4ea2759162'
// Boat
// 'https://latest.speckle.dev/streams/92b620fb17/commits/ba5df427db'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/c9ebe49824'
// Dim's dome
// 'https://latest.speckle.dev/streams/92b620fb17/commits/158d4e8bec'
// Engines 'n Shit
// 'https://latest.speckle.dev/streams/92b620fb17/commits/80b25e6e6c'
// Dim's tower
// 'https://latest.speckle.dev/streams/92b620fb17/commits/7fd3ec04c0'
//COD
// 'https://latest.speckle.dev/streams/d3c83b47bf/commits/5f76b7ef3d?overlay=34577a1a92,571d460754,4c39b56c32,a62dd3a5da&c=%5B2046.38919,1074.97765,125.18054,2088.91862,1025.71927,94.66317,0,1%5D'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/0feb23d263'
// Jonathon's not loading
// 'https://speckle.xyz/streams/ca99defd4b/commits/589b265c99'
// Jonathon's 3070
// 'https://speckle.xyz/streams/7ce9010d71/commits/d29e56fe75'
// Filter issue
// 'https://speckle.xyz/streams/f95d8deb90/commits/30f31becb6'
// Transparent
// 'https://latest.speckle.dev/streams/b5cc4e967c/objects/20343e0e8d469613a9d407499a6c38b1'
// dark
// 'https://latest.speckle.dev/streams/b5cc4e967c/commits/efdf3e2728?c=%5B-59.16128,-41.76491,-4.77376,-4.08052,-12.63558,-4.77376,0,1%5D'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?filter=%7B%7D&c=%5B-31.02357,37.60008,96.58899,11.01564,7.40652,66.0411,0,1%5D)'
// double
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?overlay=c009dbe144&filter=%7B%7D&c=%5B-104.70053,-98.80617,67.44669,6.53096,1.8739,38.584,0,1%5D'
// 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb',
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// tekla 2
// 'https://speckle.xyz/streams/be4813ccd2/commits/da85000921?c=%5B-1.12295,-2.60901,6.12402,4.77979,0.555,3.63346,0,1%5D'
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/bb7b718a1a'
const sandbox = new Sandbox(viewer as DebugViewer, multiSelectList)
// large meshes
// 'https://speckle.xyz/streams/48e6e33aa6/commits/2cf892f1b0'
// large lines
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/8f297ad0cd'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/6b1b1195c4'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/cef1e7527b'
// large lines
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// Instances Rhino
// 'https://latest.speckle.dev/streams/f92e060177/commits/1fff853107'
// Instances Revit
// 'https://latest.speckle.dev/streams/f92e060177/commits/92858681b7'
// 'https://latest.speckle.dev/streams/f92e060177/commits/655771674e'
// 'https://latest.speckle.dev/streams/f92e060177/commits/00dbbf4509'
// 'https://latest.speckle.dev/streams/f92e060177/commits/46fd255010'
// 'https://latest.speckle.dev/streams/f92e060177/commits/038a587267'
// 'https://latest.speckle.dev/streams/3f895e614f/commits/8a3e424997'
// Big curves
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// 'https://speckle.xyz/streams/7ce9010d71/commits/afda4ffdf8'
// Jonathon's lines
// 'https://speckle.xyz/streams/7ce9010d71/commits/8cd9e7e4fc'
// 'https://speckle.xyz/streams/7ce9010d71/objects/f46f95746975591c18b0b854dab5b570 '
// 'https://speckle.xyz/streams/813b728084/commits/e2f5ac9775'
// Overlayhs
// 'https://latest.speckle.dev/streams/85b9f0b9f5/commits/cdfbf3e036?overlay=71f61af444,00fe449457,53a6692b79'
//'Rafinery'
// 'https://speckle.xyz/streams/b7cac6a6df/commits/2e42381302'
// 'https://speckle.xyz/streams/7ce9010d71/commits/b8bbfd0c05?c=%5B-4.50925,11.1348,5.38124,-0.23829,0.68512,-0.09006,0,1%5D'
)
}
window.addEventListener('load', () => {
viewer.resize()
})
const container0 = document.querySelector<HTMLElement>('#renderer')
if (!container0) {
throw new Error("Couldn't find app container!")
}
/** QUERY TEST */
// container.addEventListener('mousemove', async (ev) => {
// const point = viewer.Utils.screenToNDC(ev.clientX, ev.clientY)
// const res: QueryResult = viewer.query<IntersectionQuery>({
// id: 'test',
// point,
// operation: 'Pick'
// })
// if (!res) return
// const hitPoint = {
// x: res.objects[0].point.x,
// y: res.objects[0].point.y,
// z: res.objects[0].point.z
// }
// await viewer.selectObjects([res.objects[0].object.id])
// const resProj: QueryResult = viewer.query<PointQuery>({
// id: 'test',
// point: hitPoint,
// operation: 'Project'
// })
// // console.log(viewer.Utils.NDCToScreen(res_p.x, res_p.y))
// const resUnProj: QueryResult = viewer.query<PointQuery>({
// id: 'test',
// point: { x: resProj.x, y: resProj.y, z: resProj.z },
// operation: 'Unproject'
// })
// console.log(
// hitPoint.x - resUnProj.x,
// hitPoint.y - resUnProj.y,
// hitPoint.z - resUnProj.z
// )
// })
viewer.on(
ViewerEvent.LoadProgress,
(a: { progress: number; id: string; url: string }) => {
if (a.progress >= 1) {
viewer.resize()
}
}
)
// const updt = () => {
// const resOcc = viewer.query<IntersectionQuery>({
// id: 'testX',
// point: { x: -2.2779617121296436, y: -1.9397099063369891, z: 7.411126386421243 },
// tolerance: 0.001,
// operation: 'Occlusion'
// })
// if (resOcc) console.log(resOcc.objects === null)
// requestAnimationFrame(updt)
// }
// requestAnimationFrame(updt)
viewer.on(ViewerEvent.LoadComplete, () => {
Object.assign(Sandbox.sceneParams.worldSize, Viewer.World.worldSize)
Object.assign(Sandbox.sceneParams.worldOrigin, Viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadComplete, () => {
Object.assign(Sandbox.sceneParams.worldSize, Viewer.World.worldSize)
Object.assign(Sandbox.sceneParams.worldOrigin, Viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.UnloadAllComplete, () => {
Object.assign(Sandbox.sceneParams.worldSize, Viewer.World.worldSize)
Object.assign(Sandbox.sceneParams.worldOrigin, Viewer.World.worldOrigin)
sandbox.refresh()
})
viewer.on(ViewerEvent.ObjectClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
multiSelectList.length = 0
await viewer.resetSelection()
viewer.setSectionBox()
return
}
if (!selectionInfo.multiple) multiSelectList.length = 0
const guids = multiSelectList.map((val) => val.hits[0].guid)
if (
(selectionInfo.multiple && !guids.includes(selectionInfo.hits[0].guid)) ||
multiSelectList.length === 0
) {
multiSelectList.push(selectionInfo)
}
const ids = multiSelectList.map((val) => val.hits[0].object.id)
console.log(selectionInfo)
await viewer.selectObjects(ids as string[])
})
viewer.on(ViewerEvent.ObjectDoubleClicked, async (selectionInfo: SelectionEvent) => {
if (!selectionInfo) {
viewer.zoom()
return
}
viewer.zoom([selectionInfo.hits[0].object.id as string])
})
sandbox.makeGenericUI()
sandbox.makeSceneUI()
sandbox.makeFilteringUI()
sandbox.makeBatchesUI()
// Load demo object
// setTimeout(async () => {
// const objUrl = (
// await UrlHelper.getResourceUrls(
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
// )
// )[0]
// viewer.cancelLoad(objUrl)
// }, 1500)
await sandbox.loadUrl(
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D'
// Revit sample house (good for bim-like stuff with many display meshes)
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'Super' heavy revit shit
// 'https://speckle.xyz/streams/e6f9156405/commits/0694d53bb5'
// IFC building (good for a tree based structure)
// 'https://latest.speckle.dev/streams/92b620fb17/commits/2ebd336223'
// IFC story, a subtree of the above
// 'https://latest.speckle.dev/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216'
// Small scale lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/e22f696b08'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/af6098915b?c=%5B0.02144,-0.0377,0.05554,0.00566,0.00236,0,0,1%5D'
// AutoCAD
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/d10f2af1ce'
//Blizzard world
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/aa1c393aec'
//Car
// 'https://latest.speckle.dev/streams/17d2e25a97/commits/6b6cf3d43e'
// Jonathon's
// 'https://latest.speckle.dev/streams/501258ee5f/commits/f885570011'
// Alex's cube
// 'https://latest.speckle.dev/streams/46e3e0e1ec/commits/a6392c19d6?c=%5B6.85874,2.9754,0.79022,0,0,0,0,1%5D'
// Groups of groups
// 'https://speckle.xyz/streams/1ce562e99a/commits/6fa28a5a0f'
// Arc flowers
// 'https://latest.speckle.dev/streams/9e6c4343ba/commits/037e382aa2'
// Car lines
// 'https://speckle.xyz/streams/638d3b1f83/commits/6025e2b546?c=%5B2.18058,-0.20814,9.67642,3.85491,5.05364,0,0,1%5D'
// Arc and lines
// ' https://speckle.xyz/streams/99abc74dd4/commits/b32fdcf171?c=%5B198440.6051,6522070.21462,19199.49584,176653.24219,6523663.5,0,0,1%5D'
// AUTOCAD test stream
// 'https://latest.speckle.dev/streams/3ed8357f29/commits/b49bfc73ea'
// REVIT test stream
// 'https://latest.speckle.dev/streams/c544db35f5/commits/7c29374369'
// Arcs
// 'https://latest.speckle.dev/streams/0c6ad366c4/commits/912d83412e'
// Freezers
// 'https://speckle.xyz/streams/f0532359ac/commits/98678e2a3d?c=%5B2455.15367,2689.87156,4366.68444,205.422,-149.41199,148.749,0,1%5D'
//Gergo's house
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/78bdd8eb76'
// Point cloud
// 'https://latest.speckle.dev/streams/2d19273d31/commits/9ceb423feb'
// 'https://latest.speckle.dev/streams/7707df6cae/commits/02bdf09092'
// 'https://latest.speckle.dev/streams/ca0378725b/commits/fbae00db5a'
// Luis sphere
// 'https://speckle.xyz/streams/b85d53c3b4/commits/b47f21b707'
// Crankshaft
// 'https://speckle.xyz/streams/c239718aff/commits/b3a8cfb97d'
// Building AO params
// 'https://latest.speckle.dev/streams/0dd74866d0/commits/317e210afa'
// Murder Cube
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/7f0c4d2fc1/'
// Classroom
// 'https://speckle.xyz/streams/0208ffb67b/commits/a980292728'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/328bd99997'
// 'https://latest.speckle.dev/streams/83e18d886f/commits/532bd6be3e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/f12861736e'
// 'https://latest.speckle.dev/streams/1c2b3db9fb/commits/1015d417ea'
// Jedd's views
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/e6632fe057'
// 'https://latest.speckle.dev/streams/7d051a6449/commits/7632757a33'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// MEPs (whatever they are)
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/8575fe2978'
// Alex cubes
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7'
// Tekla
// 'https://latest.speckle.dev/streams/caec6d6676/commits/588c731104'
// Purple market square
// 'https://latest.speckle.dev/streams/4ed51ed832/commits/5a313ac116'
// Sum building
// 'https://latest.speckle.dev/streams/92b620fb17/commits/4ea2759162'
// Boat
// 'https://latest.speckle.dev/streams/92b620fb17/commits/ba5df427db'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/c9ebe49824'
// Dim's dome
// 'https://latest.speckle.dev/streams/92b620fb17/commits/158d4e8bec'
// Engines 'n Shit
// 'https://latest.speckle.dev/streams/92b620fb17/commits/80b25e6e6c'
// Dim's tower
// 'https://latest.speckle.dev/streams/92b620fb17/commits/7fd3ec04c0'
//COD
// 'https://latest.speckle.dev/streams/d3c83b47bf/commits/5f76b7ef3d?overlay=34577a1a92,571d460754,4c39b56c32,a62dd3a5da&c=%5B2046.38919,1074.97765,125.18054,2088.91862,1025.71927,94.66317,0,1%5D'
// 'https://latest.speckle.dev/streams/4658eb53b9/commits/0feb23d263'
// Jonathon's not loading
// 'https://speckle.xyz/streams/ca99defd4b/commits/589b265c99'
// Jonathon's 3070
// 'https://speckle.xyz/streams/7ce9010d71/commits/d29e56fe75'
// Filter issue
// 'https://speckle.xyz/streams/f95d8deb90/commits/30f31becb6'
// Transparent
// 'https://latest.speckle.dev/streams/b5cc4e967c/objects/20343e0e8d469613a9d407499a6c38b1'
// dark
// 'https://latest.speckle.dev/streams/b5cc4e967c/commits/efdf3e2728?c=%5B-59.16128,-41.76491,-4.77376,-4.08052,-12.63558,-4.77376,0,1%5D'
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?filter=%7B%7D&c=%5B-31.02357,37.60008,96.58899,11.01564,7.40652,66.0411,0,1%5D)'
// double
// 'https://latest.speckle.dev/streams/92b620fb17/commits/b4366a7086?overlay=c009dbe144&filter=%7B%7D&c=%5B-104.70053,-98.80617,67.44669,6.53096,1.8739,38.584,0,1%5D'
// 'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb',
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// 'https://latest.speckle.dev/streams/efd2c6a31d/commits/4b495e1901'
// tekla 2
// 'https://speckle.xyz/streams/be4813ccd2/commits/da85000921?c=%5B-1.12295,-2.60901,6.12402,4.77979,0.555,3.63346,0,1%5D'
// 'https://latest.speckle.dev/streams/85bc4f61c6/commits/bb7b718a1a'
// large meshes
// 'https://speckle.xyz/streams/48e6e33aa6/commits/2cf892f1b0'
// large lines
// 'https://latest.speckle.dev/streams/444bfbd6e4/commits/8f297ad0cd'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/6b1b1195c4'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/cef1e7527b'
// large lines
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// Instances Rhino
// 'https://latest.speckle.dev/streams/f92e060177/commits/1fff853107'
// Instances Revit
// 'https://latest.speckle.dev/streams/f92e060177/commits/92858681b7'
// 'https://latest.speckle.dev/streams/f92e060177/commits/655771674e'
// 'https://latest.speckle.dev/streams/f92e060177/commits/00dbbf4509'
// 'https://latest.speckle.dev/streams/f92e060177/commits/46fd255010'
// 'https://latest.speckle.dev/streams/f92e060177/commits/038a587267'
// 'https://latest.speckle.dev/streams/3f895e614f/commits/8a3e424997'
// Big curves
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/49dad07ae2'
// 'https://speckle.xyz/streams/7ce9010d71/commits/afda4ffdf8'
// Jonathon's lines
// 'https://speckle.xyz/streams/7ce9010d71/commits/8cd9e7e4fc'
// 'https://speckle.xyz/streams/7ce9010d71/objects/f46f95746975591c18b0b854dab5b570 '
// 'https://speckle.xyz/streams/813b728084/commits/e2f5ac9775'
// Overlayhs
// 'https://latest.speckle.dev/streams/85b9f0b9f5/commits/cdfbf3e036?overlay=71f61af444,00fe449457,53a6692b79'
//'Rafinery'
// 'https://speckle.xyz/streams/b7cac6a6df/commits/2e42381302'
// 'https://speckle.xyz/streams/7ce9010d71/commits/b8bbfd0c05?c=%5B-4.50925,11.1348,5.38124,-0.23829,0.68512,-0.09006,0,1%5D'
// 'https://latest.speckle.dev/streams/caec6d6676/commits/588c731104'
// 'https://latest.speckle.dev/streams/f92e060177/commits/1fff853107'
'https://latest.speckle.dev/streams/c43ac05d04/commits/ec724cfbeb'
// 'https://latest.speckle.dev/streams/bbdf474833/commits/83d0eb7e06 '
)
createViewer('#renderer', getStream())
+11
View File
@@ -23,6 +23,17 @@
width: 100%;
}
#renderer0-controls:first-child {
pointer-events: auto;
}
#renderer1-controls:first-child {
pointer-events: auto;
}
canvas {
position: absolute;
}
.button {
border: 0;
line-height: 1.5;
+4
View File
@@ -6,6 +6,8 @@ import { PropertyInfo } from './modules/filtering/PropertyManager'
import { Query, QueryArgsResultMap, QueryResult } from './modules/queries/Query'
import { DataTree } from './modules/tree/DataTree'
import { Utils } from './modules/Utils'
import { WorldTree } from './modules/tree/WorldTree'
import { World } from './modules/World'
export interface ViewerParams {
showStats: boolean
@@ -213,9 +215,11 @@ export interface IViewer {
/** Data ops */
getDataTree(): DataTree
getWorldTree(): WorldTree
query<T extends Query>(query: T): QueryArgsResultMap[T['operation']]
queryAsync(query: Query): Promise<QueryResult>
get Utils(): Utils
get World(): World
getObjects(id: string): BatchObject[]
explode(time: number, range: number)
+8 -8
View File
@@ -13,12 +13,6 @@ import Logger from 'js-logger'
export class Assets {
private static _cache: { [name: string]: Texture } = {}
private static pmremGenerator: PMREMGenerator
public constructor(renderer: WebGLRenderer) {
Assets.pmremGenerator = new PMREMGenerator(renderer)
Assets.pmremGenerator.compileEquirectangularShader()
}
private static getLoader(src: string, assetType: AssetType): TextureLoader {
if (assetType === undefined) assetType = src.split('.').pop() as AssetType
@@ -36,7 +30,10 @@ export class Assets {
}
}
public static getEnvironment(asset: Asset | string): Promise<Texture> {
public static getEnvironment(
asset: Asset | string,
renderer: WebGLRenderer
): Promise<Texture> {
let srcUrl: string = null
let assetType: AssetType = undefined
if ((<Asset>asset).src) {
@@ -55,9 +52,12 @@ export class Assets {
loader.load(
srcUrl,
(texture) => {
const pmremRT = this.pmremGenerator.fromEquirectangular(texture)
const generator = new PMREMGenerator(renderer)
generator.compileEquirectangularShader()
const pmremRT = generator.fromEquirectangular(texture)
this._cache[srcUrl] = pmremRT.texture
texture.dispose()
generator.dispose()
resolve(this._cache[srcUrl])
},
undefined,
@@ -1,4 +1,3 @@
import { WorldTree } from './tree/WorldTree'
import { Viewer } from './Viewer'
export class DebugViewer extends Viewer {
@@ -9,8 +8,4 @@ export class DebugViewer extends Viewer {
requestRenderShadowmap() {
this.getRenderer().updateDirectLights()
}
getWorldTree() {
return WorldTree.getInstance()
}
}
@@ -376,11 +376,6 @@ export default class SectionBox extends EventEmitter {
this.controls.showZ = true
}
// setBoxFromObjects(objectIds: string[], offset = 0.05) {
// WorldTree.getInstance().walk() => Solved
// this.setBox(...)
// }
setBox(targetBox, offset = 0.05) {
let box
+55 -41
View File
@@ -33,7 +33,7 @@ import SpeckleDepthMaterial from './materials/SpeckleDepthMaterial'
import SpeckleStandardMaterial from './materials/SpeckleStandardMaterial'
import { NodeRenderView } from './tree/NodeRenderView'
import { Viewer } from './Viewer'
import { TreeNode, WorldTree } from './tree/WorldTree'
import { TreeNode } from './tree/WorldTree'
import {
CanonicalView,
DefaultLightConfiguration,
@@ -482,7 +482,10 @@ export default class SpeckleRenderer {
}
public addRenderTree(subtreeId: string) {
this.batcher.makeBatches(subtreeId, SpeckleTypeAllRenderables)
this.batcher.makeBatches(
this.viewer.getWorldTree().getRenderTree(subtreeId),
SpeckleTypeAllRenderables
)
const subtreeGroup = new Group()
subtreeGroup.name = subtreeId
subtreeGroup.layers.set(ObjectLayers.STREAM_CONTENT)
@@ -510,12 +513,14 @@ export default class SpeckleRenderer {
this.rootGroup.add(subtreeGroup)
const generator = this.batcher.makeBatchesAsync(
subtreeId,
this.viewer.getWorldTree().getRenderTree(subtreeId),
SpeckleTypeAllRenderables,
undefined,
priority
)
for await (const batch of generator) {
if (!batch) continue
this.addBatch(batch, subtreeGroup)
this.zoom()
if (batch.geometryType === GeometryType.MESH) {
@@ -572,6 +577,7 @@ export default class SpeckleRenderer {
parent.add(bvhHelper)
}
}
this.viewer.World.expandWorld(batch.bounds)
}
public removeRenderTree(subtreeId: string) {
@@ -829,7 +835,7 @@ export default class SpeckleRenderer {
const queryResult = []
for (let k = 0; k < rvs.length; k++) {
const hitId = rvs[k].renderData.id
const hitNode = WorldTree.getInstance().findId(hitId)
const hitNode = this.viewer.getWorldTree().findId(hitId)
let parentNode = hitNode
while (!parentNode.model.atomic && parentNode.parent) {
parentNode = parentNode.parent
@@ -952,11 +958,16 @@ export default class SpeckleRenderer {
let box = new Box3()
const rvs: NodeRenderView[] = []
if (objectIds.length > 0) {
WorldTree.getInstance().walk((node: TreeNode) => {
this.viewer.getWorldTree().walk((node: TreeNode) => {
if (!node.model.atomic) return true
if (!node.model.raw) return true
if (objectIds.indexOf(node.model.raw.id) !== -1) {
rvs.push(...WorldTree.getRenderTree().getRenderViewsForNode(node, node))
rvs.push(
...this.viewer
.getWorldTree()
.getRenderTree()
.getRenderViewsForNode(node, node)
)
}
return true
})
@@ -1241,41 +1252,41 @@ export default class SpeckleRenderer {
}
/** DEBUG */
public onObjectClickDebug(e) {
const results: Array<Intersection> = this._intersections.intersect(
this._scene,
this.viewer.cameraHandler.activeCam.camera,
e,
true,
this.viewer.sectionBox.getCurrentBox()
)
if (!results) {
this.batcher.resetBatchesDrawRanges()
return
}
const result = results[0]
// console.warn(result)
const rv = this.batcher.getRenderView(
result.object.uuid,
result.faceIndex !== undefined ? result.faceIndex : result.index
)
const hitId = rv.renderData.id
// public onObjectClickDebug(e) {
// const results: Array<Intersection> = this._intersections.intersect(
// this._scene,
// this.viewer.cameraHandler.activeCam.camera,
// e,
// true,
// this.viewer.sectionBox.getCurrentBox()
// )
// if (!results) {
// this.batcher.resetBatchesDrawRanges()
// return
// }
// const result = results[0]
// // console.warn(result)
// const rv = this.batcher.getRenderView(
// result.object.uuid,
// result.faceIndex !== undefined ? result.faceIndex : result.index
// )
// const hitId = rv.renderData.id
// const hitNode = WorldTree.getInstance().findId(hitId)
// console.log(hitNode)
// // const hitNode = WorldTree.getInstance(this.viewer.viewerGuid).findId(hitId)
// // console.log(hitNode)
this.batcher.resetBatchesDrawRanges()
// this.batcher.resetBatchesDrawRanges()
this.batcher.isolateRenderViewBatch(hitId)
if (this.SHOW_BVH) {
this.allObjects.traverse((obj) => {
if (obj.name.includes('_bvh')) {
obj.visible = false
}
})
this.scene.getObjectByName(result.object.id + '_bvh').visible = true
}
}
// this.batcher.isolateRenderViewBatch(hitId)
// if (this.SHOW_BVH) {
// this.allObjects.traverse((obj) => {
// if (obj.name.includes('_bvh')) {
// obj.visible = false
// }
// })
// this.scene.getObjectByName(result.object.id + '_bvh').visible = true
// }
// }
public debugShowBatches() {
this.batcher.resetBatchesDrawRanges()
@@ -1318,7 +1329,7 @@ export default class SpeckleRenderer {
const objects = batches[k].mesh.batchObjects
for (let i = 0; i < objects.length; i++) {
const center = objects[i].renderView.aabb.getCenter(new Vector3())
const dir = center.sub(Viewer.World.worldOrigin)
const dir = center.sub(this.viewer.World.worldOrigin)
dir.normalize().multiplyScalar(time * range)
objects[i].transformTRS(dir, undefined, undefined, undefined)
}
@@ -1330,8 +1341,11 @@ export default class SpeckleRenderer {
}
public getObjects(id: string): BatchObject[] {
const node = WorldTree.getInstance().findId(id)
const rvs = WorldTree.getRenderTree().getRenderViewsForNode(node, node)
const node = this.viewer.getWorldTree().findId(id)
const rvs = this.viewer
.getWorldTree()
.getRenderTree()
.getRenderViewsForNode(node, node)
const batches = this.batcher.getBatches(undefined, GeometryType.MESH) as MeshBatch[]
const meshes = batches.map((batch: MeshBatch) => batch.mesh)
const objects = meshes.flatMap((mesh) => mesh.batchObjects)
+28 -17
View File
@@ -42,10 +42,12 @@ export class Viewer extends EventEmitter implements IViewer {
private startupParams: ViewerParams
/** Viewer components */
private static world: World = new World()
private tree: WorldTree = new WorldTree()
private world: World = new World()
public static Assets: Assets
protected speckleRenderer: SpeckleRenderer
private filteringManager: FilteringManager
private propertyManager: PropertyManager
/** Legacy viewer components (will revisit soon) */
public sectionBox: SectionBox
public cameraHandler: CameraHandler
@@ -58,7 +60,7 @@ export class Viewer extends EventEmitter implements IViewer {
/** various utils/helpers */
private utils: Utils
/** Gets the World object. Currently it's used for info mostly */
public static get World(): World {
public get World(): World {
return this.world
}
@@ -84,7 +86,8 @@ export class Viewer extends EventEmitter implements IViewer {
this.container = container || document.getElementById('renderer')
if (params.showStats) {
this.stats = Stats()
this.container.appendChild(this.stats.dom)
this.container.prepend(this.stats.dom)
this.stats.dom.style.position = 'relative' // Mad CSS skills
}
this.loaders = {}
this.startupParams = params
@@ -97,8 +100,9 @@ export class Viewer extends EventEmitter implements IViewer {
this.speckleRenderer.create(this.container)
window.addEventListener('resize', this.resize.bind(this), false)
new Assets(this.speckleRenderer.renderer)
this.filteringManager = new FilteringManager(this.speckleRenderer)
new Assets()
this.filteringManager = new FilteringManager(this.speckleRenderer, this.tree)
this.propertyManager = new PropertyManager()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// ;(window as any)._V = this // For debugging! ಠ_ಠ
@@ -183,7 +187,10 @@ export class Viewer extends EventEmitter implements IViewer {
public async init(): Promise<void> {
if (this.startupParams.environmentSrc) {
Assets.getEnvironment(this.startupParams.environmentSrc)
Assets.getEnvironment(
this.startupParams.environmentSrc,
this.speckleRenderer.renderer
)
.then((value: Texture) => {
this.speckleRenderer.indirectIBL = value
})
@@ -202,7 +209,7 @@ export class Viewer extends EventEmitter implements IViewer {
resourceURL: string = null,
bypassCache = true
): PropertyInfo[] {
return PropertyManager.getProperties(resourceURL, bypassCache)
return this.propertyManager.getProperties(this.tree, resourceURL, bypassCache)
}
public selectObjects(objectIds: string[]): Promise<FilteringState> {
@@ -325,7 +332,11 @@ export class Viewer extends EventEmitter implements IViewer {
}
public getDataTree(): DataTree {
return WorldTree.getDataTree()
return this.tree.getDataTree()
}
public getWorldTree(): WorldTree {
return this.tree
}
public query<T extends Query>(query: T): QueryArgsResultMap[T['operation']] {
@@ -378,7 +389,7 @@ export class Viewer extends EventEmitter implements IViewer {
}
public getViews(): SpeckleView[] {
return WorldTree.getInstance()
return this.tree
.findAll((node: TreeNode) => {
return node.model.renderView?.speckleType === SpeckleType.View3D
})
@@ -442,7 +453,7 @@ export class Viewer extends EventEmitter implements IViewer {
await this.downloadObject(url, token, enableCaching)
let t0 = performance.now()
WorldTree.getRenderTree(url).buildRenderTree()
this.tree.getRenderTree(url).buildRenderTree()
Logger.log('SYNC Tree build time -> ', performance.now() - t0)
t0 = performance.now()
@@ -465,7 +476,7 @@ export class Viewer extends EventEmitter implements IViewer {
await this.downloadObject(url, token, enableCaching)
let t0 = performance.now()
const treeBuilt = await WorldTree.getRenderTree(url).buildRenderTreeAsync(priority)
const treeBuilt = await this.tree.getRenderTree(url).buildRenderTreeAsync(priority)
Logger.log('ASYNC Tree build time -> ', performance.now() - t0)
if (treeBuilt) {
@@ -481,7 +492,7 @@ export class Viewer extends EventEmitter implements IViewer {
public async cancelLoad(url: string, unload = false) {
this.loaders[url].cancelLoad()
WorldTree.getRenderTree(url).cancelBuild(url)
this.tree.getRenderTree(url).cancelBuild(url)
this.speckleRenderer.cancelRenderTree(url)
if (unload) {
await this.unloadObject(url)
@@ -495,8 +506,8 @@ export class Viewer extends EventEmitter implements IViewer {
(this as EventEmitter).emit(ViewerEvent.Busy, true)
delete this.loaders[url]
this.speckleRenderer.removeRenderTree(url)
WorldTree.getRenderTree(url).purge()
WorldTree.getInstance().purge(url)
this.tree.getRenderTree(url).purge()
this.tree.purge(url)
} finally {
if (--this.inProgressOperations === 0) {
;(this as EventEmitter).emit(ViewerEvent.Busy, false)
@@ -514,12 +525,12 @@ export class Viewer extends EventEmitter implements IViewer {
delete this.loaders[key]
}
this.filteringManager.reset()
WorldTree.getInstance().root.children.forEach((node) => {
this.tree.root.children.forEach((node) => {
this.speckleRenderer.removeRenderTree(node.model.id)
WorldTree.getRenderTree().purge()
this.tree.getRenderTree().purge()
})
WorldTree.getInstance().purge()
this.tree.purge()
} finally {
if (--this.inProgressOperations === 0) {
;(this as EventEmitter).emit(ViewerEvent.Busy, false)
@@ -3,6 +3,7 @@ import { ViewerEvent } from '../IViewer'
import Converter from './converter/Converter'
import EventEmitter from './EventEmitter'
import Logger from 'js-logger'
import { Viewer } from './Viewer'
/**
* Helper wrapper around the ObjectLoader class, with some built in assumptions.
*/
@@ -22,8 +23,8 @@ export default class ViewerObjectLoader {
return this._objectUrl
}
constructor(parentEmitter: EventEmitter, objectUrl, authToken, enableCaching) {
this.emiter = parentEmitter
constructor(parent: Viewer, objectUrl, authToken, enableCaching) {
this.emiter = parent
this._objectUrl = objectUrl
this.token = null
try {
@@ -64,7 +65,7 @@ export default class ViewerObjectLoader {
options: { enableCaching, customLogger: (Logger as any).log }
})
this.converter = new Converter(this.loader)
this.converter = new Converter(this.loader, parent.getWorldTree())
this.cancel = false
}
@@ -1,4 +1,4 @@
import { Material, Object3D, WebGLRenderer } from 'three'
import { Box3, Material, Object3D, WebGLRenderer } from 'three'
import { MaterialOptions } from '../materials/Materials'
import { NodeRenderView } from '../tree/NodeRenderView'
@@ -17,6 +17,8 @@ export interface Batch {
renderObject: Object3D
geometryType: GeometryType
get bounds(): Box3
getCount(): number
setBatchMaterial(material: Material): void
setVisibleRange(...range: BatchUpdateRange[])
+39 -76
View File
@@ -1,7 +1,6 @@
import { generateUUID } from 'three/src/math/MathUtils'
import MeshBatch from './MeshBatch'
import { SpeckleType } from '../converter/GeometryConverter'
import { WorldTree } from '../tree/WorldTree'
import LineBatch from './LineBatch'
import Materials from '../materials/Materials'
import { NodeRenderView } from '../tree/NodeRenderView'
@@ -18,6 +17,7 @@ import { Material, Mesh, WebGLRenderer } from 'three'
import { FilterMaterial, FilterMaterialType } from '../filtering/FilteringManager'
import Logger from 'js-logger'
import { World } from '../World'
import { RenderTree } from '../tree/RenderTree'
import SpeckleMesh from '../objects/SpeckleMesh'
export enum TransformStorage {
@@ -44,17 +44,15 @@ export default class Batcher {
}
public makeBatches(
subtreeId: string,
renderTree: RenderTree,
speckleType: SpeckleType[],
batchType?: GeometryType
) {
const renderViews = WorldTree.getRenderTree(subtreeId)
.getAtomicRenderViews(...speckleType)
.sort((a, b) => {
if (a.renderMaterialHash === 0) return -1
if (b.renderMaterialHash === 0) return 1
return a.renderMaterialHash - b.renderMaterialHash
})
const renderViews = renderTree.getAtomicRenderViews(...speckleType).sort((a, b) => {
if (a.renderMaterialHash === 0) return -1
if (b.renderMaterialHash === 0) return 1
return a.renderMaterialHash - b.renderMaterialHash
})
const materialHashes = [
...Array.from(new Set(renderViews.map((value) => value.renderMaterialHash)))
]
@@ -78,7 +76,7 @@ export default class Batcher {
for (let k = 0; k < batches.length; k++) {
const restrictedRvs = batches[k]
const batch = this.buildBatch(
subtreeId,
renderTree,
restrictedRvs,
materialHashes[i],
batchType
@@ -89,20 +87,18 @@ export default class Batcher {
}
public async *makeBatchesAsync(
subtreeId: string,
renderTree: RenderTree,
speckleType: SpeckleType[],
batchType?: GeometryType,
priority?: number
) {
const pause = World.getPause(priority)
const renderViews = WorldTree.getRenderTree(subtreeId)
.getAtomicRenderViews(...speckleType)
.sort((a, b) => {
if (a.renderMaterialHash === 0) return -1
if (b.renderMaterialHash === 0) return 1
return a.renderMaterialHash - b.renderMaterialHash
})
const renderViews = renderTree.getAtomicRenderViews(...speckleType).sort((a, b) => {
if (a.renderMaterialHash === 0) return -1
if (b.renderMaterialHash === 0) return 1
return a.renderMaterialHash - b.renderMaterialHash
})
const materialHashes = [
...Array.from(new Set(renderViews.map((value) => value.renderMaterialHash)))
]
@@ -129,7 +125,7 @@ export default class Batcher {
for (let k = 0; k < batches.length; k++) {
const restrictedRvs = batches[k]
const batch = this.buildBatch(
subtreeId,
renderTree,
restrictedRvs,
materialHashes[i],
batchType
@@ -203,13 +199,27 @@ export default class Batcher {
}
private buildBatch(
subtreeId: string,
renderTree: RenderTree,
renderViews: NodeRenderView[],
materialHash: number,
batchType?: GeometryType
): Batch {
const geometryType =
batchType !== undefined ? batchType : renderViews[0].geometryType
let batch = renderViews.filter((value) => value.renderMaterialHash === materialHash)
/** Prune any meshes with no geometry data */
batch = batch.filter((value) => value.validGeometry)
if (!batch.length) {
/** This is for the case when all renderviews have invalid geometries, and it generally
* means there is something wrong with the stream
*/
Logger.warn(
'All renderviews have invalid geometries. Skipping batch!',
renderViews
)
return null
}
const geometryType = batchType !== undefined ? batchType : batch[0].geometryType
let matRef = null
if (geometryType === GeometryType.MESH) {
@@ -230,21 +240,21 @@ export default class Batcher {
case GeometryType.MESH:
geometryBatch = new MeshBatch(
batchID,
subtreeId,
renderViews,
renderTree.id,
batch,
this.floatTextures
? TransformStorage.VERTEX_TEXTURE
: TransformStorage.UNIFORM_ARRAY
)
break
case GeometryType.LINE:
geometryBatch = new LineBatch(batchID, subtreeId, renderViews)
geometryBatch = new LineBatch(batchID, renderTree.id, batch)
break
case GeometryType.POINT:
geometryBatch = new PointBatch(batchID, subtreeId, renderViews)
geometryBatch = new PointBatch(batchID, renderTree.id, batch)
break
case GeometryType.POINT_CLOUD:
geometryBatch = new PointBatch(batchID, subtreeId, renderViews)
geometryBatch = new PointBatch(batchID, renderTree.id, batch)
break
}
@@ -469,56 +479,9 @@ export default class Batcher {
/**
* Used for debuggin only
*/
public isolateRenderView(id: string) {
const rvs = WorldTree.getRenderTree().getRenderViewsForNodeId(id)
const batchIds = [...Array.from(new Set(rvs.map((value) => value.batchId)))]
for (const k in this.batches) {
if (!batchIds.includes(k)) {
this.batches[k].setDrawRanges({
offset: 0,
count: Infinity,
material: this.materials.getFilterMaterial(
this.batches[k].renderViews[0],
FilterMaterialType.GHOST
)
})
this.batches[k].setVisibleRange(HideAllBatchUpdateRange)
} else {
const drawRanges = []
for (let i = 0; i < this.batches[k].renderViews.length; i++) {
if (!rvs.includes(this.batches[k].renderViews[i])) {
drawRanges.push({
offset: this.batches[k].renderViews[i].batchStart,
count: this.batches[k].renderViews[i].batchCount,
material: this.materials.getFilterMaterial(
this.batches[k].renderViews[i],
FilterMaterialType.GHOST
)
})
} else {
drawRanges.push({
offset: this.batches[k].renderViews[i].batchStart,
count: this.batches[k].renderViews[i].batchCount,
material: this.materials.getFilterMaterial(
this.batches[k].renderViews[i],
FilterMaterialType.SELECT
)
})
}
}
if (drawRanges.length > 0) {
this.batches[k].setDrawRanges(...drawRanges)
this.batches[k].autoFillDrawRanges()
}
}
}
}
/**
* Used for debuggin only
*/
public async isolateRenderViewBatch(id: string) {
const rv = WorldTree.getRenderTree().getRenderViewForNodeId(id)
public async isolateRenderViewBatch(id: string, renderTree: RenderTree) {
const rv = renderTree.getRenderViewForNodeId(id)
for (const k in this.batches) {
if (k !== rv.batchId) {
this.batches[k].setDrawRanges({
@@ -15,7 +15,6 @@ import { Geometry } from '../converter/Geometry'
import SpeckleLineMaterial from '../materials/SpeckleLineMaterial'
import { ObjectLayers } from '../SpeckleRenderer'
import { NodeRenderView } from '../tree/NodeRenderView'
import { Viewer } from '../Viewer'
import {
AllBatchUpdateRange,
Batch,
@@ -34,6 +33,11 @@ export default class LineBatch implements Batch {
public colorBuffer: InstancedInterleavedBuffer
private static readonly vector4Buffer: Vector4 = new Vector4()
public get bounds() {
if (!this.geometry.boundingBox) this.geometry.computeBoundingBox()
return this.geometry.boundingBox
}
public constructor(id: string, subtreeId: string, renderViews: NodeRenderView[]) {
this.id = id
this.subtreeId = subtreeId
@@ -221,7 +225,6 @@ export default class LineBatch implements Batch {
private makeLineGeometry(position: Float64Array) {
this.geometry = this.makeLineGeometryTriangle(new Float32Array(position))
Geometry.updateRTEGeometry(this.geometry, position)
Viewer.World.expandWorld(this.geometry.boundingBox)
}
private makeLineGeometryTriangle(position: Float32Array): LineSegmentsGeometry {
@@ -1,4 +1,5 @@
import {
Box3,
BufferAttribute,
BufferGeometry,
DynamicDrawUsage,
@@ -13,7 +14,6 @@ import { Geometry } from '../converter/Geometry'
import SpeckleStandardColoredMaterial from '../materials/SpeckleStandardColoredMaterial'
import SpeckleMesh from '../objects/SpeckleMesh'
import { NodeRenderView } from '../tree/NodeRenderView'
import { Viewer } from '../Viewer'
import {
AllBatchUpdateRange,
Batch,
@@ -41,6 +41,10 @@ export default class MeshBatch implements Batch {
private indexBuffer1: BufferAttribute
private indexBufferIndex = 0
public get bounds(): Box3 {
return this.mesh.BVH.getBoundingBox(new Box3())
}
public constructor(
id: string,
subtreeId: string,
@@ -548,8 +552,6 @@ export default class MeshBatch implements Batch {
this.geometry.computeBoundingSphere()
this.geometry.computeBoundingBox()
Viewer.World.expandWorld(this.geometry.boundingBox)
Geometry.updateRTEGeometry(this.geometry, position)
return this.geometry
@@ -8,7 +8,6 @@ import {
} from 'three'
import { Geometry } from '../converter/Geometry'
import { NodeRenderView } from '../tree/NodeRenderView'
import { Viewer } from '../Viewer'
import {
AllBatchUpdateRange,
Batch,
@@ -28,6 +27,11 @@ export default class PointBatch implements Batch {
public batchMaterial: Material
public mesh: Points
public get bounds() {
if (!this.geometry.boundingBox) this.geometry.computeBoundingBox()
return this.geometry.boundingBox
}
public constructor(id: string, subtreeId: string, renderViews: NodeRenderView[]) {
this.id = id
this.subtreeId = subtreeId
@@ -321,7 +325,6 @@ export default class PointBatch implements Batch {
this.geometry.computeBoundingSphere()
this.geometry.computeBoundingBox()
Viewer.World.expandWorld(this.geometry.boundingBox)
Geometry.updateRTEGeometry(this.geometry, position)
return this.geometry
@@ -16,7 +16,7 @@ export default class Coverter {
private activePromises: number
private maxChildrenPromises: number
private spoofIDs = true
private isRoot = true
private tree: WorldTree
private readonly NodeConverterMapping: {
[name: string]: ConverterNodeDelegate
@@ -38,7 +38,7 @@ export default class Coverter {
RevitInstance: this.RevitInstanceToNode.bind(this)
}
constructor(objectLoader: unknown) {
constructor(objectLoader: unknown, tree: WorldTree) {
if (!objectLoader) {
Logger.warn(
'Converter initialized without a corresponding object loader. Any objects that include references will throw errors.'
@@ -46,11 +46,10 @@ export default class Coverter {
}
this.objectLoader = objectLoader
this.lastAsyncPause = Date.now()
this.activePromises = 0
this.maxChildrenPromises = 200
WorldTree.getInstance()
this.tree = tree
}
public async asyncPause() {
@@ -99,20 +98,18 @@ export default class Coverter {
return
}
const childNode: TreeNode = WorldTree.getInstance().parse({
const childNode: TreeNode = this.tree.parse({
id: !node ? objectURL : this.getNodeId(obj),
raw: Object.assign({}, obj),
atomic: true,
root: this.isRoot,
children: []
})
this.isRoot = false
if (node === null) {
WorldTree.getInstance().addSubtree(childNode)
this.tree.addSubtree(childNode)
// console.warn(`Added root node with id ${obj.id}`)
} else {
WorldTree.getInstance().addNode(childNode, node)
this.tree.addNode(childNode, node)
// console.warn(`Added child node with id ${obj.id} to parent node ${node.model.id}`)
}
@@ -148,14 +145,14 @@ export default class Coverter {
displayValue = await this.resolveReference(displayValue)
if (!displayValue.units) displayValue.units = obj.units
try {
const nestedNode: TreeNode = WorldTree.getInstance().parse({
const nestedNode: TreeNode = this.tree.parse({
id: this.getNodeId(displayValue),
raw: Object.assign({}, displayValue),
atomic: false,
children: []
})
await this.convertToNode(displayValue, nestedNode)
WorldTree.getInstance().addNode(nestedNode, childNode)
this.tree.addNode(nestedNode, childNode)
await callback({}) // use the parent's metadata!
} catch (e) {
Logger.warn(
@@ -166,14 +163,14 @@ export default class Coverter {
for (const element of displayValue) {
const val = await this.resolveReference(element)
if (!val.units) val.units = obj.units
const nestedNode: TreeNode = WorldTree.getInstance().parse({
const nestedNode: TreeNode = this.tree.parse({
id: this.getNodeId(val),
raw: Object.assign({}, val),
atomic: false,
children: []
})
await this.convertToNode(val, nestedNode)
WorldTree.getInstance().addNode(nestedNode, childNode)
this.tree.addNode(nestedNode, childNode)
await callback({})
}
}
@@ -309,13 +306,13 @@ export default class Coverter {
node.model.raw.definition = definition
for (const def of definition.geometry) {
const ref = await this.resolveReference(def)
const childNode: TreeNode = WorldTree.getInstance().parse({
const childNode: TreeNode = this.tree.parse({
id: this.getNodeId(ref),
raw: Object.assign({}, ref),
atomic: true,
children: []
})
WorldTree.getInstance().addNode(childNode, node)
this.tree.addNode(childNode, node)
// console.warn(
// `Added child node with id ${childNode.model.id} to parent node ${node.model.id}`
// )
@@ -329,7 +326,7 @@ export default class Coverter {
if (!list) return
for (const def of list) {
const ref = await this.resolveReference(def)
const childNode: TreeNode = WorldTree.getInstance().parse({
const childNode: TreeNode = this.tree.parse({
id: this.getNodeId(ref),
raw: Object.assign({}, ref),
atomic: true,
@@ -338,7 +335,7 @@ export default class Coverter {
if (hostId) {
childNode.model.raw.host = hostId
}
WorldTree.getInstance().addNode(childNode, node)
this.tree.addNode(childNode, node)
await this.convertToNode(ref, childNode)
}
}
@@ -362,14 +359,14 @@ export default class Coverter {
let displayValue = obj.displayValue || obj.displayMesh
if (Array.isArray(displayValue)) displayValue = displayValue[0] //Just take the first display value for now (not ideal)
const ref = await this.resolveReference(displayValue)
const nestedNode: TreeNode = WorldTree.getInstance().parse({
const nestedNode: TreeNode = this.tree.parse({
id: this.getNodeId(ref),
raw: Object.assign({}, ref),
atomic: false,
children: []
})
await this.convertToNode(ref, nestedNode)
WorldTree.getInstance().addNode(nestedNode, node)
this.tree.addNode(nestedNode, node)
// deletes known unneeded fields
delete obj.Edges
@@ -433,7 +430,7 @@ export default class Coverter {
element = await this.resolveReference(element)
}
}
const nestedNode: TreeNode = WorldTree.getInstance().parse({
const nestedNode: TreeNode = this.tree.parse({
id: this.getNodeId(element),
raw: Object.assign({}, element),
atomic: false,
@@ -442,7 +439,7 @@ export default class Coverter {
await this.convertToNode(element, nestedNode)
/** We're not adding the segments as children since they shouldn't exist as individual line elements */
node.model.nestedNodes.push(nestedNode)
// WorldTree.getInstance().addNode(nestedNode, node)
// WorldTree.getInstance(this.treeInstaceId).addNode(nestedNode, node)
}
}
@@ -455,14 +452,14 @@ export default class Coverter {
}
const displayValue = await this.resolveReference(obj.displayValue)
displayValue.units = displayValue.units || obj.units
const nestedNode: TreeNode = WorldTree.getInstance().parse({
const nestedNode: TreeNode = this.tree.parse({
id: this.getNodeId(displayValue),
raw: Object.assign({}, displayValue),
atomic: false,
children: []
})
await this.convertToNode(displayValue, nestedNode)
WorldTree.getInstance().addNode(nestedNode, node)
this.tree.addNode(nestedNode, node)
}
private async CircleToNode(obj, node) {
@@ -9,6 +9,7 @@ import {
NumericPropertyInfo
} from './PropertyManager'
import SpeckleRenderer from '../SpeckleRenderer'
import { RenderTree } from '../tree/RenderTree'
export type FilteringState = {
selectedObjects?: string[]
@@ -39,6 +40,7 @@ export interface FilterMaterial {
export class FilteringManager {
public WTI: WorldTree
public RTI: RenderTree
private Renderer: SpeckleRenderer
private StateKey: string = null
@@ -50,8 +52,9 @@ export class FilteringManager {
private UserspaceColorState = new UserspaceColorState()
private ColorStringFilterState2: ColorStringFilterState = null
public constructor(renderer: SpeckleRenderer) {
this.WTI = WorldTree.getInstance()
public constructor(renderer: SpeckleRenderer, tree: WorldTree) {
this.WTI = tree
this.RTI = tree.getRenderTree()
this.Renderer = renderer
}
@@ -173,16 +176,14 @@ export class FilteringManager {
private visibilityWalk(node: TreeNode): boolean {
if (!node.model.atomic) return true
if (this.VisibilityState.ids.indexOf(node.model.raw.id) !== -1) {
this.VisibilityState.rvs.push(
...WorldTree.getRenderTree().getRenderViewsForNode(node, node)
)
this.VisibilityState.rvs.push(...this.RTI.getRenderViewsForNode(node, node))
}
return true
}
private isolationWalk(node: TreeNode): boolean {
if (!node.model.atomic || node.model.id === 'MOTHERSHIP') return true
const rvs = WorldTree.getRenderTree().getRenderViewsForNode(node, node)
if (!node.model.atomic || this.WTI.isRoot(node)) return true
const rvs = this.RTI.getRenderViewsForNode(node, node)
if (this.VisibilityState.ids.indexOf(node.model.raw.id) === -1) {
this.VisibilityState.rvs.push(...rvs)
} else {
@@ -223,10 +224,9 @@ export class FilteringManager {
const nonMatchingRvs: NodeRenderView[] = []
const colorGroups: ValueGroupColorItemNumericProps[] = []
WorldTree.getInstance().walk((node: TreeNode) => {
if (!node.model.atomic || node.model.id === 'MOTHERSHIP' || node.model.root)
return true
const rvs = WorldTree.getRenderTree().getRenderViewsForNode(node, node)
this.WTI.walk((node: TreeNode) => {
if (!node.model.atomic || this.WTI.isRoot(node)) return true
const rvs = this.RTI.getRenderViewsForNode(node, node)
const idx = matchingIds.indexOf(node.model.raw.id)
if (idx === -1) {
nonMatchingRvs.push(...rvs)
@@ -267,11 +267,11 @@ export class FilteringManager {
// windows (family instances) inside walls get the same color as the walls, even though
// they are identified as a different category.
this.WTI.walk((node: TreeNode) => {
if (!node.model.atomic || node.model.id === 'MOTHERSHIP') {
if (!node.model.atomic || this.WTI.isRoot(node)) {
return true
}
const vg = valueGroupColors.find((v) => v.ids.indexOf(node.model.raw.id) !== -1)
const rvs = WorldTree.getRenderTree().getRenderViewsForNode(node, node)
const rvs = this.RTI.getRenderViewsForNode(node, node)
if (!vg) {
nonMatchingRvs.push(...rvs)
return true
@@ -316,14 +316,14 @@ export class FilteringManager {
return { ...g, nodes: [], rvs: [] }
})
WorldTree.getInstance().walk((node: TreeNode) => {
this.WTI.walk((node: TreeNode) => {
if (!node.model?.raw?.id) return true
for (const group of localGroups) {
if (group.objectIds.includes(node.model.raw.id)) {
group.nodes.push(node)
const rvsNodes = WorldTree.getRenderTree()
.getRenderViewNodesForNode(node, node)
.map((rvNode) => rvNode.model.renderView)
const rvsNodes = this.RTI.getRenderViewNodesForNode(node, node).map(
(rvNode) => rvNode.model.renderView
)
if (rvsNodes) group.rvs.push(...rvsNodes)
}
}
@@ -354,7 +354,7 @@ export class FilteringManager {
const nodes = []
if (ids.length !== 0) {
/** This walk still takes longer than we'd like */
WorldTree.getInstance().walk((node: TreeNode) => {
this.WTI.walk((node: TreeNode) => {
if (ids.indexOf(node.model.raw.id) !== -1) {
nodes.push(node)
}
@@ -364,10 +364,7 @@ export class FilteringManager {
/** There's also quite a lot of redundancy here as well. The nodes coming are
* hierarchical and we end up getting the same render views more than once.
*/
const rvs = WorldTree.getRenderTree().getRenderViewNodesForNode(
nodes[k],
nodes[k]
)
const rvs = this.RTI.getRenderViewNodesForNode(nodes[k], nodes[k])
if (rvs) {
state.rvs.push(...rvs.map((e) => e.model.renderView))
state.ids.push(...rvs.map((e) => e.model.raw.id))
@@ -2,9 +2,7 @@ import flatten from '../../helpers/flatten'
import { TreeNode, WorldTree } from '../tree/WorldTree'
export class PropertyManager {
private static WT: WorldTree = WorldTree.getInstance()
private static propCache = {} as Record<string, PropertyInfo[]>
private propCache = {} as Record<string, PropertyInfo[]>
/**
*
@@ -12,11 +10,12 @@ export class PropertyManager {
* @param bypassCache Forces a full rescan if set to true.
* @returns a list of property infos containing basic information for filtering purposes.
*/
public static getProperties(
public getProperties(
tree: WorldTree,
resourceUrl: string = null,
bypassCache = false
): PropertyInfo[] {
let rootNode: TreeNode = PropertyManager.WT.root
let rootNode: TreeNode = tree.root
if (!bypassCache && this.propCache[resourceUrl ? resourceUrl : rootNode.model.id])
return this.propCache[resourceUrl ? resourceUrl : rootNode.model.id]
@@ -31,7 +30,7 @@ export class PropertyManager {
const propValues = {}
PropertyManager.WT.walk((node: TreeNode) => {
tree.walk((node: TreeNode) => {
if (!node.model.atomic) return true
const obj = flatten(node.model.raw)
for (const key in obj) {
+5 -5
View File
@@ -16,7 +16,7 @@ class DataTreeInternal implements DataTree {
public constructor() {
this.tree = new TreeModel()
this.root = this.tree.parse({ guid: 'MOTHERSHIP' })
this.root = this.tree.parse({ guid: WorldTree.ROOT_ID })
}
public findAll(predicate: ObjectPredicate): SpeckleObject[] {
return this.root
@@ -43,10 +43,10 @@ class DataTreeInternal implements DataTree {
}
export class DataTreeBuilder {
public static build(root: TreeNode): DataTree {
public static build(tree: WorldTree): DataTree {
const dataTree = new DataTreeInternal()
let parent = null
WorldTree.getInstance().walk((node: TreeNode) => {
tree.root.walk((node: TreeNode) => {
if (!node.parent) {
parent = dataTree.root
return true
@@ -56,7 +56,7 @@ export class DataTreeBuilder {
return localNode.model.guid === node.parent.model.id
})
const _node: TreeNode = WorldTree.getInstance().parse({
const _node: TreeNode = tree.parse({
guid: node.model.id,
data: node.model.raw,
atomic: node.model.atomic,
@@ -65,7 +65,7 @@ export class DataTreeBuilder {
parent.addChild(_node)
return true
}, root)
}, tree.root)
return dataTree as DataTree
}
}
+19 -15
View File
@@ -7,6 +7,7 @@ import { Geometry } from '../converter/Geometry'
import Logger from 'js-logger'
export class RenderTree {
private tree: WorldTree
private root: TreeNode
private _treeBounds: Box3 = new Box3()
private cancel = false
@@ -15,12 +16,17 @@ export class RenderTree {
return this._treeBounds
}
public constructor(root: TreeNode) {
this.root = root
public get id(): string {
return this.root.model.id
}
public constructor(tree: WorldTree, subtreeRoot: TreeNode) {
this.tree = tree
this.root = subtreeRoot
}
public buildRenderTree() {
this.root.walk((node: TreeNode): boolean => {
this.tree.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) {
@@ -42,7 +48,7 @@ export class RenderTree {
}
public buildRenderTreeAsync(priority: number): Promise<boolean> {
const p = WorldTree.getInstance().walkAsync(
const p = this.tree.walkAsync(
(node: TreeNode): boolean => {
const rendeNode = this.buildRenderNode(node)
node.model.renderView = rendeNode ? new NodeRenderView(rendeNode) : null
@@ -93,7 +99,7 @@ export class RenderTree {
if (node.model.raw.renderMaterial) {
return node
}
const ancestors = WorldTree.getInstance().getAncestors(node)
const ancestors = this.tree.getAncestors(node)
for (let k = 0; k < ancestors.length; k++) {
if (ancestors[k].model.raw.renderMaterial) {
return ancestors[k]
@@ -105,7 +111,7 @@ export class RenderTree {
if (node.model.raw.displayStyle) {
return node
}
const ancestors = WorldTree.getInstance().getAncestors(node)
const ancestors = this.tree.getAncestors(node)
for (let k = 0; k < ancestors.length; k++) {
if (ancestors[k].model.raw.displayStyle) {
return ancestors[k]
@@ -115,7 +121,7 @@ export class RenderTree {
public computeTransform(node: TreeNode): Matrix4 {
const transform = new Matrix4()
const ancestors = WorldTree.getInstance().getAncestors(node)
const ancestors = this.tree.getAncestors(node)
for (let k = 0; k < ancestors.length; k++) {
if (ancestors[k].model.renderView) {
const renderNode: NodeRenderData = ancestors[k].model.renderView.renderData
@@ -177,13 +183,11 @@ export class RenderTree {
if (node.model.atomic) {
return node.model.renderView
}
return WorldTree.getInstance()
.getAncestors(node)
.find((node) => node.model.atomic)
return this.tree.getAncestors(node).find((node) => node.model.atomic)
}
public getRenderViewsForNodeId(id: string): NodeRenderView[] {
const node = WorldTree.getInstance().findId(id)
const node = this.tree.findId(id)
if (!node) {
Logger.warn(`Id ${id} does not exist`)
return null
@@ -192,7 +196,7 @@ export class RenderTree {
}
public getRenderViewForNodeId(id: string): NodeRenderView {
const node = WorldTree.getInstance().findId(id)
const node = this.tree.findId(id)
if (!node) {
Logger.warn(`Id ${id} does not exist`)
return null
@@ -201,12 +205,12 @@ export class RenderTree {
}
public purge() {
this.root = null
this.tree = null
}
public cancelBuild(id: string) {
public cancelBuild(subtreeId: string) {
this.cancel = true
WorldTree.getInstance().purge(id)
this.tree.purge(subtreeId)
this.purge()
}
}
+28 -38
View File
@@ -15,56 +15,42 @@ export interface NodeData {
children: TreeNode[]
nestedNodes: TreeNode[]
atomic: boolean
/**
* Keeps track wether this the root commit object or not.
* TODO: Ask Alex wether this is somehow avoidable.
*/
root: boolean
renderView?: NodeRenderView
}
export class WorldTree {
private static instance: WorldTree
private static renderTreeInstances: { [id: string]: RenderTree } = {}
private renderTreeInstances: { [id: string]: RenderTree } = {}
private readonly supressWarnings = true
public static readonly ROOT_ID = 'ROOT'
private constructor() {
public constructor() {
this.tree = new TreeModel()
this._root = this.parse({
id: WorldTree.ROOT_ID,
raw: {},
atomic: true,
children: [],
renderView: null
})
}
public static getInstance(): WorldTree {
if (!WorldTree.instance) {
WorldTree.instance = new WorldTree()
WorldTree.instance._root = WorldTree.getInstance().parse({
id: 'MOTHERSHIP',
raw: {},
atomic: true,
children: [],
renderView: null
})
}
return WorldTree.instance
}
public static getRenderTree(subtreeId?: string): RenderTree {
if (!WorldTree.getInstance()._root) {
public getRenderTree(subtreeId?: string): RenderTree {
if (!this._root) {
console.error(`WorldTree not initialised`)
return null
}
const id = subtreeId ? subtreeId : WorldTree.getInstance().root.model.id
if (!WorldTree.renderTreeInstances[id]) {
WorldTree.renderTreeInstances[id] = new RenderTree(
WorldTree.getInstance().findId(id)
)
const id = subtreeId ? subtreeId : this.root.model.id
if (!this.renderTreeInstances[id]) {
this.renderTreeInstances[id] = new RenderTree(this, this.findId(id))
}
return WorldTree.renderTreeInstances[id]
return this.renderTreeInstances[id]
}
public static getDataTree(): DataTree {
return DataTreeBuilder.build(WorldTree.instance._root)
public getDataTree(): DataTree {
return DataTreeBuilder.build(this)
}
private tree: TreeModel
@@ -74,6 +60,10 @@ export class WorldTree {
return this._root
}
public isRoot(node: TreeNode) {
return node === this._root
}
public parse(model) {
return this.tree.parse(model)
}
@@ -155,19 +145,19 @@ export class WorldTree {
public purge(subtreeId?: string) {
if (subtreeId) {
delete WorldTree.renderTreeInstances[subtreeId]
delete this.renderTreeInstances[subtreeId]
this.removeNode(this.findId(subtreeId))
return
}
Object.keys(WorldTree.renderTreeInstances).forEach(
(key) => delete WorldTree.renderTreeInstances[key]
Object.keys(this.renderTreeInstances).forEach(
(key) => delete this.renderTreeInstances[key]
)
this._root.drop()
this._root.children.length = 0
this.tree = new TreeModel()
this._root = WorldTree.getInstance().parse({
id: 'MOTHERSHIP',
this._root = this.tree.parse({
id: WorldTree.ROOT_ID,
raw: {},
atomic: true,
children: []