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.
sources/BoxPaint_Support.c
/* BoxPaint_Support.c |
Quickdraw 3D sample code |
This file contains utility routines for QuickDraw 3d sample code. This |
app shows how to apply a texture shader to an object. Bear in mind |
that any object that you wish to texture map needs to have UV parameters |
applied. |
Nick Thompson |
(c)1994-96 Apple computer Inc., All Rights Reserved |
*/ |
/* -------------------------------------------------------------------- |
** Includes |
*/ |
#include "BoxPaint_Support.h" |
#include "QD3DDrawContext.h" |
#include "QD3DRenderer.h" |
#include "QD3DShader.h" |
#include "QD3DCamera.h" |
#include "QD3DLight.h" |
#include "QD3DGeometry.h" |
#include "QD3DGroup.h" |
#include "QD3DMath.h" |
#include "QD3DTransform.h" |
/* -------------------------------------------------------------------- |
** Global Variables |
*/ |
static TQ3Point3D documentGroupCenter; |
static float documentGroupScale; |
/* -------------------------------------------------------------------- |
** Local Functions |
*/ |
void GetGroupBBox(DocumentPtr theDocument, TQ3BoundingBox *viewBBox) ; |
static TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ; |
static TQ3Status GetDocumentGroupBoundingBox(DocumentPtr theDocument, |
TQ3BoundingBox *viewBBox); |
TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow) ; |
TQ3CameraObject MyNewCamera(WindowPtr theWindow) ; |
TQ3GroupObject MyNewLights(void) ; |
/* ---------------------------------------------------------------------------------- */ |
/* attach a shader to the group */ |
static TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) |
{ |
/* TQ3ShaderObject illuminationShader = Q3PhongIllumination_New(); |
*/ TQ3ShaderObject illuminationShader = Q3LambertIllumination_New(); |
Q3Group_AddObject(group, illuminationShader); |
Q3Object_Dispose(illuminationShader); |
return(kQ3Success); |
} |
TQ3ViewObject MyNewView(WindowPtr theWindow) |
{ |
TQ3Status myStatus; |
TQ3ViewObject myView; |
TQ3DrawContextObject myDrawContext; |
TQ3RendererObject myRenderer; |
TQ3CameraObject myCamera; |
TQ3GroupObject myLights; |
myView = Q3View_New(); |
/* Create and set draw context. */ |
if ((myDrawContext = MyNewDrawContext(theWindow)) == nil ) |
goto bail; |
if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure ) |
goto bail; |
Q3Object_Dispose( myDrawContext ) ; |
/* Create and set renderer. */ |
/* this would use the wireframe renderer */ |
/* Not very useful for this app */ |
#if 0 |
myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame); |
if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) { |
goto bail; |
} |
#else |
/* this would use the interactive plug-in renderer */ |
if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != nil ) { |
if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) { |
goto bail; |
} |
} |
else { |
goto bail; |
} |
#endif |
Q3Object_Dispose( myRenderer ) ; |
/* Create and set camera. */ |
if ( (myCamera = MyNewCamera(theWindow)) == nil ) |
goto bail; |
if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure ) |
goto bail; |
Q3Object_Dispose( myCamera ) ; |
/* Create and set lights. */ |
if ((myLights = MyNewLights()) == nil ) |
goto bail; |
if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure ) |
goto bail; |
Q3Object_Dispose(myLights); |
/* Done!!! */ |
return ( myView ); |
bail: |
/* If any of the above failed, then don't return a view. */ |
return ( nil ); |
} |
/* ---------------------------------------------------------------------------------- */ |
TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow) |
{ |
TQ3DrawContextData myDrawContextData; |
TQ3MacDrawContextData myMacDrawContextData; |
TQ3ColorARGB ClearColor; |
TQ3DrawContextObject myDrawContext ; |
/* Set the background color. */ |
ClearColor.a = 1.0; |
ClearColor.r = 1.0; |
ClearColor.g = 1.0; |
ClearColor.b = 1.0; |
/* Fill in draw context data. */ |
myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor; |
myDrawContextData.clearImageColor = ClearColor; |
myDrawContextData.paneState = kQ3False; |
myDrawContextData.maskState = kQ3False; |
myDrawContextData.doubleBufferState = kQ3True; |
myMacDrawContextData.drawContextData = myDrawContextData; |
myMacDrawContextData.window = (CGrafPtr) theWindow; /* this is the window associated with the view */ |
myMacDrawContextData.library = kQ3Mac2DLibraryNone; |
myMacDrawContextData.viewPort = nil; |
myMacDrawContextData.grafPort = nil; |
/* Create draw context and return it, if itÕs nil the caller must handle */ |
myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ; |
return myDrawContext ; |
} |
/* ---------------------------------------------------------------------------------- */ |
TQ3CameraObject MyNewCamera(WindowPtr theWindow) |
{ |
TQ3ViewAngleAspectCameraData perspectiveData; |
TQ3CameraObject camera; |
TQ3Point3D from = { 0.0, 0.0, 6.0 }; |
TQ3Point3D to = { 0.0, 0.0, 0.0 }; |
TQ3Vector3D up = { 0.0, 1.0, 0.0 }; |
float fieldOfView = 1.0; |
float hither = 0.001; |
float yon = 1000; |
TQ3Status returnVal = kQ3Failure ; |
perspectiveData.cameraData.placement.cameraLocation = from; |
perspectiveData.cameraData.placement.pointOfInterest = to; |
perspectiveData.cameraData.placement.upVector = up; |
perspectiveData.cameraData.range.hither = hither; |
perspectiveData.cameraData.range.yon = yon; |
perspectiveData.cameraData.viewPort.origin.x = -1.0; |
perspectiveData.cameraData.viewPort.origin.y = 1.0; |
perspectiveData.cameraData.viewPort.width = 2.0; |
perspectiveData.cameraData.viewPort.height = 2.0; |
perspectiveData.fov = fieldOfView; |
perspectiveData.aspectRatioXToY = |
(float) (theWindow->portRect.right - theWindow->portRect.left) / |
(float) (theWindow->portRect.bottom - theWindow->portRect.top); |
camera = Q3ViewAngleAspectCamera_New(&perspectiveData); |
return camera ; |
} |
/* ---------------------------------------------------------------------------------- */ |
TQ3GroupObject MyNewLights() |
{ |
TQ3GroupPosition myGroupPosition; |
TQ3GroupObject myLightList; |
TQ3LightData myLightData; |
TQ3PointLightData myPointLightData; |
TQ3DirectionalLightData myDirectionalLightData; |
TQ3LightObject myAmbientLight, myPointLight, myFillLight; |
TQ3Point3D pointLocation = { -10.0, 0.0, 10.0 }; |
TQ3Vector3D fillDirection = { 10.0, 0.0, 10.0 }; |
TQ3ColorRGB WhiteLight = { 1.0, 1.0, 1.0 }; |
/* Set up light data for ambient light. This light data will be used for point and fill */ |
/* light also. */ |
myLightData.isOn = kQ3True; |
myLightData.color = WhiteLight; |
/* Create ambient light. */ |
myLightData.brightness = .2; |
myAmbientLight = Q3AmbientLight_New(&myLightData); |
if ( myAmbientLight == nil ) |
goto bail; |
/* Create point light. */ |
myLightData.brightness = 1.0; |
myPointLightData.lightData = myLightData; |
myPointLightData.castsShadows = kQ3False; |
myPointLightData.attenuation = kQ3AttenuationTypeNone; |
myPointLightData.location = pointLocation; |
myPointLight = Q3PointLight_New(&myPointLightData); |
if ( myPointLight == nil ) |
goto bail; |
/* Create fill light. */ |
myLightData.brightness = .2; |
myDirectionalLightData.lightData = myLightData; |
myDirectionalLightData.castsShadows = kQ3False; |
myDirectionalLightData.direction = fillDirection; |
myFillLight = Q3DirectionalLight_New(&myDirectionalLightData); |
if ( myFillLight == nil ) |
goto bail; |
/* Create light group and add each of the lights into the group. */ |
myLightList = Q3LightGroup_New(); |
if ( myLightList == nil ) |
goto bail; |
myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight); |
if ( myGroupPosition == 0 ) |
goto bail; |
myGroupPosition = Q3Group_AddObject(myLightList, myPointLight); |
if ( myGroupPosition == 0 ) |
goto bail; |
myGroupPosition = Q3Group_AddObject(myLightList, myFillLight); |
if ( myGroupPosition == 0 ) |
goto bail; |
Q3Object_Dispose( myAmbientLight ) ; |
Q3Object_Dispose( myPointLight ) ; |
Q3Object_Dispose( myFillLight ) ; |
/* Done! */ |
return ( myLightList ); |
bail: |
/* If any of the above failed, then return nothing! */ |
return ( nil ); |
} |
static TQ3GroupPosition MyAddTransformedObjectToGroup( TQ3GroupObject theGroup, TQ3Object theObject, TQ3Vector3D *translation ) |
{ |
TQ3TransformObject transform; |
transform = Q3TranslateTransform_New(translation); |
Q3Group_AddObject(theGroup, transform); |
Q3Object_Dispose(transform); |
return Q3Group_AddObject(theGroup, theObject); |
} |
TQ3GroupObject MyNewModel() |
{ |
TQ3GroupObject myGroup = NULL; |
TQ3GeometryObject myBox; |
TQ3BoxData myBoxData; |
TQ3ShaderObject myIlluminationShader ; |
TQ3Vector3D translation; |
if ((myGroup = Q3DisplayGroup_New()) != NULL ) { |
MyAddShaderToGroup( myGroup ) ; |
/* Define a shading type for the group */ |
/* and add the shader to the group */ |
myIlluminationShader = Q3PhongIllumination_New(); |
Q3Group_AddObject(myGroup, myIlluminationShader); |
myBoxData.boxAttributeSet = nil; |
myBoxData.faceAttributeSet = nil; |
/* create the box itself */ |
Q3Point3D_Set(&myBoxData.origin, 0, 0, 0); |
Q3Vector3D_Set(&myBoxData.orientation, 0, 1, 0); |
Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 1); |
Q3Vector3D_Set(&myBoxData.minorAxis, 1, 0, 0); |
myBox = Q3Box_New(&myBoxData); |
/* put four copies of the box into the group, each one with its own translation */ |
translation.x = 0;translation.y = 0;translation.z = 0; |
MyAddTransformedObjectToGroup( myGroup, myBox, &translation ) ; |
} |
/* dispose of the objects we created here */ |
if( myIlluminationShader ) |
Q3Object_Dispose(myIlluminationShader); |
if( myBox ) |
Q3Object_Dispose( myBox ); |
/* Done! */ |
return ( myGroup ); |
} |
/* ----------------------------------------------------------------------------------------------- */ |
static TQ3Status GetDocumentGroupBoundingBox( |
DocumentPtr theDocument , |
TQ3BoundingBox *viewBBox) |
{ |
TQ3Status status; |
TQ3ViewStatus viewStatus ; |
status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate ); |
do { |
status = Document_SubmitScene( theDocument ) ; |
} while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse ); |
return status ; |
} |
/* ---------------------------------------------------------------------------------- */ |
void GetGroupBBox( |
DocumentPtr theDocument, |
TQ3BoundingBox *viewBBox) |
{ |
TQ3Point3D from = { 0.0, 0.0, 1.0 }; |
TQ3Point3D to = { 0.0, 0.0, 0.0 }; |
TQ3Vector3D up = { 0.0, 1.0, 0.0 }; |
float fieldOfView = .52359333333; |
float hither = 0.5; |
float yon = 1.5; |
TQ3GroupObject mainGroup = theDocument->fModel ; |
TQ3Status status; |
status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ; |
/* */ |
/* If we have a point model, then the "viewBBox" would end up */ |
/* being a "singularity" at the location of the point. As */ |
/* this bounding "box" is used in setting up the camera spec, */ |
/* we get bogus input into Escher. */ |
{ |
float xSize, ySize, zSize; |
xSize = viewBBox->max.x - viewBBox->min.x; |
ySize = viewBBox->max.y - viewBBox->min.y; |
zSize = viewBBox->max.z - viewBBox->min.z; |
if (xSize <= kQ3RealZero && |
ySize <= kQ3RealZero && |
zSize <= kQ3RealZero) { |
viewBBox->max.x += 0.0001; |
viewBBox->max.y += 0.0001; |
viewBBox->max.z += 0.0001; |
viewBBox->min.x -= 0.0001; |
viewBBox->min.y -= 0.0001; |
viewBBox->min.z -= 0.0001; |
} |
} |
} |
/* ------------------------------------------------------------------------ */ |
TQ3Point3D AdjustCamera( |
DocumentPtr theDocument) |
{ |
float fieldOfView; |
float hither; |
float yon; |
TQ3CameraPlacement placement; |
TQ3CameraRange range; |
TQ3BoundingBox viewBBox; |
long fromAxis; |
float maxDimension; |
float xSize, ySize, zSize; |
float weights[2] = { 0.5, 0.5 }; |
TQ3Point3D points[2]; |
TQ3Vector3D viewVector; |
TQ3Vector3D normViewVector; |
TQ3Vector3D eyeToFrontClip; |
TQ3Vector3D eyeToBackClip; |
float viewDistance; |
TQ3Vector3D diagonalVector; |
float ratio; |
TQ3CameraObject camera; |
TQ3ViewObject theView = theDocument->fView ; |
TQ3GroupObject mainGroup = theDocument->fModel ; |
TQ3Point3D *documentGroupCenter = &theDocument->fGroupCenter ; |
float *documentGroupScale = &theDocument->fGroupScale ; |
short winWidth, |
winHeight; |
winWidth = ((theDocument->fWindow)->portRect.right - (theDocument->fWindow)->portRect.left); |
winHeight = ((theDocument->fWindow)->portRect.bottom - (theDocument->fWindow)->portRect.top); |
Q3View_GetCamera( theView, &camera); |
GetGroupBBox( theDocument, &viewBBox); |
/* |
* If we have a point model, then the "viewBBox" would end up |
* being a "singularity" at the location of the point. As |
* this bounding "box" is used in setting up the camera spec, |
* we get bogus input into Escher. |
*/ |
xSize = viewBBox.max.x - viewBBox.min.x; |
ySize = viewBBox.max.y - viewBBox.min.y; |
zSize = viewBBox.max.z - viewBBox.min.z; |
if (xSize <= kQ3RealZero && |
ySize <= kQ3RealZero && |
zSize <= kQ3RealZero) { |
viewBBox.max.x += 0.0001; |
viewBBox.max.y += 0.0001; |
viewBBox.max.z += 0.0001; |
viewBBox.min.x -= 0.0001; |
viewBBox.min.y -= 0.0001; |
viewBBox.min.z -= 0.0001; |
} |
points[0] = viewBBox.min; |
points[1] = viewBBox.max; |
Q3Point3D_AffineComb(points, weights, 2, documentGroupCenter); |
/* |
* The "from" point is on a vector perpendicular to the plane |
* in which the bounding box has greatest dimension. As "up" is |
* always in the positive y direction, look at x and z directions. |
*/ |
xSize = viewBBox.max.x - viewBBox.min.x; |
zSize = viewBBox.max.z - viewBBox.min.z; |
if (xSize > zSize) { |
fromAxis = kQ3AxisZ; |
} else { |
fromAxis = kQ3AxisX; |
} |
/* |
* Compute the length of the diagonal of the bounding box. |
* |
* The hither and yon planes are adjusted so that the |
* diagonal of the bounding box is 7/8 the size of the |
* minimum dimension of the view frustum. The diagonal is used instead |
* of the maximum size (in x, y, or z) so that when you rotate |
* the object, the corners don't get clipped out. |
*/ |
Q3Point3D_Subtract( |
&viewBBox.max, |
&viewBBox.min, |
&diagonalVector); |
maxDimension = Q3Vector3D_Length(&diagonalVector); |
maxDimension *= 8.0 / 7.0; |
ratio = 1.0 / maxDimension; |
*documentGroupScale = ratio; |
Q3Camera_GetPlacement(camera, &placement); |
Q3Point3D_Subtract( |
&placement.cameraLocation, |
&placement.pointOfInterest, |
&viewVector); |
viewDistance = Q3Vector3D_Length(&viewVector); |
Q3Vector3D_Normalize(&viewVector, &normViewVector); |
Q3Vector3D_Scale(&normViewVector, |
viewDistance - ratio * maxDimension/2.0, |
&eyeToFrontClip); |
Q3Vector3D_Scale(&normViewVector, |
viewDistance + ratio * maxDimension/2.0, |
&eyeToBackClip); |
hither = Q3Vector3D_Length(&eyeToFrontClip); |
yon = Q3Vector3D_Length(&eyeToBackClip); |
fieldOfView = 2 * atan((ratio * maxDimension/2.0)/hither); |
range.hither = hither; |
range.yon = yon; |
Q3Camera_SetRange(camera, &range); |
Q3ViewAngleAspectCamera_SetFOV( |
camera, fieldOfView); |
Q3ViewAngleAspectCamera_SetAspectRatio( |
camera, (float) winWidth / (float) winHeight); |
Q3Object_Dispose(camera); |
return( *documentGroupCenter ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14