You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
18 KiB
327 lines
18 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
|
|
||
|
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 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program 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 General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||
|
or by snail mail at:
|
||
|
Cyan Worlds, Inc.
|
||
|
14617 N Newport Hwy
|
||
|
Mead, WA 99021
|
||
|
|
||
|
*==LICENSE==*/
|
||
|
#ifndef plViewTransform_inc
|
||
|
#define plViewTransform_inc
|
||
|
|
||
|
#include "hsMatrix44.h"
|
||
|
#include "hsGeometry3.h"
|
||
|
#include "hsPoint2.h"
|
||
|
|
||
|
class hsBounds3;
|
||
|
class hsStream;
|
||
|
|
||
|
// There's a lot here, but there's a lot one might want to do with view transforms.
|
||
|
// It's easiest to grab the structure thinking of it in terms of the different
|
||
|
// spaces you might want a point in. The ones supported here are:
|
||
|
// Screen - this is actual pixel values
|
||
|
// NDC - Normalized Device Coordinates, these are post W divide, so the
|
||
|
// valid range is x = [-1..1], y = [-1..1], z = [0..1]
|
||
|
// Camera - relative to the camera, with (0,0,-1) directly in front of the camera,
|
||
|
// and (0, 1, 0) directly above the camera.
|
||
|
// World - Universal world space.
|
||
|
// Map - arbitrary mapping of NDC. Like from [(-1,-1,0)..(1,1,1)] => [(0,0,0)..(1,1,1)] (default).
|
||
|
// Note that there is no object space here. There could be, but I wanted something more constant, more
|
||
|
// world independent, so the ViewTransform remains constant unless the view changes. Whatever.
|
||
|
//
|
||
|
// So we're broken into functional sections:
|
||
|
// 1) Queries on the state of this view transform, properties, matrix values, whatever. Note that you
|
||
|
// generally shouldn't be reading a value (e.g. matrix) out of the ViewTransform, but let the
|
||
|
// ViewTransform perform the operation you would with the matrix.
|
||
|
// 2) Setting state, properties, matrix values, whatever. There's a couple of really bizarre variants
|
||
|
// (like the union and intersection of view frustums). Full support is available for perspective
|
||
|
// or orthogonal views. An additional capability (not necessary) is offset transforms, useful
|
||
|
// for rendering textures. If you don't what good they are, they probably aren't any good to you.
|
||
|
// 3) Conversions of points from one space to another. You may notice that there are a whole lot of them.
|
||
|
// There is a conversion from each of the spaces above to each of the other spaces. That's 12
|
||
|
// transformations right there. But Points and Vectors actually transform differently, so there
|
||
|
// are different versions for those. Where they could be treated the same, there is an hsScalarTriple
|
||
|
// version that does the actual work, and then casting versions to come and go from the right type.
|
||
|
// 4) Read and write (note these are NOT virtual).
|
||
|
//
|
||
|
// More Notes:
|
||
|
// This class has no virtual functions.
|
||
|
// You must set the width and height for Screen queries to work (duh!).
|
||
|
// ViewPort defaults to cover the entire width and height. Viewport only affects mapping, not clipping
|
||
|
// (i.e. reducing the viewport width will still render the same stuff, just skinnier).
|
||
|
// The actual data here is very small, this is mostly a collection of functions, so where possible,
|
||
|
// just keep one of these to pass around, (e.g. rather than keeping track of FOV etc and passing
|
||
|
// those around).
|
||
|
//
|
||
|
class plViewTransform
|
||
|
{
|
||
|
public:
|
||
|
plViewTransform();
|
||
|
~plViewTransform() {}
|
||
|
|
||
|
void Reset(); // resets to default state
|
||
|
|
||
|
// Queries
|
||
|
hsBool GetOrthogonal() const { return IHasFlag(kOrthogonal); }
|
||
|
hsBool GetPerspective() const { return !GetOrthogonal(); }
|
||
|
hsBool GetViewPortRelative() const { return IHasFlag(kViewPortRelative); }
|
||
|
|
||
|
// Next, all our matrices.
|
||
|
const hsMatrix44& GetCameraToWorld() const { return fCameraToWorld; }
|
||
|
const hsMatrix44& GetWorldToCamera() const { return fWorldToCamera; }
|
||
|
const hsMatrix44& GetCameraToNDC() const { return ICheckCameraToNDC(); }
|
||
|
const hsMatrix44& GetWorldToNDC() const { return ICheckWorldToNDC(); }
|
||
|
|
||
|
hsPoint3 GetPosition() const { return GetCameraToWorld().GetTranslate(); }
|
||
|
hsVector3 GetDirection() const { return *((hsVector3 *)&GetWorldToCamera().fMap[2]); }
|
||
|
hsVector3 GetUp() const { return *((hsVector3*)&GetWorldToCamera().fMap[1]); }
|
||
|
hsVector3 GetAcross() const { return *((hsVector3*)&GetWorldToCamera().fMap[0]); }
|
||
|
|
||
|
UInt16 GetScreenWidth() const { return fWidth; }
|
||
|
UInt16 GetScreenHeight() const { return fHeight; }
|
||
|
|
||
|
void GetViewPort(hsPoint2& mins, hsPoint2& maxs) const;
|
||
|
void GetViewPort(int& loX, int& loY, int& hiX, int& hiY) const;
|
||
|
int GetViewPortWidth() const { return GetViewPortRight() - GetViewPortLeft(); }
|
||
|
int GetViewPortHeight() const { return GetViewPortBottom() - GetViewPortTop(); }
|
||
|
int GetViewPortLeft() const { return int(GetViewPortLoX()); }
|
||
|
int GetViewPortTop() const { return int(GetViewPortLoY()); }
|
||
|
int GetViewPortRight() const { return int(GetViewPortHiX()); }
|
||
|
int GetViewPortBottom() const { return int(GetViewPortHiY()); }
|
||
|
float GetViewPortLoX() const { return GetViewPortRelative() ? fViewPortX.fX * fWidth : fViewPortX.fX; }
|
||
|
float GetViewPortLoY() const { return GetViewPortRelative() ? fViewPortY.fX * fHeight : fViewPortY.fX; }
|
||
|
float GetViewPortHiX() const { return GetViewPortRelative() ? fViewPortX.fY * fWidth : fViewPortX.fY; }
|
||
|
float GetViewPortHiY() const { return GetViewPortRelative() ? fViewPortY.fY * fHeight : fViewPortY.fY; }
|
||
|
|
||
|
hsPoint3 GetMapMin() const { return fMapMin; }
|
||
|
hsPoint3 GetMapMax() const { return fMapMax; }
|
||
|
void GetMapping(hsPoint3& mapMin, hsPoint3& mapMax) const { mapMin = fMapMin; mapMax = fMapMax; }
|
||
|
|
||
|
hsScalar GetFovX() const;
|
||
|
hsScalar GetFovY() const;
|
||
|
hsScalar GetFovXDeg() const { return hsScalarRadToDeg(GetFovX()); }
|
||
|
hsScalar GetFovYDeg() const { return hsScalarRadToDeg(GetFovY()); }
|
||
|
hsScalar GetOrthoWidth() const { return fMax.fX - fMin.fX; }
|
||
|
hsScalar GetOrthoHeight() const { return fMax.fY - fMin.fY; }
|
||
|
hsScalar GetHither() const { return fMin.fZ; }
|
||
|
hsScalar GetYon() const { return fMax.fZ; }
|
||
|
void GetDepth(hsScalar& hither, hsScalar& yon) const { hither = GetHither(); yon = GetYon(); }
|
||
|
|
||
|
// Setup.
|
||
|
// First, our world to camera and back again.
|
||
|
void SetCameraTransform(const hsMatrix44& w2c, const hsMatrix44& c2w) { fWorldToCamera = w2c; fCameraToWorld = c2w; ISetFlag(kWorldToNDCSet, false); }
|
||
|
|
||
|
// Next, what kind of projection.
|
||
|
void SetOrthogonal(hsBool on) { ISetFlag(kOrthogonal, on); InvalidateTransforms(); }
|
||
|
void SetPerspective(hsBool on) { SetOrthogonal(!on); }
|
||
|
|
||
|
// Next, setting the scree/window/rendertarget size
|
||
|
void SetWidth(UInt16 w) { fWidth = w; }
|
||
|
void SetHeight(UInt16 h) { fHeight = h; }
|
||
|
void SetScreenSize(UInt16 w, UInt16 h) { SetWidth(w); SetHeight(h); }
|
||
|
|
||
|
// Next, setting the viewport. You only need to do this if you want to use the screen functions above.
|
||
|
// If you're passing in and getting out normalized device coordinates, skip this. If you don't set viewport,
|
||
|
// Defaults to 0,0,width,height (i.e. the whole screen).
|
||
|
void SetViewPort(const hsPoint2& mins, const hsPoint2& maxs, hsBool relative=true);
|
||
|
void SetViewPort(float loX, float loY, float hiX, float hiY, hsBool relative=true) { SetViewPort(hsPoint2().Set(loX, loY), hsPoint2().Set(hiX, hiY), relative); }
|
||
|
void SetViewPort(UInt16 left, UInt16 top, UInt16 right, UInt16 bottom) { SetViewPort(hsScalar(left), hsScalar(top), hsScalar(right), hsScalar(bottom), false); }
|
||
|
|
||
|
void SetMapping(const hsPoint3& mins, const hsPoint3& maxs) { SetMapMin(mins); SetMapMax(maxs); }
|
||
|
void SetMapMin(const hsPoint3& mins) { fMapMin = mins; }
|
||
|
void SetMapMax(const hsPoint3& maxs) { fMapMax = maxs; }
|
||
|
|
||
|
// Next, variants on setting up our projection matrix.
|
||
|
// Depth is pretty uniform.
|
||
|
void SetDepth(hsScalar hither, hsScalar yon) { fMin.fZ = hither; fMax.fZ = yon; InvalidateTransforms(); }
|
||
|
void SetDepth(const hsPoint2& d) { SetDepth(d.fX, d.fY); }
|
||
|
void SetHither(hsScalar hither) { fMin.fZ = hither; InvalidateTransforms(); }
|
||
|
void SetYon(hsScalar yon) { fMax.fZ = yon; InvalidateTransforms(); }
|
||
|
|
||
|
// Garden variety symmetric fov uses either of this first batch. Unless you're doing some funky projection, you don't even
|
||
|
// need to look through the rest.
|
||
|
// Degrees - all are full angles, < 180 degrees
|
||
|
void SetFovDeg(const hsPoint2& deg) { SetFovDeg(deg.fX, deg.fY); }
|
||
|
void SetFovDeg(hsScalar degX, hsScalar degY) { SetFovXDeg(degX); SetFovYDeg(degY); }
|
||
|
void SetFovXDeg(hsScalar deg) { SetFovX(hsScalarDegToRad(deg)); }
|
||
|
void SetFovYDeg(hsScalar deg) { SetFovY(hsScalarDegToRad(deg)); }
|
||
|
|
||
|
// Radians - all are full angles, < PI
|
||
|
void SetFov(const hsPoint2& rad) { SetFov(rad.fX, rad.fY); }
|
||
|
void SetFov(hsScalar radX, hsScalar radY) { SetFovX(radX); SetFovY(radY); }
|
||
|
void SetFovX(hsScalar rad) { SetHalfWidth(hsTan(rad * 0.5f)); }
|
||
|
void SetFovY(hsScalar rad) { SetHalfHeight(hsTan(rad * 0.5f)); }
|
||
|
|
||
|
// For orthogonal projection, don't call SetWidth(hsTan(fovRads)), because hsTan(f)/2 != hsTan(f/2)
|
||
|
// For non-centered, call SetWidths/Heights() directly.
|
||
|
void SetWidth(hsScalar w) { SetHalfWidth(w * 0.5f); }
|
||
|
void SetHeight(hsScalar h) { SetHalfHeight(h * 0.5f); }
|
||
|
|
||
|
// The rest do no interpretation, just stuff the values passed in.
|
||
|
void SetHalfWidth(hsScalar hw) { SetWidths(-hw, hw); }
|
||
|
void SetHalfHeight(hsScalar hh) { SetHeights(-hh, hh); }
|
||
|
void SetWidths(hsScalar minW, hsScalar maxW) { fMin.fX = minW; fMax.fX = maxW; InvalidateTransforms(); }
|
||
|
void SetHeights(hsScalar minH, hsScalar maxH) { fMin.fY = minH; fMax.fY = maxH; InvalidateTransforms(); }
|
||
|
void SetWidths(const hsPoint2& w) { SetWidths(w.fX, w.fY); }
|
||
|
void SetHeights(const hsPoint2& h) { SetHeights(h.fX, h.fY); }
|
||
|
void SetView(const hsPoint3& mins, const hsPoint3& maxs) { fMax = maxs; fMin = mins; InvalidateTransforms(); }
|
||
|
|
||
|
// Take a CAMERA SPACE bounding box and sets up the projection to just surround it.
|
||
|
// Note this doesn't swivel the camera around to see the box, it offsets the projection.
|
||
|
// Return false if there isn't a projection that will capture any of the bnd. This
|
||
|
// can be from the bnd being behind the camera.
|
||
|
hsBool SetProjection(const hsBounds3& cBnd);
|
||
|
hsBool SetProjectionWorld(const hsBounds3& wBnd);
|
||
|
|
||
|
// This lets you create insane projection matrices. Note that it won't change the answer on anything like
|
||
|
// GetFov().
|
||
|
void PreMultCameraToNDC(const hsMatrix44& m) { fCameraToNDC = m * GetCameraToNDC(); }
|
||
|
void PostMultCameraToNDC(const hsMatrix44& m) { fCameraToNDC = GetCameraToNDC() * m; }
|
||
|
void Recalc() { InvalidateTransforms(); }
|
||
|
|
||
|
// These do the obvious, constructing a single view that encompasses either the intersection or union
|
||
|
// of what the two views will see. The boolean is performed in axis aligned camera space, which lines
|
||
|
// up nicely with screen space. Note that this only makes sense for two ViewTransforms with identical
|
||
|
// CameraToWorld's (which isn't checked).
|
||
|
hsBool Intersect(const plViewTransform& view);
|
||
|
hsBool Union(const plViewTransform& view);
|
||
|
|
||
|
// Convenience to move from one space to another.
|
||
|
// Screen means pixels - Default is mapping NDC -> [0..1]. Z value of pixel is NDC Z.
|
||
|
// NDC is the ([-1..1],[-1..1],[0..1]) Normalized device coordinates.
|
||
|
// Camera is camera space.
|
||
|
// World is world space.
|
||
|
// Past that, you're on your own.
|
||
|
hsScalarTriple ScreenToNDC(const hsScalarTriple& scrP) const;
|
||
|
hsScalarTriple ScreenToCamera(const hsScalarTriple& scrP) const { return NDCToCamera(ScreenToNDC(scrP)); }
|
||
|
|
||
|
hsPoint3 ScreenToNDC(const hsPoint3& scrP) const { return hsPoint3(ScreenToNDC(hsScalarTriple(scrP))); }
|
||
|
hsPoint3 ScreenToCamera(const hsPoint3& scrP) const { return hsPoint3(ScreenToCamera(hsScalarTriple(scrP))); }
|
||
|
hsPoint3 ScreenToWorld(const hsPoint3& scrP) const { return CameraToWorld(ScreenToCamera(scrP)); }
|
||
|
|
||
|
hsVector3 ScreenToNDC(const hsVector3& scrV) const { return hsVector3(ScreenToNDC(hsScalarTriple(scrV))); }
|
||
|
hsVector3 ScreenToCamera(const hsVector3& scrV) const { return hsVector3(ScreenToCamera(hsScalarTriple(scrV))); }
|
||
|
hsVector3 ScreenToWorld(const hsVector3& scrV) const { return CameraToWorld(ScreenToCamera(scrV)); }
|
||
|
|
||
|
hsScalarTriple NDCToScreen(const hsScalarTriple& ndc) const;
|
||
|
hsScalarTriple NDCToCamera(const hsScalarTriple& ndc) const;
|
||
|
|
||
|
hsPoint3 NDCToScreen(const hsPoint3& ndc) const { return hsPoint3(NDCToScreen(hsScalarTriple(ndc))); }
|
||
|
hsPoint3 NDCToCamera(const hsPoint3& ndc) const { return hsPoint3(NDCToCamera(hsScalarTriple(ndc))); }
|
||
|
hsPoint3 NDCToWorld(const hsPoint3& ndc) const { return CameraToWorld(NDCToCamera(ndc)); }
|
||
|
|
||
|
hsVector3 NDCToScreen(const hsVector3& ndc) const { return hsVector3(NDCToScreen(hsScalarTriple(ndc))); }
|
||
|
hsVector3 NDCToCamera(const hsVector3& ndc) const { return hsVector3(NDCToCamera(hsScalarTriple(ndc))); }
|
||
|
hsVector3 NDCToWorld(const hsVector3& ndc) const { return CameraToWorld(NDCToCamera(ndc)); }
|
||
|
|
||
|
hsScalarTriple CameraToScreen(const hsScalarTriple& camP) const { return NDCToScreen(CameraToNDC(camP)); }
|
||
|
hsScalarTriple CameraToNDC(const hsScalarTriple& camP) const;
|
||
|
|
||
|
hsPoint3 CameraToScreen(const hsPoint3& camP) const { return hsPoint3(CameraToScreen(hsScalarTriple(camP))); }
|
||
|
hsPoint3 CameraToNDC(const hsPoint3& camP) const { return hsPoint3(CameraToNDC(hsScalarTriple(camP))); }
|
||
|
hsPoint3 CameraToWorld(const hsPoint3& camP) const { return GetCameraToWorld() * camP; }
|
||
|
|
||
|
hsVector3 CameraToScreen(const hsVector3& camP) const { return hsVector3(CameraToScreen(hsScalarTriple(camP))); }
|
||
|
hsVector3 CameraToNDC(const hsVector3& camP) const { return hsVector3(CameraToNDC(hsScalarTriple(camP))); }
|
||
|
hsVector3 CameraToWorld(const hsVector3& camV) const { return GetCameraToWorld() * camV; }
|
||
|
|
||
|
hsPoint3 WorldToScreen(const hsPoint3& worldP) const { return (hsPoint3)CameraToScreen(WorldToCamera(worldP)); }
|
||
|
hsPoint3 WorldToNDC(const hsPoint3& worldP) const { return CameraToNDC(WorldToCamera(worldP)); }
|
||
|
hsPoint3 WorldToCamera(const hsPoint3& worldP) const { return GetWorldToCamera() * worldP; }
|
||
|
|
||
|
hsVector3 WorldToScreen(const hsVector3& worldV) const { return (hsVector3)CameraToScreen(WorldToCamera(worldV)); }
|
||
|
hsVector3 WorldToNDC(const hsVector3& worldP) const { return CameraToNDC(WorldToCamera(worldP)); }
|
||
|
hsVector3 WorldToCamera(const hsVector3& worldV) const { return GetWorldToCamera() * worldV; }
|
||
|
|
||
|
hsScalarTriple NDCToMap(const hsScalarTriple& ndcP) const;
|
||
|
hsScalarTriple CameraToMap(const hsScalarTriple& camP) const { return NDCToMap(CameraToNDC(camP)); }
|
||
|
|
||
|
hsPoint3 NDCToMap(const hsPoint3& ndcP) const { return hsPoint3(NDCToMap(hsScalarTriple(ndcP))); }
|
||
|
hsPoint3 CameraToMap(const hsPoint3& camP) const { return hsPoint3(CameraToMap(hsScalarTriple(camP))); }
|
||
|
hsPoint3 WorldToMap(const hsPoint3& worldP) const { return CameraToMap(WorldToCamera(worldP)); }
|
||
|
|
||
|
hsVector3 NDCToMap(const hsVector3& ndcP) const { return hsVector3(NDCToMap(hsScalarTriple(ndcP))); }
|
||
|
hsVector3 CameraToMap(const hsVector3& camP) const { return hsVector3(CameraToMap(hsScalarTriple(camP))); }
|
||
|
hsVector3 WorldToMap(const hsVector3& worldP) const { return CameraToMap(WorldToCamera(worldP)); }
|
||
|
|
||
|
void Read(hsStream* s);
|
||
|
void Write(hsStream* s);
|
||
|
|
||
|
protected:
|
||
|
enum
|
||
|
{
|
||
|
kNone = 0x0,
|
||
|
kOrthogonal = 0x1,
|
||
|
kSymmetric = 0x2,
|
||
|
kCameraToNDCSet = 0x4,
|
||
|
kWorldToNDCSet = 0x8,
|
||
|
kSetMask = kCameraToNDCSet | kWorldToNDCSet,
|
||
|
kViewPortRelative = 0x10
|
||
|
};
|
||
|
|
||
|
mutable UInt32 fFlags;
|
||
|
|
||
|
hsMatrix44 fCameraToWorld;
|
||
|
hsMatrix44 fWorldToCamera;
|
||
|
|
||
|
hsPoint3 fMin; // minTanX/X, minTanY/Y, hither
|
||
|
hsPoint3 fMax; // maxTanX/X, maxTanY/Y, yon
|
||
|
|
||
|
// Screen (or rendertarget) dimensions in pixels.
|
||
|
UInt16 fWidth;
|
||
|
UInt16 fHeight;
|
||
|
|
||
|
// Viewport can be stored as fraction of screen size, so the view transform's viewport
|
||
|
// can be set up independent of the size of the window it's applied to.
|
||
|
hsPoint3 fViewPortX; // min, max, 1 / (max-min)
|
||
|
hsPoint3 fViewPortY; // min, max, 1 / (max-min)
|
||
|
|
||
|
// For arbitrary mapping (unconfined to pixel coords or NDC), just set what you want
|
||
|
// to map to.
|
||
|
hsPoint3 fMapMin;
|
||
|
hsPoint3 fMapMax;
|
||
|
|
||
|
// Some mutables. These are just the calculated from the above (e.g. fov, depth, perspective, etc).
|
||
|
mutable hsMatrix44 fCameraToNDC;
|
||
|
mutable hsMatrix44 fWorldToNDC;
|
||
|
|
||
|
// Have to set a limit here on the smallest the hither plane can be.
|
||
|
static const hsScalar kMinHither;
|
||
|
|
||
|
void ISetCameraToNDC() const;
|
||
|
hsBool ICameraToNDCSet() const { return IHasFlag(kCameraToNDCSet); }
|
||
|
const hsMatrix44& ICheckCameraToNDC() const { if( !ICameraToNDCSet() ) ISetCameraToNDC(); return fCameraToNDC; }
|
||
|
|
||
|
void ISetWorldToNDC() const { fWorldToNDC = GetCameraToNDC() * fWorldToCamera; ISetFlag(kWorldToNDCSet); }
|
||
|
hsBool IWorldToNDCSet() const { return IHasFlag(kWorldToNDCSet); }
|
||
|
const hsMatrix44& ICheckWorldToNDC() const { if( !IWorldToNDCSet() ) ISetWorldToNDC(); return fWorldToNDC; }
|
||
|
|
||
|
hsBool IGetMaxMinsFromBnd(const hsBounds3& bnd, hsPoint3& mins, hsPoint3& maxs) const;
|
||
|
|
||
|
void InvalidateTransforms() { ISetFlag(kCameraToNDCSet|kWorldToNDCSet, false); }
|
||
|
|
||
|
// Flags - generic
|
||
|
hsBool IHasFlag(UInt32 f) const { return 0 != (fFlags & f); }
|
||
|
void ISetFlag(UInt32 f, hsBool on=true) const { if(on) fFlags |= f; else fFlags &= ~f; }
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif // plViewTransform_inc
|