"The CropBox.Min and CropBox.Max points are marked in the previous picture. Note that the CropBox.Min point in a perspective view is generated by projecting the crop box front clip
plane onto the back clip plane." - RPTHOMAS108
목표
다른 gui나 pangolin같은 그래픽 편집, 디스플레이 툴에서 화면을 움직이면 레빗에서도 똑같이 화면을 움직이는 기능을 구현하려고 한다.
사용할 기능(추측)
Pangolin에서 마우스로 제어를 하면 Revit에서 움직이게 하려고 한다.
Pangolin에서 전해줄 정보 -------------------> 레빗에서 받는 정보
0. 좌표계의 원점 -----------------------------> 레빗에서의 원점 좌표 (원래 레빗 내부에서 좌표계가 3개가 중첩되어 사용하는 배경으로 인해, 레빗에서 좌표계 변환은 기존에도 존재했다. 그래서 이번에도 해줘야 했다고 생각했다만..)
1. Viewer 좌표 ------------------------------> 레빗에서의 Viewer 좌표
2. Front clipping plane 좌표 ---------------> 레빗에서의 Front clippping plane 좌표
3. Target point -----------------------------> 레빗에서의 Target point
4. CropBox.Max, CropBox.Min -------------> 레빗에서의 CropBox.Max, CropBox.Min
아래 예시와 비교해서 정말로 필요한 정보는 target, position (둘다 global offset이 된 상태라면 원래대로 되돌려야 한다), 그리고 up vector 임을 알았다.
참고할 예시
Forge Viewer 에서 마우스로 제어, 시점이동 --------> 레빗에서 시점 이동
1. 레빗에서 Perspective view3D 생성(카메라 이름, position, up, target)
2. 레빗에서 orientation 3D 생성(position, up, sigitdir>>position에서 구함)
Forge Viewer | Revit |
focal length | |
target | target |
poisition | poisition |
up | up |
target - position = sightdir |
// 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);
}
}
코드 출처: https://forge.autodesk.com/blog/map-forge-viewer-camera-back-revit
Position 구하는 법
slam에서 trajactory 인 것 같다.
target point 구하는 법
레빗에서 마우스로 찍는 target point. 사용자는 target point와 camera position을 따로 조작한다. target point를 건들지 않고 camera poistion만 움직이면 카메라가 target point를 중심으로 빙그르르하게 돈다. 이를 slam으로 가져와서 생각해보면, 카메라의 모든 움직임을 어떤 대상을 바라보고 회전하는 것이라 생각했을때, 그 회전하는 원의 중심이다. 따라서 target point는 항시 변한다. 구하는 법은 모르겠다.
up vector 구하는 법
rotation quaternion에서 revit view 와 up vector 를 뽑는다.
internal static void ToMatrix(Quaternion quaternion, out Transform matrix)
{
matrix = Transform.Identity;
// source -> http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation#Quaternion_to_Matrix
double x2 = quaternion.X * quaternion.X;
double y2 = quaternion.Y * quaternion.Y;
double z2 = quaternion.Z * quaternion.Z;
double xy = quaternion.X * quaternion.Y;
double xz = quaternion.X * quaternion.Z;
double yz = quaternion.Y * quaternion.Z;
double wx = quaternion.W * quaternion.X;
double wy = quaternion.W * quaternion.Y;
double wz = quaternion.W * quaternion.Z;
// This calculation would be a lot more complicated for non-unit length quaternions
// Note: The constructor of Matrix4 expects the Matrix in column-major format like
// expected by OpenGL
// Revit has a 3x3 matrix and vector objects for its columns
// so Matrix.M1? would be Transform.BasisX in Revit
// matrix.M11 = 1.0f - 2.0f * (y2 + z2);
// matrix.M12 = 2.0f * (xy - wz);
// matrix.M13 = 2.0f * (xz + wy);
// matrix.M14 = 0.0f;
XYZ xvec = new XYZ(1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f * (xz + wy));
// matrix.M21 = 2.0f * (xy + wz);
// matrix.M22 = 1.0f - 2.0f * (x2 + z2);
// matrix.M23 = 2.0f * (yz - wx);
// matrix.M24 = 0.0f;
XYZ yvec = new XYZ(2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx));
// matrix.M31 = 2.0f * (xz - wy);
// matrix.M32 = 2.0f * (yz + wx);
// matrix.M33 = 1.0f - 2.0f * (x2 + y2);
// matrix.M34 = 0.0f;
XYZ zvec = new XYZ(2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2));
// the big question now is if we can safely omit this or if this
// will break down the calculations. This column would be the translation
// part of the 4x4 matrix. Note also that the next single return solution
// has this row as the standard (0,0,0,1) that you would expect from a 4x4
// matrix.M41 = 2.0f * (xz - wy);
// matrix.M42 = 2.0f * (yz + wx);
// matrix.M43 = 1.0f - 2.0f * (x2 + y2);
// matrix.M44 = 0.0f;
// return Matrix4( 1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f * (xz + wy), 0.0f,
// 2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f,
// 2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), 0.0f,
// 0.0f, 0.0f, 0.0f, 1.0f)
matrix.BasisX = xvec;
matrix.BasisY = yvec;
matrix.BasisZ = zvec;
}