iTwin / viewer-sample-angular

An Angular implementation of the iTwin Viewer.
MIT License
1 stars 3 forks source link

RotateViewTool returns wrong view state. #17

Open ckddbs opened 1 year ago

ckddbs commented 1 year ago
  1. run "Rotate About Point" command
  2. drag mouse
  3. receive event through Viewport.onViewChanged.
  4. check viewport.view.getRotation() and viewport.view.camera.getEyePoint().

{"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383625,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002074393,"position":[775901.1764567774,83362.04057236538,164.3654347798],"up":[0.31235927889119036,0.5588053242033663,0.7682241147819729],"lookAt":[0.3748345102142041,0.6705724274715027,-0.6401809974276446]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383624,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000003429743,"position":[775901.1847090344,83362.03235651828,164.36543477957613],"up":[0.3097718046542242,0.5602438205842738,0.768224114785784],"lookAt":[0.3717295131658997,0.6722986386865919,-0.6401809974230716]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383625,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002471336,"position":[775901.1925322656,83362.02463750466,164.3654347796171],"up":[0.30732367557008683,0.5615904806055307,0.7682241147785853],"lookAt":[0.3687917317937629,0.6739146452540545,-0.64018099743171]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383625,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002665539,"position":[775901.1999282427,83362.01740163761,164.3654347795625],"up":[0.30501351087785383,0.5628485298974151,0.7682241148091381],"lookAt":[0.3660195092533606,0.6754243180551119,-0.6401809973950462]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383624,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002120981,"position":[775901.2068375256,83362.01069535746,164.3654347797217],"up":[0.30285906599040885,0.5640107229475727,0.7682241147917536],"lookAt":[0.36343415207977453,0.6768189622414763,-0.640180997415908]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383625,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.0000000268122,"position":[775901.2132837774,83362.00448452741,164.36543477946756],"up":[0.30085218909889994,0.5650837722828369,0.7682241148369393],"lookAt":[0.36102587816874726,0.6781066331407499,-0.6401809973616845]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002383625,"position":[775900.1084124747,83363.40633772255,163.19281517634263],"up":[0.23171524210905503,0.09390393836825683,0.968240722616681],"lookAt":[0.8973534444099719,0.3636576587105408,-0.250019805348951]}}} {"operationType":"ITWIN_VIEW_CHANGED","operationArgs":{"cameraParam":{"ortho":false,"fov":90.00000002386126,"position":[775901.2192609733,83361.99876491263,164.3654347795321],"up":[0.29899406086136826,0.5660691308825831,0.7682241148463547],"lookAt":[0.3587961042361057,0.6792890741182718,-0.6401809973503858]}}}

aruniverse commented 1 year ago

What does this mean? How is it "wrong"? What is the text output you've dumped? Please provide relevant code along with your issue.

ckddbs commented 1 year ago
Screenshot 2022-12-20 at 11 19 19 AM
ckddbs commented 1 year ago

For example, above image shows viewport.view.camera.getEyePoint() in onViewChanged() while dragging mouse (= rotating camera). getEyePoint() returns multiple same positions which seem like starting position.

ckddbs commented 1 year ago
  1. Select "Rotate About Point" tool.
  2. Rotate model
  3. Check Camera position in console.

https://github.com/ckddbs/viewer-sample-angular/commit/5031608297b0026c454b4561b59a93350988e7d7

Screenshot 2022-12-20 at 3 44 12 PM
bbastings commented 1 year ago

This is just how this particular tool works, it always applies the rotation from the starting view frustum. So first it calls setupViewFromFrustum to put the view back to it's initial state, then it transforms the frustum and calls setupFromFrustum/setupFromView to apply the new rotation (see commented lines below).

public perform(ptNpc: Point3d): boolean { const tool = this.viewTool; const vp = tool.viewport!;

if (this._anchorPtNpc.isAlmostEqual(ptNpc, 1.0e-2)) // too close to anchor pt
  ptNpc.setFrom(this._anchorPtNpc);

const currentFrustum = vp.getFrustum(CoordSystem.World, false);
const frustumChange = !currentFrustum.equals(this._activeFrustum);
if (frustumChange)
  this._frustum.setFrom(currentFrustum);
else {
  if (!vp.setupViewFromFrustum(this._frustum)) // <- HERE
    return false;
}

const currPt = vp.npcToView(ptNpc);
if (frustumChange)
  this._anchorPtNpc.setFrom(ptNpc);

const view = vp.view;
let angle: Angle;
let worldAxis: Vector3d;
const worldPt = tool.targetCenterWorld;
if (!view.allow3dManipulations()) {
  const centerPt = vp.worldToView(worldPt);
  const firstPt = vp.npcToView(this._anchorPtNpc);
  const vector0 = Vector2d.createStartEnd(centerPt, firstPt);
  const vector1 = Vector2d.createStartEnd(centerPt, currPt);
  angle = vector0.angleTo(vector1);
  worldAxis = Vector3d.unitZ();
} else {
  const viewRect = vp.viewRect;

  vp.npcToView(ptNpc, currPt);
  const firstPt = vp.npcToView(this._anchorPtNpc);

  const xDelta = (currPt.x - firstPt.x);
  const yDelta = (currPt.y - firstPt.y);

  // Movement in screen x == rotation about drawing Z (preserve up) or rotation about screen  Y...
  const xAxis = ToolSettings.preserveWorldUp && !vp.viewingGlobe ? (undefined !== this._depthPoint ? vp.view.getUpVector(this._depthPoint) : Vector3d.unitZ()) : vp.rotation.getRow(1);

  // Movement in screen y == rotation about screen X...
  const yAxis = vp.rotation.getRow(0);

  const xRMatrix = xDelta ? Matrix3d.createRotationAroundVector(xAxis, Angle.createRadians(Math.PI / (viewRect.width / xDelta)))! : Matrix3d.identity;
  const yRMatrix = yDelta ? Matrix3d.createRotationAroundVector(yAxis, Angle.createRadians(Math.PI / (viewRect.height / yDelta)))! : Matrix3d.identity;
  const worldRMatrix = yRMatrix.multiplyMatrixMatrix(xRMatrix);
  const result = worldRMatrix.getAxisAndAngleOfRotation();
  angle = Angle.createRadians(-result.angle.radians);
  worldAxis = result.axis;
}

const worldMatrix = Matrix3d.createRotationAroundVector(worldAxis, angle);
if (undefined !== worldMatrix) {
  const worldTransform = Transform.createFixedPointAndMatrix(worldPt, worldMatrix);
  const frustum = this._frustum.transformBy(worldTransform);
  view.setupFromFrustum(frustum);
  if (view.is3d())
    view.alignToGlobe(view.getCenter());
  this.changeFocusFromDepthPoint(); // if we have a valid depth point, set it focus distance from it
  vp.setupFromView(); // <- HERE
}

vp.getWorldFrustum(this._activeFrustum);
this._lastPtNpc.setFrom(ptNpc);

return true;

}

ckddbs commented 1 year ago

Ok. I'm implementing a feature syncing our (Cupix) view and iTwin's bidirectional. However, syncing is based on that camera change, so other view is oscillating. How can I filter actual (rendered) camera parameter? Or is there another view change event?

bbastings commented 1 year ago

Maybe save the information and do the actual sync for the changed viewports (if any) in ViewManager.onFinishRender?