3D Graphics Programming with QuickDraw 3D
This section also shows how to define a custom attribute type. To do so, you need to provide definitions of the data associated with that attribute type and an attribute metahandler to define a set of attribute-handling methods. See "Defining Custom Attribute Types," beginning on page 5-9 for complete details.
Q3AttributeSet_New
function. You configure the attribute set by adding the desired attributes to the set, using the Q3AttributeSet_Add
function. Finally, you attach the configured attribute set to an object by calling an appropriate QuickDraw3D routine. For example, to attach an attribute set to a vertex of a triangle, you call the function Q3Triangle_SetVertexAttributeSet
. Listing 5-1 illustrates how to set the three vertices of a triangle to a specific diffuse color.Listing 5-1 Creating and configuring a vertex attribute set
TQ3Status MySetTriangleVerticesDiffuseColor
(TQ3GeometryObject triangle, TQ3ColorRGB color)
{
TQ3AttributeSet myAttrSet; /*attribute set*/
TQ3Status myResult; /*result code*/
unsigned long myIndex; /*vertex index*/
/*Create a new empty attribute set.*/
myAttrSet = Q3AttributeSet_New
();
if (myAttrSet == NULL)
return (kQ3Failure);
/*Add the specified color attribute to the attribute set.*/
myResult = Q3AttributeSet_Add
(myAttrSet, kQ3AttributeTypeDiffuseColor, &color);
if (myResult == kQ3Failure)
return (kQ3Failure);
/*Attach the attribute set to each triangle vertex.*/
for (myIndex = 0; myIndex < 3; myIndex++) {
myResult = Q3Triangle_SetVertexAttributeSet
(triangle, myIndex, myAttrSet);
if (myResult == kQ3Failure)
return (kQ3Failure);
}
return (kQ3Success);
}
You can assign any number of different attribute types to a single attribute set. The function defined in Listing 5-1 assigns only one attribute--a diffuse color--to the new attribute set.
If you want to change the value of a certain attribute in an attribute set, you can simply overwrite the data associated with that attribute by calling Q3AttributeSet_Add
once again. You can remove an attribute from an attribute set by calling Q3AttributeSet_Clear
. To remove all attributes from an attribute set, you can call Q3AttributeSet_Empty
.
Q3AttributeSet_GetNextAttributeType
function that you can use to iterate through the attributes in an attribute kQ3AttributeTypeNone
to Q3AttributeSet_GetNextAttributeType
. Q3AttributeSet_GetNextAttributeType
, which returns kQ3AttributeTypeNone
when you reach the end of the list of attributes. Listing 5-2 illustrates how to use Q3AttributeSet_GetNextAttributeType
Listing 5-2 Counting the attributes in an attribute set
unsigned long MyCountAttributesInSet (TQ3AttributeSet
mySet)
{
unsigned long myCount; /*attribute count*/
TQ3AttributeType myType; /*attribute type*/
TQ3Status myResult; /*result code*/
for (myCount = 0,
myType = kQ3AttributeTypeNone,
myResult =
Q3AttributeSet_GetNextAttributeType(mySet, &myType);
myType != kQ3AttributeTypeNone;
myResult =
Q3AttributeSet_GetNextAttributeType(mySet, &myType)) {
myCount++;
}
return (myCount);
}
Notice that the Q3AttributeSet_GetNextAttributeType
function returns a result code that indicates whether the call succeeded or failed. In general, the call fails only if the attribute set is invalid in some way.Q3AttributeSet_Add
, and you retrieve the data associated with a custom attribute by calling Q3AttributeSet_Get
.
To define a custom attribute type, you first define the internal structure of the data associated with your custom attribute type. Then you must write an attribute metahandler to define a set of attribute-handling methods. QuickDraw3D calls those methods at certain times to handle operations on attribute sets that contain your custom attribute. For example, when you call Q3Triangle_Write
to write a triangle to a file, QuickDraw3D might need to call your attribute's handler to write your custom attribute data to the file.
Suppose that you want to define a custom attribute that contains data about temperature over time. You might use the MyTemperatureData
structure, defined like this:
typedef struct MyTemperatureData { unsigned long startTime; /*starting time*/ unsigned long nTemps; /*no. temps in array*/ float *temperatures; /*array of temps*/ } MyTemperatureData;Your attribute metahandler is an application-defined function that returns the addresses of the methods associated with the custom attribute type. A metahandler can define some or all of the methods indicated by these constants:
kQ3MethodTypeObjectReadData kQ3MethodTypeObjectTraverse kQ3MethodTypeObjectWrite kQ3MethodTypeElementCopyAdd kQ3MethodTypeElementDelete kQ3MethodTypeElementCopyDuplicate kQ3MethodTypeElementCopyGet kQ3MethodTypeElementCopyReplace kQ3MethodTypeAttributeCopyInherit kQ3MethodTypeAttributeInheritListing 5-3 defines a simple attribute metahandler. See "Defining an Object Metahandler," beginning on page 3-15 for a more complete description of metahandlers.
Listing 5-3 Reporting custom attribute methods
TQ3FunctionPointer MyTemperatureDataMetaHandler (TQ3MethodType methodType) { switch (methodType) { case kQ3MethodTypeElementDelete: return (TQ3FunctionPointer) MyTemperatureDataDispose; case kQ3MethodTypeElementCopyReplace: return (TQ3FunctionPointer) MyTemperatureDataCopyReplace; case kQ3MethodTypeAttributeCopyInherit: return (TQ3FunctionPointer) kQ3True; case kQ3MethodTypeAttributeInherit: return (TQ3FunctionPointer) kQ3True; default: return (NULL); } }As you can see, the
MyTemperatureDataMetaHandler
metahandler simply returns the appropriate function address, or NULL
if the metahandler does
The metahandler defined in Listing 5-3 installs the MyTemperatureDataDispose
function as the custom attribute's dispose method, which QuickDraw3D calls whenever you clear your custom attribute or replace an existing custom attribute. A dispose method is passed a pointer to the data associated with an attribute. Your dispose method should deallocate any storage you allocated yourself. Listing 5-4 shows a simple dispose method.
Listing 5-4 Disposing of a custom attribute's data
TQ3Status MyTemperatureDataDispose (MyTemperatureData *tmpData) { if (tData->temperatures != NULL) { free(tmpData->temperatures); tData->temperatures = NULL; } return kQ3Success; }If you do not define a dispose method, QuickDraw3D automatically disposes of the block of data allocated when a custom attribute was added to an attribute set. If the data associated with a custom attribute is always of a fixed size and does not contain any pointers to other data that needs to be disposed of, you do not need to define a dispose or copy method.
The metahandler defined in Listing 5-3 installs the MyTemperatureDataCopyReplace
function as the custom attribute's copy method. A copy method is passed two pointers, specifying the source and target addresses of the data to copy. Listing 5-5 shows a simple copy method.
Listing 5-5 Copying a custom attribute's data
TQ3Status MyTemperatureDataCopyReplace (const MyTemperatureData *src, MyTemperatureData *dst) { float *temp; if (dst->nTemps != src->nTemps) { temp = realloc(dst->temperatures, nTemps * sizeof(float)); if (temp == NULL) return (kQ3Failure); } dst->startTime = src->startTime; dst->nTemps = src->nTemps; dst->temperatures = temp; memcpy(temp, dst->temperatures, dst->nTemps * sizeof(float)); return (kQ3Success); }If you do not define a copy method, QuickDraw3D automatically copies the block of data using a default memory copy method.
The inherit method simply requests a Boolean value that indicates whether you want your custom attribute to be inherited down the class hierarchy. You should return kQ3True
if you want your attribute to be inherited or kQ3False
if not.
Before you can use a custom attribute type, you need to register your attribute metahandler with QuickDraw3D by calling the Q3AttributeClass_Register
function. You might execute the MyStartUpQuickDraw3D
function defined in Listing 5-6 at application startup time.
Listing 5-6 Initializing QuickDraw3D and registering a custom attribute type
TQ3AttributeType gAttributeType_Temperature; void MyStartUpQuickDraw3D (void) { TQ3ObjectClass myAttrib; if (Q3Initialize() == kQ3Failure) /*initialize QuickDraw3D*/ MyFailRoutine(); /*register attribute type*/ myAttrib =Q3
AttributeClass_Register
( gAttributeTypeTemperature, "MyCompany:SurfWorks:Temperature", sizeof(MyTemperatureData), MyTemperatureData_MetaHandler); if (myAttrib == kQ3ObjectTypeInvalid) MyFailRoutine(); }
Let us know what you think of these prototype pages.
Generated with Harlequin WebMaker