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.
326 lines
18 KiB
326 lines
18 KiB
/*==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
|
|
|