A Simple Geometry library for OpenGL |
by Steve Baker |
SG is now a part of PLIB.
Since SG is designed to work with OpenGL, it uses similar naming conventions - all SG functions and other tokens that work with 'float' precision math begin with 'sg' or 'SG'. There are a complete duplicate set that work with double-precision math that begin with 'sgd' or 'SGD'.
Most SG functions are implemented as 'inline' C++ functions - for speed. The underlying data types are mostly just arrays of 'float' - which is generally the most efficient type for OpenGL programs.
SGD functions are absolutely identical to their SG eqivelents - except for working at double precision. This document doesn't describe the SGD functions, types and constants. That would just be a waste of space - they are just identical to the SG versions.
SGfloat -- A floating point number. sgVec2 -- A two element array of SGfloat sgVec3 -- A three element array of SGfloat sgVec4 -- A four element array of SGfloat sgMat4 -- A 4x4 element array of SGfloat sgCoord -- A simple structure used to hold euler rotation angles and a 3D translation: struct sgCoord { sgVec3 xyz ; sgVec3 hpr ; } ; sgLine3 -- An infinite line defined by a point somewhere on the line and a normalized direction vector. struct sgLine3 { sgVec3 point_on_line ; sgVec3 direction_vector ; /* Should be a unit vector */ } ; sgLineSegment3 -- A finite line segment defined by it's end-points. struct sgLineSegment3 { sgVec3 a ; sgVec3 b ; } ; sgQuat -- An sgVec4 holding the values (x, y, z, w), representing the quaternion w + xi + yj + zk. class sgSphere -- A sphere class sgBox -- An axially aligned box. class sgFrustum -- An OpenGL-style view frustum ...and the same again but in 'double' precision: SGDfloat, sgdVec2, sgdVec3, sgdVec4, sgdMat4, sgdCoord, sgdQuat, sgdSphere, sgdBox, sgdFrustumNote 1: All angles used by or produced by SG are in degrees.
Note 2: Some functions refer to 'angle+axis' notation for rotations, this is the same format that glRotate uses.
SG calls the first form a "line segment" (sgLineSegment3) and the second form an "infinite line" (sgLine3).
You can find the infinite line that is the extension of a line segment using:
void sgLineSegment3ToLine3 ( sgLine3 *line, const sgLineSegment3 lineseg ) ;You can also find the distance from a point to either a line or a line segment using:
SGfloat sgDistToLineVec3 ( const sgLine3 line, const sgVec3 pnt ) SGfloat sgDistToLineSegmentVec3 ( const sgLineSegment3 line, const sgVec3 pnt ) SGfloat sgDistSquaredToLineVec3 ( const sgLine3 line, const sgVec3 pnt ) ; SGfloat sgDistSquaredToLineSegmentVec3 ( const sgLineSegment3 line, const sgVec3 pnt ) ;Try to use the 'Squared' versions if you can because you save a costly sqrt() function.
sgMat4 m ; sgMakeIdentMat4 ( m ) ; glLoadMatrixf ( (GMfloat *) m ) ; ...or... sgdMat4 m ; sgdMakeIdentMat4 ( m ) ; glLoadMatrixd ( (GMdouble *) m ) ;Another way to look at this is to treat an sgMat4 as an array of four sgVec4's. This is convenient for quite a few reasons. For example, you can extract the sgVec3 that represents the translation part of an sgMat4 by just using mat[3] anywhere where an sgMat3 would be appropriate.
There is a rich set of functions to interconvert sgCoord's and sgMat4's.
It can be used to encode rotations. The quaternion (sin(a) * v, cos(a)) rotates 2*a about the vector v.
There is a rich set of functions to interconvert sgQuat's and other rotation structures.
class sgSphere { SGfloat *getCenter (void) ; SGfloat getRadius (void) ; void setCenter ( SGfloat x, SGfloat y, SGfloat z ) ; void setRadius ( SGfloat r ) ; int isEmpty (void) ; void empty (void) ; void orthoXform ( sgMat4 m ) ; void extend ( sgSphere *s ) ; void extend ( sgBox *b ) ; void extend ( sgVec3 v ) ; int intersects ( sgSphere *s ) ; int intersects ( sgVec4 plane ) ; int intersects ( sgBox *b ) ; } ;Since sgSphere's are frequently used as simple bounding shapes for field-of-view culling, it's quite useful to be able to represent an 'empty' sphere. This is represented using a sphere of Negative radius.
The 'isEmpty()' function tests to see if a sphere is empty and the 'empty()' function forces the sphere to be empty.
The 'orthoXform(m)' function transforms the sphere using the matrix 'm' - which must be orthographic (since transforming a sphere by a non-ortho matrix yields something that's no longer spherical).
Three 'extend' functions allow you to expand the sphere to encompass other shapes. These functions attempt to minimise the radius of the resulting sphere:
void extend ( sgSphere *s ) ; void extend ( sgBox *b ) ; void extend ( sgVec3 v ) ;There are also three intersection tests that return TRUE if the volume contained by the sphere intersects the volume contained by the other object:
int intersects ( sgSphere *s ) ; int intersects ( sgBox *b ) ; int intersects ( sgVec4 plane ) ;('plane' in this context means the semi-infinite volume bounded by that plane).
class sgBox { public: SGfloat *getMin (void) ; SGfloat *getMax (void) ; void setMin ( SGfloat x, SGfloat y, SGfloat z ) ; void setMax ( SGfloat x, SGfloat y, SGfloat z ) ; int isEmpty(void) ; void empty (void) ; void extend ( sgSphere *s ) ; void extend ( sgBox *b ) ; void extend ( sgVec3 v ) ; int intersects ( sgSphere *s ) ; int intersects ( sgBox *b ) ; int intersects ( sgVec4 plane ) ; } ;Since sgBox'es are frequently used as simple bounding shapes for field-of-view culling, it's quite useful to be able to represent an 'empty' box. This is represented using a box whose minumum vertex has larger X, Y or Z than its maximum vertex.
The 'isEmpty()' function tests to see if a box is empty and the 'empty()' function forces the box to be empty.
Three 'extend' functions allow you to expand the box to encompass other shapes. These functions attempt to minimise the size of the resulting box:
void extend ( sgSphere *s ) ; void extend ( sgBox *b ) ; void extend ( sgVec3 v ) ;There are also three intersection tests that return TRUE if the volume contained by the box intersects the volume contained by the other object:
int intersects ( sgSphere *s ) ; int intersects ( sgBox *b ) ; int intersects ( sgVec4 plane ) ;('plane' in this context means the semi-infinite volume bounded by that plane).
class sgFrustum { public: sgFrustum (void) ; void setFrustum ( SGfloat left , SGfloat right, SGfloat bottom, SGfloat top, SGfloat near , SGfloat far ) ; void setFOV ( SGfloat h, SGfloat v ) ; void setNearFar ( SGfloat n, SGfloat f ) ; SGfloat getHFOV (void) ; SGfloat getVFOV (void) ; SGfloat getNear (void) ; SGfloat getFar (void) ; SGfloat getLeft (void) ; SGfloat getRight(void) ; SGfloat getTop (void) ; SGfloat getBot (void) ; void getFOV ( SGfloat *h, SGfloat *v ) ; void getNearFar ( SGfloat *n, SGfloat *f ) ; int contains ( sgVec3 p ) ; int contains ( sgSphere *s ) ; } ;There are two ways to use an sgFrustum:
int contains ( sgVec3 p ) ; int contains ( sgSphere *s ) ;...returns SG_OUTSIDE if the point or sphere lies entirely outside the volume contained by the frustum, SG_INSIDE if it lies entirely inside the frustum - or SG_STRADDLE if they only partially overlap.
#define SG_PI #define SG_DEGREES_TO_RADIANS #define SG_RADIANS_TO_DEGREES
sgAddVec3 ( dst, a, b ) ; /* dst = a + b ; */ sgAddVec3 ( dst, a ) ; /* dst += a ; */The following functions exist:
void sgZeroVec2 ( sgVec2 dst ) ; void sgZeroVec3 ( sgVec3 dst ) ; void sgZeroVec4 ( sgVec4 dst ) ; void sgSetVec2 ( sgVec2 dst, SGfloat x, SGfloat y ) ; void sgSetVec3 ( sgVec3 dst, SGfloat x, SGfloat y, SGfloat z ) ; void sgSetVec4 ( sgVec4 dst, SGfloat x, SGfloat y, SGfloat z, SGfloat w ) ; void sgCopyVec2 ( sgVec2 dst, sgVec2 src ) void sgCopyVec3 ( sgVec3 dst, sgVec3 src ) void sgCopyVec4 ( sgVec4 dst, sgVec4 src ) void sgAddVec2 ( sgVec2 dst, sgVec2 src ) void sgAddVec3 ( sgVec3 dst, sgVec3 src ) void sgAddVec4 ( sgVec4 dst, sgVec4 src ) void sgAddVec2 ( sgVec2 dst, sgVec2 src1, sgVec2 src2 ) void sgAddVec3 ( sgVec3 dst, sgVec3 src1, sgVec3 src2 ) void sgAddVec4 ( sgVec4 dst, sgVec4 src1, sgVec4 src2 ) void sgSubVec2 ( sgVec2 dst, sgVec2 src ) void sgSubVec3 ( sgVec3 dst, sgVec3 src ) void sgSubVec4 ( sgVec4 dst, sgVec4 src ) void sgSubVec2 ( sgVec2 dst, sgVec2 src1, sgVec2 src2 ) void sgSubVec3 ( sgVec3 dst, sgVec3 src1, sgVec3 src2 ) void sgSubVec4 ( sgVec4 dst, sgVec4 src1, sgVec4 src2 ) void sgNegateVec2 ( sgVec2 dst ) void sgNegateVec3 ( sgVec3 dst ) void sgNegateVec4 ( sgVec4 dst ) void sgNegateVec2 ( sgVec2 dst, sgVec2 src ) void sgNegateVec3 ( sgVec3 dst, sgVec3 src ) void sgNegateVec4 ( sgVec4 dst, sgVec4 src ) void sgScaleVec2 ( sgVec2 dst, SGfloat s ) void sgScaleVec3 ( sgVec3 dst, SGfloat s ) void sgScaleVec4 ( sgVec4 dst, SGfloat s ) void sgScaleVec2 ( sgVec2 dst, sgVec2 src, SGfloat s ) void sgScaleVec3 ( sgVec3 dst, sgVec3 src, SGfloat s ) void sgScaleVec4 ( sgVec4 dst, sgVec4 src, SGfloat s ) void sgAddScaledVec2 ( sgVec2 dst, svVec2 src, SGfloat s ) void sgAddScaledVec3 ( sgVec3 dst, svVec3 src, SGfloat s ) void sgAddScaledVec4 ( sgVec4 dst, svVec4 src, SGfloat s ) void sgAddScaledVec2 ( sgVec2 dst, sgVec2 a, svVec2 b, SGfloat s ) void sgAddScaledVec3 ( sgVec3 dst, sgVec3 a, svVec3 b, SGfloat s ) void sgAddScaledVec4 ( sgVec4 dst, sgVec4 a, svVec4 b, SGfloat s ) int sgCompareVec2 ( sgVec2 a, sgVec2 b, SGfloat tol ) int sgCompareVec3 ( sgVec3 a, sgVec3 b, SGfloat tol ) int sgCompareVec4 ( sgVec4 a, sgVec4 b, SGfloat tol ) int sgEqualVec2 ( sgVec2 a, sgVec2 b ) int sgEqualVec3 ( sgVec3 a, sgVec3 b ) int sgEqualVec4 ( sgVec4 a, sgVec4 b ) SGfloat sgScalarProductVec2 ( sgVec2 a, sgVec2 b ) SGfloat sgScalarProductVec3 ( sgVec3 a, sgVec3 b ) SGfloat sgScalarProductVec4 ( sgVec4 a, sgVec4 b ) void sgVectorProductVec3 ( sgVec3 dst, sgVec3 a, sgVec3 b ) void sgLerp2 ( sgVec2 dst, sgVec2 a, sgVec2 b, SGfloat f ) void sgLerp3 ( sgVec3 dst, sgVec3 a, sgVec3 b, SGfloat f ) void sgLerp4 ( sgVec4 dst, sgVec4 a, sgVec4 b, SGfloat f ) SGfloat sgDistanceSquaredVec2 ( sgVec2 a, sgVec2 b ) SGfloat sgDistanceSquaredVec3 ( sgVec3 a, sgVec3 b ) SGfloat sgDistanceSquaredVec4 ( sgVec4 a, sgVec4 b ) SGfloat sgDistanceVec2 ( sgVec2 a, sgVec2 b ) SGfloat sgDistanceVec3 ( sgVec3 a, sgVec3 b ) SGfloat sgDistanceVec4 ( sgVec4 a, sgVec4 b ) SGfloat sgLengthVec2 ( sgVec2 src ) SGfloat sgLengthVec3 ( sgVec3 src ) SGfloat sgLengthVec4 ( sgVec4 src ) /* Anglo-US spelling issues.*/ #define sgNormalizeVec2 sgNormaliseVec2 #define sgNormalizeVec3 sgNormaliseVec3 #define sgNormalizeVec4 sgNormaliseVec4 void sgNormaliseVec2 ( sgVec2 dst ) void sgNormaliseVec3 ( sgVec3 dst ) void sgNormaliseVec4 ( sgVec4 dst ) void sgNormaliseVec2 ( sgVec2 dst, sgVec2 src ) void sgNormaliseVec3 ( sgVec3 dst, sgVec3 src ) void sgNormaliseVec4 ( sgVec4 dst, sgVec4 src )
void sgCopyMat4 ( sgMat4 dst, sgMat4 src ) ; void sgMakeIdentMat4 ( sgMat4 dst ) ; void sgMakeCoordMat4 ( sgMat4 dst, SGfloat x, SGfloat y, SGfloat z, SGfloat h, SGfloat p, SGfloat r ) ; void sgMakeCoordMat4 ( sgMat4 dst, sgVec3 xyz, sgVec3 hpr ) void sgMakeCoordMat4 ( sgMat4 dst, sgCoord *src ) void sgMakeRotMat4 ( sgMat4 dst, sgVec3 hpr ) void sgMakeRotMat4 ( sgMat4 dst, SGfloat h, SGfloat p, SGfloat r ) void sgMakeRotMat4 ( sgMat4 dst, SGfloat angle, sgVec3 axis ) ; void sgMakeTransMat4 ( sgMat4 dst, sgVec3 xyz ) ; void sgMakeTransMat4 ( sgMat4 dst, SGfloat x, SGfloat y, SGfloat z ) ; void sgQuatToMatrix ( sgMat4 dst, sgQuat quat ) ;You can multiply matrices:
void sgMultMat4 ( sgMat4 dst, sgMat4 a, sgMat4 b ) ; void sgPostMultMat4 ( sgMat4 dst, sgMat4 a ) ; void sgPreMultMat4 ( sgMat4 dst, sgMat4 a ) ;You can do a cheap matrix inversion (for simple rotate/translate matrices):
void sgTransposeNegateMat4 ( sgMat4 dst ) ; void sgTransposeNegateMat4 ( sgMat4 dst, sgMat4 src ) ;...or a (more costly) full matrix inversion (for more complex matrices):
void sgInvertMat4 ( sgMat4 dst ) ; void sgInvertMat4 ( sgMat4 dst, sgMat4 src ) ;You can transform vertices and points using a matrix. The 'Vec*' forms assume that the vector merely need to be rotated (like you would want for a surface normal for example) - whilst the 'Pnt*' forms perform a rotate and translate. If your matrix is more complex and contains scaling, shearing, perspective and such like, then use the 'FullXform' versions - which are quite a bit slower than the other forms:
void sgXformVec3 ( sgVec3 dst, sgMat4 mat ) ; void sgXformVec3 ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ; void sgXformPnt3 ( sgVec3 dst, sgMat4 mat ) ; void sgXformPnt3 ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ; void sgFullXformPnt3 ( sgVec3 dst, sgMat4 mat ) ; void sgFullXformPnt3 ( sgVec3 dst, sgVec3 src, sgMat4 mat ) ; void sgXformVec4 ( sgVec4 dst, sgMat4 mat ) ; void sgXformVec4 ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ; void sgXformPnt4 ( sgVec4 dst, sgMat4 mat ) ; void sgXformPnt4 ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ; void sgFullXformPnt4 ( sgVec4 dst, sgMat4 mat ) ; void sgFullXformPnt4 ( sgVec4 dst, sgVec4 src, sgMat4 mat ) ;
void sgZeroCoord ( sgCoord *dst ) ; void sgSetCoord ( sgCoord *dst, SGfloat x, SGfloat y, SGfloat z, SGfloat h, SGfloat p, SGfloat r ) ; void sgSetCoord ( sgCoord *dst, sgVec3 xyz, sgVec3 hpr ) ; void sgSetCoord ( sgCoord *coord, sgMat4 src ) ; void sgCopyCoord ( sgCoord *dst, sgCoord *src ) ;The varient of sgSetCoord that takes a matrix is somewhat tricky since there is no way to convert matrices that are not simple rotate/translates into sgCoord's - and even when the matrix is a simple rotate/translate, there are a number of possible sets of Euler rotations that could be appropriate to reproduce the behavior of the input matrix. sgSetCoord picks a valid rotation - but just beware of this behaviour.
void sgMakeIdentQuat ( sgQuat dst ) ;Creates the identity quaternion. (0,0,0,1)
void sgSetQuat ( sgQuat dst, SGfloat w, SGfloat x, SGfloat y, SGfloat z ) ;Sets the four components of the quaternion.
void sgCopyQuat ( sgQuat dst, sgQuat src ) ;Copies one quaternion into another.
void sgNormalizeQuat ( sgQuat dst ) ; void sgNormalizeQuat ( sgQuat dst, sgQuat src ) ;For most operations, quaternions need to be normalized, these functions ensure that they are.
void sgInvertQuat ( sgQuat dst ) ; void sgInvertQuat ( sgQuat dst, sgQuat src ) ;Computes the inverse of a quaternion.
void sgQuatToAngleAxis ( SGfloat *angle, sgVec3 axis, const sgQuat src ) ; void sgQuatToAngleAxis ( SGfloat *angle, SGfloat *x, SGfloat *y, SGfloat *z, const sgQuat src ) ;Converts a quaternion into an angle+axis format - just the thing to pass to glRotate for example.
void sgAngleAxisToQuat ( sgQuat dst, SGfloat angle, const sgVec3 axis ) ; void sgAngleAxisToQuat ( sgQuat dst, SGfloat angle, SGfloat x, SGfloat y, SGfloat z ) ;Converts an angle+axis rotation into a quaternion.
void sgMultQuat ( sgQuat dst, sgQuat a, sgQuat b ) ; void sgPostMultQuat ( sgQuat dst, sgQuat q ) ; void sgPreMultQuat ( sgQuat dst, sgQuat q ) ;Concatenates quaternion rotations.
void sgQuatToMatrix ( sgMat4 mat, sgQuat quat ) ; void sgMatrixToQuat ( sgQuat quat, sgMat4 mat ) ;Converts between quaternions and rotation matrices.
void sgPostRotQuat ( sgQuat dst, SGfloat angle, sgVec3 axis ) void sgPostRotQuat ( sgQuat dst, SGfloat angle, SGfloat x, SGfloat y, SGfloat z ) void sgPreRotQuat ( sgQuat dst, SGfloat angle, SGfloat x, SGfloat y, SGfloat z )Rotates a quaternion by an angle/axis rotation.
extern void sgSlerpQuat ( sgQuat dst, const sgQuat from, const sgQuat to, const SGfloat t ) ;Interpolates between two quaternions.
void sgHPRfromVec3 ( sgVec3 hpr, sgVec3 src ) ;This function takes a vector representing a direction and computes a suitable heading and pitch angle to look along that vector. Since the roll angle is indeterminate, it is set to zero.
void sgMakeNormal ( sgVec3 dst, sgVec3 a, sgVec3 b, sgVec3 c ) ;This finds the surface normal of a triangle given three vertices.
SGfloat sgDistToPlaneVec3 ( sgVec4 plane, sgVec3 pnt ) ;This returns the distance from the point to the plane.
SGfloat sgHeightAbovePlaneVec3 ( sgVec4 plane, sgVec3 pnt ) ;This returns the vertical (Z) distance from the point to the plane (my applications tend to use Z-is-up conventions).
void sgMakePlane ( sgVec4 dst, sgVec3 a, sgVec3 b, sgVec3 c ) ;This finds the plane equation of a triangle given three vertices.
void sgMakePlane ( sgVec4 dst, sgVec3 normal, sgVec3 pnt ) ;This finds the plane equation of a plane given its normal and a point on the plane.
int sgIsectPlanePlane ( sgVec3 point, sgVec3 dir, sgVec4 plane1, sgVec4 plane2 ) ;Two planes intersect along an infinite line. Given two planes, this routine routines a point and direction for the intersection line. It returns FALSE if presented with two parallel planes, TRUE otherwise.
int sgIsectInfLinePlane ( sgVec3 dst, sgVec3 l_org, sgVec3 l_vec, sgVec4 plane ) ;Given a plane, and an infinite line, this routine routines the point at which they intersect. It returns FALSE if presented with a line which is parallel to the plane, TRUE otherwise.
int sgIsectInfLineInfLine ( sgVec3 dst, sgVec3 l1_org, sgVec3 l1_vec, sgVec3 l2_org, sgVec3 l2_vec ) ;Two infinite lines in 3D are unlikely to intersect - even if you actually expect them to (because of roundoff error). Hence, this routine sets dst to the closest point on the second line which is closest to the first line. Return FALSE if the lines are parallel, TRUE otherwise.
SGfloat sgIsectLinesegPlane ( sgVec3 dst, sgVec3 v1, sgVec3 v2, sgVec4 plane ) ;Intersect a line segment with a plane. The return result is in the range 0..1 if the intersection lies between v1 and v2, >1 if beyond v2 and <0 if before v1. FLT_MAX is returned if the vector does not intersect the plane.
void sgdSetVec2 ( sgdVec2 dst, sgVec2 src ) ; void sgdSetVec3 ( sgdVec3 dst, sgVec3 src ) ; void sgdSetVec4 ( sgdVec4 dst, sgVec4 src ) ; void sgdSetMat4 ( sgdMat4 dst, sgMat4 src ) ; void sgdSetCoord ( sgdCoord *dst, sgCoord *src ) ; void sgSetVec2 ( sgVec2 dst, sgdVec2 src ) ; void sgSetVec3 ( sgVec3 dst, sgdVec3 src ) ; void sgSetVec4 ( sgVec4 dst, sgdVec4 src ) ; void sgSetMat4 ( sgMat4 dst, sgdMat4 src ) ; void sgSetCoord ( sgCoord *dst, sgdCoord *src ) ;