Hello Curtis,

> First let me explain what I need to do.  I need to configure an
> "asymmetric view frustum".  I need to place 3 monitors next to each
> other, aligned along a flat plane.  The view drawn in each monitor needs
> to be projected on that same flat plane.  I cannot just set a view
> offset for the side channels because the view won't come out right.  If
> I simply rotate the view offset and take a symmetric view frustum, the
> plane of projection will be perpendicular to the viewer in each
> channel.  That won't work for this particular application and it's not
> what I need.  I need to configure an actual asymmetric view frustum for
> each side channel.  If someone thinks they can help me, but is confused
> by my description, I'm happy to explain this further.  Think about this

I hope the attached camera class might help you - I implemented support for
off-center and tiled projections for a similar project (powerwall
rendering).

The parts that probably interest you are in update_frustum() and the
parameters set via the setTiling(...) method.

hope to help,

 Manuel
/***************************************************************************
                          Camera.cpp  -  description
                             -------------------
    begin                : Thu Apr 18 2002
    written by           : Manuel Massing
    email                : [EMAIL PROTECTED]
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "Camera.h"

Camera::Camera()
{
	n = 1.0f;
	f = 4000.f;
	fov = 45.f;
	eye_separation = 0.f;
	focal_length = 2000.f;

	resx = 1; resy = 1;
	llx = lly = 0;
	aspect = 4.f/3.f;;
	tile_t = tile_r = 1.f;
	tile_b = tile_l = 0.f;
	frustum_dirty = orientation_dirty = obj2clip_dirty = true;
}

void Camera::update_frustum() const
{
	if (frustum_dirty) {
		// Calculate the symmetric frustum extent at focal distance.
		float frustum_top = focal_length*tanf(fov*M_PI/360.f);
		float frustum_left = -frustum_top*aspect*((tile_t-tile_b)/(tile_r-tile_l));

		// Adapt frustum to desired viewport region.
		t = (2.f*tile_t - 1.f)*frustum_top;
		b = (2.f*tile_b - 1.f)*frustum_top;
		l = (1.f - 2.f*tile_l)*(frustum_left - eye_separation);
		r = (1.f - 2.f*tile_r)*(frustum_left - eye_separation);

		float rescale = n/focal_length;
		t*= rescale;
		b*= rescale;
		l*= rescale;
		r*= rescale;
	
		local_clipplane[cpNear] = Plane(Vector3d(0.f, 0.f, -1.f), n); // near
		local_clipplane[cpFar] = Plane(Vector3d(0.f, 0.f, 1.f), -f);  // far

		Vector3d cpn(n, 0, l);
		cpn*=1.f/cpn.length();
		local_clipplane[cpLeft] = Plane(cpn, 0);                      // left

		cpn = !Vector3d(-n, 0, -r);
		local_clipplane[cpRight] = Plane(cpn, 0);                     // right

		cpn = !Vector3d(0, -n, -t);
		local_clipplane[cpTop] = Plane(cpn, 0);                       // top

		cpn = !Vector3d(0, n, b);
		local_clipplane[cpBottom] = Plane(cpn, 0);                    // bottom

		frustum = Matrix4x4( 2.f*n/(r - l),  0.f,             (r + l)/(r - l),   0.f,
		                     0.f,            2.f*n/(t - b),   (t + b)/(t - b),   0.f,
		                     0.f,            0.f,            -(f + n)/(f - n),  -2.f*f*n/(f - n),
		                     0.f,            0.f,            -1.f,               0.f            );


		#ifdef LOGGING
		if (hasLogfile())
			get_logfile().setItem("fov", fov);
		#endif

		frustum_dirty = false;
		obj2clip_dirty = true;
	}
}

void Camera::update_modelview() const
{
	if (orientation_dirty) {
		if (vp == vpLookAt) {
			Vector3d xAxis, zAxis, tmp_up = up;

			zAxis = !(lookat - pos);
			xAxis = !cross(zAxis, tmp_up);
			tmp_up = !cross(xAxis, zAxis);

			Matrix3x3 rotation = Matrix3x3(xAxis, tmp_up, -zAxis).transpose();
			modelview = Matrix4x4(rotation, rotation*(-pos - eye_separation*xAxis));
		}
		else if (vp == vpTargetRoll) {
			Vector3d xAxis, zAxis, tmp_up = up;
			zAxis = !(lookat - pos);
			// Make sure we are not looking down the y-axis
			if (!zAxis.x && !zAxis.z)
				tmp_up = Vector3d(-zAxis.y, 0.f, 0.f);
			else
				tmp_up = Vector3d(0.f, 1.f, 0.f);

			xAxis = !cross(zAxis, tmp_up);
			tmp_up = !cross(xAxis, zAxis);

			Matrix3x3 rotation = Matrix3x3(xAxis, tmp_up, -zAxis).transpose();
			Matrix3x3 camroll;
			camroll.setRotateZ(roll);
			rotation = camroll * rotation;
			modelview = Matrix4x4(rotation, rotation*(-pos - eye_separation*xAxis));
		}
		else if (vp == vpOrientationMatrix) {
			Matrix3x3 rotate = orientation.transpose();
			modelview = Matrix4x4(rotate, rotate*(-pos - eye_separation*orientation(0)));
		}
		obj2clip_dirty = true;
		orientation_dirty = false;
	}
}

void Camera::update() const
{
	update_frustum();
	update_modelview();
	if (obj2clip_dirty) {
		for (int i = 0; i < 6; i++)
			world_clipplane[i] = modelview.affine_inverse()*local_clipplane[i];
		obj2clip = modelview*frustum;
		obj2clip_dirty = false;
	}

}

void Camera::activate() const
{
	update();

	//should compare to current viewport (static)
	glViewport(llx, lly, resx, resy);

	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf((GLfloat *)&frustum.mat);

	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf((GLfloat *)&modelview.mat);
}

void Camera::setResolution(int resX, int resY, int llX, int llY, float PixelAspect)
{
	resx = resX;
	resy = resY;
	llx = llX;
	lly = lly;
	aspect = (float)resX*PixelAspect/(float)resY;
}

void Camera::setDistanceRange(float Near, float Far)
{
	frustum_dirty = true;
	n = Near;
	f = Far;
}

void Camera::getDistanceRange(float &Near, float &Far) const
{
	Near = n;
	Far = f;
}

void Camera::setFoV(float FoV)
{
	frustum_dirty = true;
	fov = FoV;
}

void Camera::setSeparation(float Separation)
{
	if (fabs(eye_separation - Separation) > 0.f) {
		eye_separation = Separation;
		frustum_dirty = true;
		orientation_dirty = true;
	}
}

float Camera::getSeparation() const
{
	return eye_separation;
}

void Camera::setFocalLength(float FocalLength)
{
	if (focal_length != FocalLength) {
		focal_length = FocalLength;
		if (eye_separation > 0.f)
			frustum_dirty = true;
	}
}

float Camera::getFocalLength() const
{
	return focal_length;
}

// Set the camera tiling,
void Camera::setTiling(float tileL, float tileR, float tileT, float tileB)
{
	assert((0.f <= tileL) && (1.f >= tileL));
	assert((0.f <= tileR) && (1.f >= tileR));
	assert((0.f <= tileT) && (1.f >= tileT));
	assert((0.f <= tileB) && (1.f >= tileB));
	assert(tileL < tileR);
	assert(tileB < tileT);

	if ((tile_l != tileL) || (tile_r != tileR) ||(tile_t != tileT) || (tile_b != tileB)) {
		tile_l = tileL;
		tile_r = tileR;
		tile_t = tileT;
		tile_b = tileB;
		frustum_dirty = true;
	}
}


void Camera::setPosition(const Vector3d &Pos)
{
	orientation_dirty = true;
	pos = Pos;
}

void Camera::setTargetRoll(const Vector3d &Pos, const Vector3d &Target, float Roll)
{
	orientation_dirty = true; vp = vpTargetRoll;
	pos = Pos;
	lookat = Target;
	roll = Roll;
}

void Camera::setLookAt(const Vector3d &Pos, const Vector3d &LookAt, const Vector3d &Up)
{
	orientation_dirty = true; vp = vpLookAt;
	pos = Pos;
	lookat = LookAt;
	up = Up;
}

void Camera::setOrientation(const Matrix3x3 &Orientation)
{
	orientation_dirty = true; vp = vpOrientationMatrix;
	orientation = Orientation;
}

Matrix3x3 Camera::getOrientation() const
{
	update();
	return modelview.get3x3SubMatrix().transpose();
}

float Camera::getFoV() const
{
	return fov;
}

void Camera::getResolution(int &resX, int &resY) const
{
	resX = resx;
	resY = resy;
}

// returns projected size in pixels of a unit length element along
// the x and y direction at distance "distance" from the image plane.
void Camera::getPixelProjection(float &pixelsX, float &pixelsY, float distance) const
{
	distance*= 2.f*tanf(fov*M_PI/360.f);
	pixelsY = (float)resy/distance;
	pixelsX = (float)resx/(distance*aspect);
}

const Vector3d &Camera::getPosition() const
{
	return pos;
}

  const Vector3d &Camera::getLookAt() const
{
	return lookat;
}

Vector3d Camera::getRightVector() const
{
	update_modelview();
	return Vector3d(modelview.mat[0], modelview.mat[4], modelview.mat[8]);
}

Vector3d Camera::getUpVector() const
{
	update_modelview();
	return Vector3d(modelview.mat[1], modelview.mat[5], modelview.mat[9]);
}

Vector3d Camera::getForwardVector() const
{
	update_modelview();
	return -Vector3d(modelview.mat[2], modelview.mat[6], modelview.mat[10]);
}

const Plane &Camera::getClipPlane(eClipPlane cp) const
{
	uint i = (uint)cp;
	if (i > 5) i = 0;
	update();
	return world_clipplane[i];
}

Vector3d Camera::unprojectFromDevice(const Vector3d &win) const
{
	update();
	Vector4d w((win.x + llx)*2.f/resx - 1.f, 
	           (win.y + lly)*2.f/resy - 1.f,
	            win.z,
	            1.f);

	//cout<<"unproject clip space: ";                 
	//w.print();

	w = obj2clip.inverse()*w;
	return Vector3d(w.x/w.w, w.y/w.w, w.z/w.w);
}

void Camera::draw() const
{
	update();
	Matrix4x4 cam2world = modelview.affine_inverse();

	float fn_ratio = 250.f; // f/n;
	Vector3d lt = cam2world*Vector3d(l, t, -n);
	Vector3d lt_f = cam2world*(Vector3d(l, t, -n)*fn_ratio);
	Vector3d rt = cam2world*Vector3d(r, t, -n);
	Vector3d rt_f = cam2world*(Vector3d(r, t, -n)*fn_ratio);
	Vector3d lb = cam2world*Vector3d(l, b, -n);
	Vector3d lb_f = cam2world*(Vector3d(l, b, -n)*fn_ratio);
	Vector3d rb = cam2world*Vector3d(r, b, -n);
	Vector3d rb_f = cam2world*(Vector3d(r, b, -n)*fn_ratio);


	glColor3f(1.0f, 1.f, 1.f);
	glBegin(GL_LINES);
	glVertex3fv((GLfloat *)&pos);
	glVertex3fv((GLfloat *)&lt);
	glVertex3fv((GLfloat *)&pos);
	glVertex3fv((GLfloat *)&rt);
	glVertex3fv((GLfloat *)&pos);
	glVertex3fv((GLfloat *)&lb);
	glVertex3fv((GLfloat *)&pos);

	glVertex3fv((GLfloat *)&rb);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glVertex3fv((GLfloat *)&lt);
	glVertex3fv((GLfloat *)&rt);
	glVertex3fv((GLfloat *)&rb);
	glVertex3fv((GLfloat *)&lb);
	glEnd();

	glColor3f(1.f, 1.f, 1.f);
	glBegin(GL_LINES);
	glVertex3fv((GLfloat *)&lt);
	glVertex3fv((GLfloat *)&lt_f);
	glVertex3fv((GLfloat *)&rt);
	glVertex3fv((GLfloat *)&rt_f);
	glVertex3fv((GLfloat *)&lb);
	glVertex3fv((GLfloat *)&lb_f);
	glVertex3fv((GLfloat *)&rb);
	glVertex3fv((GLfloat *)&rb_f);
	glEnd();

/*	glBegin(GL_LINE_LOOP);
	glVertex3fv((GLfloat *)&lt_f);

	glVertex3fv((GLfloat *)&rt_f);
	glVertex3fv((GLfloat *)&rb_f);
	glVertex3fv((GLfloat *)&lb_f);
	glEnd();*/
}
/***************************************************************************
                          Camera.h -  description
                             -------------------
    begin                : Sun Apr 7 2002
    written by           : Manuel Massing
    email                : [EMAIL PROTECTED]
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef CAMERA_H
#define CAMERA_H

#include "Global.h"

#include <cmath>
#include "glew.h"

#include "Base.h"
#include "Matrix.h"
#include "Plane.h"
#include "Vector.h"

/**
 *\brief An OpenGL camera class.
 *
 *\class Camera
 * This class is responsible for building of projection and model-view matrices
 * from different parametrisations, for the specification of clipping planes and for updating
 * OpenGL projection & model-view matrices accordingly.
 */
 
class Camera : protected Base {
protected:
	/**
	 *\enum eViewParametrisations Indicates employed viewing parameters.
	 */
	enum eViewParametrisations {
		vpLookAt,
		vpTargetRoll,
		vpOrientationMatrix
	};

	mutable Plane local_clipplane[6];
	mutable Plane world_clipplane[6];

	/**
	 * The Modelview Matrix can be specified in several independent ways,
	 * hence we select the appropriate ones via the enumeration variable "vp".
	 */
	eViewParametrisations vp;
	Vector3d pos, lookat, up;
	Matrix3x3 orientation;
	// (Unfortunately, C++ doesn't allow for classes in unions)
	union {
		float roll;
	};

	mutable Matrix4x4 obj2clip, modelview, frustum;

	/* The projection matrix is parametrized by horizontal and vertical resolution and the
         coordinates of the lower left corner of the viewport */
	int resx, resy;
	int llx, lly;

	/* The camera frustum is parametrized by the vertical field of view, the distance of
	   near & far clipping plane, the display aspect-ratio, and occular distance
	   (relative to the camera position). */
	float fov;
	float aspect;
	float n, f;
	float eye_separation;
	float focal_length;

	mutable float l, r, b, t;

	//! Indicates wether to use the camera for a sector of a tiled display.
	bool tiled_display;
	float tile_l, tile_r, tile_t, tile_b; // Percent of the imaging plane, left = 0, right = 1


	/**
	 * "Dirty flags" specify changed parameters to allow for deferred matrix calculations
	 */
	mutable bool obj2clip_dirty, frustum_dirty, orientation_dirty;

	/**
	 * Updates the projection matrix and clipping planes according to field of view and aspect ratio.
       */
	void update_frustum() const;
	/**
       * Updates the camera model-view matrix according to the chosen paramterisation.
       */
	void update_modelview() const;
      /**
	 * Updates all camera matrices and the clipping planes if viewing parameters have changed.
       */
	void update() const;

public:
	enum eClipPlane {
		cpLeft   = 0,
		cpRight  = 1,
		cpBottom = 2,
		cpTop    = 3,
		cpNear   = 4,
		cpFar    = 5
	};

	enum eClipFlags {
		cfLeft   = 1<<cpLeft,
		cfRight  = 1<<cpRight,
		cfBottom = 1<<cpBottom,
		cfTop    = 1<<cpTop,
		cfNear   = 1<<cpNear,
		cfFar    = 1<<cpFar
	};

	Camera();

	/**
	 * Updates OpenGL projection and model-view matrix according to current viewing parameters.
	 */
	void activate() const;

	/**
	 * Sets the resolution of the destination display
	 */
	void setResolution(int resX, int resY, int llX = 0, int llY = 0, float PixelAspect = 1.0f);

	/**
	 * Sets the distance between eyepoint and near and far clipping planes.
	 */
	void setDistanceRange(float Near, float Far);

	/**
	 * Sets the vertical field-of-view.
	 * \param FoV The field-of-view angle in degrees.
	 */
	void setFoV(float FoV);

	/**
	 * Set the distance of the eye relative to the center of projection on the imaging plane.
	 */
//	void setEyeDistance(float eyeDistanceH, int eyeDistanceV);

	/**
	 * Indicate the viewport sector when rendering part of a tiled display, e.g. on a PowerWall.
	 */
	 void setTiling(float tileL = 0.f, float tileR = 1.f, float tileT = 0.f, float tileB = 1.f);

	/**
	 * Sets the camera position, in world space coordinates.
	 */
	void setPosition(const Vector3d &Pos);

	/**
	 * Specify camera orientation using position and target, and a roll angle.
	 *\param Pos Camera position in world space coordinates.
	 *\param Target Target of the camera in world space coordinates.
	 *\param Roll The amount of roll applied to the camera, in degrees.
	 */
	void setTargetRoll(const Vector3d &Pos, const Vector3d &Target, float Roll);

	/**
	 * Specify camera orientation using position, lookat and the up vector.
	 *\param Pos Camera position in world space coordinates.
	 *\param LookAt Target of the camera in world space coordinates.
	 *\param Up An estimation of the up (y-axis) vector, which defines the camera roll.
	 */
	void setLookAt(const Vector3d &Pos, const Vector3d &LookAt, const Vector3d &Up);

	void setOrientation(const Matrix3x3 &Orientation);
	Matrix3x3 getOrientation() const;


	void  setSeparation(float Separation);
	float getSeparation() const;

	void  setFocalLength(float FocalLength);
	float getFocalLength() const;

	float getFoV() const;
	void  getResolution(int &resX, int &resY) const;

	/**
	 * \returns the horizontal and vertical projected size of an unit length square
	 * parallel to, and at distance "distance" from the imaging plane.
	 */
	void getPixelProjection(float &pixelsX, float &pixelsY, float distance = 1.0f) const;

	Vector3d projectFromWorldspace(Vector3d &wpos) const
	{
		return Vector3d(0, 0, 0);
	}

	/**
	 *\returns the current camera position.
	 */
	const Vector3d &getPosition() const;
	const Vector3d &getLookAt()   const;

	Vector3d getRightVector()   const;
	Vector3d getUpVector()      const;
	Vector3d getForwardVector() const;

	const Plane &getClipPlane(eClipPlane cp) const;
	void getDistanceRange(float &near, float &far) const;

	/**
	 * Transforms the given vector into the canonical view frustum [origo = center of projection]
	 */
	Vector3d projectToScreen(const Vector3d &v) const
	{
		update();
		Vector4d s = obj2clip*Vector4d(v, 1.0f);
		float fac = 1.f/s.w;
		return Vector3d(s.x*fac, s.y*fac, s.z*fac);
	}

	/**
	 * Transforms the given vector into window coordinates [origo = lower left corner]
	 */
	Vector3d projectToWindow(const Vector3d &v) const
	{
		update();
		Vector4d s = obj2clip*Vector4d(v, 1.0f);
		float fac = 0.5f/s.w;
		return Vector3d(s.x*fac + 0.5f, s.y*fac + 0.5f, fabs(f-n)*s.z/s.w + (n+f/2.f));
	}
	
	/**
	 * Transforms the given vector from device coordinates [origo = lower left corner]
	 * to world space coordinates.
	 * \note that the z value is given in depth buffer units, not in normalized device
	 * coordinates!
	 */
	Vector3d unprojectFromDevice(const Vector3d &win) const;

	/**
	 * Projects the given vector and converts it into 2d device coordinates [origo = lower left corner]
	 */
	Vector3d projectToDevice(const Vector3d &v) const
	{
		update();
		Vector4d s = obj2clip*Vector4d(v, 1.0f);
		float fac = 0.5f/s.w;
		return Vector3d(llx + s.x*fac*(float)resx + resx*0.5f, lly + s.y*fac*(float)resy + resy*0.5f, s.z/s.w);
	}


	void draw() const;

	int getClippingFlags(const Vector3d &v) const
	{
		update();

		// Transform the vertex to clip-coordinates
		Vector4d clip_coord = obj2clip*Vector4d(v, 1.0f);

		int c = 0;
		if (clip_coord.x < -clip_coord.w)
			c|= cfLeft;
		if (clip_coord.x >  clip_coord.w)
			c|= cfRight;
		if (clip_coord.y < -clip_coord.w)
			c|= cfBottom;
		if (clip_coord.y >  clip_coord.w)
			c|= cfTop;
		if (clip_coord.z < -clip_coord.w)
			c|= cfNear;
		if (clip_coord.z >  clip_coord.w)
			c|= cfFar;

		return c;
	}
};

#endif
_______________________________________________
Flightgear-devel mailing list
[email protected]
http://mail.flightgear.org/mailman/listinfo/flightgear-devel
2f585eeea02e2c79d7b1d8c4963bae2d

Reply via email to