Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix navigator lookAt point calculation considering terrain elevation #247

Open
ComBatVision opened this issue Oct 28, 2021 · 0 comments
Open

Comments

@ComBatVision
Copy link

Now Navigator takes point on zero altitude as a root point for gesture. It makes gestures to rotate around point below the surface. It is wrong!

Camera should take lookAt point on the surface and the best way is just use pick terrain on the gesture begin.

    @Override
    protected LookAt cameraToLookAt(Globe globe, Camera camera, LookAt result) {
        this.cameraToViewingMatrix(globe, camera, this.modelview);

        // Pick terrain located behind the viewport center point
        PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
        if (terrainPickedObject != null) {
            // Use picked terrain position including approximate rendered altitude
            this.originPos.set(terrainPickedObject.getTerrainPosition());
            globe.geographicToCartesian(this.originPos.latitude, this.originPos.longitude, this.originPos.altitude, this.originPoint);
            globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
        } else {
            // Center is outside the globe - use point on horizon
            this.modelview.extractEyePoint(this.forwardRay.origin);
            this.modelview.extractForwardVector(this.forwardRay.direction);
            this.forwardRay.pointAt(globe.horizonDistance(camera.altitude), this.originPoint);
            globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos);
            globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
        }

        // Calculate look at rotation matrix (code from original Navigator implementation)
        this.modelview.multiplyByMatrix(this.origin);

        result.latitude = this.originPos.latitude;
        result.longitude = this.originPos.longitude;
        result.altitude = this.originPos.altitude;
        result.range = -this.modelview.m[11];
        result.heading = this.modelview.extractHeading(camera.roll); // disambiguate heading and roll
        result.tilt = this.modelview.extractTilt();
        result.roll = camera.roll; // roll passes straight through

        return result;
    }

It is also required to remove one line from BasicFrameController.renderTerrainPickedObject() method:
this.pickPos.altitude = 0; // report the actual altitude, which may not lie on the terrain's surface
This line reset picked point altitude calculated using current terrain details tesellation and do not allows to use pick functionality in Navigator.

Movement event should be executed via handler, because its listeners may call Navigator.getAsLookAt(), which use pick() and render next frame from inside of current frame causing recursion.
Initial frame rendering must call stop event to initialize UI with initial navigator state.

class CustomNavigatorEventSupport extends NavigatorEventSupport {

    private Handler moveHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            onNavigatorMoved();
            return false;
        }
    });

    CustomNavigatorEventSupport(CustomWorldWindow wwd) {
        super(wwd);
    }

    @Override
    public void onFrameRendered(RenderContext rc) {
        if (this.listeners.isEmpty()) {
            return; // no listeners to notify; ignore the event
        }

        if (this.lastModelview == null) { // this is the first frame; copy the frame's modelview
            this.lastModelview = new Matrix4(rc.modelview);
            // Notify listeners with stopped event on first frame
            this.stopHandler.removeMessages(0 /*what*/);
            this.stopHandler.sendEmptyMessage(0 /*what*/);
        } else if (!this.lastModelview.equals(rc.modelview)) { // the frame's modelview has changed
            this.lastModelview.set(rc.modelview);
            // Notify the listeners of a navigator moved event.
            this.moveHandler.removeMessages(0 /*what*/);
            this.moveHandler.sendEmptyMessage(0/*what*/);
            // Schedule a navigator stopped event after a specified delay in milliseconds.
            this.stopHandler.removeMessages(0 /*what*/);
            this.stopHandler.sendEmptyMessageDelayed(0 /*what*/, this.stoppedEventDelay);
        }
    }

    @Override
    public void reset() {
        super.reset();
        this.moveHandler.removeMessages(0/*what*/);
    }

}

WorldWindEarth#9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant