Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] Create dummy SceneEntity for unused SceneMeshes #1410

Merged
merged 1 commit into from
Mar 13, 2024

Conversation

xeolabs
Copy link
Member

@xeolabs xeolabs commented Mar 13, 2024

This PR changes the way SceneModel handles SceneMesh instances that are not used by any SceneEntity instances.

We cannot have SceneMesh instances that are not used by SceneEntities, because that throws certain renderer counters out of whack.

If we create meshes with SceneModel.createMesh()but don't actually use those meshes with SceneModel.createEntity(), then we end up with internal counters (numVisible, numHighlighted etc) becoming incorrect.

This is because certain internal counts are initialised by SceneModel.createMesh(), independently of whether SceneModel.createEntity() is ever called.

We fixed this in #1336 by making SceneModel buffer the parameters for each SceneModel.createMesh(), without immediately creating the SceneMesh, then created the SceneMesh in SceneModel.createEntity(), only if that SceneMesh was used by the SceneEntity.

This created an undesirable memory spike in SceneModel.finalize() for large models.

We therefore undo that fix (#1336) with this PR and make a better fix that does not create a memory spike.

After this PR

This PR restores SceneModel to its original behaviour of making SceneModel.createMesh() create the SceneMesh immediately, but this time SceneModel.finalize() will create a dummy SceneEntity that owns all unused SceneMesh instances, and logs a warning.

The following example illustrates this.

We create a SceneModel with two extra SceneMeshes, which are copies of one of the yellow table leg and the purple table top, each offset downloads on the Y-axis to separate them visually. A warning is logged in the console, and a dummy SceneEntity is created in the SceneModel to claim them.

Screenshot from 2024-03-13 14-01-47

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();

@xeolabs xeolabs merged commit acdde63 into master Mar 13, 2024
2 of 4 checks passed
@xeolabs xeolabs added this to the 2.5.3 milestone Mar 13, 2024
<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>

Check warning

Code scanning / CodeQL

Inclusion of functionality from an untrusted source Medium test

Script loaded from content delivery network with no integrity check.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant