From b4a880b57dfedce81fc4f65c91a5aa2e379b8317 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 25 Jun 2018 17:55:56 +0200 Subject: [PATCH] feat(core): allow layer to specify which elements should be updatable Some layers use separated objects for metadata and drawable objects. For instance PointCloudProvider or 3dTilesProvider. Attached layers (e.g texture layers) expect to receive a drawable object, in their update function. So this commit adds an optional metadataToElements function to layers, allowing them to returns the to-be-updated object from metadata, and its parent. --- src/Core/Layer/Layer.js | 15 +++ src/Core/MainLoop.js | 35 +++++- src/Process/3dTilesProcessing.js | 17 +-- src/Process/LayeredMaterialNodeProcessing.js | 25 ++--- src/Process/PointCloudProcessing.js | 7 +- src/Provider/3dTilesProvider.js | 28 ++++- src/Provider/PointCloudProvider.js | 33 +++++- test/3dtiles_unit_test.js | 12 +- test/3dtilesprovider_unit_test.js | 24 ++++ test/pointcloudprovider_unit_test.js | 24 +++- utils/debug/3dTilesDebug.js | 110 ++++++++++--------- utils/debug/PointCloudDebug.js | 6 +- 12 files changed, 234 insertions(+), 102 deletions(-) create mode 100644 test/3dtilesprovider_unit_test.js diff --git a/src/Core/Layer/Layer.js b/src/Core/Layer/Layer.js index f6ae6a233a..68f7e5d0f4 100644 --- a/src/Core/Layer/Layer.js +++ b/src/Core/Layer/Layer.js @@ -80,6 +80,21 @@ function GeometryLayer(id, object3d) { // Setup default picking method this.pickObjectsAt = (view, mouse, radius) => Picking.pickObjectsAt(view, mouse, radius, this.object3d); + // Attached layers expect to receive the visual representation of a layer (= THREE object with a material). + // So if a layer's update function don't process this kind of object, the layer must provide + // a getObjectToUpdateForAttachedLayers function that returns the correct object to update for attached + // layer. + // See 3dtilesProvider or PointCloudProvider for examples. + // eslint-disable-next-line arrow-body-style + this.getObjectToUpdateForAttachedLayers = (obj) => { + if (obj.parent && obj.material) { + return { + element: obj, + parent: obj.parent, + }; + } + }; + this.postUpdate = () => {}; } diff --git a/src/Core/MainLoop.js b/src/Core/MainLoop.js index bd7a1f7a3a..d2a36a9bd3 100644 --- a/src/Core/MainLoop.js +++ b/src/Core/MainLoop.js @@ -65,10 +65,37 @@ function updateElements(context, geometryLayer, elements) { // and then update Debug.js:addGeometryLayerDebugFeatures const newElementsToUpdate = geometryLayer.update(context, geometryLayer, element); - // update attached layers - for (const attachedLayer of geometryLayer._attachedLayers) { - if (attachedLayer.ready) { - attachedLayer.update(context, attachedLayer, element); + const sub = geometryLayer.getObjectToUpdateForAttachedLayers(element); + + if (sub) { + if (sub.element) { + if (__DEBUG__) { + if (!(sub.element.isObject3D)) { + throw new Error(` + Invalid object for attached layer to update. + Must be a THREE.Object and have a THREE.Material`); + } + } + // update attached layers + for (const attachedLayer of geometryLayer._attachedLayers) { + if (attachedLayer.ready) { + attachedLayer.update(context, attachedLayer, sub.element, sub.parent); + } + } + } else if (sub.elements) { + for (let i = 0; i < sub.elements.length; i++) { + if (!(sub.elements[i].isObject3D)) { + throw new Error(` + Invalid object for attached layer to update. + Must be a THREE.Object and have a THREE.Material`); + } + // update attached layers + for (const attachedLayer of geometryLayer._attachedLayers) { + if (attachedLayer.ready) { + attachedLayer.update(context, attachedLayer, sub.elements[i], sub.parent); + } + } + } } } updateElements(context, geometryLayer, newElementsToUpdate); diff --git a/src/Process/3dTilesProcessing.js b/src/Process/3dTilesProcessing.js index 18a9be13af..8f78e4b6a6 100644 --- a/src/Process/3dTilesProcessing.js +++ b/src/Process/3dTilesProcessing.js @@ -16,6 +16,11 @@ function requestNewTile(view, scheduler, geometryLayer, metadata, parent, redraw return scheduler.execute(command); } +function getChildTiles(tile) { + // only keep children that have the same layer and a valid tileId + return tile.children.filter(n => n.layer == tile.layer && n.tileId); +} + function subdivideNode(context, layer, node, cullingTest) { if (node.additiveRefinement) { // Additive refinement can only fetch visible children. @@ -88,7 +93,7 @@ function _subdivideNodeAdditive(context, layer, node, cullingTest) { } function _subdivideNodeSubstractive(context, layer, node) { - if (!node.pendingSubdivision && node.children.filter(n => n.layer == layer).length == 0) { + if (!node.pendingSubdivision && getChildTiles(node).length == 0) { const childrenTiles = layer.tileIndex.index[node.tileId].children; if (childrenTiles === undefined || childrenTiles.length === 0) { return; @@ -195,7 +200,7 @@ function cleanup3dTileset(layer, n, depth = 0) { n.parent.remove(n); } } else { - const tiles = n.children.filter(n => n.tileId != undefined); + const tiles = getChildTiles(n); n.remove(...tiles); } } @@ -330,11 +335,11 @@ export function process3dTilesNode(cullingTest, subdivisionTest) { subdivideNode(context, layer, node, cullingTest); // display iff children aren't ready setDisplayed(node, node.pendingSubdivision || node.additiveRefinement); - returnValue = node.children.filter(n => n.layer == layer); + returnValue = getChildTiles(node); } else { setDisplayed(node, true); - for (const n of node.children.filter(n => n.layer == layer)) { + for (const n of getChildTiles(node)) { n.visible = false; markForDeletion(layer, n); } @@ -342,7 +347,7 @@ export function process3dTilesNode(cullingTest, subdivisionTest) { // toggle wireframe if (node.content && node.content.visible) { node.content.traverse((o) => { - if (o.material) { + if (o.layer == layer && o.material) { o.material.wireframe = layer.wireframe; } }); @@ -351,8 +356,6 @@ export function process3dTilesNode(cullingTest, subdivisionTest) { } markForDeletion(layer, node); - - return undefined; }; } diff --git a/src/Process/LayeredMaterialNodeProcessing.js b/src/Process/LayeredMaterialNodeProcessing.js index 9af513f9c5..f7064899f1 100644 --- a/src/Process/LayeredMaterialNodeProcessing.js +++ b/src/Process/LayeredMaterialNodeProcessing.js @@ -140,11 +140,7 @@ function checkNodeElevationTextureValidity(texture, noDataValue) { tData[l - Math.sqrt(l)] > noDataValue; } -export function updateLayeredMaterialNodeImagery(context, layer, node) { - if (!node.parent) { - return; - } - +export function updateLayeredMaterialNodeImagery(context, layer, node, parent) { const material = node.material; // Initialisation @@ -156,10 +152,10 @@ export function updateLayeredMaterialNodeImagery(context, layer, node) { // because even if this tile is outside of the layer, it could inherit it's // parent texture if (!layer.noTextureParentOutsideLimit && - node.parent && - node.parent.material && - node.parent.getIndexLayerColor && - node.parent.getIndexLayerColor(layer.id) >= 0) { + parent && + parent.material && + parent.getIndexLayerColor && + parent.getIndexLayerColor(layer.id) >= 0) { // ok, we're going to inherit our parent's texture } else { node.layerUpdateState[layer.id].noMoreUpdatePossible(); @@ -185,7 +181,7 @@ export function updateLayeredMaterialNodeImagery(context, layer, node) { const sequence = ImageryLayers.getColorLayersIdOrderedBySequence(imageryLayers); material.setSequence(sequence); - initNodeImageryTexturesFromParent(node, node.parent, layer); + initNodeImageryTexturesFromParent(node, parent, layer); } // Proposed new process, two separate processes: @@ -303,10 +299,7 @@ export function updateLayeredMaterialNodeImagery(context, layer, node) { }); } -export function updateLayeredMaterialNodeElevation(context, layer, node) { - if (!node.parent) { - return; - } +export function updateLayeredMaterialNodeElevation(context, layer, node, parent) { // TODO: we need either // - compound or exclusive layers // - support for multiple elevation layers @@ -320,7 +313,7 @@ export function updateLayeredMaterialNodeElevation(context, layer, node) { // Init elevation layer, and inherit from parent if possible if (node.layerUpdateState[layer.id] === undefined) { node.layerUpdateState[layer.id] = new LayerUpdateState(); - initNodeElevationTextureFromParent(node, node.parent, layer); + initNodeElevationTextureFromParent(node, parent, layer); currentElevation = material.getElevationLayerLevel(); const minLevel = layer.options.zoom ? layer.options.zoom.min : 0; if (currentElevation >= minLevel) { @@ -401,7 +394,7 @@ export function updateLayeredMaterialNodeElevation(context, layer, node) { // Quick check to avoid using elevation texture with no data value // If we have no data values, we use value from the parent tile // We should later implement multi elevation layer to choose the one to use at each level - insertSignificantValuesFromParent(terrain.texture, node, node.parent, layer); + insertSignificantValuesFromParent(terrain.texture, node, parent, layer); } node.setTextureElevation(terrain); diff --git a/src/Process/PointCloudProcessing.js b/src/Process/PointCloudProcessing.js index 92d7183873..53df6562d1 100644 --- a/src/Process/PointCloudProcessing.js +++ b/src/Process/PointCloudProcessing.js @@ -206,7 +206,6 @@ export default { layer.group.add(elt.obj); elt.obj.updateMatrixWorld(true); - elt.obj.owner = elt; elt.promise = null; }, (err) => { if (err instanceof CancelledCommandException) { @@ -265,7 +264,7 @@ export default { // This format doesn't require points to be evenly distributed, so // we're going to sort the nodes by "importance" (= on screen size) // and display only the first N nodes - layer.group.children.sort((p1, p2) => p2.owner.sse - p1.owner.sse); + layer.group.children.sort((p1, p2) => p2.userData.metadata.sse - p1.userData.metadata.sse); let limitHit = false; layer.displayedCount = 0; @@ -284,7 +283,7 @@ export default { const now = Date.now(); for (let i = layer.group.children.length - 1; i >= 0; i--) { const obj = layer.group.children[i]; - if (!obj.material.visible && (now - obj.owner.notVisibleSince) > 10000) { + if (!obj.material.visible && (now - obj.userData.metadata.notVisibleSince) > 10000) { // remove from group layer.group.children.splice(i, 1); @@ -292,7 +291,7 @@ export default { obj.geometry.dispose(); obj.material = null; obj.geometry = null; - obj.owner.obj = null; + obj.userData.metadata.obj = null; if (__DEBUG__) { if (obj.boxHelper) { diff --git a/src/Provider/3dTilesProvider.js b/src/Provider/3dTilesProvider.js index 0453882f7c..1d572d83ef 100644 --- a/src/Provider/3dTilesProvider.js +++ b/src/Provider/3dTilesProvider.js @@ -8,7 +8,7 @@ import { init3dTilesLayer } from '../Process/3dTilesProcessing'; import utf8Decoder from '../utils/Utf8Decoder'; export function $3dTilesIndex(tileset, baseURL) { - let counter = 0; + let counter = 1; this.index = {}; const inverseTileTransform = new THREE.Matrix4(); const recurse = function recurse_f(node, baseURL, parent) { @@ -60,9 +60,33 @@ export function $3dTilesIndex(tileset, baseURL) { }; } +export function getObjectToUpdateForAttachedLayers(meta) { + if (meta.content) { + const result = []; + meta.content.traverse((obj) => { + if (obj.isObject3D && obj.material && obj.layer == meta.layer) { + result.push(obj); + } + }); + const p = meta.parent; + if (p && p.content) { + return { + elements: result, + parent: p.content, + }; + } else { + return { + elements: result, + }; + } + } +} + function preprocessDataLayer(layer, view, scheduler) { layer.sseThreshold = layer.sseThreshold || 16; layer.cleanupDelay = layer.cleanupDelay || 1000; + // override the default method, since updated objects are metadata in this case + layer.getObjectToUpdateForAttachedLayers = getObjectToUpdateForAttachedLayers; layer._cleanableTiles = []; return Fetcher.json(layer.url, layer.networkOptions).then((tileset) => { @@ -180,6 +204,8 @@ function executeCommand(command) { const setLayer = (obj) => { obj.layers.set(layer.threejsLayer); + obj.userData.metadata = metadata; + obj.layer = layer; }; if (path) { // Check if we have relative or absolute url (with tileset's lopocs for example) diff --git a/src/Provider/PointCloudProvider.js b/src/Provider/PointCloudProvider.js index 14ca8faa9e..001025302a 100644 --- a/src/Provider/PointCloudProvider.js +++ b/src/Provider/PointCloudProvider.js @@ -93,6 +93,7 @@ function parseOctree(layer, hierarchyStepSize, root) { baseurl: url, bbox: bounds, layer, + parent: snode, }; snode.children.push(item); stack.push(item); @@ -196,6 +197,22 @@ function parseMetadata(metadata, layer) { layer.supportsProgressiveDisplay = customBinFormat; } +export function getObjectToUpdateForAttachedLayers(meta) { + if (meta.obj) { + const p = meta.parent; + if (p && p.obj) { + return { + element: meta.obj, + parent: p.obj, + }; + } else { + return { + element: meta.obj, + }; + } + } +} + export default { preprocessDataLayer(layer, view) { if (!layer.file) { @@ -231,6 +248,9 @@ export default { layer.update = PointCloudProcessing.update; layer.postUpdate = PointCloudProcessing.postUpdate; + // override the default method, since updated objects are metadata in this case + layer.getObjectToUpdateForAttachedLayers = getObjectToUpdateForAttachedLayers; + // this probably needs to be moved to somewhere else layer.pickObjectsAt = (view, mouse, radius) => Picking.pickPointsAt(view, mouse, radius, layer); @@ -253,31 +273,32 @@ export default { executeCommand(command) { const layer = command.layer; - const node = command.requester; + const metadata = command.requester; // Query HRC if we don't have children metadata yet. - if (node.childrenBitField && node.children.length === 0) { - parseOctree(layer, layer.metadata.hierarchyStepSize, node).then(() => command.view.notifyChange(layer, false)); + if (metadata.childrenBitField && metadata.children.length === 0) { + parseOctree(layer, layer.metadata.hierarchyStepSize, metadata).then(() => command.view.notifyChange(layer, false)); } // `isLeaf` is for lopocs and allows the pointcloud server to consider that the current // node is the last one, even if we could subdivide even further. // It's necessary because lopocs doens't know about the hierarchy (it generates it on the fly // when we request .hrc files) - const url = `${node.baseurl}/r${node.name}.${layer.extension}?isleaf=${command.isLeaf ? 1 : 0}`; + const url = `${metadata.baseurl}/r${metadata.name}.${layer.extension}?isleaf=${command.isLeaf ? 1 : 0}`; return Fetcher.arrayBuffer(url, layer.fetchOptions).then(buffer => layer.parse(buffer, layer.metadata.pointAttributes)).then((geometry) => { const points = new THREE.Points(geometry, layer.material.clone()); addPickingAttribute(points); points.frustumCulled = false; points.matrixAutoUpdate = false; - points.position.copy(node.bbox.min); + points.position.copy(metadata.bbox.min); points.scale.set(layer.metadata.scale, layer.metadata.scale, layer.metadata.scale); points.updateMatrix(); points.tightbbox = geometry.boundingBox.applyMatrix4(points.matrix); points.layers.set(layer.threejsLayer); points.layer = layer; - points.extent = Extent.fromBox3(command.view.referenceCrs, node.bbox); + points.extent = Extent.fromBox3(command.view.referenceCrs, metadata.bbox); + points.userData.metadata = metadata; return points; }); }, diff --git a/test/3dtiles_unit_test.js b/test/3dtiles_unit_test.js index 2d0feaacb1..f9672b1051 100644 --- a/test/3dtiles_unit_test.js +++ b/test/3dtiles_unit_test.js @@ -65,7 +65,7 @@ describe('Distance computation using boundingVolume.region', function () { const tileset = tilesetWithRegion(); const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); computeNodeSSE(camera, tile); @@ -78,7 +78,7 @@ describe('Distance computation using boundingVolume.region', function () { const tileset = tilesetWithRegion(m); const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); computeNodeSSE(camera, tile); @@ -99,7 +99,7 @@ describe('Distance computation using boundingVolume.box', function () { const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); computeNodeSSE(camera, tile); @@ -114,7 +114,7 @@ describe('Distance computation using boundingVolume.box', function () { const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); tile.updateMatrixWorld(true); @@ -137,7 +137,7 @@ describe('Distance computation using boundingVolume.sphere', function () { const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); computeNodeSSE(camera, tile); @@ -152,7 +152,7 @@ describe('Distance computation using boundingVolume.sphere', function () { const tileIndex = new $3dTilesIndex(tileset, ''); const tile = new Object3D(); - configureTile(tile, { }, tileIndex.index['0']); + configureTile(tile, { }, tileIndex.index['1']); tile.updateMatrixWorld(true); diff --git a/test/3dtilesprovider_unit_test.js b/test/3dtilesprovider_unit_test.js new file mode 100644 index 0000000000..24045a29e6 --- /dev/null +++ b/test/3dtilesprovider_unit_test.js @@ -0,0 +1,24 @@ +/* global describe, it */ +import assert from 'assert'; +import { Group, Mesh } from 'three'; +import { getObjectToUpdateForAttachedLayers } from '../src/Provider/3dTilesProvider'; + +describe('getObjectToUpdateForAttachedLayers', function () { + it('should correctly return all children', function () { + const layer = { }; + const tile = { + content: new Group(), + layer, + }; + + for (let i = 0; i < 3; i++) { + const mesh = new Mesh(); + mesh.layer = layer; + tile.content.add(mesh); + } + + const result = getObjectToUpdateForAttachedLayers(tile); + assert.ok(Array.isArray(result.elements)); + assert.ok(result.elements.length, 3); + }); +}); diff --git a/test/pointcloudprovider_unit_test.js b/test/pointcloudprovider_unit_test.js index c33b10b5ff..6141311ba5 100644 --- a/test/pointcloudprovider_unit_test.js +++ b/test/pointcloudprovider_unit_test.js @@ -1,7 +1,5 @@ -/* global describe, it */ import assert from 'assert'; -import { _testing } from '../src/Provider/PointCloudProvider'; - +import { getObjectToUpdateForAttachedLayers, _testing } from '../src/Provider/PointCloudProvider'; describe('PointCloudProvider', function () { it('should correctly parse normal information in metadata', function () { @@ -51,3 +49,23 @@ describe('PointCloudProvider', function () { }); }); + +describe('getObjectToUpdateForAttachedLayers', function () { + it('should correctly no-parent for the root', function () { + const meta = { + obj: 'a', + }; + assert.equal(getObjectToUpdateForAttachedLayers(meta).element, 'a'); + }); + it('should correctly return the element and its parent', function () { + const meta = { + obj: 'a', + parent: { + obj: 'b', + }, + }; + const result = getObjectToUpdateForAttachedLayers(meta); + assert.equal(result.element, 'a'); + assert.equal(result.parent, 'b'); + }); +}); diff --git a/utils/debug/3dTilesDebug.js b/utils/debug/3dTilesDebug.js index 7b92572690..b6999ecc00 100644 --- a/utils/debug/3dTilesDebug.js +++ b/utils/debug/3dTilesDebug.js @@ -3,14 +3,20 @@ import OBBHelper from './OBBHelper'; import View from '../../src/Core/View'; import GeometryDebug from './GeometryDebug'; -export default function create3dTilesDebugUI(datDebugTool, view, layer) { - const gui = GeometryDebug.createGeometryDebugUI(datDebugTool, view, layer); +const invMatrixChangeUpVectorZtoY = new THREE.Matrix4().getInverse(new THREE.Matrix4().makeRotationX(Math.PI / 2)); +const invMatrixChangeUpVectorZtoX = new THREE.Matrix4().getInverse(new THREE.Matrix4().makeRotationZ(-Math.PI / 2)); + +export default function create3dTilesDebugUI(datDebugTool, view, _3dTileslayer) { + const gui = GeometryDebug.createGeometryDebugUI(datDebugTool, view, _3dTileslayer); + + const regionBoundingBoxParent = new THREE.Group(); + view.scene.add(regionBoundingBoxParent); // add wireframe - GeometryDebug.addWireFrameCheckbox(gui, view, layer); + GeometryDebug.addWireFrameCheckbox(gui, view, _3dTileslayer); // Bounding box control - const obb_layer_id = `${layer.id}_obb_debug`; + const obb_layer_id = `${_3dTileslayer.id}_obb_debug`; const debugIdUpdate = function debugIdUpdate(context, layer, node) { const enabled = context.camera.camera3D.layers.test({ mask: 1 << layer.threejsLayer }); @@ -18,74 +24,74 @@ export default function create3dTilesDebugUI(datDebugTool, view, layer) { if (!enabled) { return; } - var obbChildren = node.children.filter(n => n.layer == layer); + const metadata = node.userData.metadata; - if (node.visible && node.boundingVolume) { - // 3dTiles case - let helper; - if (obbChildren.length == 0) { + let helper = node.userData.obb; + + if (node.visible && metadata.boundingVolume) { + if (!helper) { // 3dtiles with region - if (node.boundingVolume.region) { - helper = new OBBHelper(node.boundingVolume.region, `id:${node.id}`); - helper.position.copy(node.boundingVolume.region.position); - helper.rotation.copy(node.boundingVolume.region.rotation); - node.add(helper); - helper.layer = layer; - // add the ability to hide all the debug obj for one layer at once - const l = context.view.getLayers(l => l.id === obb_layer_id)[0]; - const l3js = l.threejsLayer; - helper.layers.set(l3js); - helper.children[0].layers.set(l3js); - helper.updateMatrixWorld(); + if (metadata.boundingVolume.region) { + helper = new OBBHelper(metadata.boundingVolume.region, `id:${node.id}`); + helper.position.copy(metadata.boundingVolume.region.position); + helper.rotation.copy(metadata.boundingVolume.region.rotation); + regionBoundingBoxParent.add(helper); } // 3dtiles with box - if (node.boundingVolume.box) { - const size = node.boundingVolume.box.getSize(); + if (metadata.boundingVolume.box) { + const size = metadata.boundingVolume.box.getSize(); const g = new THREE.BoxGeometry(size.x, size.y, size.z); const material = new THREE.MeshBasicMaterial({ wireframe: true }); helper = new THREE.Mesh(g, material); - node.boundingVolume.box.getCenter(helper.position); - node.add(helper); - helper.layer = layer; - // add the ability to hide all the debug obj for one layer at once - const l = context.view.getLayers(l => l.id === obb_layer_id)[0]; - const l3js = l.threejsLayer; - helper.layers.set(l3js); - helper.updateMatrixWorld(); + metadata.boundingVolume.box.getCenter(helper.position); } // 3dtiles with Sphere - if (node.boundingVolume.sphere) { - const geometry = new THREE.SphereGeometry(node.boundingVolume.sphere.radius, 32, 32); + if (metadata.boundingVolume.sphere) { + const geometry = new THREE.SphereGeometry(metadata.boundingVolume.sphere.radius, 32, 32); const material = new THREE.MeshBasicMaterial({ wireframe: true }); helper = new THREE.Mesh(geometry, material); - helper.position.copy(node.boundingVolume.sphere.center); - node.add(helper); + helper.position.copy(metadata.boundingVolume.sphere.center); + } + + if (helper) { helper.layer = layer; // add the ability to hide all the debug obj for one layer at once - const l = context.view.getLayers(l => l.id === obb_layer_id)[0]; - const l3js = l.threejsLayer; + const l3js = layer.threejsLayer; helper.layers.set(l3js); + if (helper.children.length) { + helper.children[0].layers.set(l3js); + } + node.userData.obb = helper; + helper.updateMatrixWorld(); + } + + if (helper && !metadata.boundingVolume.region) { + // compensate B3dm orientation correction + const gltfUpAxis = _3dTileslayer.asset.gltfUpAxis; + helper.updateMatrix(); + if (gltfUpAxis === undefined || gltfUpAxis === 'Y') { + helper.matrix.premultiply(invMatrixChangeUpVectorZtoY); + } else if (gltfUpAxis === 'X') { + helper.matrix.premultiply(invMatrixChangeUpVectorZtoX); + } + helper.applyMatrix(new THREE.Matrix4()); + + node.add(helper); helper.updateMatrixWorld(); } } else { - helper = obbChildren[0]; + helper = node.userData.obb; } if (helper) { helper.visible = true; - for (const child of node.children.filter(n => n.layer == layer)) { - if (typeof child.setMaterialVisibility === 'function') { - child.setMaterialVisibility(true); - } - child.visible = true; + if (typeof helper.setMaterialVisibility === 'function') { + helper.setMaterialVisibility(true); } } - } else { - // hide obb children - for (const child of node.children.filter(n => n.layer == layer)) { - if (typeof child.setMaterialVisibility === 'function') { - child.setMaterialVisibility(false); - } - child.visible = false; + } else if (helper) { + helper.visible = false; + if (typeof helper.setMaterialVisibility === 'function') { + helper.setMaterialVisibility(false); } } }; @@ -96,14 +102,14 @@ export default function create3dTilesDebugUI(datDebugTool, view, layer) { type: 'debug', update: debugIdUpdate, visible: false, - }, layer).then((l) => { + }, _3dTileslayer).then((l) => { gui.add(l, 'visible').name('Bounding boxes').onChange(() => { view.notifyChange(); }); }); // The sse Threshold for each tile - gui.add(layer, 'sseThreshold', 0, 100).name('sseThreshold').onChange(() => { + gui.add(_3dTileslayer, 'sseThreshold', 0, 100).name('sseThreshold').onChange(() => { view.notifyChange(); }); } diff --git a/utils/debug/PointCloudDebug.js b/utils/debug/PointCloudDebug.js index 840b9511c4..6e7da5216d 100644 --- a/utils/debug/PointCloudDebug.js +++ b/utils/debug/PointCloudDebug.js @@ -59,11 +59,11 @@ export default { for (const pts of layer.group.children) { pts.material.visible = false; for (const name of stickies) { - if (pts.owner.name == name) { + if (pts.userData.metadata.name == name) { pts.material.visible = true; - } else if (!isInHierarchy(pts.owner, name)) { + } else if (!isInHierarchy(pts.userData.metadata, name)) { continue; - } else if (pts.owner.name.length < name.length) { + } else if (pts.userData.metadata.name.length < name.length) { pts.material.visible = layer.dbgDisplayParents; break; } else {