#include "scene.h" #include "video.h" #include #include #include "gl.h" #include #include #define inrange(a, b, c) ((a) >= (b) && (a) <= (c)) Scene::Scene() { running = true; grid = false; terrain = true; gravity = true; last_node = NULL; do_select = false; show_selection = false; dialog = false; /* setup shader programs */ GLVertexShader terrain_vertex("shaders/terrain_vertex.glsl"); GLFragmentShader terrain_fragment("shaders/terrain_fragment.glsl"); terrain_program.attach(terrain_vertex); terrain_program.attach(terrain_fragment); terrain_program.link(); terrain_program.use(); GLint tex1loc = glGetUniformLocation(terrain_program.get_program(), "tex[1]"); glUniform1i(tex1loc, 1); GLint tex2loc = glGetUniformLocation(terrain_program.get_program(), "tex[2]"); glUniform1i(tex2loc, 2); GLint markloc = glGetUniformLocation(terrain_program.get_program(), "marktex"); glUniform1i(markloc, 3); glUseProgram(0); /* load textures */ grass_texture = load_texture("textures/zooboing-366-grass.jpg"); rock_texture = load_texture("textures/zooboing-825-stone-modified.jpg"); soil_texture = load_texture("textures/zooboing-469-sand-modified.jpg"); marker_texture = load_texture("textures/cross.png"); /* load heightmap */ SDL_Surface *hm = IMG_Load("heightmap.png"); float *heightmap = new float[hm->w * hm->h]; for(int x = 0; x < hm->w; x++) { for(int y = 0; y < hm->h; y++) { Uint8 *p = (Uint8*)hm->pixels + y * hm->pitch + x * hm->format->BytesPerPixel; heightmap[y*hm->w + x] = ((float)(*p) / 256) * 20; } } int w = hm->w; int h = hm->h; SDL_FreeSurface(hm); qt = new Quadtree(w, h, heightmap); /* load font */ font = new FTTextureFont("fonts/VeraMono.ttf"); font->FaceSize(10); gui = new GUI(); } Scene::~Scene() { if(qt) delete qt; delete font; delete gui; } void Scene::lookat() { const float cam_height = 1.7; /* calculate cartesian coordinates for the center vector where yaw is Φ and pitch is θ * x = cos Φ sin θ * y = cos θ * z = sin Φ sin θ */ Vector3 center(sinf(pitch) * cosf(yaw), cosf(pitch), sinf(pitch) * sinf(yaw)); center += pos; center.y += cam_height; //Vector3 up(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch)); Vector3 up(-cosf(pitch) * cosf(yaw), sinf(pitch), -cosf(pitch) * sinf(yaw)); gluLookAt(pos.x, pos.y+cam_height, pos.z, center.x, center.y, center.z, up.x, up.y, up.z); } void Scene::move(float forward, float right, int steps) { Vector2 dir; dir.x += forward*cosf(yaw); dir.y += forward*sinf(yaw); dir.x += right*cosf(yaw+M_PI_2); dir.y += right*sinf(yaw+M_PI_2); // ensure that the vector length is 1.0 dir /= dir.length(); dir *= 0.005; dir *= steps; float x = pos.x + dir.x; //if(inrange(x, -2, 2)) pos.x = x; float z = pos.z + dir.y; //if(inrange(z, -2, 2)) pos.z = z; //pos.x += dir.x; //pos.z += dir.y; } bool Scene::select(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; } void Scene::update() { qt->update(pos.x, pos.z); } void Scene::events() { SDL_Event event; 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_t: terrain = !terrain; break; case SDLK_SPACE: yvel = .05; break; case SDLK_h: gravity = !gravity; break; case SDLK_u: update(); break; case SDLK_TAB: dialog = !dialog; default: break; } break; case SDL_MOUSEBUTTONDOWN: switch(event.button.button) { case SDL_BUTTON_LEFT: if(dialog) CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton); break; case SDL_BUTTON_RIGHT: if(dialog) CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton); break; } break; case SDL_MOUSEBUTTONUP: switch(event.button.button) { case SDL_BUTTON_LEFT: if(dialog) CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton); else { sx = event.button.x; sy = event.button.y; do_select = true; } break; case SDL_BUTTON_RIGHT: if(dialog) CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton); else show_selection = false; break; case SDL_BUTTON_WHEELUP: if(dialog) CEGUI::System::getSingleton().injectMouseWheelChange(1); else qt->raise(selected.x, selected.z, gui->radius_sb->getScrollPosition(), gui->focus_sb->getScrollPosition(), gui->strength_sb->getScrollPosition()); break; case SDL_BUTTON_WHEELDOWN: if(dialog) CEGUI::System::getSingleton().injectMouseWheelChange(-1); else qt->raise(selected.x, selected.z, gui->radius_sb->getScrollPosition(), gui->focus_sb->getScrollPosition(), -gui->strength_sb->getScrollPosition()); break; } break; case SDL_MOUSEMOTION: if(dialog) { CEGUI::System::getSingleton().injectMousePosition(event.motion.x, event.motion.y); break; } if(event.motion.x == video::width/2 && event.motion.y == video::height/2) break; yaw += (float)event.motion.xrel / 500; pitch += (float)event.motion.yrel / 500; if(yaw > 2*M_PI) yaw -= 2*M_PI; else if(yaw < 0) yaw += 2*M_PI; if(pitch > M_PI) pitch = M_PI; else if(pitch < 0) pitch = 0; SDL_WarpMouse(video::width/2, video::height/2); break; case SDL_VIDEORESIZE: CEGUI::System::getSingleton().notifyDisplaySizeChanged(CEGUI::Size(event.resize.w, event.resize.h)); break; } } } void Scene::render() { unsigned int time = SDL_GetTicks(); unsigned int steps = time - last_time + 1; last_time = time; CEGUI::System::getSingleton().injectTimePulse(0.001*(float)steps); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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]) pos.y -= 0.002*steps*(keystate[SDLK_LSHIFT]?10:1); if(keystate[SDLK_e]) pos.y += 0.002*steps*(keystate[SDLK_LSHIFT]?10:1); if(moved && (forward || right)) { move(forward, right, steps*(keystate[SDLK_LSHIFT]?10:1)); } std::string move_str; Quadtree::QuadNode *node = qt->find(pos.x, pos.z); if(node) { if(gravity) { float y = node->get_height(pos.x, pos.z); if(pos.y > y && !keystate[SDLK_e]) yvel -= 9.81 * steps / 85000; if(yvel < -.5) yvel = -.5; pos.y += yvel * steps; if(pos.y < y) { pos.y = y; yvel = 0; } } move_str = (boost::format("%s n: %.2f,%.2f %.2fx%.2f c: %d,%d %dx%d") % pos.str() % node->x % node->y % node->width % node->height % node->chunk->x % node->chunk->y % node->chunk->width % node->chunk->height).str(); if(last_node != node) { last_node = node; update(); } } 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); unsigned int chunks_rendered = 0; if(terrain) { terrain_program.use(); GLint show_sel = glGetUniformLocation(terrain_program.get_program(), "show_sel"); glUniform1i(show_sel, show_selection ? 1 : 0); if(show_selection) { GLint selpos = glGetUniformLocation(terrain_program.get_program(), "selpos"); glUniform3f(selpos, selected.x, selected.y, selected.z); } glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, grass_texture); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, rock_texture); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, soil_texture); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, marker_texture); std::queue q; q.push(qt->root); while(!q.empty()) { Quadtree::QuadChunk *chunk = q.front(); q.pop(); if(!chunk->nodes) { for(int i = 0; i < 4; i++) q.push(chunk->children[i]); continue; } else if(!chunk->vbo_object) continue; chunks_rendered++; glBindBuffer(GL_ARRAY_BUFFER, chunk->vbo_object); glVertexPointer(3, GL_FLOAT, 0, NULL); glNormalPointer(GL_FLOAT, 0, (GLvoid*)(chunk->vertices*3*sizeof(float))); glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)(chunk->vertices*3*sizeof(float)*2)); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glDrawArrays(GL_TRIANGLES, 0, chunk->vertices); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } glBindBuffer(GL_ARRAY_BUFFER, 0); glDisable(GL_TEXTURE_2D); // set active to texture0 to avoid breaking the texture font glActiveTexture(GL_TEXTURE0); glUseProgram(0); } if(grid) { if(terrain) glColor3f(0, 0, 0); else glColor3f(1, 1, 1); std::queue q; q.push(qt->root); while(!q.empty()) { Quadtree::QuadChunk *chunk = q.front(); q.pop(); if(!chunk->nodes) { for(int i = 0; i < 4; i++) q.push(chunk->children[i]); continue; } else if(chunk->vbo_object) { for(unsigned int i = 0; i < chunk->node_count; i++) chunk->nodes[i]->draw_grid(); } } } //glDisable(GL_LIGHTING); float px, py, pz; if(do_select && select(sx, sy, px, py, pz)) { do_select = false; selected = Vector3(px, py, pz); show_selection = true; } video::ortho(); float height = font->LineHeight(); glColor3f(1, 1, 1); glTranslatef(0, video::height-height, 0); font->Render((boost::format("%dx%d chunks: %d gravity: %d steps: %d") % qt->width % qt->height % chunks_rendered % gravity % steps).str().c_str()); //font->Render((boost::format("%dx%d %d levels %d nodes tree creation time: %f steps: %d update: %d") // % scene.qt->width % scene.qt->height % scene.qt->levels % scene.qt->nodes % scene.qt->init_time % steps % scene.qt->thread_running).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()); glTranslatef(0, -height, 0); font->Render(selected.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()); }*/ if(dialog) gui->render(); SDL_GL_SwapBuffers(); } GLuint Scene::load_texture(const char *filename) { GLuint texture; SDL_Surface *surface = IMG_Load(filename); 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, &texture); glBindTexture(GL_TEXTURE_2D, 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) { gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image->w, image->h, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); } else { gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image->w, image->h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); } SDL_FreeSurface(surface); SDL_FreeSurface(image); return texture; }