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 }