/*******************************************************************************
* Copyright 2013 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.worldwind.common.input.hydra;
import gov.nasa.worldwind.awt.ViewInputAttributes.ActionAttributes;
import gov.nasa.worldwind.awt.ViewInputAttributes.DeviceAttributes;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.view.BasicView;
import gov.nasa.worldwind.view.ViewUtil;
import gov.nasa.worldwind.view.orbit.OrbitView;
import au.gov.ga.earthsci.worldwind.common.input.IOrbitInputProvider;
import au.gov.ga.earthsci.worldwind.common.input.IProviderOrbitViewInputHandler;
import au.gov.ga.earthsci.worldwind.common.view.delegate.IDelegateView;
import au.gov.ga.earthsci.worldwind.common.view.orbit.AbstractView;
import au.gov.ga.earthsci.worldwind.common.view.target.ITargetView;
/**
* Input provider implementation for the Razer Hydra controller.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class HydraOrbitInputProvider implements IOrbitInputProvider, IHydraListener
{
private final static ActionAttributes horizontalAttributes = new ActionAttributes(3e-4, 1e2, false, 0.4);
private final static ActionAttributes verticalAttributes = new ActionAttributes(1e0, 1e0, false, 0.85);
private final static ActionAttributes headingAttributes = new ActionAttributes(1e2, 1e2, false, 0.85);
private final static ActionAttributes pitchAttributes = new ActionAttributes(5e1, 1e2, false, 0.85);
private final static DeviceAttributes deviceAttributes = new DeviceAttributes(1.0);
private final static ActionAttributes flyHorizontalAttributes = new ActionAttributes(5e3, 5e7, false, 0.4);
private IProviderOrbitViewInputHandler inputHandler;
private float x1, y1, x2, y2, z;
private boolean flip1 = true, flip2 = false;
private long lastNanos;
private boolean fly = false;
public HydraOrbitInputProvider()
{
Hydra.getInstance().addListener(this);
}
@Override
public void updated(HydraEvent event)
{
}
@Override
public void stickChanged(HydraStickEvent event)
{
lastNanos = System.nanoTime();
x1 = event.stick1[0];
y1 = event.stick1[1];
x2 = event.stick2[0];
y2 = event.stick2[1];
if (inputHandler != null)
{
inputHandler.markViewChanged();
}
}
@Override
public void triggerChanged(HydraTriggerEvent event)
{
lastNanos = System.nanoTime();
z = event.trigger1 - event.trigger2;
if (inputHandler != null)
{
inputHandler.markViewChanged();
}
}
@Override
public void buttonChanged(HydraButtonEvent event)
{
if (!event.down)
{
if (event.button == HydraButtonEvent.STICK)
{
if (event.controller == 1)
{
flip1 = !flip1;
}
else
{
flip2 = !flip2;
}
}
else if (event.button == HydraButtonEvent.START)
{
fly = !fly;
OrbitView view = inputHandler.getView();
if (view instanceof ITargetView)
{
((ITargetView) view).setTargetMode(fly);
((ITargetView) view).setDrawAxisMarker(!fly);
}
}
}
}
@Override
public void apply(IProviderOrbitViewInputHandler inputHandler)
{
this.inputHandler = inputHandler;
if (x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0 || z != 0)
{
long currentNanos = System.nanoTime();
double time = (currentNanos - lastNanos) / 1e9d;
lastNanos = currentNanos;
if (!fly)
{
double mult1 = flip1 ? -1 : 1;
double mult2 = flip2 ? -1 : 1;
double translationAngle = Math.atan2(-x1 * mult1, y1 * mult1);
double translationSpeed = Math.sqrt(x1 * x1 + y1 * y1);
if (translationSpeed != 0)
{
inputHandler.onRotateFree(Angle.fromRadians(translationAngle),
Angle.fromDegrees(time * translationSpeed), deviceAttributes, horizontalAttributes);
}
if (z != 0)
{
double zoomChange = time * z;
inputHandler.onVerticalTranslate(zoomChange, zoomChange, deviceAttributes, verticalAttributes);
}
if (x2 != 0)
{
Angle headingMoveChange =
Angle.fromDegrees(time * x2 * inputHandler.getScaleValueRotate(headingAttributes) * mult2);
inputHandler.onRotateView(headingMoveChange, Angle.ZERO, headingAttributes);
}
if (y2 != 0)
{
Angle pitchChange =
Angle.fromDegrees(time * -y2 * inputHandler.getScaleValueRotate(pitchAttributes));
inputHandler.onRotateView(Angle.ZERO, pitchChange, pitchAttributes);
}
}
else
{
OrbitView view = inputHandler.getView();
DrawContext dc = null;
if (view instanceof AbstractView)
{
dc = ((AbstractView) view).getDC();
}
else if (view instanceof BasicView)
{
dc = ((BasicView) view).getDC();
}
else if (view instanceof IDelegateView)
{
dc = ((IDelegateView) view).getDC();
}
if (dc == null)
{
//need dc to calculate altitude below
return;
}
Globe globe = view.getGlobe();
Position eyePosition = view.getEyePosition();
Vec4 eyePoint = view.getEyePoint();
Vec4 forward = view.getForwardVector();
Vec4 up = view.getUpVector();
Vec4 side = forward.cross3(up);
double altitude = ViewUtil.computeElevationAboveSurface(dc, eyePosition);
double radius = globe.getRadius();
double[] range = flyHorizontalAttributes.getValues();
double speed = getScaleValue(range[0], range[1], altitude, 3.0 * radius, true) * time;
double eyeDistance = 1;
Vec4 centerPoint = eyePoint.add3(forward.multiply3(eyeDistance));
centerPoint = centerPoint.add3(forward.multiply3(y1 * speed));
centerPoint = centerPoint.add3(side.multiply3(x1 * speed));
double altitudeChange = speed * z;
Position centerPosition = globe.computePositionFromPoint(centerPoint);
centerPosition = new Position(centerPosition, centerPosition.elevation + altitudeChange);
double maximumAltitude = radius * 0.5;
double centerAltitude = ViewUtil.computeElevationAboveSurface(dc, centerPosition);
if (centerAltitude > maximumAltitude)
{
centerPosition = new Position(centerPosition,
centerPosition.elevation - centerAltitude + maximumAltitude);
}
view.setZoom(eyeDistance);
view.setCenterPosition(centerPosition);
view.setPitch(Angle.POS90);
view.setHeading(view.getHeading().addDegrees(x2 * time * 100d));
}
inputHandler.markViewChanged();
}
}
protected double getScaleValue(double minValue, double maxValue, double value, double range, boolean isExp)
{
double t = value / range;
t = t < 0 ? 0 : (t > 1 ? 1 : t);
if (isExp)
{
t = Math.pow(2.0, t) - 1.0;
}
return (minValue * (1.0 - t) + maxValue * t);
}
}