Skip to content

Commit

Permalink
feat(core): allow layer to specify which elements should be updatable
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
peppsac committed Jul 10, 2018
1 parent 0488dcc commit 3d8dfc4
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 93 deletions.
15 changes: 15 additions & 0 deletions src/Core/Layer/Layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {};
}

Expand Down
35 changes: 31 additions & 4 deletions src/Core/MainLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 10 additions & 7 deletions src/Process/3dTilesProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -330,19 +335,19 @@ 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);
}
}
// 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;
}
});
Expand All @@ -351,8 +356,6 @@ export function process3dTilesNode(cullingTest, subdivisionTest) {
}

markForDeletion(layer, node);

return undefined;
};
}

Expand Down
25 changes: 9 additions & 16 deletions src/Process/LayeredMaterialNodeProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 3 additions & 4 deletions src/Process/PointCloudProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -284,15 +283,15 @@ 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);

obj.material.dispose();
obj.geometry.dispose();
obj.material = null;
obj.geometry = null;
obj.owner.obj = null;
obj.userData.metadata.obj = null;

if (__DEBUG__) {
if (obj.boxHelper) {
Expand Down
28 changes: 27 additions & 1 deletion src/Provider/3dTilesProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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)
Expand Down
33 changes: 27 additions & 6 deletions src/Provider/PointCloudProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function parseOctree(layer, hierarchyStepSize, root) {
baseurl: url,
bbox: bounds,
layer,
parent: snode,
};
snode.children.push(item);
stack.push(item);
Expand Down Expand Up @@ -142,6 +143,22 @@ function addPickingAttribute(points) {
return points;
}

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) {
Expand Down Expand Up @@ -175,6 +192,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);

Expand Down Expand Up @@ -234,31 +254,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(layer.parse).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;
});
},
Expand Down
24 changes: 24 additions & 0 deletions test/3dtilesprovider_unit_test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
Loading

0 comments on commit 3d8dfc4

Please sign in to comment.