I'm working on a game that includes a base path (vec array) that is turned into an interpolated curved path using a hermit function. I created this function based on multiple examples in different code languages than I needed. They were also for 2D paths only, and I need it for 3D. I'm using the Imgtec PVR SDK on the iPhone.
The problem is that it isn't working entirely as it should. For some reason it smooths only about half the path (and not exactly the first half of the node list but half on the X-axis I think) and makes a mess of the rest. I've spent a couple of days now trying to get this to work perfectly, checking my path, the order of the nodes in the basepath etc. My math skills are not so good, I get what the function is supposed to do, but I'm kinda lost on how to troubleshoot/fix this function.
Following is the code (posting it entirely for clarity but also because I figure it could be useful to others when it actually works):
//// ThePath.mm//#import "ThePath.h"@implementation ThePath@synthesize basePath;@synthesize curvedPath;@synthesize STEP, startTangent, endTangent;- (id) init { [super init]; basePath = [[NSMutableArray alloc] init]; return self;}- (void) setStartEndTangent { //first two nodes PathNode* startN0 = [self.basePath objectAtIndex:0]; PathNode* startN1 = [self.basePath objectAtIndex:1]; //last two nodes PathNode* startN2 = [self.basePath objectAtIndex:basePath.count-1]; PathNode* startN3 = [self.basePath objectAtIndex:basePath.count-2]; PVRTVec4 startTangentTemp ((startN1.pos.x - startN0.pos.x) *2,0,0,0); startTangent = startTangentTemp; PVRTVec4 endTangentTemp((startN2.pos.x - startN3.pos.x)*2), 0,0,0); endTangent = endTangentTemp;}- (int) countVerts:(int)num{ int numOutVerts = 1; for(int i=0; i < num-1; i++) { numOutVerts += (STEP+1); } return numOutVerts;}- (void) makeCurve{ int numInVerts = basePath.count; int numOutVerts = [self countVerts:numInVerts]; //NSLog(@"numInVerts: %d numOutVerts: %d ", numInVerts, numOutVerts); //alloc all outnodes in curvedpath curvedPath = [[NSMutableArray alloc] init]; for (int it=0; it<numOutVerts; it++) { PathNode* newNode = [[PathNode alloc] init]; [curvedPath addObject:newNode]; } PathNode* inNode = [self.basePath objectAtIndex:0]; PathNode* outNode = [self.curvedPath objectAtIndex:0]; PVRTVec4 outPos(inNode.pos.x, inNode.pos.y, inNode.pos.z,0); outNode.pos = outPos; int startPosn = 1; // tangents for the current curve segment between two points PVRTVec4 t0(0.0f); PVRTVec4 t1(0.0f); for (int i=0; i < numInVerts-1; i++) { if (i == 0) { t0.x = startTangent.x; t0.y = startTangent.y; t0.z = startTangent.z; } else { // use previous t1 tangent t0.x = t1.x; t0.y = t1.y; t0.z = t1.z; if (i == numInVerts-2) { // next point is the last one t1.x = endTangent.x; t1.y = endTangent.y; t1.z = endTangent.z; } else { PathNode* prevNode = [basePath objectAtIndex:i-1]; PathNode* nextNode = [basePath objectAtIndex:i+1]; double xLen = nextNode.pos.x - prevNode.pos.x; double yLen = nextNode.pos.y - prevNode.pos.y; double zLen = nextNode.pos.z - prevNode.pos.z; t1.x = xLen/2; t1.y = yLen/2; t1.z = zLen/2; } } PathNode* thisInNode = [self.basePath objectAtIndex:i]; PathNode* nextInNode = [self.basePath objectAtIndex:i+1]; PathNode* thisStartPosNode = [self.curvedPath objectAtIndex:startPosn]; [self makeHermit:i :startPosn :t0 :t1]; startPosn += (STEP+1); }}- (void) makeHermit:(int)i:(int)startPosn:(PVRTVec4)t0:(PVRTVec4)t1 { double x0, y0, z0, x1, y1, z1; PathNode* thisNode = [self.basePath objectAtIndex:i]; PathNode* nextNode = [self.basePath objectAtIndex:i+1]; x0 = thisNode.pos.x; y0 = thisNode.pos.y; z0 = thisNode.pos.z; x1 = nextNode.pos.x; y1 = nextNode.pos.y; z1 = nextNode.pos.z; double xCoord, yCoord, zCoord; double tStep = 1.0/(STEP+1); double t; for(int i=0; i < STEP; i++) { t = tStep * (i+1); PathNode* thisOutNode = [self.curvedPath objectAtIndex:startPosn+i]; xCoord = ([self fh1:t] * x0) + ([self fh2:t] * x1) + ([self fh3:t] * t0.x) + ([self fh4:t] * t1.x); yCoord = ([self fh1:t] * y0) + ([self fh2:t] * y1) + ([self fh3:t] * t0.y) + ([self fh4:t] * t1.y); zCoord = ([self fh1:t] * z0) + ([self fh2:t] * z1) + ([self fh3:t] * t0.z) + ([self fh4:t] * t1.z); PVRTVec4 thisOutPos(xCoord, yCoord, zCoord, 0); thisOutNode.pos = thisOutPos; } PathNode* thislastOutNode = [self.curvedPath objectAtIndex:startPosn+STEP]; PVRTVec4 thisLastOutPos(x1, y1, z1, 0); thislastOutNode.pos = thisLastOutPos; }- (double) fh1:(double)t { return (2.0)*pow(t,3) - (3.0*t*t) + 1; }- (double) fh2:(double)t { return (-2.0)*pow(t,3) + (3.0*t*t); }- (double) fh3:(double)t { return pow(t,3) - (2.0*t*t) + t; }- (double) fh4:(double)t { return pow(t,3) - (t*t); }@end
//// ThePath.h//#import <Foundation/Foundation.h>#include "OGLES2Tools.h"#import "PathNode.h"@interface ThePath : NSObject { NSMutableArray* basePath; NSMutableArray* curvedPath; NSInteger STEP; PVRTVec4 startTangent; PVRTVec4 endTangent;}@property(nonatomic, retain) NSMutableArray* basePath;@property(nonatomic, retain) NSMutableArray* curvedPath;@property(nonatomic, assign) NSInteger STEP;@property(nonatomic, assign) PVRTVec4 startTangent;@property(nonatomic, assign) PVRTVec4 endTangent;- (int) countVerts:(int)num;- (void) makeHermit:(int)i:(int)startPosn:(PVRTVec4)t0:(PVRTVec4)t1;- (void) makeCurve;- (double) fh1:(double)t;- (double) fh2:(double)t;- (double) fh3:(double)t;- (double) fh4:(double)t;- (void) setStartEndTangent;@end
STEP is set first, then setStartEndTangent is called, and then makeCurve. My basepath has 84 nodes, with STEP 30 that results in 1314 nodes in the curvedpath. It seems the curved path is about 50% correct and the rest is sorta folded onto the other half. I think I'm abs()-ing or flipping a value too much.
Any help would be appreciated. :)
[Edited by - JHiemstra on October 23, 2010 11:35:30 AM]