Planes in 3D Graphics | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||||||||||||
Download: |
|
|||||||||||||||
Planes in 3D Graphics |
gPlane is the first step to posing uniformly scaled 3D Objects, or placing things in 3D space.
The key thing with the journey to making a 3D world is deciding which conventions to follow when there are several ways of doing things that appear similar. The set of conventions used here result in great optimisations later on in the system.
A Right-Handed World
Put a piece of paper on a table and think of its bottom left corner as the Global Origin - the point (0,0,0). The bottom edge is the X-axis pointing to the right, and the Left edge is the Y-Axis pointing forwards just like a map; the Z-Axis is 'up'.
The paper is occupying the global XY plane: the place where z is always zero and only X and Y have non-zero values. A pen resting on the paper would exist in the space where X and Y and Z are all positive numbers. A pen to the left of the page would have a negative X value. This global coordinate system is using the 'Right-Hand Rule' (thumb is x, fore-finger is y, middle-finger is z), which is useful to remember when playing with 'Vector Cross Products'. Put the back of your hand on the bottom left corner of your paper, with your thumb pointing along the bottom edge and your fore-finger pointing along the left edge: they are orthogonal (at 90 degrees to each other). Now point your middle finger 'up' and all three fingers are each orthogonal to each other. If you did the same with your left hand, your middle finger would end up pointing down (so choosing which hand to use is choosing how the third dimension is going to relate to the first two). Back to your Right Hand: without moving your (now orthogonal) fingers, move your hand so your thumb touches your nose, and your hands new position describes some random new plane relative to the global plane (the paper). Your new plane has a new origin and probably new axes that no longer line up with the paper. All planes in the world must have x,y and z axes in the same direction relative to each other (your fingers are not allowed to move although you can move your hand anywhere).
When given a choice, opt for the most natural option: the Right Hand Rule is used because everyone (especially left-handed people) agrees that we live in a right-handed person's world, so it is easy to remember "It's a Right-Handed World". OpenGL uses a Right Hand world with the Y-Axis being up (it's thinking of the world through a vertical screen, not as a map on a desk). DirectX uses a Left Hand world with the Y-Axis being up. Feeding your worlds global coordinate system to another system is not difficult, so (as no standard exists) use the most natural: the right hand and a map (or XY graph that is taught at school). Another way to see the world as a coordinate system is to think of a tiled floor: each tile is a coordinate in the XY plane. Pick an origin and define the location of your chair relative to the origin.
Planes are for drawing on
To fully imagine a new plane that is more useful, a plane that we can draw on is needed, but rather than hold up a sheet of paper for ages, look for a rectangular flat window with a spot of dirt on. The bottom left-hand corner is the window's origin, its X-Axis is along its bottom, its Y-Axis is up its left-hand edge and its Z-Axis will be towards you. The spot of dirt on the window is at a particular point on the window's plane which you can measure in two dimensions (X,Y) where X is how far from the left edge the spot is (distance from its origin in the X direction), and Y is how far from the bottom edge the spot is (distance from its origin in the Y direction).
From the Plane's drawing to the World and back
gPlane holds the window's details and allows you to do things like convert the spot's coordinates from being relative to the Window Plane, to relative to the piece of paper on your desk. Transforming coordinates from Local to Global and back allows us to draw on a two dimensional plane and place the two dimensional objects in the three dimensional world. It also allows us to render the three dimensional world onto a two dimensional screen. If the spot of dirt on the window lines up with something outside (assuming you're currently inside), you can imagine that the thing outside has global coordinates that can be mapped onto the window's two dimensional coordinate system. gPlane can do that for you very quickly and efficiently.
To know everything about the window, gPlane needs to hold a the Window's Origin as a point relative to the Global Origin (the corner of your piece of paper). gPlane also needs to store the coordinate system of the window as three orthogonal unit vectors relative to the Global unit vectors (the edges of your piece of paper). It it not enough to just store the Plane's Origin and Normal (the Z-Axis): that doesn't tell you which round the plane is (the X-Axis could be any direction away from the origin on the plane): the spot of dirt can not be located without specifying an axis on the plane.
Advanced:
There are conventions for creating the other axes from a Normal axis that are used in Computer Aided Machining: a system may specify that the X or Y axis should be horizontal. That leaves two potential directions for the third axis to lie. That decision is made using another boolean: 'Clockwise Twist' which means: looking down the horizontal axis to the origin, the third axis is clockwise from the normal (or not). Those principles are handled within g3Vector::GetAxes which uses HorizontalAxisType to create new axes from a normal vector.So it is necessary to store at least one other axis as well as the Normal...
The Magic Begins...
If all three axes are stored, they can be held in such a way that they form a transformation matrix which can directly transform points! This speeds up many processes including 'camera placement' as used by gPose which is derived from gPlane. To make this work, the axes need to be stored as 'unit vectors' which means that the vector length is exactly 1. g3Vector::Normalise() will set the length to 1.
If 'Origin' is the Local Origin (in World Coordinates) and xAxis, yAxis and zAxis are the normalised local axes, they can be stored as a transformation matrix as follows:
M[12]={xAxis.x, yAxis.x, zAxis.x, Origin.x, xAxis.y, yAxis.y, zAxis.y, Origin.y, xAxis.z, yAxis.z, zAxis.z, Origin.z};Since gPlane owns this transformation matrix, it is natural to think of that matrix as the plane's way to convert from Local to Global coordinates.
The method: g3Point TransformToPlane(const g3Point& p) const; simply multiplies the point, p by the above matrix and the result is the point in global coordinates.To translate from global to local coordinates, the global point must be multiplied by the inverse of the matrix. Calculating the inverse of a matrix is verbose but, fortunately, our matrix is a little special in that the 3x3 part holding the normalised vectors is entirely orthogonal and the inverse of an orthogonal matrix is its transpose, so all that is needed for that part is to mirror the positions of the numbers in the matrix around the leading diagonal! The inverse of the point section is a projection that reduces to a dot product leaving:
I[12]={xAxis.x, xAxis.y, xAxis.z, -xAxis.Dot(Origin), yAxis.x, yAxis.y, yAxis.z, -yAxis.Dot(Origin), zAxis.x, zAxis.y, zAxis.z, -zAxis.Dot(Origin)};It may be useful to know that the z projection I[11] is the distance from the global origin to the closest part of the plane...Rather than re-calculate these values, gPlane stores both the matrix and its inverse for fast transformations. When you come to serialize a gPlane (save and load) you only need to save the xAxis,zAxis and Origin. zAxis is generally the most important one to be accurate, so store that and when you load the gPlane, yAxis=zAxis.Cross(xAxis);
Graphics libraries like OpenGL work using 4x4 matrices but that holds no advantages here and makes finding the inverse far more time consuming and verbose. gglCamera.h shows how to implement an OpenGL interface from a gPlane.
Flat Land
Imagine a tree with rectangular leaves. Most of the leaves will need their own plane onto which a rectangle is drawn. An optimisation is to notice that all horizontal leaves could use the global XY plane as a drawing surface and the z coordinate says how high off the floor that leaf is. gPlane can recognise that you asked for a horizontal plane and simply use Flat-land (no transormation required). Unless you call NoFlatLand(), optimisations are used if the plane is horizontal and its X-Axis is in the same direction as the global X-Axis (FlatLand==true) and Local coordinates are the same as Global coordinates.
Advanced:
If you are creating many planes (as you would for a tree with planar leaves), it is valuable to minimise the number of planes you are creating: the ideal is to have no parallel planes, so all the parallel leaves share one plane, but have a height above the plane. Create a g3PV with NormalisePlaneOrigin=true in the constructor and it will take the origin and normal you provide and create a plane with a new origin (on the same plane) which is the closest point on that plane to the Global Origin (from I[11] mentioned earlier). When you create the next plane, compare the origins and normals to see if the objects could use the same plane but be on different z-levels. To use this, when you ask your application for a new plane to be created, check if that plane already exists and return a pointer to the existing one if it does.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.