diff --git a/examples/debug/fisheye.html b/examples/debug/fisheye.html
index 47be0f75..a6841c9a 100644
--- a/examples/debug/fisheye.html
+++ b/examples/debug/fisheye.html
@@ -91,7 +91,7 @@
function generateChunk() {
const cameraType = CAMERA_TYPE_FISHEYE;
- const height = 2495;
+ const height = 2496;
const width = 2496;
const focal = 0.2212;
const k1 = 0.1282;
diff --git a/src/component/bearing/BearingComponent.ts b/src/component/bearing/BearingComponent.ts
index 115041a3..212ca65c 100644
--- a/src/component/bearing/BearingComponent.ts
+++ b/src/component/bearing/BearingComponent.ts
@@ -473,9 +473,9 @@ export class BearingComponent extends Component {
transform, vertices, directions, pointsPerLine, this._viewportCoords);
const projections = bearings
.map(b => this._spatial.projectToPlane(b, [0, 1, 0]))
- .map(p => [p[0], p[2]]);
+ .map(p => [p[0], -p[2]]);
- const angles = projections.map(p => Math.atan2(p[0], -p[1]));
+ const angles = projections.map(p => Math.abs(Math.atan2(p[0], p[1])));
const fov = 2 * Math.min(...angles);
return fov;
diff --git a/src/geo/Geo.ts b/src/geo/Geo.ts
index 1ae63036..e6e1a300 100644
--- a/src/geo/Geo.ts
+++ b/src/geo/Geo.ts
@@ -43,8 +43,23 @@ export function computeBearings(
camera.up.copy(transform.upVector());
camera.position.copy(new THREE.Vector3().fromArray(transform.unprojectSfM([0, 0], 0)));
camera.lookAt(new THREE.Vector3().fromArray(transform.unprojectSfM([0, 0], 10)));
- camera.updateMatrix();
- camera.updateMatrixWorld(true);
+
+ return computeCameraBearings(
+ camera,
+ transform,
+ basicVertices,
+ basicDirections,
+ pointsPerLine,
+ viewportCoords);
+}
+
+export function computeCameraBearings(
+ camera: THREE.Camera,
+ transform: Transform,
+ basicVertices: number[][],
+ basicDirections: number[][],
+ pointsPerLine: number,
+ viewportCoords: ViewportCoords): number[][] {
const basicPoints: number[][] = [];
for (let side: number = 0; side < basicVertices.length; ++side) {
@@ -59,8 +74,10 @@ export function computeBearings(
}
}
+ camera.updateMatrix();
+ camera.updateMatrixWorld(true);
const bearings: number[][] = [];
- for (const [index, basicPoint] of basicPoints.entries()) {
+ for (const basicPoint of basicPoints) {
const worldPoint = transform.unprojectBasic(basicPoint, 10000);
const cameraPoint = new THREE.Vector3()
.fromArray(viewportCoords.worldToCamera(worldPoint, camera));
diff --git a/src/render/RenderCamera.ts b/src/render/RenderCamera.ts
index 3bfef717..813e3883 100644
--- a/src/render/RenderCamera.ts
+++ b/src/render/RenderCamera.ts
@@ -13,7 +13,7 @@ import { State } from "../state/State";
import { AnimationFrame } from "../state/interfaces/AnimationFrame";
import { EulerRotation } from "../state/interfaces/EulerRotation";
import { isSpherical } from "../geo/Geo";
-import { MathUtils, Matrix3, Quaternion, Vector2, Vector3 } from "three";
+import { MathUtils, Vector3 } from "three";
export class RenderCamera {
private _spatial: Spatial;
@@ -47,6 +47,8 @@ export class RenderCamera {
private _currentProjectedPoints: number[][];
private _previousProjectedPoints: number[][];
+ private _currentBearings: number[][];
+
private _currentFov: number;
private _previousFov: number;
@@ -95,6 +97,8 @@ export class RenderCamera {
this._currentProjectedPoints = [];
this._previousProjectedPoints = [];
+ this._currentBearings = [];
+
this._currentFov = this._initialFov;
this._previousFov = this._initialFov;
@@ -193,8 +197,7 @@ export class RenderCamera {
this.setSize(this._size);
}
if (this._state === State.Earth) {
- const y = this._fovToY(this._perspective.fov, this._zoom);
- this._stateTransitionFov = this._yToFov(y, 0);
+ this._stateTransitionFov = this._zoomedFovToFov(this._perspective.fov, this._zoom);
}
this._changed = true;
@@ -207,6 +210,7 @@ export class RenderCamera {
this._currentImageId = currentImageId;
this._currentSpherical = isSpherical(state.currentTransform.cameraType);
this._currentProjectedPoints = this._computeProjectedPoints(state.currentTransform);
+ this._currentBearings = this._computeBearings(state.currentTransform);
this._currentCameraUp.copy(state.currentCamera.up);
this._currentTransformUp.copy(state.currentTransform.upVector());
this._currentTransformForward.copy(
@@ -247,6 +251,7 @@ export class RenderCamera {
if (this._changed) {
this._currentFov = this._computeCurrentFov(zoom);
+ this._computeCurrentBearingFov(zoom);
this._previousFov = this._computePreviousFov(zoom);
}
@@ -264,8 +269,7 @@ export class RenderCamera {
const startFov = this._stateTransitionFov;
const endFov = this._focalToFov(state.camera.focal);
const fov = MathUtils.lerp(startFov, endFov, sta);
- const y = this._fovToY(fov, 0);
- this._perspective.fov = this._yToFov(y, zoom);
+ this._perspective.fov = this._fovToZoomedFov(fov, zoom);
break;
}
case State.Custom:
@@ -350,19 +354,22 @@ export class RenderCamera {
return elementWidth === 0 ? 0 : elementWidth / elementHeight;
}
- private _computeUpRotation(up: Vector3, refForward: Vector3, refUp: Vector3): number {
- const right = new Vector3().crossVectors(refForward, refUp).normalize();
- const normal = new Vector3().crossVectors(refUp, right).normalize();
-
- const xzFromNormal = new Quaternion()
- .setFromUnitVectors(normal, new Vector3(0, 1, 0));
- const upXz = up.clone().applyQuaternion(xzFromNormal);
- const refUpXz = refUp.clone().applyQuaternion(xzFromNormal);
+ private _computeCurrentBearingFov(zoom: number): number {
+ if (this._perspective.aspect === 0) {
+ return 0;
+ }
- const upRad = Math.acos(new Vector2(upXz.x, upXz.z).normalize().x);
- const refUpRad = Math.acos(new Vector2(refUpXz.x, refUpXz.z).normalize().x);
+ if (!this._currentImageId) {
+ return this._initialFov;
+ }
- return refUpRad - upRad;
+ return this._currentSpherical ?
+ this._fovToZoomedFov(90, zoom) :
+ this._computeVerticalBearingFov(
+ this._currentBearings,
+ this._renderMode,
+ zoom,
+ this.perspective.aspect);
}
private _computeCurrentFov(zoom: number): number {
@@ -375,16 +382,12 @@ export class RenderCamera {
}
return this._currentSpherical ?
- this._yToFov(1, zoom) :
- this._computeVerticalFov(
+ this._fovToZoomedFov(90, zoom) :
+ this._computeVerticalBearingFov(
this._currentProjectedPoints,
this._renderMode,
zoom,
- this.perspective.aspect,
- this._computeUpRotation(
- this._currentTransformUp,
- this._currentTransformForward,
- this._currentCameraUp));
+ this.perspective.aspect);
}
private _computeFov(): number {
@@ -406,16 +409,25 @@ export class RenderCamera {
return !this._previousImageId ?
this._currentFov :
this._previousSpherical ?
- this._yToFov(1, zoom) :
- this._computeVerticalFov(
+ this._fovToZoomedFov(90, zoom) :
+ this._computeVerticalBearingFov(
this._previousProjectedPoints,
this._renderMode,
zoom,
- this.perspective.aspect,
- this._computeUpRotation(
- this._previousTransformUp,
- this._previousTransformForward,
- this._previousCameraUp));
+ this.perspective.aspect);
+ }
+
+ private _computeBearings(transform: Transform): number[][] {
+ const vertices = [[0, 0], [1, 0], [1, 1], [0, 1]];
+ const directions = [[1, 0], [0, 1], [-1, 0], [0, -1]];
+ const pointsPerLine = 5;
+
+ return Geo.computeBearings(
+ transform,
+ vertices,
+ directions,
+ pointsPerLine,
+ this._viewportCoords);
}
private _computeProjectedPoints(transform: Transform): number[][] {
@@ -431,17 +443,6 @@ export class RenderCamera {
this._viewportCoords);
}
- private _computeRequiredVerticalFov(
- projectedPoint: number[],
- zoom: number,
- aspect: number): number {
- const maxY = Math.max(
- Math.abs(projectedPoint[0]) / aspect,
- Math.abs(projectedPoint[1]));
-
- return this._yToFov(maxY, zoom);
- }
-
private _computeRotation(camera: Camera): EulerRotation {
let direction: THREE.Vector3 = camera.lookat.clone().sub(camera.position);
let up: THREE.Vector3 = camera.up.clone();
@@ -452,32 +453,22 @@ export class RenderCamera {
return { phi: phi, theta: theta };
}
- private _computeVerticalFov(
- projectedPoints: number[][],
+ private _computeVerticalBearingFov(
+ bearings: number[][],
renderMode: RenderMode,
zoom: number,
- aspect: number,
- theta: number): number {
-
- const sinTheta = Math.sin(theta);
- const cosTheta = Math.cos(theta);
-
- const fovs = projectedPoints
- .map(
- ([x, y]: number[]): number => {
- const rotatedPoint = [
- cosTheta * x - sinTheta * y,
- sinTheta * x + cosTheta * y,
- ];
- return this._computeRequiredVerticalFov(
- rotatedPoint,
- zoom,
- aspect);
- });
-
- const fovMax = this._yToFov(this._fovToY(125, 0), zoom);
- const minFov = Math.min(...fovs) * 0.995;
- const vFovFill = Math.min(minFov, fovMax);
+ aspect: number): number {
+
+ const projections = bearings
+ .map(b => this._spatial.projectToPlane(b, [1, 0, 0]))
+ .map(p => [p[1], -p[2]]);
+
+ const fovs = projections.map(p => Math.abs(Math.atan2(p[0], p[1])));
+ const vFovMin = fovs.length > 0 ? this._spatial.radToDeg(0.995 * 2 * Math.min(...fovs)) : 125;
+ const fovMin = this._fovToZoomedFov(vFovMin, zoom);
+ const fovMax = this._fovToZoomedFov(125, zoom);
+ console.log(zoom, vFovMin, fovMin, fovMax);
+ const vFovFill = Math.min(fovMin, fovMax);
if (renderMode === RenderMode.Fill) {
return vFovFill;
}
@@ -501,18 +492,18 @@ export class RenderCamera {
return vFov;
}
- private _yToFov(y: number, zoom: number): number {
- return 2 * Math.atan(y / Math.pow(2, zoom)) * 180 / Math.PI;
+ private _fovToZoomedFov(fov: number, zoom: number): number {
+ return fov / Math.pow(2, zoom);
}
+ private _zoomedFovToFov(zoomedFov: number, zoom: number): number {
+ return Math.pow(2, zoom) * zoomedFov;
+ }
+
private _focalToFov(focal: number): number {
return 2 * Math.atan2(1, 2 * focal) * 180 / Math.PI;
}
- private _fovToY(fov: number, zoom: number): number {
- return Math.pow(2, zoom) * Math.tan(Math.PI * fov / 360);
- }
-
private _interpolateFov(v1: number, v2: number, alpha: number): number {
return alpha * v1 + (1 - alpha) * v2;
}