Skip to content

Commit

Permalink
fix: BoundingBox mesh detection (#904)
Browse files Browse the repository at this point in the history
* fix: Update outside layout material

* fix: Boundingbox mesh detection race condition

* fix: Add boundingInfoMesh and implement bounding box generation

* fix: Filter PLAYER_ROOT and CAMERA_ROOT from Metrics component

* refactor: BoundingBox position updates by setting the node as parent
  • Loading branch information
cyaiox authored Feb 26, 2024
1 parent c38196f commit 35a11db
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const IGNORE_MESHES = ['BackgroundHelper', 'BackgroundPlane', 'BackgroundSkybox'

const Metrics = withSdk<WithSdkProps>(({ sdk }) => {
const ROOT = sdk.engine.RootEntity
const PLAYER_ROOT = sdk.engine.PlayerEntity
const CAMERA_ROOT = sdk.engine.CameraEntity
const [showMetrics, setShowMetrics] = React.useState(false)
const [metrics, setMetrics] = React.useState<Metrics>({
triangles: 0,
Expand All @@ -62,10 +64,20 @@ const Metrics = withSdk<WithSdkProps>(({ sdk }) => {

const handleUpdateMetrics = useCallback(() => {
const meshes = sdk.scene.meshes.filter(
(mesh) => !(IGNORE_MESHES.includes(mesh.id) || mesh.id.startsWith(GROUND_MESH_PREFIX))
(mesh) =>
!(
IGNORE_MESHES.includes(mesh.id) ||
mesh.id.startsWith(GROUND_MESH_PREFIX) ||
mesh.id.startsWith('BoundingMesh')
)
)
const triangles = meshes.reduce((acc, mesh) => acc + mesh.getTotalVertices(), 0)
const entities = (sdk.components.Nodes.getOrNull(ROOT)?.value ?? [ROOT]).length - 1
const entities =
(
sdk.components.Nodes.getOrNull(ROOT)?.value.filter(
(node) => ![PLAYER_ROOT, CAMERA_ROOT].includes(node.entity)
) ?? [ROOT]
).length - 1
const uniqueTextures = new Set(
sdk.scene.textures
.filter((texture) => !IGNORE_TEXTURES.includes(texture.name))
Expand Down
62 changes: 43 additions & 19 deletions packages/@dcl/inspector/src/lib/babylon/decentraland/EcsEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ export class EcsEntity extends BABYLON.TransformNode {
usedComponents = new Map<number, ComponentDefinition<unknown>>()
meshRenderer?: BABYLON.AbstractMesh
gltfContainer?: BABYLON.AbstractMesh
boundingInfoMesh?: BABYLON.AbstractMesh
gltfAssetContainer?: BABYLON.AssetContainer
textShape?: BABYLON.Mesh
material?: BABYLON.StandardMaterial | BABYLON.PBRMaterial
#gltfPathLoading?: IFuture<string>
#gltfAssetContainerLoading: IFuture<BABYLON.AssetContainer> = future()
#isLocked?: boolean = false
#assetLoading: IFuture<BABYLON.AbstractMesh> = future()

ecsComponentValues: EcsComponents = {}

Expand Down Expand Up @@ -73,6 +75,9 @@ export class EcsEntity extends BABYLON.TransformNode {
this.deleteComponent(component)
}

// then dispose the boundingInfoMesh if exists
this.boundingInfoMesh?.dispose()

// and then proceed with the native engine disposal
super.dispose(true, false)
}
Expand Down Expand Up @@ -118,6 +123,20 @@ export class EcsEntity extends BABYLON.TransformNode {
this.#gltfAssetContainerLoading.resolve(gltfAssetContainer)
}

setGltfContainer(mesh: BABYLON.AbstractMesh) {
this.gltfContainer = mesh
this.#assetLoading.resolve(mesh)
}

setMeshRenderer(mesh: BABYLON.AbstractMesh) {
this.meshRenderer = mesh
this.#assetLoading.resolve(mesh)
}

onAssetLoaded() {
return this.#assetLoading
}

isHidden() {
const container = this.gltfContainer ?? this.meshRenderer
return container ? !container.isEnabled(false) : false
Expand All @@ -140,10 +159,6 @@ export class EcsEntity extends BABYLON.TransformNode {
}
}

getPickableMesh() {
return this.getChildMeshes(false).find((mesh) => !!mesh.isPickable)
}

isLocked() {
return this.#isLocked
}
Expand All @@ -152,6 +167,19 @@ export class EcsEntity extends BABYLON.TransformNode {
this.#isLocked = lock
}

generateBoundingBox() {
if (this.boundingInfoMesh) return

const meshesBoundingBox = this.getMeshesBoundingBox()

this.boundingInfoMesh = new BABYLON.Mesh(`BoundingMesh-${this.id}`)
this.boundingInfoMesh.parent = this

this.boundingInfoMesh.setBoundingInfo(
new BABYLON.BoundingInfo(meshesBoundingBox.minimum, meshesBoundingBox.maximum, this.getWorldMatrix())
)
}

initEventHandlers() {
if (this.entityId !== this.context.deref()!.engine.RootEntity) {
// Initialize this event to handle the entity's position update
Expand Down Expand Up @@ -192,22 +220,14 @@ export function findParentEntityOfType<T extends EcsEntity>(
return (parent as any as T) || null
}

async function validateEntityIsOutsideLayout(eventData: EcsEntity) {
// When dropping a new entity, waits until the gltf is loaded
if (eventData.isGltfPathLoading()) {
await eventData.onGltfContainerLoaded()
}
// Get the entity's pickable mesh
const mesh = eventData.getPickableMesh()
async function validateEntityIsOutsideLayout(entity: EcsEntity) {
// Waits until the asset is loaded
await entity.onAssetLoaded()
const mesh = entity.boundingInfoMesh
if (mesh) {
// Update the mesh's bounding box visibility
const meshBoundingBox = eventData.getMeshesBoundingBox()
mesh.setBoundingInfo(
new BABYLON.BoundingInfo(meshBoundingBox.minimum, meshBoundingBox.maximum, eventData.getWorldMatrix())
)
mesh.onAfterWorldMatrixUpdateObservable.add((eventMeshData) =>
updateMeshBoundingBoxVisibility(eventData, eventMeshData as BABYLON.AbstractMesh)
)
mesh.onAfterWorldMatrixUpdateObservable.add(() => {
updateMeshBoundingBoxVisibility(entity, mesh)
})
}
}

Expand All @@ -216,11 +236,15 @@ function updateMeshBoundingBoxVisibility(entity: EcsEntity, mesh: BABYLON.Abstra
const { isEntityOutsideLayout } = getLayoutManager(scene)

if (isEntityOutsideLayout(mesh)) {
if (mesh.showBoundingBox) return

for (const childMesh of entity.getChildMeshes(false)) {
addOutsideLayoutMaterial(childMesh, scene)
}
mesh.showBoundingBox = true
} else {
if (!mesh.showBoundingBox) return

for (const childMesh of entity.getChildMeshes(false)) {
removeOutsideLayoutMaterial(childMesh)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ async function tryLoadGltfAsync(sceneId: string, entity: EcsEntity, filePath: st
.filter(($) => $.name === '__root__')
.forEach((mesh) => {
mesh.parent = entity
entity.gltfContainer = mesh
entity.setGltfContainer(mesh)
})

entity.generateBoundingBox()
entity.setGltfAssetContainer(assetContainer)
entity.resolveGltfPathLoading(filePath)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ export const putMeshRendererComponent: ComponentOperation = (entity, component)
}

if (mesh) {
entity.meshRenderer = mesh
entity.meshRenderer.parent = entity
mesh.parent = entity
entity.setMeshRenderer(mesh)
entity.generateBoundingBox()
}

// make the renderer interactable only if the entity is Pickable
Expand Down
1 change: 1 addition & 0 deletions packages/@dcl/inspector/src/lib/babylon/setup/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function setupEngine(engine: BABYLON.Engine, canvas: HTMLCanvasElement, p
// Material for entity outside layout
const entityOutsideLayoutMaterial = new BABYLON.StandardMaterial('entity_outside_layout', scene)
entityOutsideLayoutMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0)
entityOutsideLayoutMaterial.backFaceCulling = false

BABYLON.Database.IDBStorageEnabled = true
engine.enableOfflineSupport = true
Expand Down

0 comments on commit 35a11db

Please sign in to comment.