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.

343 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/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
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