Skip to content

Commit

Permalink
perf(3dtiles): fix loading time overhead due to internal structures p…
Browse files Browse the repository at this point in the history
…re-filling

BREAKING CHANGES:
* C3DTFeature constructor parameters changed from
(tileId, batchId, groups, info, userData, object3d) to
(tileId, batchId, groups, userData, object3d)
* C3DTilesLayer.findBatchTable() is not exposed in the API anymore
  • Loading branch information
jailln committed Feb 14, 2024
1 parent c04e784 commit 5d2f384
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 45 deletions.
43 changes: 32 additions & 11 deletions src/Core/3DTiles/C3DTFeature.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
// eslint-disable-next-line no-unused-vars
import { Object3D, Box3 } from 'three';

/**
* Finds the batch table of an object in a 3D Tiles layer. This is
* for instance needed when picking because we pick the geometric
* object which is not at the same level in the layer structure as
* the batch table.
* @param {THREE.Object3D} object - a 3D geometric object
* @returns {C3DTBatchTable|null} - the batch table of the object
*/
function findBatchTable(object) {
if (object.batchTable) {
return object.batchTable;
}
if (object.parent) {
return findBatchTable(object.parent);
}
return null;
}

/**
* C3DTFeature is a feature of a 3DTiles
*
* @class C3DTFeature
* @param {number} tileId - tileId
* @param {number} batchId - batch id
* @param {Array<{start:number,count:number}>} groups - groups in geometry.attributes matching batchId
* @param {object} info - info in the batchTable
* @param {object} [userData] - some userData
* @param {Object3D} object3d - object3d in which feature is present
* @property {number} tileId - tile id
* @property {Object3D} object3d - object3d in which feature is present
* @property {number} batchId - batch id
* @property {Array<{start:number,count:number}>} groups - groups in geometry.attributes matching batchId
* @property {object} info - info in the batchTable
* @property {object} [userData] - some userData
*/
class C3DTFeature {
#info;
constructor(tileId, batchId, groups, info, userData, object3d) {
if (!object3d) {
console.error('BREAKING CHANGE: C3DTFeature constructor changed from (tileId, batchId, groups, info, userData) to (tileId, batchId, groups, info, userData, object3d)');
}

constructor(tileId, batchId, groups, userData, object3d) {
/** @type {Object3D} */
this.object3d = object3d;

Expand All @@ -37,11 +49,11 @@ class C3DTFeature {
/** @type {object} */
this.userData = userData;

/** @type {object} */
this.#info = info;

/** @type {number} */
this.tileId = tileId;

// Lazy-loaded batch table information for this.batchId.
this.#info = null;
}

/**
Expand Down Expand Up @@ -84,10 +96,19 @@ class C3DTFeature {
}

/**
*
* Gets the information from the tile batch table for this C3DTFeature batch id.
* @returns {object} - batchTable info
*/
getInfo() {
if (this.#info) {
return this.#info;
}
const batchTable = findBatchTable(this.object3d);
if (!batchTable) {
console.warn(`[C3DTFeature]: No batch table found for tile ${this.tileId}.`);
return null; // or return undefined;
}
this.#info = batchTable.getInfoById(this.batchId);
return this.#info;
}
}
Expand Down
27 changes: 2 additions & 25 deletions src/Layer/C3DTilesLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,25 +225,6 @@ class C3DTilesLayer extends GeometryLayer {
}
}

/**
* Finds the batch table of an object in a 3D Tiles layer. This is
* for instance needed when picking because we pick the geometric
* object which is not at the same level in the layer structure as
* the batch table. More details here on itowns internal
* organization of 3DTiles:
* https://github.com/MEPP-team/RICT/blob/master/Doc/iTowns/Doc.md#itowns-internal-organisation-of-3d-tiles-data
* @param {THREE.Object3D} object - a 3D geometric object
* @returns {C3DTBatchTable} - the batch table of the object
*/
findBatchTable(object) {
if (object.batchTable) {
return object.batchTable;
}
if (object.parent) {
return this.findBatchTable(object.parent);
}
}

/**
* Get the closest c3DTileFeature of an intersects array.
* @param {Array} intersects - @return An array containing all
Expand Down Expand Up @@ -303,11 +284,6 @@ class C3DTilesLayer extends GeometryLayer {
this.tilesC3DTileFeatures.set(tileContent.tileId, new Map()); // initialize
tileContent.traverse((child) => {
if (object3DHasFeature(child)) {
const batchTable = this.findBatchTable(child);
if (!batchTable) {
throw new Error('no batchTable');
}

const batchIdAttribute = child.geometry.getAttribute('_BATCHID');
let currentBatchId = batchIdAttribute.getX(0);
let start = 0;
Expand All @@ -328,14 +304,15 @@ class C3DTilesLayer extends GeometryLayer {
tileContent.tileId,
currentBatchId,
[{ start, count }], // initialize with current group
batchTable.getInfoById(currentBatchId),
{},
child,
);
this.tilesC3DTileFeatures.get(tileContent.tileId).set(currentBatchId, c3DTileFeature);
}
};

// TODO: Could be simplified by incrementing of 1 and stopping the iteration at positionAttributeSize.count
// See https://github.com/iTowns/itowns/pull/2266#discussion_r1483285122
const positionAttribute = child.geometry.getAttribute('position');
const positionAttributeSize = positionAttribute.count * positionAttribute.itemSize;
for (let index = 0; index < positionAttributeSize; index += positionAttribute.itemSize) {
Expand Down
28 changes: 19 additions & 9 deletions test/unit/3dtilesbatchtable.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import assert from 'assert';
import C3DTBatchTable from 'Core/3DTiles/C3DTBatchTable';
import { obj2ArrayBuff } from './utils';

describe('3D Tiles batch table', function () {
// encode a javascript object into an arraybuffer (based on the 3D Tiles batch table encoding)
function obj2ArrayBuff(obj) {
const objJSON = JSON.stringify(obj);
const encoder = new TextEncoder();
const objUtf8 = encoder.encode(objJSON);
const objUint8 = new Uint8Array(objUtf8);
return objUint8.buffer;
}

it('Should parse JSON batch table from buffer', function () {
const batchTable = {
a1: ['bah', 'tah', 'ratata', 'lo'],
Expand Down Expand Up @@ -54,4 +46,22 @@ describe('3D Tiles batch table', function () {

assert.deepStrictEqual(expectedBatchTable, batchTableObj.content);
});

it('Should get batch table info for a given id', function () {
const batchTableJSON = {
name: ['Ferme', 'Mas des Tourelles', 'Mairie'],
height: [10, 12, 6],
};
const batchTableBuffer = obj2ArrayBuff(batchTableJSON);
const batchTable = new C3DTBatchTable(batchTableBuffer, batchTableBuffer.byteLength, 0, 4, {});

const batchInfo = batchTable.getInfoById(0);
const expectedBatchInfo = {
batchTable: {
name: 'Ferme',
height: 10,
},
};
assert.deepStrictEqual(batchInfo, expectedBatchInfo);
});
});
56 changes: 56 additions & 0 deletions test/unit/3dtilesbatchtablehierarchy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import assert from 'assert';
import C3DTBatchTableHierarchyExtension from 'Core/3DTiles/C3DTBatchTableHierarchyExtension';

const batchTableHierarchyJSON = {
classes: [
{
name: 'Wall',
length: 6,
instances: {
color: ['white', 'red', 'yellow', 'gray', 'brown', 'black'],
},
},
{
name: 'Building',
length: 3,
instances: {
name: ['unit29', 'unit20', 'unit93'],
address: ['100 Main St', '102 Main St', '104 Main St'],
},
},
{
name: 'Owner',
length: 3,
instances: {
type: ['city', 'resident', 'commercial'],
id: [1120, 1250, 6445],
},
},
],
instancesLength: 12,
classIds: [0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2],
parentCounts: [1, 3, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0],
parentIds: [6, 6, 10, 11, 7, 11, 7, 8, 8, 10, 10, 9],
};

const batchTableHierarchy = new C3DTBatchTableHierarchyExtension(batchTableHierarchyJSON);

describe('3D Tiles batch table hierarchy extension', function () {
it('Should get info for a given id', function () {
const expectedInfo = {
Wall: {
color: 'white',
},
Building: {
name: 'unit29',
address: '100 Main St',
},
Owner: {
type: 'resident',
id: 1250,
},
};
const info = batchTableHierarchy.getInfoById(0);
assert.deepStrictEqual(info, expectedInfo);
});
});
28 changes: 28 additions & 0 deletions test/unit/3dtilesfeature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import assert from 'assert';
import * as THREE from 'three';
import C3DTFeature from 'Core/3DTiles/C3DTFeature';
import C3DTBatchTable from 'Core/3DTiles/C3DTBatchTable';
import { obj2ArrayBuff } from './utils';


describe('3D tiles feature', () => {
const obj = new THREE.Object3D();
const batchTableJson = {
id: [12, 158],
height: [13, 22],
};
const batchTableBuffer = obj2ArrayBuff(batchTableJson);
obj.batchTable = new C3DTBatchTable(batchTableBuffer, batchTableBuffer.byteLength, 0, 2, {});
const feature = new C3DTFeature(1, 1, { start: 0, count: 6 }, {}, obj);

it('Get batch table info for feature batch id', function () {
const expectedInfo = {
batchTable: {
id: 158,
height: 22,
},
};
const info = feature.getInfo();
assert.deepStrictEqual(info, expectedInfo);
});
});
9 changes: 9 additions & 0 deletions test/unit/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export function compareArrayWithEpsilon(arr1, arr2, epsilon) {
}
return true;
}

// encode a javascript object into an arraybuffer (based on the 3D Tiles batch table encoding)
export function obj2ArrayBuff(obj) {
const objJSON = JSON.stringify(obj);
const encoder = new TextEncoder();
const objUtf8 = encoder.encode(objJSON);
const objUint8 = new Uint8Array(objUtf8);
return objUint8.buffer;
}

0 comments on commit 5d2f384

Please sign in to comment.