13 Jun 2019

Map Forge Viewer Camera back to Revit

 

More recently, I received a request from one of the Forge Viewer customers, it's about converting camera state of the Viewer back to the Revit via Revit API. So, I got a chance to make it happen. Before getting started, several things you need to know about the Revit camera model:

  • The Revit default camera has a FOV value which almost equal to 50 in degree, the focal length is 38.6mm and the frame size is 36mm.
  • The Revit default image size from the rendering is 6”
  • To adjust the FOV of Revit camera, we must take advantage of changing the 3D views’ crop size. There is no direct way to modify FOV with the Revit API.
  • The view of the Forge Viewer has a wider camera angle than the Revit's.

Note. The above Revit camera parameters are from my trial-and-error, there are no exact values for Revit, i.e. they are approximate values.

 

Ok, let's start our conversion steps now (Note. The following steps are for the perspective camera mode):

  • From the Forge Viewer part:
    1. Obtain focal length, target, position and up vector from the Viewer camera of the current view.
      • Call Viewer3D#getFocalLength() for the focal length.
      • Call Viewer3D#getState({ viewport: true }) to obtain necessary camera state of current view, for example:
      • {
          "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
          }
        }
    2. Obtain the global offset of the currently loaded model (Note. the Viewer use global offset to adjust the position of the loaded model by default to avoid both issues of floating precision and z-buffer fighting):
      • Call viewer.model.getData().globalOffset for the value, for example:
      • {
          "x": -0.253891,
          "y": -45.556179,
          "z": 6.134186
        }
    3. Subtract the globalOffset from target and position of the Viewer camera:
      • 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}
  • In Revit part:
    1. In this part, we will take advantage of both the crop region and the 3D perspective view in Revit.
    2. Use the Viewer's camera state to calculate Revit 3D view orientation and create a perspective 3D view:
      • // 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);
        	}
        }
      • The result of the above code:
      • LMV-Revit-Mapping-Addin-1st-Resutl
    3. Use the default camera parameters mentioned above (from the trial-and-error) to calculate bounds of the crop region:
      • The Revit approximate default camera has a FOV value which almost equal to 50 in degree, the focal length is 38.6mm and the frame size is 36mm.
      • The Revit approximate default image size from the rendering is 6”
      • Therefore, the bounds of the crop region:
        • Width: 6" x 38.6 / focal length of Viewer camera, 6" x 38.6 / 12 = 19.3" = 490.22 mm
        • Height: 19.3" x the proportions of the regular film camera frame, 19.3" x 2/3 = 12.87" = 326.898 mm
      • Set Revit Crop Region via Revit UI and remember to pick the Field of view inside the Crop region dialog:
      • LMV Revit Edit Crop Region
      • And here is the final result:
      • Changing crop size
      • Forge Viewer view

Enjoy it!

 

Related Article