00001 using System;
00002 using Microsoft.Xna.Framework;
00003 using Microsoft.Xna.Framework.Graphics;
00004
00005 namespace Mindshifter
00006 {
00010 public enum TransformSpace
00011 {
00012 CameraSpace,
00013 WorldSpace,
00014 }
00015
00020 public sealed class QuaternionCamera : ICamera
00021 {
00022 private Matrix mViewMatrix;
00023 private Matrix mInverseViewMatrix;
00024
00025 private Projection mProjection;
00026
00027 private Vector3 mPosition;
00028 private Quaternion mOrientation;
00029
00030 private bool mRequiresUpdate;
00031
00035 public Matrix ViewMatrix
00036 {
00037 get
00038 {
00039 if (mRequiresUpdate)
00040 BuildMatrix();
00041
00042 return mViewMatrix;
00043 }
00044 }
00045
00049 public Matrix InverseViewMatrix
00050 {
00051 get
00052 {
00053 if (mRequiresUpdate)
00054 BuildMatrix();
00055
00056 return mInverseViewMatrix;
00057 }
00058 }
00059
00063 public Matrix ProjectionMatrix
00064 {
00065 get { return mProjection.Matrix; }
00066 }
00067
00071 public Projection Projection
00072 {
00073 get { return mProjection; }
00074 }
00075
00079 public Vector3 Position
00080 {
00081 get { return mPosition; }
00082 set
00083 {
00084 mPosition = value;
00085 mRequiresUpdate = true;
00086 }
00087 }
00088
00092 public Quaternion Orientation
00093 {
00094 get { return mOrientation; }
00095 set
00096 {
00097 Quaternion.Normalize(ref value, out mOrientation);
00098 mRequiresUpdate = true;
00099 }
00100 }
00101
00105 public float X
00106 {
00107 get { return mPosition.X; }
00108 set
00109 {
00110 mPosition.X = value;
00111 mRequiresUpdate = true;
00112 }
00113 }
00114
00118 public float Y
00119 {
00120 get { return mPosition.Y; }
00121 set
00122 {
00123 mPosition.Y = value;
00124 mRequiresUpdate = true;
00125 }
00126 }
00127
00131 public float Z
00132 {
00133 get { return mPosition.Z; }
00134 set
00135 {
00136 mPosition.Z = value;
00137 mRequiresUpdate = true;
00138 }
00139 }
00140
00144 public Vector3 Up
00145 {
00146 get
00147 {
00148 if (mRequiresUpdate)
00149 BuildMatrix();
00150
00151 return mInverseViewMatrix.Up;
00152 }
00153 }
00154
00158 public Vector3 Forward
00159 {
00160 get
00161 {
00162 if (mRequiresUpdate)
00163 BuildMatrix();
00164
00165 return mInverseViewMatrix.Forward;
00166 }
00167 }
00168
00172 public Vector3 Right
00173 {
00174 get
00175 {
00176 if (mRequiresUpdate)
00177 BuildMatrix();
00178
00179 return mInverseViewMatrix.Right;
00180 }
00181 }
00182
00186 public BoundingFrustum Frustum
00187 {
00188 get { return new BoundingFrustum(ViewMatrix * ProjectionMatrix); }
00189 }
00190
00196 public QuaternionCamera()
00197 : this(Vector3.Zero, Quaternion.Identity, (float)Math.PI / 4f, 4f /3f, 1f, 1000f)
00198 {
00199
00200 }
00201
00209 public QuaternionCamera(Vector3 position, Quaternion orientation)
00210 : this(position, orientation, (float)Math.PI / 4f, 4f / 3f, 1f, 1000f)
00211 {
00212
00213 }
00214
00225 public QuaternionCamera(Vector3 position, Quaternion orientation, float fov,
00226 float aspect, float near, float far)
00227 {
00228 mPosition = position;
00229 mOrientation = orientation;
00230 mViewMatrix = Matrix.Identity;
00231 mInverseViewMatrix = Matrix.Identity;
00232
00233 mProjection = new Projection(fov, aspect, near, far);
00234
00235 mRequiresUpdate = true;
00236 }
00237
00247 public QuaternionCamera(Vector3 position, Quaternion orientation, Rectangle rect,
00248 float near, float far)
00249 {
00250 mPosition = position;
00251 mOrientation = orientation;
00252 mViewMatrix = Matrix.Identity;
00253 mInverseViewMatrix = Matrix.Identity;
00254
00255 mProjection = new Projection(rect.X, rect.Y, rect.Width, rect.Height, near, far);
00256
00257 mRequiresUpdate = true;
00258 }
00259
00265 public void Pitch(float pitchAmount, TransformSpace space)
00266 {
00267 if (mRequiresUpdate)
00268 BuildMatrix();
00269
00270 switch (space)
00271 {
00272 case TransformSpace.CameraSpace:
00273 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Normalize(mInverseViewMatrix.Right), pitchAmount);
00274 break;
00275
00276 case TransformSpace.WorldSpace:
00277 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Right, pitchAmount);
00278 break;
00279 }
00280
00281 mRequiresUpdate = true;
00282 }
00283
00288 public void Pitch(float pitchAmount)
00289 {
00290 Pitch(pitchAmount, TransformSpace.CameraSpace);
00291 }
00292
00298 public void Yaw(float yawAmount, TransformSpace space)
00299 {
00300 if (mRequiresUpdate)
00301 BuildMatrix();
00302
00303 switch (space)
00304 {
00305 case TransformSpace.CameraSpace:
00306 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Normalize(mInverseViewMatrix.Up), yawAmount);
00307 break;
00308
00309 case TransformSpace.WorldSpace:
00310 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Up, yawAmount);
00311 break;
00312 }
00313
00314 mRequiresUpdate = true;
00315 }
00316
00321 public void Yaw(float yawAmount)
00322 {
00323 Yaw(yawAmount, TransformSpace.CameraSpace);
00324 }
00325
00331 public void Roll(float rollAmount, TransformSpace space)
00332 {
00333 if (mRequiresUpdate)
00334 BuildMatrix();
00335
00336 switch (space)
00337 {
00338 case TransformSpace.CameraSpace:
00339 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Normalize(mInverseViewMatrix.Backward), rollAmount);
00340 break;
00341
00342 case TransformSpace.WorldSpace:
00343 mOrientation *= Quaternion.CreateFromAxisAngle(Vector3.Backward, rollAmount);
00344 break;
00345 }
00346
00347 mRequiresUpdate = true;
00348 }
00349
00354 public void Roll(float rollAmount)
00355 {
00356 Roll(rollAmount, TransformSpace.CameraSpace);
00357 }
00358
00364 public void Translate(Vector3 amount, TransformSpace space)
00365 {
00366 if (mRequiresUpdate)
00367 BuildMatrix();
00368
00369 switch (space)
00370 {
00371 case TransformSpace.CameraSpace:
00372 mPosition += mInverseViewMatrix.Right * amount.X +
00373 mInverseViewMatrix.Up * amount.Y + mInverseViewMatrix.Backward * amount.Z;
00374 break;
00375
00376 case TransformSpace.WorldSpace:
00377 mPosition += amount;
00378 break;
00379 }
00380
00381 mRequiresUpdate = true;
00382 }
00383
00388 public void Translate(Vector3 amount)
00389 {
00390 Translate(amount, TransformSpace.CameraSpace);
00391 }
00392
00398 public void TranslateX(float amount, TransformSpace space)
00399 {
00400 if (mRequiresUpdate)
00401 BuildMatrix();
00402
00403 switch (space)
00404 {
00405 case TransformSpace.CameraSpace:
00406 mPosition += mInverseViewMatrix.Right * amount;
00407 break;
00408
00409 case TransformSpace.WorldSpace:
00410 mPosition.X += amount;
00411 break;
00412 }
00413
00414 mRequiresUpdate = true;
00415 }
00416
00421 public void TranslateX(float amount)
00422 {
00423 TranslateX(amount, TransformSpace.CameraSpace);
00424 }
00425
00431 public void TranslateY(float amount, TransformSpace space)
00432 {
00433 if (mRequiresUpdate)
00434 BuildMatrix();
00435
00436 switch (space)
00437 {
00438 case TransformSpace.CameraSpace:
00439 mPosition += mInverseViewMatrix.Up * amount;
00440 break;
00441
00442 case TransformSpace.WorldSpace:
00443 mPosition.Y += amount;
00444 break;
00445 }
00446
00447 mRequiresUpdate = true;
00448 }
00449
00454 public void TranslateY(float amount)
00455 {
00456 TranslateY(amount, TransformSpace.CameraSpace);
00457 }
00458
00464 public void TranslateZ(float amount, TransformSpace space)
00465 {
00466 if (mRequiresUpdate)
00467 BuildMatrix();
00468
00469 switch (space)
00470 {
00471 case TransformSpace.CameraSpace:
00472 mPosition += mInverseViewMatrix.Backward * amount;
00473 break;
00474
00475 case TransformSpace.WorldSpace:
00476 mPosition.Z += amount;
00477 break;
00478 }
00479
00480 mRequiresUpdate = true;
00481 }
00482
00487 public void TranslateZ(float amount)
00488 {
00489 TranslateZ(amount, TransformSpace.CameraSpace);
00490 }
00491
00495 private void BuildMatrix()
00496 {
00497 Vector3 P;
00498 Quaternion O;
00499 Matrix T;
00500 Matrix R;
00501
00502 Vector3.Negate(ref mPosition, out P);
00503 Quaternion.Negate(ref mOrientation, out O);
00504
00505 Matrix.CreateTranslation(ref P, out T);
00506 Matrix.CreateFromQuaternion(ref O, out R);
00507
00508 Matrix.Multiply(ref T, ref R, out mViewMatrix);
00509 Matrix.Invert(ref mViewMatrix, out mInverseViewMatrix);
00510
00511 mRequiresUpdate = false;
00512 }
00513
00518 public void Update(float elapsedSeconds)
00519 {
00520 if (mRequiresUpdate)
00521 BuildMatrix();
00522 }
00523
00529 public Ray GetPickRay(Vector2 position, Viewport viewport)
00530 {
00531 Vector3 near = viewport.Unproject(new Vector3(position, 0f), ProjectionMatrix,
00532 ViewMatrix, Matrix.Identity);
00533
00534 Vector3 far = viewport.Unproject(new Vector3(position, 1f), ProjectionMatrix,
00535 ViewMatrix, Matrix.Identity);
00536
00537 return new Ray(near, Vector3.Normalize(far - near));
00538 }
00539 }
00540 }