/* 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/entity.h" #include "core/entityglobe.h" #include "core/gameinterface.h" #include "core/range.h" #include "math/functions.h" #include "model/fragment.h" #include "model/material.h" #include "model/model.h" #include "render/debugdrawer.h" #include "render/draw.h" #include "render/dust.h" #include "render/light.h" #include "render/lightenvironment.h" #include "render/render.h" #include "render/textures.h" #include "render/gl.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; bool draw_particles = true; bool draw_lights = true; typedef std::map Globes; Globes globes_list; LightEnvironment lightenv_zone; /* ---- Prepare the renderer state --------------------------------- */ void pass_prepare(float seconds) { using namespace model; // render settings for this pass_prepare draw_lights = true; if (r_lights && (r_lights->value() <= 0.0f)) { draw_lights = false; } draw_particles = true; if (r_particles && (r_particles->value() <= 0.0f)) { draw_particles = false; } // initialize lights lightenv_zone.clear(); // clear current list of globes globes_list.clear(); // zone ambient light GLfloat zone_ambient[4]; for (size_t i = 0; i < 3; i++) { zone_ambient[i] = core::localplayer()->zone()->ambient_color()[i]; } zone_ambient[3] = 1.0f; gl::lightmodel(GL_LIGHT_MODEL_AMBIENT, zone_ambient); // zone light sources 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 zone lights if (globe->has_flag(core::Entity::Bright)) { float zone_light_brightness = math::max(globe->color().r, math::max(globe->color().g, globe->color().b)); Light *zone_light = new Light(globe->location(), globe->color()); zone_light->set_attenuation(zone_light_brightness * 1.5f, zone_light_brightness * 0.00001f, zone_light_brightness * 0.000000001f); lightenv_zone.add(zone_light); } } else { // this reloads entity models if required by r_restart if (!entity->model() && entity->modelname().size()) { entity->set_model(model::Model::load(entity->modelname())); } } } // zone lights are draw in world space coordinates lightenv_zone.draw(); lightenv_zone.enable(); } /* ----- Skybox ---------------------------------------------------- */ /* * To use quake3 skyboxes: * switch left and right images * rotate top image 90 degrees clockwise * rotate bottom image 90 degrees counter-clockwise * */ void draw_pass_sky() { if (!(r_sky && r_sky->value())) return; if (!core::localplayer()->zone()) return; if (core::localplayer()->zone()->sky().size()) { Textures::load_cubemap("textures/sky/" + core::localplayer()->zone()->sky()); } else { return; } gl::enable(GL_TEXTURE_CUBE_MAP); gl::push(); gl::translate(Camera::eye()); gl::color(1.0f, 1.0f, 1.0f, 1.0f); const float r = 128.0f; gl::begin((r_wireframe && r_wireframe->value()) ? gl::LineLoop : gl::Quads); // front gl::texcoord(1, 1, 1); gl::vertex(r, r, r); gl::texcoord(1, -1, 1); gl::vertex(r, -r, r); gl::texcoord(1, -1, -1); gl::vertex(r, -r, -r); gl::texcoord(1, 1, -1); gl::vertex(r, r, -r); // right gl::texcoord(1, -1, 1); gl::vertex(r, -r, r); gl::texcoord(-1, -1, 1); gl::vertex(-r, -r, r); gl::texcoord(-1, -1, -1); gl::vertex(-r, -r, -r); gl::texcoord(1, -1, -1); gl::vertex(r, -r, -r); // back gl::texcoord(-1, -1, 1); gl::vertex(-r, -r, r); gl::texcoord(-1, 1, 1); gl::vertex(-r, r, r); gl::texcoord(-1, 1, -1); gl::vertex(-r, r, -r); gl::texcoord(-1, -1, -1); gl::vertex(-r, -r, -r); // left gl::texcoord(-1, 1, 1); gl::vertex(-r, r, r); gl::texcoord(1, 1, 1); gl::vertex(r, r, r); gl::texcoord(1, 1, -1); gl::vertex(r, r, -r); gl::texcoord(-1, 1, -1); gl::vertex(-r, r, -r); // up gl::texcoord(-1, 1, 1); gl::vertex(-r, r, r); gl::texcoord(-1, -1, 1); gl::vertex(-r, -r, r); gl::texcoord(1, -1, 1); gl::vertex(r, -r, r); gl::texcoord(1, 1, 1); gl::vertex(r, r, r); // down gl::texcoord(1, 1, -1); gl::vertex(r, r, -r); gl::texcoord(1, -1, -1); gl::vertex(r, -r, -r); gl::texcoord(-1, -1, -1); gl::vertex(-r, -r, -r); gl::texcoord(-1, 1, -1); gl::vertex(-r, r, -r); gl::end(); gl::disable(GL_TEXTURE_CUBE_MAP); gl::pop(); Stats::quads += 6; } /* ---- Draw a wireframe box --------------------------------------- */ void draw_box(const math::Vector3f & minbox, const math::Vector3f & maxbox) { // 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(); // body 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(); } /* ---- Globes ----------------------------------------------------- */ void draw_sphere(const math::Color & 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_corona(const math::Vector3f location, const math::Color & color, const float radius, const size_t corona_id) { // draw the globe's corona if (corona_id) { math::Vector3f v = location - Camera::eye(); v.normalize(); float a = dotproduct(v, Camera::axis().forward()); if (a > 0.1f) { gl::enable(GL_BLEND); gl::disable(GL_DEPTH_TEST); gl::depthmask(GL_FALSE); // disable depth buffer writes gl::enable(GL_TEXTURE_2D); Textures::bind(corona_id); math::Color drawcolor(color); drawcolor.a = a - 0.1f; gl::color(drawcolor); gl::begin(gl::Quads); gl::texcoord(0, 1); gl::vertex((Camera::axis().up() - Camera::axis().left()) * radius * 4.0f); gl::texcoord(0, 0); gl::vertex((Camera::axis().up() + Camera::axis().left()) * radius * 4.0f); gl::texcoord(1, 0); gl::vertex((Camera::axis().up() * -1 + Camera::axis().left()) * radius * 4.0f); gl::texcoord(1, 1); gl::vertex((Camera::axis().up() * -1 - Camera::axis().left()) * radius * 4.0f); gl::end(); Stats::quads++; gl::disable(GL_TEXTURE_2D); gl::enable(GL_DEPTH_TEST); gl::depthmask(GL_TRUE); // enable depth buffer writes gl::disable(GL_BLEND); } } } void draw_globe_rings(const math::Color & color, const size_t rings_id) { gl::color(color); gl::enable(GL_BLEND); //gl::disable(GL_CULL_FACE); gl::depthmask(GL_FALSE); // disable depth buffer writes Textures::bind(rings_id); gl::begin(gl::Quads); // top gl::normal(0.0f, 0.0f, 1.0f); gl::texcoord(0, 0); gl::vertex(-2.0f, -2.0f, 0.0f); gl::normal(0.0f, 0.0f, 1.0f); gl::texcoord(1, 0); gl::vertex(2.0f, -2.0f, 0.0f); gl::normal(0.0f, 0.0f, 1.0f); gl::texcoord(1, 1); gl::vertex(2.0f, 2.0f, 0.0f); gl::normal(0.0f, 0.0f, 1.0f); gl::texcoord(0, 1); gl::vertex(-2.0f, 2.0f, 0.0f); //bottom gl::normal(0.0f, 0.0f, -1.0f); gl::texcoord(0, 1); gl::vertex(-2.0f, 2.0f, 0.0f); gl::normal(0.0f, 0.0f, -1.0f); gl::texcoord(1, 1); gl::vertex(2.0f, 2.0f, 0.0f); gl::normal(0.0f, 0.0f, -1.0f); gl::texcoord(1, 0); gl::vertex(2.0f, -2.0f, 0.0f); gl::normal(0.0f, 0.0f, -1.0f); gl::texcoord(0, 0); gl::vertex(-2.0f, -2.0f, 0.0f); gl::end(); gl::depthmask(GL_TRUE); // enable depth buffer writes //gl::enable(GL_CULL_FACE); gl::disable(GL_BLEND); Stats::quads += 2; } void draw_pass_globes() { // FIXME is this ever reset ? GLfloat globe_specular[] = { 0.25f, 0.25f, 0.25f, 1.0f }; gl::material(GL_FRONT, GL_SPECULAR, globe_specular); // Globes have to be rendered distance sorted, closest last. // Globes behind farplane are rescaled and repositioned. for (Globes::reverse_iterator rit = globes_list.rbegin(); rit != globes_list.rend(); rit++) { const core::EntityGlobe *globe = (*rit).second; math::Vector3f location(globe->location()); float radius = globe->radius(); 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); lightenv_zone.draw(location - globe->location()); } gl::push(); gl::translate(location); if (globe->has_flag(core::Entity::Bright)) { gl::disable(GL_LIGHTING); if (globe->corona_id()) { // draw globe corona // corona is rendered in camera space draw_globe_corona(location, globe->color(), radius, globe->corona_id()); } } if (globe->texture_id()) { // textured globe Textures::bind(globe->texture_id()); gl::enable(GL_TEXTURE_2D); } 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); if (globe->rings_id()) { if (!globe->texture_id()) { gl::enable(GL_TEXTURE_2D); } draw_globe_rings(globe->color(), globe->rings_id()); } gl::pop(); if (globe->texture_id() || globe->rings_id()) { gl::disable(GL_TEXTURE_2D); } if (globe->has_flag(core::Entity::Bright)) { gl::enable(GL_LIGHTING); } if (ext_render(globe)->distance() > (FARPLANE - globe->radius())) { gl::depthmask(GL_TRUE); lightenv_zone.draw(); } } } /* ---- Default entities ------------------------------------------ */ void draw_entity_sphere(const core::Entity* entity) { draw_sphere(entity->color(), entity->radius()); } void draw_entity_cube(const core::Entity* entity) { const 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) { const float radius = entity->radius() / 2.0f; /* ---- 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->has_flag(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->has_flag(core::Entity::Bright)) { gl::enable(GL_LIGHTING); } gl::pop(); } } } /* ---- Model Fragments -------------------------------------------- */ void draw_fragment_normals(const 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(); gl::begin(gl::Lines); const float s = 0.05f; for (size_t i = 0; i < vertex_count; i++) { const float *n = &core::game()->vertexarray()->ptr()[(index+i) * 8 + 2]; const float *v = &core::game()->vertexarray()->ptr()[(index+i) * 8 + 5]; //gl::normal(-n[0], -n[1], -n[2]); gl::vertex(v[0], v[1], v[2]); //gl::normal(n[0], n[1], n[2]); gl::vertex(v[0] + n[0] * s, v[1] + n[1] * s, v[2] + n[2] * s); } gl::end(); } void draw_fragment(const 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.01f; for (size_t i = 0; i < vertex_count; i++) { const float *n = &core::game()->vertexarray()->ptr()[(index+i) * 8 + 2]; const float *v = &core::game()->vertexarray()->ptr()[(index+i) * 8 + 5]; gl::normal(-n[0], -n[1], -n[2]); gl::vertex(v[0], v[1], v[2]); gl::normal(n[0], n[1], n[2]); gl::vertex(v[0] + n[0] * s, v[1] + n[1] * s, v[2] + n[2] * s); } gl::end(); } Stats::fragments++; } void draw_model_fragments(model::Model *model, const math::Color & color_primary, const math::Color & color_secondary, const float enginetime, const bool detail, const bool power, const float thrust) { State::set_color(color_primary); State::set_color_second(color_secondary); State::set_color_engine(model->enginecolor() * thrust); State::set_power(power); for (model::Model::Groups::const_iterator git = model->groups().begin(); git != model->groups().end(); git++) { const model::FragmentGroup *group = (*git); gl::push(); gl::translate(group->location()); gl::multmatrix(group->axis()); const float s = group->scale(); if (s) gl::scale(s, s, s); if (group->type() == model::FragmentGroup::Rotate) { const float rotation_angle = math::degrees360f((group->engine() ? enginetime : core::game()->time()) * group->speed()); gl::rotate(-rotation_angle, group->movement()); } else if (group->type() == model::FragmentGroup::Move ) { const float freq = group->speed() / group->distance(); math::Vector3f translation(group->movement() * group->distance()); translation *= sinf((group->engine() ? enginetime : core::game()->time()) * M_PI * freq) * 0.5f + 0.5f; gl::translate(translation); } for (model::FragmentGroup::Fragments::const_iterator fit = group->fragments().begin(); fit != group->fragments().end(); fit++) { const model::Fragment *fragment = (*fit); for (model::Material::Layers::const_iterator lit = fragment->material()->layers().begin(); lit != fragment->material()->layers().end(); ++lit) { State::use_material_layer(fragment->material(), (*lit)); draw_fragment(fragment, detail); State::reset(); } if (r_normals->value()) { // force reset of material settings for the next fragment State::reset(); gl::color(0.75f, 0.0f, 0.0f); draw_fragment_normals(fragment, detail); State::reset(); } } gl::pop(); } } // draw entity axis void draw_model_axis(const core::Entity *entity) { // axis const float r = entity->model()->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() { /* * FIXME * fragmentgroups with movement tied to engine activity can not be synchronized with server-side collision * * For moving and rotating fragmentgroups, the enginetime must match * the time used by core:: to calculate the rotation or movement * of the asssociated collision models. * Currently, enginetime is client-side only, and there is no way * to sync core and render movement of fragmentgroups without sacrificing * the functionality of the FragmentGroup::engine() flag. */ 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()); const float modelscale = entity->radius() / entity->model()->radius(); gl::scale(modelscale, modelscale, modelscale); draw_model_fragments( entity->model(), entity->color(), entity->color_second(), ext_render(entity)->enginetime(), ext_render(entity)->detailvisible(), ext_render(entity)->power(), ext_render(entity)->thrust() ); if (r_slots && r_slots->value()) { draw_slots(entity); } if (r_bbox->value()) { gl::disable(GL_DEPTH_TEST); gl::color(entity->color().r * 0.5f, entity->color().g * 0.5f, entity->color().b * 0.5f, 0.25f); draw_box(entity->model()->box().min(), entity->model()->box().max()); gl::enable(GL_DEPTH_TEST); gl::color(entity->color()); draw_box(entity->model()->box().min(), entity->model()->box().max()); } if (r_axis->value()) { draw_model_axis(entity); } gl::pop(); } } } /* ---- Model FX --------------------------------------------------- */ void draw_model_lights(model::Model *model, const float scale, const math::Vector3f & entity_location, const math::Axis & entity_axis, const math::Color & entity_color, const math::Color & entity_color_second, const float thrust, const float fuzz) { float t = 0.0f; float a = 0.0f; float light_size = 0.0f; math::Vector3f location; math::Vector3f offset; math::Color color; math::Axis flare_axis; // disable culling by default gl::disable(GL_CULL_FACE); model::Cull current_cull = model::CullNone; size_t current_texture = Textures::bind("textures/fx/flare00"); gl::begin(gl::Quads); // draw model lights for (model::Model::Lights::iterator lit = model->lights().begin(); lit != model->lights().end(); lit++) { model::Light *light = (*lit); // engine activated lights if (light->engine()) { if (thrust < 0.001f) { continue; // next light } } // strobe frequency if (light->strobe()) { t = (core::application()->time() + fuzz - light->offset()) * light->frequency(); if ((t - floorf(t)) > light->time()) { continue; // next light } } // entity color overrides light color // light color overrides engine color if (light->entity()) { if (light->entity_second()) { // entity tertiary color for (size_t i = 0; i < 3; i++) { color[i] = (entity_color[i] + entity_color_second[i]) / 2; } } else { // entity primary color color.assign(entity_color); } } else if (light->entity_second()) { // entity secondary color color.assign(entity_color_second); } else if (light->has_color()) { // light color color.assign(light->color()); } else if (light->engine()) { // FIXME engine_color or thrust_activated color.assign(model->enginecolor()); } else { color.assign(light->color()); } // default alpha is 0.8, engine flag alters alpha a = 0.8f; if (light->engine()) { a *= thrust; } color.a = a; location.assign(entity_location + (entity_axis * light->location()) * scale); light_size = light->radius() * scale; // track OpenGL state changes if (current_texture != light->texture()) { gl::end(); current_texture = Textures::bind(light->texture()); gl::begin(gl::Quads); } // draw the quad // FIXME add random per-light rotation gl::color(color); gl::texcoord(1, 0); gl::vertex(location + (Camera::axis().up() - Camera::axis().left()) * light_size); gl::texcoord(0, 0); gl::vertex(location + (Camera::axis().up() + Camera::axis().left()) * light_size); gl::texcoord(0, 1); gl::vertex(location + (Camera::axis().up() * -1 + Camera::axis().left()) * light_size); gl::texcoord(1, 1); gl::vertex(location + (Camera::axis().up() * -1 - Camera::axis().left()) * light_size); Stats::quads++; } // draw flares for (model::Model::Flares::iterator flit = model->flares().begin(); flit != model->flares().end(); flit++) { model::Flare *flare = (*flit); // engine activated flares if (flare->engine()) { if (thrust < 0.001f) { continue; // next flare } } // strobe frequency if (flare->strobe()) { t = (core::application()->time() + 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.001f) { continue; // next flare } // entity color overrides flare color // flare color overrides engine color if (flare->entity()) { if (flare->entity_second()) { // entity tertiary color for (size_t i = 0; i < 3; i++) { color[i] = (entity_color[i] + entity_color_second[i]) / 2; } } else { // entity primary color color.assign(entity_color); } } else if (flare->entity_second()) { // entity secondary color color.assign(entity_color_second); } else if (flare->has_color()) { // flare color color.assign(flare->color()); } else if (flare->engine()) { // FIXME engine_color or thrust_activated color.assign(model->enginecolor()); } else { color.assign(flare->color()); } // default alpha is 0.8, engine flag alters alpha // alpha decreases with viewing angle a *= 0.8f; if (flare->engine()) { a *= thrust; } color.a = a; location.assign(entity_location + (entity_axis * flare->location()) * scale); light_size = flare->radius() * scale; // 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); gl::texcoord(1, 0); gl::vertex(location + (flare_axis.up() + flare_axis.left()) * light_size); gl::texcoord(0, 0); gl::vertex(location + (flare_axis.up() - flare_axis.left()) * light_size); gl::texcoord(0, 1); gl::vertex(location + (flare_axis.up() * -1 - flare_axis.left()) * light_size); gl::texcoord(1, 1); gl::vertex(location + (flare_axis.up() * -1 + flare_axis.left()) * light_size); Stats::quads++; } gl::end(); gl::cullface(GL_BACK); gl::enable(GL_CULL_FACE); } void draw_pass_model_fx(float elapsed) { // enable texturing gl::enable(GL_TEXTURE_2D); for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (entity->model() && ext_render(entity)->detailvisible()) { // draw lights and flares if (ext_render(entity)->power() && draw_lights) { const float modelscale = entity->radius() / entity->model()->radius(); draw_model_lights(entity->model(), modelscale, entity->location(), entity->axis(), entity->color(), entity->color_second(), ext_render(entity)->thrust(), ext_render(entity)->fuzz() ); } // draw particle systems if (draw_particles && ext_render(entity)->particles().size()) { for (RenderExt::ParticleSystems::iterator it = ext_render(entity)->particles().begin(); it != ext_render(entity)->particles().end(); ++it) { (*it)->draw(elapsed); } } } } // restore the default depth buffer comparison function gl::depthfunc(GL_LEQUAL); 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->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); gl::disable(GL_DEPTH_TEST); // disable depth testing gl::depthmask(GL_FALSE); // disable depth buffer writing gl::polygonmode(GL_FRONT, GL_FILL); draw_pass_sky(); // draw the skybox gl::depthmask(GL_TRUE); // enable writing to the depth buffer gl::depthfunc(GL_LEQUAL); // default depth buffer comparison function gl::enable(GL_DEPTH_TEST); // enable depth testing gl::enable(GL_CULL_FACE); // enable culling gl::enable(GL_COLOR_MATERIAL); // enable color tracking // enable wireframe mode if requested if (r_wireframe && r_wireframe->value()) { glPolygonMode(GL_FRONT, GL_LINE); } // check if the vertexarray needs re-uploading // set vertex array pointers if (State::has_vbo()) { gl::bindbuffer(GL_ARRAY_BUFFER, State::vbo()); if (core::game()->vertexarray()->dirty()) { gl::bufferdata(GL_ARRAY_BUFFER, sizeof(float) * core::game()->vertexarray()->index(), core::game()->vertexarray()->ptr(), GL_STATIC_DRAW); core::game()->vertexarray()->set_dirty(false); } // Interleaved format is GL_T2F_N3F_V3F // void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr ) gl::texcoordpointer(2, GL_FLOAT, 8 * sizeof(GLfloat), 0); // void glNormalPointer(GLenum type, GLsizei stride, const GLvoid *ptr) gl::normalpointer(GL_FLOAT, 8 * sizeof(GLfloat), (void*) (2 * sizeof(GLfloat))); // void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) gl::vertexpointer(3, GL_FLOAT, 8 * sizeof(GLfloat), (void*) (5 * sizeof(GLfloat))); } else { gl::interleavedarrays(GL_T2F_N3F_V3F, 0, core::game()->vertexarray()->ptr()); } // enable vertex arrays gl::enableclientstate(GL_TEXTURE_COORD_ARRAY); gl::enableclientstate(GL_NORMAL_ARRAY); gl::enableclientstate(GL_VERTEX_ARRAY); State::set_normalize(true); 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 State::set_normalize(false); gl::enable(GL_BLEND); gl::depthmask(GL_FALSE); // disable depth buffer writing draw_pass_spacegrid(); // draw the blue spacegrid if (!core::localplayer()->view()) { math::Color dust_color(core::localplayer()->zone()->ambient_color()); float s = math::max(math::max(dust_color[0], dust_color[1]), dust_color[2]); dust_color *= 0.8f / s; Dust::draw(dust_color); // draw spacedust } // draw entity lights, flares and particles if (draw_lights || draw_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); } } gl::disableclientstate(GL_VERTEX_ARRAY); gl::disableclientstate(GL_NORMAL_ARRAY); gl::disableclientstate(GL_TEXTURE_COORD_ARRAY); // draw physics if (r_physics && r_physics->value()) { if (zone->physics()) { if (!zone->physics()->getDebugDrawer()) zone->physics()->setDebugDrawer(&bullet_debugdrawer); // draw physics bodies in red gl::color(1.0f, 0.0f, 0.0f, 1.0f); gl::begin(gl::Lines); zone->physics()->debugDrawWorld(); gl::end(); } } gl::depthmask(GL_TRUE); // enable depth buffer writing gl::disable(GL_DEPTH_TEST); // disable depth buffer testing gl::disable(GL_CULL_FACE); // disable culling // GL_BLEND and GL_COLOR_MATERIAL must be enabled for the GUI //gl::disable(GL_COLOR_MATERIAL); // disable color tracking // clear current zone light environment lightenv_zone.disable(); lightenv_zone.clear(); // clear current list of globes globes_list.clear(); } // draw model slots: weapon slots and docking ports void draw_slots(const core::Entity *entity) { if (!entity->model()) { return; } if (entity->model()->docks().size()) { // draw docks: pass 1 - background with GL_DEPTH_TEST disabled gl::disable(GL_DEPTH_TEST); gl::color(0.0f, 0.5f, 0.5f, 0.25f); for (model::Model::Docks::iterator dit = entity->model()->docks().begin(); dit != entity->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; } draw_box(minbox, maxbox); } // draw docks: pass 2 - highlight with GL_DEPTH_TEST enabled gl::enable(GL_DEPTH_TEST); gl::color(0.0f, 1.0f, 1.0f, 1.0f); for (model::Model::Docks::iterator dit = entity->model()->docks().begin(); dit != entity->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; } draw_box(minbox, maxbox); } } if (entity->slots()) { // draw weapon slots: pass 1 - background with GL_DEPTH_TEST disabled gl::disable(GL_DEPTH_TEST); gl::color(0.5f, 0.0f, 0.0f, 0.25f); for(core::Slots::iterator it = entity->slots()->begin(); it != entity->slots()->end(); ++it) { core::Slot *slot = (*it); math::Vector3f maxbox(slot->location()); math::Vector3f minbox(slot->location()); for (size_t i = 0; i < 3; i++) { maxbox[i] += 0.025; minbox[i] -= 0.025; } draw_box(minbox, maxbox); } // draw weapon slots: pass 2 - highlight with GL_DEPTH_TEST enabled gl::enable(GL_DEPTH_TEST); gl::color(1.0f, 0.0f, 0.0f); for(core::Slots::iterator it = entity->slots()->begin(); it != entity->slots()->end(); ++it) { core::Slot *slot = (*it); math::Vector3f maxbox(slot->location()); math::Vector3f minbox(slot->location()); for (size_t i = 0; i < 3; i++) { maxbox[i] += 0.025; minbox[i] -= 0.025; } draw_box(minbox, maxbox); } } gl::enable(GL_DEPTH_TEST); } }