带 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 中显示房间信息