diff --git a/packages/viewer-sandbox/src/Extensions/BoxSelection.ts b/packages/viewer-sandbox/src/Extensions/BoxSelection.ts index 4de0d08de6..04ac3ca579 100644 --- a/packages/viewer-sandbox/src/Extensions/BoxSelection.ts +++ b/packages/viewer-sandbox/src/Extensions/BoxSelection.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { InputEvent } from '@speckle/viewer' import { ObjectLayers } from '@speckle/viewer' +import { NodeRenderView } from '@speckle/viewer' import { SelectionExtension } from '@speckle/viewer' import { BatchObject } from '@speckle/viewer' import { @@ -10,7 +11,6 @@ import { GeometryType, MeshBatch } from '@speckle/viewer' -import { NodeRenderView } from '@speckle/viewer/dist/modules/tree/NodeRenderView' import { Matrix4, ShaderMaterial, @@ -39,6 +39,7 @@ export class BoxSelection extends Extension { public constructor(viewer: IViewer, private cameraController: ICameraProvider) { super(viewer) /** Get the SelectionExtension. We'll need it to remotely enable/disable it */ + //@ts-ignore this.selectionExtension = this.viewer.getExtension(SelectionExtension) /** Create the drag box */ @@ -60,8 +61,7 @@ export class BoxSelection extends Extension { } this.frameLock = false } - - private onPointerDown(e) { + private onPointerDown(e: Vector2 & { event: PointerEvent }) { if (e.event.altKey) { /** Disable camera controller. We want to be able to drag the selection box */ this.cameraController.enabled = false @@ -82,7 +82,7 @@ export class BoxSelection extends Extension { this.viewer.requestRender() } - private onPointerMove(e) { + private onPointerMove(e: Vector2 & { event: PointerEvent }) { /** Selection box only when holding the alt key */ if (!e.event.altKey || !this.dragging || this.frameLock) return /** Copy the current point */ @@ -123,7 +123,7 @@ export class BoxSelection extends Extension { **/ const selectionRvs: Array = [] for (let b = 0; b < batches.length; b++) { - batches[b].mesh.BVH.shapecast({ + batches[b].mesh.TAS.shapecast({ /** This is the callback from the TAS's bounds internal nodes */ intersectsTAS: (box: Box3) => { /** We continue traversion only if the selection box intersects an internal node */ diff --git a/packages/viewer-sandbox/src/Extensions/ExtendedSelection.ts b/packages/viewer-sandbox/src/Extensions/ExtendedSelection.ts index b1745ad24a..b49c61b318 100644 --- a/packages/viewer-sandbox/src/Extensions/ExtendedSelection.ts +++ b/packages/viewer-sandbox/src/Extensions/ExtendedSelection.ts @@ -12,7 +12,7 @@ export class ExtendedSelection extends SelectionExtension { /** This object will recieve the TransformControls translation */ private dummyAnchor: Object3D = new Object3D() /** Stock three.js gizmo */ - private transformControls: TransformControls = null + private transformControls: TransformControls | undefined public init() { /** We set the layers to PROPS so that the viewer regular pipeline ignores it */ @@ -93,9 +93,9 @@ export class ExtendedSelection extends SelectionExtension { } const center = box.getCenter(new Vector3()) this.dummyAnchor.position.copy(center) - if (attach) this.transformControls.attach(this.dummyAnchor) + if (attach) this.transformControls?.attach(this.dummyAnchor) else { - this.transformControls.detach() + this.transformControls?.detach() } } diff --git a/packages/viewer-sandbox/src/Sandbox.ts b/packages/viewer-sandbox/src/Sandbox.ts index d8f018ef9c..b7eba663aa 100644 --- a/packages/viewer-sandbox/src/Sandbox.ts +++ b/packages/viewer-sandbox/src/Sandbox.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Box3, SectionTool, TreeNode, WorldTree } from '@speckle/viewer' -import { Vector3 } from '@speckle/viewer' +import { Box3, SectionTool, TreeNode } from '@speckle/viewer' import { CanonicalView, DebugViewer, @@ -26,9 +25,12 @@ import { MeasurementsExtension } from '@speckle/viewer' import { FilteringExtension } from '@speckle/viewer' import { CameraController } from '@speckle/viewer' import { UpdateFlags } from '@speckle/viewer' +import { Viewer } from '@speckle/viewer' + +import { Euler, Vector3 } from 'three' export default class Sandbox { - private viewer: DebugViewer + private viewer: Viewer private pane: Pane private tabs private viewsFolder!: FolderApi @@ -144,22 +146,22 @@ export default class Sandbox { viewer.on(ViewerEvent.LoadComplete, async (url: string) => { this.addStreamControls(url) - // this.addViewControls() + this.addViewControls() this.addBatches() this.properties = await this.viewer.getObjectProperties() this.batchesParams.totalBvhSize = this.getBVHSize() this.refresh() }) - viewer.on(ViewerEvent.UnloadComplete, (url: string) => { + viewer.on(ViewerEvent.UnloadComplete, async (url: string) => { url this.removeViewControls() this.addViewControls() - this.properties = this.viewer.getObjectProperties() + this.properties = await this.viewer.getObjectProperties() }) - viewer.on(ViewerEvent.UnloadAllComplete, (url: string) => { + viewer.on(ViewerEvent.UnloadAllComplete, async (url: string) => { this.removeViewControls() this.addViewControls() - this.properties = this.viewer.getObjectProperties() + this.properties = await this.viewer.getObjectProperties() // viewer.World.resetWorld() url }) @@ -245,7 +247,7 @@ export default class Sandbox { title: views[k].name ? views[k].name : 'Unnamed' }) .on('click', () => { - this.viewer.setView(views[k], true) + this.viewer.getExtension(CameraController).setCameraView(views[k], true) }) } } @@ -286,7 +288,11 @@ export default class Sandbox { // const origin = unionBox.getCenter(new Vector3()) objects.forEach((obj: BatchObject) => { // obj.transformTRS(position.value, rotation.value, scale.value, origin) - obj.position = position.value + obj.position = new Vector3( + position.value.x, + position.value.y, + position.value.z + ) }) this.viewer.requestRender() }) @@ -306,7 +312,12 @@ export default class Sandbox { // const origin = unionBox.getCenter(new Vector3()) objects.forEach((obj: BatchObject) => { // obj.transformTRS(position.value, rotation.value, scale.value, origin) - obj.euler = rotation.value + obj.euler = new Euler( + rotation.value.x, + rotation.value.y, + rotation.value.z, + 'XYZ' + ) }) this.viewer.requestRender() }) @@ -359,9 +370,9 @@ export default class Sandbox { reader.readAsText(file, 'UTF-8') reader.onload = async (readerEvent) => { - const content = readerEvent?.target?.result + const content = readerEvent?.target?.result as string const loader = new ObjLoader(this.viewer.getWorldTree(), file.name, content) - await this.viewer.loadObject(loader, 1, true) + await this.viewer.loadObject(loader, true) } } input.click() @@ -405,11 +416,12 @@ export default class Sandbox { title: 'Zoom Extents' }) zoomExtents.on('click', () => { - this.viewer.zoom( - this.selectionList.map((val) => val.hits[0].object.id) as string[], - undefined, - true - ) + this.viewer + .getExtension(CameraController) + .setCameraView( + this.selectionList.map((val) => val.hits[0].node.model.id) as string[], + true + ) }) this.tabs.pages[0].addSeparator() @@ -471,8 +483,10 @@ export default class Sandbox { setTimeout(resolve, ms) }) for (let i = 0; i < 24; i++) { - this.viewer.setView({ azimuth: Math.PI / 12, polar: 0 }, false) - this.viewer.getRenderer().resetPipeline(true) + this.viewer + .getExtension(CameraController) + .setCameraView({ azimuth: Math.PI / 12, polar: 0 }, false) + this.viewer.getRenderer().resetPipeline() await waitForAnimation(1000) } }) @@ -488,7 +502,9 @@ export default class Sandbox { title: sides[k] }) .on('click', () => { - this.viewer.setView(sides[k] as CanonicalView) + this.viewer + .getExtension(CameraController) + .setCameraView(sides[k] as CanonicalView, true) }) } } @@ -544,30 +560,6 @@ export default class Sandbox { this.viewer.requestRender() }) - postFolder - .addInput({ near: 0.01 }, 'near', { - min: 0, - max: 2, - step: 0.001 - }) - .on('change', (ev) => { - this.viewer.cameraHandler.activeCam.camera.near = ev.value - this.viewer.cameraHandler.activeCam.camera.updateProjectionMatrix() - this.viewer.requestRender() - }) - - postFolder - .addInput({ far: 10 }, 'far', { - min: 0, - max: 10000, - step: 1 - }) - .on('change', (ev) => { - this.viewer.cameraHandler.activeCam.camera.far = ev.value - this.viewer.cameraHandler.activeCam.camera.updateProjectionMatrix() - this.viewer.requestRender() - }) - postFolder .addInput(this.sceneParams, 'tonemapping', { options: { @@ -835,8 +827,7 @@ export default class Sandbox { }) .on('change', (value) => { this.viewer.getRenderer().sunLight.shadow.bias = value.value - this.viewer.requestRenderShadowmap() - this.viewer.requestRender() + this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS) }) directLightFolder @@ -848,8 +839,7 @@ export default class Sandbox { }) .on('change', (value) => { this.viewer.getRenderer().sunLight.shadow.radius = value.value - this.viewer.requestRenderShadowmap() - this.viewer.requestRender() + this.viewer.requestRender(UpdateFlags.RENDER | UpdateFlags.SHADOWS) }) const indirectLightsFolder = lightsFolder.addFolder({ @@ -1106,7 +1096,7 @@ export default class Sandbox { title: 'Undiff' }) unDiffButton.on('click', async () => { - this.viewer.undiff() + this.viewer.getExtension(DiffExtension).undiff() }) container @@ -1234,9 +1224,10 @@ export default class Sandbox { url, authToken, true, + undefined, 1 ) - await this.viewer.loadObject(loader, 1, true) + await this.viewer.loadObject(loader, true) } localStorage.setItem('last-load-url', url) } diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 78eeea76ad..9232fd360c 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -5,8 +5,7 @@ import { SelectionEvent, ViewerEvent, DebugViewer, - Viewer, - WorldTree + Viewer } from '@speckle/viewer' import './style.css' @@ -19,7 +18,6 @@ import { DiffExtension, FilteringExtension } from '@speckle/viewer' -import { GeometryType } from '@speckle/viewer' const createViewer = async (containerName: string, stream: string) => { const container = document.querySelector(containerName) @@ -90,33 +88,7 @@ const createViewer = async (containerName: string, stream: string) => { console.warn(viewer.getRenderer().renderingStats) Object.assign(sandbox.sceneParams.worldSize, viewer.World.worldSize) Object.assign(sandbox.sceneParams.worldOrigin, viewer.World.worldOrigin) - const categories = {} - //@ts-ignore - await viewer.getWorldTree().walkAsync((node) => { - //@ts-ignore - if (!categories[node.model.raw.speckle_type]) { - //@ts-ignore - categories[node.model.raw.speckle_type] = 0 - } - //@ts-ignore - categories[node.model.raw.speckle_type]++ - return true - }) - console.log(categories) - sandbox.refresh() - const COUNT = 16000 - await viewer.getWorldTree().walkAsync((node: TreeNode) => { - if ( - viewer.getWorldTree().isRoot(node) || - node.parent.model.id === WorldTree.ROOT_ID || - !node.model.renderView - ) - return true - const dice = Math.random() - if (dice < 0.5 && sandbox.ids.length < COUNT) sandbox.ids.push(node.model.id) - return true - }) }) viewer.on(ViewerEvent.UnloadComplete, () => { @@ -211,7 +183,7 @@ const getStream = () => { // '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/c1faab5c62/commits/e6632fe057' // 'https://latest.speckle.dev/streams/7d051a6449/commits/7632757a33' // 'https://latest.speckle.dev/streams/4658eb53b9/commits/d8ec9cccf7' // MEPs (whatever they are) @@ -334,7 +306,7 @@ const getStream = () => { // 'https://latest.speckle.dev/streams/97750296c2/commits/5386a0af02' // 700k+ objects 30kk tris // 'https://latest.speckle.dev/streams/97750296c2/commits/2a6fd781f2' // NEW - 'https://latest.speckle.dev/streams/97750296c2/commits/48f0567a88' // 1015849 objects + // 'https://latest.speckle.dev/streams/97750296c2/commits/48f0567a88' // 1015849 objects // 'https://latest.speckle.dev/streams/97750296c2/commits/aec0841f7e' // 11k objects // 'https://latest.speckle.dev/streams/97750296c2/commits/96ffc3c786' // 92209 objects // 'https://latest.speckle.dev/streams/97750296c2/commits/92115d3789' // 390974 objects 19kk tris diff --git a/packages/viewer/readme.md b/packages/viewer/readme.md index 625c9bd83e..cb5c7d02c9 100644 --- a/packages/viewer/readme.md +++ b/packages/viewer/readme.md @@ -1,22 +1,31 @@ -# The Speckle Viewer +# The Speckle Viewer API 2.0 [![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white)](https://speckle.community) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/) [![npm version](https://badge.fury.io/js/%40speckle%2Fviewer.svg)](https://badge.fury.io/js/%40speckle%2Fviewer) -## Disclaimer +## Documentation -We're working to stabilize the 2.0 API, and until then there will be breaking changes. +Quick Reference guide [here](https://www.notion.so/speckle/Viewer-API-2-0-Quick-Reference-0638b4f31b7c4e04a7ef0c1a0ac20854) +Complete documentation _coming soon_ -## Development +## Examples + +You can find readily available examples here: -You can debug and test the viewer is through the `viewer-sandbox` package. Some simple steps to get going: +- [Basic Setup](https://codesandbox.io/s/basic-setup-jf4ccn) +- [Measurements Extension](https://codesandbox.io/s/measurement-tool-frmffj) +- [Obj Loader](https://codesandbox.io/s/obj-loader-pydvhz) +- [Expanding an Extension (Manipulating Objects)](https://codesandbox.io/s/manipulating-objects-yydx4q) +- [Custom Extension (Categorize)](https://codesandbox.io/s/categorize-7jw7lw) + +## Development - run `yarn` in the repo root to install and link all required dependencies. - run `yarn dev` in the viewer package (here) to start a dev build with automatic code refresing of the viewer code. - run `yarn dev` in the viewer-sandbox package to start the viewer sandbox. -## API +## Feedback -The Viewer's API is [documented here](https://speckle.notion.site/Viewer-API-Documentation-11f7bcbf3d2547c2985b0c988fb9889e). +We would love to hear your feedback in [this forum thread](https://speckle.community/t/seeking-feedback-on-the-new-viewer-api-structure/7231). ## Community diff --git a/packages/viewer/src/modules/batching/BatchObject.ts b/packages/viewer/src/modules/batching/BatchObject.ts index 9818b2d867..db612722cb 100644 --- a/packages/viewer/src/modules/batching/BatchObject.ts +++ b/packages/viewer/src/modules/batching/BatchObject.ts @@ -8,7 +8,10 @@ import { } from '../objects/AccelerationStructure' import { MeshBVH } from 'three-mesh-bvh' -export type VectorLike = { x: number; y: number; z?: number; w?: number } +export type VectorLike = + | { x: number; y: number; z?: number; w?: number } + | undefined + | null export class BatchObject { protected _renderView: NodeRenderView