Skip to content

Commit

Permalink
Comment Bubbles WASD Fix (#3920)
Browse files Browse the repository at this point in the history
* feat(viewer-lib): Added  property to PointQueryResult which tells if the requested point is in frustum when projecting and unprojecting

* feat(frontend-2): Updated the comment bubbles screen location computation step to account for their anchor's inclusion in the camera's frustum
  • Loading branch information
AlexandruPopovici authored Feb 3, 2025
1 parent 86c3489 commit cff48b9
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 21 deletions.
34 changes: 21 additions & 13 deletions packages/frontend-2/lib/viewer/composables/anchorPoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,44 @@ export function useViewerAnchoredPointCalculator(params: {
*/
const calculate = (target: Vector3) => {
let targetLoc: Nullable<{ x: number; y: number }> = null
let inFrustum: boolean | undefined = false
if (parentEl.value) {
const targetProjectionResult = viewer.query<PointQuery>({
point: target,
operation: 'Project'
})
targetLoc = viewer.Utils.NDCToScreen(
targetProjectionResult.x,
targetProjectionResult.y,
parentEl.value.clientWidth,
parentEl.value.clientHeight
)
inFrustum ||= targetProjectionResult.inFrustum
/** If not in camera's frustum, don't bother projecting */
if (inFrustum)
targetLoc = viewer.Utils.NDCToScreen(
targetProjectionResult.x,
targetProjectionResult.y,
parentEl.value.clientWidth,
parentEl.value.clientHeight
)

// round it out
if (targetLoc) {
targetLoc.x = round(targetLoc.x)
targetLoc.y = round(targetLoc.y)
}

// logger.debug(targetLoc, targetProjectionResult, target, new Date().toISOString())
}

const targetOcclusionRes = viewer.query<IntersectionQuery>({
point: target,
tolerance: 0.001,
operation: 'Occlusion'
})
let isOccluded: boolean | undefined = true
/** If not in camera's frustum don't bother intersecting */
if (inFrustum) {
const targetOcclusionRes = viewer.query<IntersectionQuery>({
point: target,
tolerance: 0.001,
operation: 'Occlusion'
})
isOccluded = !!targetOcclusionRes.objects?.length
}

return {
screenLocation: targetLoc?.x && targetLoc?.y ? targetLoc : null,
isOccluded: !!targetOcclusionRes.objects?.length,
isOccluded,
style: <Partial<CSSProperties>>{
...(targetLoc?.x && targetLoc?.y
? {
Expand Down
42 changes: 34 additions & 8 deletions packages/viewer/src/modules/queries/PointQuerySolver.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Vector3 } from 'three'
import { Frustum, Vector3 } from 'three'
import SpeckleRenderer from '../SpeckleRenderer.js'
import type { PointQuery, PointQueryResult } from './Query.js'
import Logger from '../utils/Logger.js'

const frustumBuf: Frustum = new Frustum()
const vec3Buff: Vector3 = new Vector3()

export class PointQuerySolver {
private renderer!: SpeckleRenderer
private renderer: SpeckleRenderer

public setContext(renderer: SpeckleRenderer) {
this.renderer = renderer
Expand All @@ -25,28 +28,51 @@ export class PointQuerySolver {
private solveProjection(query: PointQuery): PointQueryResult {
// WORLD
const projected = new Vector3(query.point.x, query.point.y, query.point.z)
if (this.renderer.renderingCamera) projected.project(this.renderer.renderingCamera)
else Logger.error('Could not run query. Camera is null')
let inFrustum = false

if (this.renderer.renderingCamera) {
/** We need to check frustum inclusion in view space. */
vec3Buff.copy(projected)
vec3Buff.applyMatrix4(this.renderer.renderingCamera.matrixWorldInverse)

/** We check in-frustum *before* projection */
inFrustum = frustumBuf
.setFromProjectionMatrix(this.renderer.renderingCamera.projectionMatrix)
.containsPoint(vec3Buff)
projected.project(this.renderer.renderingCamera)
} else Logger.error('Could not run query. Camera is null')

return {
// NDC
x: projected.x,
y: projected.y,
z: projected.z
z: projected.z,
inFrustum
}
}

private solveUnprojection(query: PointQuery): PointQueryResult {
// NDC
let inFrustum = false
const unprojected = new Vector3(query.point.x, query.point.y, query.point.z)
if (this.renderer.renderingCamera)
if (this.renderer.renderingCamera) {
unprojected.unproject(this.renderer.renderingCamera)
else Logger.error('Could not run query. Camera is null')

/** We need to check frustum inclusion in view space. */
vec3Buff.copy(unprojected)
vec3Buff.applyMatrix4(this.renderer.renderingCamera.matrixWorldInverse)

/** We check in-frustum *after* projection */
inFrustum = frustumBuf
.setFromProjectionMatrix(this.renderer.renderingCamera.projectionMatrix)
.containsPoint(vec3Buff)
} else Logger.error('Could not run query. Camera is null')
return {
// WORLD
x: unprojected.x,
y: unprojected.y,
z: unprojected.z
z: unprojected.z,
inFrustum
}
}
}
1 change: 1 addition & 0 deletions packages/viewer/src/modules/queries/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface PointQueryResult {
y: number
z?: number
w?: number
inFrustum?: boolean
}

export interface IntersectionQueryResult {
Expand Down

0 comments on commit cff48b9

Please sign in to comment.