0

Is there a way to create a SurfaceShadingNode without passing a dbId of existing Room created in Revit?

According to API documentation, I understand that I can create a LevelRoomMap using custom bounds and then use it to generate SurfaceShadingData. For that, I have to restructure my code.

So, I would be grateful if someone could help me with way to define a SurfaceShadingNode with custom bounds.

Thanks! Bandu

Bandu W
  • 99
  • 8
  • 1
    Creating custom SurfaceShadingNode will also require code adjustment, as I know, so I'm afraid that is impossible without changing your code, unless the custom bounds are from the model directly like Revit Rooms. – Eason Kang Aug 22 '22 at 09:16
  • Sure, I will follow that. I was hoping that there is a way to create `new SurfaceShadingNode` by passing bounds instead of 'dbId' (of en existing room) – Bandu W Aug 22 '22 at 09:26

1 Answers1

0

Unfortunately, it is impossible to create SurfaceShadingNode without passing dbIds due to its implementation, but we can use Scene Builder to add custom geometries with the dbId support.

/////////////////////////////////////////////////////////////////////
// Copyright (c) Autodesk, Inc. All rights reserved
// Written by Forge Partner Development
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
/////////////////////////////////////////////////////////////////////

(function () {
    /**
     * Helper of converting THREE.Box3 to THREE.Mesh
     * @class
     */
    class BoxMeshHelper extends THREE.Mesh {
        constructor(box) {
            const geometry = new THREE.BufferGeometry();
            const positionNumComponents = 3;
            const normalNumComponents = 3;
            const uvNumComponents = 2;
            geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(36 * positionNumComponents), positionNumComponents));
            geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(36 * positionNumComponents), normalNumComponents));
            geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(36 * uvNumComponents), uvNumComponents));

            super(geometry, new THREE.MeshPhongMaterial({ color: 0xffff00, side: THREE.DoubleSide, opacity: 0.4, transparent: true }));

            this.type = 'BoxMeshHelper';
            this.box = box;

            this.positionNumComponents = 3;
            this.normalNumComponents = 3;
            this.uvNumComponents = 2;

            this.update();
        }

        update() {
            const box = this.box;

            if (box.isEmpty()) return;

            const min = box.min;
            const max = box.max;

            /*
              5____4
            1/___0/|
            | 6__|_7
            2/___3/
    
            0: max.x, max.y, max.z
            1: min.x, max.y, max.z
            2: min.x, min.y, max.z
            3: max.x, min.y, max.z
            4: max.x, max.y, min.z
            5: min.x, max.y, min.z
            6: min.x, min.y, min.z
            7: max.x, min.y, min.z
            */

            const vertices = [
                // front
                { pos: [min.x, min.y, max.z], norm: [0, 0, 1], uv: [0, 1], },
                { pos: [max.x, min.y, max.z], norm: [0, 0, 1], uv: [1, 1], },
                { pos: [min.x, max.y, max.z], norm: [0, 0, 1], uv: [0, 0], },

                { pos: [min.x, max.y, max.z], norm: [0, 0, 1], uv: [0, 0], },
                { pos: [max.x, min.y, max.z], norm: [0, 0, 1], uv: [1, 1], },
                { pos: [max.x, max.y, max.z], norm: [0, 0, 1], uv: [1, 0], },
                // right
                { pos: [max.x, min.y, max.z], norm: [1, 0, 0], uv: [0, 1], },
                { pos: [max.x, min.y, min.z], norm: [1, 0, 0], uv: [1, 1], },
                { pos: [max.x, max.y, max.z], norm: [1, 0, 0], uv: [0, 0], },

                { pos: [max.x, max.y, max.z], norm: [1, 0, 0], uv: [0, 0], },
                { pos: [max.x, min.y, min.z], norm: [1, 0, 0], uv: [1, 1], },
                { pos: [max.x, max.y, min.z], norm: [1, 0, 0], uv: [1, 0], },
                // back
                { pos: [max.x, min.y, min.z], norm: [0, 0, -1], uv: [0, 1], },
                { pos: [min.x, min.y, min.z], norm: [0, 0, -1], uv: [1, 1], },
                { pos: [max.x, max.y, min.z], norm: [0, 0, -1], uv: [0, 0], },

                { pos: [max.x, max.y, min.z], norm: [0, 0, -1], uv: [0, 0], },
                { pos: [min.x, min.y, min.z], norm: [0, 0, -1], uv: [1, 1], },
                { pos: [min.x, max.y, min.z], norm: [0, 0, -1], uv: [1, 0], },
                // left
                { pos: [min.x, min.y, min.z], norm: [-1, 0, 0], uv: [0, 1], },
                { pos: [min.x, min.y, max.z], norm: [-1, 0, 0], uv: [1, 1], },
                { pos: [min.x, max.y, min.z], norm: [-1, 0, 0], uv: [0, 0], },

                { pos: [min.x, max.y, min.z], norm: [-1, 0, 0], uv: [0, 0], },
                { pos: [min.x, min.y, max.z], norm: [-1, 0, 0], uv: [1, 1], },
                { pos: [min.x, max.y, max.z], norm: [-1, 0, 0], uv: [1, 0], },
                // top
                { pos: [max.x, max.y, min.z], norm: [0, 1, 0], uv: [0, 1], },
                { pos: [min.x, max.y, min.z], norm: [0, 1, 0], uv: [1, 1], },
                { pos: [max.x, max.y, max.z], norm: [0, 1, 0], uv: [0, 0], },

                { pos: [max.x, max.y, max.z], norm: [0, 1, 0], uv: [0, 0], },
                { pos: [min.x, max.y, min.z], norm: [0, 1, 0], uv: [1, 1], },
                { pos: [min.x, max.y, max.z], norm: [0, 1, 0], uv: [1, 0], },
                // bottom
                { pos: [max.x, min.y, max.z], norm: [0, -1, 0], uv: [0, 1], },
                { pos: [min.x, min.y, max.z], norm: [0, -1, 0], uv: [1, 1], },
                { pos: [max.x, min.y, min.z], norm: [0, -1, 0], uv: [0, 0], },

                { pos: [max.x, min.y, min.z], norm: [0, -1, 0], uv: [0, 0], },
                { pos: [min.x, min.y, max.z], norm: [0, -1, 0], uv: [1, 1], },
                { pos: [min.x, min.y, min.z], norm: [0, -1, 0], uv: [1, 0], },
            ];

            const positions = [];
            const normals = [];
            const uvs = [];
            for (const vertex of vertices) {
                positions.push(...vertex.pos);
                normals.push(...vertex.norm);
                uvs.push(...vertex.uv);
            }

            this.geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(positions), this.positionNumComponents);
            this.geometry.attributes.normal = new THREE.BufferAttribute(new Float32Array(normals), this.normalNumComponents);
            this.geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array(uvs), this.uvNumComponents);

            this.geometry.attributes.position.needsUpdate = true;
            this.geometry.attributes.normal.needsUpdate = true;
            this.geometry.attributes.uv.needsUpdate = true;

            this.geometry.computeBoundingSphere();
        }
    }

    class DataVizCustomBoundsExt extends Autodesk.Viewing.Extension {
        constructor(viewer, options) {
            super(viewer, options);

            this.dbIdPrefix = 1000;
        }

        get dataVizExt() {
            return this.viewer.getExtension('Autodesk.DataVisualization');
        }

        get sceneBuilderExt() {
            return this.viewer.getExtension('Autodesk.Viewing.SceneBuilder');
        }

        async renderBounds(bounds) {
            if (!(bounds instanceof THREE.Box3)) return;

            let mesh = new BoxMeshHelper(bounds);
            let boundMesh = new THREE.Mesh(
                mesh.geometry.clone(),
                this.boundsMaterial
            );
            boundMesh.dbId = ++this.dbIdPrefix;

            this.modelBuilder.addMesh(
                boundMesh
            );

            const shadingGroup = new Autodesk.DataVisualization.Core.SurfaceShadingGroup('Bounds');

            const boundNode = new Autodesk.DataVisualization.Core.SurfaceShadingNode(`Bound ${boundMesh.dbId}`, boundMesh.dbId);
            boundNode.addPoint(
                new Autodesk.DataVisualization.Core.SurfaceShadingPoint('Bound sensor', bounds.center(), ['Temperature'])
            );

            shadingGroup.addChild(boundNode);

            const heatmapData = new Autodesk.DataVisualization.Core.SurfaceShadingData();
            heatmapData.addChild(shadingGroup);

            // Initialize with model loaded from forge
            let model = this.modelBuilder.model;
            heatmapData.initialize(model);
            await this.dataVizExt.setupSurfaceShading(model, heatmapData);

            this.dataVizExt.renderSurfaceShading('Bounds', 'Temperature', this.getSensorValue);
        }

        getSensorValue() {
            return Math.random();
        }

        async load() {
            await Promise.all([
                this.viewer.loadExtension('Autodesk.Viewing.SceneBuilder'),
                this.viewer.loadExtension('Autodesk.DataVisualization'),
            ]);

            await this.viewer.waitForLoadDone();

            this.modelBuilder = await this.sceneBuilderExt.addNewModel({
                conserveMemory: false,
                modelNameOverride: 'Custom Bounds',
                loadAsHidden: true
            });

            const matName = 'bounds-mat';
            const boundsMat = new THREE.MeshPhongMaterial({ color: 0xffff00, side: THREE.DoubleSide, opacity: 0.4, transparent: true });
            this.modelBuilder.addMaterial(matName, boundsMat);
            this.boundsMaterial = this.modelBuilder.findMaterial(matName);

            return true;
        }

        unload() {
            this.dataVizExt.removeSurfaceShading(this.modelBuilder.model);
            this.viewer.unloadModel(this.modelBuilder.model);
            return true;
        }
    }

    Autodesk.Viewing.theExtensionManager.registerExtension('Autodesk.ADN.DataVizCustomBoundsExt', DataVizCustomBoundsExt);
})();


const dataVizCustomBoundsExt = await viewer.loadExtension('Autodesk.ADN.DataVizCustomBoundsExt');

// Custom bounds
let bounds = new THREE.Box3(new THREE.Vector3(-87.49999999999999, -123.7500076293945, -29.434711456298828), new THREE.Vector3(0, -0.0000018146972706745137, 0));

dataVizCustomBoundsExt.renderBounds(bounds);

Demo: https://youtu.be/O_itfTAvfrQ?t=11

enter image description here

Eason Kang
  • 6,155
  • 1
  • 7
  • 24