Package com.ardor3d.input.control

Source Code of com.ardor3d.input.control.OrbitCamControl

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.input.control;

import com.ardor3d.framework.Canvas;
import com.ardor3d.input.MouseState;
import com.ardor3d.input.logical.InputTrigger;
import com.ardor3d.input.logical.LogicalLayer;
import com.ardor3d.input.logical.MouseWheelMovedCondition;
import com.ardor3d.input.logical.TriggerAction;
import com.ardor3d.input.logical.TriggerConditions;
import com.ardor3d.input.logical.TwoInputStates;
import com.ardor3d.math.MathUtils;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.scenegraph.Spatial;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

/**
* <p>
* Orbital type Camera controller. Basically, this class references a camera and provides methods for moving that camera
* around a target position or spatial using spherical polar coordinates.
* </p>
* <p>
* To use, create a new instance of OrbitCamController. Update the controller in your update loop.
* </p>
* <p>
* Example: Creates a new control, adds mouse triggers to a Logical Layer, and sets the default location (15 units away,
* 0 degrees ascent, 0 degrees azimuth).
* </p>
*
* <pre>
* // ... in init
* control = new OrbitCamControl(myCamera, targetLocation);
* control.setupMouseTriggers(myLogicalLayer, true);
* control.setSphereCoords(15, 0, 0);
*
* // ...in update loop
* control.update(timer.getTimePerFrame());
* </pre>
*/
public class OrbitCamControl {

    /**
     * Our absolute min/max ascent (pitch) angle, in radians. This is set at 89.95 degrees to prevent the camera's
     * direction from becoming parallel to the world up vector.
     */
    public static final double ABSOLUTE_MAXASCENT = 89.95 * MathUtils.DEG_TO_RAD;

    /**
     * The camera we are modifying.
     */
    protected Camera _camera;
    protected Vector3 _worldUpVec = new Vector3(Vector3.UNIT_Y);

    protected Vector3 _sphereCoords = new Vector3();
    protected Vector3 _camPosition = new Vector3();

    protected Vector3 _lookAtPoint = new Vector3();
    protected Spatial _lookAtSpatial = null;
    protected TargetType _targetType;

    protected boolean _invertedX = false;
    protected boolean _invertedY = false;
    protected boolean _invertedWheel = true;

    protected double _zoomSpeed = 0.01;
    protected double _baseDistance = 15;
    protected double _minZoomDistance = 1;
    protected double _maxZoomDistance = 100;

    protected double _minAscent = -ABSOLUTE_MAXASCENT;
    protected double _maxAscent = ABSOLUTE_MAXASCENT;
    protected double _xSpeed = 0.01;
    protected double _ySpeed = 0.01;

    protected boolean _dirty = true;

    protected InputTrigger _mouseTrigger;

    public enum TargetType {
        Point, Spatial
    }

    /**
     * Construct a new orbit controller
     *
     * @param cam
     *            the camera to control
     * @param target
     *            a world location to lock our sights on.
     */
    public OrbitCamControl(final Camera cam, final ReadOnlyVector3 target) {
        _camera = cam;
        _targetType = TargetType.Point;
        _lookAtPoint.set(target);
    }

    /**
     * Construct a new orbit controller
     *
     * @param cam
     *            the camera to control
     * @param target
     *            a spatial whose world location we'll lock our sights on.
     */
    public OrbitCamControl(final Camera cam, final Spatial target) {
        _camera = cam;
        _targetType = TargetType.Spatial;
        _lookAtSpatial = target;
    }

    public Camera getCamera() {
        return _camera;
    }

    public void setCamera(final Camera camera) {
        _camera = camera;
    }

    public ReadOnlyVector3 getWorldUpVec() {
        return _worldUpVec;
    }

    public void setWorldUpVec(final ReadOnlyVector3 worldUpVec) {
        _worldUpVec.set(worldUpVec);
        _dirty = true;
    }

    public void setInvertedWheel(final boolean invertedWheel) {
        _invertedWheel = invertedWheel;
    }

    public boolean isInvertedWheel() {
        return _invertedWheel;
    }

    public void setInvertedX(final boolean invertedX) {
        _invertedX = invertedX;
    }

    public boolean isInvertedX() {
        return _invertedX;
    }

    public void setInvertedY(final boolean invertedY) {
        _invertedY = invertedY;
    }

    public boolean isInvertedY() {
        return _invertedY;
    }

    public Vector3 getLookAtPoint() {
        return _lookAtPoint;
    }

    /**
     * Sets a specific world location for the camera to point at and circle around.
     *
     * @param point
     */
    public void setLookAtPoint(final Vector3 point) {
        _dirty = !point.equals(_lookAtPoint);
        _lookAtPoint = point;
        _targetType = TargetType.Point;
    }

    public Spatial getLookAtSpatial() {
        return _lookAtSpatial;
    }

    /**
     * Sets a spatial to look at. We'll use the world transform of the spatial, so its transform needs to be up to date.
     *
     * @param spatial
     */
    public void setLookAtSpatial(final Spatial spatial) {
        _dirty = spatial != _lookAtSpatial; // identity equality
        _lookAtSpatial = spatial;
        _targetType = TargetType.Spatial;
    }

    public TargetType getTargetType() {
        return _targetType;
    }

    public double getZoomSpeed() {
        return _zoomSpeed;
    }

    public void setZoomSpeed(final double zoomSpeed) {
        _zoomSpeed = zoomSpeed;
    }

    public double getBaseDistance() {
        return _baseDistance;
    }

    public void setBaseDistance(final double baseDistance) {
        _baseDistance = baseDistance;
        zoom(0);
    }

    public double getMaxAscent() {
        return _maxAscent;
    }

    public void setMaxAscent(final double maxAscent) {
        _maxAscent = Math.min(maxAscent, ABSOLUTE_MAXASCENT);
        move(0, 0);
    }

    public double getMinAscent() {
        return _minAscent;
    }

    public void setMinAscent(final double minAscent) {
        _minAscent = Math.max(minAscent, -ABSOLUTE_MAXASCENT);
        move(0, 0);
    }

    public double getMaxZoomDistance() {
        return _maxZoomDistance;
    }

    public void setMaxZoomDistance(final double maxZoomDistance) {
        _maxZoomDistance = maxZoomDistance;
        zoom(0);
    }

    public double getMinZoomDistance() {
        return _minZoomDistance;
    }

    public void setMinZoomDistance(final double minZoomDistance) {
        _minZoomDistance = minZoomDistance;
        zoom(0);
    }

    public double getXSpeed() {
        return _xSpeed;
    }

    public void setXSpeed(final double speed) {
        _xSpeed = speed;
    }

    public double getYSpeed() {
        return _ySpeed;
    }

    public void setYSpeed(final double speed) {
        _ySpeed = speed;
    }

    public void setSphereCoords(final ReadOnlyVector3 sphereCoords) {
        _sphereCoords.set(sphereCoords);
        makeDirty();
    }

    public void setSphereCoords(final double x, final double y, final double z) {
        _sphereCoords.set(x, y, z);
        makeDirty();
    }

    protected void updateTargetPos() {
        if (_targetType == TargetType.Spatial) {
            final double x = _lookAtPoint.getX();
            final double y = _lookAtPoint.getY();
            final double z = _lookAtPoint.getZ();
            _lookAtSpatial.getWorldTransform().applyForward(Vector3.ZERO, _lookAtPoint);
            if (x != _lookAtPoint.getX() || y != _lookAtPoint.getY() || z != _lookAtPoint.getZ()) {
                makeDirty();
            }
        }
    }

    public void makeDirty() {
        _dirty = true;
    }

    /**
     * Zoom camera in/out from the target point.
     *
     * @param percent
     *            a value applied to the baseDistance to determine how far in/out to zoom. Inverted if
     *            {@link #isInvertedWheel()} is true.
     */
    public void zoom(final double percent) {
        final double amount = (_invertedWheel ? -1 : 1) * percent * _baseDistance;
        _sphereCoords.setX(MathUtils.clamp(_sphereCoords.getX() + amount, _minZoomDistance, _maxZoomDistance));
        makeDirty();
    }

    /**
     *
     * @param xDif
     *            a value applied to the azimuth value of our spherical coordinates. Inverted if {@link #isInvertedX()}
     *            is true.
     * @param yDif
     *            a value applied to the theta value of our spherical coordinates. Inverted if {@link #isInvertedY()} is
     *            true.
     */
    public void move(final double xDif, final double yDif) {
        final double azimuthAccel = _invertedX ? -xDif : xDif;
        final double thetaAccel = _invertedY ? -yDif : yDif;

        // update our master spherical coords, using x and y movement
        _sphereCoords.setY(MathUtils.moduloPositive(_sphereCoords.getY() - azimuthAccel, MathUtils.TWO_PI));
        _sphereCoords.setZ(MathUtils.clamp(_sphereCoords.getZ() + thetaAccel, _minAscent, _maxAscent));
        makeDirty();
    }

    /**
     * Update the position of the Camera controlled by this object.
     *
     * @param time
     *            a delta time, in seconds. Not used currently, but might be useful for doing "ease-in" of camera
     *            movements.
     */
    public void update(final double time) {
        updateTargetPos();

        if (!_dirty) {
            return;
        }
        if (_worldUpVec.getY() == 1) {
            MathUtils.sphericalToCartesian(_sphereCoords, _camPosition);
        } else if (_worldUpVec.getZ() == 1) {
            MathUtils.sphericalToCartesianZ(_sphereCoords, _camPosition);
        }

        _camera.setLocation(_camPosition.addLocal(_lookAtPoint));

        _camera.lookAt(_lookAtPoint, _worldUpVec);
        _dirty = false;
    }

    public void setupMouseTriggers(final LogicalLayer layer, final boolean dragOnly) {
        // Mouse look
        final Predicate<TwoInputStates> someMouseDown = Predicates.or(TriggerConditions.leftButtonDown(), Predicates
                .or(TriggerConditions.rightButtonDown(), TriggerConditions.middleButtonDown()));
        final Predicate<TwoInputStates> scrollWheelMoved = new MouseWheelMovedCondition();
        final Predicate<TwoInputStates> dragged = Predicates.and(TriggerConditions.mouseMoved(), someMouseDown);
        final TriggerAction mouseAction = new TriggerAction() {

            // Test boolean to allow us to ignore first mouse event. First event can wildly vary based on platform.
            private boolean firstPing = true;

            public void perform(final Canvas source, final TwoInputStates inputStates, final double tpf) {
                final MouseState mouse = inputStates.getCurrent().getMouseState();
                if (mouse.getDx() != 0 || mouse.getDy() != 0) {
                    if (!firstPing) {
                        move(_xSpeed * mouse.getDx(), _ySpeed * mouse.getDy());
                    } else {
                        firstPing = false;
                    }
                }

                if (mouse.getDwheel() != 0) {
                    zoom(_zoomSpeed * mouse.getDwheel());
                }
            }
        };

        final Predicate<TwoInputStates> predicate = Predicates.or(scrollWheelMoved, dragOnly ? dragged
                : TriggerConditions.mouseMoved());
        _mouseTrigger = new InputTrigger(predicate, mouseAction);
        layer.registerTrigger(_mouseTrigger);
    }
}
TOP

Related Classes of com.ardor3d.input.control.OrbitCamControl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.