#include "video.h" #include "scene.h" #include "vector.h" #include "quadtree.h" #include "shader.h" #include #include #include #include #define GL_GLEXT_PROTOTYPES #include //#include #include //#include #include #include #include #include #include #include FTFont *font; Scene scene; struct hit_record { uint32_t hits; int32_t min_z; int32_t max_z; uint32_t name; }; // TODO: reimplement int _select(Quadtree& qt, int x, int y) { y = 600-y; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GLuint buf[64] = {0}; GLint hits, view[4] = {0}; uint32_t id = 0; glSelectBuffer(64, buf); glGetIntegerv(GL_VIEWPORT, view); glRenderMode(GL_SELECT); glInitNames(); //glPushName(0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(x, y, 1, 1, view); gluPerspective(45, (float)view[2] / (float)view[3], 1, 10000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); scene.lookat(); std::vector nodes; uint32_t index = 0; std::queue q; q.push(qt.root); while(!q.empty()) { Quadtree::QuadNode *node = q.front(); q.pop(); if(node->vertex_array) { glPushName(++index); /*glBegin(GL_QUADS); glVertex3f(node->x - node->width/2, glEnd();*/ glPopName(); node->draw(); } else for(int i = 0; i < 4; i++) q.push(node->children[i]); } glMatrixMode(GL_PROJECTION); glPopMatrix(); //glMatrixMode(GL_MODELVIEW); //glFlush(); hits = glRenderMode(GL_RENDER); //std::cout << "hits: " << hits << std::endl; for(int i = 0; i < hits; i++) { struct hit_record *hit = (struct hit_record*)(buf + i*sizeof(hit_record)); //std::cout << " hits: " << hit->hits << " min_z: " << hit->min_z << " max_z: " << hit->max_z << std::endl; if(hit->hits == 1 && hit->name > 0) id = hit->name; } glMatrixMode(GL_MODELVIEW); //std::cout << "id: " << id << std::endl; if(id > 0) { //return map[vectors[id-1]]; } //return Vector3::p(); //return id; return 0; } bool select(Quadtree& qt, int x, int y, float& px, float& py, float& pz) { GLint view[4] = {0}; GLdouble mvmatrix[16] = {0}, projmatrix[16] = {0}; glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); glGetIntegerv(GL_VIEWPORT, view); y = view[3] - y - 1; float z; glReadBuffer(GL_BACK); glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); GLdouble _px, _py, _pz; if(gluUnProject(x, y, z, mvmatrix, projmatrix, view, &_px, &_py, &_pz) == GLU_TRUE) { px = _px; py = _py; pz = _pz; return true; } return false; } int main(int argc, char **argv) { video::width = 1280; video::height = 720; video::init(); GLShaderProgram program; GLVertexShader terrain_vertex("terrain_vertex.glsl"); GLFragmentShader terrain_fragment("terrain_fragment.glsl"); program.attach(terrain_vertex); program.attach(terrain_fragment); program.link(); font = new FTTextureFont("font.ttf"); font->FaceSize(10); SDL_Surface *hm = IMG_Load("heightmap6.png"); unsigned char *pixels = (unsigned char*)hm->pixels; float *heightmap = new float[hm->w * hm->h]; for(int x = 0; x < hm->w; x++) { for(int y = 0; y < hm->h; y++) { heightmap[y*hm->w + x] = ((float)(pixels[y*hm->w + x]) / 256) * 20; } } int level = 3; Quadtree *qt = new Quadtree(hm->w, hm->h, heightmap, level); //Quadtree qt(qt_size, qt_size, (int)ceil(sqrt(qt_size))); GLuint grass_texture; { SDL_Surface *surface = IMG_Load("Grass0073_3_S.jpg"); SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w, surface->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); SDL_Rect area; area.x = area.y = 0; area.w = surface->w; area.h = surface->h; SDL_BlitSurface(surface, &area, image, &area); //glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glGenTextures(1, &grass_texture); glBindTexture(GL_TEXTURE_2D, grass_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if(image->format->Amask) { //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image->w, image->h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); } else { //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->w, image->h, 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->w, image->h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); } SDL_FreeSurface(surface); SDL_FreeSurface(image); } //SDL_WM_GrabInput(SDL_GRAB_ON); SDL_Event event; bool running = true; bool grid = false; bool gravity = true; SDL_WarpMouse(video::width/2, video::height/2); unsigned int last_time = SDL_GetTicks(); /*boost::timer t; double last_time = 0;*/ Vector3 selected; while(running) { unsigned int time = SDL_GetTicks(); //double time = t.elapsed(); //t.restart(); unsigned int steps = time - last_time + 1; //double steps = (time - last_time) * 1000; last_time = time; bool do_select = 0; int sx, sy; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: running = false; break; case SDL_KEYDOWN: switch(event.key.keysym.sym) { case SDLK_ESCAPE: running = false; break; case SDLK_g: grid = !grid; break; case SDLK_KP_PLUS: case SDLK_PLUS: qt->create_nodes(++level); level = qt->levels; break; case SDLK_KP_MINUS: case SDLK_MINUS: if(level > 1) { qt->create_nodes(--level); } break; case SDLK_SPACE: scene.yvel = .05; break; case SDLK_h: gravity = !gravity; break; default: break; } break; case SDL_MOUSEBUTTONUP: switch(event.button.button) { case SDL_BUTTON_LEFT: sx = event.button.x; sy = event.button.y; do_select = true; // TODO: reimplement selection break; case SDL_BUTTON_WHEELUP: case SDL_BUTTON_WHEELDOWN: // TODO: reimplement? break; } case SDL_MOUSEMOTION: if(event.motion.x == video::width/2 && event.motion.y == video::height/2) break; scene.yaw += (float)event.motion.xrel / 500; scene.pitch += (float)event.motion.yrel / 500; if(scene.yaw > 2*M_PI) scene.yaw -= 2*M_PI; else if(scene.yaw < 0) scene.yaw += 2*M_PI; if(scene.pitch > M_PI) scene.pitch = M_PI; else if(scene.pitch < 0) scene.pitch = 0; SDL_WarpMouse(video::width/2, video::height/2); break; } } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); video::persp(); unsigned char *keystate = SDL_GetKeyState(NULL); float forward = 0; float right = 0; bool moved = false; if(keystate[SDLK_w]) { moved = true; forward++; } if(keystate[SDLK_s]) { moved = true; forward--; } if(keystate[SDLK_a]) { moved = true; right--; } if(keystate[SDLK_d]) { moved = true; right++; } if(keystate[SDLK_q]) scene.pos.y -= 0.002*steps*(keystate[SDLK_LSHIFT]?10:1); if(keystate[SDLK_e]) scene.pos.y += 0.002*steps*(keystate[SDLK_LSHIFT]?10:1); if(moved && (forward || right)) scene.move(forward, right, steps*(keystate[SDLK_LSHIFT]?10:1)); std::string move_str; Quadtree::QuadNode *node = qt->find(scene.pos.x, scene.pos.z); if(node) { if(gravity) { float y = node->get_height(scene.pos.x, scene.pos.z); if(scene.pos.y > y && !keystate[SDLK_e]) scene.yvel -= 9.81 * steps / 85000; if(scene.yvel < -.5) scene.yvel = -.5; scene.pos.y += scene.yvel * steps; if(scene.pos.y < y) { scene.pos.y = y; scene.yvel = 0; } } move_str = (boost::format("%s %.2f,%.2f %.2fx%.2f %d") % scene.pos.str() % node->x % node->y % node->width % node->height % node->level).str(); } scene.lookat(); glEnable(GL_LIGHTING); const float light_pos[4] = {50, 100, 50, 1}; //const float light_pos[4] = {0, 1, 0, 0}; glLightfv(GL_LIGHT0, GL_POSITION, light_pos); if(!grid) { program.use(); glBindTexture(GL_TEXTURE_2D, grass_texture); glEnable(GL_TEXTURE_2D); glBindBuffer(GL_ARRAY_BUFFER, qt->vbo_object); glVertexPointer(3, GL_FLOAT, 0, NULL); glNormalPointer(GL_FLOAT, 0, (GLvoid*)(qt->vertices*3*sizeof(float))); glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)(qt->vertices*3*sizeof(float)*2)); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glDrawArrays(GL_TRIANGLES, 0, qt->vertices); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); glUseProgram(0); } else { std::queue q; q.push(qt->root); while(!q.empty()) { Quadtree::QuadNode *node = q.front(); q.pop(); if(node->vertex_array) { grid ? node->draw_grid() : node->draw(); } else for(int i = 0; i < 4; i++) q.push(node->children[i]); } } glDisable(GL_LIGHTING); float px, py, pz; if(do_select && select(*qt, sx, sy, px, py, pz)) { selected = Vector3(px, py, pz); } glColor3f(1, 0, 0); glBegin(GL_TRIANGLE_FAN); glVertex3f(selected.x, selected.y, selected.z); glVertex3f(selected.x+.5, selected.y+1, selected.z-.5); glVertex3f(selected.x+.5, selected.y+1, selected.z+.5); glVertex3f(selected.x-.5, selected.y+1, selected.z+.5); glVertex3f(selected.x-.5, selected.y+1, selected.z-.5); glVertex3f(selected.x+.5, selected.y+1, selected.z-.5); glEnd(); video::ortho(); float height = font->LineHeight(); glColor3f(1, 1, 1); glTranslatef(0, video::height-height, 0); font->Render((boost::format("%dx%d %d levels %d nodes tree creation time: %f steps: %d") % qt->width % qt->height % qt->levels % qt->nodes % qt->init_time % steps).str().c_str()); //glTranslatef(0, height, 0); //font->Render((boost::format("selected: %x") % selected).str().c_str()); glTranslatef(0, -height, 0); font->Render(move_str.c_str()); /*if(selected) { glTranslatef(0, height, 0); //font->Render((boost::format("(%s %s %s %s)") % selected->a->str() % selected->b->str() % selected->c->str() % selected->d->str()).str().c_str()); font->Render(selected->str().c_str()); }*/ SDL_GL_SwapBuffers(); usleep(1000); } video::free(); return 0; }