/* 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/application.h" #include "core/gameinterface.h" #include "core/range.h" #include "model/fragment.h" #include "model/material.h" #include "model/model.h" #include "render/render.h" #include "render/textures.h" #include "render/draw.h" #include "render/dust.h" #include "render/gl.h" #include "math/functions.h" namespace render { size_t Stats::tris = 0; size_t Stats::quads = 0; size_t Stats::fragments = 0; void Stats::clear() { tris = 0; quads = 0; fragments = 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); core::Zone *zone = 0; float zone_light[4]; // locaton of the zone light math::Color zone_color; // color of the zone light bool has_zone_light = false; typedef std::map Globes; Globes globes_list; /* ---- Prepare the renderer state --------------------------------- */ void pass_prepare(float seconds) { using namespace model; // lighting settings for the default light GL_LIGHT0 GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat ambient_light[] = { 0.01f, 0.01f, 0.01f, 1.0f }; GLfloat diffuse_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat specular_light[] = { 0.2f, 0.2f, 0.2f, 1.0f }; for (size_t i = 0; i < 3; i++) { light_position[i] = Camera::eye()[i]; } glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_light); glLightfv(GL_LIGHT0, GL_SPECULAR, specular_light); // GL_LIGHT0 is always enabled gl::enable(GL_LIGHT0); // reset light state gl::disable(GL_LIGHT1); has_zone_light = false; zone_color.assign(1.0); // clear current list of globes globes_list.clear(); for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (!ext_render(entity)) { new RenderExt(entity); } entity->extension((size_t) core::Extension::Render)->frame(seconds); // globes if (entity->type() == core::Entity::Globe) { core::EntityGlobe *globe = static_cast(entity); // add the globe to the globes list if (globe->visible() && !ext_render(globe)->behind()) { globes_list[ext_render(globe)->distance()] = globe; } // add level lights if (globe->flag_is_set(core::Entity::Bright)) { // bright globes set level light 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++) { zone_light[i] = globe->location()[i]; zone_color[i] = globe->color()[i]; diffuse_light[i] = globe->color()[i] * 0.4; } zone_light[3] = 1.0f; diffuse_light[3] = 1.0f; glLightfv(GL_LIGHT1, GL_POSITION, zone_light); 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); has_zone_light = true; } } } } /* ----- Skybox ---------------------------------------------------- */ void draw_sphere_inside(math::Color const & color, float radius) { gl::scale(radius, radius, radius); gl::color(color); size_t index = (model::SPHERESEGMENTS) * (model::SPHERESEGMENTS - 1); 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; } } void draw_pass_sky() { if (!(r_sky && r_sky->value())) return; if (!core::localplayer()->zone()) return; if (!core::localplayer()->zone()->sky().size()) return; if (!core::localplayer()->zone()->sky_texture()) { std::string texture_name("textures/env/"); texture_name.append(core::localplayer()->zone()->sky()); core::localplayer()->zone()->set_sky_texture(Textures::load(texture_name)); if (!core::localplayer()->zone()->sky_texture()) { core::localplayer()->zone()->set_sky(""); return; } } Textures::bind(core::localplayer()->zone()->sky_texture()); gl::enable(GL_TEXTURE_2D); gl::push(); gl::translate(Camera::eye()); draw_sphere_inside(math::Color(), 1016.0f); gl::pop(); gl::disable(GL_TEXTURE_2D); } /* ---- Globes ----------------------------------------------------- */ void draw_sphere(math::Color const & color, float radius) { 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; } } void draw_globe(const core::EntityGlobe* globe) { /* Globes have to be rendered distance sorted, closest last. Globes behind farplane are rescaled and repositioned. */ math::Vector3f location(globe->location()); float radius = globe->radius(); if (globe->flag_is_set(core::Entity::Bright)) { // bright globe, render fullbright gl::disable(GL_LIGHTING); } else { // disable camera light, level light only gl::disable(GL_LIGHT0); } if (globe->render_texture) { // textured globe Textures::bind(globe->render_texture); gl::enable(GL_TEXTURE_2D); } if (ext_render(globe)->distance() > (FARPLANE - globe->radius())) { // globe is behind the far plane, make a fake size calculation location = Camera::eye() + (location - Camera::eye()) * ((FARPLANE - globe->radius()) / ext_render(globe)->distance()); radius *= (FARPLANE - globe->radius()) / (ext_render(globe)->distance()); gl::depthmask(GL_FALSE); if (has_zone_light) { // move zone light float fake_light[4]; for (size_t i = 0; i < 3; i++) { fake_light[i] = zone_light[i] + location[i] - globe->location()[i]; } fake_light[3] = 1.0f; glLightfv(GL_LIGHT1, GL_POSITION, fake_light); } } gl::push(); gl::translate(location); gl::multmatrix(globe->axis()); if (globe->rotationspeed()) { float angle = math::degrees360f(core::application()->time() * globe->rotationspeed()); gl::rotate(angle, math::Vector3f::Zaxis()); } draw_sphere(globe->color(), radius); gl::pop(); if (globe->flag_is_set(core::Entity::Bright)) { math::Vector3f v = globe->location() - Camera::eye(); v.normalize(); float a = dotproduct(v, Camera::axis().forward()); if (a > 0.1f) { // FIXME a corona is actually just a giant flare if (!globe->render_texture) { gl::enable(GL_TEXTURE_2D); } Textures::bind("textures/fx/corona"); /* if (ext_render(globe)->distance() <= farplane) { gl::depthmask(GL_FALSE); }*/ gl::disable(GL_DEPTH_TEST); math::Color color(globe->color()); color.a = a - 0.1f; gl::color(color); gl::enable(GL_BLEND); gl::begin(gl::Quads); glTexCoord2f(0, 1); gl::vertex(location + (Camera::axis().up() - Camera::axis().left()) * radius * 4.0f); glTexCoord2f(0, 0); gl::vertex(location + (Camera::axis().up() + Camera::axis().left()) * radius * 4.0f); glTexCoord2f(1, 0); gl::vertex(location + (Camera::axis().up() * -1 + Camera::axis().left()) * radius * 4.0f); glTexCoord2f(1, 1); gl::vertex(location + (Camera::axis().up() * -1 - Camera::axis().left()) * radius * 4.0f); gl::end(); Stats::quads++; gl::disable(GL_BLEND); /* if (ext_render(globe)->distance() <= farplane) { gl::depthmask(GL_TRUE); }*/ if (!globe->render_texture) { gl::disable(GL_TEXTURE_2D); } gl::enable(GL_DEPTH_TEST); } } if (ext_render(globe)->distance() > (FARPLANE - globe->radius())) { gl::depthmask(GL_TRUE); if (has_zone_light) { // restore zone light glLightfv(GL_LIGHT1, GL_POSITION, zone_light); } } if (globe->flag_is_set(core::Entity::Bright)) { gl::enable(GL_LIGHTING); } else { gl::enable(GL_LIGHT0); } if (globe->render_texture) { gl::disable(GL_TEXTURE_2D); } } void draw_pass_globes() { // draw globes first, closest last for (Globes::reverse_iterator rit = globes_list.rbegin(); rit != globes_list.rend(); rit++) { draw_globe((*rit).second); } } /* ---- Default entities ------------------------------------------ */ void draw_entity_sphere(const core::Entity* entity) { draw_sphere(entity->color(), entity->radius()); } void draw_entity_cube(const core::Entity* entity) { float radius = entity->radius(); 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_diamond(const core::Entity* entity) { float radius = entity->radius() / 2; /* ---- draw axis lines ---- */ gl::color(entity->color_second()); gl::begin(gl::Lines); gl::vertex(1.25f * radius, 0, 0); gl::vertex(2* radius, 0, 0); gl::vertex(0, 1.25f * radius, 0); gl::vertex(0, 2* radius, 0); gl::vertex(0, 0, 1.25f * radius); gl::vertex(0, 0, 2 * radius); gl::vertex(-1.25f * radius, 0, 0); gl::vertex(-2 * radius, 0, 0); gl::vertex(0, -1.25f * radius, 0); gl::vertex(0, -2 * radius, 0); gl::vertex(0, 0, -1.25f * radius); gl::vertex(0, 0, -2 * radius); gl::end(); /* ---- draw rotating body lines ---- */ float angle = (core::application()->time() + ext_render(entity)->fuzz()) * 45.0f; angle = angle - 360.0f * floorf(angle / 360.0f); gl::rotate(angle, math::Vector3f::Zaxis()); if (r_wireframe->value() == 0.0f) { glPolygonMode(GL_FRONT, GL_LINE); } gl::color(entity->color()); gl::begin(gl::TriangleFan); gl::normal(0, 0 , 1); gl::vertex(0, 0, radius); gl::normal(1, 0 , 0); gl::vertex(radius, 0.0f, 0.0f); gl::normal(0, 1, 0); gl::vertex(0.0f, radius, 0.0f); gl::normal(-1, 0 , 0); gl::vertex(-radius, 0.0f, 0.0f); gl::normal(0, -1, 0); gl::vertex(0.0f, -radius, 0.0f); gl::normal(1, 0 , 0); gl::vertex(radius, 0.0f, 0.0f); gl::end(); gl::begin(gl::TriangleFan); gl::normal(0, 0 , -1); gl::vertex(0, 0, -radius); gl::normal(1, 0, 0); gl::vertex(radius, 0.0f, 0.0f); gl::normal(0, -1, 0); gl::vertex(0.0f, -radius, 0.0f); gl::normal(-1, 0 , 0); gl::vertex(-radius, 0.0f, 0.0f); gl::normal(0, 1, 0); gl::vertex(0.0f, radius, 0.0f); gl::normal(1, 0 , 0); gl::vertex(radius, 0.0f, 0.0f); gl::end(); if (r_wireframe->value() == 0.0f) { glPolygonMode(GL_FRONT, GL_FILL); } } void draw_entity_axis(const core::Entity* entity) { float r = entity->radius(); gl::begin(gl::Lines); gl::color(entity->color_second()); 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(); } void draw_pass_default() { for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); ++it) { core::Entity *entity = (*it); if (!entity->model() && (entity->type() != core::Entity::Globe) && !entity->serverside() && !ext_render(entity)->behind()) { gl::push(); gl::translate(entity->location()); gl::multmatrix(entity->axis()); if (entity->flag_is_set(core::Entity::Bright)) { gl::disable(GL_LIGHTING); } switch (entity->shape()) { case core::Entity::Sphere: draw_entity_sphere(entity); break; case core::Entity::Diamond: draw_entity_diamond(entity); break; case core::Entity::Axis: draw_entity_axis(entity); break; case core::Entity::Cube: default: draw_entity_cube(entity); break; } if (entity->flag_is_set(core::Entity::Bright)) { gl::enable(GL_LIGHTING); } gl::pop(); } } } /* ---- Model Fragments -------------------------------------------- */ void draw_fragment(model::Fragment *fragment, bool draw_details) { size_t index = fragment->index(); size_t vertex_count = fragment->structural_size(); if (draw_details) vertex_count += fragment->detail_size(); switch (fragment->type()) { case model::Fragment::Triangles: glDrawArrays(gl::Triangles, index, vertex_count); Stats::tris += vertex_count / 3; break; case model::Fragment::Quads: glDrawArrays(gl::Quads, index, vertex_count); Stats::quads += vertex_count / 4; break; } if (r_normals->value()) { gl::begin(gl::Lines); gl::color(1.0f, 0.0f, 0.0f); const float s = 0.1f; const float *n = core::game()->vertexarray()->normal() + index * 3; const float *v = core::game()->vertexarray()->vertex() + index * 3; for (size_t i = 0; i < vertex_count; i++) { gl::normal(-n[i*3], -n[i*3 +1 ], -n[i*3 +2]); gl::vertex(v[i*3], v[i*3 +1 ], v[i*3 +2]); gl::normal(n[i*3], n[i*3 +1 ], n[i*3 +2]); gl::vertex(v[i*3] + n[i*3] * s, v[i*3 +1] + n[i*3 +1] * s, v[i*3 +2] + n[i*3+2] * s); } gl::end(); } Stats::fragments++; } void draw_model_fragments(model::Model *model, const math::Color & color_primary, const math::Color & color_secondary, const bool detail, const bool power, const float thrust) { // default material, lighted and geometry color const model::Material *material = 0; math::Color color(1.0f, 1.0f, 1.0f); // current drawing color bool use_light = true; // gl::disable(GL_LIGHTING) is set bool use_texture = false; // texturing bool use_env = false; // environment mapping //bool use_color_array = true; // glEnableClientState(GL_COLOR_ARRAY) is set // TODO this should probably be initialized somewhere else glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); for (model::Model::Groups::iterator git = model->groups().begin(); git != model->groups().end(); git++) { model::FragmentGroup *group = (*git); if (group->transform()) { gl::push(); gl::translate(group->location()); if (group->type() == model::FragmentGroup::Rotate) { float rotation_angle = math::degrees360f(core::application()->time() * group->speed()); gl::rotate(-rotation_angle, group->axis().forward()); } else { gl::multmatrix(group->axis()); } const float s = group->scale(); if (s) gl::scale(s, s, s); } for (model::FragmentGroup::iterator fit = group->begin(); fit != group->end(); fit++) { model::Fragment *fragment = (*fit); if (fragment->material() != material) { material = fragment->material(); if (material) { if (material->flags() & model::Material::Engine) { /* if (use_color_array) { glDisableClientState(GL_COLOR_ARRAY); use_color_array = false; } */ color.assign(model->enginecolor() * thrust); } else if (material->flags() & model::Material::Tertiary) { /* if (use_color_array) { use_color_array = false; glDisableClientState(GL_COLOR_ARRAY); } */ if ((material->flags() & model::Material::Tertiary) == model::Material::Tertiary) { for (size_t i = 0; i < 3; i++) color[i] = (color_primary[i] + color_secondary[i]) / 2; } else if ((material->flags() & model::Material::Secondary) == model::Material::Secondary) { color.assign(color_secondary); } if ((material->flags() & model::Material::Primary) == model::Material::Primary) { color.assign(color_primary); } color.r *= material->color().r; color.g *= material->color().g; color.b *= material->color().b; } else { /* if (!use_color_array) { glEnableClientState(GL_COLOR_ARRAY); use_color_array = true; } */ color.assign(material->color()); } if (power && (material->flags() & model::Material::Bright)) { if (use_light) { gl::disable(GL_LIGHTING); use_light = false; } } else if (power && (material->flags() & model::Material::Engine)) { if (use_light) { gl::disable(GL_LIGHTING); use_light = false; } } else { if (!use_light) { gl::enable(GL_LIGHTING); use_light = true; } } if (material->flags() & model::Material::Texture) { Textures::bind(material->texture_id()); if (!use_texture) { gl::enable(GL_TEXTURE_2D); use_texture = true; } } if (material->flags() & model::Material::Environment) { if (!(material->flags() & model::Material::Texture)) { // use sky as envmap if the material defines no texture if (core::localplayer()->zone()->sky_texture()) { Textures::bind(core::localplayer()->zone()->sky_texture()); gl::enable(GL_TEXTURE_2D); use_texture = true; } else if (use_texture) { gl::disable(GL_TEXTURE_2D); use_texture = false; } } if (!use_env) { // enable env mapping gl::enable(GL_TEXTURE_GEN_S); gl::enable(GL_TEXTURE_GEN_T); use_env = true; } } else { if (use_env) { // disable env mapping gl::disable(GL_TEXTURE_GEN_S); gl::disable(GL_TEXTURE_GEN_T); use_env = false; } } if (!(material->flags() & (model::Material::Texture + model::Material::Environment))) { if (use_texture) { gl::disable(GL_TEXTURE_2D); use_texture = false; } } } else { // material not found if (use_light) { // disable lighting gl::disable(GL_LIGHTING); use_light = false; } if (use_env) { // disable env mapping gl::disable(GL_TEXTURE_GEN_S); gl::disable(GL_TEXTURE_GEN_T); use_env = false; } if (use_texture) { gl::disable(GL_TEXTURE_2D); use_texture = false; } /* if (!use_color_array) { glEnableClientState(GL_COLOR_ARRAY); use_color_array = true; } */ color.assign(1.0f, 0.0f, 1.0f); } } gl::color(color); draw_fragment(fragment, detail); } if (group->transform()) { gl::pop(); } } if (!use_light) { gl::enable(GL_LIGHTING); } if (use_env) { // disable env mapping gl::disable(GL_TEXTURE_GEN_S); gl::disable(GL_TEXTURE_GEN_T); use_env = false; } if (use_texture) { gl::disable(GL_TEXTURE_2D); use_texture = false; } /* if (!use_color_array) { glEnableClientState(GL_COLOR_ARRAY); } */ } void draw_model_fragments(core::Entity *entity) { model::Model *model = entity->model(); if (!model) return; bool power = true; float thrust = 0.0f; if ((entity->type() == core::Entity::Dynamic) || (entity->type() == core::Entity::Controlable)) { int state = static_cast(entity)->state(); if ((state == core::Entity::NoPower) || (state == core::Entity::Destroyed)) { power = false; } else if (entity->type() == core::Entity::Controlable) { core::EntityControlable *ec = static_cast(entity); if ((ec->state() == core::Entity::Impulse) || (ec->state() == core::Entity::ImpulseInitiate)) { thrust = 1.0f; } else { thrust = ec->thrust(); } } } draw_model_fragments(model, entity->color(), entity->color_second(), ext_render(entity)->detailvisible(), power, thrust); } // draw bounding box void draw_model_bbox(model::Model *model) { // 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(); // body 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(); } // draw entity axis void draw_model_axis(const core::Entity *entity) { // axis const float r = entity->radius() * 1.5f; gl::begin(gl::Lines); gl::color(entity->color()); gl::vertex(-r, 0.0f, 0.0f); gl::vertex(r, 0.0f, 0.0f); gl::vertex(0.0f, -r, 0.0f); gl::vertex(0.0f, r, 0.0f); gl::vertex(0.0f, 0.0f, -r); gl::vertex(0.0f, 0.0f, r); const math::Axis worldaxis(entity->axis().transpose()); gl::color(1.0f, 0.0f, 0.0f); gl::vertex(worldaxis.forward() * -1 * r); gl::vertex(worldaxis.forward() * r); gl::vertex(worldaxis.left() * -1 * r); gl::vertex(worldaxis.left() * r); gl::vertex(worldaxis.up() * -1 * r); gl::vertex(worldaxis.up() * r); gl::end(); } void draw_pass_model_fragments() { for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (entity->model() && ext_render(entity)->visible() && !ext_render(entity)->behind()) { gl::push(); gl::translate(entity->location()); gl::multmatrix(entity->axis()); draw_model_fragments(entity); if (r_bbox->value()) { gl::color(entity->color()); draw_model_bbox(entity->model()); } if (r_axis->value()) { draw_model_axis(entity); } gl::pop(); } } } /* ---- Model FX --------------------------------------------------- */ void draw_pass_model_fx(float elapsed) { float t = 0.0f; float a = 0.0f; float light_size = 0.0f; bool power = true; float thrust = 0.0f; math::Vector3f location; math::Vector3f offset; math::Color color; math::Axis flare_axis; for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); power = true; if ((entity->type() == core::Entity::Controlable) || (entity->type() == core::Entity::Dynamic)) { int state = static_cast(entity)->state(); if ((state == core::Entity::NoPower) || (state == core::Entity::Destroyed)) { power = false; } } if (entity->model() && ext_render(entity)->detailvisible() && power) { // disable culling by default gl::disable(GL_CULL_FACE); model::Cull current_cull = model::CullNone; // default light texture size_t current_texture = Textures::bind("textures/fx/flare00"); gl::enable(GL_TEXTURE_2D); gl::begin(gl::Quads); // draw model lights for (model::Model::Lights::iterator lit = entity->model()->lights().begin(); lit != entity->model()->lights().end(); lit++) { model::Light *light = (*lit); // engine activated lights if (light->engine()) { if (entity->type() == core::Entity::Controlable) { core::EntityControlable *ec = static_cast(entity); if ((ec->state() == core::Entity::ImpulseInitiate) || (ec->state() == core::Entity::Impulse)) { thrust = 1.0f; } else { thrust = ec->thrust(); if (thrust < 0.001f) { continue; // next light } } } else { continue; // next light } } // strobe frequency if (light->strobe()) { t = (core::application()->time() + ext_render(entity)->fuzz() - light->offset()) * light->frequency(); if ((t - floorf(t)) > light->time()) { continue; // next light } } // default alpha is 0.8 a = 0.8f; if (light->entity()) { color.assign(entity->color()); } else if (light->engine()) { color.assign(entity->model()->enginecolor()); a *= thrust; } else { color.assign(light->color()); } color.a = a; location.assign(entity->location() + (entity->axis() * light->location())); light_size = 0.0625f * light->radius(); // track OpenGL state changes if (current_texture != light->texture()) { gl::end(); current_texture = Textures::bind(light->texture()); gl::begin(gl::Quads); } // draw the quad 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 (model::Model::Flares::iterator flit = entity->model()->flares().begin(); flit != entity->model()->flares().end(); flit++) { model::Flare *flare = (*flit); // engine activated flares if (flare->engine()) { if (entity->type() == core::Entity::Controlable) { core::EntityControlable *ec = static_cast(entity); if ((ec->state() == core::Entity::ImpulseInitiate) || (ec->state() == core::Entity::Impulse)) { thrust = 1.0f; } else { thrust = ec->thrust(); if (thrust < 0.001f) { continue; // next flare } } } else { continue; // next flare } } // strobe frequency if (flare->strobe()) { t = (core::application()->time() + ext_render(entity)->fuzz() - flare->offset()) * flare->frequency(); if ((t - floorf(t)) > flare->time()) { continue; // next flare } } // calulcate viewing angle factor flare_axis.assign(entity->axis() * flare->axis()); a = math::absf(dotproduct(flare_axis.forward(), Camera::axis().forward())); if (a < 0.01f) { continue; // next flare } // alpha decreases with viewing angle a *= 0.8f; if (flare->entity()) { color.assign(entity->color()); } else if (flare->engine()) { color.assign(entity->model()->enginecolor()); a *= thrust; } else { color.assign(flare->color()); } color.a = a; location.assign(entity->location() + (entity->axis() * flare->location())); light_size = 0.0625f * flare->radius(); // track OpenGL state changes if ((current_cull != flare->cull()) || (current_texture != flare->texture())) { gl::end(); if (current_texture != flare->texture()) { current_texture = Textures::bind(flare->texture()); } if (current_cull != flare->cull()) { if (flare->cull() == model::CullNone) { gl::disable(GL_CULL_FACE); current_cull = model::CullNone; } else { if (current_cull == model::CullNone) { gl::enable(GL_CULL_FACE); } if (flare->cull() == model::CullBack) { gl::cullface(GL_BACK); current_cull = model::CullBack; } else { gl::cullface(GL_FRONT); current_cull = model::CullFront; } } } gl::begin(gl::Quads); } // draw the quad 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++; } gl::end(); // draw particle systems if (r_particles->value() && ext_render(entity)->particles().size()) { for (RenderExt::ParticleSystems::iterator it = ext_render(entity)->particles().begin(); it != ext_render(entity)->particles().end(); it++) { ParticleSystem *particlesystem = (*it); if (current_cull != particlesystem->cull()) { if (particlesystem->cull() == model::CullNone) { gl::disable(GL_CULL_FACE); current_cull = model::CullNone; } else { if (current_cull == model::CullNone) { gl::enable(GL_CULL_FACE); } if (particlesystem->cull() == model::CullBack) { gl::cullface(GL_BACK); current_cull = model::CullBack; } else { gl::cullface(GL_FRONT); current_cull = model::CullFront; } } } particlesystem->draw(elapsed); } } } } gl::disable(GL_TEXTURE_2D); gl::cullface(GL_BACK); gl::enable(GL_CULL_FACE); } void draw_pass_model_radius() { for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (entity->model() && ext_render(entity)->visible()) { gl::push(); gl::translate(entity->location()); math::Color color = entity->color(); color.a = 0.25f; draw_sphere(color, entity->model()->radius()); gl::pop(); } } } void draw_pass_spacegrid() { if (!(r_grid && r_grid->value())) return; 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) { zone = core::localplayer()->zone(); // calculate client state pass_prepare(seconds); // enable wireframe mode if requested if (r_wireframe && r_wireframe->value()) { glPolygonMode(GL_FRONT, GL_LINE); } else { glPolygonMode(GL_FRONT, GL_FILL); } // set vertex array pointers glVertexPointer(3, GL_FLOAT, 0, core::game()->vertexarray()->vertex()); glNormalPointer(GL_FLOAT, 0, core::game()->vertexarray()->normal()); glTexCoordPointer(3, GL_FLOAT, 0, core::game()->vertexarray()->texture()); // glColorPointer(3, GL_FLOAT, sizeof(float) * (model::VERTEXSIZE - 3), core::game()->vertexarray()->color()); // enable vertex arrays glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); // glDisableClientState(GL_COLOR_ARRAY); gl::disable(GL_DEPTH_TEST); gl::depthmask(GL_FALSE); // disable depth buffer writing draw_pass_sky(); // draw the skysphere gl::depthmask(GL_TRUE); // enable writing to the depth buffer gl::enable(GL_DEPTH_TEST); gl::enable(GL_CULL_FACE); // enable culling gl::enable(GL_COLOR_MATERIAL); // enable color tracking if (r_normalize && r_normalize->value()) { // enable full normalization gl::enable(GL_NORMALIZE); } else { // enable rescaling of normals gl::enable(GL_RESCALE_NORMAL); } gl::enable(GL_LIGHTING); // enable lighting draw_pass_globes(); // draw globes draw_pass_default(); // draw entities without model draw_pass_model_fragments(); gl::disable(GL_LIGHTING); // disable lighting if (r_normalize && r_normalize->value()) { // disable full normalization gl::disable(GL_NORMALIZE); } else { // disable resaling of normals gl::disable(GL_RESCALE_NORMAL); } gl::enable(GL_BLEND); gl::depthmask(GL_FALSE); // disable depth buffer writing draw_pass_spacegrid(); // draw the blue spacegrid if (!core::localplayer()->view()) { Dust::draw(zone_color); // draw spacedust } // draw entity lights, flares and particles draw_pass_model_fx(seconds); // draw entity radius globe if (r_radius && r_radius->value()) { if (r_normalize && r_normalize->value()) { // enable full normalization gl::enable(GL_NORMALIZE); } else { // enable rescaling of normals gl::enable(GL_RESCALE_NORMAL); } gl::enable(GL_LIGHTING); draw_pass_model_radius(); gl::disable(GL_LIGHTING); if (r_normalize && r_normalize->value()) { // disable full normalization gl::disable(GL_NORMALIZE); } else { // disable resaling of normals gl::disable(GL_RESCALE_NORMAL); } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); gl::disable(GL_COLOR_MATERIAL); // disable color tracking gl::disable(GL_CULL_FACE); // disable culling gl::depthmask(GL_TRUE); // enable depth buffer writing gl::disable(GL_DEPTH_TEST); // disable depth buffer testing // GL_BLEND must be enabled for the GUI } // draw HUD target world space geometry, like dock indicators void draw_target(core::Entity *entity) { model::Model *model = entity->model(); if (!model) return; if (!model->docks().size()) return; float d = math::distance(core::localcontrol()->location(), entity->location()) - entity->radius() - core::localcontrol()->radius(); if (d > core::range::fxdistance) return; gl::enable(GL_DEPTH_TEST); gl::push(); gl::translate(entity->location()); gl::multmatrix(entity->axis()); gl::color(0, 1.0f, 1.0f, 1.0f); for (model::Model::Docks::iterator dit = model->docks().begin(); dit != model->docks().end(); dit++) { model::Dock *dock = (*dit); math::Vector3f maxbox(dock->location()); math::Vector3f minbox(dock->location()); for (size_t i = 0; i < 3; i++) { maxbox[i] += 0.025; minbox[i] -= 0.025; } // top gl::begin(gl::LineLoop); gl::vertex(maxbox.x(), maxbox.y(), maxbox.z()); gl::vertex(minbox.x(), maxbox.y(), maxbox.z()); gl::vertex(minbox.x(), minbox.y(), maxbox.z()); gl::vertex(maxbox.x(), minbox.y(), maxbox.z()); gl::end(); // bottom gl::begin(gl::LineLoop); gl::vertex(maxbox.x(), maxbox.y(), minbox.z()); gl::vertex(minbox.x(), maxbox.y(), minbox.z()); gl::vertex(minbox.x(), minbox.y(), minbox.z()); gl::vertex(maxbox.x(), minbox.y(), minbox.z()); gl::end(); gl::begin(gl::Lines); gl::vertex(maxbox.x(), maxbox.y(), maxbox.z()); gl::vertex(maxbox.x(), maxbox.y(), minbox.z()); gl::vertex(minbox.x(), maxbox.y(), maxbox.z()); gl::vertex(minbox.x(), maxbox.y(), minbox.z()); gl::vertex(minbox.x(), minbox.y(), maxbox.z()); gl::vertex(minbox.x(), minbox.y(), minbox.z()); gl::vertex(maxbox.x(), minbox.y(), maxbox.z()); gl::vertex(maxbox.x(), minbox.y(), minbox.z()); gl::end(); } gl::pop(); gl::disable(GL_DEPTH_TEST); } }