带 Forge 的房间

Posted

技术标签:

【中文标题】带 Forge 的房间【英文标题】:Rooms with Forge 【发布时间】:2021-12-23 22:27:32 【问题描述】:

我的目标是在 Forge 查看器中查看 Revit 房间。该应用程序位于 .NET Core 中。我已经尝试实现 GenerateMasterViews。 我用来实现这一点的代码是:

        [Route("api/forge/modelderivative/jobs")]
        public async Task<dynamic> TranslateObject([FromBody]TranslateObjectModel objModel)
        
            dynamic oauth = await OAuthController.GetInternalAsync();

            // prepare the payload

            var advOutputPayload = new JobSvf2OutputPayloadAdvanced();
            advOutputPayload.GenerateMasterViews = true;

            List<JobPayloadItem> outputs = new List<JobPayloadItem>()
            
            new JobPayloadItem(
                JobPayloadItem.TypeEnum.Svf2,
                new List<JobPayloadItem.ViewsEnum>()
                
                JobPayloadItem.ViewsEnum._2d,
                JobPayloadItem.ViewsEnum._3d
                ,
                advOutputPayload
                )
            ;
            JobPayload job;
            job = new JobPayload(new JobPayloadInput(objModel.objectName), new JobPayloadOutput(outputs));

            // start the translation
            DerivativesApi derivative = new DerivativesApi();
            derivative.Configuration.AccessToken = oauth.access_token;
            dynamic jobPosted = await derivative.TranslateAsync(job);
            return jobPosted;
        
Autodesk.Viewing.Initializer(options, () => 
    viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer'));
    viewer.start();
    var documentId = 'urn:' + urn;
    Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
  );


function onDocumentLoadSuccess(doc) 
  var viewables = doc.getRoot().getDefaultGeometry();
  viewer.loadDocumentNode(doc, viewables).then(i => 
    // documented loaded, any action?
  );

但我无法让它工作。

我已查找信息,但此网址:https://forge.autodesk.com/en/docs/model-derivative/v2/tutorials/prep-roominfo4viewer/option2/ 和此网址: https://forge.autodesk.com/en/docs/model-derivative/v2/tutorials/prep-roominfo4viewer/option1/ 他们不工作,我不知道该怎么做。


【问题讨论】:

我通过这个实现了它:getDefaultGeometry (true)。现在我要研究如何知道房间里有什么物品。 【参考方案1】:

要检查对象是否在房间内,我们可以执行以下操作:

    获取每个房间和对象的边界
getBoundingBox(dbId, model) 
    const it = model.getInstanceTree();
    const fragList = model.getFragmentList();
    let bounds = new THREE.Box3();

    it.enumNodeFragments(dbId, (fragId) => 
        let box = new THREE.Box3();
        fragList.getWorldBounds(fragId, box);
        bounds.union(box);
    , true);

    return bounds;

    迭代房间和对象,并使用containsBox 或containsPoint 检查它们的边界框是否有交集。

    如果你想做一个急性碰撞检查,你可以利用ThreeCSG.js 做几何相交。这是一篇博文,展示了如何将 ThreeCSG.js 与 Forge Viewer 集成。

https://forge.autodesk.com/blog/boolean-operations-forge-viewer

注意。此过程会降低查看器的性能,因为 javascript 在 Web 浏览器的单线程上运行,因此您可以使用 web worker 等技术在单独的线程。

更新:

这是一个展示上述想法的工作示例扩展:

/////////////////////////////////////////////////////////////////////
// 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 () 
    const Utility = 
        /**
         * Rest an object
         * @param Object obj An object to be reset.
         * ref: https://***.com/a/24090180
         */
        resetObject: function (obj) 
            for (let key in Object.getOwnPropertyNames(obj)) 
                if (!obj.hasOwnProperty(key)) continue;

                let val = obj[key];
                switch (typeof val) 
                    case 'string':
                        obj[key] = ''; break;
                    case 'number':
                        obj[key] = 0; break;
                    case 'boolean':
                        obj[key] = false; break;
                    case 'object':
                        if (val === null) break;
                        if (val instanceof Array) 
                            while (obj[key].length > 0) 
                                obj[key].pop();
                            
                            break;
                        
                        val = ;
                        //Or recursively clear the sub-object
                        //resetObject(val);
                        break;
                
            
        
    ;

    /**
     * A Forge Viewer extension for loading and rendering Revit Grids by AEC Model Data
     * @class
     */
    class RoomLocatorExtension extends Autodesk.Viewing.Extension 
        constructor(viewer, options) 
            super(viewer, options);

            this.roomCategoryName = options.roomCategoryName || 'Revit Rooms';//'Revit Habitaciones'
            this.onContextMenu = this.onContextMenu.bind(this);
        

        onContextMenu(menu, status) 
            if (status.hasSelected) 
                menu.push(
                    title: 'Find room',
                    target: async () => 
                        let selSet = this.viewer.getSelection();
                        this.viewer.clearSelection();

                        const roomDbIds = await this.locateElementByRoom(selSet[0]);
                        if (!roomDbIds || roomDbIds.length <= 0) return;

                        this.viewer.select(roomDbIds);
                    
                );
            
        

        async getPropertiesAsync(dbId, model) 
            return new Promise((resolve, reject) => 
                model.getProperties2(
                    dbId,
                    (result) => resolve(result),
                    (error) => reject(error)
                );
            );
        

        async getElementsByCategoryAsync(category) 
            return new Promise((resolve, reject) => 
                this.viewer.search(
                    category,
                    (dbIds) => resolve(dbIds),
                    (error) => reject(error),
                    ['Category'],
                     searchHidden: true 
                );
            );
        

        async getRoomDbIds() 
            try 
                const roomDbIds = await this.getElementsByCategoryAsync(this.roomCategoryName);
                if (!roomDbIds || roomDbIds.length <= 0) 
                    throw new Error('No Rooms found in current model');
                

                return roomDbIds;
             catch (ex) 
                console.warn(`[RoomLocatorExtension]: $ex`);
                throw new Error('No room found');
            
        

        getBoundingBox(dbId, model) 
            const it = model.getInstanceTree();
            const fragList = model.getFragmentList();
            let bounds = new THREE.Box3();

            it.enumNodeFragments(dbId, (fragId) => 
                let box = new THREE.Box3();
                fragList.getWorldBounds(fragId, box);
                bounds.union(box);
            , true);

            return bounds;
        

        getLeafFragIds(model, leafId) 
            const instanceTree = model.getData().instanceTree;
            const fragIds = [];

            instanceTree.enumNodeFragments(leafId, function (fragId) 
                fragIds.push(fragId);
            );

            return fragIds;
        

        getComponentGeometryInfo(dbId, model) 
            const viewer = this.viewer;
            const viewerImpl = viewer.impl;
            const fragIds = this.getLeafFragIds(model, dbId);
            let matrixWorld = null;

            const meshes = fragIds.map((fragId) => 
                const renderProxy = viewerImpl.getRenderProxy(model, fragId);

                const geometry = renderProxy.geometry;
                const attributes = geometry.attributes;
                const positions = geometry.vb ? geometry.vb : attributes.position.array;

                const indices = attributes.index.array || geometry.ib;
                const stride = geometry.vb ? geometry.vbstride : 3;
                const offsets = geometry.offsets;

                matrixWorld = matrixWorld || renderProxy.matrixWorld.elements;

                return 
                    positions,
                    indices,
                    offsets,
                    stride
                ;
            );

            return 
                matrixWorld,
                meshes
            ;
        

        getComponentGeometry(data, vertexArray) 
            const offsets = [
                
                    count: data.indices.length,
                    index: 0,
                    start: 0
                
            ];

            for (let oi = 0, ol = offsets.length; oi < ol; ++oi) 
                let start = offsets[oi].start;
                let count = offsets[oi].count;
                let index = offsets[oi].index;

                for (let i = start, il = start + count; i < il; i += 3) 
                    const a = index + data.indices[i];
                    const b = index + data.indices[i + 1];
                    const c = index + data.indices[i + 2];

                    const vA = new THREE.Vector3();
                    const vB = new THREE.Vector3();
                    const vC = new THREE.Vector3();

                    vA.fromArray(data.positions, a * data.stride);
                    vB.fromArray(data.positions, b * data.stride);
                    vC.fromArray(data.positions, c * data.stride);

                    vertexArray.push(vA);
                    vertexArray.push(vB);
                    vertexArray.push(vC);
                
            
        

        buildComponentMesh(data) 
            const vertexArray = [];

            for (let idx = 0; idx < data.nbMeshes; ++idx) 
                const meshData = 
                    positions: data['positions' + idx],
                    indices: data['indices' + idx],
                    stride: data['stride' + idx]
                

                this.getComponentGeometry(meshData, vertexArray);
            

            const geometry = new THREE.Geometry();

            for (let i = 0; i < vertexArray.length; i += 3) 
                geometry.vertices.push(vertexArray[i]);
                geometry.vertices.push(vertexArray[i + 1]);
                geometry.vertices.push(vertexArray[i + 2]);

                const face = new THREE.Face3(i, i + 1, i + 2);
                geometry.faces.push(face);
            

            const matrixWorld = new THREE.Matrix4();
            matrixWorld.fromArray(data.matrixWorld);

            const mesh = new THREE.Mesh(geometry);
            mesh.applyMatrix(matrixWorld);
            mesh.boundingBox = data.boundingBox;
            mesh.bsp = new ThreeBSP(mesh)
            mesh.dbId = data.dbId;

            return mesh;
        

        buildCsgMesh(dbId, model) 
            const geometry = this.getComponentGeometryInfo(dbId, model);
            const data = 
                boundingBox: this.getBoundingBox(dbId, model),
                matrixWorld: geometry.matrixWorld,
                nbMeshes: geometry.meshes.length,
                dbId
            ;

            geometry.meshes.forEach((mesh, idx) => 
                data['positions' + idx] = mesh.positions;
                data['indices' + idx] = mesh.indices;
                data['stride' + idx] = mesh.stride;
            );

            return this.buildComponentMesh(data);
        

        async buildBBoxes() 
            try 
                const model = this.viewer.model;
                const roomBBoxes = ;
                const roomDbIds = await this.getRoomDbIds();
                for (let i = 0; i < roomDbIds.length; i++) 
                    let dbId = roomDbIds[i];
                    let bbox = await this.getBoundingBox(dbId, model);
                    roomBBoxes[dbId] = bbox;
                
                this.cachedBBoxes['rooms'] = roomBBoxes;
             catch (ex) 
                console.warn(`[RoomLocatorExtension]: $ex`);
                throw new Error('Cannot build bounding boxes from rooms');
            
        

        async locateElementByRoom(dbId) 
            let bbox = await this.getBoundingBox(dbId, this.viewer.model);
            const roomDbIds = Object.keys(this.cachedBBoxes['rooms']);
            const roomBoxes = Object.values(this.cachedBBoxes['rooms']);

            // Coarse Phase Collision
            const coarseResult = [];
            for (let i = 0; i < roomDbIds.length; i++) 
                let roomDbId = roomDbIds[i];
                let roomBox = roomBoxes[i];

                if (roomBox.containsBox(bbox)) 
                    coarseResult.push(parseInt(roomDbId));
                 else 
                    if (roomBox.containsPoint(bbox.min) || roomBox.containsPoint(bbox.max) || roomBox.containsPoint(bbox.center())) 
                        coarseResult.push(parseInt(roomDbId));
                    
                
            

            // Fine Phase Collision
            const fineResult = [];
            let elementCsgMesh = this.buildCsgMesh(dbId, this.viewer.model);

            for (let i = 0; i < coarseResult.length; i++) 
                let roomDbId = coarseResult[i];
                let roomCsgMesh = this.buildCsgMesh(roomDbId, this.viewer.model);

                let result = elementCsgMesh.bsp.intersect(roomCsgMesh.bsp);
                if (result.tree.polygons.length <= 0) 
                    result = roomCsgMesh.bsp.intersect(elementCsgMesh.bsp);

                    // if (!this.viewer.overlays.hasScene('csg'))
                    //     this.viewer.overlays.addScene('csg');
                    // else
                    //     this.viewer.overlays.clearScene('csg');

                    // let mat = new THREE.MeshBasicMaterial( color: 'red' )
                    // let mesh = result.toMesh(mat);
                    // this.viewer.overlays.addMesh(mesh, 'csg')

                    if (result.tree.polygons.length <= 0) continue;
                

                fineResult.push(roomDbId);
            

            return fineResult;
        

        async load() 
            await Autodesk.Viewing.Private.theResourceLoader.loadScript(
                'https://cdn.jsdelivr.net/gh/Wilt/ThreeCSG@develop/ThreeCSG.js',
                'ThreeBSP'
            );

            if (!window.ThreeBSP)
                throw new Error('Cannot load ThreeCSG.js, please download a copy from https://github.com/Wilt/ThreeCSG/blob/develop/ThreeCSG.js')

            await this.viewer.waitForLoadDone();

            this.cachedBBoxes = ;
            await this.buildBBoxes();

            this.viewer.registerContextMenuCallback(
                'RoomLocatorExtension',
                this.onContextMenu
            );

            return true;
        

        unload() 
            Utility.resetObject(this.cachedBBoxes);
            this.viewer.unregisterContextMenuCallback(
                'RoomLocatorExtension',
                this.onContextMenu
            );

            return true;
        
    

    Autodesk.Viewing.theExtensionManager.registerExtension('RoomLocatorExtension', RoomLocatorExtension);
)();

快照:

【讨论】:

我正在尝试使用 ThreeCSG.js 的选项,因为我尝试了使用边界框的选项,但它不适用于非矩形的房间。视图的每个元素我都需要知道它属于哪个房间。你能给我提供更多信息吗?我看过博客和 ThreeBSP,但我不太清楚如何将它应用到我的具体案例中。谢谢

以上是关于带 Forge 的房间的主要内容,如果未能解决你的问题,请参考以下文章

Forge 数据可视化不适用于 Revit 房间 [ITA]

尝试在 Autodesk Forge / Bim360 中显示房间信息

如何在 Forge Autodesk 中仅显示特定视图

Dashboard.js Autodesk Forge

如何在 Forge Viewer 中使 Revit Room 可点击?

Autodesk forge 突出显示子对象