Skip to content

Commit

Permalink
Merge pull request #1410 from xeokit/remove-deferred-scenemesh-creation
Browse files Browse the repository at this point in the history
[FIX] Create dummy SceneEntity for unused SceneMeshes
  • Loading branch information
xeolabs authored Mar 13, 2024
2 parents 2ffd79d + 017bad9 commit acdde63
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 23 deletions.
180 changes: 180 additions & 0 deletions examples/scenemodel/test_scenemodel_unusedMeshes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xeokit Example</title>
<link href="../css/pageStyle.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>
</head>
<body>
<input type="checkbox" id="info-button"/>
<label for="info-button" class="info-button"><i class="far fa-3x fa-question-circle"></i></label>
<canvas id="myCanvas"></canvas>
<div class="slideout-sidebar">
<img class="info-icon" src="../../assets/images/performance_model_icon.png"/>
<h1>SceneModel</h1>
<h2>Non-realistic rendering, geometry reuse, triangle primitives</h2>
<p>
<a href="../../docs/class/src/viewer/scene/model/SceneModel.js~SceneModel.html"
target="_other">SceneModel</a> is a WebGL2-based <a
href="../../docs/class/src/viewer/scene/model/SceneModel.js~SceneModel.html"
target="_other">SceneModel</a> implementation that stores model geometry as data textures on the GPU.
</p>
<h3>Components Used</h3>
<ul>
<li>
<a href="../../docs/class/src/viewer/Viewer.js~Viewer.html"
target="_other">Viewer</a>
</li>
<li>
<a href="../../docs/class/src/viewer/scene/model/SceneModel.js~SceneModel.html"
target="_other">SceneModel</a>
</li>
</ul>
</div>
</body>

<script type="module">

import {Viewer, SceneModel} from "../../dist/xeokit-sdk.es.js";

const viewer = new Viewer({
canvasId: "myCanvas",
transparent: true,
dtxEnabled: true
});

viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
viewer.scene.camera.look = [0, -5.75, 0];
viewer.scene.camera.up = [0.37, 0.91, -0.11];

const sceneModel = new SceneModel(viewer.scene, {
id: "table",
position: [0, 0, 0],
scale: [1, 1, 1],
rotation: [0, 0, 0],
edges: true,
idModel: true
});

sceneModel.createGeometry({
id: "myBoxGeometry",
primitive: "triangles",
positions: [
1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1,
-1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1,
-1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1
],
indices: [
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9, 10, 8, 10, 11, // top
12, 13, 14, 12, 14, 15, // left
16, 17, 18, 16, 18, 19, // bottom
20, 21, 22, 20, 22, 23
]
});

sceneModel.createMesh({
id: "redLegMesh",
geometryId: "myBoxGeometry",
position: [-4, -6, -4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [1, 0.3, 0.3]
});

sceneModel.createEntity({
id: "redLeg",
meshIds: ["redLegMesh"],
isObject: true
});

sceneModel.createMesh({
id: "greenLegMesh",
geometryId: "myBoxGeometry",
position: [4, -6, -4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [0.3, 1.0, 0.3]
});

sceneModel.createEntity({
id: "greenLeg",
meshIds: ["greenLegMesh"],
isObject: true
});

sceneModel.createMesh({
id: "blueLegMesh",
geometryId: "myBoxGeometry",
position: [4, -6, 4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [0.3, 0.3, 1.0]
});

sceneModel.createEntity({
id: "blueLeg",
meshIds: ["blueLegMesh"],
isObject: true
});

sceneModel.createMesh({
id: "yellowLegMesh",
geometryId: "myBoxGeometry",
position: [-4, -6, 4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [1.0, 1.0, 0.0]
});

sceneModel.createMesh({
id: "yellowLegMeshExcess",
geometryId: "myBoxGeometry",
position: [-4, -16, 4],
scale: [1, 3, 1],
rotation: [0, 0, 0],
color: [1.0, 1.0, 0.0]
});

sceneModel.createEntity({
id: "yellowLeg",
meshIds: ["yellowLegMesh"],
isObject: true
});

sceneModel.createMesh({
id: "purpleTableTopMesh",
geometryId: "myBoxGeometry",
position: [0, -3, 0],
scale: [6, 0.5, 6],
rotation: [0, 0, 0],
color: [1.0, 0.3, 1.0]
});

sceneModel.createMesh({
id: "purpleTableTopMeshExcess",
geometryId: "myBoxGeometry",
position: [0, -13, 0],
scale: [6, 0.5, 6],
rotation: [0, 0, 0],
color: [1.0, 0.3, 1.0]
});

sceneModel.createEntity({
id: "purpleTableTop",
meshIds: ["purpleTableTopMesh"],
isObject: true
});

sceneModel.finalize();




</script>
</html>

51 changes: 28 additions & 23 deletions src/viewer/scene/model/SceneModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,8 @@ export class SceneModel extends Component {
this._vboBatchingLayers = {};
this._dtxLayers = {};

this._meshList = [];

this.layerList = []; // For GL state efficiency when drawing, InstancingLayers are in first part, BatchingLayers are in second
this._entityList = [];

Expand All @@ -1155,11 +1157,9 @@ export class SceneModel extends Component {
this._textureSets = {};
this._transforms = {};
this._meshes = {};
this._unusedMeshes = {};
this._entities = {};

this._scheduledMeshes = {};
this._meshesCfgsBeforeMeshCreation = {};

/** @private **/
this.renderFlags = new RenderFlags();

Expand Down Expand Up @@ -2728,7 +2728,7 @@ export class SceneModel extends Component {
* @param {Number} [cfg.opacity=1] Opacity in range ````[0..1]````. Overridden by texture set ````colorTexture````.
* @param {Number} [cfg.metallic=0] Metallic factor in range ````[0..1]````. Overridden by texture set ````metallicRoughnessTexture````.
* @param {Number} [cfg.roughness=1] Roughness factor in range ````[0..1]````. Overridden by texture set ````metallicRoughnessTexture````.
* @returns {Boolean} True = successfully mesh was created. False = error during creation of a mesh.
* @returns {SceneModelMesh} The new mesh.
*/
createMesh(cfg) {

Expand All @@ -2737,7 +2737,7 @@ export class SceneModel extends Component {
return false;
}

if (this._scheduledMeshes[cfg.id]) {
if (this._meshes[cfg.id]) {
this.error(`[createMesh] SceneModel already has a mesh with this ID: ${cfg.id}`);
return false;
}
Expand Down Expand Up @@ -3057,17 +3057,7 @@ export class SceneModel extends Component {

cfg.numPrimitives = this._getNumPrimitives(cfg);

this._meshesCfgsBeforeMeshCreation[cfg.id] = cfg;

return true;
}

_createDefaultIndices(numIndices) {
const indices = [];
for (let i = 0; i < numIndices; i++) {
indices.push(i);
}
return indices;
return this._createMesh(cfg);
}

_createMesh(cfg) {
Expand Down Expand Up @@ -3099,6 +3089,9 @@ export class SceneModel extends Component {
cfg.meshMatrix = cfg.transform.worldMatrix;
}
mesh.portionId = mesh.layer.createPortion(mesh, cfg);
this._meshes[cfg.id] = mesh;
this._unusedMeshes[cfg.id] = mesh;
this._meshList.push(mesh);
return mesh;
}

Expand Down Expand Up @@ -3459,19 +3452,15 @@ export class SceneModel extends Component {
const meshId = cfg.meshIds[i];
let mesh = this._meshes[meshId]; // Trying to get already created mesh
if (!mesh) { // Checks if there is already created mesh for this meshId
let meshCfg = this._meshesCfgsBeforeMeshCreation[meshId]; // Trying to get already created cfg
if (!meshCfg) { // Checks if there is already created cfg for this meshId
this.error(`Mesh with this ID not found: "${meshId}" - ignoring this mesh`); // There is no such cfg
continue;
}
mesh = this._createMesh(meshCfg) // There is no such mesh yet, but there is already created cfg, so it creates this mesh
this._meshes[cfg.id] = mesh; // Now it will also add this mesh to dictionary of created meshes
this.error(`Mesh with this ID not found: "${meshId}" - ignoring this mesh`); // There is no such cfg
continue;
}
if (mesh.parent) {
this.error(`Mesh with ID "${meshId}" already belongs to object with ID "${mesh.parent.id}" - ignoring this mesh`);
continue;
}
meshes.push(mesh);
delete this._unusedMeshes[meshId];
}
const lodCullable = true;
const entity = new SceneModelEntity(
Expand All @@ -3495,6 +3484,7 @@ export class SceneModel extends Component {
if (this.destroyed) {
return;
}
this._createDummyEntityForUnusedMeshes();
for (let i = 0, len = this.layerList.length; i < len; i++) {
const layer = this.layerList[i];
layer.finalize();
Expand Down Expand Up @@ -3571,6 +3561,21 @@ export class SceneModel extends Component {
}
}

/** @private */
_createDummyEntityForUnusedMeshes() {
const unusedMeshIds = Object.keys(this._unusedMeshes);
if (unusedMeshIds.length > 0) {
const entityId = `${this.id}-dummyEntityForUnusedMeshes`;
this.warn(`Creating dummy SceneModelEntity "${entityId}" for unused SceneMeshes: [${unusedMeshIds.join(",")}]`)
this.createEntity({
id: entityId,
meshIds: unusedMeshIds,
isObject: true
});
}
this._unusedMeshes = {};
}

_getActiveSectionPlanesForLayer(layer) {
const renderFlags = this.renderFlags;
const sectionPlanes = this.scene._sectionPlanesState.sectionPlanes;
Expand Down

0 comments on commit acdde63

Please sign in to comment.