/* 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 */ #include "world.h" #include "world_entity.h" #include "collision.h" #include "track_manager.h" #include "track.h" #include "player.h" #include "command_node.h" #include "camera.h" #include "environment.h" #include "p_node.h" #include "null_parent.h" #include "helper_parent.h" #include using namespace std; /** \brief create a new World This creates a new empty world! */ World::World (char* name) { this->setClassName ("World"); this->worldName = name; this->debugWorldNr = -1; this->entities = new tList(); } World::World (int worldID) { this->debugWorldNr = worldID; this->worldName = NULL; this->entities = new tList(); } /** \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("World::~World() - deleting current world\n"); CommandNode* cn = Orxonox::getInstance()->getLocalInput(); cn->unbind(this->localPlayer); cn->reset(); this->localCamera->destroy(); this->nullParent->destroy (); //delete this->testCurve; /* WorldEntity* entity = entities->enumerate(); while( entity != NULL ) { entity->destroy(); entity = entities->nextElement(); } this->entities->destroy(); */ /* FIX the parent list has to be cleared - not possible if we got the old list also*/ //delete this->entities; //delete this->localCamera; /* this->localPlayer hasn't to be deleted explicitly, it is contained in entities*/ } GLfloat ctrlpoints[4][3] = { {20.0, 10.0, 5.0}, {40.0, -10.0, 0.0}, {60.0, -10.0, 5.0}, {80.0, 10.0, 5.0}}; ErrorMessage World::init() { this->bPause = false; CommandNode* cn = Orxonox::getInstance()->getLocalInput(); cn->addToWorld(this); cn->enable(true); glMap1f (GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]); glEnable (GL_MAP1_VERTEX_3); //theNurb = gluNewNurbsRenderer (); //gluNurbsProperty (theNurb, GLU_NURBS_MODE, GLU_NURBS_TESSELLATOR); //gluNurbsProperty (theNurb, GLU_NURBS_VERTEX, vertexCallback ); } ErrorMessage World::start() { printf("World::start() - starting current World: nr %i\n", this->debugWorldNr); this->bQuitOrxonox = false; this->bQuitCurrentGame = false; this->mainLoop(); } ErrorMessage World::stop() { printf("World::stop() - got stop signal\n"); this->bQuitCurrentGame = true; } ErrorMessage World::pause() { this->isPaused = true; } ErrorMessage World::resume() { this->isPaused = false; } void World::destroy() { } void World::displayLoadScreen () { printf ("World::displayLoadScreen - start\n"); int w = 680; int h = 480; glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)w/(GLfloat)h, .5f ,150.0f); glMatrixMode(GL_MODELVIEW); SDL_Surface *pBitmap[1]; unsigned int textureArray[1]; char *strFileName = "orx_tex.bmp"; int textureID = 0; pBitmap[0] = SDL_LoadBMP (strFileName); if( pBitmap[0] == NULL) return; if(pBitmap[0] == NULL) // If we can't load the file, quit! { printf (" Failed loading %s\n", strFileName); SDL_Quit (); } glGenTextures(1, &textureArray[textureID]); /* Bind the texture to the texture arrays index and init the texture */ glBindTexture(GL_TEXTURE_2D, textureArray[textureID]); /* Rearrange the pixelData since openGL has a different pixel orientation */ int width = pBitmap[0]->w; int height = pBitmap[0]->h; unsigned char * data = (unsigned char *)(pBitmap[0]->pixels); unsigned char * newData = new unsigned char[width * height * 3]; int channels = 3; /* RGB channel number*/ int bytesPerPixel = pBitmap[0]->format->BytesPerPixel; /* this is the real swapping algorithm */ for( int i = 0 ; i < (height / 2) ; ++i ) for( int j = 0 ; j < width * bytesPerPixel; j += bytesPerPixel ) for(int k = 0; k < bytesPerPixel; ++k) swap( data[ (i * width * bytesPerPixel) + j + k], data[ ( (height - i - 1) * width * bytesPerPixel ) + j + k]); // the following lines extract R,G and B values from any bitmap for(int i = 0; i < (width * height); ++i) { byte r,g,b; Uint32 pixel_value = 0; /* the following loop extracts the pixel (however wide it is 8,16,24 or 32) and creates a long with all these bytes taken together. */ for(int j = bytesPerPixel - 1 ; j >= 0; --j) { pixel_value = pixel_value << 8; pixel_value = pixel_value | data[ (i * bytesPerPixel) + j ]; } SDL_GetRGB(pixel_value, pBitmap[0]->format, (Uint8 *)&r, (Uint8 *)&g, (Uint8 *)&b); newData[(i * channels) + 0] = r; newData[(i * channels) + 1] = g; newData[(i * channels) + 2] = b; pixel_value = 0; } /* Build Mipmaps (builds different versions of the picture for distances - looks better) */ gluBuild2DMipmaps (GL_TEXTURE_2D, 3, pBitmap[0]->w, pBitmap[0]->h, GL_RGB, GL_UNSIGNED_BYTE, newData); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); delete [] newData; SDL_FreeSurface(pBitmap[0]); /* ------------painten */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(0, 0, 6, 0, 0, 0, 0, 1, 0); // Bind the texture stored at the zero index of g_Texture[] //glBindTexture(GL_TEXTURE_2D, g_Texture[0]); // Display a quad texture to the screen glBegin(GL_QUADS); // glTexCoord2f() takes the X and Y offset (or U and V) into the bitmap. // Then, the next point sent to be rendered attaches that part of the bitmap // to itself. The (U, V) coordinates range from (0, 0) being the top left corner // of the bitmap, to (1, 1) being the bottom left corner of the bitmap. // You can go above 1 but it just is wrapped around back to zero and repeats the texture. // Try setting the 1's to 2's and see what it does, then try setting them to 0.5's. // The higher the number, the more instances of the texture will appear on the square, // Where the lower the number, it stretches the incomplete texture over the surface of the square. // For every vertice we need a U V coordinate, as shown below. You might have to play // around with the values to make it texture correctly, otherwise it will be flipped, upside down, // or skewed. It also depends on where you are looking at it. We are looking down the -Z axis. // Display the top left vertice glTexCoord2f(0.0f, 1.0f); glVertex3f(-2.5, 2.5, 0); // Display the bottom left vertice glTexCoord2f(0.0f, 0.0f); glVertex3f(-2.5, -2.5, 0); // Display the bottom right vertice glTexCoord2f(1.0f, 0.0f); glVertex3f(2.5, -2.5, 0); // Display the top right vertice glTexCoord2f(1.0f, 1.0f); glVertex3f(2.5, 2.5, 0); glEnd(); SDL_GL_SwapBuffers(); glDisable (GL_TEXTURE_2D); glDeleteTextures (1, &textureArray[textureID]); SDL_Delay (1000); printf ("World::displayLoadScreen - end\n"); } void World::releaseLoadScreen () { printf ("World::releaseLoadScreen - start\n"); printf ("World::releaseLoadScreen - end\n"); } void World::load() { if(this->debugWorldNr != -1) { trackManager = TrackManager::getInstance(); 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: { this->nullParent = NullParent::getInstance (); this->nullParent->setName ("NullParent"); // create some path nodes this->pathnodes = new Vector[6]; this->pathnodes[0] = Vector(0, 0, 0); this->pathnodes[1] = Vector(1000, 0, 0); // this->pathnodes[2] = Vector(-100, 140, 0); // this->pathnodes[3] = Vector(0, 180, 0); // this->pathnodes[4] = Vector(100, 140, 0); // this->pathnodes[5] = Vector(100, 40, 0); // create the tracks this->tracklen = 2; this->track = new Track[2]; for( int i = 0; i < this->tracklen; i++) { this->track[i] = Track( i, (i+1)%this->tracklen, &this->pathnodes[i], &this->pathnodes[(i+1)%this->tracklen]); } // !\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 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); this->localCamera->setName ("camera"); this->getCamera()->bind (myPlayer); this->localPlayer->addChild (this->localCamera); Vector* es = new Vector (50, 2, 0); Quaternion* qs = new Quaternion (); WorldEntity* env = new Environment(); env->setName ("env"); this->spawn(env, es, qs); break; } case DEBUG_WORLD_1: { /* this->testCurve = new UPointCurve(); this->testCurve->addNode(Vector( 0, 0, 0)); this->testCurve->addNode(Vector(10, 0, 5)); this->testCurve->addNode(Vector(20, -5,-5)); this->testCurve->addNode(Vector(30, 5, 10)); this->testCurve->addNode(Vector(40, 0,-10)); this->testCurve->addNode(Vector(50, 0,-10)); */ this->nullParent = NullParent::getInstance (); this->nullParent->setName ("NullParent"); // create some path nodes this->pathnodes = new Vector[6]; this->pathnodes[0] = Vector(0, 0, 0); this->pathnodes[1] = Vector(20, 10, 10); this->pathnodes[2] = Vector(40, 0, 10); this->pathnodes[3] = Vector(60, 10, 0); this->pathnodes[4] = Vector(80, 20, 10); this->pathnodes[5] = Vector(30, 50, 0); // create the tracks this->tracklen = 6; this->track = new Track[6]; for( int i = 0; i < this->tracklen; i++) { this->track[i] = Track( i, (i+1)%this->tracklen, &this->pathnodes[i], &this->pathnodes[(i+1)%this->tracklen]); } // 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); this->localCamera->setName ("camera"); this->getCamera()->bind (myPlayer); this->localPlayer->addChild (this->localCamera); 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); glLoadIdentity(); glColor3f(1.0,0,0); glBegin(GL_QUADS); int sizeX = 100; int sizeZ = 80; float length = 1000; float width = 200; float widthX = float (length /sizeX); float widthZ = float (width /sizeZ); float height [sizeX][sizeZ]; Vector normal_vectors[sizeX][sizeZ]; for ( int i = 0; inextElement(); 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; entity = this->entities->enumerate(); while( entity != NULL ) { if( entity->bDraw ) entity->draw(); entity = this->entities->nextElement(); } // draw debug coord system glCallList (objectList); } /** \brief updates Placements and notifies entities when they left the world This runs trough all WorldEntities and maps Locations to Placements if they are bound, checks whether they left the level boundaries and calls appropriate functions. */ void World::update () { /* //List *l; WorldEntity* entity; Location* loc; Placement* plc; Uint32 t; // l = entities->enumerate(); entity = this->entities->enumerate(); while( entity != NULL ) { if( !entity->isFree() ) { loc = entity->getLocation(); plc = entity->getPlacement(); t = loc->part; if( t >= tracklen ) { printf("An entity is out of the game area\n"); entity->leftWorld (); } else { while( track[t].mapCoords( loc, plc) ) { track[t].postLeave (entity); if( loc->part >= tracklen ) { printf("An entity has left the game area\n"); entity->leftWorld (); break; } track[loc->part].postEnter (entity); } } } else { } entity = entities->nextElement(); } */ } /** \brief relays the passed time since the last frame to entities and Track parts \param deltaT: the time passed since the last frame in milliseconds */ void World::timeSlice (Uint32 deltaT) { //List *l; WorldEntity* entity; float seconds = deltaT / 1000.0; this->nullParent->update (seconds); //this->nullParent->processTick (seconds); entity = entities->enumerate(); while( entity != NULL) { entity->tick (seconds); entity = entities->nextElement(); } //for( int i = 0; i < tracklen; i++) track[i].tick (seconds); } /** \brief removes level data from memory */ void World::unload() { if( pathnodes) delete []pathnodes; if( track) delete []pathnodes; } /** \brief calls the correct mapping function to convert a given "look at"-Location to a Camera Placement */ void World::calcCameraPos (Location* loc, Placement* plc) { track[loc->part].mapCamera (loc, plc); } void World::setTrackLen(Uint32 len) { this->tracklen = len; } int World::getTrackLen() { return this->tracklen; } /** \brief function to put your own debug stuff into it. it can display informations about the current class/procedure */ void World::debug() { printf ("World::debug() - starting debug\n"); PNode* p1 = NullParent::getInstance (); PNode* p2 = new PNode (new Vector(2, 2, 2), p1); PNode* p3 = new PNode (new Vector(4, 4, 4), p1); PNode* p4 = new PNode (new Vector(6, 6, 6), p2); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p1->shiftCoor (new Vector(-1, -1, -1)); printf("World::debug() - shift\n"); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p1->update (1); printf ("World::debug() - update\n"); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p2->shiftCoor (new Vector(-1, -1, -1)); p1->update (2); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p2->setAbsCoor (new Vector(1,2,3)); p1->update (2); p1->debug (); p2->debug (); p3->debug (); p4->debug (); p1->destroy (); /* 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("World::mainLoop() - Entering main loop\n"); while( !this->bQuitOrxonox && !this->bQuitCurrentGame) /* \todo implement pause */ { // Network this->synchronize (); // Process input this->handleInput (); if( this->bQuitCurrentGame || this->bQuitOrxonox) { printf("World::mainLoop() - leaving loop earlier...\n"); break; } // Process time this->timeSlice (); // Process collision this->collision (); // 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("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::timeSlice () { Uint32 currentFrame = SDL_GetTicks(); if(!this->bPause) { Uint32 dt = currentFrame - this->lastFrame; if(dt > 0) { float fps = 1000/dt; printf("fps = %f\n", fps); } else { /* the frame-rate is limited to 100 frames per second, all other things are for nothing. */ printf("fps = 1000 - frame rate is adjusted\n"); SDL_Delay(10); dt = 10; } this->timeSlice (dt); this->update (); this->localCamera->timeSlice(dt); } this->lastFrame = currentFrame; } /** \brief compute collision detection */ void World::collision () { this->collide (); } /** \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 give back active camera this passes back the actualy active camera \todo ability to define more than one camera or camera-places */ Camera* World::getCamera() { return this->localCamera; } /** \brief add and spawn a new entity to this world \param entity to be added */ void World::spawn(WorldEntity* entity) { if( this->nullParent != NULL && entity->parent == NULL) this->nullParent->addChild (entity); this->entities->add (entity); entity->postSpawn (); } /** \brief add and spawn a new entity to this world \param entity to be added \param location where to add */ void World::spawn(WorldEntity* entity, Vector* absCoor, Quaternion* absDir) { entity->setAbsCoor (absCoor); entity->setAbsDir (absDir); if( this->nullParent != NULL && entity->parent == NULL) this->nullParent->addChild (entity); 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) { return false; } void World::swap (unsigned char &a, unsigned char &b) { unsigned char temp; temp = a; a = b; b = temp; }