最近,我收到一个客户的需求,希望可以把 Viewer 的相机状态通过 Revit API 还原到 Revit 里。所以我们来看看要如何实现这个要求。在开始之前,你要先知道一些有关于 Revit 相机的事情:
- Revit 预设的相机 FOV 值大约为 50 度,焦距为 38.6mm,片幅尺寸为 36mm。
- Revit 默认的渲染图片尺寸为 6 英吋。
- 为了调整 Revit 相机的 FOV 值,我们必须利用修改 3D 视图的裁剪尺寸来完成。因为 Revit API 没有直接的方法可以修改相机的 FOV 值。
- Viewer 的相机视角比 Revit 的相机视角宽。
注意:上述关于 Revit 的相机参数皆为我反复测试得出,Revit 没有确切的值,即皆为近似值。
好的,我们转换过程的总思路如下(注意:接下来的步骤适用 透视相机 模式):
-
Forge Viewer 的部分:
-
从当前视图的 Viewer 相机获取焦距,目标,位置和上向量。
- 调用 Viewer3D#getFocalLength 以取得焦距。
-
调用 Viewer3D#getState({viewport: true}) 以取得当前视图必要的相机状态,例如:
{ "viewport": { "name": "","eye": [ -14.870469093323, 36.571562767029, -1.2129259109497 ], "target": [ -14.770469665527, 36.571967124939, -1.2129259109497 ], "up": [ 0, 0, 1 ], "worldUpVector": [ 0, 0, 1 ], "pivotPoint": [ -14.770469665527, 36.571967124939, -1.2129259109497 ], "distanceToOrbit": 0.10000024532334, "aspectRatio": 3.1789297658863, "projection": "perspective", "isOrthographic": false, "fieldOfView": 90.68087674208 } }
-
获取当前加载模型的 global offset(注意:Viewer 默认使用 global offset 来调整加载模型的位置,以避免浮点运算精度和 z-buffer fighting 的问题):
-
调用 viewer.model.getData().globalOffset 取得 global offset 的值,例如:
{ "x": -0.253891, "y": -45.556179, "z": 6.134186 }
-
-
从 Viewer 相机的目标和位置减去 globalOffset:
const state = viewer.getState({viewport: true}); const globalOffset = viewer.model.getData().globalOffset const currentTarget = new THREE.Vector3().fromArray( state.viewport.target); // {x: -14.770469665527344, y: 36.571967124938965, z: -1.212925910949707} const currentPosition = new THREE.Vector3().fromArray( state.viewport.eye); // {x: -14.870469093322754, y: 36.57156276702881, z: -1.212925910949707} const originTarget = currentTarget.clone().add( globalOffset); // {x: -15.02436066552734, y: -8.984211875061035, z: 4.921260089050291} const originPosition = currentPosition.clone().add( globalOffset); // {x: -15.12436009332275, y: -8.984616232971192, z: 4.921260089050291}
-
-
Revit 的部分:
- 在这个部分,我们将利用 Revit 中的裁剪区域和 3D 透视图来完成我们的目标。
-
使用 Viewer 的相机状态计算 Revit 3D 视图方向并创建透视 3D 视图:
// From Forge Viewer //const currentTarget = new THREE.Vector3().fromArray( state.viewport.target); // {x: -14.770469665527344, y: 36.571967124938965, z: -1.212925910949707} //const currentPosition = new THREE.Vector3().fromArray( state.viewport.eye); // {x: -14.870469093322754, y: 36.57156276702881, z: -1.212925910949707} //const originTarget = currentTarget.clone().add( globalOffset); // {x: -15.02436066552734, y: -8.984211875061035, z: 4.921260089050291} //const originPosition = currentPosition.clone().add( globalOffset); // {x: -15.12436009332275, y: -8.984616232971192, z: 4.921260089050291} //const up = new THREE.Vector3().fromArray( state.viewport.up); // {x: 0, y: 0, z: 1} using(var trans = new Transaction(this.Document, "Map LMV Camera")) { try {if(trans.Start() == TransactionStatus.Started) {IEnumerable<ViewFamilyType> viewFamilyTypes = from elem in new FilteredElementCollector(this.Document).OfClass(typeof(ViewFamilyType)) let type = elem as ViewFamilyType where type.ViewFamily == ViewFamily.ThreeDimensional select type; // Create a new Perspective View3D View3D view3D = View3D.CreatePerspective(this.Document, viewFamilyTypes.First().Id); Random rnd = new Random(); view3D.Name = string.Format("Camera{0}", rnd.Next()) ; // By default, the 3D view uses a default orientation. // Change the orientation by creating and setting a ViewOrientation3D var position = new XYZ(-15.12436009332275, -8.984616232971192, 4.921260089050291); var up = new XYZ(0,0,1); var target = new XYZ(-15.02436066552734, -8.984211875061035, 4.921260089050291); var sightDir = target.Subtract(position).Normalize(); var orientation = new ViewOrientation3D(position, up, sightDir); view3D.SetOrientation(orientation); // turn off the far clip plane with standard parameter API Parameter farClip = view3D.LookupParameter("Far Clip Active"); farClip.Set(0); Parameter cropRegionVisible = view3D.LookupParameter("Crop Region Visible"); cropRegionVisible.Set(1); Parameter cropView = view3D.LookupParameter("Crop View"); cropView.Set(1); trans.Commit();} } catch(Exception ex) {trans.RollBack(); TaskDialog.Show("Revit", ex.Message); } }
- 上述程序代码的结果:
- 上述程序代码的结果:
-
使用上述提到经过反复测试而来的相机参数来计算裁剪区域的范围:
- Revit 相机预设的 FOV 值近似 50 度,焦距为 38.6mm,片幅尺寸为 36mm。
- Revit 默认的渲染图片尺寸近似值为 6 吋。
-
因此,裁剪区域的范围计算为:
- 宽度:6″ x 38.6 / 视图的相机焦距。6″ x 38.6 / 12 = 19.3″ = 490.22 mm
-
高度:19.3″ x 常规相机片幅的比例。19.3″ x 2/3 = 12.87″ = 326.898 mm
- 透过 Revit UI 设定 Revit 裁切区域并记住在裁剪区域的对话框中选择 Field of view:
- 这是最终结果:
此篇文章同步发布于 Forge 官方博客 https://forge.autodesk.com/bl…,希望有帮助!