Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Source/SR_PipelineSetup.c
/****************************************************************************** |
** ** |
** Module: SR_PipelineSetup.c ** |
** ** |
** ** |
** Purpose: Sample Renderer setup routines, etc. ** |
** ** |
** ** |
** ** |
** Copyright (C) 1996 Apple Computer, Inc. All rights reserved. ** |
** ** |
** ** |
*****************************************************************************/ |
#include <assert.h> |
#include "QD3D.h" |
#include "QD3DMath.h" |
#include "QD3DCamera.h" |
#include "QD3DView.h" |
#include "QD3DTransform.h" |
#include "QD3DRenderer.h" |
#include "QD3DShader.h" |
#include "SR.h" |
#include "SR_Math.h" |
/****************************************************************************** |
** ** |
** Setup Routines ** |
** ** |
*****************************************************************************/ |
/*===========================================================================*\ |
* |
* Routine: SR_ClipPlanesInDC() |
* |
* Comments: Compute clipping planes in device coordinates |
* |
\*===========================================================================*/ |
static void SR_ClipPlanesInDC( |
TQ3Matrix4x4 *frustumToDC, |
float *clipPlanesInDC) |
{ |
TQ3Point3D clipPlanesInFrustum[2]; |
TQ3RationalPoint4D clipPlanesInDevCoords[2]; |
assert(frustumToDC != NULL); |
assert(clipPlanesInDC != NULL); |
clipPlanesInFrustum[0].x = -1.0; /* xmin */ |
clipPlanesInFrustum[0].y = -1.0; /* ymin */ |
clipPlanesInFrustum[0].z = -1.0; /* zmin */ |
clipPlanesInFrustum[1].x = 1.0; /* xmax */ |
clipPlanesInFrustum[1].y = 1.0; /* ymax */ |
clipPlanesInFrustum[1].z = 0.0; /* zmax */ |
Q3Point3D_To4DTransformArray( |
(TQ3Point3D *)&clipPlanesInFrustum[0], |
frustumToDC, |
&clipPlanesInDevCoords[0], |
2, |
sizeof(TQ3Point3D), |
sizeof(TQ3RationalPoint4D)); |
/* |
* xmin, xmax |
*/ |
clipPlanesInDC[0] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[0].x); |
clipPlanesInDC[1] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[1].x - 1.0); |
/* |
* ymin, ymax |
*/ |
clipPlanesInDC[2] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[1].y); |
clipPlanesInDC[3] = FLOAT_ROUND_TO_LONG(clipPlanesInDevCoords[0].y - 1.0); |
/* |
* zmin, zmax |
*/ |
clipPlanesInDC[4] = clipPlanesInDevCoords[0].z; |
clipPlanesInDC[5] = clipPlanesInDevCoords[1].z; |
} |
/****************************************************************************** |
** ** |
** Attributes Update ** |
** ** |
*****************************************************************************/ |
/*===========================================================================*\ |
* |
* Routine: SR_Update_DiffuseColor() |
* |
* Comments: Tracks changes to diffuse color state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_DiffuseColor( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3ColorRGB *diffuseColor) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(diffuseColor != NULL); |
srPrivate->viewDiffuseColor = *diffuseColor; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_HighlightState() |
* |
* Comments: Tracks changes to highlight state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_HighlightState( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3Boolean *highlightState) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
srPrivate->viewHighlightState = |
(highlightState == NULL) ? kQ3False : *highlightState; |
return (kQ3Success); |
} |
/****************************************************************************** |
** ** |
** Styles ** |
** ** |
*****************************************************************************/ |
/*===========================================================================*\ |
* |
* Routine: SR_Update_BackfacingStyle() |
* |
* Comments: Tracks changes to backfacing style state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_BackfacingStyle( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3BackfacingStyle *backfacingStyle) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(backfacingStyle != NULL); |
srPrivate->backfacingStyle = *backfacingStyle; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_OrientationStyle() |
* |
* Comments: Tracks changes to orientation style state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_OrientationStyle( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3OrientationStyle *orientationStyle) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(orientationStyle != NULL); |
srPrivate->orientationStyle = *orientationStyle; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_FillStyle() |
* |
* Comments: Tracks changes to fill style state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_FillStyle( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3FillStyle *fillStyle) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(fillStyle != NULL); |
srPrivate->viewFillStyle = *fillStyle; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_HighlightStyle() |
* |
* Comments: Tracks changes to highlight style state in the view |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_HighlightStyle( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3AttributeSet *highlightAttributeSet) |
{ |
UNUSED(view); |
assert(view != NULL); |
assert(srPrivate != NULL); |
/* |
* Bump reference count on incoming attribute set, if any |
*/ |
if (*highlightAttributeSet != NULL) { |
Q3Shared_GetReference(*highlightAttributeSet); |
} |
/* |
* Get rid of existing highlight attribute set |
*/ |
if (srPrivate->viewHighlightAttributeSet != NULL) { |
Q3Object_Dispose(srPrivate->viewHighlightAttributeSet); |
} |
/* |
* Set the highlight attribute set |
*/ |
srPrivate->viewHighlightAttributeSet = *highlightAttributeSet; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_UpdateNonInvertibleMatrix() |
* |
* Comments: Deal with noninvertible local-to-world matrices |
* |
\*===========================================================================*/ |
static void SR_UpdateNonInvertibleMatrix( |
TSRPrivate *srPrivate, |
TQ3Boolean parallelProjection) |
{ |
TQ3Matrix3x3 submatrix; |
long indexPtr[3]; |
float rowInterchanges; |
long rank; |
/* Singular */ |
/* Copy upper-left 3x3 submatrix to temporary array */ |
submatrix.value[0][0] = srPrivate->transforms.localToWorld.value[0][0]; |
submatrix.value[0][1] = srPrivate->transforms.localToWorld.value[0][1]; |
submatrix.value[0][2] = srPrivate->transforms.localToWorld.value[0][2]; |
submatrix.value[1][0] = srPrivate->transforms.localToWorld.value[1][0]; |
submatrix.value[1][1] = srPrivate->transforms.localToWorld.value[1][1]; |
submatrix.value[1][2] = srPrivate->transforms.localToWorld.value[1][2]; |
submatrix.value[2][0] = srPrivate->transforms.localToWorld.value[2][0]; |
submatrix.value[2][1] = srPrivate->transforms.localToWorld.value[2][1]; |
submatrix.value[2][2] = srPrivate->transforms.localToWorld.value[2][2]; |
/* |
* Decompose singular matrix into lower-triangular/upper-echelon form |
*/ |
SRMatrix_LUDecomposeSingular3x3( |
submatrix.value, |
indexPtr, |
&rowInterchanges, |
&rank); |
srPrivate->normalLocalToWorldRank = rank; |
if ((rank == 2) && |
(SRMatrix_ComputeFlatLand( |
&srPrivate->transforms.localToWorld, |
&submatrix, |
parallelProjection, |
&srPrivate->eyeVectorInWorldCoords, |
&srPrivate->eyePointInWorldCoords, |
&srPrivate->eyeVectorInLocalCoords, |
&srPrivate->flatWorld) == kQ3Success)) { |
return; |
} |
/* |
* Fill in some arbitrary values if failure, or rank is 0, 1, or 3 |
* |
* rank = 0 |
* |
* All geometry is transformed to a single point. Lighting and |
* culling have no meaning so choose any arbitrary eye vector. |
* Renderers should render nothing, but let's set the values |
* to something in case they try to use them. |
* |
* rank = 0 |
* |
* All geometry is transformed to a line. Lighting and |
* culling have no meaning so choose any arbitrary eye vector. |
* Renderers should render nothing, but let's set the values |
* to something in case they try to use them. |
* |
* rank = 1 |
* |
* TODO: |
* The 4x4 matrix is singular, but the 3x3 upper-left submatrix |
* has rank 3 so the element[3][3] is zero. This means that |
* all geometry is transformed to infinity. This is useful concept |
* for mathematicians, but for now, let's do the wrong thing until |
* somebody reports a bug. |
*/ |
SRVector4D_Set(&srPrivate->eyeVectorInLocalCoords, 0.0, 0.0, 1.0, 0.0); |
Q3Vector3D_Set(&srPrivate->flatWorld.normal, 0.0, 0.0, 1.0); |
srPrivate->flatWorld.constant = 0.0; |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_LocalToWorldMatrix() |
* |
* Comments: Track changes to local-to-world transform |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_LocalToWorldMatrix( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3Matrix4x4 *localToWorld) |
{ |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(localToWorld != NULL); |
UNUSED(view); |
srPrivate->transforms.localToWorld = *localToWorld; |
srPrivate->transforms.dirtyLocalToWorld = kQ3True; |
srPrivate->transforms.validLocalToWorld = kQ3True; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_LocalToFrustumMatrix() |
* |
* Comments: Track changes to local-to-frustum transform |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_LocalToFrustumMatrix( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3Matrix4x4 *localToFrustum) |
{ |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(localToFrustum != NULL); |
UNUSED(view); |
srPrivate->transforms.localToFrustum = *localToFrustum; |
srPrivate->transforms.dirtyLocalToFrustum = kQ3True; |
srPrivate->transforms.validLocalToFrustum = kQ3True; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_Update_WorldToFrustumMatrix() |
* |
* Comments: Track changes to world-to-frustum transform |
* |
\*===========================================================================*/ |
TQ3Status SR_Update_WorldToFrustumMatrix( |
TQ3ViewObject view, |
TSRPrivate *srPrivate, |
TQ3Matrix4x4 *worldToFrustum) |
{ |
assert(view != NULL); |
assert(srPrivate != NULL); |
assert(worldToFrustum != NULL); |
UNUSED(view); |
srPrivate->transforms.worldToFrustum = *worldToFrustum; |
srPrivate->transforms.dirtyWorldToFrustum = kQ3True; |
srPrivate->transforms.validWorldToFrustum = kQ3True; |
return (kQ3Success); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_SetupRegionDependentTransformations() |
* |
* Comments: Update the frustum-to-device coordinates transform |
* |
\*===========================================================================*/ |
TQ3Status SR_SetupRegionDependentTransformations( |
TSRPrivate *srPrivate) |
{ |
float frustumWindow[6] = { -1.0, 1.0, 1.0, -1.0, -1.0, 0.0 }; |
float deviceViewport[6]; |
TQ3Status status = kQ3Success; |
/* |
* The orientation of Frustum Coordinates is x right, y up, z toward. |
* The orientation of Device Coordinates is x right, y down, z toward. |
* The y axis is inverted in the mapping so the min and max values are |
* reversed in the Frustum boundaries. |
*/ |
{ |
float tmp1, tmp2; |
/* |
* xmin |
*/ |
Q3XDrawRegion_GetDeviceOffsetX(srPrivate->drawRegion, &tmp1); |
deviceViewport[0] = tmp1; |
/* |
* xmax |
*/ |
Q3XDrawRegion_GetWindowScaleX(srPrivate->drawRegion, &tmp1); |
Q3XDrawRegion_GetDeviceOffsetX(srPrivate->drawRegion, &tmp2); |
deviceViewport[1] = tmp1 - 1.0 + tmp2; |
/* |
* ymin |
*/ |
Q3XDrawRegion_GetDeviceOffsetY(srPrivate->drawRegion, &tmp1); |
deviceViewport[2] = tmp1; |
/* |
* ymax |
*/ |
Q3XDrawRegion_GetWindowScaleY(srPrivate->drawRegion, &tmp1); |
Q3XDrawRegion_GetDeviceOffsetY(srPrivate->drawRegion, &tmp2); |
deviceViewport[3] = tmp1 - 1.0 + tmp2; |
/* |
* zmin, xmax |
*/ |
deviceViewport[4] = -1.0; |
deviceViewport[5] = 0.0; |
} |
srPrivate->transforms.frustumToDC.value[0][0] |
= (deviceViewport[1] - deviceViewport[0]) / |
(frustumWindow[1] - frustumWindow[0]); |
srPrivate->transforms.frustumToDC.value[1][1] |
= (deviceViewport[3] - deviceViewport[2]) / |
(frustumWindow[3] - frustumWindow[2]); |
srPrivate->transforms.frustumToDC.value[2][2] |
= (deviceViewport[5] - deviceViewport[4]) / |
(frustumWindow[5] - frustumWindow[4]); |
srPrivate->transforms.frustumToDC.value[3][0] |
= - srPrivate->transforms.frustumToDC.value[0][0] * |
frustumWindow[0] + deviceViewport[0]; |
srPrivate->transforms.frustumToDC.value[3][1] |
= - srPrivate->transforms.frustumToDC.value[1][1] * |
frustumWindow[2] + deviceViewport[2]; |
srPrivate->transforms.frustumToDC.value[3][2] |
= - srPrivate->transforms.frustumToDC.value[2][2] * |
frustumWindow[4] + deviceViewport[4]; |
{ |
TQ3Matrix4x4 *deviceTransform; |
if (Q3XDrawRegion_GetDeviceTransform( |
srPrivate->drawRegion, |
&deviceTransform) != NULL) { |
SR_ClipPlanesInDC( |
deviceTransform, |
&srPrivate->clipPlanesInDC[0]); |
} else { |
status = kQ3Failure; |
} |
} |
srPrivate->transforms.dirtyFrustumToDC = kQ3False; |
srPrivate->transforms.validFrustumToDC = kQ3True; |
return (status); |
} |
/*===========================================================================*\ |
* |
* Routine: SR_SetupPipelineInitCamera() |
* |
* Comments: Set up intitial camera info. |
* |
\*===========================================================================*/ |
void SR_SetupPipelineInitCamera( |
TSRPrivate *srPrivate, |
TQ3CameraObject camera) |
{ |
assert(srPrivate != NULL); |
assert(camera != NULL); |
if (srPrivate->camera != NULL) { |
Q3Object_Dispose(srPrivate->camera); |
} |
srPrivate->camera = Q3Shared_GetReference(camera); |
srPrivate->cameraType = Q3Camera_GetType(camera); |
Q3Camera_GetPlacement(camera, &srPrivate->cameraPlacement); |
srPrivate->backfacingStyle = kQ3BackfacingStyleRemove; |
srPrivate->orientationStyle = kQ3OrientationStyleCounterClockwise; |
Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToWorld); |
srPrivate->transforms.dirtyLocalToWorld = kQ3False; |
srPrivate->transforms.validLocalToWorld = kQ3False; |
Q3Matrix4x4_SetIdentity(&srPrivate->transforms.worldToFrustum); |
srPrivate->transforms.dirtyWorldToFrustum = kQ3False; |
srPrivate->transforms.validWorldToFrustum = kQ3False; |
Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToFrustum); |
srPrivate->transforms.dirtyLocalToFrustum = kQ3False; |
srPrivate->transforms.validLocalToFrustum = kQ3False; |
Q3Matrix4x4_SetIdentity(&srPrivate->transforms.frustumToDC); |
srPrivate->transforms.dirtyFrustumToDC = kQ3False; |
srPrivate->transforms.validFrustumToDC = kQ3False; |
Q3Matrix4x4_SetIdentity(&srPrivate->transforms.localToDC); |
srPrivate->transforms.dirtyLocalToDC = kQ3False; |
srPrivate->transforms.validLocalToDC = kQ3False; |
} |
/*===========================================================================*\ |
* |
* Routine: SR_SetupPipelineExit() |
* |
* Comments: Dispose of things to which SR has a reference. |
* |
\*===========================================================================*/ |
void SR_SetupPipelineExit( |
TSRPrivate *srPrivate) |
{ |
assert(srPrivate != NULL); |
/* |
* Dispose of highlight attribute set |
*/ |
if (srPrivate->viewHighlightAttributeSet != NULL) { |
Q3Object_Dispose(srPrivate->viewHighlightAttributeSet); |
srPrivate->viewHighlightAttributeSet = NULL; |
} |
/* |
* Dispose of camera |
*/ |
if (srPrivate->camera != NULL) { |
Q3Object_Dispose(srPrivate->camera); |
srPrivate->camera = NULL; |
} |
} |
/*===========================================================================*\ |
* |
* Routine: SR_UpdatePipeline() |
* |
* Comments: |
* |
\*===========================================================================*/ |
TQ3Status SR_UpdatePipeline( |
TSRPrivate *srPrivate) |
{ |
TQ3Status status = kQ3Success; |
assert(srPrivate != NULL); |
/* |
* If no transforms have changed since the last primitive, |
* then simply return. |
*/ |
if (!(srPrivate->transforms.dirtyLocalToWorld || |
srPrivate->transforms.dirtyLocalToFrustum || |
srPrivate->transforms.dirtyWorldToFrustum || |
srPrivate->transforms.dirtyFrustumToDC || |
srPrivate->transforms.dirtyLocalToDC)) { |
return (kQ3Success); |
} |
/* |
* Create local-to-device-coordinates transform |
*/ |
Q3Matrix4x4_Multiply( |
&srPrivate->transforms.localToFrustum, |
&srPrivate->transforms.frustumToDC, |
&srPrivate->transforms.localToDC); |
/* |
* |
* Compute eye vector or point in local coordinates by |
* transforming the eye vector or point in world coordinates |
* back to local coordinates. If the local-to-world matrix |
* is singular and the upper-left 3x3 submatrix has rank 2, |
* compute the eye vector in local coordinates and the plane |
* equation of the transformed geometry in world coordinates |
* such that the vectors in both coordinate systems points |
* in the same hemisphere as the eye. |
* |
*/ |
if (srPrivate->cameraType == kQ3CameraTypeOrthographic) { |
TQ3Vector3D v; |
TSRVector4D eyeVectorWC; |
TQ3Matrix4x4 inverse; |
/* Orthographic projection */ |
/* Calculate world eye vector from camera placement */ |
Q3Point3D_Subtract( |
&srPrivate->cameraPlacement.cameraLocation, |
&srPrivate->cameraPlacement.pointOfInterest, |
&v); |
SRVector3D_To4D(&v, &eyeVectorWC); |
SRVector4D_Normalize(&eyeVectorWC, &srPrivate->eyeVectorInWorldCoords); |
if (Q3Matrix4x4_Invert( |
&srPrivate->transforms.localToWorld, |
&inverse) != NULL) { |
TSRVector4D eyeVectorLC; |
TQ3Matrix4x4 transpose; |
/* Nonsingular */ |
Q3Matrix4x4_Transpose( |
&srPrivate->transforms.localToWorld, |
&transpose); |
SRVector4D_Transform( |
&eyeVectorWC, |
&transpose, |
&eyeVectorLC); |
SRVector4D_Normalize( |
&eyeVectorLC, |
&srPrivate->eyeVectorInLocalCoords); |
srPrivate->normalLocalToWorldRank = 3; |
} else { |
SR_UpdateNonInvertibleMatrix(srPrivate, kQ3True); |
} |
} else /* Non orthographic */ { |
TQ3Matrix4x4 inverse; |
/* Perspective projection */ |
/* Get world eye location from camera placement */ |
Q3Point3D_To4D( |
&srPrivate->cameraPlacement.cameraLocation, |
&srPrivate->eyePointInWorldCoords); |
if (Q3Matrix4x4_Invert( |
&srPrivate->transforms.localToWorld, |
&inverse) != NULL) { |
float wValue; |
/* Nonsingular */ |
Q3RationalPoint4D_Transform( |
&srPrivate->eyePointInWorldCoords, |
&inverse, |
&srPrivate->eyePointInLocalCoords); |
wValue = 1.0 / srPrivate->eyePointInLocalCoords.w; |
srPrivate->eyePointInLocalCoords.x *= wValue; |
srPrivate->eyePointInLocalCoords.y *= wValue; |
srPrivate->eyePointInLocalCoords.z *= wValue; |
srPrivate->eyePointInLocalCoords.w = 1.0; |
srPrivate->normalLocalToWorldRank = 3; |
} else { |
SR_UpdateNonInvertibleMatrix(srPrivate, kQ3False); |
} |
} |
/* |
* Set the states of the transforms to "clean" |
*/ |
srPrivate->transforms.dirtyLocalToWorld = kQ3False; |
srPrivate->transforms.dirtyLocalToFrustum = kQ3False; |
srPrivate->transforms.dirtyWorldToFrustum = kQ3False; |
srPrivate->transforms.dirtyFrustumToDC = kQ3False; |
srPrivate->transforms.dirtyLocalToDC = kQ3False; |
/* |
* Set validity state |
*/ |
srPrivate->transforms.validLocalToDC = kQ3True; |
return (kQ3Success); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14