view engine/core/view/camera.h @ 363:714673208050

Adding the CameraEdit plugin to the editor. You must enable it in your settings.xml file. Still not fully tested.
author prock@33b003aa-7bff-0310-803a-e67f0ece8222
date Wed, 21 Oct 2009 15:19:59 +0000
parents 7e5717105212
children f27880d4c08c
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2005-2008 by the FIFE team                              *
 *   http://www.fifengine.de                                               *
 *   This file is part of FIFE.                                            *
 *                                                                         *
 *   FIFE is free software; you can redistribute it and/or                 *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

#ifndef FIFE_VIEW_CAMERA_H
#define FIFE_VIEW_CAMERA_H

// Standard C++ library includes
#include <string>
#include <map>

// 3rd party library includes

// FIFE includes
// These includes are split up in two parts, separated by one empty line
// First block: files included from the FIFE root src directory
// Second block: files included from the same folder
#include "model/structures/location.h"
#include "util/math/matrix.h"
#include "util/structures/rect.h"

#include "rendererbase.h"

namespace FIFE {

	typedef Point3D ScreenPoint;
	class Layer;
	class Instance;
	class ImagePool;
	class AnimationPool;
	class RenderBackend;
	typedef std::map<Layer*, std::vector<Instance*> > t_layer_to_instances;

	/** Camera describes properties of a view port shown in the main screen
	 *  Main screen can have multiple cameras active simultanously
	 *  Different cameras can have different properties, like location
	 *  to shoot, zoom or tilt
	 */
	class Camera: public IRendererListener, public IRendererContainer {
	public:
		/** Constructor
		 * Camera needs to be added to the view. If not done so, it is not rendered.
		 * @param id identifier for the camera
		 * @param layer layer where camera is bind. Camera is bind to a layer for two reasons:
		 *   * camera's scaling is done based on cell image dimensions. Cell image is layer based (@see setCellImageDimensions)
		 *   * camera could be bind to a pather, which operates on layer
		 * @param viewport used viewport for the camera. Viewport is measured in pixels in relation to game main screen
		 * @param emc coordinate, where camera is focused on given layer
		 * @param renderbackend to use with rendering
		 * @param ipool to use with rendering
		 * @param apool to use with rendering
		 */
		Camera(const std::string& id, 
			Layer* layer,
			Rect viewport,
			ExactModelCoordinate emc, 
			RenderBackend* renderbackend,
			ImagePool* ipool,
			AnimationPool* apool);

		/** Destructor
		 */
		virtual ~Camera();

		/** Gets the identifier for this camera.
		 */
		const std::string& getId() const { return m_id; }

		/** Sets the identifier for this camera.
		 */
		void setId(const std::string& id) { m_id = id; }

		/** Sets tilt for the camera.
		 * e.g. overhead camera has tilt 0, while traditional isometric camera has tilt 45
		 * @param tilt tilt for the camera
		 */
		void setTilt(double tilt);

		/** Gets camera tilt
		 * @return tilt of camera
		 */
		double getTilt() const;

		/** Sets rotation for the camera.
		 * Rotation can be visualized by thinking camera that rotates around an object
		 * that it is rendering
		 * @param rotation rotation for the camera
		 */
		void setRotation(double rotation);

		/** Gets camera rotation
		 * @return rotation of the camera
		 */
		double getRotation() const;

		/** Sets zoom for the camera.
		 * @param zoom zoom for the camera
		 */
		void setZoom(double zoom);

		/** Gets camera zoom
		 * @return zoom of the camera
		 */
		double getZoom() const;

		/** Sets screen cell image dimensions.
		 * Cell image dimension is basically width and height of a bitmap, that covers
		 * one cell in the layer where camera is bind
		 * @return Point Point containing x=width and y=height
		 */
		void setCellImageDimensions(unsigned int width, unsigned int height);
		
		/** Gets screen cell image dimensions.
		 * @see setCellImageDimensions
		 * @return Point containing x=width and y=height
		 */
		Point getCellImageDimensions();

		/** Gets screen cell image dimensions for given layer.
		* @return Point Point containing x=width and y=height
		*/
		Point getCellImageDimensions(Layer* layer);
		
		/** Sets the location for camera
		 * @param location location (center point) to render
		 */
		void setLocation(const Location& location);

		/** Gets the location camera is rendering
		 * @return camera location
		 */
		Location getLocation() const;

		/** Gets a reference to the camera location
		 * @note if you change returned location without calling Camera::setLocation(...),
		 *       remember to call Camera::refresh() (otherwise camera transforms are not updated)
		 * @return reference to the camera location
		 */
		Location& getLocationRef();

		/** Attaches the camera to an instance.
		 * @param instance Instance to which the camera shall be attached
		 * @note The camera can only be attached to an instance at the same layer!
		 */
		void attach(Instance *instance);

		/** Detaches the camera from an instance.
		 */
		void detach();

		/** Returns instance where camera is attached. NULL if not attached
		 */
		Instance* getAttached() const { return m_attachedto; }
		
		/** Sets the viewport for camera
		 * viewport is rectangle inside the view where camera renders
		 * @param viewport area for camera render
		 */
		void setViewPort(const Rect& viewport);

		/** Gets the viewport for camera
		 * @return camera viewport
		 */
		const Rect& getViewPort() const;

		/** Transforms given point from screen coordinates to map coordinates
		 * @param screen_coords screen coordinates to transform
		 * @param z_calculated if true, z-value (depth cut point) is pre-calculated. If false, camera calculates it
		 * @return point in map coordinates
		 */
		ExactModelCoordinate toMapCoordinates(ScreenPoint screen_coords, bool z_calculated=true);

		/** Transforms given point from map coordinates to screen coordinates
		 *  @return point in screen coordinates
		 */
		ScreenPoint toScreenCoordinates(ExactModelCoordinate map_coords);

		/** Sets camera enabled / disabled
		 */
		void setEnabled(bool enabled);

		/** Gets if camera is enabled / disabled
		 */
		bool isEnabled();
		
		/** Gets angle of vector defined by given locations and camera properties (e.g. rotation)
		 *  @return angle in polar coordinates
		 */
		inline int getAngleBetween(const Location& loc1, const Location& loc2) {
			static const double VECTOR_MULTIP = 100000.0;
			ExactModelCoordinate c1 = loc1.getMapCoordinates();
			ExactModelCoordinate c2 = loc2.getMapCoordinates();
			ExactModelCoordinate cd((c2.x - c1.x) * VECTOR_MULTIP, (c2.y - c1.y) * VECTOR_MULTIP, 0);
			c2.x = c1.x + cd.x;
			c2.y = c1.y + cd.y;
			ScreenPoint pt1 = this->toScreenCoordinates(c1);
			ScreenPoint pt2 = this->toScreenCoordinates(c2);
			double dy = (pt2.y - pt1.y);
			double dx = (pt2.x - pt1.x);
			int angle = static_cast<int>(atan2(-dy,dx)*(180.0/M_PI));
			return angle;
		}
		
		/** Returns instances that match given screen coordinate
		 * @param screen_coords screen coordinates to be used for hit search
		 * @param layer layer to use for search
		 * @param instances list of instances that is filled based on hit test results
		 */
		void getMatchingInstances(ScreenPoint screen_coords, Layer& layer, std::list<Instance*>& instances);
		
		/** Returns instances that match given screen coordinate
		 * @param screen_point1 top left screen coordinates to be used for hit search
		 * @param screen_point2 right bottom screen coordinates to be used for hit search
		 * @param layer layer to use for search
		 * @param instances list of instances that is filled based on hit test results
		 */
		void getMatchingInstances(Rect screen_rect, Layer& layer, std::list<Instance*>& instances);

		/** Returns instances that match given location. Instances are sorted based on camera view, so that "topmost"
		 * instance is first in returned list
		 * @param loc location where to fetch instances from
		 * @param instances list of instances that is filled based on hit test results
		 * @param use_exactcoordinates if true, comparison is done using exact coordinates. if not, cell coordinates are used
		 */
		void getMatchingInstances(Location& loc, std::list<Instance*>& instances, bool use_exactcoordinates=false);
		
		/** General update routine.
		 * In this function, the camera's position gets updated when its attached
		 * to another instance.
		 * @note call this only once in engine update cycle, so that tracking between
		 *  current position and previous position keeps in sync. This information
		 *  is used e.g. by view to fix the pixel wobbling problem
		 */
		void update();

		/** Refreshes camera view in case e.g. location is updated directly (not via setLocation)
		 * @note calling this function marks camera as "warped", therefore it causes all instance
		 * positions to be recalculated. If you constantly call this, you end up with pixel wobbling
		 * effect when camera is moved.
		 */
		void refresh();

		/** Resets temporary values from last update round, like warped flag
		 */
		void resetUpdates();

		/** Adds new renderer on the view. Ownership is transferred to the camera.
		 */
		void addRenderer(RendererBase* renderer);

		/** Gets renderer with given name
		 */
		RendererBase* getRenderer(const std::string& name);

		/** resets active layer information on all renderers.
		 */
		void resetRenderers();
		
		/** calculates z-value for given screenpoint
		 */
		void calculateZValue(ScreenPoint& screen_coords);

		void onRendererPipelinePositionChanged(RendererBase* renderer);
		void onRendererEnabledChanged(RendererBase* renderer);
		
		/** Renders camera
		 */
		void render();
		
	private:
		std::string m_id;

		/** Updates the camera transformation matrix T with requested values.
		 * The requests are done using these functions :
		 * - setLocation
		 * - setRotation
		 * - setTilt
		 */
		void updateMatrices();
		
		/** Updates camera reference scale
		 * Reference scale is in a sense an internal zooming factor,
		 * which adjusts cell dimensions in logical space to ones shown on
		 * screen. Calculation is based on current camera properties (e.g. rotation)
		 * + given cell image dimensions for camera's layer
		 */
		void updateReferenceScale();

		/** Gets logical cell image dimensions for given layer
		 */
		DoublePoint getLogicalCellDimensions(Layer* layer);
		
		DoubleMatrix m_matrix;
		DoubleMatrix m_inverse_matrix;
		double m_tilt;
		double m_rotation;
		double m_zoom;
		Location m_location;
		ScreenPoint m_prev_origo;
		ScreenPoint m_cur_origo;
		Rect m_viewport;
		bool m_view_updated;
		unsigned int m_screen_cell_width;
		unsigned int m_screen_cell_height;
		double m_reference_scale;
		bool m_enabled;
		Instance* m_attachedto;
		// caches calculated image dimensions for already queried & calculated layers
		std::map<Layer*, Point> m_image_dimensions;
		bool m_iswarped;
		
		// list of renderers managed by the view
		std::map<std::string, RendererBase*> m_renderers;
		std::list<RendererBase*> m_pipeline;
		bool m_updated; // false, if view has never been updated before
	
		RenderBackend* m_renderbackend;
		ImagePool* m_ipool;
		AnimationPool* m_apool;
		
		// caches layer -> instances structure between renders e.g. to fast query of mouse picking order
		t_layer_to_instances m_layer_to_instances;
	};
}
#endif