6fc7c06e9c
* Quick hack to demo how an offline loader would work with as little complication as possible * Further simplified yielding objects in offline mode * Commented out the URL thing * Implemented SpeckleOfflineLoader. JSON parsing is implemented at object-loader level, completely isolated from the rest of the implementation in order to avoid regression * Isolated ObjectLoader creation in base SpeckleLoader class so any extended classes can overwrite the way the object loader is created and used * Removed the big json sample file * Updated version * Removed unused functions from objectloader * Restored viewer package version * Fixed typo * Renamed and moved the sample offline Speckle JSON * Replaced the default JSON object sample with a much smaller one since we don't want to increase the sandbox's build size by 10 megs * Fixed a linting error
1309 lines
40 KiB
TypeScript
1309 lines
40 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
import {
|
|
ArcticViewPipeline,
|
|
ClearFlags,
|
|
DefaultLightConfiguration,
|
|
DefaultPipeline,
|
|
InputType,
|
|
MRTEdgesPipeline,
|
|
MRTPenViewPipeline,
|
|
MRTShadedViewPipeline,
|
|
NormalsPass,
|
|
ObjectLayers,
|
|
OutputPass,
|
|
Pipeline,
|
|
SectionTool,
|
|
SpeckleOfflineLoader,
|
|
SpeckleRenderer,
|
|
SpeckleStandardMaterial,
|
|
TAAPipeline,
|
|
TreeNode
|
|
} from '@speckle/viewer'
|
|
import {
|
|
CanonicalView,
|
|
Viewer,
|
|
PropertyInfo,
|
|
SelectionEvent,
|
|
SunLightConfiguration,
|
|
ViewerEvent,
|
|
BatchObject,
|
|
VisualDiffMode,
|
|
MeasurementType,
|
|
ExplodeExtension,
|
|
DiffExtension,
|
|
SpeckleLoader,
|
|
ObjLoader,
|
|
UrlHelper,
|
|
LoaderEvent,
|
|
UpdateFlags
|
|
} from '@speckle/viewer'
|
|
import { FolderApi, Pane } from 'tweakpane'
|
|
import { DiffResult } from '@speckle/viewer'
|
|
import { Units } from '@speckle/viewer'
|
|
import { SelectionExtension } from '@speckle/viewer'
|
|
import { FilteringExtension } from '@speckle/viewer'
|
|
import { MeasurementsExtension } from '@speckle/viewer'
|
|
import { CameraController } from '@speckle/viewer'
|
|
import { AssetType, Assets } from '@speckle/viewer'
|
|
import Neutral from '../assets/hdri/Neutral.png'
|
|
import Mild from '../assets/hdri/Mild.png'
|
|
import Mild2 from '../assets/hdri/Mild2.png'
|
|
import Sharp from '../assets/hdri/Sharp.png'
|
|
import Bright from '../assets/hdri/Bright.png'
|
|
|
|
import { Euler, Vector3, Box3, Color, LinearFilter } from 'three'
|
|
import { GeometryType } from '@speckle/viewer'
|
|
import { MeshBatch } from '@speckle/viewer'
|
|
|
|
export default class Sandbox {
|
|
private viewer: Viewer
|
|
private pane: Pane
|
|
private tabs
|
|
private viewsFolder!: FolderApi
|
|
private streams: { [url: string]: Array<unknown> } = {}
|
|
private properties: PropertyInfo[]
|
|
private selectionList: SelectionEvent[]
|
|
private objectControls: FolderApi | null = null
|
|
private batchesFolder: FolderApi | null = null
|
|
public ids: Array<string> = []
|
|
|
|
public urlParams = {
|
|
url: 'https://latest.speckle.systems/streams/c43ac05d04/commits/ec724cfbeb'
|
|
}
|
|
|
|
public sceneParams = {
|
|
worldSize: { x: 0, y: 0, z: 0 },
|
|
worldOrigin: { x: 0, y: 0, z: 0 },
|
|
pixelThreshold: 0.5,
|
|
exposure: 0.5,
|
|
tonemapping: 4, //'ACESFilmicToneMapping',
|
|
contrast: 1,
|
|
saturation: 1,
|
|
hdri: Mild,
|
|
minRoughness: 0.5
|
|
}
|
|
|
|
public pipelineParams = {
|
|
pipelineOutput: 8,
|
|
accumulationFrames: 16,
|
|
dynamicAoEnabled: false,
|
|
dynamicAoParams: {
|
|
intensity: 1.5,
|
|
scale: 0,
|
|
kernelRadius: 5,
|
|
bias: 0.2,
|
|
normalsType: 2,
|
|
blurEnabled: true,
|
|
blurRadius: 2,
|
|
blurStdDev: 4,
|
|
blurDepthCutoff: 0.007
|
|
},
|
|
staticAoEnabled: true,
|
|
staticAoParams: {
|
|
intensity: 1,
|
|
kernelRadius: 30, // Screen space
|
|
kernelSize: 16,
|
|
bias: 0.01,
|
|
minDistance: 0,
|
|
maxDistance: 0.008
|
|
}
|
|
}
|
|
|
|
public lightParams: SunLightConfiguration = {
|
|
enabled: true,
|
|
castShadow: true,
|
|
intensity: 5,
|
|
color: 0xffffff,
|
|
azimuth: 0.75,
|
|
elevation: 1.33,
|
|
radius: 0,
|
|
indirectLightIntensity: 1.2,
|
|
shadowcatcher: true
|
|
}
|
|
|
|
public batchesParams = {
|
|
showBvh: false,
|
|
totalBvhSize: 0,
|
|
explode: 0,
|
|
culling: true
|
|
}
|
|
|
|
public filterParams = {
|
|
filterBy: 'Volume'
|
|
}
|
|
|
|
public shadowCatcherParams = {
|
|
textureSize: 512,
|
|
weights: { x: 1, y: 1, z: 0, w: 1 },
|
|
blurRadius: 16,
|
|
stdDeviation: 4,
|
|
sigmoidRange: 1.1,
|
|
sigmoidStrength: 2
|
|
}
|
|
|
|
public measurementsParams = {
|
|
enabled: false,
|
|
visible: true,
|
|
type: MeasurementType.POINTTOPOINT,
|
|
vertexSnap: true,
|
|
units: 'm',
|
|
precision: 2
|
|
}
|
|
|
|
public constructor(
|
|
container: HTMLElement,
|
|
viewer: Viewer,
|
|
selectionList: SelectionEvent[]
|
|
) {
|
|
this.viewer = viewer
|
|
this.selectionList = selectionList
|
|
this.pane = new Pane({ title: 'Speckle Sandbox', expanded: true })
|
|
// Mad HTML/CSS skills
|
|
container.appendChild(this.pane['containerElem_'])
|
|
|
|
this.pane['containerElem_'].style = 'pointer-events:auto;'
|
|
|
|
this.tabs = this.pane.addTab({
|
|
pages: [
|
|
{ title: 'General' },
|
|
{ title: 'Scene' },
|
|
{ title: 'Filtering' },
|
|
{ title: 'Batches' },
|
|
{ title: 'Diff' },
|
|
{ title: 'Measurements' }
|
|
]
|
|
})
|
|
this.properties = []
|
|
|
|
viewer.on(ViewerEvent.LoadComplete, async (url: string) => {
|
|
url
|
|
this.viewer.setLightConfiguration(DefaultLightConfiguration)
|
|
this.addStreamControls(url)
|
|
this.addViewControls()
|
|
this.addBatches()
|
|
this.properties = await this.viewer.getObjectProperties()
|
|
this.batchesParams.totalBvhSize = this.getBVHSize()
|
|
this.refresh()
|
|
})
|
|
viewer.on(ViewerEvent.UnloadComplete, async () => {
|
|
this.removeViewControls()
|
|
this.addViewControls()
|
|
this.properties = await this.viewer.getObjectProperties()
|
|
})
|
|
viewer.on(ViewerEvent.UnloadAllComplete, async () => {
|
|
this.removeViewControls()
|
|
this.addViewControls()
|
|
this.properties = await this.viewer.getObjectProperties()
|
|
})
|
|
viewer.on(ViewerEvent.ObjectClicked, (selectionEvent: SelectionEvent | null) => {
|
|
if (selectionEvent && selectionEvent.hits) {
|
|
const firstHitNode = selectionEvent.hits[0].node
|
|
if (firstHitNode) {
|
|
this.addObjectControls(firstHitNode)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
public refresh() {
|
|
this.pane.refresh()
|
|
}
|
|
|
|
private addBatches() {
|
|
if (this.batchesFolder) this.batchesFolder.dispose()
|
|
|
|
this.batchesFolder = this.tabs.pages[3].addFolder({ title: 'Batches' })
|
|
const batchIds = this.viewer.getRenderer().getBatchIds()
|
|
for (let k = 0; k < batchIds.length; k++) {
|
|
const button = this.batchesFolder.addButton({
|
|
title: this.viewer.getRenderer().getBatchSize(batchIds[k]).toString()
|
|
})
|
|
button.on('click', () => {
|
|
this.viewer.getRenderer().isolateBatch(batchIds[k])
|
|
this.viewer.requestRender()
|
|
})
|
|
}
|
|
}
|
|
|
|
private addStreamControls(url: string) {
|
|
const folder = this.tabs.pages[0].addFolder({
|
|
title: `Object: ${url.split('/').reverse()[0]}`
|
|
})
|
|
|
|
folder.addInput({ url }, 'url', {
|
|
title: 'URL',
|
|
disabled: true
|
|
})
|
|
const position = { value: { x: 0, y: 0, z: 0 } }
|
|
folder.addInput(position, 'value', { label: 'Position' }).on('change', () => {
|
|
const tree = this.viewer.getWorldTree()
|
|
const rvs = tree.getRenderTree(url)?.getRenderViewsForNodeId(url)
|
|
if (rvs) {
|
|
for (let k = 0; k < rvs.length; k++) {
|
|
const object = this.viewer.getRenderer().getObject(rvs[k])
|
|
if (object)
|
|
object.transformTRS(position.value, undefined, undefined, undefined)
|
|
}
|
|
}
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
this.viewer.getRenderer().updateShadowCatcher()
|
|
})
|
|
|
|
folder
|
|
.addButton({
|
|
title: 'Unload'
|
|
})
|
|
.on('click', () => {
|
|
this.removeStreamControls(url)
|
|
})
|
|
this.streams[url] = []
|
|
this.streams[url].push(folder)
|
|
}
|
|
|
|
private removeStreamControls(url: string) {
|
|
void this.viewer.unloadObject(url)
|
|
;(this.streams[url][0] as { dispose: () => void }).dispose()
|
|
delete this.streams[url]
|
|
}
|
|
|
|
private addViewControls() {
|
|
const views = this.viewer.getViews()
|
|
this.viewsFolder = this.tabs.pages[0].addFolder({
|
|
title: 'Views',
|
|
expanded: true
|
|
})
|
|
for (let k = 0; k < views.length; k++) {
|
|
const view = views[k]
|
|
this.viewsFolder
|
|
.addButton({
|
|
title: view.name ? view.name : 'Unnamed'
|
|
})
|
|
.on('click', () => {
|
|
this.viewer.getExtension(CameraController).setCameraView(views[k], true)
|
|
})
|
|
}
|
|
}
|
|
|
|
private removeViewControls() {
|
|
this.viewsFolder.dispose()
|
|
}
|
|
|
|
public addObjectControls(node: TreeNode) {
|
|
if (this.objectControls) {
|
|
this.objectControls.dispose()
|
|
}
|
|
this.objectControls = this.tabs.pages[0].addFolder({
|
|
title: `Object: ${node.model.id}`
|
|
})
|
|
|
|
const rvs = this.viewer.getWorldTree().getRenderTree().getRenderViewsForNode(node)
|
|
const objects: BatchObject[] = []
|
|
for (let k = 0; k < rvs.length; k++) {
|
|
const batchObject = this.viewer.getRenderer().getObject(rvs[k])
|
|
if (batchObject) {
|
|
objects.push(batchObject)
|
|
}
|
|
}
|
|
const position = { value: { x: 0, y: 0, z: 0 } }
|
|
const rotation = { value: { x: 0, y: 0, z: 0 } }
|
|
const scale = { value: { x: 1, y: 1, z: 1 } }
|
|
this.objectControls
|
|
.addInput(position, 'value', { label: 'Position' })
|
|
.on('change', () => {
|
|
// const unionBox: Box3 = new Box3()
|
|
// objects.forEach((obj: BatchObject) => {
|
|
// unionBox.union(obj.renderView.aabb)
|
|
// })
|
|
// const origin = unionBox.getCenter(new Vector3())
|
|
objects.forEach((obj: BatchObject) => {
|
|
obj.transformTRS(position.value)
|
|
// obj.position = new Vector3(
|
|
// position.value.x,
|
|
// position.value.y,
|
|
// position.value.z
|
|
// )
|
|
})
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
this.objectControls
|
|
.addInput(rotation, 'value', {
|
|
label: 'Rotation Euler',
|
|
x: { step: 0.1 },
|
|
y: { step: 0.1 },
|
|
z: { step: 0.1 }
|
|
})
|
|
.on('change', () => {
|
|
// const unionBox: Box3 = new Box3()
|
|
// objects.forEach((obj: BatchObject) => {
|
|
// unionBox.union(obj.renderView.aabb)
|
|
// })
|
|
// const origin = unionBox.getCenter(new Vector3())
|
|
objects.forEach((obj: BatchObject) => {
|
|
// obj.transformTRS(position.value, rotation.value, scale.value, origin)
|
|
obj.euler = new Euler(
|
|
rotation.value.x,
|
|
rotation.value.y,
|
|
rotation.value.z,
|
|
'XYZ'
|
|
)
|
|
})
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
this.objectControls
|
|
.addInput(scale, 'value', {
|
|
label: 'Scale',
|
|
x: { step: 0.1 },
|
|
y: { step: 0.1 },
|
|
z: { step: 0.1 }
|
|
})
|
|
.on('change', () => {
|
|
const unionBox: Box3 = new Box3()
|
|
objects.forEach((obj: BatchObject) => {
|
|
unionBox.union(obj.renderView.aabb || new Box3())
|
|
})
|
|
const origin = unionBox.getCenter(new Vector3())
|
|
objects.forEach((obj: BatchObject) => {
|
|
obj.transformTRS(position.value, rotation.value, scale.value, origin)
|
|
})
|
|
this.viewer.requestRender()
|
|
})
|
|
}
|
|
|
|
public makeGenericUI() {
|
|
this.tabs.pages[0].addInput(this.urlParams, 'url', {
|
|
title: 'url'
|
|
})
|
|
|
|
const loadButton = this.tabs.pages[0].addButton({
|
|
title: 'Load Url'
|
|
})
|
|
|
|
loadButton.on('click', () => {
|
|
void this.loadUrl(this.urlParams.url)
|
|
})
|
|
|
|
const loadObjButton = this.tabs.pages[0].addButton({
|
|
title: 'Load OBJ'
|
|
})
|
|
loadObjButton.on('click', async () => {
|
|
/** Load from string */
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.onchange = (e) => {
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
|
|
const file = e.target?.files[0] as Blob & { name: string }
|
|
|
|
const reader = new FileReader()
|
|
reader.readAsText(file, 'UTF-8')
|
|
|
|
reader.onload = async (readerEvent) => {
|
|
const content = readerEvent?.target?.result as string
|
|
const loader = new ObjLoader(this.viewer.getWorldTree(), file.name, content)
|
|
await this.viewer.loadObject(loader, true)
|
|
}
|
|
}
|
|
input.click()
|
|
/** Load as resource */
|
|
// import BrandenburgGate from '../assets/BrandenburgGate.png'
|
|
// const loader = new ObjLoader(this.viewer.getWorldTree(), brandnburd)
|
|
// await this.viewer.loadObject(loader, true)
|
|
})
|
|
|
|
const clearButton = this.tabs.pages[0].addButton({
|
|
title: 'Clear All'
|
|
})
|
|
|
|
clearButton.on('click', () => {
|
|
void this.viewer.unloadAll()
|
|
})
|
|
|
|
this.tabs.pages[0].addSeparator()
|
|
this.tabs.pages[0].addSeparator()
|
|
|
|
const toggleSectionBox = this.tabs.pages[0].addButton({
|
|
title: 'Toggle Section Box'
|
|
})
|
|
toggleSectionBox.on('click', () => {
|
|
let box = this.viewer.getRenderer().boxFromObjects(
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
this.selectionList.map((val) => val.hits[0].node.model.raw.id) as string[]
|
|
)
|
|
if (!box) {
|
|
box = this.viewer.getRenderer().sceneBox
|
|
}
|
|
this.viewer.getExtension(SectionTool).setBox(box)
|
|
this.viewer.getExtension(SectionTool).toggle()
|
|
})
|
|
|
|
const toggleSectionBoxVisibility = this.tabs.pages[0].addButton({
|
|
title: 'Toggle Section Box Visibility'
|
|
})
|
|
toggleSectionBoxVisibility.on('click', () => {
|
|
this.viewer.getExtension(SectionTool).visible =
|
|
!this.viewer.getExtension(SectionTool).visible
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
const toggleProjection = this.tabs.pages[0].addButton({
|
|
title: 'Toggle Projection'
|
|
})
|
|
toggleProjection.on('click', () => {
|
|
this.viewer.getExtension(CameraController).toggleCameras()
|
|
})
|
|
|
|
const zoomExtents = this.tabs.pages[0].addButton({
|
|
title: 'Zoom Extents'
|
|
})
|
|
zoomExtents.on('click', () => {
|
|
this.viewer.getExtension(CameraController).setCameraView(
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
this.selectionList.map((val) => val.hits[0].node.model.id) as string[],
|
|
true
|
|
)
|
|
})
|
|
|
|
this.tabs.pages[0].addSeparator()
|
|
this.tabs.pages[0].addSeparator()
|
|
|
|
const darkModeToggle = this.tabs.pages[0].addButton({
|
|
title: '🌞 / 🌛'
|
|
})
|
|
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', () => {
|
|
let dark = false
|
|
if (document.getElementById('renderer'))
|
|
dark =
|
|
document.getElementById('renderer')?.classList.toggle('background-dark') ||
|
|
false
|
|
else
|
|
dark =
|
|
document.getElementById('multi-root')?.classList.toggle('background-dark') ||
|
|
false
|
|
|
|
localStorage.setItem('dark', dark ? `dark` : `light`)
|
|
})
|
|
|
|
const screenshot = this.tabs.pages[0].addButton({
|
|
title: 'Screenshot'
|
|
})
|
|
screenshot.on('click', async () => {
|
|
// console.warn(await this.viewer.screenshot())
|
|
this.viewer
|
|
.getExtension(FilteringExtension)
|
|
.hideObjects(['1facfaaf1d3682707edd9ac20ef34e62'])
|
|
})
|
|
|
|
const rotate = this.tabs.pages[0].addButton({
|
|
title: '360'
|
|
})
|
|
rotate.on('click', async () => {
|
|
const waitForAnimation = async (ms = 70) =>
|
|
await new Promise((resolve) => {
|
|
setTimeout(resolve, ms)
|
|
})
|
|
for (let i = 0; i < 24; i++) {
|
|
this.viewer
|
|
.getExtension(CameraController)
|
|
.setCameraView({ azimuth: Math.PI / 12, polar: 0 }, false)
|
|
this.viewer.requestRender(UpdateFlags.RENDER_RESET)
|
|
await waitForAnimation(1000)
|
|
}
|
|
})
|
|
this.tabs.pages[0].addSeparator()
|
|
|
|
const pipeline = { output: 0 }
|
|
this.tabs.pages[0]
|
|
.addInput(pipeline, 'output', {
|
|
label: 'Pipeline',
|
|
options: {
|
|
DEFAULT: 0,
|
|
EDGED: 1,
|
|
SHADED: 2,
|
|
PEN: 3,
|
|
ARCTIC: 4,
|
|
TAA: 5,
|
|
DEBUG_NORMALS: 6
|
|
}
|
|
})
|
|
.on('change', (value) => {
|
|
switch (value.value) {
|
|
case 0:
|
|
this.viewer.getRenderer().pipeline = new DefaultPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 1:
|
|
this.viewer.getRenderer().pipeline = new MRTEdgesPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 2:
|
|
this.viewer.getRenderer().pipeline = new MRTShadedViewPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 3:
|
|
this.viewer.getRenderer().pipeline = new MRTPenViewPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 4:
|
|
this.viewer.getRenderer().pipeline = new ArcticViewPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 5:
|
|
this.viewer.getRenderer().pipeline = new TAAPipeline(
|
|
this.viewer.getRenderer()
|
|
)
|
|
break
|
|
case 6:
|
|
this.viewer.getRenderer().pipeline = new (class extends Pipeline {
|
|
constructor(speckleRenderer: SpeckleRenderer) {
|
|
super(speckleRenderer)
|
|
const normalPass = new NormalsPass()
|
|
normalPass.setLayers([ObjectLayers.STREAM_CONTENT_MESH])
|
|
normalPass.setClearColor(0x000000, 1)
|
|
normalPass.setClearFlags(ClearFlags.COLOR | ClearFlags.DEPTH)
|
|
normalPass.outputTarget = Pipeline.createRenderTarget({
|
|
minFilter: LinearFilter,
|
|
magFilter: LinearFilter
|
|
})
|
|
normalPass.outputTarget.samples = 4
|
|
|
|
const outputPass = new OutputPass()
|
|
outputPass.setTexture('tDiffuse', normalPass.outputTarget?.texture)
|
|
outputPass.options = { inputType: InputType.Normals }
|
|
|
|
this.passList.push(normalPass, outputPass)
|
|
}
|
|
})(this.viewer.getRenderer())
|
|
|
|
default:
|
|
break
|
|
}
|
|
this.viewer.requestRender(UpdateFlags.RENDER_RESET)
|
|
})
|
|
|
|
this.tabs.pages[0].addSeparator()
|
|
const colors = this.tabs.pages[0].addButton({
|
|
title: `PM's Colors`
|
|
})
|
|
colors.on('click', async () => {
|
|
const colorNodes = this.viewer.getWorldTree().findAll(
|
|
(node: TreeNode) =>
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
node.model.renderView &&
|
|
node.model.renderView.renderData.colorMaterial &&
|
|
node.model.renderView.geometryType === GeometryType.MESH
|
|
)
|
|
const colorMap: { [color: number]: Array<string> } = {}
|
|
for (let k = 0; k < colorNodes.length; k++) {
|
|
const node = colorNodes[k]
|
|
|
|
const color: number = node.model.renderView.renderData.colorMaterial.color
|
|
if (!colorMap[color]) colorMap[color] = []
|
|
|
|
colorMap[color].push(node.model.id)
|
|
}
|
|
const colorGroups = []
|
|
|
|
for (const color in colorMap) {
|
|
colorGroups.push({
|
|
objectIds: colorMap[color],
|
|
color: '#' + new Color(Number.parseInt(color)).getHexString()
|
|
})
|
|
}
|
|
console.log(colorGroups)
|
|
this.viewer.getExtension(FilteringExtension).setUserObjectColors(colorGroups)
|
|
})
|
|
|
|
this.tabs.pages[0]
|
|
.addInput({ dampening: 30 }, 'dampening', {
|
|
label: 'Dampening',
|
|
min: 0,
|
|
max: 300,
|
|
step: 10
|
|
})
|
|
.on('change', (value) => {
|
|
this.viewer.getExtension(CameraController).options = {
|
|
damperDecay: value.value
|
|
}
|
|
})
|
|
|
|
this.tabs.pages[0].addSeparator()
|
|
|
|
const canonicalViewsFolder = this.tabs.pages[0].addFolder({
|
|
title: 'Canonical Views',
|
|
expanded: false
|
|
})
|
|
const sides = ['front', 'back', 'top', 'bottom', 'right', 'left', '3d']
|
|
for (let k = 0; k < sides.length; k++) {
|
|
canonicalViewsFolder
|
|
.addButton({
|
|
title: sides[k]
|
|
})
|
|
.on('click', () => {
|
|
this.viewer
|
|
.getExtension(CameraController)
|
|
.setCameraView(sides[k] as CanonicalView, true)
|
|
})
|
|
}
|
|
}
|
|
|
|
makeSceneUI() {
|
|
const worldFolder = this.tabs.pages[1].addFolder({
|
|
title: 'World',
|
|
expanded: true
|
|
})
|
|
worldFolder.addInput(this.sceneParams.worldSize, 'x', {
|
|
disabled: true,
|
|
label: 'Size-x',
|
|
step: 0.00000001
|
|
})
|
|
worldFolder.addInput(this.sceneParams.worldSize, 'y', {
|
|
disabled: true,
|
|
label: 'Size-y',
|
|
step: 0.00000001
|
|
})
|
|
worldFolder.addInput(this.sceneParams.worldSize, 'z', {
|
|
disabled: true,
|
|
label: 'Size-z',
|
|
step: 0.00000001
|
|
})
|
|
worldFolder.addSeparator()
|
|
worldFolder.addInput(this.sceneParams.worldOrigin, 'x', {
|
|
disabled: true,
|
|
label: 'Origin-x'
|
|
})
|
|
worldFolder.addInput(this.sceneParams.worldOrigin, 'y', {
|
|
disabled: true,
|
|
label: 'Origin-y'
|
|
})
|
|
worldFolder.addInput(this.sceneParams.worldOrigin, 'z', {
|
|
disabled: true,
|
|
label: 'Origin-z'
|
|
})
|
|
|
|
this.tabs.pages[1].addSeparator()
|
|
const postFolder = this.tabs.pages[1].addFolder({
|
|
title: 'Post',
|
|
expanded: true
|
|
})
|
|
|
|
postFolder
|
|
.addInput(this.sceneParams, 'tonemapping', {
|
|
options: {
|
|
Linear: 1,
|
|
ACES: 4
|
|
}
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getRenderer().renderer.toneMapping = this.sceneParams.tonemapping
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
postFolder
|
|
.addInput(this.sceneParams, 'exposure', {
|
|
min: 0,
|
|
max: 1
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getRenderer().renderer.toneMappingExposure =
|
|
this.sceneParams.exposure
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
/** Disabled color grading for now
|
|
postFolder
|
|
.addInput(this.sceneParams, 'contrast', {
|
|
min: 0,
|
|
max: 2
|
|
})
|
|
.on('change', () => {
|
|
const batches = this.viewer
|
|
.getRenderer()
|
|
.batcher.getBatches(undefined, GeometryType.MESH)
|
|
batches.forEach((batch: MeshBatch) => {
|
|
const materials = batch.materials as SpeckleStandardMaterial[]
|
|
materials.forEach((material: SpeckleStandardMaterial) => {
|
|
material.userData.contrast.value = this.sceneParams.contrast
|
|
material.needsCopy = true
|
|
})
|
|
})
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
})
|
|
postFolder
|
|
.addInput(this.sceneParams, 'saturation', {
|
|
min: 0,
|
|
max: 2
|
|
})
|
|
.on('change', () => {
|
|
const batches = this.viewer
|
|
.getRenderer()
|
|
.batcher.getBatches(undefined, GeometryType.MESH)
|
|
batches.forEach((batch: MeshBatch) => {
|
|
const materials = batch.materials as SpeckleStandardMaterial[]
|
|
materials.forEach((material: SpeckleStandardMaterial) => {
|
|
material.userData.saturation.value = this.sceneParams.saturation
|
|
material.needsCopy = true
|
|
})
|
|
})
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
})
|
|
*/
|
|
|
|
postFolder
|
|
.addInput(this.sceneParams, 'minRoughness', {
|
|
label: 'Shininess',
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.05
|
|
})
|
|
.on('change', () => {
|
|
const batches = this.viewer
|
|
.getRenderer()
|
|
.batcher.getBatches(undefined, GeometryType.MESH)
|
|
batches.forEach((batch: MeshBatch) => {
|
|
const materials = batch.materials as SpeckleStandardMaterial[]
|
|
materials.forEach((material: SpeckleStandardMaterial) => {
|
|
material.updateArtificialRoughness(1 - this.sceneParams.minRoughness)
|
|
})
|
|
})
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
})
|
|
|
|
const lightsFolder = this.tabs.pages[1].addFolder({
|
|
title: 'Lights',
|
|
expanded: false
|
|
})
|
|
const directLightFolder = lightsFolder.addFolder({
|
|
title: 'Direct',
|
|
expanded: true
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'enabled', {
|
|
label: 'Sun Enabled'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'castShadow', {
|
|
label: 'Sun Shadows'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'intensity', {
|
|
label: 'Sun Intensity',
|
|
min: 0,
|
|
max: 10
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'color', {
|
|
view: 'color',
|
|
label: 'Sun Color'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'elevation', {
|
|
label: 'Sun Elevation',
|
|
min: 0,
|
|
max: Math.PI
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'azimuth', {
|
|
label: 'Sun Azimuth',
|
|
min: -Math.PI * 0.5,
|
|
max: Math.PI * 0.5
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
directLightFolder
|
|
.addInput(this.lightParams, 'radius', {
|
|
label: 'Sun Radius',
|
|
min: 0,
|
|
max: 1000
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
|
|
directLightFolder
|
|
.addInput({ bias: -0.001 }, 'bias', {
|
|
label: 'Shadow Bias',
|
|
min: -0.001,
|
|
max: 0,
|
|
step: 0.00001
|
|
})
|
|
.on('change', (value) => {
|
|
this.viewer.getRenderer().sunLight.shadow.bias = value.value
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
})
|
|
|
|
directLightFolder
|
|
.addInput({ radius: 2 }, 'radius', {
|
|
label: 'Shadow Radius',
|
|
min: 0,
|
|
max: 6,
|
|
step: 1
|
|
})
|
|
.on('change', (value) => {
|
|
this.viewer.getRenderer().sunLight.shadow.radius = value.value
|
|
this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS)
|
|
})
|
|
|
|
const indirectLightsFolder = lightsFolder.addFolder({
|
|
title: 'Indirect',
|
|
expanded: true
|
|
})
|
|
indirectLightsFolder
|
|
.addInput(this.sceneParams, 'hdri', {
|
|
label: 'HDRI',
|
|
options: {
|
|
Neutral,
|
|
Mild,
|
|
Mild2,
|
|
Sharp,
|
|
Bright
|
|
}
|
|
})
|
|
.on('change', async (value) => {
|
|
this.viewer.getRenderer().indirectIBL = await Assets.getEnvironment(
|
|
{
|
|
id: this.sceneParams.hdri,
|
|
src: value.value,
|
|
type: AssetType.TEXTURE_EXR
|
|
},
|
|
this.viewer.getRenderer().renderer
|
|
)
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
indirectLightsFolder
|
|
.addInput(this.lightParams, 'indirectLightIntensity', {
|
|
label: 'Probe Intensity',
|
|
min: 0,
|
|
max: 10
|
|
})
|
|
.on('change', (value) => {
|
|
value
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
|
|
const shadowcatcherFolder = this.tabs.pages[1].addFolder({
|
|
title: 'Shadowcatcher',
|
|
expanded: true
|
|
})
|
|
|
|
shadowcatcherFolder
|
|
.addInput(this.lightParams, 'shadowcatcher', { label: 'Enabled' })
|
|
.on('change', (value) => {
|
|
value
|
|
this.viewer.setLightConfiguration(this.lightParams)
|
|
})
|
|
|
|
const updateShadowcatcher = () => {
|
|
const shadowCatcher = this.viewer.getRenderer().shadowcatcher
|
|
if (shadowCatcher) {
|
|
shadowCatcher.configuration = this.shadowCatcherParams
|
|
this.viewer.getRenderer().updateShadowCatcher()
|
|
}
|
|
}
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'textureSize', {
|
|
label: 'Texture Size',
|
|
min: 1,
|
|
max: 1024,
|
|
step: 1
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'weights', {
|
|
label: 'weights',
|
|
x: { min: 0, max: 100 },
|
|
y: { min: 0, max: 100 },
|
|
z: { min: -100, max: 100 },
|
|
w: { min: -100, max: 100 }
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'blurRadius', {
|
|
label: 'Blur Radius',
|
|
min: 1,
|
|
max: 128,
|
|
step: 1
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'stdDeviation', {
|
|
label: 'Blur Std Deviation',
|
|
min: 1,
|
|
max: 128,
|
|
step: 1
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'sigmoidRange', {
|
|
label: 'Sigmoid Range',
|
|
min: -10,
|
|
max: 10,
|
|
step: 0.1
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
shadowcatcherFolder
|
|
.addInput(this.shadowCatcherParams, 'sigmoidStrength', {
|
|
label: 'Sigmoid Strength',
|
|
min: -10,
|
|
max: 10,
|
|
step: 0.1
|
|
})
|
|
.on('change', () => {
|
|
updateShadowcatcher()
|
|
})
|
|
}
|
|
|
|
makeFilteringUI() {
|
|
const filteringFolder = this.tabs.pages[2].addFolder({
|
|
title: 'Filtering',
|
|
expanded: true
|
|
})
|
|
|
|
filteringFolder.addInput(this.filterParams, 'filterBy', {
|
|
options: {
|
|
Volume: 'parameters.HOST_VOLUME_COMPUTED.value',
|
|
Area: 'parameters.HOST_AREA_COMPUTED.value',
|
|
Elevation: 'Elevation',
|
|
SpeckleType: 'speckle_type',
|
|
DisplayName: 'DisplayName',
|
|
EmbodiedCarbon: 'EmbodiedCarbon',
|
|
Floor: 'Floor',
|
|
Name: 'name',
|
|
TypeName: 'parameters.SYMBOL_NAME_PARAM.value',
|
|
Id: 'id',
|
|
DSD: 'DSD.@TYPE'
|
|
}
|
|
})
|
|
|
|
filteringFolder
|
|
.addButton({
|
|
title: 'Apply Filter'
|
|
})
|
|
.on('click', () => {
|
|
const data = this.properties.find((value) => {
|
|
return value.key === this.filterParams.filterBy
|
|
}) as PropertyInfo
|
|
this.viewer.getExtension(FilteringExtension).setColorFilter(data)
|
|
this.pane.refresh()
|
|
})
|
|
|
|
filteringFolder
|
|
.addButton({
|
|
title: 'Clear Filters'
|
|
})
|
|
.on('click', () => {
|
|
this.viewer.getExtension(FilteringExtension).resetFilters()
|
|
})
|
|
}
|
|
|
|
public makeBatchesUI() {
|
|
const container = this.tabs.pages[3]
|
|
const showBatches = container.addButton({
|
|
title: 'ShowBatches'
|
|
})
|
|
showBatches.on('click', () => {
|
|
this.viewer.getRenderer().debugShowBatches()
|
|
this.viewer.requestRender()
|
|
})
|
|
|
|
container.addInput(this.batchesParams, 'totalBvhSize', {
|
|
label: 'BVH Size(MB)',
|
|
disabled: true
|
|
})
|
|
container
|
|
.addInput(this.batchesParams, 'explode', {
|
|
label: 'Explode',
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.001
|
|
})
|
|
.on('change', (value) => {
|
|
value
|
|
this.viewer
|
|
.getExtension(ExplodeExtension)
|
|
.setExplode(this.batchesParams.explode)
|
|
})
|
|
// container
|
|
// .addInput(Sandbox.batchesParams, 'culling', {
|
|
// label: 'Culling'
|
|
// })
|
|
// .on('change', (value) => {
|
|
// this.viewer
|
|
// .getRenderer()
|
|
// .setExplodeTime(Sandbox.batchesParams.explode)
|
|
// })
|
|
}
|
|
|
|
public makeDiffUI() {
|
|
const container = this.tabs.pages[4]
|
|
const diffButton = container.addButton({
|
|
title: 'Diff'
|
|
})
|
|
const diffParams = {
|
|
time: 0,
|
|
mode: VisualDiffMode.COLORED
|
|
}
|
|
let diffResult: DiffResult | null = null
|
|
diffButton.on('click', async () => {
|
|
diffResult = await this.viewer.getExtension(DiffExtension).diff(
|
|
//building
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/bcf37136dea9fe9397cdfd84012f616a',
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/94af0a6b4eaa318647180f8c230cb867',
|
|
// cubes
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/d2510c59c203b73473f8bbfe637e0552',
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/1c327da824fdb04629eb48675101d7b7',
|
|
// sketchup
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/06bed1819e6c61d9df7196d424ab1eec',
|
|
// 'https://latest.speckle.systems/streams/aea12cab71/objects/9026f1d6495789b9eab31b5028c9a8ef',
|
|
//latest
|
|
'https://latest.speckle.systems/streams/cdbe82b016/objects/c14d1a33fd68323193813ec215737472',
|
|
'https://latest.speckle.systems/streams/cdbe82b016/objects/16676fc95a9ead877f6a825d9e28cbe8',
|
|
//lines
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/3b42d6ef51d3110b4e33b9f8cdc9f357',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/774384d431fb34d447d4696abbc4b816',
|
|
// points
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/7118603b197c00944f53be650ce721ec',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/4ffcf75dc4a28ed52500df73d08058ee',
|
|
// randos
|
|
// 'https://latest.speckle.systems/streams/3ed8357f29/objects/d8786c21f277be67a0ea2cd43a1930df',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/8247bbc53865b0e0cb5ee4e252e66216',
|
|
// instances
|
|
// 'https://app.speckle.systems/streams/be0f962efb/objects/37639741c363a123100eda8044f2fe3f',
|
|
// 'https://app.speckle.systems/streams/be0f962efb/objects/746024a9d42eca632889ff9f7685d329',
|
|
// blocks
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/a4e2fad01e69cd886ecbfedf221f5301',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/a3c6c58ef9872b17125c9ab2b009e5cd',
|
|
// instances & hosting
|
|
// 'https://app.speckle.systems/streams/be0f962efb/objects/fb4f291a13f05f325a5575fddd4276d0',
|
|
// 'https://app.speckle.systems/streams/be0f962efb/objects/21cf63a1496e366b34501429ce7ad2f5',
|
|
// bug
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/91d69894f2ac7b3b2b6de4616d89e478',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/ce55c0fb40e77fbfc894d4c27568f1f9',
|
|
// bug
|
|
// 'https://latest.speckle.systems/streams/0c6ad366c4/objects/03f0a8bf0ed8064865eda87a865c7212',
|
|
// 'https://latest.speckle.systems/streams/0c6ad366c4/objects/33ef6b9b547dc9688eb40157b967eab9',
|
|
// large
|
|
// 'https://app.speckle.systems/streams/e6f9156405/objects/650f358d8aac50168d9e9226ef6f5cbc',
|
|
// 'https://latest.speckle.systems/streams/92b620fb17/objects/1154ca1d997ac631571db55f84cb703d',
|
|
// cubes
|
|
// 'https://latest.speckle.systems/streams/0c6ad366c4/objects/03f0a8bf0ed8064865eda87a865c7212',
|
|
// 'https://latest.speckle.systems/streams/0c6ad366c4/objects/33ef6b9b547dc9688eb40157b967eab9',
|
|
// DUI3
|
|
|
|
VisualDiffMode.COLORED,
|
|
localStorage.getItem('AuthTokenLatest') as string
|
|
)
|
|
})
|
|
const unDiffButton = container.addButton({
|
|
title: 'Undiff'
|
|
})
|
|
unDiffButton.on('click', async () => {
|
|
void this.viewer.getExtension(DiffExtension).undiff()
|
|
})
|
|
|
|
container
|
|
.addInput(diffParams, 'time', {
|
|
label: 'Diff Time',
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.1
|
|
})
|
|
.on('change', (value) => {
|
|
if (!diffResult) return
|
|
this.viewer.getExtension(DiffExtension).updateVisualDiff(value.value)
|
|
this.viewer.requestRender()
|
|
})
|
|
container
|
|
.addInput(diffParams, 'mode', {
|
|
options: {
|
|
COLORED: VisualDiffMode.COLORED,
|
|
PLAIN: VisualDiffMode.PLAIN
|
|
}
|
|
})
|
|
.on('change', (value) => {
|
|
if (!diffResult) return
|
|
this.viewer
|
|
.getExtension(DiffExtension)
|
|
.updateVisualDiff(diffParams.time, value.value)
|
|
this.viewer.requestRender()
|
|
})
|
|
}
|
|
|
|
public makeMeasurementsUI() {
|
|
const container = this.tabs.pages[5]
|
|
container
|
|
.addInput(this.measurementsParams, 'enabled', {
|
|
label: 'Enabled'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(SelectionExtension).enabled =
|
|
!this.measurementsParams.enabled
|
|
this.viewer.getExtension(MeasurementsExtension).enabled =
|
|
this.measurementsParams.enabled
|
|
})
|
|
container
|
|
.addInput(this.measurementsParams, 'visible', {
|
|
label: 'Visible'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).options =
|
|
this.measurementsParams
|
|
})
|
|
container
|
|
.addInput(this.measurementsParams, 'type', {
|
|
label: 'Type',
|
|
options: {
|
|
PERPENDICULAR: MeasurementType.PERPENDICULAR,
|
|
POINTTOPOINT: MeasurementType.POINTTOPOINT
|
|
}
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).options =
|
|
this.measurementsParams
|
|
})
|
|
container
|
|
.addInput(this.measurementsParams, 'vertexSnap', {
|
|
label: 'Snap'
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).options =
|
|
this.measurementsParams
|
|
})
|
|
|
|
container
|
|
.addInput(this.measurementsParams, 'units', {
|
|
label: 'Units',
|
|
options: Units
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).options =
|
|
this.measurementsParams
|
|
})
|
|
container
|
|
.addInput(this.measurementsParams, 'precision', {
|
|
label: 'Precision',
|
|
step: 1,
|
|
min: 1,
|
|
max: 5
|
|
})
|
|
.on('change', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).options =
|
|
this.measurementsParams
|
|
})
|
|
container
|
|
.addButton({
|
|
title: 'Delete'
|
|
})
|
|
.on('click', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).removeMeasurement()
|
|
})
|
|
container
|
|
.addButton({
|
|
title: 'Delete All'
|
|
})
|
|
.on('click', () => {
|
|
this.viewer.getExtension(MeasurementsExtension).clearMeasurements()
|
|
})
|
|
}
|
|
|
|
private getBVHSize() {
|
|
let size = 0
|
|
const objects = this.viewer.getRenderer().allObjects
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
objects.traverse((obj: any) => {
|
|
if (obj.hasOwnProperty('boundsTreeSizeInBytes')) {
|
|
size += obj['boundsTreeSizeInBytes']
|
|
// console.log(obj['boundsTreeSizeInBytes'] / 1024 / 1024)
|
|
}
|
|
})
|
|
|
|
return size / 1024 / 1024
|
|
}
|
|
|
|
public async loadUrl(url: string) {
|
|
const authToken = localStorage.getItem(
|
|
url.includes('latest') ? 'AuthTokenLatest' : 'AuthToken'
|
|
) as string
|
|
const objUrls = await UrlHelper.getResourceUrls(url, authToken)
|
|
for (const objUrl of objUrls) {
|
|
console.log(`Loading ${url}`)
|
|
const loader = new SpeckleLoader(
|
|
this.viewer.getWorldTree(),
|
|
objUrl,
|
|
authToken,
|
|
true,
|
|
undefined
|
|
)
|
|
/** Too spammy */
|
|
// loader.on(LoaderEvent.LoadProgress, (arg: { progress: number; id: string }) => {
|
|
// console.warn(arg)
|
|
// })
|
|
loader.on(LoaderEvent.LoadCancelled, (resource: string) => {
|
|
console.warn(`Resource ${resource} loading was canceled`)
|
|
})
|
|
loader.on(LoaderEvent.LoadWarning, (arg: { message: string }) => {
|
|
console.error(`Loader warning: ${arg.message}`)
|
|
})
|
|
|
|
void this.viewer.loadObject(loader, true)
|
|
}
|
|
localStorage.setItem('last-load-url', url)
|
|
}
|
|
|
|
public async loadJSON(json: string) {
|
|
const loader = new SpeckleOfflineLoader(this.viewer.getWorldTree(), json)
|
|
loader.on(LoaderEvent.LoadCancelled, (resource: string) => {
|
|
console.warn(`Resource ${resource} loading was canceled`)
|
|
})
|
|
loader.on(LoaderEvent.LoadWarning, (arg: { message: string }) => {
|
|
console.error(`Loader warning: ${arg.message}`)
|
|
})
|
|
|
|
void this.viewer.loadObject(loader, true)
|
|
}
|
|
}
|