This is DN3DString.m in view mode; [Download] [Up]
/*************************************************** ** ** DN3DString.m ** ** By Brian Hobbs (brian@ny.shl.com) ** ** This class actually creates the 3D text string. ** It's performed through the PSW_tracestring call. ** The rest of the algorithm is massaging the bezier ** control points into the proper format to be ** recognized by RiPatchMesh, which physically ** creates the text in Renderman. ** ** This code is public domain. Feel free to use ** it in any way you wish. I'd like to see any ** changes you make, tho. ** ****************************************************/ #import "DN3DString.h" #import "CharPaths.h" #import <ri/ri.h> #define SCALE 100 #define MOVETO -1000 #define LINETO -2000 #define CURVETO -3000 #define CLOSE -4000 #define EMPTY -5000 @implementation DN3DString:N3DShape - init { int i; [super init]; resolution[0] = 35; resolution[1] = 1; textFont = (char *)NXZoneMalloc([self zone], sizeof(char) * 256); for (i=0; i<256; i++) textFont[i] = 0; return self; } - setResolution:(float)newRes { resolution[0] = newRes; return self; } - setTextString:(const char *)aString andFont:(const char *)aFont { float pathStack[1000]; int *numCurves, *counter; int i,j,k; for (i=0; i<1000; i++) pathStack[i] = EMPTY; PSW_tracestring(aString, aFont, &(pathStack[0])); if (mesh){ for (i=0; i<numPaths; i++) NXZoneFree([self zone], mesh[i]); NXZoneFree([self zone], mesh); } if (numCtrlPoints) NXZoneFree([self zone], numCtrlPoints); numPaths = i = 0; while (pathStack[i] != EMPTY){ // find the number of paths if (pathStack[i] == CLOSE) numPaths++; i++; } mesh = (RtFloat **)NXZoneMalloc([self zone], sizeof(RtFloat *) * numPaths); numCtrlPoints = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths); numCurves = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths); counter = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths); for (i=0; i<numPaths; i++){ numCtrlPoints[i] = 0; numCurves[i] = 0; counter[i] = 0; } i = j = 0; // find the number of bezier control points of each path... while (pathStack[i] != EMPTY){ if (pathStack[i] == LINETO || pathStack[i] == CURVETO) numCurves[j]++; if (pathStack[i] == CLOSE){ numCtrlPoints[j] = (4 + ((numCurves[j] - 1) * 3)); mesh[j] = (RtFloat *)NXZoneMalloc([self zone], sizeof(RtFloat) * (numCtrlPoints[j] * 2 * 3)); j++; } i++; } j = 0; for (k = 0; k < 2; k++){ i = j = 0; while ((pathStack[i] != EMPTY) && (j != numPaths)){ // then properly fill the mesh switch((int)pathStack[i]){ case LINETO : mesh[j][counter[j]] = (float)pathStack[i-2] / SCALE; mesh[j][++counter[j]] = (float)pathStack[i-1] / SCALE; mesh[j][++counter[j]] = (float)k / 4.0; mesh[j][++counter[j]] = (float)pathStack[i-2] / SCALE; mesh[j][++counter[j]] = (float)pathStack[i-1] / SCALE; mesh[j][++counter[j]] = (float)k / 4.0; counter[j]++; break; case CLOSE : j++; break; case MOVETO: case CURVETO: break; default : mesh[j][counter[j]] = (float)pathStack[i] / SCALE; counter[j]++; if (((counter[j] + 1) % 3) == 0){ mesh[j][counter[j]] = (float)k / 4.0; counter[j]++; } break; } i++; } } NXZoneFree([self zone], numCurves); NXZoneFree([self zone], counter); return self; } - setTextFont:(const char *)aFont { strcpy(textFont, (char *)aFont); return self; } - setTextString:(const char *)aString { // assumes setTextFont previously invoked [self setTextString:aString andFont:(const char *)textFont]; return self; } - renderSelf:(RtToken)context { int i; RiBasis(RiBezierBasis, RI_BEZIERSTEP, RiBezierBasis, RI_BEZIERSTEP); RiGeometricApproximation( RI_TESSELATION, RI_PARAMETRIC, resolution, RI_NULL ); // RiRotate(-90,1,0,0); RiTranslate(0,0,-0.5); RiScale(0.15, 0.15, 0.15); for (i=0; i<numPaths; i++) RiPatchMesh(RI_BILINEAR, (RtInt)numCtrlPoints[i], RI_PERIODIC, (RtInt)2, RI_NONPERIODIC, RI_P, (RtPointer)mesh[i], RI_NULL); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.