/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_TRACK_MANAGER #include "track_manager.h" #include "base_object.h" #include "load_param.h" #include "p_node.h" #include "track_node.h" #include "stdincl.h" #include "list.h" #include "text.h" #include "t_animation.h" #include "tinyxml.h" #include "substring.h" #include using namespace std; /** * initializes a TrackElement (sets the default values) */ TrackElement::TrackElement() { this->setClassID(CL_TRACK_ELEMENT, "TrackElement"); this->isFresh = true; this->isHotPoint = false; this->isSavePoint = false; this->isFork = false; this->isJoined = false; this->mainJoin = false; this->ID = -1; this->startingTime = 0; this->duration = TMAN_DEFAULT_DURATION; this->endTime = 1; this->jumpTime = 0; this->width = TMAN_DEFAULT_WIDTH; this->nodeCount = 0; this->curve = NULL; this->childCount = 0; this->children = NULL; this->history = NULL; this->subject = NULL; this->condFunc = &TrackElement::random; } /** * destroys all alocated memory) @todo eventually when deleting a TrackElement you would not like to delete all its preceding TrackElements */ TrackElement::~TrackElement() { // deleting the Curve delete this->curve; // deleting all the Children of this TrackNode. if ((!this->isJoined &&this->childCount > 0) || (this->isJoined && this->mainJoin)) // only if this is the MainJoin. { tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); while (enumElem) { delete enumElem; enumElem = iterator->nextElement(); } delete iterator; delete this->children; } } /** * Searches through all the TrackElements for trackID. * @param trackID The ID to search for. * @returns The TrackElement if Found, NULL otherwise. */ TrackElement* TrackElement::findByID(unsigned int trackID) { // return if Found. if (this->ID == trackID) return this; // search all children if (this->childCount > 0) { tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); TrackElement* tmpElem = NULL; while (enumElem) { if ((tmpElem = enumElem->findByID(trackID)) != NULL) return tmpElem; enumElem = iterator->nextElement(); } delete iterator; } // if not found return NULL; } /** * Searches through all the TrackElements for a trackName * @param trackName The name to search for. * @returns The TrackElement if Found, NULL otherwise. */ TrackElement* TrackElement::findByName(const char* trackName) { // return if Found. if (this->getName() && !strcmp(this->getName(), trackName)) return this; // search all children if (this->childCount > 0) { tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); TrackElement* tmpElem; while (enumElem) { if ((tmpElem = enumElem->findByName(trackName))) return tmpElem; enumElem = iterator->nextElement(); } delete iterator; } // if not found return NULL; } /** * checks if there are any BackLoops in the Track * @returns true if NO loop was found, false Otherwise You actually have to act on false!! */ bool TrackElement::backLoopCheck() const { tList* trackList = new tList; this->backLoopCheckAtomic(trackList); delete trackList; // only returns if everything worked out return true; } /** * checks if there are any BackLoops in the Track. * @param trackList A list of stored tracks, to search in. * @returns true if NO loop was found, false Otherwise You actually have to act on false!! */ bool TrackElement::backLoopCheckAtomic(tList* trackList) const { if (trackList->inList(this)) return false; trackList->add(this); if (this->children) { tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); while (enumElem) { if (!enumElem->backLoopCheckAtomic(trackList)) return false; } delete iterator; } return true; } /** * @param childCount which child to return * @returns the n-the children (starting at 0). Be aware, that when the trackElement has no Children, NULL will be returned */ TrackElement* TrackElement::getChild(unsigned int childCount) const { // if the the trackElement has no children return NULL. if (this->childCount == 0) return NULL; // we cannot return the childCount+m's Child, so we return the last. if (childCount > this->childCount) childCount = this->childCount; tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); for (unsigned int i = 0; i < childCount; i++) enumElem = iterator->nextElement(); delete iterator; return enumElem; } /** * prints out debug information about this TrackElement */ void TrackElement::debug() const { PRINT(0)("--== TrackElement:%i ==--", this->ID); if(this->getName()) PRINT(0)("--++Name: %s++--", this->getName()); if(this->isFresh) PRINT(0)(" -- has not jet eddited in any way --\n"); PRINT(0)("\n TimeTable: startingTime=%f; endTime=%f; duration=%f; jumpTime=%f\n", this->startingTime, this->endTime, this->duration, this->jumpTime); PRINT(0)(" consists of %d Points\n", this->nodeCount); if (this->childCount == 0) PRINT(0)(" has no child\n"); else if (this->childCount == 1) PRINT(0)(" has 1 child: =%d=\n", this->getChild(0)->ID); else if (this->childCount > 1) { PRINT(0)(" has %d children: ", this->childCount); //TrackElement* enumElem = this->children->enumerate(); tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); while (enumElem) { PRINT(0)("=%d= ", enumElem->ID); enumElem = iterator->nextElement(); } delete iterator; PRINT(0)("\n"); } if(this->isHotPoint) PRINT(0)(" is a special Point:\n"); if(this->isSavePoint) PRINT(0)(" is a SavePoint\n"); if(this->isFork) { PRINT(0)(" is A Fork with with %d children.\n", this->childCount); } if(this->isJoined) PRINT(0)(" is Joined at the End\n"); if(!this->backLoopCheck()) /* this should not happen */ PRINT(2)(" THERE IS A BACKLOOP TO THIS ELEMENT\n"); } /** * CONDITION that chooses the first child for the decision (static) * @param nothing Nothing in this function * @returns the chosen child */ int TrackElement::lowest(const void* nothing) const { return 0; } /** * CONDITION that chooses the last child for the decision (static) * @param nothing Nothing in this function * @returns the chosen child */ int TrackElement::highest(const void* nothing) const { return this->childCount-1; } /** * CONDITION that chooses a random child for the decision (static) * @param nothing Nothing in this function * @returns the chosen child */ int TrackElement::random(const void* nothing) const { int i = (int)floor ((float)rand()/(float)RAND_MAX * (float)this->childCount); if (i >= this->childCount) return this->childCount-1; else return i; } /** * CONDITION that chooses child 0, if the node(probably Player) is left of its parent (z<0)) and 1/right otherwise. * @param node The node to act upon. * @returns the chosen child */ int TrackElement::leftRight(const void* node) const { PNode* tmpNode = (PNode*)node; if (tmpNode->getRelCoor().z < 0) return 0; else return 1; } /** * CONDITION that chooses the child, that has the nearest distance to the node (probably player). * @param node The node to act upon. * @returns the chosen child This is rather dangerous, because one must carefully set the points on the curve. The best Way is to set the nodes as wide away of each other as possible, but take into consideration, that if the nodes are to far from a center node, the center will be chosen. (play with this!!). */ int TrackElement::nearest(const void* node) const { PNode* tmpNode = (PNode*)node; Vector nodeRelCoord = tmpNode->getRelCoor(); float minDist = 100000000; int childNumber = 0; int i = 0; //TrackElement* enumElem = this->children->enumerate(); tIterator* iterator = this->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); while (enumElem) { float dist = (nodeRelCoord - enumElem->curve->getNode(4)).len(); if (dist < minDist) { minDist = dist; childNumber = i; } i++; enumElem = iterator->nextElement(); } delete iterator; PRINTF(4)("PathDecision with nearest algorithm: %d\n", childNumber); return childNumber; } //////////////////////// ///// TRACKMANAGER ///// //////////////////////// /** * standard constructor */ TrackManager::TrackManager() { this->setClassID(CL_TRACK_MANAGER, "TrackManager"); this->setName("TrackManager"); TrackManager::singletonRef = this; // do this because otherwise the TrackNode cannot get The instance of the TrackManager PRINTF(3)("Initializing the TrackManager\n"); // setting up the First TrackElement this->firstTrackElem = new TrackElement(); this->firstTrackElem->ID = 1; this->firstTrackElem->setName("root"); this->currentTrackElem = firstTrackElem; this->curveType = CURVE_BEZIER; this->localTime = 0; this->maxTime = 0; this->trackElemCount = 1; this->trackNode = new TrackNode(); this->setBindSlave(this->trackNode); // initializing the Text this->trackText = new Text("fonts/earth.ttf", 30); this->trackText->setAlignment(E2D_ALIGN_SCREEN_CENTER); // initializing the Animation for the Text. this->textAnimation = new tAnimation(this->trackText, &Text::setBlending); this->textAnimation->addKeyFrame(1.0, 3.0, ANIM_NEG_EXP); this->textAnimation->addKeyFrame(0.0, .001); this->textAnimation->setInfinity(ANIM_INF_CONSTANT); } /** * loads a trackElement from a TiXmlElement * @param root the TiXmlElement to load the Data from */ bool TrackManager::loadParams(const TiXmlElement* root) { double x, y, z, d; LOAD_PARAM_START_CYCLE(root, element); { LoadParam_CYCLE(element, "WorkOn", this, TrackManager, workOnS) .describe("Selects a TrackElement (by name) to work on"); LoadParam_CYCLE(element, "Point", this, TrackManager, addPoint) .describe("Adds a new Point to the currently selected TrackElement"); LoadParam_CYCLE(element, "Duration", this, TrackManager, setDuration) .describe("Sets the Duration of the currently selected TrackElement"); LoadParam_CYCLE(element, "HotPoint", this, TrackManager, addHotPoint) .describe("Sets a new Point that acts as a hot point. meaning, the curve will flow through this Point"); LoadParam_CYCLE(element, "SavePoint", this, TrackManager, setSavePointS) .describe("Sets the current selected Point to a Savepoint, meaning that the curve will be ended and a new one starts, and that one starts again from this point on"); LoadParam_CYCLE(element, "Fork", this, TrackManager, forkS) .describe("Forks the Path into multiple forked Path names seperated by ','"); LoadParam_CYCLE(element, "Join", this, TrackManager, joinS) .describe("Joins multiple joining Path names seperated by ','"); /* if( !strcmp( element->Value(), "Fork")) { container = element->FirstChild(); if( container->ToText()) { assert( container->Value() != NULL); PRINTF(4)("Loaded Fork: %s\n", container->Value()); forkS(container->Value()); } } */ /* if( !strcmp( element->Value(), "Join")) { container = element->FirstChild(); if( container->ToText()) { assert( container->Value() != NULL); PRINTF0("Loaded Join: %s\n", container->Value()); joinS(container->Value()); } } */ } LOAD_PARAM_END_CYCLE(element); } /** * standard destructor */ TrackManager::~TrackManager() { PRINTF(3)("Destruct TrackManager\n"); PRINTF(4)("Deleting all the TrackElements\n"); delete this->firstTrackElem; delete this->trackText; // the tracknode should be deleted here, but is deleted by pNode: -> null_parent // we do not have a TrackManager anymore TrackManager::singletonRef = NULL; } //! Singleton Reference to TrackManager TrackManager* TrackManager::singletonRef = NULL; // INITIALIZE // /** * reserves Space for childCount children * @param childCount The Count of children to make space for. * @param trackElem The TrackElement to appy this to. (if NULL chose this->currentTrackElement) */ void TrackManager::initChildren(unsigned int childCount, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; trackElem->childCount = childCount; trackElem->mainJoin = true; // this tells join, that this one is the Main Join, if it tries to join multiple Tracks trackElem->children = new tList(); for (int i = 0; i < childCount; i++) { // create a new Element TrackElement* newElem = new TrackElement(); // setting up the new ID newElem->ID = ++trackElemCount; // setting up the Time newElem->startingTime = trackElem->endTime + trackElem->jumpTime; // adds the conection Point this->addPointV(trackElem->curve->getNode(trackElem->curve->getNodeCount()), newElem); // add the new child to the childList. trackElem->children->add(newElem); } // setting the Name of the new TrackElement to the name of the last one + _childI if (trackElem->getName()) { for (int i = 0; i < trackElem->childCount; i++) { char* childName = new char[strlen(trackElem->getName())+10]; sprintf(childName, "%s_child%d", trackElem->getName(), i); trackElem->getChild(i)->setName(childName); } } // select the first Child to work on. this->currentTrackElem = trackElem->getChild(0); } /** * Sets the trackID we are working on. * @param trackID the trackID we are working on */ void TrackManager::workOn(unsigned int trackID) { TrackElement* tmpElem = this->firstTrackElem->findByID(trackID); if (tmpElem) this->currentTrackElem = tmpElem; else PRINTF(2)("TrackElement %d not Found, leaving unchanged\n", trackID); PRINTF(4)("now Working on %d\n", this->currentTrackElem->ID); } /** * Sets the TrackElement to work on * @param trackName the Name of the Track to work on */ void TrackManager::workOnS(const char* trackName) { TrackElement* tmpElem = this->firstTrackElem->findByName(trackName); if (tmpElem) this->currentTrackElem = tmpElem; else PRINTF(2)("TrackElement %s not Found, leaving unchanged\n", trackName); PRINTF(4)("now Working on %d\n", this->currentTrackElem->ID); } /** * Sets the Type of the Curve * @param curveType The Type to set * @param trackElem the TrackElement that should get a new Curve. * this will possibly get obsolete during the process. */ void TrackManager::setCurveType(CurveType curveType, TrackElement* trackElem) { if (!trackElem->isFresh) { PRINTF(2)("It is not possible to change the type of a Curve after you have have appended some points to it\n"); return; } this->curveType = curveType; switch (curveType) { case CURVE_BEZIER: trackElem->curve = new BezierCurve(); break; } } /** * @param duration the duration of the TrackElement \see void TrackManager::setDuration(float duration, TrackElement* trackElem) */ void TrackManager::setDuration(float duration) { this->setDuration(duration, NULL); } /** * Sets the duration of the current path in seconds. * @param duration The duration in seconds. * @param trackElem The TrackElement to apply this to. */ void TrackManager::setDuration(float duration, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; trackElem->duration = duration; trackElem->endTime = trackElem->startingTime + duration; } /** * adds a point to trackElem * @param x x coord * @param y y coord * @param z z coord * @param trackElem The TrackElement to add the Point to */ void TrackManager::addPoint(float x, float y, float z) { this->addPointV(Vector(x,y,z)); } /** * adds a point to trackElem * @param newPoint The point to add. * @param trackElem The TrackElement to add the Point to */ void TrackManager::addPointV(Vector newPoint, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; if (trackElem->isFresh) { this->setCurveType(TMAN_DEFAULT_CURVETYPE, trackElem); trackElem->isFresh = false; } trackElem->curve->addNode(newPoint); trackElem->nodeCount++; } /** * adds a new Hot Point * @param x: the x coordinate of the hotpoint * @param y: the y coordinate of the hotpoint * @param z: the z coordinate of the hotpoint \see int TrackManager::addHotPointV(Vector newPoint, TrackElement* trackElem) */ void TrackManager::addHotPoint(float x, float y, float z) { this->addHotPointV(Vector(x, y, z)); } /** * adds save/splitpoint. * @param newPoint The point to add. * @param trackElem if supplied it will add a hotpoint on this TrackElement * @returns A Pointer to a newly appended Curve */ int TrackManager::addHotPointV(Vector newPoint, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; PRINTF(4)("setting up a HotPoint\n"); if (trackElem->isFresh) { trackElem->isFresh = false; } // @todo HotPoint Handling. trackElem->curve->addNode(newPoint); trackElem->nodeCount++; this->initChildren(1, trackElem); } /** @todo this must be better */ void TrackManager::setSavePointS(const char* nextElementName) { this->setSavePoint(NULL); if (strcmp(nextElementName, "")) this->firstTrackElem->findByID(this->trackElemCount)->setName(nextElementName); } /** * Sets the last HotPoint into a savePoint. * @param trackElem The TrackElement to appy this to. (if NULL chose this->currentTrackElement) * @returns A Pointer to a newly appended Curve If no HotPoint was defined the last added Point will be rendered into a savePoint. \n If the HotPoint was defined as a fork the Point will \b not be set into a savePoint. */ void TrackManager::setSavePoint(TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; PRINTF(4)("setting up a SavePoint.\n"); if (trackElem->isFork || trackElem->isSavePoint) { PRINTF(2)("%d is already finished \n", trackElem->ID); return; } trackElem->isSavePoint = true; trackElem->isHotPoint = true; this->initChildren(1, trackElem); } /** * adds some interessting non-linear movments through the level. * @param count The Count of children the fork will produce If no HotPoint was defined the last added Point will be rendered into a fork. \n If the HotPoint was defined as a savePoint the Point will \b not be set into a fork. */ void TrackManager::fork(unsigned int count, ...) { int* trackIDs = new int[count]; this->forkV(count, trackIDs, NULL); va_list ID; va_start (ID, count); for(int i = 0; i < count; i++) { *va_arg (ID, int*) = trackIDs[i]; } va_end(ID); delete []trackIDs; } /** * @param count how many children to produce * @param ... the information on the children (these are the Stings of their names \see TrackManager::fork(unsigned int count, ...) does the same as fork, but has an array of strings as an input. */ void TrackManager::forkS(unsigned int count, ...) { int* trackIDs = new int[count]; this->forkV(count, trackIDs, NULL); va_list name; va_start (name, count); for(int i = 0; i < count; i++) { this->firstTrackElem->findByID(trackIDs[i])->setName(va_arg(name, const char*)); } va_end(name); delete []trackIDs; } /** \see TrackManager::fork(unsigned int count, ...) */ void TrackManager::forkS(const char* forkString) { SubString strings(forkString, ','); int* trackIDs = new int[strings.getCount()]; this->forkV(strings.getCount(), trackIDs, NULL); for(int i = 0; i < strings.getCount(); i++) { this->firstTrackElem->findByID(trackIDs[i])->setName(strings.getString(i)); } delete []trackIDs; } /** * adds some interessting non-linear movments through the level. * @param count The Count of childrens the current HotPoint will have. * @param trackIDs A Pointer to an Array of ints which will hold the trackID's (the user will have to reserve space for this). * @param trackNames the names for the tracks as a char-arrey-array * @param trackElem The TrackElement to appy this to. (if NULL choose this->currentTrackElement) \see TrackManager::fork(unsigned int count, ...) */ void TrackManager::forkV(unsigned int count, int* trackIDs, char** trackNames, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; PRINTF(4)("Forking with %d children\n", count); if (trackElem->isSavePoint) return; trackElem->isFork = true; trackElem->isHotPoint = true; for(int i = 0; i < count; i++) trackIDs[i]=this->trackElemCount+1+i; this->initChildren(count, trackElem); } /** * decides under what condition a certain Path will be chosen. * @param trackID the trackID to apply this to. * @param cond the CONDITION of the decision * @param subject the Subject that will be decided upon with CONDITION cond. */ void TrackManager::condition(unsigned int trackID, CONDITION cond, void* subject) { this->condition(cond, subject, this->firstTrackElem->findByID(trackID)); } /** * decides under what condition a certain Path will be chosen. * @param cond the CONDITION of the decision * @param subject the Subject that will be decided upon with CONDITION cond. * @param trackElem The TrackElement to appy this to. (if NULL chose this->currentTrackElement) */ void TrackManager::condition(CONDITION cond, void* subject, TrackElement* trackElem) { if (!trackElem) trackElem = this->currentTrackElem; if (!trackElem->isFork) { PRINTF(2)("%d is not a Fork, and no condition can be set in this case\n", trackElem->ID); return; } else { switch (cond) { case LOWEST: trackElem->condFunc = &TrackElement::lowest; break; case HIGHEST: trackElem->condFunc = &TrackElement::highest; break; case RANDOM: trackElem->condFunc = &TrackElement::random; break; case LEFTRIGHT: trackElem->condFunc = &TrackElement::leftRight; break; case NEAREST: trackElem->condFunc = &TrackElement::nearest; break; case ENEMYKILLED: break; } trackElem->subject=subject; } } /** * joins some tracks together again. * @param count The count of Paths to join. Join will set the localTime to the longest time a Path has to get to this Point. \n Join will join all curves to the first curve, meaning that all the tangents will be matched. */ void TrackManager::join(unsigned int count, ...) { int* trackIDs = new int [count]; va_list ID; va_start (ID, count); for(int i = 0; i < count; i++) { trackIDs[i] = va_arg (ID, int); } va_end(ID); this->joinV(count, trackIDs); delete []trackIDs; } /** * Joins some Tracks together again. * @param count The count of trackElements to join \see void TrackManager::join(unsigned int count, ...) The difference to void TrackManager::join(unsigned int count, ...) is, that this function takes the Names of the TrackElements as inputs and not their ID */ void TrackManager::joinS(unsigned int count, ...) { int* trackIDs = new int [count]; va_list NAME; va_start (NAME, count); for(int i = 0; i < count; i++) { const char* name = va_arg (NAME, char*); TrackElement* tmpElem = this->firstTrackElem->findByName(name); if (tmpElem) trackIDs[i] = tmpElem->ID; else PRINTF(1)("Trying to join a Track, of which the name does not exist: %s\n", name); } va_end(NAME); this->joinV(count, trackIDs); delete []trackIDs; } /** \see void TrackManager::join(unsigned int count, ...) */ void TrackManager::joinS(const char* joinString) { SubString strings(joinString, ','); int* trackIDs = new int[strings.getCount()]; this->joinV(strings.getCount(), trackIDs); for(unsigned int i = 0; i < strings.getCount(); i++) { TrackElement* tmpElem = this->firstTrackElem->findByName(strings.getString(i)); if (tmpElem != NULL) trackIDs[i] = tmpElem->ID; else { PRINTF(1)("Trying to join a Track, of which the name does not exist: %s\n", strings.getString(i)); trackIDs[i] = -1; } } this->joinV(strings.getCount(), trackIDs); delete []trackIDs; } /** * joins some tracks together again. * @param count The count of Paths to join. * @param trackIDs an Array with the trackID's to join * * @see void TrackManager::join(unsigned int count, ...) */ void TrackManager::joinV(unsigned int count, int* trackIDs) { TrackElement* tmpTrackElem; TrackElement* tmpJoinElem; for (unsigned int i = 0; i < count; i++) if (!this->firstTrackElem->findByID(trackIDs[i])) { PRINTF(1)("Trying to Connect Paths that do not exist yet: %d\n Not Joining Anything\n", trackIDs[i]); return; } PRINTF(3)("Joining %d tracks and merging to Track %d\n", count, trackIDs[0]); // checking if there is a back-loop-connection and ERROR if it is. tmpTrackElem = this->firstTrackElem->findByID(trackIDs[0]); if (!tmpTrackElem->backLoopCheck()) { PRINTF(2)("Backloop connection detected at joining trackElements\n -> TRACK WILL NOT BE JOINED\n"); return; } TrackElement* firstJoint = this->firstTrackElem->findByID(trackIDs[0]); float tmpLatestTime = firstJoint->endTime; Vector tmpEndPoint = firstJoint->curve->getNode(firstJoint->curve->getNodeCount()); Vector tmpTangentPoint = firstJoint->curve->getNode(firstJoint->curve->getNodeCount()-1); Vector tmpc2Point = firstJoint->curve->getNode(firstJoint->curve->getNodeCount()-2); firstJoint->isJoined = true; // firstJoint->mainJoin = true; if(!firstJoint->isHotPoint) this->setSavePoint(firstJoint); // Timing: for (unsigned int i = 0; i < count; i++) { if(tmpJoinElem = this->firstTrackElem->findByID(trackIDs[i])) { if (tmpJoinElem->childCount == 0 && tmpJoinElem->endTime > tmpLatestTime) tmpLatestTime = tmpJoinElem->endTime; } } // time the main Join. firstJoint->jumpTime = tmpLatestTime - firstJoint->endTime; // Joining: for (int i = 1; i < count; i++) { if( tmpJoinElem = this->firstTrackElem->findByID(trackIDs[i])) { if (tmpJoinElem->childCount > 0) printf("!!This Curve has children, and as such will not be joined!!\n You can try joining other childless TrackElements to this one!"); else { this->addPointV(tmpc2Point, tmpJoinElem); this->addPointV(tmpTangentPoint, tmpJoinElem); this->addPointV(tmpEndPoint, tmpJoinElem); // time all other Joins tmpJoinElem->jumpTime = tmpLatestTime - tmpJoinElem->endTime; //Copying Joint-Info tmpJoinElem->children = firstJoint->children; tmpJoinElem->childCount = firstJoint->childCount; tmpJoinElem->isSavePoint = firstJoint->isSavePoint; tmpJoinElem->isFork = firstJoint->isFork; tmpJoinElem->isJoined = true; } } } if(firstJoint->children) { //TrackElement* enumElem = firstJoint->children->enumerate(); tIterator* iterator = firstJoint->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); while (enumElem) { PRINTF(5)("Setting startingTime of %d to %f.\n", enumElem->ID, tmpLatestTime); enumElem->startingTime = tmpLatestTime; enumElem->endTime = tmpLatestTime + enumElem->duration; enumElem = iterator->nextElement(); } delete iterator; } } /** * finalizes the TrackSystem. after this it will not be editable anymore @todo check for any inconsistencies, output errors */ void TrackManager::finalize() { for (int i = 1; i<= trackElemCount ;i++) { TrackElement* tmpElem = this->firstTrackElem->findByID(i); if( tmpElem->childCount > 0 && tmpElem->mainJoin) { tIterator* iterator = tmpElem->children->getIterator(); TrackElement* enumElem = iterator->firstElement(); //TrackElement* enumElem = tmpElem->children->enumerate(); while (enumElem) { // c1-continuity enumElem->curve->addNode(enumElem->curve->getNode(0) + ((enumElem->curve->getNode(0) - tmpElem->curve->getNode(tmpElem->curve->getNodeCount()-1)) ),2); enumElem->nodeCount++; // c2-continuity enumElem->curve->addNode((tmpElem->curve->getNode(tmpElem->curve->getNodeCount())- tmpElem->curve->getNode(tmpElem->curve->getNodeCount()-1)) * 4 + tmpElem->curve->getNode(tmpElem->curve->getNodeCount()-2), 3); enumElem->nodeCount++; PRINTF(5)("accelerations: %d-in: count: %d, %f, %f, %f\n %d-out: count: %d %f, %f, %f\n", tmpElem->ID, tmpElem->nodeCount, tmpElem->curve->calcAcc(0.999).x, tmpElem->curve->calcAcc(0.999).y, tmpElem->curve->calcAcc(0.999).z, enumElem->ID, enumElem->nodeCount, enumElem->curve->calcAcc(0).x, enumElem->curve->calcAcc(0).y, enumElem->curve->calcAcc(0).z); enumElem = iterator->nextElement(); } delete iterator; } } for (int i = 1; i <= trackElemCount;i++) if (this->firstTrackElem->findByID(i)->endTime > this->maxTime) this->maxTime = this->firstTrackElem->findByID(i)->endTime; // very bad implemented :/ } // RUNTIME // /** * calculates the Position for the localTime of the Track. * @returns the calculated Position */ Vector TrackManager::calcPos() const { return this->currentTrackElem->curve->calcPos((this->localTime-this->currentTrackElem->startingTime)/this->currentTrackElem->duration); } /** * calculates the Rotation for the localTime of the Track. * @returns the calculated Rotation */ Vector TrackManager::calcDir() const { return this->currentTrackElem->curve->calcDir((this->localTime - this->currentTrackElem->startingTime)/this->currentTrackElem->duration); } /** * @returns the current Width of the track */ float TrackManager::getWidth() const { return this->currentTrackElem->width; } /** * Advances the local-time of the Track around dt * @param dt The time about which to advance. This function also checks, if the TrackElement has to be changed. */ void TrackManager::tick(float dt) { PRINTF(4)("CurrentTrackID: %d, LocalTime is: %f, timestep is: %f\n", this->currentTrackElem->ID, this->localTime, dt); if (this->localTime <= this->firstTrackElem->duration) this->jumpTo(this->localTime); if (this->localTime <= this->maxTime) this->localTime += dt; if (this->localTime > this->currentTrackElem->endTime && this->currentTrackElem->children) { if (this->currentTrackElem->jumpTime != 0.0) this->jumpTo(this->localTime + this->currentTrackElem->jumpTime); // jump to the next TrackElement and also set the history of the new Element to the old one. TrackElement* tmpHistoryElem = this->currentTrackElem; this->currentTrackElem = this->currentTrackElem->getChild(this->choosePath(this->currentTrackElem)); this->currentTrackElem->history = tmpHistoryElem; if (this->currentTrackElem->getName()) { this->trackText->setText(this->currentTrackElem->getName()); this->textAnimation->replay(); } } if (this->bindSlave) { Vector tmp = this->calcPos(); Quaternion quat = Quaternion(this->calcDir(), Vector(this->currentTrackElem->curve->calcAcc((localTime-this->currentTrackElem->startingTime)/this->currentTrackElem->duration).x,1,this->currentTrackElem->curve->calcAcc((localTime-this->currentTrackElem->startingTime)/this->currentTrackElem->duration).z)); Vector v(0.0, 1.0, 0.0); Quaternion q(-PI/2, v); quat = quat * q; this->bindSlave->setAbsCoor(tmp); this->bindSlave->setAbsDir(quat); } } /** * Jumps to a certain point on the Track. * @param time The time on the Track to jump to. This should be used to Jump backwards on a Track, because moving forward means to change between the Path. (it then tries to choose the default.) Max is trackLengthMax. */ void TrackManager::jumpTo(float time) { if (time == 0) { this->currentTrackElem = this->firstTrackElem; if (this->currentTrackElem->getName()) { this->trackText->setText(this->currentTrackElem->getName()); this->textAnimation->play(); } } this->localTime = time; } /** * a Function that decides which Path we should follow. * @param trackElem The Path to choose. */ int TrackManager::choosePath(TrackElement* trackElem) { return (trackElem->*(trackElem->condFunc))(trackElem->subject); } /** * Sets the PNode, that should be moved along the Tack * @param bindSlave the PNode to set */ void TrackManager::setBindSlave(PNode* bindSlave) { this->bindSlave = bindSlave; } /** * @returns the main TrackNode */ PNode* TrackManager::getTrackNode() { return this->trackNode; } // DEBUG // /** * Imports a model of the Graph into the OpenGL-environment. * @param dt The Iterator used in seconds for Painting the Graph. This is for testing facility only. Do this if you want to see the Path inside the Level. eventually this will all be packed into a gl-list. */ void TrackManager::drawGraph(float dt) const { for (int i = 1; i <= trackElemCount; i++) { glBegin(GL_LINE_STRIP); TrackElement* tmpElem = this->firstTrackElem->findByID(i); if (tmpElem->curve) for(float f = 0.0; f < 1.0; f+=dt) { // printf("%f, %f, %f\n",trackManager->calcPos().x, trackManager->calcPos().y, trackManager->calcPos().z); Vector tmpVector = tmpElem->curve->calcPos(f); glVertex3f(tmpVector.x, tmpVector.y, tmpVector.z); } glEnd(); } } /** * outputs debug information about the trackManager * @param level how much debug */ void TrackManager::debug(unsigned int level) const { PRINT(0)("=========================================\n"); PRINT(0)("= CLASS TRACKMANAGER::debug information =\n"); PRINT(0)("=========================================\n"); // PRINT(0)("Status is: % PRINT(0)(" Consists of %d elements\n", this->trackElemCount); PRINT(0)(" localTime is: %f\n", this->localTime); if (level >= 2) { for (int i = 1; i <= trackElemCount; i++) { TrackElement* tmpElem = this->firstTrackElem->findByID(i); tmpElem->debug(); } } PRINT(0)("-----------------------------------------\n"); }