Hi all,
I am current try to port the google 3DTiles service into BJS.
I took a reference from the following GitHub projects.
ebeaufay/threedtiles: 3DTiles viewer for three.js (github.com)
playcanvas/earthatile: Engine-agnostic runtime for 3D Tiles geospatial datasets (github.com)
It is more or less working now. But I am facing extreme low FPS when rendering 3DTiles.
The above two projects in other engine e.g. PlayCanvas and ThreeJS works smoothly. And I cannot find any magic code implemented in these projects. I am new to BJS and JavaScript, thus not very sure about what is the exact issue.
Question 1: Is there any way that tiles can be rendered incrementally without freeze the scene?
The two methods so far I have tried, but with no luck.
Method 1. Set Interval
setInterval(function () { tileRenderer.update(); }, 10);
Method 2. Coroutine (load each tile node in different frame)
function* updateTileManager(tileManager, cameraPos) {
const expandedNodes = Array.from(tileManager.expandedNodes);
for (const node of expandedNodes) {
// If the node is not in range or in view, collapse it
if (!(tileManager.isInView(node) && tileManager.isInRange(node, cameraPos))) {
tileManager.collapseNode(node);
}
if (node.children) {
for (const child of node.children) {
if (child.children && tileManager.isInView(child) && tileManager.isInRange(child, cameraPos)) {
tileManager.expandNode(child).catch(err => console.error(`Error expanding node: ${node.id}`, err)); // load tile content from glb
}
}
}
yield;
}
}
The other question is about the way to handle tile assets. I am currently using AssetContainer to handle each tile resource (glb file). And when the tile is in range, addAllToScene() and when out, removeAllFromScene() will be called (code below). I can experience low FPS with this method (even after all the tiles are downloaded and cached in memory). I also tried using setEnabled() but seems the performance is similar.
Question 2: Is there any suggestion how I can improve the performance? Do i need to split the assetcontainer into different frames as well?
const load = async (node) => {
let container = nodeToEntity.get(node);
if (!container) {
const uri = node.content.uri;
// console.log('LOADING: ’ + uri);
const url = ${this._apiUrl}${uri}?key=${this._apiKey}&session=${this.tileManager.session}
;
try {
container = await BABYLON.SceneLoader.LoadAssetContainerAsync("", url, scene);
container.meshes.forEach(m => {
m.alwaysSelectAsActiveMesh = true
})
container.materials.forEach(m => {
m.freeze();
})
} catch (err) {
console.error("An error occurred while loading the GLB:", err);
}
}
container.addAllToScene();
container.rootNodes[0].parent = tileMapNode;
nodeToEntity.set(node, container);
};
const unload = (node) => {
// console.log('UNLOADING: ' + node.content.uri);
const container = nodeToEntity.get(node);
if (container) {
// container.dispose();
// nodeToEntity.delete(node);
container.rootNodes[0].parent = null;
container.removeAllFromScene();
}
};
const show = (node) => {
// console.log('SHOWING: ' + node.content.uri);
const container = nodeToEntity.get(node);
if (container) {
// container.rootNodes[0].setEnabled(true);
container.addAllToScene(); // entries.rootNodes[0].setEnabled(true);
// container.rootNodes[0].resetLocalMatrix(false);
container.rootNodes[0].parent = tileMapNode;
}
};
const hide = (node) => {
// console.log('HIDING: ' + node.content.uri);
const container = nodeToEntity.get(node);
if (container) {
// container.rootNodes[0].setEnabled(false);
container.rootNodes[0].parent = null;
container.removeAllFromScene();// entries.rootNodes[0].setEnabled(false);
}
};