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/WRay_Scene.c
/* |
* WRay_Scene.c |
* |
* QuickDraw 3D 1.6 Sample |
* Robert Dierkes |
* |
* 07/28/98 RDD Created. |
*/ |
/*------------------*/ |
/* Include Files */ |
/*------------------*/ |
#include "QD3D.h" |
#include "QD3DDrawContext.h" |
#include "QD3DCamera.h" |
#include "QD3DLight.h" |
#include "QD3DGeometry.h" |
#include "QD3DGroup.h" |
#include "QD3DMath.h" |
#include "QD3DRenderer.h" |
#include "QD3DShader.h" |
#include "QD3DTransform.h" |
#include "QD3DView.h" |
#include "WRay_Error.h" |
#include "WRay_Document.h" |
#include "WRay_Main.h" |
#include "WRay_Memory.h" |
#include "WRay_Scene.h" |
#include "WRay_System.h" |
#include <math.h> |
/*------------------*/ |
/* Constants */ |
/*------------------*/ |
#define kDefaultCamLoc 0.0, 0.0, 10.0 |
#define kDefaultPtOfInt 0.0, 0.0, 0.0 |
#define kDefaultUpVector 0.0, 1.0, 0.0 |
#define kAngleDelta Q3Math_DegreesToRadians(2.0f) |
/*----------------------*/ |
/* Global Declarations */ |
/*----------------------*/ |
static TQ3CameraObject gCamera = NULL; |
static float gRotationAngle = Q3Math_DegreesToRadians(90.0f); |
static TQ3Boolean gDoRotation = kQ3False; |
/*----------------------*/ |
/* Local Prototypes */ |
/*----------------------*/ |
static |
TQ3DrawContextObject Scene_NewDrawContext( |
WindowPtr pWindow); |
static |
TQ3Status Scene_ChangeViewAttributes( |
TQ3ViewObject view); |
static |
TQ3Status Scene_NewRenderer( |
TQ3ViewObject view, |
TQ3ObjectType rendererType); |
static |
TQ3CameraObject Scene_NewCamera( |
Rect *pWindowRect); |
static |
TQ3GroupObject Scene_NewLights( |
void); |
static |
TQ3Status Scene_NewStyles( |
TQ3GroupObject group); |
static |
TQ3Status Scene_NewScene( |
TQ3GroupObject group); |
static |
TQ3Status Scene_NewBoxSides( |
TQ3GroupObject group, |
TQ3BoundingBox *pBBox); |
/* |
* Scene_NewView |
*/ |
TQ3ViewObject Scene_NewView( |
WindowPtr pWindow) |
{ |
TQ3Status status = kQ3Failure; |
TQ3ViewObject view; |
TQ3DrawContextObject drawContext; |
TQ3CameraObject camera; |
TQ3GroupObject lights; |
view = Q3View_New(); |
DEBUG_ASSERT(view != NULL, Scene_NewView); |
if (view == NULL) { |
goto bail; |
} |
/* Create and set draw context */ |
DEBUG_ASSERT(pWindow != NULL, Scene_NewView); |
if ((drawContext = Scene_NewDrawContext(pWindow)) == NULL) { |
goto bail; |
} |
if ((status = Q3View_SetDrawContext(view, drawContext)) == kQ3Failure) { |
goto bail; |
} |
Object_Dispose_NULL(&drawContext); |
/* Change view's attributes */ |
if ((status = Scene_ChangeViewAttributes(view)) == kQ3Failure) { |
goto bail; |
} |
/* Create and set renderer */ |
if ((status = Scene_NewRenderer(view, kQ3RendererTypeInteractive)) == kQ3Failure) { |
goto bail; |
} |
/* Create and set camera */ |
if ((camera = Scene_NewCamera(&pWindow->portRect)) == NULL) { |
goto bail; |
} |
if ((status = Q3View_SetCamera(view, camera)) == kQ3Failure) { |
goto bail; |
} |
/* Save reference to camera */ |
gCamera = camera; |
Object_Dispose_NULL(&camera); |
/* Create and set lights */ |
if ((lights = Scene_NewLights()) == NULL) { |
goto bail; |
} |
if ((status = Q3View_SetLightGroup(view, lights)) == kQ3Failure) { |
goto bail; |
} |
Object_Dispose_NULL(&lights); |
return (view); |
bail: |
return NULL; |
} |
/* |
* Scene_NewDrawContext |
*/ |
static |
TQ3DrawContextObject Scene_NewDrawContext( |
WindowPtr pWindow) |
{ |
TQ3DrawContextObject drawContext; |
TQ3DrawContextData *pDCData; |
TQ3MacDrawContextData macDrawContextData; |
pDCData = &macDrawContextData.drawContextData; |
#define kDefaultClearImageColor 1.0, 0.15, 0.15, 0.15 |
/* Fill in common draw context data */ |
pDCData->clearImageMethod = kQ3ClearMethodWithColor; |
Q3ColorARGB_Set(&pDCData->clearImageColor, kDefaultClearImageColor); |
pDCData->paneState = kQ3False; |
pDCData->maskState = kQ3False; |
/* Mac draw context */ |
pDCData->doubleBufferState = kQ3True; |
/* This is the window associated with the view */ |
DEBUG_ASSERT(pWindow != NULL, Scene_NewDrawContext); |
macDrawContextData.window = (CGrafPtr) pWindow; |
macDrawContextData.library = kQ3Mac2DLibraryNone; |
macDrawContextData.viewPort = NULL; |
macDrawContextData.grafPort = NULL; |
/* Create draw context and return it, if itÕs nil the caller must handle */ |
drawContext = Q3MacDrawContext_New(&macDrawContextData); |
DEBUG_ASSERT(drawContext != NULL, Scene_NewDrawContext); |
#undef kDefaultClearImageColor |
return drawContext; |
} |
/* |
* Scene_ChangeViewAttributes |
*/ |
TQ3Status Scene_ChangeViewAttributes( |
TQ3ViewObject view) |
{ |
TQ3Status status = kQ3Failure; |
TQ3AttributeSet attributeSet = NULL; |
TQ3ColorRGB color = { 1.0, 0.0, 0.0 }; |
status = Q3View_GetDefaultAttributeSet(view, &attributeSet); |
DEBUG_ASSERT(status == kQ3Success, Q3View_GetDefaultAttributeSet); |
DEBUG_ASSERT(attributeSet != NULL, Q3View_GetDefaultAttributeSet); |
if (status == kQ3Failure) { |
return status; |
} |
/* Change view's specular color */ |
Q3AttributeSet_Add(attributeSet, kQ3AttributeTypeSpecularColor, &color); |
status = Q3View_SetDefaultAttributeSet(view, attributeSet); |
DEBUG_ASSERT(status == kQ3Success, Q3View_SetDefaultAttributeSet); |
Object_Dispose_NULL(&attributeSet); |
return status; |
} |
/* |
* Scene_NewRenderer |
*/ |
TQ3Status Scene_NewRenderer( |
TQ3ViewObject view, |
TQ3ObjectType rendererType) |
{ |
TQ3Status status = kQ3Failure; |
TQ3RendererObject renderer = NULL; |
if (view == NULL) { |
ERROR_DEBUG_STR("Scene_NewRenderer: view == NULL."); |
return status; |
} |
/* Use the interactive software renderer if present */ |
if ((renderer = Q3Renderer_NewFromType(rendererType)) != NULL) { |
if ((status = Q3View_SetRenderer(view, renderer)) == kQ3Success) { |
/* Use hardware accelerator for IR if present */ |
if (rendererType == kQ3RendererTypeInteractive) { |
Q3InteractiveRenderer_SetDoubleBufferBypass(renderer, kQ3True); |
} |
} |
else { |
ERROR_DEBUG_STR("Scene_NewRenderer: Q3View_SetRenderer failed."); |
} |
} |
else { |
/* Default to wireframe renderer */ |
if ((renderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame)) != NULL) { |
if ((status = Q3View_SetRenderer(view, renderer)) == kQ3Failure) { |
ERROR_DEBUG_STR("Scene_NewRenderer: Q3View_SetRenderer(WF) failed."); |
} |
} |
} |
Object_Dispose_NULL(&renderer); |
return status; |
} |
/* |
* Scene_NewCamera |
*/ |
static |
TQ3CameraObject Scene_NewCamera( |
Rect *pWindowRect) |
{ |
TQ3ViewAngleAspectCameraData |
perspectiveData; |
TQ3CameraObject camera; |
TQ3Point3D from = { kDefaultCamLoc }; |
TQ3Point3D to = { kDefaultPtOfInt }; |
TQ3Vector3D up = { kDefaultUpVector }; |
perspectiveData.cameraData.placement.cameraLocation = from; |
perspectiveData.cameraData.placement.pointOfInterest = to; |
perspectiveData.cameraData.placement.upVector = up; |
perspectiveData.cameraData.range.hither = kDefaultHither; |
perspectiveData.cameraData.range.yon = kDefaultYon; |
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 = kDefaultFieldOfView; |
perspectiveData.aspectRatioXToY = |
(float) (pWindowRect->right - pWindowRect->left) / |
(float) (pWindowRect->bottom - pWindowRect->top); |
camera = Q3ViewAngleAspectCamera_New(&perspectiveData); |
DEBUG_ASSERT(camera != NULL, Scene_NewCamera); |
return camera; |
} |
/*----------------------------------------------------------------------------------*/ |
/* |
* Scene_NewLights |
*/ |
static |
TQ3GroupObject Scene_NewLights( |
void) |
{ |
TQ3GroupPosition groupPosition = NULL; |
TQ3GroupObject lightGroup = NULL; |
TQ3LightObject light = NULL; |
TQ3DirectionalLightData comboLightData; |
/* Create light group and add each of the lights into the group */ |
lightGroup = Q3LightGroup_New(); |
if (lightGroup == NULL) |
goto bail; |
/* Create ambient light */ |
comboLightData.lightData.isOn = kQ3True; |
comboLightData.lightData.brightness = 0.6f; |
Q3ColorRGB_Set(&comboLightData.lightData.color, 1.0f, 1.0f, 1.0f); |
light = Q3AmbientLight_New(&comboLightData.lightData); |
DEBUG_ASSERT(light != NULL, Q3AmbientLight_New); |
if (light == NULL) |
goto bail; |
groupPosition = Q3Group_AddObject(lightGroup, light); |
DEBUG_ASSERT(groupPosition != NULL, Q3Group_AddObject); |
if (groupPosition == NULL) |
goto bail; |
Object_Dispose_NULL(&light); |
/* Create a yellow directional light */ |
comboLightData.lightData.brightness = 0.9f; |
Q3ColorRGB_Set(&comboLightData.lightData.color, 0.9f, 0.9f, 0.0f); |
comboLightData.castsShadows = kQ3True; |
Q3Vector3D_Set(&comboLightData.direction, 1.0f, -1.0f, -2.0f); |
light = Q3DirectionalLight_New(&comboLightData); |
DEBUG_ASSERT(light != NULL, Q3DirectionalLight_New); |
if (light == NULL) |
goto bail; |
groupPosition = Q3Group_AddObject(lightGroup, light); |
DEBUG_ASSERT(groupPosition != NULL, Q3Group_AddObject); |
if (groupPosition == NULL) |
goto bail; |
Object_Dispose_NULL(&light); |
/* Create a magenta directional light */ |
comboLightData.lightData.brightness = 0.9f; |
Q3ColorRGB_Set(&comboLightData.lightData.color, 0.9f, 0.0f, 0.9f); |
comboLightData.castsShadows = kQ3True; |
Q3Vector3D_Set(&comboLightData.direction, -1.0f, 1.0f, 2.0f); |
light = Q3DirectionalLight_New(&comboLightData); |
DEBUG_ASSERT(light != NULL, Q3DirectionalLight_New); |
if (light == NULL) |
goto bail; |
groupPosition = Q3Group_AddObject(lightGroup, light); |
DEBUG_ASSERT(groupPosition != NULL, Q3Group_AddObject); |
if (groupPosition == NULL) |
goto bail; |
Object_Dispose_NULL(&light); |
return (lightGroup); |
bail: |
Object_Dispose_NULL(&light); |
Object_Dispose_NULL(&lightGroup); |
return (NULL); |
} |
/* |
* Scene_NewModel |
*/ |
TQ3GroupObject Scene_NewModel( |
void) |
{ |
TQ3Status status = kQ3Failure; |
TQ3GroupObject model = NULL; |
TQ3DisplayGroupState state; |
TQ3ShaderObject illuminationShader = NULL; |
if ((model = Q3OrderedDisplayGroup_New()) != NULL) { |
/* Make group inline so state isn't pushed */ |
if (Q3DisplayGroup_GetState(model, &state) == kQ3Success) { |
state |= kQ3DisplayGroupStateMaskIsInline; |
Q3DisplayGroup_SetState(model, state); |
} |
/* Add shader to model */ |
if ((illuminationShader = Q3PhongIllumination_New()) != NULL) { |
Q3Group_AddObject(model, illuminationShader); |
Object_Dispose_NULL(&illuminationShader); |
/* Add styles to model */ |
if ((status = Scene_NewStyles(model)) == kQ3Success) { |
/* Add geometries to model */ |
status = Scene_NewScene(model); |
} |
} |
if (status == kQ3Failure) { |
Object_Dispose_NULL(&model); |
} |
} |
return (model); |
} |
/* |
* Scene_NewStyles |
*/ |
static |
TQ3Status Scene_NewStyles( |
TQ3GroupObject group) |
{ |
TQ3StyleObject style; |
if (group == NULL) { |
ERROR_DEBUG_STR("Scene_NewStyles: group == NULL."); |
return kQ3Failure; |
} |
/* Interpolation Style */ |
if ((style = Q3InterpolationStyle_New(kQ3InterpolationStyleVertex)) == NULL) { |
ERROR_DEBUG_STR("Scene_NewStyles: Q3InterpolationStyle_New failed."); |
return kQ3Failure; |
} |
Q3Group_AddObject(group, style); |
Object_Dispose_NULL(&style); |
/* Backfacing Style */ |
if ((style = Q3BackfacingStyle_New(kQ3BackfacingStyleBoth)) == NULL) { |
ERROR_DEBUG_STR("Scene_NewStyles: Q3BackfacingStyle_New failed."); |
return kQ3Failure; |
} |
Q3Group_AddObject(group, style); |
Object_Dispose_NULL(&style); |
/* Fill Style */ |
if ((style = Q3FillStyle_New(kQ3FillStyleFilled)) == NULL) { |
ERROR_DEBUG_STR("Scene_NewStyles: Q3FillStyle_New failed."); |
return kQ3Failure; |
} |
Q3Group_AddObject(group, style); |
Object_Dispose_NULL(&style); |
/* Subdivision Style */ |
{ |
TQ3SubdivisionStyleData data; |
data.method = kQ3SubdivisionMethodConstant; |
data.c1 = 9.0f; |
data.c2 = 9.0f; |
if ((style = Q3SubdivisionStyle_New(&data)) == NULL) { |
ERROR_DEBUG_STR("Scene_NewStyles: Q3SubdivisionStyle_New failed."); |
return kQ3Failure; |
} |
Q3Group_AddObject(group, style); |
Object_Dispose_NULL(&style); |
} |
return kQ3Success; |
} |
#pragma mark - |
/* |
* Scene_NewScene |
*/ |
static |
TQ3Status Scene_NewScene( |
TQ3GroupObject group) |
{ |
TQ3Status status = kQ3Success; |
TQ3BoundingBox sceneBBox; |
TQ3GeometryObject geometry = NULL; |
TQ3CylinderData cylinderData; |
TQ3AttributeSet attr = NULL; |
unsigned long rx, cy; |
#define kNumRows 5 /* X */ |
#define kNumColumns 7 /* Y */ |
#define kCylHeight 4.0f |
#define kCylDiameter (2.0f * kCylRadius) |
#define kCylGap (0.9f * kCylRadius) |
#define kSceneWidth ((kNumColumns * kCylDiameter) + ((kNumColumns-1) * kCylGap)) |
#define kSceneHeight ((kNumRows * kCylDiameter) + ((kNumRows-1) * kCylGap)) |
#define kSceneDepth kCylHeight |
#define kSceneHalfWidth (kSceneWidth / 2.0f) |
#define kSceneHalfHeight (kSceneHeight / 2.0f) |
#define kSceneHalfDepth (kSceneDepth / 2.0f) |
if (group == NULL) { |
ERROR_DEBUG_STR("Scene_NewScene: group == NULL."); |
return kQ3Failure; |
} |
/* Create a 4 sided box */ |
Q3Point3D_Set(&sceneBBox.min, -kSceneHalfWidth, -kSceneHalfHeight, -kSceneHalfDepth); |
Q3Point3D_Set(&sceneBBox.max, kSceneHalfWidth, kSceneHalfHeight, kSceneHalfDepth); |
sceneBBox.isEmpty = kQ3False; |
if (Scene_NewBoxSides(group, &sceneBBox) == kQ3Failure) { |
return kQ3Failure; |
} |
/* Create cylinders inside box */ |
for (rx = 0; rx < kNumColumns; rx++) { |
for (cy = 0; cy < kNumRows; cy++) { |
/* Skip middle geometry */ |
if ((rx == (kNumColumns / 2)) && (cy == (kNumRows / 2))) { |
continue; |
} |
/* Every other geometry */ |
if ((rx + cy) & 1) { |
continue; |
} |
/* Cylinder is parallel to z-axis */ |
Q3Point3D_Set(&cylinderData.origin, |
(((float) rx) * (kCylDiameter + kCylGap)) + kCylRadius - kSceneHalfWidth, |
(((float) cy) * (kCylDiameter + kCylGap)) + kCylRadius - kSceneHalfHeight, |
-kSceneHalfDepth); |
Q3Vector3D_Set(&cylinderData.orientation, 0.0, 0.0, kCylHeight); |
Q3Vector3D_Set(&cylinderData.majorRadius, kCylRadius, 0.0, 0.0); |
Q3Vector3D_Set(&cylinderData.minorRadius, 0.0, kCylRadius, 0.0); |
cylinderData.uMin = cylinderData.vMin = 0.0f; |
cylinderData.uMax = cylinderData.vMax = 1.0f; |
cylinderData.caps = kQ3EndCapMaskTop | kQ3EndCapMaskBottom; |
cylinderData.interiorAttributeSet = NULL; |
cylinderData.topAttributeSet = NULL; |
cylinderData.faceAttributeSet = NULL; |
cylinderData.bottomAttributeSet = NULL; |
cylinderData.cylinderAttributeSet = NULL; |
attr = Q3AttributeSet_New(); |
if (attr != NULL) { |
TQ3ColorRGB rgbColor; |
Q3ColorRGB_Set(&rgbColor, |
0.0, |
(float) (kNumColumns - rx) / kNumColumns, |
(float) cy / kNumRows); |
Q3AttributeSet_Add(attr, kQ3AttributeTypeDiffuseColor, &rgbColor); |
} |
cylinderData.cylinderAttributeSet = attr; |
geometry = Q3Cylinder_New(&cylinderData); |
if (geometry == NULL) { |
status = kQ3Failure; |
break; |
} |
Object_Dispose_NULL(&attr); |
Q3Group_AddObject(group, geometry); |
Object_Dispose_NULL(&geometry); |
} |
} |
Object_Dispose_NULL(&attr); |
Object_Dispose_NULL(&geometry); |
return status; |
} |
static |
TQ3Status Scene_NewBoxSides( |
TQ3GroupObject group, |
TQ3BoundingBox *pBBox) |
{ |
#define kMaxPolygons 4 |
#define kMaxPolyVerts 4 |
#define kOutsetScale 0.15f |
TQ3Status status = kQ3Success; |
TQ3GeometryObject geometry = NULL; |
TQ3PolygonData polyData; |
TQ3Vertex3D vertices[kMaxPolyVerts], |
*pVertex; |
TQ3Point3D min, max; |
unsigned long side; |
TQ3AttributeSet attr = NULL; |
float boxOutset; |
min = pBBox->min; |
max = pBBox->max; |
boxOutset = (max.x - min.x) * kOutsetScale; |
min.x -= boxOutset; max.x += boxOutset; |
boxOutset = (max.y - min.y) * kOutsetScale; |
min.y -= boxOutset; max.y += boxOutset; |
boxOutset = (max.z - min.z) * kOutsetScale; |
min.z -= boxOutset; max.z += boxOutset; |
polyData.numVertices = kMaxPolyVerts; |
polyData.vertices = vertices; |
pVertex = polyData.vertices; |
vertices[0].attributeSet = |
vertices[1].attributeSet = |
vertices[2].attributeSet = |
vertices[3].attributeSet = NULL; |
for (side = 0; side < kMaxPolygons; side++) { |
attr = Q3AttributeSet_New(); |
if (attr != NULL) { |
TQ3ColorRGB rgbColor; |
Q3ColorRGB_Set(&rgbColor, 0.0, 0.5, 0.5); |
Q3AttributeSet_Add(attr, kQ3AttributeTypeDiffuseColor, &rgbColor); |
} |
polyData.polygonAttributeSet = attr; |
switch (side) { |
case 0: |
Q3Point3D_Set(&pVertex[0].point, min.x, min.y, min.z); |
Q3Point3D_Set(&pVertex[1].point, min.x, min.y, max.z); |
Q3Point3D_Set(&pVertex[2].point, max.x, min.y, max.z); |
Q3Point3D_Set(&pVertex[3].point, max.x, min.y, min.z); |
break; |
case 1: |
Q3Point3D_Set(&pVertex[0].point, max.x, min.y, min.z); |
Q3Point3D_Set(&pVertex[1].point, max.x, min.y, max.z); |
Q3Point3D_Set(&pVertex[2].point, max.x, max.y, max.z); |
Q3Point3D_Set(&pVertex[3].point, max.x, max.y, min.z); |
break; |
case 2: |
Q3Point3D_Set(&pVertex[0].point, min.x, max.y, min.z); |
Q3Point3D_Set(&pVertex[1].point, max.x, max.y, min.z); |
Q3Point3D_Set(&pVertex[2].point, max.x, max.y, max.z); |
Q3Point3D_Set(&pVertex[3].point, min.x, max.y, max.z); |
break; |
case 3: |
Q3Point3D_Set(&pVertex[0].point, min.x, min.y, min.z); |
Q3Point3D_Set(&pVertex[1].point, min.x, max.y, min.z); |
Q3Point3D_Set(&pVertex[2].point, min.x, max.y, max.z); |
Q3Point3D_Set(&pVertex[3].point, min.x, min.y, max.z); |
break; |
} |
geometry = Q3Polygon_New(&polyData); |
Object_Dispose_NULL(&polyData.polygonAttributeSet); |
if (geometry == NULL) { |
return kQ3Failure; |
} |
Q3Group_AddObject(group, geometry); |
Object_Dispose_NULL(&geometry); |
} |
return kQ3Success; |
} |
#pragma mark - |
/* |
* Scene_IsRotating |
*/ |
TQ3Boolean Scene_IsRotating( |
void) |
{ |
return gDoRotation; |
} |
/* |
* Scene_SetIsRotating |
*/ |
TQ3Boolean Scene_SetIsRotating( |
TQ3Boolean newIsRotating) |
{ |
TQ3Boolean oldIsRotating = gDoRotation; |
gDoRotation = newIsRotating; |
return oldIsRotating; |
} |
/* |
* Scene_Rotate |
* |
* Global: |
* gCamera |
*/ |
TQ3Status Scene_Rotate( |
void) |
{ |
TQ3Status status = kQ3Failure; |
TQ3CameraPlacement placement; |
float cameraDist; |
if (gDoRotation) { |
gRotationAngle += kAngleDelta; |
status = Q3Camera_GetPlacement(gCamera, &placement); |
DEBUG_ASSERT(status == kQ3Success, Q3Camera_GetPlacement); |
cameraDist = Q3Point3D_Distance(&placement.cameraLocation, &placement.pointOfInterest); |
/* Assumes pointOfInterest is at origin any pointOfInterest.y is 0 */ |
placement.cameraLocation.x = cos(gRotationAngle) * cameraDist; |
placement.cameraLocation.z = sin(gRotationAngle) * cameraDist; |
status = Q3Camera_SetPlacement(gCamera, &placement); |
DEBUG_ASSERT(status == kQ3Success, Q3Camera_SetPlacement); |
} |
return status; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14