// // MyTube.m // B-spline // // Created by Alexander Powell on Wed Aug 28 2002. // Copyright (c) 2002 __MyCompanyName__. All rights reserved. // #import "MyTube.h" @implementation MyTube - (id)init { if (self = [super init]) { points = [[NSMutableArray alloc] init]; normals = [[NSMutableArray alloc] init]; pointsPerPoly = 0; return self; } return nil; } - (void)dealloc { [points release]; [normals release]; [super dealloc]; } - (MyPoint*)calcNormalv1:(MyPoint *)i1 v2: (MyPoint *)i2 v3:(MyPoint*)i3 { MyPoint *v1, *v2; MyPoint *outV; // calculate vector from point 2 to point 1 v1 = [[MyPoint alloc] initWithX: ([i1 x] - [i2 x]) y: ([i1 y] - [i2 y]) z: ([i1 z] - [i2 z])]; // calculate vector from point 3 to point 2 v2 = [[MyPoint alloc] initWithX: ([i2 x] - [i3 x]) y: ([i2 y] - [i3 y]) z: ([i2 z] - [i3 z])]; // Compute the vector cross product to get a surface normal outV = [[MyPoint alloc] initWithX: ([v1 y] * [v2 z] - [v1 z] * [v2 y]) y: ([v1 z] * [v2 x] - [v1 x] * [v2 z]) z: ([v1 x] * [v2 y] - [v1 y] * [v2 x])]; [outV normalize]; // normalize the vector return ([outV autorelease]); } - (void)calculateNormals { int ii, jj; int numPoints = [points count]; int numPolys = numPoints / pointsPerPoly; // Reset the normals array [normals release]; normals = [[NSMutableArray alloc] init]; for (jj = 0; jj < numPolys; jj++) { for (ii = 0; ii < pointsPerPoly; ii++) { MyPoint *firstPoint, *secondPoint, *thirdPoint; MyPoint *n1, *n2, *n3, *n4, *newNorm; // normals at the point int iiplusone = (ii + 1) % pointsPerPoly; int jjplusone = (jj + 1) % numPolys; int iiminusone = ((ii - 1) + pointsPerPoly) % pointsPerPoly; int jjminusone = ((jj - 1) + numPolys) % numPolys; // this point's quad firstPoint = [points objectAtIndex: (jj * pointsPerPoly) + ii]; secondPoint = [points objectAtIndex: ((jj * pointsPerPoly) + iiplusone)]; thirdPoint = [points objectAtIndex: ((jjplusone * pointsPerPoly) + iiplusone)]; n1 = [self calcNormalv1: firstPoint v2: secondPoint v3: thirdPoint]; // top quad firstPoint = [points objectAtIndex: (jj * pointsPerPoly) + iiminusone]; secondPoint = [points objectAtIndex: ((jj * pointsPerPoly) + ii)]; thirdPoint = [points objectAtIndex: ((jjplusone * pointsPerPoly) + ii)]; n2 = [self calcNormalv1: firstPoint v2: secondPoint v3: thirdPoint]; // left quad firstPoint = [points objectAtIndex: (jjminusone * pointsPerPoly) + ii]; secondPoint = [points objectAtIndex: ((jjminusone * pointsPerPoly) + iiplusone)]; thirdPoint = [points objectAtIndex: ((jj * pointsPerPoly) + iiplusone)]; n3 = [self calcNormalv1: firstPoint v2: secondPoint v3: thirdPoint]; // top left quad firstPoint = [points objectAtIndex: (jjminusone * pointsPerPoly) + iiminusone]; secondPoint = [points objectAtIndex: ((jjminusone * pointsPerPoly) + ii)]; thirdPoint = [points objectAtIndex: ((jj * pointsPerPoly) + ii)]; n4 = [self calcNormalv1: firstPoint v2: secondPoint v3: thirdPoint]; // Average the normals around this vertex, and record that. newNorm = [[MyPoint alloc] initWithX: (([n1 x] + [n2 x] + [n3 x] + [n4 x]) / 4.0f) y: (([n1 y] + [n2 y] + [n3 y] + [n4 y]) / 4.0f) z: (([n1 z] + [n2 z] + [n3 z] + [n4 z]) / 4.0f)]; [normals addObject: newNorm]; [newNorm release]; } } } - (void)loadFromFile:(NSString *)filename { int ii, curPoly; NSString *file, *currentPolyString; NSArray *pointsAsStrings, *polysAsStrings; // Reset the points array [points release]; points = [[NSMutableArray alloc] init]; file = [NSString stringWithContentsOfFile: filename]; polysAsStrings = [file componentsSeparatedByString: @"\n"]; for (curPoly = 0; curPoly < [polysAsStrings count]; curPoly++) { currentPolyString = [polysAsStrings objectAtIndex: curPoly]; pointsAsStrings = [currentPolyString componentsSeparatedByString: @" : "]; pointsPerPoly = [pointsAsStrings count]; for (ii = 0; ii < pointsPerPoly; ii++) { MyPoint *newPoint; float currentZ = 0.0f; NSString *currentPointString = [pointsAsStrings objectAtIndex: ii]; NSArray *currentPointAsStrings = [currentPointString componentsSeparatedByString: @","]; // Read the first 2 components as x and y, respectively, and the third (if it exists) // as z.... if ([currentPointAsStrings count] > 2) currentZ = [[currentPointAsStrings objectAtIndex: 2] floatValue]; newPoint = [[MyPoint alloc] initWithX: [[currentPointAsStrings objectAtIndex: 0] floatValue] y: [[currentPointAsStrings objectAtIndex: 1] floatValue] z: currentZ]; [points addObject: newPoint]; [newPoint release]; // release now, since the point was retained when added to the array } } // Update the normals array [self calculateNormals]; } - (MyTube*)subdivideWith:(int)subdivisionType { SubFunc subdivisionFunc; int ii, jj; int newPointsPerPoly; int numPolygons; NSMutableArray *smoothedOldPolys, *allPolys; MyTube *newTube = [[MyTube alloc] init]; smoothedOldPolys = [[NSMutableArray alloc] init]; allPolys = [[NSMutableArray alloc] init]; switch (subdivisionType) { case SPLIT_AND_TWEAK: subdivisionFunc = splitAndTweak; break; case FOUR_POINT: subdivisionFunc = fourPoint; break; } numPolygons = [points count] / pointsPerPoly; // Smooth each of the polygons for (ii = 0; ii < numPolygons; ii++) // ii is current polygon number we're looking at { NSRange myRange; NSMutableArray *pointsInQuestion; NSMutableArray *newPolyPoints; myRange.location = ii * pointsPerPoly; // The beginning of the current poly myRange.length = pointsPerPoly; // The number of points in the poly pointsInQuestion = [[NSMutableArray alloc] init]; [pointsInQuestion setArray: [points subarrayWithRange: myRange]]; newPolyPoints = subdivisionFunc(pointsInQuestion); newPointsPerPoly = [newPolyPoints count]; [smoothedOldPolys addObjectsFromArray: newPolyPoints]; [pointsInQuestion release]; // cleanup [newPolyPoints release]; } // Interpolate new polys // We choose the first, second (and so on) points from each poly, and smooth those for (ii = 0; ii < newPointsPerPoly; ii++) // ii is current point index { NSMutableArray *pointsInQuestion; NSMutableArray *newPolyPoints; pointsInQuestion = [[NSMutableArray alloc] init]; // Loop to extract the points we need for (jj = 0; jj < numPolygons; jj++) [pointsInQuestion addObject: [smoothedOldPolys objectAtIndex: ((jj * newPointsPerPoly) + ii)]]; newPolyPoints = subdivisionFunc(pointsInQuestion); // if we haven't initialized our all polys array, do so if ([allPolys count] == 0) { int kk; for (kk = 0; kk < [newPolyPoints count] * newPointsPerPoly; kk++) [allPolys addObject: [NSNull null]]; } // Loop to add the points to our inbetween polygons array for (jj = 0; jj < [newPolyPoints count]; jj++) { [allPolys replaceObjectAtIndex: ((jj * newPointsPerPoly) + ii) withObject: [newPolyPoints objectAtIndex: jj]]; } [pointsInQuestion release]; // cleanup [newPolyPoints release]; } [newTube setPoints: allPolys]; [newTube setPointsPerPoly: newPointsPerPoly]; [newTube calculateNormals]; // Clean up [smoothedOldPolys release]; [allPolys release]; return newTube; // CAUTION: must be released by caller... this is kinda sloppy } - (void)setPoints:(NSMutableArray *)newPoints { [newPoints retain]; [points release]; points = newPoints; } - (void)setPointsPerPoly:(int)newPointsPerPoly { pointsPerPoly = newPointsPerPoly; } - (void)drawNormals { int ii, jj; int numPolys = [points count] / pointsPerPoly; float normSize = 0.1f; glDisable(GL_LIGHTING); glDisable(GL_FOG); glColor3f(1.0f, 0.0f, 0.0f); glBegin(GL_LINES); for (ii = 0; ii < pointsPerPoly; ii++) { for (jj = 0; jj < numPolys; jj++) { MyPoint *pointInQuestion = [points objectAtIndex: (jj * pointsPerPoly) + ii]; MyPoint *norm = [normals objectAtIndex: (jj * pointsPerPoly) + ii]; glVertex3f([pointInQuestion x], [pointInQuestion y], [pointInQuestion z]); glVertex3f([pointInQuestion x] + ([norm x] * normSize), [pointInQuestion y] + ([norm y] * normSize), [pointInQuestion x] + ([norm z] * normSize)); } } glEnd(); } - (void)drawSolid { int ii, jj; int numPolys = [points count] / pointsPerPoly; glEnable(GL_DEPTH_TEST); for (ii = 0; ii < pointsPerPoly; ii++) { for (jj = 0; jj < numPolys; jj++) { MyPoint *firstPoint, *secondPoint, *thirdPoint, *fourthPoint; MyPoint *firstNorm, *secondNorm, *thirdNorm, *fourthNorm; // normal int jjplusone = (jj + 1) % numPolys; int jjplustwo = (jj + 2) % numPolys; int iiplusone = (ii + 1) % pointsPerPoly; int iiplustwo = (ii + 2) % pointsPerPoly; firstPoint = [points objectAtIndex: (jj * pointsPerPoly) + ii]; secondPoint = [points objectAtIndex: ((jj * pointsPerPoly) + iiplusone)]; thirdPoint = [points objectAtIndex: ((jjplusone * pointsPerPoly) + iiplusone)]; fourthPoint = [points objectAtIndex: ((jjplusone * pointsPerPoly) + ii)]; /* // Dynamic calculation of normals firstNorm = [self calcNormalv1: firstPoint v2: secondPoint v3: thirdPoint]; secondNorm = [self calcNormalv1: secondPoint v2: [points objectAtIndex: ((jj * pointsPerPoly) + iiplustwo)] v3: [points objectAtIndex: ((jjplusone * pointsPerPoly) + iiplustwo)]]; thirdNorm = [self calcNormalv1: thirdPoint v2: [points objectAtIndex: ((jjplusone * pointsPerPoly) + iiplustwo)] v3: [points objectAtIndex: ((jjplustwo * pointsPerPoly) + iiplustwo)]]; fourthNorm = [self calcNormalv1: fourthPoint v2: [points objectAtIndex: ((jjplusone * pointsPerPoly) + iiplusone)] v3: [points objectAtIndex: ((jjplustwo * pointsPerPoly) + iiplusone)]]; */ firstNorm = [normals objectAtIndex: (jj * pointsPerPoly) + ii]; secondNorm = [normals objectAtIndex: ((jj * pointsPerPoly) + iiplusone)]; thirdNorm = [normals objectAtIndex: ((jjplusone * pointsPerPoly) + iiplusone)]; fourthNorm = [normals objectAtIndex: ((jjplusone * pointsPerPoly) + ii)]; glBegin(GL_QUADS); glNormal3f([firstNorm x], [firstNorm y], [firstNorm z]); glVertex3f([firstPoint x], [firstPoint y], [firstPoint z]); glNormal3f([secondNorm x], [secondNorm y], [secondNorm z]); glVertex3f([secondPoint x], [secondPoint y], [secondPoint z]); glNormal3f([thirdNorm x], [thirdNorm y], [thirdNorm z]); glVertex3f([thirdPoint x], [thirdPoint y], [thirdPoint z]); glNormal3f([fourthNorm x], [fourthNorm y], [fourthNorm z]); glVertex3f([fourthPoint x], [fourthPoint y], [fourthPoint z]); glEnd(); } } } - (void)drawSelf { int ii; int numPolys = [points count] / pointsPerPoly; float currentColor[4]; // Draw contained polys first for (ii = 0; ii < numPolys; ii++) { NSRange myRange; NSMutableArray *pointsInQuestion; MyPolygon *currentPoly; myRange.location = ii * pointsPerPoly; // The beginning of the current poly myRange.length = pointsPerPoly; // The number of points in the poly currentPoly = [[MyPolygon alloc] init]; pointsInQuestion = [[NSMutableArray alloc] init]; [pointsInQuestion setArray: [points subarrayWithRange: myRange]]; [currentPoly setPoints: pointsInQuestion]; [currentPoly drawSelf]; // The actual drawing // cleanup [pointsInQuestion release]; [currentPoly release]; } // We're gonna draw the rest of the lines at one-third opacity, to help demonstrate // the structure of the tube. glGetFloatv(GL_CURRENT_COLOR, currentColor); glColor4f(currentColor[0], currentColor[1], currentColor[2], currentColor[3] / 3.0f); // Now connect the polys with less-opaque lines for (ii = 0; ii < pointsPerPoly; ii++) { NSMutableArray *pointsInQuestion; MyPolygon *currentPoly; int jj; currentPoly = [[MyPolygon alloc] init]; pointsInQuestion = [[NSMutableArray alloc] init]; for (jj = 0; jj < numPolys; jj++) [pointsInQuestion addObject: [points objectAtIndex: ((jj * pointsPerPoly) + ii)]]; [currentPoly setPoints: pointsInQuestion]; [currentPoly drawSelf]; // The actual drawing // cleanup [pointsInQuestion release]; [currentPoly release]; } } @end