Path::Path(WorldData* data) { worldData = data; currentPoint = 1; previousPoint = 0; vector<PathPointData>::iterator newPoint; for (newPoint = data->path.begin(); newPoint != data->path.end(); ++newPoint) { PathPoint point = PathPoint(*newPoint); points.push_back(point); } currentPoint = 1; previousPoint = 0; }
bool BezierController::OnNextStep() { if (!mLastPathPoint) { return false; } while (mCurrentPoint != mEndPoint && mTotalTime > (*mCurrentPoint).mTime) { mLastPathPoint = &(*mCurrentPoint); ++mCurrentPoint; } //if we reached the end of the path0 //it means our time step was greater than //our path point resolution so we'll just stop //at the last valid path point if (mCurrentPoint == mEndPoint) { StepObject(mLastPathPoint->mPoint); mPath.clear(); if (mShouldLoop) { // This needs to be false in order to restart mIsRunning = false; // Restart from the beginning Start(); return true; } return false; } const PathPoint& p = (*mCurrentPoint).mPoint; const float currentTime = (*mCurrentPoint).mTime; //else if our elapsed time is equal to the next points time //ie. the step for the controller = the step for the last BezierNode //then we just move to the next point if (std::abs(currentTime - mTotalTime) < 0.0001f) { StepObject(p); } //else if our elapsed time is equal to the previous points time //just move the the previous point else if (std::abs(mLastPathPoint->mTime - mTotalTime) < 0.0001f) { StepObject(mLastPathPoint->mPoint); } //if our elapsed time is between the last and next pathpoint //we will interpolate between the two points else if (mLastPathPoint->mTime <= mTotalTime && currentTime >= mTotalTime) { float perc = (mTotalTime - mLastPathPoint->mTime) / (currentTime - mLastPathPoint->mTime ); osg::Vec3 from = p.GetPosition(); osg::Vec3 to = mLastPathPoint->mPoint.GetPosition(); osg::Vec3 vec; vec[0] = ((1.0 - perc) * from[0]) + (perc * to[0]); vec[1] = ((1.0 - perc) * from[1]) + (perc * to[1]); vec[2] = ((1.0 - perc) * from[2]) + (perc * to[2]); osg::Quat quat; quat.slerp(perc, mLastPathPoint->mPoint.GetOrientation(), p.GetOrientation()); StepObject(PathPoint(vec, quat)); } else { //else something went wrong assert(0); } return true; }
PathPoint Path::getStart() const { return PathPoint(_checkPoints, _distanceView); }
TrackGenerator::TrackGenerator() { /* * Phase 1 - * Generate the track tree and place control points that wrap * closely around the tree segments. */ // Start by generating the tree TrackTreeNode* rootTrackTreeNode = new TrackTreeNode(0.0, 0.0, NULL, 0); // Place the control points TrackTreeNode* current_start = rootTrackTreeNode; TrackTreeNode* previous_start = rootTrackTreeNode->neighbors[0]; TrackTreeNode* current = current_start; TrackTreeNode* previous = previous_start; vector<vec3> controlPts; do { TrackTreeNode* next = current->getNextNode(previous); if (previous == next) { // happens if current node only has one segment vec2 direction = current->xz - previous->xz; direction.normalize(); direction *= current->radius; vec2 offset(-direction[1], direction[0]); vec2 temp = current->xz + direction + offset; vec3 v(temp[0], RAND * 20 + 10, temp[1]); controlPts.push_back(v); temp = current->xz + direction - offset; v = vec3(temp[0], RAND * 20 + 10, temp[1]); controlPts.push_back(v); } else { // Otherwise, do the bisector thing vec2 direction1 = current->xz - previous->xz; direction1.normalize(); direction1 = vec2(-direction1[1], direction1[0]); vec2 direction2 = next->xz - current->xz; direction2.normalize(); direction2 = vec2(-direction2[1], direction2[0]); vec2 newDir = direction1 + direction2; double angle = acos(direction1 * direction2); newDir.normalize(); newDir *= current->radius / cos(angle / 2); newDir += current->xz; // First, randomly assign heights vec3 v(newDir[0], RAND * 20 + 10, newDir[1]); controlPts.push_back(v); } // Advance previous = current; current = next; } while (current != current_start || previous != previous_start); bool majorChange = true; while (majorChange) { majorChange = false; /* * Phase 2 - * Merge nearby control points and prune control points with angles * that are too acute. Repeat until the track stops changing. */ bool changed = true; while (changed) { changed = false; vector<vec3> tempControlPts; // Merge close control points first double threshold = 20.0; int size = controlPts.size(); for (int i = 0; i < size; i++) { vec3 current = controlPts[i]; vec3 previous = controlPts[(i + size - 1) % size]; vec3 next = controlPts[(i + 1) % size]; vec3 distvec1 = current - previous; vec3 distvec2 = next - current; distvec1[1] = 0.0; distvec2[1] = 0.0; double dist1 = distvec1.length(); double dist2 = distvec2.length(); if (dist1 > threshold && dist2 > threshold) { tempControlPts.push_back(current); } else if (dist2 <= threshold) { changed = true; vec3 newPoint = (current + next) / 2; tempControlPts.push_back(newPoint); } majorChange |= changed; } controlPts = tempControlPts; // Next, prune sharply angled control points tempControlPts.clear(); size = controlPts.size(); for (int i = 0; i < size; i++) { vec3 current = controlPts[i]; vec3 previous = controlPts[(i + size - 1) % size]; vec3 next = controlPts[(i + 1) % size]; vec3 dir1 = current - previous; vec3 dir2 = current - next; dir1.normalize(); dir2.normalize(); if (acos(dir1 * dir2) < M_PI / 4) { changed = true; } else { tempControlPts.push_back(current); } majorChange |= changed; } controlPts = tempControlPts; } /* * Phase 3 - * Add height information and then adjust to make sure that * the track will not overlap too closely. */ // TODO - compare segment distances to make sure that they aren't overalapping too closely changed = true; while (changed) { changed = false; for (unsigned int i = 0; i < controlPts.size() - 1; i++) { for (unsigned int j = i + 1; j < controlPts.size(); j++) { unsigned int jn = (j+1)%controlPts.size(); pair<vec3, vec3> seg1(controlPts[i], controlPts[i+1]); pair<vec3, vec3> seg2(controlPts[j], controlPts[jn]); vec3 check = checkSegments(seg1, seg2); if (check.length() > 0.01) { changed = true; controlPts[i] += check / 2; controlPts[i+1] += check / 2; controlPts[j] -= check / 2; controlPts[jn] -= check / 2; controlPts[i][1] = max(controlPts[i][1], 4.0); controlPts[i+1][1] = max(controlPts[i+1][1], 4.0); controlPts[j][1] = max(controlPts[j][1], 4.0); controlPts[jn][1] = max(controlPts[jn][1], 4.0); } } } majorChange |= changed; } } /* * Recenter the track */ vector<vec3>::iterator it = controlPts.begin(); double xMin = (*it)[0]; double xMax = (*it)[0]; double zMin = (*it)[2]; double zMax = (*it)[2]; it++; while (it != controlPts.end()) { xMin = min(xMin, (*it)[0]); xMax = max(xMax, (*it)[0]); zMin = min(zMin, (*it)[2]); zMax = max(zMax, (*it)[2]); it++; } double xMid = (xMax + xMin) / 2; double zMid = (zMax + zMin) / 2; for (unsigned int i = 0; i < controlPts.size(); i++) { controlPts[i][0] -= xMid; controlPts[i][2] -= zMid; } xWidth = xMax - xMin; zWidth = zMax - zMin; /* * Add banking to make better turns, and add to pathPts */ for (unsigned int i = 0; i < controlPts.size(); i++) { vec2 current = vec2(controlPts[i], VY); vec2 prev = vec2(controlPts[(i - 1 + controlPts.size()) % controlPts.size()], VY); vec2 next = vec2(controlPts[(i + 1) % controlPts.size()], VY); double angle = angleBetween(current, prev, next); vec2 dir = current - prev; vec2 turn = next - current; vec3 dir2(dir[0], 0, dir[1]); vec3 turn2(turn[0], 0, turn[1]); double bankAmount = 45 * pow(0.999, angle * dir.length() * turn.length() / 100); if ((dir2 ^ turn2)[1] > 0) { bankAmount *= -1; } pathPts.push_back(PathPoint(controlPts[i], bankAmount)); } }