/* 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: Patrick Boenzli co-programmer: Christian Meyer */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_WORLD #include "world.h" #include "orxonox.h" #include "p_node.h" #include "null_parent.h" #include "helper_parent.h" #include "track_node.h" #include "world_entity.h" #include "player.h" #include "camera.h" #include "environment.h" #include "skysphere.h" #include "skybox.h" #include "satellite.h" #include "terrain.h" #include "light.h" #include "text_engine.h" #include "track_manager.h" #include "garbage_collector.h" #include "animation_player.h" #include "command_node.h" #include "glmenu_imagescreen.h" #include "list.h" using namespace std; WorldInterface* WorldInterface::singletonRef = 0; /** \brief private constructor because of singleton */ WorldInterface::WorldInterface() { this->worldIsInitialized = false; this->worldReference = NULL; } /** \brief public deconstructor */ WorldInterface::~WorldInterface() { this->singletonRef = NULL; this->worldIsInitialized = false; this->worldReference = NULL; } /** \brief gets the singleton instance \returns singleton instance */ WorldInterface* WorldInterface::getInstance() { if( singletonRef == NULL) singletonRef = new WorldInterface(); return singletonRef; } /** \brief initializes the interface \param reference to the world if the worldinterface is not initilizes, there wont be any useable interface */ void WorldInterface::init(World* world) { this->worldReference = world; if( world != NULL) { this->worldIsInitialized = true; PRINTF(3)("WorldInterface up and running\n"); } } /** \brief gets the entity list from the world \return entity list */ tList* WorldInterface::getEntityList() { if( this->worldIsInitialized) return this->worldReference->getEntities(); PRINT(1)("Someone tried to use the WorldInterface before it has been initizlized! this can result in SEGFAULTs!\n"); return NULL; } /** \brief create a new World This creates a new empty world! */ World::World (char* name) { this->init(name, -1); //NullParent* np = NullParent::getInstance(); } /** \brief creates a new World... \param worldID with this ID */ World::World (int worldID) { this->init(NULL, worldID); } /** \brief remove the World from memory delete everything explicitly, that isn't contained in the parenting tree! things contained in the tree are deleted automaticaly */ World::~World () { PRINTF(3)("World::~World() - deleting current world\n"); CommandNode* cn = Orxonox::getInstance()->getLocalInput(); cn->unbind(this->localPlayer); cn->reset(); ResourceManager::getInstance()->debug(); ResourceManager::getInstance()->unloadAllByPriority(RP_LEVEL); ResourceManager::getInstance()->debug(); delete WorldInterface::getInstance(); delete this->nullParent; delete this->entities; delete this->lightMan; delete this->trackManager; TextEngine::getInstance()->flush(); AnimationPlayer::getInstance()->debug(); delete AnimationPlayer::getInstance(); // this should be at the end of the unloading sequence. //delete garbagecollecor //delete animator } /** \brief initializes the world. set all stuff here that is world generic and does not use to much memory because the real init() function StoryEntity::init() will be called shortly before start of the game. since all worlds are initiated/referenced before they will be started. NO LEVEL LOADING HERE - NEVER! */ void World::init(char* name, int worldID) { this->setClassName ("World"); this->worldName = name; this->debugWorldNr = worldID; this->entities = new tList(); AnimationPlayer::getInstance(); // initializes the animationPlayer } /** \brief this is executed before load since the load function sometimes needs data, that has been init before the load and after the proceeding storyentity has finished */ ErrorMessage World::preLoad() { /* init the world interface */ WorldInterface* wi = WorldInterface::getInstance(); wi->init(this); this->garbageCollector = GarbageCollector::getInstance(); } /** \brief loads the World by initializing all resources, and set their default values. */ ErrorMessage World::load() { // BezierCurve* tmpCurve = new BezierCurve(); if(this->debugWorldNr != -1) { // initializing Font this->glmis->step(); // initializing the TrackManager trackManager = TrackManager::getInstance(); //trackManager->addPoint(Vector(0,0,0)); trackManager->addPoint(Vector(150, -35, 5)); trackManager->addPoint(Vector(200,-35, 5)); trackManager->addPoint(Vector(250, -35, 5)); trackManager->addPoint(Vector(320,-33,-.55)); trackManager->setDuration(2); trackManager->setSavePoint(); trackManager->addPoint(Vector(410, 0, 0)); trackManager->addPoint(Vector(510, 20, -10)); trackManager->addPoint(Vector(550, 20, -10)); trackManager->addPoint(Vector(570, 20, -10)); trackManager->setDuration(5); int fork11, fork12; trackManager->fork(2, &fork11, &fork12); trackManager->workOn(fork11); trackManager->addPoint(Vector(640, 25, -30)); trackManager->addPoint(Vector(700, 40, -120)); trackManager->addPoint(Vector(800, 50, -150)); trackManager->addPoint(Vector(900, 60, -100)); trackManager->addPoint(Vector(900, 60, -70)); trackManager->addPoint(Vector(990, 65, -15)); trackManager->addPoint(Vector(1050, 65, -10)); trackManager->addPoint(Vector(1100, 65, -20)); trackManager->setDuration(10); trackManager->workOn(fork12); trackManager->addPoint(Vector(640, 25, 20)); trackManager->addPoint(Vector(670, 50, 120)); trackManager->addPoint(Vector(700, 70, 80)); trackManager->addPoint(Vector(800, 70, 65)); trackManager->addPoint(Vector(850, 65, 65)); trackManager->addPoint(Vector(920, 35, 40)); trackManager->addPoint(Vector(945, 40, 40)); trackManager->addPoint(Vector(970, 24, 40)); trackManager->addPoint(Vector(1000, 40, -7)); trackManager->setDuration(10); trackManager->join(2, fork11, fork12); trackManager->workOn(5); trackManager->addPoint(Vector(1200, 60, -50)); trackManager->addPoint(Vector(1300, 50, -50)); trackManager->addPoint(Vector(1400, 40, -50)); trackManager->addPoint(Vector(1500, 40, -60)); trackManager->addPoint(Vector(1600, 35, -55)); trackManager->addPoint(Vector(1700, 45, -40)); trackManager->addPoint(Vector(1750, 60, -40)); trackManager->addPoint(Vector(1770, 80, -40)); trackManager->addPoint(Vector(1800, 100, -40)); trackManager->setDuration(10); trackManager->finalize(); /*monitor progress*/ this->glmis->step(); // LIGHT initialisation lightMan = LightManager::getInstance(); lightMan->setAmbientColor(.1,.1,.1); lightMan->addLight(); // lightMan->setAttenuation(1.0, .01, 0.0); // lightMan->setDiffuseColor(1,1,1); // lightMan->addLight(1); // lightMan->setPosition(20, 10, -20); // lightMan->setDiffuseColor(0,0,0); lightMan->debug(); switch(this->debugWorldNr) { /* this loads the hard-coded debug world. this only for simplicity and will be removed by a reald world-loader, which interprets a world-file. if you want to add an own debug world, just add a case DEBUG_WORLD_[nr] and make whatever you want... */ case DEBUG_WORLD_0: { lightMan->setPosition(-5.0, 10.0, -40.0); this->nullParent = NullParent::getInstance (); this->nullParent->setName ("NullParent"); // !\todo old track-system has to be removed //create helper for player //HelperParent* hp = new HelperParent (); /* the player has to be added to this helper */ // create a player this->localPlayer = new Player (); this->localPlayer->setName ("player"); this->spawn (this->localPlayer); /*monitor progress*/ //this->glmis->step(); this->glmis->step(); // bind input Orxonox *orx = Orxonox::getInstance (); orx->getLocalInput()->bind (this->localPlayer); // bind camera this->localCamera = new Camera(); this->localCamera->setName ("camera"); /*monitor progress*/ this->glmis->step(); // Create SkySphere // this->skySphere = new Skysphere("../data/pictures/sky-replace.jpg"); // this->skySphere->setName("SkySphere"); // this->localCamera->addChild(this->skySphere); // this->spawn(this->skySphere); skyBox = new SkyBox(); skyBox->setTexture("pictures/sky/skybox", "jpg"); skyBox->setParent(localCamera); this->spawn(skyBox); /*monitor progress*/ this->glmis->step(); WorldEntity* env = new Environment(); env->setName ("env"); this->spawn(env); /* Vector* es = new Vector (10, 5, 0); Quaternion* qs = new Quaternion (); WorldEntity* pr = new Primitive(P_CYLINDER); pr->setName("primitive"); this->spawn(pr, this->localPlayer, es, qs, PNODE_MOVEMENT); */ /*monitor progress*/ this->glmis->step(); // trackManager->setBindSlave(env); PNode* tn = trackManager->getTrackNode(); tn->addChild(this->localPlayer); this->localCamera->lookAt(tn); //localCamera->setParent(TrackNode::getInstance()); tn->addChild(this->localCamera); // localCamera->lookAt(tn); this->localPlayer->setMode(PNODE_ALL); //Vector* cameraOffset = new Vector (0, 5, -10); trackManager->condition(2, LEFTRIGHT, this->localPlayer); this->glmis->step(); break; } case DEBUG_WORLD_1: { lightMan->setPosition(.0, .0, .0); lightMan->setAttenuation(1.0, .01, 0.0); lightMan->setSpecularColor(1,0,0); this->nullParent = NullParent::getInstance (); this->nullParent->setName ("NullParent"); // create a player WorldEntity* myPlayer = new Player(); myPlayer->setName ("player"); this->spawn(myPlayer); this->localPlayer = myPlayer; // bind input Orxonox *orx = Orxonox::getInstance(); orx->getLocalInput()->bind (myPlayer); // bind camera this->localCamera = new Camera (); this->localCamera->setName ("camera"); this->localCamera->lookAt(LightManager::getInstance()->getLight(0)); this->localCamera->setParent(this->localPlayer); // Create SkySphere skySphere = new Skysphere("../data/pictures/sky-replace.jpg"); this->localPlayer->addChild(this->skySphere); this->spawn(this->skySphere); Vector* es = new Vector (20, 0, 0); Quaternion* qs = new Quaternion (); lightMan->getLight(0)->setParent(trackManager->getTrackNode()); break; } case DEBUG_WORLD_2: { lightMan->setAmbientColor(.1,.1,.1); lightMan->addLight(); lightMan->setPosition(-5.0, 10.0, -40.0); this->nullParent = NullParent::getInstance (); this->nullParent->setName ("NullParent"); // !\todo old track-system has to be removed //create helper for player //HelperParent* hp = new HelperParent (); /* the player has to be added to this helper */ // create a player this->localPlayer = new Player (); this->localPlayer->setName ("player"); this->spawn (this->localPlayer); /*monitor progress*/ //this->glmis->step(); this->glmis->step(); // bind input Orxonox *orx = Orxonox::getInstance (); orx->getLocalInput()->bind (this->localPlayer); // bind camera this->localCamera = new Camera(); this->localCamera->setName ("camera"); this->localCamera->lookAt(this->localPlayer); this->localCamera->setParent(this->localPlayer); /*monitor progress*/ this->glmis->step(); // Create SkySphere this->skySphere = new Skysphere("../data/pictures/sky-replace.jpg"); this->skySphere->setName("SkySphere"); this->spawn(this->skySphere); this->localCamera->addChild(this->skySphere); this->skySphere->setMode(PNODE_MOVEMENT); /*monitor progress*/ this->glmis->step(); WorldEntity* baseNode = new Satellite(Vector(1,0,1), 1.2); this->localPlayer->addChild(baseNode); baseNode->setRelCoor(Vector(10.0, 2.0, 1.0)); this->spawn(baseNode); WorldEntity* secondNode = new Satellite(Vector(0,0,1), 2.0); baseNode->addChild(secondNode); secondNode->setRelCoor(Vector(0.0, 0.0, 3.0)); this->spawn(secondNode); WorldEntity* thirdNode = new Satellite(Vector(0,0,1), 1.0); secondNode->addChild(thirdNode); thirdNode->setRelCoor(Vector(2.0, 0.0, 0.0)); this->spawn(thirdNode); WorldEntity* b = new Environment(); this->localPlayer->addChild(b); b->setRelCoor(Vector(10.0, 1.0, 1.0)); this->spawn(b); WorldEntity* c = new Environment(); this->localPlayer->addChild(c); c->setRelCoor(Vector(10.0, 2.0, -1.0)); this->spawn(c); /* KeyFrame* f1 = new KeyFrame; f1->position = new Vector(-1.1, 0.0, 2.6); f1->direction = new Quaternion(); f1->time = 1.0; f1->mode = NEG_EXP; KeyFrame* f2 = new KeyFrame; f2->position = new Vector(-2.1, 0.0, 2.6); f2->direction = new Quaternion(); f2->time = 0.1; f2->mode = NEG_EXP; KeyFrame* f3 = new KeyFrame; f3->position = new Vector(10.0, 2.0, -1.0); f3->direction = new Quaternion(); f3->time = 0.2; f3->mode = NEG_EXP; KeyFrame* f4 = new KeyFrame; f4->position = new Vector(10.0, 5.0, -1.0); f4->direction = new Quaternion(); f4->time = 1.0; f4->mode = NEG_EXP; this->simpleAnimation->animatorBegin(); this->simpleAnimation->selectObject(b); this->simpleAnimation->setAnimationMode(SINGLE); this->simpleAnimation->addKeyFrame(f1); this->simpleAnimation->addKeyFrame(f2); this->simpleAnimation->start(); this->simpleAnimation->selectObject(c); this->simpleAnimation->addKeyFrame(f3); this->simpleAnimation->addKeyFrame(f4); this->simpleAnimation->start(); this->simpleAnimation->animatorEnd(); */ /* Vector* es = new Vector (10, 5, 0); Quaternion* qs = new Quaternion (); WorldEntity* pr = new Primitive(P_CYLINDER); pr->setName("primitive"); this->spawn(pr, this->localPlayer, es, qs, PNODE_MOVEMENT); */ /*monitor progress*/ this->glmis->step(); // trackManager->setBindSlave(env); PNode* tn = trackManager->getTrackNode(); tn->addChild(this->localPlayer); //localCamera->setParent(TrackNode::getInstance()); tn->addChild(this->localCamera); // localCamera->lookAt(tn); this->localPlayer->setMode(PNODE_ALL); //Vector* cameraOffset = new Vector (0, 5, -10); trackManager->condition(2, LEFTRIGHT, this->localPlayer); this->glmis->step(); break; } default: printf("World::load() - no world with ID %i found", this->debugWorldNr ); } } else if(this->worldName != NULL) { } // initialize debug coord system objectList = glGenLists(1); glNewList (objectList, GL_COMPILE); // trackManager->drawGraph(.01); trackManager->debug(2); glEndList(); terrain = new Terrain("../data/worlds/newGround.obj"); terrain->setRelCoor(Vector(0,-10,0)); this->spawn(terrain); } /** \brief initializes a new World shortly before start this is the function, that will be loaded shortly before the world is started */ ErrorMessage World::init() { this->bPause = false; CommandNode* cn = Orxonox::getInstance()->getLocalInput(); cn->addToWorld(this); cn->enable(true); } /** \brief starts the World */ ErrorMessage World::start() { PRINTF(3)("World::start() - starting current World: nr %i\n", this->debugWorldNr); this->bQuitOrxonox = false; this->bQuitCurrentGame = false; this->mainLoop(); } /** \brief stops the world. This happens, when the player decides to end the Level. */ ErrorMessage World::stop() { PRINTF(3)("World::stop() - got stop signal\n"); this->bQuitCurrentGame = true; } /** \brief pauses the Game */ ErrorMessage World::pause() { this->isPaused = true; } /** \brief ends the pause Phase */ ErrorMessage World::resume() { this->isPaused = false; } /** \brief destroys the World */ ErrorMessage World::destroy() { } /** \brief shows the loading screen */ void World::displayLoadScreen () { PRINTF(3)("World::displayLoadScreen - start\n"); //GLMenuImageScreen* this->glmis = GLMenuImageScreen::getInstance(); this->glmis->init(); this->glmis->setMaximum(8); this->glmis->draw(); PRINTF(3)("World::displayLoadScreen - end\n"); } /** \brief removes the loadscreen, and changes over to the game \todo take out the delay */ void World::releaseLoadScreen () { PRINTF(3)("World::releaseLoadScreen - start\n"); this->glmis->setValue(this->glmis->getMaximum()); //SDL_Delay(500); PRINTF(3)("World::releaseLoadScreen - end\n"); } /** \brief gets the list of entities from the world \returns entity list */ tList* World::getEntities() { return this->entities; } /** \brief this returns the current game time \returns elapsed game time */ double World::getGameTime() { return this->gameTime; } /** \brief checks for collisions This method runs through all WorldEntities known to the world and checks for collisions between them. In case of collisions the collide() method of the corresponding entities is called. */ void World::collide () { /* List *a, *b; WorldEntity *aobj, *bobj; a = entities; while( a != NULL) { aobj = a->nextElement(); if( aobj->bCollide && aobj->collisioncluster != NULL) { b = a->nextElement(); while( b != NULL ) { bobj = b->nextElement(); if( bobj->bCollide && bobj->collisioncluster != NULL ) { unsigned long ahitflg, bhitflg; if( check_collision ( &aobj->place, aobj->collisioncluster, &ahitflg, &bobj->place, bobj->collisioncluster, &bhitflg) ); { aobj->collide (bobj, ahitflg, bhitflg); bobj->collide (aobj, bhitflg, ahitflg); } } b = b->nextElement(); } } a = a->enumerate(); } */ } /** \brief runs through all entities calling their draw() methods */ void World::draw () { /* draw entities */ WorldEntity* entity; glLoadIdentity(); //entity = this->entities->enumerate(); tIterator* iterator = this->entities->getIterator(); entity = iterator->nextElement(); while( entity != NULL ) { if( entity->bDraw ) entity->draw(); //entity = this->entities->nextElement(); entity = iterator->nextElement(); } delete iterator; glCallList (objectList); TextEngine::getInstance()->draw(); lightMan->draw(); // must be at the end of the drawing procedure, otherwise Light cannot be handled as PNodes // } /** \brief function to put your own debug stuff into it. it can display informations about the current class/procedure */ void World::debug() { PRINTF(2)("debug() - starting debug\n"); PNode* p1 = NullParent::getInstance (); PNode* p2 = new PNode (Vector(2, 2, 2), p1); PNode* p3 = new PNode (Vector(4, 4, 4), p1); PNode* p4 = new PNode (Vector(6, 6, 6), p2); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p1->shiftCoor (Vector(-1, -1, -1)); printf("World::debug() - shift\n"); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p1->update (0); printf ("World::debug() - update\n"); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p2->shiftCoor (Vector(-1, -1, -1)); p1->update (0); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p2->setAbsCoor (Vector(1,2,3)); p1->update (0); p1->debug (); p2->debug (); p3->debug (); p4->debug (); delete p1; /* WorldEntity* entity; printf("counting all entities\n"); printf("World::debug() - enumerate()\n"); entity = entities->enumerate(); while( entity != NULL ) { if( entity->bDraw ) printf("got an entity\n"); entity = entities->nextElement(); } */ } /** \brief main loop of the world: executing all world relevant function in this loop we synchronize (if networked), handle input events, give the heart-beat to all other member-entities of the world (tick to player, enemies etc.), checking for collisions drawing everything to the screen. */ void World::mainLoop() { this->lastFrame = SDL_GetTicks (); PRINTF(3)("World::mainLoop() - Entering main loop\n"); while( !this->bQuitOrxonox && !this->bQuitCurrentGame) /* \todo implement pause */ { PRINTF(3)("World::mainloop() - number of entities: %i\n", this->entities->getSize()); // Network this->synchronize (); // Process input this->handleInput (); if( this->bQuitCurrentGame || this->bQuitOrxonox) break; // Process time this->tick (); // Update the state this->update (); // Process collision this->collide (); // Draw this->display (); // for( int i = 0; i < 5000000; i++) {} /* \todo this is to slow down the program for openGl Software emulator computers, reimplement*/ } PRINTF(3)("World::mainLoop() - Exiting the main loop\n"); } /** \brief synchronize local data with remote data */ void World::synchronize () { // Get remote input // Update synchronizables } /** \brief run all input processing the command node is the central input event dispatcher. the node uses the even-queue from sdl and has its own event-passing-queue. */ void World::handleInput () { // localinput CommandNode* cn = Orxonox::getInstance()->getLocalInput(); cn->process(); // remoteinput } /** \brief advance the timeline this calculates the time used to process one frame (with all input handling, drawing, etc) the time is mesured in ms and passed to all world-entities and other classes that need a heart-beat. */ void World::tick () { Uint32 currentFrame = SDL_GetTicks(); if(!this->bPause) { this->dt = currentFrame - this->lastFrame; if( this->dt > 0) { float fps = 1000/dt; // temporary, only for showing how fast the text-engine is char tmpChar[20]; sprintf(tmpChar, "fps: %4.0f", fps); } else { /* the frame-rate is limited to 100 frames per second, all other things are for nothing. */ PRINTF(2)("fps = 1000 - frame rate is adjusted\n"); SDL_Delay(10); this->dt = 10; } //this->timeSlice (dt); /* function to let all entities tick (iterate through list) */ float seconds = this->dt / 1000.0; this->gameTime += seconds; //entity = entities->enumerate(); tIterator* iterator = this->entities->getIterator(); WorldEntity* entity = iterator->nextElement(); while( entity != NULL) { entity->tick (seconds); entity = iterator->nextElement(); } delete iterator; //skySphere->updatePosition(localCamera->absCoordinate); /* update tick the rest */ this->trackManager->tick(this->dt); this->localCamera->tick(this->dt); this->garbageCollector->tick(seconds); AnimationPlayer::getInstance()->tick(seconds); } this->lastFrame = currentFrame; } /** \brief this function gives the world a consistant state after ticking (updating the world state) this will give a constistant state to the whole system. */ void World::update() { this->garbageCollector->update(); this->nullParent->update (dt); } /** \brief render the current frame clear all buffers and draw the world */ void World::display () { // clear buffer glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // set camera this->localCamera->apply (); // draw world this->draw(); // draw HUD /* \todo draw HUD */ // flip buffers SDL_GL_SwapBuffers(); //SDL_Surface* screen = Orxonox::getInstance()->getScreen (); //SDL_Flip (screen); } /** \brief add and spawn a new entity to this world \param entity to be added */ void World::spawn(WorldEntity* entity) { this->entities->add (entity); entity->postSpawn (); } /** \brief add and spawn a new entity to this world \param entity to be added \param absCoor At what coordinates to add this entity. \param absDir In which direction should it look. */ void World::spawn(WorldEntity* entity, Vector* absCoor, Quaternion* absDir) { this->entities->add (entity); entity->setAbsCoor (*absCoor); entity->setAbsDir (*absDir); entity->postSpawn (); } /** \brief add and spawn a new entity to this world \param entity to be added \param entity to be added to (PNode) \param At what relative coordinates to add this entity. \param In which relative direction should it look. */ void World::spawn(WorldEntity* entity, PNode* parentNode, Vector* relCoor, Quaternion* relDir, int parentingMode) { this->nullParent = NullParent::getInstance(); if( parentNode != NULL) { parentNode->addChild (entity); entity->setRelCoor (*relCoor); entity->setRelDir (*relDir); entity->setMode(parentingMode); this->entities->add (entity); entity->postSpawn (); } } /** \brief commands that the world must catch \returns false if not used by the world */ bool World::command(Command* cmd) { if( !strcmp( cmd->cmd, "view0")) this->localCamera->setViewMode(VIEW_NORMAL); else if( !strcmp( cmd->cmd, "view1")) this->localCamera->setViewMode(VIEW_BEHIND); else if( !strcmp( cmd->cmd, "view2")) this->localCamera->setViewMode(VIEW_FRONT); else if( !strcmp( cmd->cmd, "view3")) this->localCamera->setViewMode(VIEW_LEFT); else if( !strcmp( cmd->cmd, "view4")) this->localCamera->setViewMode(VIEW_RIGHT); else if( !strcmp( cmd->cmd, "view5")) this->localCamera->setViewMode(VIEW_TOP); return false; }