/* render/draw.cc This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include #include #include "core/core.h" #include "model/model.h" #include "render/render.h" #include "render/textures.h" #include "render/draw.h" namespace render { size_t Stats::tris = 0; size_t Stats::quads = 0; void Stats::clear() { tris = 0; quads = 0; } math::Vector3f v0(1, -1, 1); math::Vector3f v1(1, 1, 1); math::Vector3f v2(-1, 1, 1); math::Vector3f v3(-1, -1, 1); math::Vector3f v4(1, -1, -1); math::Vector3f v5(1, 1, -1); math::Vector3f v6(-1, 1, -1); math::Vector3f v7(-1, -1, -1); const float drawdistance = 128.0f; const float drawfxdistance = 32.0f; float angle = 0; // function to test flags inline bool flag_is_set(unsigned int spawnflags, unsigned int flag) { return ((spawnflags & flag) == flag); } /* ----- Default Entity shapes ------------------------------------- */ void draw_sphere(math::Color const & color, float radius) { //gl::push(); gl::scale(radius, radius, radius); gl::color(color); size_t index = 0; size_t count = (model::SPHERESEGMENTS)*2; // draw body for (int j=0; j < (model::SPHERESEGMENTS-1)/2; j++) { glDrawArrays(gl::QuadStrip, index, count); index += count; Stats::quads += count/2-1; } //gl::pop(); } void draw_entity_sphere(core::Entity *entity) { if ((entity->type() == core::Entity::Globe) && !flag_is_set(entity->flags(), core::Entity::Bright)) { gl::disable(GL_LIGHT0); // disable camera light, level light only core::EntityGlobe *globe = (core::EntityGlobe *)entity; if (globe->render_texture) { Textures::bind(globe->render_texture); gl::enable(GL_TEXTURE_2D); } } draw_sphere(entity->color(), entity->radius()); if ((entity->type() == core::Entity::Globe) && !flag_is_set(entity->flags(), core::Entity::Bright)) { core::EntityGlobe *globe = (core::EntityGlobe *)entity; if (globe->render_texture) { gl::disable(GL_TEXTURE_2D); } gl::enable(GL_LIGHT0); } } void draw_entity_cube(core::Entity *entity) { float radius = entity->radius()/2; gl::scale(radius, radius, radius); gl::color(entity->color()); gl::begin(gl::Quads); // top gl::normal(0,0,1); gl::vertex(v0); gl::vertex(v1); gl::vertex(v2); gl::vertex(v3); // bottom gl::normal(0,0, -1); gl::vertex(v7); gl::vertex(v6); gl::vertex(v5); gl::vertex(v4); // sides gl::normal(1,0,0); gl::vertex(v1); gl::vertex(v0); gl::vertex(v4); gl::vertex(v5); gl::normal(-1,0,0); gl::vertex(v3); gl::vertex(v2); gl::vertex(v6); gl::vertex(v7); gl::normal(0,1,0); gl::vertex(v2); gl::vertex(v1); gl::vertex(v5); gl::vertex(v6); gl::normal(0,-1,0); gl::vertex(v0); gl::vertex(v3); gl::vertex(v7); gl::vertex(v4); gl::end(); } void draw_entity_axis(core::Entity *entity) { using namespace render; float r = entity->radius(); gl::begin(gl::Lines); gl::color(1.0f, 0.0f, 0.0f); gl::vertex(r,0.0f,0.0f); gl::color(entity->color()); gl::vertex(-r,0.0f,0.0f); gl::vertex(0.0f,r/2,0.0f); gl::vertex(0.0f,-r/2,0.0f); gl::vertex(0.0f,0.0f,r); gl::vertex(0.0f,0.0f,-r); gl::end(); } /* ----- Entity models --------------------------------------------- */ void draw_model_vertex(core::Entity *entity) { size_t count = entity->model()->vertex_structural(); if (entity->state()->detailvisible()) count += entity->model()->vertex_detail(); // draw model vertices if (count) { size_t index = entity->model()->first_vertex(); glDrawArrays(gl::Triangles, index, count); Stats::tris += count/3; } } void draw_model_lvertex(core::Entity *entity) { size_t count = entity->model()->lvertex_structural(); if (entity->state()->detailvisible()) count += entity->model()->lvertex_detail(); // draw model lvertices if (count) { gl::disable(GL_LIGHTING); size_t index = entity->model()->first_lvertex(); glDrawArrays(gl::Triangles, index, count); Stats::tris += count/3; gl::enable(GL_LIGHTING); } } void draw_model_evertex(core::Entity *entity) { size_t count = entity->model()->evertex_structural(); if (entity->state()->detailvisible()) count += entity->model()->evertex_detail(); // draw model evertices if (count) { size_t index = entity->model()->first_evertex(); render::gl::color(entity->color()); glDrawArrays(gl::Triangles, index, count); Stats::tris += count/3; } } /* ----- Render passes --------------------------------------------- */ /* calculate entity visibility */ void pass_prepare(float seconds) { // reset light state gl::disable(GL_LIGHT1); std::map::iterator it; for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; // load entity models and light flare textures if (!entity->model() && entity->modelname().size()) { entity->entity_model = model::Model::load(entity->modelname()); if (!entity->model()) { entity->entity_modelname.clear(); } else { // set entity radius to model radius entity->entity_radius = entity->entity_model->radius(); for (std::list::iterator lit = entity->model()->model_light.begin(); lit != entity->model()->model_light.end(); lit++) { model::Light *light = (*lit); // load flare texture // FIXME optimize std::stringstream flarename; flarename << "bitmaps/fx/flare" << std::setfill('0') << std::setw(2) << light->flare(); light->render_texture = Textures::load(flarename.str()); } for (std::list::iterator flit = entity->model()->model_flare.begin(); flit != entity->model()->model_flare.end(); flit++) { model::Flare *flare = (*flit); // load flare texture // FIXME optimize std::stringstream flarename; flarename << "bitmaps/fx/flare" << std::setfill('0') << std::setw(2) << flare->flare(); flare->render_texture = Textures::load(flarename.str()); } } } if (!entity->state()) { entity->entity_clientstate = new core::ClientState(entity); } entity->state()->state_visible = true; entity->state()->state_detailvisible = false; // calculate visibility for entities with models if (entity->model()) { entity->state()->state_visible = false; float dq = math::distancesquared(Camera::eye(), entity->location()); if (dq <= drawfxdistance*drawfxdistance*entity->model()->radius()) { // entites within drawing distance entity->state()->state_visible = true; entity->state()->state_detailvisible = true; } else if (dq <= drawdistance*drawdistance*entity->model()->radius()) { // entities within drawdistance entity->state()->state_visible = true; entity->state()->state_detailvisible = false; } } else { if (entity->type() == core::Entity::Globe) { core::EntityGlobe *globe = (core::EntityGlobe *) entity; if (flag_is_set(globe->flags(), core::Entity::Bright)) { // bright globes set level light GLfloat light_position[4]; GLfloat diffuse_light[4]; GLfloat ambient_light[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat specular_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; for (size_t i=0; i <3; i++) { light_position[i] = globe->location()[i]; diffuse_light[i] = globe->color()[i] * 0.4; } light_position[3] = 1.0f; diffuse_light[3] = 1.0f; glLightfv(GL_LIGHT1, GL_POSITION, light_position); glLightfv(GL_LIGHT1, GL_AMBIENT, ambient_light); glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_light); glLightfv(GL_LIGHT1, GL_SPECULAR, specular_light); gl::enable(GL_LIGHT1); } else { // load globe textures // FIXME optimize if (globe->texture().size()) { std::stringstream texname; texname << "textures/" << globe->texture(); globe->render_texture = Textures::load(texname.str()); } } } } } } /* Draw entities without model */ void draw_pass_default() { std::map::iterator it; for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; // draw entities without model if (!entity->model()) { gl::push(); gl::translate(entity->state()->location()); gl::multmatrix(entity->state()->axis()); if (flag_is_set(entity->flags(), core::Entity::Bright)) { gl::disable(GL_LIGHTING); } switch(entity->shape()) { case core::Entity::Sphere: draw_entity_sphere(entity); break; case core::Entity::Diamond: case core::Entity::Axis: draw_entity_axis(entity); break; case core::Entity::Cube: default: draw_entity_cube(entity); break; } if (flag_is_set(entity->flags(), core::Entity::Bright)) { gl::enable(GL_LIGHTING); } gl::pop(); } else if (r_bbox->value()) { // draw bounding box if requested model::Model *model = entity->model(); gl::color(entity->color()); gl::push(); gl::translate(entity->state()->location()); gl::multmatrix(entity->state()->axis()); // top gl::begin(gl::LineLoop); gl::vertex(model->model_maxbbox.x, model->model_maxbbox.y, model->model_maxbbox.z); gl::vertex(model->model_minbbox.x, model->model_maxbbox.y, model->model_maxbbox.z); gl::vertex(model->model_minbbox.x, model->model_minbbox.y, model->model_maxbbox.z); gl::vertex(model->model_maxbbox.x, model->model_minbbox.y, model->model_maxbbox.z); gl::end(); // bottom gl::begin(gl::LineLoop); gl::vertex(model->model_maxbbox.x, model->model_maxbbox.y, model->model_minbbox.z); gl::vertex(model->model_minbbox.x, model->model_maxbbox.y, model->model_minbbox.z); gl::vertex(model->model_minbbox.x, model->model_minbbox.y, model->model_minbbox.z); gl::vertex(model->model_maxbbox.x, model->model_minbbox.y, model->model_minbbox.z); gl::end(); gl::begin(gl::Lines); gl::vertex(model->model_maxbbox.x, model->model_maxbbox.y, model->model_maxbbox.z); gl::vertex(model->model_maxbbox.x, model->model_maxbbox.y, model->model_minbbox.z); gl::vertex(model->model_minbbox.x, model->model_maxbbox.y, model->model_maxbbox.z); gl::vertex(model->model_minbbox.x, model->model_maxbbox.y, model->model_minbbox.z); gl::vertex(model->model_minbbox.x, model->model_minbbox.y, model->model_maxbbox.z); gl::vertex(model->model_minbbox.x, model->model_minbbox.y, model->model_minbbox.z); gl::vertex(model->model_maxbbox.x, model->model_minbbox.y, model->model_maxbbox.z); gl::vertex(model->model_maxbbox.x, model->model_minbbox.y, model->model_minbbox.z); gl::end(); gl::pop(); } } } /* Draw model vertices*/ void draw_pass_model_vertex() { std::map::iterator it; for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; if (entity->model() && entity->state()->visible()) { gl::push(); gl::translate(entity->state()->location()); gl::multmatrix(entity->state()->axis()); draw_model_vertex(entity); draw_model_lvertex(entity); gl::pop(); } } } /* Draw entites with model evertices */ void draw_pass_model_evertex() { std::map::iterator it; for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; if (entity->model() && entity->state()->visible()) { gl::push(); gl::translate(entity->state()->location()); gl::multmatrix(entity->state()->axis()); draw_model_evertex(entity); gl::pop(); } } } /* draw model lights and engines */ void draw_pass_model_fx() { float t; size_t flare_texture = Textures::bind("bitmaps/fx/flare00"); size_t engine_texture = Textures::find("bitmaps/fx/flare01"); gl::enable(GL_TEXTURE_2D); gl::begin(gl::Quads); for (std::map::iterator it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; if (entity->model() && entity->state()->detailvisible()) { // draw model lights for (std::list::iterator lit = entity->model()->model_light.begin(); lit != entity->model()->model_light.end(); lit++) { // strobe frequency t = 1.0f; if ((*lit)->strobe()) t = (core::application()->time() + entity->state()->fuzz() - (*lit)->offset()) * (*lit)->frequency(); if (!(*lit)->strobe() || (( t - floorf(t)) <= (*lit)->time())) { math::Vector3f location = entity->state()->location() + (entity->state()->axis() * (*lit)->location()); float light_size = 0.0625 * (*lit)->radius(); if ((*lit)->render_texture != flare_texture) { gl::end(); flare_texture = Textures::bind((*lit)->render_texture); gl::begin(gl::Quads); } math::Color color; if ((*lit)->entity()) { color.assign(entity->color()); } else { color.assign((*lit)->color()); } color.a = 0.8; gl::color(color); glTexCoord2f(0,1); gl::vertex(location + (Camera::axis().up() - Camera::axis().left()) * light_size); glTexCoord2f(0,0); gl::vertex(location + (Camera::axis().up() + Camera::axis().left()) * light_size); glTexCoord2f(1,0); gl::vertex(location + (Camera::axis().up() * -1 + Camera::axis().left()) * light_size); glTexCoord2f(1,1); gl::vertex(location + (Camera::axis().up() * -1 - Camera::axis().left()) * light_size); Stats::quads++; } } // draw flares for (std::list::iterator flit = entity->model()->model_flare.begin(); flit != entity->model()->model_flare.end(); flit++) { model::Flare *flare = (*flit); // strobe frequency t = 1.0f; if (flare->strobe()) t = (core::application()->time() + entity->state()->fuzz() - flare->offset()) * flare->frequency(); if (!flare->strobe() || (( t - floorf(t)) <= flare->time())) { math::Axis flare_axis(entity->state()->axis()); if (flare->angle()) flare_axis.change_direction(flare->angle()); math::Vector3f location = entity->state()->location() + (entity->state()->axis() * flare->location()); float light_size = 0.0625 * flare->radius(); if (flare->render_texture != flare_texture) { gl::end(); flare_texture = Textures::bind(flare->render_texture); gl::begin(gl::Quads); } math::Color color; if (flare->entity()) { color.assign(entity->color()); } else { color.assign(flare->color()); } float a = dotproduct(flare_axis.forward(), Camera::axis().forward()); if (a < -0.1f) { color.a = -a - 0.1f; gl::color(color); glTexCoord2f(0,1); gl::vertex(location + (flare_axis.up() + flare_axis.left()) * light_size); glTexCoord2f(0,0); gl::vertex(location + (flare_axis.up() - flare_axis.left()) * light_size); glTexCoord2f(1,0); gl::vertex(location + (flare_axis.up() * -1 - flare_axis.left()) * light_size); glTexCoord2f(1,1); gl::vertex(location + (flare_axis.up() * -1 + flare_axis.left()) * light_size); Stats::quads++; } } } // draw model engines for Controlable entities if (entity->type() == core::Entity::Controlable && entity->model()->model_engine.size()) { if (flare_texture != engine_texture) { gl::end(); flare_texture = Textures::bind(engine_texture); gl::begin(gl::Quads); } float u = static_cast(entity)->thrust(); t = entity->state()->fuzz() + core::application()->time() * 4; t = t - floorf(t); if (t > 0.5) t = 1-t; t = 0.75f + t/2; for(std::list::iterator eit = entity->model()->model_engine.begin(); eit != entity->model()->model_engine.end(); eit++) { math::Vector3f location = entity->state()->location() + (entity->state()->axis() * (*eit)->location()); float engine_size = 0.0625 * (*eit)->radius() * t; math::Color color(1.0f, 0.0f, 0.0f, 0.9f * u); gl::color(color); glTexCoord2f(0,1); gl::vertex(location + (entity->state()->axis().up() - entity->state()->axis().left()) * engine_size); glTexCoord2f(0,0); gl::vertex(location + (entity->state()->axis().up() + entity->state()->axis().left()) * engine_size); glTexCoord2f(1,0); gl::vertex(location + (entity->state()->axis().up() * -1 + entity->state()->axis().left()) * engine_size); glTexCoord2f(1,1); gl::vertex(location + (entity->state()->axis().up() * -1 - entity->state()->axis().left()) * engine_size); Stats::quads++; } } } } gl::end(); gl::disable(GL_TEXTURE_2D); } void draw_pass_model_corona() { if (!(r_radius && r_radius->value())) return; for (std::map::iterator it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) { core::Entity *entity = (*it).second; if (entity->model() && entity->state() && entity->state()->visible()) { gl::push(); gl::translate(entity->state()->location()); math::Color color = entity->color(); color.a = 0.25f; draw_sphere(color, entity->model()->radius()); gl::pop(); } } } void draw_pass_spacegrid() { int gridsize = 32; float s = 1.0f / gridsize; float z = -4.0f; float dx = Camera::target().x - floorf(Camera::target().x); float dy = Camera::target().y - floorf(Camera::target().y); gl::push(); gl::translate(Camera::target()); gl::color(0,0, 1.0f); gl::normal(0, 0, 1.0f); gl::begin(gl::Lines); for (int i=-gridsize; i <= gridsize; i++) { gl::color(0,0, 0, 0); gl::vertex(i-dx, -gridsize-dy, z); gl::color(0,0, (gridsize-abs(i))*s, (gridsize-abs(i))*s); gl::vertex(i-dx, -dy, z ); gl::vertex(i-dx, -dy ,z ); gl::color(0,0, 0, 0); gl::vertex(i-dx, gridsize-dy, z); gl::vertex(-gridsize-dx, i-dy, z ); gl::color(0,0, (gridsize-abs(i))*s, (gridsize-abs(i))*s); gl::vertex(-dx, i-dy, z); gl::vertex(-dx, i-dy, z); gl::color(0,0, 0, 0); gl::vertex(gridsize-dx, i-dy, z); } gl::end(); gl::pop(); } /* ----- Main draw routine ----------------------------------------- */ void draw(float seconds) { Stats::clear(); // used for animations angle += 180.0f * seconds; if( angle > 360.0f ) { angle -= 360.0f; } Camera::draw(seconds); // draw the current camera transformation pass_prepare(seconds); gl::enable(GL_DEPTH_TEST); // enable depth buffer writing gl::enable(GL_CULL_FACE); // enable culling gl::enable(GL_COLOR_MATERIAL); // enable color tracking gl::enable(GL_LIGHTING); gl::disable(GL_BLEND); // disbable alpha blending for world polys gl::disable(GL_RESCALE_NORMAL); glVertexPointer(3, GL_FLOAT, 0, vertexarray->vertex()); glNormalPointer(GL_FLOAT, 0, vertexarray->normal()); glColorPointer(3, GL_FLOAT, 0, vertexarray->color()); glTexCoordPointer(3, GL_FLOAT, 0, vertexarray->texture()); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (r_wireframe && r_wireframe->value()) { glPolygonMode(GL_FRONT, GL_LINE); } else { glPolygonMode(GL_FRONT, GL_FILL); } draw_pass_model_vertex(); // draw entities with model glDisableClientState(GL_COLOR_ARRAY); gl::enable(GL_RESCALE_NORMAL); // rescale normals by the transformation matrix scale factor draw_pass_default(); // draw entities without model gl::disable(GL_RESCALE_NORMAL); draw_pass_model_evertex(); // draw entities with model, vertices with entity color gl::disable(GL_LIGHTING); gl::enable(GL_BLEND); draw_pass_spacegrid(); // draw the blue spacegrid draw_pass_model_fx(); // draw entity lights and engines gl::enable(GL_LIGHTING); gl::enable(GL_RESCALE_NORMAL); draw_pass_model_corona(); // draw entity radius glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); gl::disable(GL_RESCALE_NORMAL); gl::disable(GL_LIGHTING); gl::disable(GL_COLOR_MATERIAL); // disable color tracking gl::disable(GL_CULL_FACE); // disable culling gl::disable(GL_DEPTH_TEST); // disable depth buffer // GL_BLEND must be enabled for the GUI } }