Logo Search packages:      
Sourcecode: k3d version File versions

mesh_instance.cpp

Go to the documentation of this file.
// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
            \author Timothy M. Shead (tshead@k-3d.com)
            \author Romain Behar (romainbehar@yahoo.com)
*/

#include <k3dsdk/classes.h>
#include <k3dsdk/bounded.h>
#include <k3dsdk/glutility.h>
#include <k3dsdk/imaterial.h>
#include <k3dsdk/measurement.h>
#include <k3dsdk/object.h>
#include <k3dsdk/persistence.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/mesh_filter.h>
#include <k3dsdk/module.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/renderman.h>
#include <k3dsdk/transformable.h>
#include <k3dsdk/viewport.h>

#include "blobby_polygonizer.h"
#include "subdiv_algorithms.h"

#include <limits>
#include <map>

namespace libk3dmesh
{

namespace detail
{

/// Calculates the center point (as an average) for a face
k3d::vector3 center_point(const k3d::face& Face)
{
      k3d::vector3 center(0, 0, 0);

      unsigned long count = 0;
      for(k3d::split_edge* edge = Face.first_edge; edge; edge = edge->face_clockwise)
            {
                  count++;
                  center += edge->vertex->position;
                  if(Face.first_edge == edge->face_clockwise)
                        break;
            }
      if(count)
            center /= count;

      return center;
}

/// Functor object for applying a transformation to a collection of points
00072 struct transform_points
{
      transform_points(const k3d::matrix4& Matrix) :
            matrix(Matrix)
      {
      }

      void operator()(k3d::point* const Point)
      {
            Point->position = matrix * Point->position;
      }

      const k3d::matrix4 matrix;
};

/////////////////////////////////////////////////////////////////////////////
// draw_blobby

/// Draws the components that make up a blobby (Visitor Design Pattern)
00091 class draw_blobby :
      public k3d::blobby::visitor
{
public:
      void visit_constant(k3d::blobby::constant&)
      {
      }

      void visit_ellipsoid(k3d::blobby::ellipsoid& Ellipsoid)
      {
            glBegin(GL_POINTS);
                  glVertex3dv(Ellipsoid.origin->position.n);
            glEnd();
      }

      void visit_segment(k3d::blobby::segment& Segment)
      {
            glLineWidth(Segment.radius);
            glBegin(GL_LINES);
                  glVertex3dv(Segment.start->position.n);
                  glVertex3dv(Segment.end->position.n);
            glEnd();
      }

      void visit_subtract(k3d::blobby::subtract& Subtract)
      {
            Subtract.subtrahend->accept(*this);
            Subtract.minuend->accept(*this);
      }

      void visit_divide(k3d::blobby::divide& Divide)
      {
            Divide.dividend->accept(*this);
            Divide.divisor->accept(*this);
      }

      void visit_add(k3d::blobby::add& Add)
      {
            Add.operands_accept(*this);
      }

      void visit_multiply(k3d::blobby::multiply& Multiply)
      {
            Multiply.operands_accept(*this);
      }

      void visit_min(k3d::blobby::min& Min)
      {
            Min.operands_accept(*this);
      }

      void visit_max(k3d::blobby::max& Max)
      {
            Max.operands_accept(*this);
      }
};

/////////////////////////////////////////////////////////////////////////////
// select_blobby

/// Draws the components that make up a blobby (Visitor Design Pattern)
00152 class select_blobby :
      public k3d::blobby::visitor
{
public:
      void visit_constant(k3d::blobby::constant&)
      {
      }

      void visit_ellipsoid(k3d::blobby::ellipsoid& Ellipsoid)
      {
            k3d::glPushName(&Ellipsoid);
            glBegin(GL_POINTS);
                  glVertex3dv(Ellipsoid.origin->position.n);
            glEnd();
            k3d::glPopName();
      }

      void visit_segment(k3d::blobby::segment& Segment)
      {
            k3d::glPushName(&Segment);
            glLineWidth(Segment.radius);
            glBegin(GL_LINES);
                  glVertex3dv(Segment.start->position.n);
                  glVertex3dv(Segment.end->position.n);
            glEnd();
            k3d::glPopName();
      }

      void visit_subtract(k3d::blobby::subtract& Subtract)
      {
            Subtract.subtrahend->accept(*this);
            Subtract.minuend->accept(*this);
      }

      void visit_divide(k3d::blobby::divide& Divide)
      {
            Divide.dividend->accept(*this);
            Divide.divisor->accept(*this);
      }

      void visit_add(k3d::blobby::add& Add)
      {
            Add.operands_accept(*this);
      }

      void visit_multiply(k3d::blobby::multiply& Multiply)
      {
            Multiply.operands_accept(*this);
      }

      void visit_min(k3d::blobby::min& Min)
      {
            Min.operands_accept(*this);
      }

      void visit_max(k3d::blobby::max& Max)
      {
            Max.operands_accept(*this);
      }
};

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// mesh_instance_implementation

class mesh_instance_implementation :
      public k3d::bounded<k3d::viewport::drawable<k3d::ri::renderable<k3d::mesh_filter<k3d::transformable<k3d::persistent<k3d::object> > > > > >
{
      typedef k3d::bounded<k3d::viewport::drawable<k3d::ri::renderable<k3d::mesh_filter<k3d::transformable<k3d::persistent<k3d::object> > > > > > base;

public:
      mesh_instance_implementation(k3d::idocument& Document) :
            base(Document),
            m_show_blobby_surface(k3d::init_name("blobby_surface") + k3d::init_description("Show blobbies surfaces [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_sds_cache(0),
            m_sds_levels(k3d::init_name("sds_levels") + k3d::init_description("Subdivision Levels [integer]") + k3d::init_constraint(k3d::constraint::minimum(1UL)) + k3d::init_document(Document) + k3d::init_value(3) + k3d::init_precision(0) + k3d::init_step_increment(1) + k3d::init_units(typeid(k3d::measurement::scalar))),
            m_color(k3d::init_name("color") + k3d::init_description("Color [color]") + k3d::init_value(k3d::color(0, 0, 0)) + k3d::init_document(Document))
      {
            register_property(m_show_blobby_surface);
            register_property(m_sds_levels);
            register_property(m_color);

            enable_serialization(k3d::persistence::proxy(m_show_blobby_surface));
            enable_serialization(k3d::persistence::proxy(m_sds_levels));
            enable_serialization(k3d::persistence::proxy(m_color));

            m_input_mesh.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_cache));
            m_sds_levels.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_cache));

            m_input_mesh.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_geometry));
            m_input_matrix.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_geometry));
            m_position.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_geometry));
            m_orientation.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_geometry));
            m_scale.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_reset_geometry));

            m_input_matrix.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));
            m_position.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));
            m_orientation.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));
            m_scale.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));
            m_show_blobby_surface.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));
            m_color.changed_signal().connect(SigC::slot(*this, &mesh_instance_implementation::async_redraw_all));

            m_output_mesh.need_data_signal().connect(SigC::slot(*this, &mesh_instance_implementation::on_create_geometry));
      }

      void on_reset_geometry()
      {
            m_output_mesh.reset();
      }

      k3d::mesh* on_create_geometry()
      {
            // Get the input geometry ...
            k3d::mesh* const input = m_input_mesh.property_value();
            if(!input)
                  return 0;

            // Create output geometry ...
            k3d::mesh* const output = new k3d::mesh();
            k3d::deep_copy(*input, *output);

            // Transform the points ...
            std::for_each(output->points.begin(), output->points.end(), detail::transform_points(matrix()));

            return output;
      }

      void on_reset_cache()
      {
            /** \todo When this method is called, it's time to reset our cache of tessellated / subdivision mesh drawing data */

            m_output_mesh.reset();

            // Schedule a display refresh ...
            k3d::viewport::redraw_all(document(), k3d::iviewport::ASYNCHRONOUS);

            // Clear blobby cache
            blobby_surfaces_vertices.clear();
            blobby_surfaces_normals.clear();
            blobby_surfaces_polygons.clear();
            blobby_cache_map.clear();

            // Clear SDS cache
            delete m_sds_cache;
            m_sds_cache = 0;
      }

      void on_create_cache()
      {
            /** \todo When this method is called, it's time to create our cache of tessellated / subdivision mesh drawing data */
            const k3d::mesh* const mesh = m_input_mesh.property_value();
            if(!mesh)
                  return;

            // Create blobby polygonized surfaces cache
            for(k3d::mesh::blobbies_t::const_iterator blobby = mesh->blobbies.begin(); blobby != mesh->blobbies.end(); ++blobby)
                  {
                        blobby_cache_map_t::const_iterator cached_blobby = blobby_cache_map.find(*blobby);
                        if(cached_blobby == blobby_cache_map.end())
                              {
                                    // Cache polygonized surface for blobby
                                    detail::vertices_t blobby_vertices;
                                    detail::vertices_t blobby_normals;
                                    detail::polygons_t blobby_polygons;
                                    detail::polygonize_blobby(*blobby, 0, blobby_vertices, blobby_normals, blobby_polygons);

                                    // Save surface
                                    blobby_cache_map[*blobby] = blobby_surfaces_vertices.size();
                                    blobby_surfaces_vertices.push_back(blobby_vertices);
                                    blobby_surfaces_normals.push_back(blobby_normals);
                                    blobby_surfaces_polygons.push_back(blobby_polygons);
                              }
                  }

            // If no SDS cache, generate the cache for all SDS polyhedra
            if(!m_sds_cache)
                  {
                        bool is_sds = false;
                        for(k3d::mesh::polyhedra_t::const_iterator it = mesh->polyhedra.begin(); it != mesh->polyhedra.end(); ++it)
                              if (k3d::polyhedron::CATMULL_CLARK_SUBDIVISION_MESH == (*it)->type)
                                    is_sds = true;

                        if(is_sds)
                              {
                                    m_sds_cache = new k3d::mesh();
                                    subdiv::catmull_clark(m_sds_levels.property_value(), *mesh, *m_sds_cache);
                              }
                  }
      }

      const k3d::bounding_box extents()
      {
            k3d::bounding_box results;

            const k3d::mesh* const mesh = m_input_mesh.property_value();
            if(!mesh)
                  return results;

            for(k3d::mesh::points_t::const_iterator point = mesh->points.begin(); point != mesh->points.end(); ++point)
                  {
                        results.px = std::max(results.px, (*point)->position[0]);
                        results.py = std::max(results.py, (*point)->position[1]);
                        results.pz = std::max(results.pz, (*point)->position[2]);
                        results.nx = std::min(results.nx, (*point)->position[0]);
                        results.ny = std::min(results.ny, (*point)->position[1]);
                        results.nz = std::min(results.nz, (*point)->position[2]);
                  }

            return results;
      }

      void on_viewport_draw(const k3d::viewport::render_state& State)
      {
            // No input, so we're done ...
            const k3d::mesh* mesh = m_input_mesh.property_value();
            if(!mesh)
                  return;

            // Update the drawing cache as-needed ...
            on_create_cache();

            // Have a nurbs renderer cached if we need it ...
            nurbs_renderer_t nurbs = 0;

            const k3d::color color = m_color.property_value();
            const k3d::color selected_color(1, 1, 1);

            // Handle SDS
            if(m_sds_cache)
                  {
                  const k3d::mesh* const cage = m_input_mesh.property_value();
                  // draw the cage, only points and edges
                  draw_points(cage->points.begin(), cage->points.end(), true, selected_color);
                  draw_points(cage->points.begin(), cage->points.end(), false, color);

                  draw_point_groups(cage->point_groups.begin(), cage->point_groups.end(), true, selected_color);
                  draw_point_groups(cage->point_groups.begin(), cage->point_groups.end(), false, color);
                  // draw the edges
                  if(!cage->polyhedra.empty()) {
                        draw_polyhedron_edges(cage->polyhedra.begin(), cage->polyhedra.end(), true, selected_color);
                        draw_polyhedron_edges(cage->polyhedra.begin(), cage->polyhedra.end(), false, color);
                  }

                  mesh = m_sds_cache;
                  }

            if(State.draw_points)
                  {
                        draw_points(mesh->points.begin(), mesh->points.end(), true, selected_color);
                        draw_points(mesh->points.begin(), mesh->points.end(), false, color);

                        draw_point_groups(mesh->point_groups.begin(), mesh->point_groups.end(), true, selected_color);
                        draw_point_groups(mesh->point_groups.begin(), mesh->point_groups.end(), false, color);
                  }

            if(State.draw_linear_curves && !mesh->linear_curve_groups.empty())
                  {
                        draw_linear_curve_groups(mesh->linear_curve_groups.begin(), mesh->linear_curve_groups.end(), true, selected_color);
                        draw_linear_curve_groups(mesh->linear_curve_groups.begin(), mesh->linear_curve_groups.end(), false, color);
                  }

            if(State.draw_cubic_curves && !mesh->cubic_curve_groups.empty())
                  {
                        draw_cubic_curve_groups(mesh->cubic_curve_groups.begin(), mesh->cubic_curve_groups.end(), true, selected_color);
                        draw_cubic_curve_groups(mesh->cubic_curve_groups.begin(), mesh->cubic_curve_groups.end(), false, color);
                  }

            if(State.draw_nucurves && !mesh->nucurve_groups.empty())
                  {
                        if(!nurbs)
                              nurbs = nurbs_renderer(State);

                        draw_nucurve_groups(nurbs, mesh->nucurve_groups.begin(), mesh->nucurve_groups.end(), true, selected_color);
                        draw_nucurve_groups(nurbs, mesh->nucurve_groups.begin(), mesh->nucurve_groups.end(), false, color);
                  }

            if(State.draw_face_orientations && !mesh->polyhedra.empty())
                  draw_polyhedron_orientations(mesh->polyhedra.begin(), mesh->polyhedra.end(), selected_color);

            if(State.draw_edges)
                  {
                        if(!mesh->polyhedra.empty())
                              {
                                    draw_polyhedron_edges(mesh->polyhedra.begin(), mesh->polyhedra.end(), true, selected_color);
                                    draw_polyhedron_edges(mesh->polyhedra.begin(), mesh->polyhedra.end(), false, color);
                              }

                        if(!mesh->bilinear_patches.empty())
                              {
                                    draw_bilinear_patch_edges(mesh->bilinear_patches.begin(), mesh->bilinear_patches.end(), true, selected_color);
                                    draw_bilinear_patch_edges(mesh->bilinear_patches.begin(), mesh->bilinear_patches.end(), false, color);
                              }

                        if(!mesh->bicubic_patches.empty())
                              {
                                    draw_bicubic_patch_edges(mesh->bicubic_patches.begin(), mesh->bicubic_patches.end(), true, selected_color);
                                    draw_bicubic_patch_edges(mesh->bicubic_patches.begin(), mesh->bicubic_patches.end(), false, color);
                              }

                        if(!mesh->nupatches.empty())
                              {
                                    if(!nurbs)
                                          nurbs = nurbs_renderer(State);

                                    draw_nupatch_edges(nurbs, mesh->nupatches.begin(), mesh->nupatches.end(), true, selected_color);
                                    draw_nupatch_edges(nurbs, mesh->nupatches.begin(), mesh->nupatches.end(), false, color);
                              }
                  }

            if(State.draw_faces && !mesh->polyhedra.empty())
                  {
                        draw_polyhedra(mesh->polyhedra.begin(), mesh->polyhedra.end(), State.draw_two_sided);
                  }

            if(State.draw_bilinear_patches && !mesh->bilinear_patches.empty())
                  {
                        draw_bilinear_patches(mesh->bilinear_patches.begin(), mesh->bilinear_patches.end(), State.draw_two_sided);
                  }

            if(State.draw_bicubic_patches && !mesh->bicubic_patches.empty())
                  {
                        draw_bicubic_patches(mesh->bicubic_patches.begin(), mesh->bicubic_patches.end(), State.draw_two_sided);
                  }

            if(State.draw_nupatches && !mesh->nupatches.empty())
                  {
                        if(!nurbs)
                              nurbs = nurbs_renderer(State);

                        draw_nupatches(nurbs, mesh->nupatches.begin(), mesh->nupatches.end(), State.draw_two_sided);
                  }

            if(State.draw_blobbies && !mesh->blobbies.empty())
                  {
                        draw_blobbies(mesh->blobbies.begin(), mesh->blobbies.end(), true, selected_color);
                        draw_blobbies(mesh->blobbies.begin(), mesh->blobbies.end(), false, color);
                  }
      }

      void on_viewport_select(const k3d::viewport::render_state& State)
      {
            // No input, so we're done ...
            const k3d::mesh* const mesh = m_input_mesh.property_value();
            if(!mesh)
                  return;

            // Update the drawing cache as-needed ...
            on_create_cache();

            sdpgl::store_attributes attributes();

            // Have a nurbs renderer cached if we need it ...
            nurbs_renderer_t nurbs = 0;

            // At the top-level, provide selection of this object ...
            k3d::glPushName(this);

            // Then, provide selection of the underlying mesh ...
            k3d::glPushName(mesh);

            if(!mesh->points.empty())
                  select_points(mesh->points.begin(), mesh->points.end());

            if(!mesh->point_groups.empty())
                  select_point_groups(mesh->point_groups.begin(), mesh->point_groups.end());

            if(!mesh->polyhedra.empty())
                  {
                        select_polyhedra_edges(mesh->polyhedra.begin(), mesh->polyhedra.end());
                        select_polyhedra(mesh->polyhedra.begin(), mesh->polyhedra.end());
                  }

            if(!mesh->linear_curve_groups.empty())
                  select_linear_curve_groups(mesh->linear_curve_groups.begin(), mesh->linear_curve_groups.end());

            if(!mesh->cubic_curve_groups.empty())
                  select_cubic_curve_groups(mesh->cubic_curve_groups.begin(), mesh->cubic_curve_groups.end());

            if(!mesh->bilinear_patches.empty())
                  select_bilinear_patches(mesh->bilinear_patches.begin(), mesh->bilinear_patches.end());

            if(!mesh->nucurve_groups.empty())
                  {
                        if(!nurbs)
                              nurbs = nurbs_renderer(State);

                        select_nucurves(nurbs, mesh->nucurve_groups.begin(), mesh->nucurve_groups.end());
                  }

            if(!mesh->bicubic_patches.empty())
                  select_bicubic_patches(mesh->bicubic_patches.begin(), mesh->bicubic_patches.end());

            if(!mesh->nupatches.empty())
                  {
                        if(!nurbs)
                              nurbs = nurbs_renderer(State);

                        select_nupatches(nurbs, mesh->nupatches.begin(), mesh->nupatches.end());
                  }

            if(!mesh->blobbies.empty())
                  select_blobbies(mesh->blobbies.begin(), mesh->blobbies.end());

            k3d::glPopName();
            k3d::glPopName();
      }

      void on_renderman_render(const k3d::ri::render_state& State)
      {
            // No input, so we're done ...
            const k3d::mesh* const mesh = m_input_mesh.property_value();
            if(!mesh)
                  return;

            k3d::ri::render(*mesh, State);
      }

      k3d::iplugin_factory& factory()
      {
            return get_factory();
      }

      static k3d::iplugin_factory& get_factory()
      {
            static k3d::plugin_factory<
                  k3d::document_plugin<mesh_instance_implementation>,
                  k3d::interface_list<k3d::imesh_source,
                  k3d::interface_list<k3d::imesh_sink,
                  k3d::interface_list<k3d::itransform_source,
                  k3d::interface_list<k3d::itransform_sink > > > > > factory(
                        k3d::classes::MeshInstance(),
                        "MeshInstance",
                        "Renders an instance of a geometric mesh",
                        "Objects",
                        k3d::iplugin_factory::STABLE);

            return factory;
      }

private:
      void draw_points(const k3d::mesh::points_t::const_iterator Begin, const k3d::mesh::points_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);
//          glPointSize(3.0);

            for(k3d::mesh::points_t::const_iterator point = Begin; point != End; ++point)
                  {
                        if((*point)->selected != SelectionState)
                              continue;

                        glBegin(GL_POINTS);
                        glVertex3dv((*point)->position.n);
                        glEnd();
                  }
      }

      void select_points(const k3d::mesh::points_t::const_iterator Begin, const k3d::mesh::points_t::const_iterator End)
      {
            for(k3d::mesh::points_t::const_iterator point = Begin; point != End; ++point)
                  {
                        k3d::glPushName(*point);

                        glBegin(GL_POINTS);
                        glVertex3dv((*point)->position.n);
                        glEnd();

                        k3d::glPopName();
                  }
      }

      void draw_point_groups(const k3d::mesh::point_groups_t::const_iterator Begin, const k3d::mesh::point_groups_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);
//          glPointSize(3.0);

            for(k3d::mesh::point_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        if((*group)->selected != SelectionState)
                              continue;

                        glBegin(GL_POINTS);
                        for(k3d::point_group::points_t::const_iterator point = (*group)->points.begin(); point != (*group)->points.end(); ++point)
                              glVertex3dv((*point)->position.n);
                        glEnd();
                  }
      }

      void select_point_groups(const k3d::mesh::point_groups_t::const_iterator Begin, const k3d::mesh::point_groups_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);

            for(k3d::mesh::point_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        k3d::glPushName(*group);

                        glBegin(GL_POINTS);
                        for(k3d::point_group::points_t::const_iterator point = (*group)->points.begin(); point != (*group)->points.end(); ++point)
                              glVertex3dv((*point)->position.n);
                        glEnd();

                        k3d::glPopName();
                  }
      }

      void draw_linear_curve_groups(const k3d::mesh::linear_curve_groups_t::const_iterator Begin, const k3d::mesh::linear_curve_groups_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);
            glLineWidth(1.0);

            for(k3d::mesh::linear_curve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        const GLenum mode = (*group)->wrap ? GL_LINE_LOOP : GL_LINE_STRIP;
                        for(k3d::linear_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
                              {
                                    if((*curve)->selected != SelectionState)
                                          continue;

                                    glBegin(mode);
                                    for(k3d::linear_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
                                          glVertex3dv((*control_point)->position.n);
                                    glEnd();
                              }
                  }
      }

      void select_linear_curve_groups(const k3d::mesh::linear_curve_groups_t::const_iterator Begin, const k3d::mesh::linear_curve_groups_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);

            for(k3d::mesh::linear_curve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        k3d::glPushName(*group);

                        const GLenum mode = (*group)->wrap ? GL_LINE_LOOP : GL_LINE_STRIP;
                        for(k3d::linear_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
                              {
                                    k3d::glPushName(*curve);

                                    glBegin(mode);
                                    for(k3d::linear_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
                                          glVertex3dv((*control_point)->position.n);
                                    glEnd();

                                    k3d::glPopName();
                              }

                        k3d::glPopName();
                  }
      }

      void draw_cubic_curve_groups(const k3d::mesh::cubic_curve_groups_t::const_iterator Begin, const k3d::mesh::cubic_curve_groups_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);
            glLineWidth(1.0);

            const unsigned int v_count = 8;
            const GLint v_order = 4;
            const GLint v_stride = 3;

            glEnable(GL_MAP1_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid1d(v_count, 0.0, 1.0);

            GLdouble patch_points[4 * 3];
            for(k3d::mesh::cubic_curve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
//                      const GLenum mode = (*group)->wrap ? GL_LINE_LOOP : GL_LINE_STRIP;
                        for(k3d::cubic_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
                              {
                                    if((*curve)->selected != SelectionState)
                                          continue;

                                    GLdouble* pp = patch_points;
                                    for(k3d::cubic_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
                                          {
                                                const k3d::vector3& v = (*control_point)->position;
                                                *pp++ = v[0];
                                                *pp++ = v[1];
                                                *pp++ = v[2];
                                          }
                                    glMap1d(GL_MAP1_VERTEX_3, 0, 1, v_stride, v_order, patch_points);
                                    glEvalMesh1(GL_LINE, 0, v_count);
                              }
                  }
      }

      void select_cubic_curve_groups(const k3d::mesh::cubic_curve_groups_t::const_iterator Begin, const k3d::mesh::cubic_curve_groups_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);

            const unsigned int v_count = 8;
            const GLint v_order = 4;
            const GLint v_stride = 3;

            glEnable(GL_MAP1_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid1d(v_count, 0.0, 1.0);

            GLdouble patch_points[4 * 3];
            for(k3d::mesh::cubic_curve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        k3d::glPushName(*group);

//                      const GLenum mode = (*group)->wrap ? GL_LINE_LOOP : GL_LINE_STRIP;
                        for(k3d::cubic_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
                              {
                                    k3d::glPushName(*curve);

                                    GLdouble* pp = patch_points;
                                    for(k3d::cubic_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
                                          {
                                                const k3d::vector3& v = (*control_point)->position;
                                                *pp++ = v[0];
                                                *pp++ = v[1];
                                                *pp++ = v[2];
                                          }
                                    glMap1d(GL_MAP1_VERTEX_3, 0, 1, v_stride, v_order, patch_points);
                                    glEvalMesh1(GL_LINE, 0, v_count);

                                    k3d::glPopName();
                              }

                        k3d::glPopName();
                  }
      }

      void draw_nucurve_groups(const nurbs_renderer_t Nurbs, const k3d::mesh::nucurve_groups_t::const_iterator Begin, const k3d::mesh::nucurve_groups_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            for(k3d::mesh::nucurve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        for(k3d::nucurve_group::curves_t::const_iterator nucurve = (*group)->curves.begin(); nucurve != (*group)->curves.end(); ++nucurve)
                              {
                                    k3d::nucurve& curve = **nucurve;
                                    if(curve.selected != SelectionState)
                                          continue;

                                    std::vector<GLfloat> gl_knot_vector(curve.knots.begin(), curve.knots.end());

                                    k3d::nucurve::control_points_t& control_points = curve.control_points;
                                    std::vector<GLfloat> gl_control_points;
                                    gl_control_points.reserve(4 * control_points.size());
                                    for(unsigned int i = 0; i != control_points.size(); ++i)
                                          {
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[0]);
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[1]);
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[2]);
                                                gl_control_points.push_back(control_points[i].weight);
                                          }

                                    gluBeginCurve(Nurbs);
                                    gluNurbsCurve(Nurbs, gl_knot_vector.size(), &gl_knot_vector[0], 4, &gl_control_points[0], curve.order, GL_MAP1_VERTEX_4);
                                    gluEndCurve(Nurbs);
                              }
                  }
      }

      void select_nucurves(const nurbs_renderer_t Nurbs, const k3d::mesh::nucurve_groups_t::const_iterator Begin, const k3d::mesh::nucurve_groups_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();
            glDisable(GL_LIGHTING);

            for(k3d::mesh::nucurve_groups_t::const_iterator group = Begin; group != End; ++group)
                  {
                        for(k3d::nucurve_group::curves_t::const_iterator nucurve = (*group)->curves.begin(); nucurve != (*group)->curves.end(); ++nucurve)
                              {
                                    k3d::nucurve& curve = **nucurve;
                                    k3d::nucurve::control_points_t& control_points = curve.control_points;

                                    std::vector<GLfloat> gl_knot_vector(curve.knots.begin(), curve.knots.end());

                                    std::vector<GLfloat> gl_control_points;
                                    gl_control_points.reserve(4 * control_points.size());
                                    for(unsigned int i = 0; i != control_points.size(); ++i)
                                          {
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[0]);
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[1]);
                                                gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[2]);
                                                gl_control_points.push_back(control_points[i].weight);
                                          }

                                    k3d::glPushName(*nucurve);

                                    gluBeginCurve(Nurbs);
                                    gluNurbsCurve(Nurbs, gl_knot_vector.size(), &gl_knot_vector[0], 4, &gl_control_points[0], curve.order, GL_MAP1_VERTEX_4);
                                    gluEndCurve(Nurbs);

                                    k3d::glPopName();
                              }
                  }
      }

      void draw_polyhedron_edges(const k3d::mesh::polyhedra_t::const_iterator Begin, const k3d::mesh::polyhedra_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();
            glDisable(GL_LIGHTING);

            glColor3d(Color.red, Color.green, Color.blue);

            for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Begin; polyhedron != End; ++polyhedron)
                  {
                        // Draws all edges that are part of a specific face
/*
                        for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
                              {
                                    for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
                                          {
                                                if(edge->selected == SelectionState)
                                                      {
                                                            glBegin(GL_LINES);
                                                            glVertex3dv(edge->vertex->position);
                                                            glVertex3dv(edge->face_clockwise->vertex->position);
                                                            glEnd();
                                                      }

                                                if((*face)->first_edge == edge->face_clockwise)
                                                      break;
                                          }
                              }
*/
                        // Alternative implementation that draws every edge that can be drawn, whether properly connected to a face or not
                        for(k3d::polyhedron::edges_t::const_iterator e = (*polyhedron)->edges.begin(); e != (*polyhedron)->edges.end(); ++e)
                              {
                                    k3d::split_edge* const edge = *e;

                                    if(edge->selected != SelectionState)
                                          continue;

                                    if(edge->vertex)
                                          {
                                                if(edge->face_clockwise && edge->face_clockwise->vertex)
                                                      {
                                                            glBegin(GL_LINES);
                                                            glVertex3dv(edge->vertex->position);
                                                            glVertex3dv(edge->face_clockwise->vertex->position);
                                                            glEnd();
                                                            continue;
                                                      }

                                                if(edge->companion && edge->companion->vertex)
                                                      {
                                                            glBegin(GL_LINES);
                                                            glVertex3dv(edge->vertex->position);
                                                            glVertex3dv(edge->companion->vertex->position);
                                                            glEnd();
                                                            continue;
                                                      }
                                          }
                              }
                  }
      }

      void set_material_color(const k3d::color& color)
      {
            GLfloat diffuse[4];

            diffuse[0] = static_cast<GLfloat>(color.red);
            diffuse[1] = static_cast<GLfloat>(color.green);
            diffuse[2] = static_cast<GLfloat>(color.blue);
            diffuse[3] = 1.0f;

            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
      }

      void draw_polyhedra(const k3d::mesh::polyhedra_t::const_iterator Begin, const k3d::mesh::polyhedra_t::const_iterator End, const bool TwoSided)
      {
            sdpgl::store_attributes attributes();
            glEnable(GL_LIGHTING);
            glColor3d(0.8, 0.8, 1);

            glFrontFace(GL_CW);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

            if(TwoSided)
                  glDisable(GL_CULL_FACE);
            else
                  glEnable(GL_CULL_FACE);

            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonOffset(1.0, 1.0);

            // Cache color change (probably not necessary)
            bool color_changed = false;

            for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Begin; polyhedron != End; ++polyhedron)
                  {
                        k3d::viewport::setup_material((*polyhedron)->material);

                        // Draw polygon
                        for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
                              {
                                    // Change color if specified
                                    k3d::parameters_t::const_iterator color = (*face)->uniform_data.find("Cs");
                                    if(color != (*face)->uniform_data.end())
                                          {
                                                set_material_color(boost::any_cast<k3d::color>(color->second));
                                                color_changed = true;
                                          }
                                    else if(color_changed)
                                          {
                                                set_material_color(k3d::color(0.8, 0.8, 1.0));
                                                color_changed = false;
                                          }

                                    // Check for point normals
                                    const k3d::vector3 face_normal = k3d::normal(**face);
                                    std::vector<k3d::vector3> points;
                                    std::vector<k3d::vector3> normals;
                                    k3d::parameters_t::const_iterator N;
                                    for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
                                          {
                                                points.push_back(edge->vertex->position);

                                                N = edge->facevarying_data.find("N");
                                                if(N != edge->facevarying_data.end())
                                                      {
                                                            normals.push_back(boost::any_cast<k3d::ri::normal>(N->second));
                                                      }
                                                else
                                                      {
                                                            N = edge->vertex->vertex_data.find("N");
                                                            if(N != edge->vertex->vertex_data.end())
                                                                  {
                                                                        normals.push_back(boost::any_cast<k3d::ri::normal>(N->second));
                                                                  }
                                                            else
                                                                  {
                                                                        normals.push_back(face_normal);
                                                                  }
                                                      }

                                                if(edge->face_clockwise == (*face)->first_edge)
                                                      break;
                                          }

                                    // Draw polygon
                                    glBegin(GL_POLYGON);
                                    std::vector<k3d::vector3>::iterator point = points.begin();
                                    std::vector<k3d::vector3>::iterator normal = normals.begin();
                                    for(; point != points.end(); point++, normal++)
                                          {
                                                glNormal3dv(*normal);
                                                glVertex3dv(*point);
                                          }
                                    glEnd();
                              }
                  }
      }

      void draw_polyhedron_orientations(const k3d::mesh::polyhedra_t::const_iterator Begin, const k3d::mesh::polyhedra_t::const_iterator End, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Begin; polyhedron != End; ++polyhedron)
                  {
                        for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
                              {
                                    const k3d::split_edge* const edge1 = (*face)->first_edge;
                                    if(!edge1)
                                          continue;

                                    const k3d::split_edge* const edge2 = edge1->face_clockwise;
                                    if(!edge2)
                                          continue;

                                    k3d::vector3 center = detail::center_point(**face);
                                    k3d::vector3 point1 = k3d::mix(center, edge1->vertex->position, 0.8);
                                    k3d::vector3 point2 = k3d::mix(center, edge2->vertex->position, 0.8);

                                    glBegin(GL_LINES);
                                    glVertex3dv(point1);
                                    glVertex3dv(point2);
                                    glEnd();

                                    glBegin(GL_POINTS);
                                    glVertex3dv(point1);
                                    glEnd();
                              }
                  }
      }

      void select_polyhedra_edges(const k3d::mesh::polyhedra_t::const_iterator Begin, const k3d::mesh::polyhedra_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Begin; polyhedron != End; ++polyhedron)
                  {
/*
                        for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
                              {
                                    for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
                                          {
                                                k3d::glPushName(edge);

                                                glBegin(GL_LINES);
                                                glVertex3dv(edge->vertex->position);
                                                glVertex3dv(edge->face_clockwise->vertex->position);
                                                glEnd();

                                                k3d::glPopName();

                                                if((*face)->first_edge == edge->face_clockwise)
                                                      break;
                                          }
                              }
*/
                        // Alternative implementation that draws every edge that can be drawn, whether properly connected to a face or not
                        for(k3d::polyhedron::edges_t::const_iterator e = (*polyhedron)->edges.begin(); e != (*polyhedron)->edges.end(); ++e)
                              {
                                    k3d::split_edge* const edge = *e;

                                    if(edge->vertex)
                                          {
                                                if(edge->face_clockwise && edge->face_clockwise->vertex)
                                                      {
                                                            k3d::glPushName(edge);

                                                            glBegin(GL_LINES);
                                                            glVertex3dv(edge->vertex->position);
                                                            glVertex3dv(edge->face_clockwise->vertex->position);
                                                            glEnd();

                                                            k3d::glPopName();

                                                            continue;
                                                      }

                                                if(edge->companion && edge->companion->vertex)
                                                      {
                                                            k3d::glPushName(edge);

                                                            glBegin(GL_LINES);
                                                            glVertex3dv(edge->vertex->position);
                                                            glVertex3dv(edge->companion->vertex->position);
                                                            glEnd();

                                                            k3d::glPopName();

                                                            continue;
                                                      }
                                          }
                              }
                  }
      }

      void select_polyhedra(const k3d::mesh::polyhedra_t::const_iterator Begin, const k3d::mesh::polyhedra_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glFrontFace(GL_CW);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            glDisable(GL_CULL_FACE);

            for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Begin; polyhedron != End; ++polyhedron)
                  {
                        for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
                              {
                                    k3d::glPushName(*face);

                                    glBegin(GL_POLYGON);
                                    for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
                                          {
                                                glVertex3dv(edge->vertex->position);
                                                if((*face)->first_edge == edge->face_clockwise)
                                                      break;
                                          }
                                    glEnd();

                                    k3d::glPopName();
                              }
                  }
      }

      void draw_bilinear_patch_edges(const k3d::mesh::bilinear_patches_t::const_iterator Begin, const k3d::mesh::bilinear_patches_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            const unsigned int u_count = 10;
            const unsigned int v_count = 10;
            const GLint u_order = 2;
            const GLint v_order = 2;
            const GLint u_stride = 3;
            const GLint v_stride = 2 * u_stride;

            glEnable(GL_MAP2_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[2 * 2 * 3];
            for(k3d::mesh::bilinear_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        if((*patch)->selected != SelectionState)
                              continue;

                        GLdouble* pp = patch_points;
                        for(k3d::bilinear_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, &patch_points[0]);

                        glEvalMesh2(GL_LINE, 0, 0, 0, v_count);
                        glEvalMesh2(GL_LINE, u_count, u_count, 0, v_count);
                        glEvalMesh2(GL_LINE, 0, u_count, 0, 0);
                        glEvalMesh2(GL_LINE, 0, u_count, v_count, v_count);
                  }
      }

      void draw_bilinear_patches(const k3d::mesh::bilinear_patches_t::const_iterator Begin, const k3d::mesh::bilinear_patches_t::const_iterator End, const bool TwoSided)
      {
            sdpgl::store_attributes attributes();
            glEnable(GL_LIGHTING);

            const unsigned int u_count = 10;
            const unsigned int v_count = 10;
            const GLint u_order = 2;
            const GLint v_order = 2;
            const GLint u_stride = 3;
            const GLint v_stride = 2 * u_stride;

            glFrontFace(GL_CCW);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            if(TwoSided)
                  glDisable(GL_CULL_FACE);
            else
                  glEnable(GL_CULL_FACE);

            glEnable(GL_MAP2_VERTEX_3);
            glEnable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[2 * 2 * 3];
            for(k3d::mesh::bilinear_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        k3d::viewport::setup_material((*patch)->material);

                        GLdouble* pp = patch_points;
                        for(k3d::bilinear_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, &patch_points[0]);
                        glEvalMesh2(GL_FILL, 0, u_count, 0, v_count);
                  }
      }

      void select_bilinear_patches(const k3d::mesh::bilinear_patches_t::const_iterator Begin, const k3d::mesh::bilinear_patches_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();
            glDisable(GL_LIGHTING);

            const unsigned int u_count = 10;
            const unsigned int v_count = 10;
            const GLint u_order = 2;
            const GLint v_order = 2;
            const GLint u_stride = 3;
            const GLint v_stride = 2 * u_stride;

            glFrontFace(GL_CCW);
            glDisable(GL_CULL_FACE);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

            glEnable(GL_MAP2_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[2 * 2 * 3];
            for(k3d::mesh::bilinear_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        k3d::glPushName(*patch);

                        GLdouble* pp = patch_points;
                        for(k3d::bilinear_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, &patch_points[0]);
                        glEvalMesh2(GL_FILL, 0, u_count, 0, v_count);

                        k3d::glPopName();
                  }
      }

      void draw_bicubic_patch_edges(const k3d::mesh::bicubic_patches_t::const_iterator Begin, const k3d::mesh::bicubic_patches_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            const unsigned int u_count = 8;
            const unsigned int v_count = 8;
            const GLint u_order = 4;
            const GLint v_order = 4;
            const GLint u_stride = 3;
            const GLint v_stride = 4 * u_stride;

            glEnable(GL_MAP2_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[4 * 4 * 3];
            for(k3d::mesh::bicubic_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        if((*patch)->selected != SelectionState)
                              continue;

                        GLdouble* pp = patch_points;
                        for(k3d::bicubic_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, patch_points);

                        glEvalMesh2(GL_LINE, 0, 0, 0, v_count);
                        glEvalMesh2(GL_LINE, u_count, u_count, 0, v_count);
                        glEvalMesh2(GL_LINE, 0, u_count, 0, 0);
                        glEvalMesh2(GL_LINE, 0, u_count, v_count, v_count);
                  }
      }

      void draw_bicubic_patches(const k3d::mesh::bicubic_patches_t::const_iterator Begin, const k3d::mesh::bicubic_patches_t::const_iterator End, const bool TwoSided)
      {
            sdpgl::store_attributes attributes();
            glEnable(GL_LIGHTING);

            const unsigned int u_count = 5;
            const unsigned int v_count = 5;
            const GLint u_order = 4;
            const GLint v_order = 4;
            const GLint u_stride = 3;
            const GLint v_stride = 4 * u_stride;

            glFrontFace(GL_CCW);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            if(TwoSided)
                  glDisable(GL_CULL_FACE);
            else
                  glEnable(GL_CULL_FACE);

            glEnable(GL_MAP2_VERTEX_3);
            glEnable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[4 * 4 * 3];
            for(k3d::mesh::bicubic_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        k3d::viewport::setup_material((*patch)->material);

                        GLdouble* pp = patch_points;
                        for(k3d::bicubic_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, &patch_points[0]);
                        glEvalMesh2(GL_FILL, 0, u_count, 0, v_count);
                  }
      }

      void select_bicubic_patches(const k3d::mesh::bicubic_patches_t::const_iterator Begin, const k3d::mesh::bicubic_patches_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();
            glDisable(GL_LIGHTING);

            const unsigned int u_count = 8;
            const unsigned int v_count = 8;
            const GLint u_order = 4;
            const GLint v_order = 4;
            const GLint u_stride = 3;
            const GLint v_stride = 4 * u_stride;

            glFrontFace(GL_CCW);
            glDisable(GL_CULL_FACE);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

            glEnable(GL_MAP2_VERTEX_3);
            glDisable(GL_AUTO_NORMAL);
            glMapGrid2d(u_count, 0.0, 1.0, v_count, 0.0, 1.0);

            GLdouble patch_points[4 * 4 * 3];
            for(k3d::mesh::bicubic_patches_t::const_iterator patch = Begin; patch != End; ++patch)
                  {
                        k3d::glPushName(*patch);

                        GLdouble* pp = patch_points;
                        for(k3d::bicubic_patch::control_points_t::const_iterator control_point = (*patch)->control_points.begin(); control_point != (*patch)->control_points.end(); ++control_point)
                              {
                                    const k3d::vector3& v = (*control_point)->position;

                                    *pp++ = v[0];
                                    *pp++ = v[1];
                                    *pp++ = v[2];
                              }

                        glMap2d(GL_MAP2_VERTEX_3, 0, 1, u_stride, u_order, 0, 1, v_stride, v_order, &patch_points[0]);
                        glEvalMesh2(GL_FILL, 0, u_count, 0, v_count);

                        k3d::glPopName();
                  }
      }

      void draw_nupatch_edges(const nurbs_renderer_t Nurbs, const k3d::mesh::nupatches_t::const_iterator Begin, const k3d::mesh::nupatches_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            gluNurbsProperty(Nurbs, GLU_DISPLAY_MODE, GLU_OUTLINE_PATCH);

            for(k3d::mesh::nupatches_t::const_iterator nupatch = Begin; nupatch != End; ++nupatch)
                  {
                        if((**nupatch).selected != SelectionState)
                              continue;

                        render_nupatch(Nurbs, **nupatch);
                  }
      }

      void draw_nupatches(const nurbs_renderer_t Nurbs, const k3d::mesh::nupatches_t::const_iterator Begin, const k3d::mesh::nupatches_t::const_iterator End, const bool TwoSided)
      {
            sdpgl::store_attributes attributes();

            glEnable(GL_LIGHTING);
            glEnable(GL_AUTO_NORMAL);

            if(TwoSided)
                  glDisable(GL_CULL_FACE);
            else
                  glEnable(GL_CULL_FACE);

            glPolygonOffset(1.0, 1.0);
            glEnable(GL_POLYGON_OFFSET_FILL);

            gluNurbsProperty(Nurbs, GLU_DISPLAY_MODE, GLU_FILL);

            for(k3d::mesh::nupatches_t::const_iterator nupatch = Begin; nupatch != End; ++nupatch)
                  {
                        k3d::viewport::setup_material((**nupatch).material);
                        render_nupatch(Nurbs, **nupatch);
                  }

            glDisable(GL_POLYGON_OFFSET_FILL);
      }

      void select_nupatches(const nurbs_renderer_t Nurbs, const k3d::mesh::nupatches_t::const_iterator Begin, const k3d::mesh::nupatches_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glDisable(GL_AUTO_NORMAL);
            glDisable(GL_CULL_FACE);

            gluNurbsProperty(Nurbs, GLU_DISPLAY_MODE, GLU_FILL);

            for(k3d::mesh::nupatches_t::const_iterator nupatch = Begin; nupatch != End; ++nupatch)
                  {
                        k3d::glPushName(*nupatch);
                        render_nupatch(Nurbs, **nupatch);
                        k3d::glPopName();
                  }
      }

      void render_nupatch(const nurbs_renderer_t Nurbs, const k3d::nupatch& Patch)
      {
            const unsigned int u_control_points_count = Patch.u_knots.size() - Patch.u_order;
            const unsigned int v_control_points_count = Patch.v_knots.size() - Patch.v_order;

            assert_warning(u_control_points_count * v_control_points_count == Patch.control_points.size());

            std::vector<GLfloat> gl_u_knot_vector(Patch.u_knots.begin(), Patch.u_knots.end());
            std::vector<GLfloat> gl_v_knot_vector(Patch.v_knots.begin(), Patch.v_knots.end());

            const GLint gl_u_stride = 4;
            const GLint gl_v_stride = gl_u_stride * u_control_points_count;

            const k3d::nupatch::control_points_t& control_points = Patch.control_points;
            std::vector<GLfloat> gl_control_points;
            gl_control_points.reserve(4 * control_points.size());
            for(unsigned int i = 0; i != control_points.size(); ++i)
                  {
                        gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[0]);
                        gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[1]);
                        gl_control_points.push_back(control_points[i].weight * control_points[i].position->position[2]);
                        gl_control_points.push_back(control_points[i].weight);
                  }

            gluBeginSurface(Nurbs);

            gluNurbsSurface(Nurbs,
                  gl_u_knot_vector.size(),
                  &gl_u_knot_vector[0],
                  gl_v_knot_vector.size(),
                  &gl_v_knot_vector[0],
                  gl_u_stride,
                  gl_v_stride,
                  &gl_control_points[0],
                  Patch.u_order,
                  Patch.v_order,
                  GL_MAP2_VERTEX_4);

            gluEndSurface(Nurbs);
      }

      void draw_blobbies(const k3d::mesh::blobbies_t::const_iterator Begin, const k3d::mesh::blobbies_t::const_iterator End, const bool SelectionState, const k3d::color& Color)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);
            glColor3d(Color.red, Color.green, Color.blue);

            detail::draw_blobby draw_blobby;
            for(k3d::mesh::blobbies_t::const_iterator blobby = Begin; blobby != End; ++blobby)
                  (*blobby)->accept(draw_blobby);

            // Offset solid geometry, so it's cleanly visible ...
            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonOffset(1.0, 1.0);

            // Solid drawing ...
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            glEnable(GL_LIGHTING);

            if(m_show_blobby_surface.property_value())
                  for(k3d::mesh::blobbies_t::const_iterator blobby = Begin; blobby != End; ++blobby)
                        render_blobby_surface(*blobby);
      }

      void select_blobbies(const k3d::mesh::blobbies_t::const_iterator Begin, const k3d::mesh::blobbies_t::const_iterator End)
      {
            sdpgl::store_attributes attributes();

            glDisable(GL_LIGHTING);

            detail::select_blobby select_blobby;
            for(k3d::mesh::blobbies_t::const_iterator blobby = Begin; blobby != End; ++blobby)
                  (*blobby)->accept(select_blobby);

            glFrontFace(GL_CW);
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            glDisable(GL_CULL_FACE);

            if(m_show_blobby_surface.property_value())
                  for(k3d::mesh::blobbies_t::const_iterator blobby = Begin; blobby != End; ++blobby)
                        {
                              k3d::glPushName((*blobby)->root);
                              render_blobby_surface(*blobby);
                              k3d::glPopName();
                        }
      }

      void render_blobby_surface(k3d::blobby* Opcode)
      {
            // Get surface number
            blobby_cache_map_t::const_iterator surface = blobby_cache_map.find(Opcode);
            if(surface == blobby_cache_map.end())
                  return;

            unsigned long surface_number = surface->second;

            // Output cached surface
            const detail::vertices_t& blobby_vertices = blobby_surfaces_vertices[surface_number];
            const detail::vertices_t& blobby_normals = blobby_surfaces_normals[surface_number];
            const detail::polygons_t& blobby_polygons = blobby_surfaces_polygons[surface_number];
            for(unsigned long p = 0; p < blobby_polygons.size(); ++p)
                  {
                        detail::polygon_t path = blobby_polygons[p];

                        // Calculate normal
                        unsigned long pointcount = path.size();
                        if(pointcount < 3)
                              continue;

                        // Draw polygon
                        glBegin(GL_POLYGON);
                        for(unsigned long i = 0; i < path.size(); ++i)
                              {
                                    // Invert normal (Blobby field decreases from in to out)
                                    k3d::vector3 normal = -blobby_normals[path[i]];
                                    glNormal3dv(normal);
                                    k3d::vector3 vertex = blobby_vertices[path[i]];
                                    glVertex3dv(vertex);
                              }
                        glEnd();
                  }
      }

      /// Blobby polygonized surfaces caching variables
      typedef std::map<k3d::blobby*, unsigned long> blobby_cache_map_t;
      blobby_cache_map_t blobby_cache_map;

      typedef std::vector<detail::vertices_t> blobby_surfaces_vertices_t;
      typedef std::vector<detail::polygons_t> blobby_surfaces_polygons_t;

      blobby_surfaces_vertices_t blobby_surfaces_vertices;
      blobby_surfaces_vertices_t blobby_surfaces_normals;
      blobby_surfaces_polygons_t blobby_surfaces_polygons;

      /// Blobby preview type
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_show_blobby_surface;

      /// Cached subdivision data
      k3d::mesh* m_sds_cache;
      /// SDS preview levels
      k3d_measurement_property(unsigned long, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::with_constraint) m_sds_levels;

      /// Color highlighting for the editor viewport
      k3d_data_property(k3d::color, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::no_constraint) m_color;
};

/////////////////////////////////////////////////////////////////////////////
// mesh_instance_factory

k3d::iplugin_factory& mesh_instance_factory()
{
      return mesh_instance_implementation::get_factory();
}

} // namespace libk3dmesh



Generated by  Doxygen 1.6.0   Back to index