Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WEB-1261 #2478

Merged
merged 4 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/viewer-sandbox/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const getStream = () => {
// 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'
'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'https://latest.speckle.dev/streams/c1faab5c62/commits/ab1a1ab2b6'
// 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8'
// 'https://latest.speckle.dev/streams/58b5648c4d/commits/60371ecb2d'
Expand Down Expand Up @@ -390,8 +390,9 @@ const getStream = () => {
// 'https://app.speckle.systems/projects/20f72acc58/models/2cf8a736f8'
// Rhino sRGB vertex colors
// 'https://app.speckle.systems/projects/47bbaf594f/models/ef78e94f72'
// 'https://app.speckle.systems/projects/47bbaf594f/models/de52414725f8937b1f0e2f550ef9ca52'
// qGIS sRGB vertex colors
'https://latest.speckle.systems/projects/5a6609a4b9/models/10f4931e8c'
// 'https://latest.speckle.systems/projects/5a6609a4b9/models/10f4931e8c'
)
}

Expand Down
14 changes: 7 additions & 7 deletions packages/viewer/src/modules/SpeckleRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,8 +604,13 @@ export default class SpeckleRenderer {

private addBatch(batch: Batch, parent: Object3D) {
const batchRenderable = batch.renderObject
parent.add(batch.renderObject)

parent.add(batchRenderable)
if (
batchRenderable instanceof SpeckleMesh ||
batchRenderable instanceof SpeckleInstancedMesh
) {
if (batchRenderable.TAS.bvhHelper) parent.add(batchRenderable.TAS.bvhHelper)
}
if (batch.geometryType === GeometryType.MESH) {
batchRenderable.traverse((obj: Object3D) => {
if (obj instanceof Mesh) {
Expand All @@ -621,11 +626,6 @@ export default class SpeckleRenderer {
)
}
})

const meshRenderable = batchRenderable as SpeckleMesh | SpeckleInstancedMesh
meshRenderable.TAS.boxHelpers.forEach((helper: Box3Helper) => {
this.scene.add(helper)
})
}
this.viewer.World.expandWorld(batch.bounds)
}
Expand Down
13 changes: 8 additions & 5 deletions packages/viewer/src/modules/UrlHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ async function getOldResourceUrls(url: string, authToken?: string): Promise<stri
// supports commit based urls
if (url.includes('commits')) {
const commitId = url.split('/commits/')[1].substring(0, 10)
const objUrl = await getCommitReferencedObjectUrl({
origin: parsed.origin,
streamId,
commitId
})
const objUrl = await getCommitReferencedObjectUrl(
{
origin: parsed.origin,
streamId,
commitId
},
authToken
)
objsUrls.push(objUrl)
}

Expand Down
51 changes: 30 additions & 21 deletions packages/viewer/src/modules/extensions/CameraController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,18 +369,32 @@ export class CameraController extends Extension implements SpeckleCamera {
}

public updateCameraPlanes(targetVolume?: Box3, offsetScale: number = 1) {
const renderer = this.viewer.getRenderer()
if (!renderer.renderingCamera) return

if (!targetVolume) targetVolume = this.viewer.getRenderer().sceneBox
let nearPlane = this.computeNearCameraPlaneEmpiric(targetVolume, offsetScale)
if (this._options.nearPlaneCalculation === NearPlaneCalculation.ACCURATE)
this.updateNearCameraPlaneAccurate(targetVolume, offsetScale)
else if (this._options.nearPlaneCalculation === NearPlaneCalculation.EMPIRIC)
this.updateNearCameraPlaneEmpiric(targetVolume, offsetScale)
nearPlane = this.computeNearCameraPlaneAccurate(
targetVolume,
offsetScale,
nearPlane
)
if (nearPlane) {
renderer.renderingCamera.near = nearPlane
renderer.renderingCamera.updateProjectionMatrix()
}
this.updateFarCameraPlane()
}

protected updateNearCameraPlaneEmpiric(targetVolume?: Box3, offsetScale: number = 1) {
protected computeNearCameraPlaneEmpiric(
targetVolume?: Box3,
offsetScale: number = 1
): number | undefined {
if (!targetVolume) return

if (targetVolume.isEmpty()) {
Logger.error('Cannot set camera planes for empty volume')
Logger.warn('Cannot set camera planes for empty volume')
return
}

Expand All @@ -394,22 +408,17 @@ export class CameraController extends Extension implements SpeckleCamera {
const fitWidthDistance = fitHeightDistance / camAspect
const distance = offsetScale * Math.max(fitHeightDistance, fitWidthDistance)

this._renderingCamera.near =
this._renderingCamera === this.perspectiveCamera ? distance / 100 : 0.001
this._renderingCamera.updateProjectionMatrix()
return this.perspectiveCamera ? distance / 100 : 0.001
}

protected updateNearCameraPlaneAccurate(
protected computeNearCameraPlaneAccurate(
targetVolume?: Box3,
offsetScale: number = 1
) {
const renderer = this.viewer.getRenderer()
if (!renderer.renderingCamera) return

const minDist = this.getClosestGeometryDistance()
offsetScale: number = 1,
fallback?: number
): number | undefined {
const minDist = this.getClosestGeometryDistance(fallback)
if (minDist === Number.POSITIVE_INFINITY) {
this.updateNearCameraPlaneEmpiric(targetVolume, offsetScale)
return
return this.computeNearCameraPlaneEmpiric(targetVolume, offsetScale)
}

const camFov =
Expand All @@ -423,9 +432,8 @@ export class CameraController extends Extension implements SpeckleCamera {
Math.pow(Math.tan(((camFov / 180) * Math.PI) / 2), 2) *
(Math.pow(camAspect, 2) + 1)
)
renderer.renderingCamera.near = nearPlane
renderer.renderingCamera.updateProjectionMatrix()
// console.log(minDist, nearPlane)
return nearPlane
}

protected updateFarCameraPlane() {
Expand Down Expand Up @@ -456,7 +464,7 @@ export class CameraController extends Extension implements SpeckleCamera {
renderer.renderingCamera.updateProjectionMatrix()
}

protected getClosestGeometryDistance(): number {
protected getClosestGeometryDistance(fallback?: number): number {
const cameraPosition = this._renderingCamera.position
const cameraTarget = this.getTarget()
const cameraDir = new Vector3().subVectors(cameraTarget, cameraPosition).normalize()
Expand All @@ -468,7 +476,8 @@ export class CameraController extends Extension implements SpeckleCamera {
for (let b = 0; b < batches.length; b++) {
const result = batches[b].mesh.TAS.closestPointToPointHalfplane(
cameraPosition,
cameraDir
cameraDir,
fallback
)
if (!result) continue
minDist = Math.min(minDist, result.distance)
Expand Down
11 changes: 0 additions & 11 deletions packages/viewer/src/modules/objects/SpeckleMesh.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
BackSide,
Box3,
Box3Helper,
BufferAttribute,
BufferGeometry,
Color,
DataTexture,
DoubleSide,
FloatType,
Expand All @@ -24,7 +22,6 @@ import {
} from 'three'
import { BatchObject } from '../batching/BatchObject.js'
import Materials from '../materials/Materials.js'
import { ObjectLayers } from '../../IViewer.js'
import { TopLevelAccelerationStructure } from './TopLevelAccelerationStructure.js'
import { SpeckleRaycaster } from './SpeckleRaycaster.js'
import Logger from '../utils/Logger.js'
Expand Down Expand Up @@ -76,9 +73,6 @@ export default class SpeckleMesh extends Mesh {
public transformsTextureUniform: DataTexture
public transformsArrayUniforms: Matrix4[] | null = null

private debugBatchBox = false
private boxHelper!: Box3Helper

public get TAS(): TopLevelAccelerationStructure {
return this.tas
}
Expand Down Expand Up @@ -244,11 +238,6 @@ export default class SpeckleMesh extends Mesh {
this.geometry.boundingBox.copy(this.tas.bounds)
if (!this.geometry.boundingSphere) this.geometry.boundingSphere = new Sphere()
this.geometry.boundingBox.getBoundingSphere(this.geometry.boundingSphere)
if (!this.boxHelper && this.debugBatchBox) {
this.boxHelper = new Box3Helper(this.tas.bounds, new Color(0xff0000))
this.boxHelper.layers.set(ObjectLayers.PROPS)
if (this.parent) this.parent.add(this.boxHelper)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {
Box3,
Box3Helper,
BufferAttribute,
Color,
FrontSide,
Material,
Matrix4,
Mesh,
Ray,
Side,
Vector3
} from 'three'
import { MeshBVHVisualizer } from 'three-mesh-bvh'
import { BatchObject } from '../batching/BatchObject.js'
import { ExtendedTriangle, HitPointInfo } from 'three-mesh-bvh'
import type {
Expand Down Expand Up @@ -37,7 +37,7 @@ import { AccelerationStructure } from './AccelerationStructure.js'
fine as it is. If we really really really need that 100% accuracy, we'll just make it relative to the origin
*/
export class TopLevelAccelerationStructure {
private static debugBoxes = false
private debugBVH = false
private static cubeIndices = [
// front
0, 1, 2, 2, 3, 0,
Expand All @@ -57,8 +57,8 @@ export class TopLevelAccelerationStructure {
public batchObjects: BatchObject[] = []
public bounds: Box3 = new Box3(new Vector3(0, 0, 0), new Vector3(0, 0, 0))

public boxHelpers: Box3Helper[] = []
public accelerationStructure: AccelerationStructure
public bvhHelper: MeshBVHVisualizer

public constructor(batchObjects: BatchObject[]) {
this.batchObjects = batchObjects
Expand All @@ -85,12 +85,6 @@ export class TopLevelAccelerationStructure {
vertOffset / 3 + TopLevelAccelerationStructure.CUBE_VERTS

vertOffset += TopLevelAccelerationStructure.CUBE_VERTS * 3

if (TopLevelAccelerationStructure.debugBoxes) {
const helper = new Box3Helper(boxBounds, new Color(0xff0000))
helper.layers.set(ObjectLayers.PROPS)
this.boxHelpers.push(helper)
}
}
this.accelerationStructure = new AccelerationStructure(
AccelerationStructure.buildBVH(indices, vertices)
Expand All @@ -99,6 +93,16 @@ export class TopLevelAccelerationStructure {
this.accelerationStructure.outputTransform = new Matrix4()
this.accelerationStructure.inputOriginTransform = new Matrix4()
this.accelerationStructure.outputOriginTransfom = new Matrix4()

if (this.debugBVH) {
const mesh = new Mesh(this.accelerationStructure.geometry)
mesh.layers.set(ObjectLayers.OVERLAY)
mesh.geometry.boundsTree = this.accelerationStructure.bvh
this.bvhHelper = new MeshBVHVisualizer(mesh)
this.bvhHelper.layers.set(ObjectLayers.OVERLAY)
this.bvhHelper.children[0].layers.set(ObjectLayers.OVERLAY)
this.bvhHelper.update()
}
}

private updateVertArray(box: Box3, offset: number, outPositions: number[]) {
Expand Down Expand Up @@ -144,8 +148,6 @@ export class TopLevelAccelerationStructure {
const basBox =
this.batchObjects[k].accelerationStructure.getBoundingBox(boxBuffer)
this.updateVertArray(basBox, start * 3, positions)

if (TopLevelAccelerationStructure.debugBoxes) this.boxHelpers[k].box.copy(basBox)
}
this.accelerationStructure.bvh.refit()
}
Expand Down Expand Up @@ -328,6 +330,7 @@ export class TopLevelAccelerationStructure {
public closestPointToPointHalfplane(
point: Vector3,
planeNormal: Vector3,
fallback?: number,
target: HitPointInfo = {
point: new Vector3(),
distance: 0,
Expand Down Expand Up @@ -359,6 +362,26 @@ export class TopLevelAccelerationStructure {
intersectsBounds: (_box: Box3, _isLeaf, score: number) => {
return score < closestDistanceSq && score < maxThresholdSq
},
intersectsRange: (triangleOffset: number) => {
/** The index buffer for the bvh's geometry will *never* be undefined as it uses indexed geometry */
const indexBufferAttribute: BufferAttribute = this.accelerationStructure
.geometry.index as BufferAttribute
const vertIndex = indexBufferAttribute.array[triangleOffset * 3]
const batchObjectIndex = Math.trunc(
vertIndex / TopLevelAccelerationStructure.CUBE_VERTS
)
const batchObject: BatchObject = this.batchObjects[batchObjectIndex]
/** Because we cannot get a proper min distance to geometry when *Inside* the bounds of a batch object's bounds,
* we just use the provided fallback value as min dist. Single meshes made of dijoint sets are particularly susceptible
* to incorrect min dist calculation, unless we go inside their BAS. But we want to avoid that all cost speed reasons
*/
const ret = batchObject.aabb.containsPoint(point)
if (ret && fallback !== undefined) {
closestDistanceSq = fallback * fallback
}
/** We do not interrupt traversal, there might be other closer valid geometry available */
return false
},

intersectsTriangle: (tri, triIndex) => {
tri.closestPointToPoint(point, temp)
Expand Down
5 changes: 5 additions & 0 deletions packages/viewer/src/type-augmentations/three.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { MeshBVH } from 'three-mesh-bvh'

declare module 'three' {
interface Raycaster {
firstHitOnly: boolean
}
interface BufferGeometry {
boundsTree: MeshBVH
}
}
export {}