Logo Search packages:      
Sourcecode: k3d version File versions

viewport.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 Tim Shead (tshead@k-3d.com)
*/

#include <k3dsdk/classes.h>
#include <k3dsdk/frames.h>
#include <k3dsdk/geometry.h>
#include <k3dsdk/iprojection.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/object.h>
#include <k3dsdk/persistence.h>
#include <k3dsdk/measurement.h>
#include <k3dsdk/module.h>
#include <k3dsdk/property.h>
#include <k3dsdk/property_group_collection.h>
#include <k3dsdk/renderman.h>
#include <k3dsdk/time_source.h>
#include <k3dsdk/transformable.h>
#include <k3dsdk/viewport.h>

#include <sdpgl/sdpgl.h>

#include <iostream>

#ifdef      WIN32
#ifdef      near
#undef      near
#endif      //near
#ifdef      far
#undef      far
#endif      //far
#endif      //WIN32

namespace libk3dviewport
{

namespace detail
{

/// Functor object for initializing light sources during viewport drawing
00061 class light_setup
{
public:
      light_setup() :
            m_light_number(0)
      {
      }

      void operator()(k3d::iobject* const Object)
      {
            k3d::viewport::ilight* const light = dynamic_cast<k3d::viewport::ilight*>(Object);
            if(light)
                  light->setup_viewport_light(++m_light_number);
      }

private:
      unsigned long m_light_number;
};

/// Functor for drawing objects in the viewport
00081 class draw
{
public:
      draw(const k3d::viewport::render_state& State) :
            m_state(State)
      {
      }

      void operator()(k3d::iobject* const Object)
      {
            k3d::viewport::idrawable* const drawable = dynamic_cast<k3d::viewport::idrawable*>(Object);
            if(drawable)
                  drawable->viewport_draw(m_state);
      }

private:
      const k3d::viewport::render_state& m_state;
};

/// Functor for drawing objects in the viewport
00101 class draw_selection
{
public:
      draw_selection(const k3d::viewport::render_state& State) :
            m_state(State)
      {
      }

      void operator()(k3d::iobject* const Object)
      {
            k3d::viewport::idrawable* const drawable = dynamic_cast<k3d::viewport::idrawable*>(Object);
            if(drawable)
                  drawable->viewport_select(m_state);
      }

private:
      const k3d::viewport::render_state& m_state;
};

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// viewport_implementation

class viewport_implementation :
      public k3d::transformable<k3d::persistent<k3d::object> >,
      public k3d::iviewport,
      public k3d::viewport::iselection_engine,
      public k3d::property_group_collection
{
      typedef k3d::transformable<k3d::persistent<k3d::object> > base;

public:
      viewport_implementation(k3d::idocument& Document) :
            base(Document),
            m_point_size(k3d::init_name("point_size") + k3d::init_description("Point size [integer]") + k3d::init_value(4) + k3d::init_precision(1) + k3d::init_step_increment(1) + k3d::init_units(typeid(k3d::measurement::scalar)) + k3d::init_document(Document)),
            m_background_color(k3d::init_name("background_color") + k3d::init_description("Background color [color]") + k3d::init_value(k3d::color(0.8, 0.8, 0.8)) + k3d::init_document(Document)),
            m_fog(k3d::init_name("fog") + k3d::init_description("Fog [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_fog_near(k3d::init_name("fog_near") + k3d::init_description("Fog near distance [number]") + k3d::init_value(0.0) + k3d::init_constraint(k3d::constraint::minimum(0.0)) + k3d::init_document(Document) + k3d::init_precision(1) + k3d::init_step_increment(0.1) + k3d::init_units(typeid(k3d::measurement::distance))),
            m_fog_far(k3d::init_name("fog_far") + k3d::init_description("Fog far distance [number]") + k3d::init_value(100.0) + k3d::init_constraint(k3d::constraint::minimum(0.0)) + k3d::init_document(Document) + k3d::init_precision(1) + k3d::init_step_increment(0.1) + k3d::init_units(typeid(k3d::measurement::distance))),
            m_orthographic(k3d::init_name("orthographic") + k3d::init_description("Orthographic [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_left(k3d::init_name("left") + k3d::init_description("Left [number]") + k3d::init_value(-0.5) + k3d::init_document(Document) + k3d::init_precision(2) + k3d::init_step_increment(0.01) + k3d::init_units(typeid(k3d::measurement::scalar))),
            m_right(k3d::init_name("right") + k3d::init_description("Right [number]") + k3d::init_value(0.5) + k3d::init_document(Document) + k3d::init_precision(2) + k3d::init_step_increment(0.01) + k3d::init_units(typeid(k3d::measurement::scalar))),
            m_top(k3d::init_name("top") + k3d::init_description("Top [number]") + k3d::init_value(0.5) + k3d::init_document(Document) + k3d::init_precision(2) + k3d::init_step_increment(0.01) + k3d::init_units(typeid(k3d::measurement::scalar))),
            m_bottom(k3d::init_name("bottom") + k3d::init_description("Bottom [number]") + k3d::init_value(-0.5) + k3d::init_document(Document) + k3d::init_precision(2) + k3d::init_step_increment(0.01) + k3d::init_units(typeid(k3d::measurement::scalar))),
            m_near(k3d::init_name("near") + k3d::init_description("Near plane distance [number]") + k3d::init_value(1.0) + k3d::init_constraint(k3d::constraint::minimum(0.0)) + k3d::init_document(Document) + k3d::init_precision(1) + k3d::init_step_increment(0.1) + k3d::init_units(typeid(k3d::measurement::distance))),
            m_far(k3d::init_name("far") + k3d::init_description("Far plane distance [number]") + k3d::init_value(1000.0) + k3d::init_constraint(k3d::constraint::minimum(0.0)) + k3d::init_document(Document) + k3d::init_precision(1) + k3d::init_step_increment(0.1) + k3d::init_units(typeid(k3d::measurement::distance))),
            m_headlight(k3d::init_name("headlight") + k3d::init_description("Headlight [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_show_lights(k3d::init_name("show_lights") + k3d::init_description("Show Lights [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_draw_points(k3d::init_name("draw_points") + k3d::init_description("Draw Points [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_draw_edges(k3d::init_name("draw_edges") + k3d::init_description("Draw Edges [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_faces(k3d::init_name("draw_faces") + k3d::init_description("Draw Faces [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_linear_curves(k3d::init_name("draw_linear_curves") + k3d::init_description("Draw Linear Curves [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_cubic_curves(k3d::init_name("draw_cubic_curves") + k3d::init_description("Draw Cubic Curves [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_nucurves(k3d::init_name("draw_nucurves") + k3d::init_description("Draw NURBS Curves [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_bilinear_patches(k3d::init_name("draw_bilinear_patches") + k3d::init_description("Draw Bilinear Patches [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_bicubic_patches(k3d::init_name("draw_bicubic_patches") + k3d::init_description("Draw Bicubic Patches [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_nupatches(k3d::init_name("draw_nupatches") + k3d::init_description("Draw NURBS Patches [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_blobbies(k3d::init_name("draw_blobbies") + k3d::init_description("Draw Blobbies [boolean]") + k3d::init_value(true) + k3d::init_document(Document)),
            m_draw_face_orientations(k3d::init_name("draw_face_orientations") + k3d::init_description("Draw Face Orientations [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_draw_two_sided(k3d::init_name("draw_two_sided") + k3d::init_description("Draw Two Sided [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_draw_safe_zone(k3d::init_name("draw_safe_zone") + k3d::init_description("Draw Safe Zone [boolean]") + k3d::init_value(false) + k3d::init_document(Document)),
            m_host(k3d::init_name("viewport_host") + k3d::init_description("Viewport Host [object]") + k3d::init_object_value(0) + k3d::init_document(Document)),
            m_perspective_projection(m_left, m_right, m_top, m_bottom, m_near, m_far),
            m_orthographic_projection(m_left, m_right, m_top, m_bottom, m_near, m_far)
      {
            enable_serialization(k3d::persistence::proxy(m_point_size));
            enable_serialization(k3d::persistence::proxy(m_background_color));
            enable_serialization(k3d::persistence::proxy(m_fog));
            enable_serialization(k3d::persistence::proxy(m_fog_near));
            enable_serialization(k3d::persistence::proxy(m_fog_far));
            enable_serialization(k3d::persistence::proxy(m_orthographic));
            enable_serialization(k3d::persistence::proxy(m_left));
            enable_serialization(k3d::persistence::proxy(m_right));
            enable_serialization(k3d::persistence::proxy(m_top));
            enable_serialization(k3d::persistence::proxy(m_bottom));
            enable_serialization(k3d::persistence::proxy(m_near));
            enable_serialization(k3d::persistence::proxy(m_far));
            enable_serialization(k3d::persistence::proxy(m_headlight));
            enable_serialization(k3d::persistence::proxy(m_show_lights));
            enable_serialization(k3d::persistence::proxy(m_draw_points));
            enable_serialization(k3d::persistence::proxy(m_draw_edges));
            enable_serialization(k3d::persistence::proxy(m_draw_faces));
            enable_serialization(k3d::persistence::proxy(m_draw_linear_curves));
            enable_serialization(k3d::persistence::proxy(m_draw_cubic_curves));
            enable_serialization(k3d::persistence::proxy(m_draw_nucurves));
            enable_serialization(k3d::persistence::proxy(m_draw_bilinear_patches));
            enable_serialization(k3d::persistence::proxy(m_draw_bicubic_patches));
            enable_serialization(k3d::persistence::proxy(m_draw_nupatches));
            enable_serialization(k3d::persistence::proxy(m_draw_blobbies));
            enable_serialization(k3d::persistence::proxy(m_draw_face_orientations));
            enable_serialization(k3d::persistence::proxy(m_draw_two_sided));
            enable_serialization(k3d::persistence::proxy(m_draw_safe_zone));
            enable_serialization(k3d::persistence::object_proxy(m_host));

            register_property(m_point_size);
            register_property(m_background_color);
            register_property(m_fog);
            register_property(m_fog_near);
            register_property(m_fog_far);
            register_property(m_orthographic);
            register_property(m_left);
            register_property(m_right);
            register_property(m_top);
            register_property(m_bottom);
            register_property(m_near);
            register_property(m_far);
            register_property(m_headlight);
            register_property(m_show_lights);
            register_property(m_draw_points);
            register_property(m_draw_edges);
            register_property(m_draw_faces);
            register_property(m_draw_linear_curves);
            register_property(m_draw_cubic_curves);
            register_property(m_draw_nucurves);
            register_property(m_draw_bilinear_patches);
            register_property(m_draw_bicubic_patches);
            register_property(m_draw_nupatches);
            register_property(m_draw_blobbies);
            register_property(m_draw_face_orientations);
            register_property(m_draw_two_sided);
            register_property(m_draw_safe_zone);
            register_property(m_host);

            k3d::iproperty_group_collection::group visibility_group("Visibility");
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_points));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_edges));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_faces));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_linear_curves));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_cubic_curves));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_nucurves));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_bilinear_patches));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_bicubic_patches));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_nupatches));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_blobbies));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_face_orientations));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_two_sided));
            visibility_group.properties.push_back(&static_cast<k3d::iproperty&>(m_draw_safe_zone));

            k3d::iproperty_group_collection::group projection_group("Projection");
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_orthographic));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_left));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_right));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_top));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_bottom));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_near));
            projection_group.properties.push_back(&static_cast<k3d::iproperty&>(m_far));

            register_property_group(visibility_group);
            register_property_group(projection_group);

            m_point_size.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_background_color.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_fog.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_fog_near.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_fog_far.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_orthographic.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_left.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_right.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_top.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_bottom.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_near.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_far.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_headlight.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_show_lights.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_points.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_edges.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_faces.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_linear_curves.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_cubic_curves.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_nucurves.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_bilinear_patches.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_bicubic_patches.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_nupatches.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_blobbies.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_face_orientations.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_two_sided.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_draw_safe_zone.changed_signal().connect(SigC::bind(m_redraw_request_signal.slot(), k3d::iviewport::ASYNCHRONOUS));
            m_host.changed_signal().connect(SigC::slot(*this, &viewport_implementation::on_host_changed));
      }

      ~viewport_implementation()
      {
      }

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

      static k3d::iplugin_factory& get_factory()
      {
            static k3d::plugin_factory<k3d::document_plugin<viewport_implementation> > factory(
                  k3d::classes::Viewport(),
                  "Viewport",
                  "Viewport",
                  "Objects",
                  k3d::iplugin_factory::STABLE);

            return factory;
      }

      void setup_projection()
      {
            k3d::iprojection& projection = this->projection();

            k3d::iperspective* const perspective = dynamic_cast<k3d::iperspective*>(&projection);
            if(perspective)
                  {
                        glFrustum(
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->left())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->right())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->bottom())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->top())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->near())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), perspective->far())));
                        return;
                  }

            k3d::iorthographic* const orthographic = dynamic_cast<k3d::iorthographic*>(&projection);
            if(orthographic)
                  {
                        glOrtho(
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->left())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->right())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->bottom())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->top())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->near())),
                              boost::any_cast<double>(k3d::get_property_value(document().dag(), orthographic->far())));
                        return;
                  }

            std::cerr << error << __PRETTY_FUNCTION__ << ": unknown projection type" << std::endl;
      }

      void constrain_screen_aspect_ratio(double& Ratio)
      {
            // If we're attached to a host, give it first shot; otherwise, let the ratio float ...
            if(m_host.interface())
                  m_host.interface()->constrain_screen_aspect_ratio(Ratio);

            // Update our viewing frustum to match
            m_left.set_value(-0.5 * Ratio * std::abs(m_bottom.value() - m_top.value()));
            m_right.set_value(0.5 * Ratio * std::abs(m_bottom.value() - m_top.value()));
      }

      k3d::iprojection& projection()
      {
            k3d::iprojection* result = m_host.interface() ? m_host.interface()->projection() : 0;
            if(!result)
                  {
                        if(m_orthographic.property_value())
                              result = &m_orthographic_projection;
                        else
                              result = &m_perspective_projection;
                  }

            return *result;
      }

      void set_host(k3d::iviewport_host* Host)
      {
            m_host.set_object(dynamic_cast<k3d::iobject*>(Host));
      }

      k3d::iviewport_host* host()
      {
            return m_host.interface();
      }
      
      k3d::iviewport::host_changed_signal_t& host_changed_signal()
      {
            return m_host.changed_signal();
      }

      aspect_ratio_changed_signal_t& aspect_ratio_changed_signal()
      {
            return m_aspect_ratio_changed_signal;
      }

      void on_host_changed()
      {
            // Any time the host changes, reset our transformation matrix dependencies
            k3d::idag::dependencies_t dependencies;
            dependencies.insert(std::make_pair(&m_input_matrix.property(), m_host.interface() ? k3d::get_typed_property<k3d::matrix4>(*m_host.interface(), "output_matrix") : static_cast<k3d::iproperty*>(0)));
            document().dag().set_dependencies(dependencies);

            m_host_aspect_ratio_changed_connection.disconnect();

            if(m_host.interface())
                  m_host_aspect_ratio_changed_connection = m_host.interface()->aspect_ratio_changed_signal().connect(m_aspect_ratio_changed_signal.slot());
      }

      void load_complete()
      {
            base::load_complete();

            if(m_host.interface())
                  m_host_aspect_ratio_changed_connection = m_host.interface()->aspect_ratio_changed_signal().connect(m_aspect_ratio_changed_signal.slot());

            if(k3d::application().user_interface())
                  k3d::application().user_interface()->show_viewport(*this);
      }

      void redraw(const unsigned long PixelWidth, const unsigned long PixelHeight, const unsigned long FontListBase)
      {
            // If width or height are zero, we're done ...
            if(!PixelWidth || !PixelHeight)
                  return;

            // Clear background ...
            glClearDepth(1.0);
            const k3d::color background_color(m_background_color.property_value());
            glClearColor(background_color.red, background_color.green, background_color.blue, 0.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            // Set point size ...
            glPointSize(m_point_size.property_value());

            // Setup stippling for path normals / bezier control lines ...
            glLineStipple(2, 0xaaaa);
            glDisable(GL_LINE_STIPPLE);
            glDisable(GL_POLYGON_STIPPLE);

            // Setup culling ...
            glFrontFace(GL_CW);
            glCullFace(GL_BACK);

            // Setup dithering ...
            glDisable(GL_DITHER);

            // Setup antialiasing ...
            glDisable(GL_LINE_SMOOTH);
            glDisable(GL_BLEND);
            glLineWidth(1.0f);

            // Disable stencil ...
            glDisable(GL_STENCIL_TEST);
            glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
            glStencilMask(0x00);

            // Set Z buffer options ...
            glDepthMask(GL_TRUE);
            glDepthFunc(GL_LESS);
            glEnable(GL_DEPTH_TEST);

            // Normalization ...
            glShadeModel(GL_SMOOTH);
            glEnable(GL_NORMALIZE);
            glEnable(GL_AUTO_NORMAL);

            // Prepare texture options ...
            glDisable(GL_TEXTURE_2D);

            // Setup texture matrix
            glMatrixMode(GL_TEXTURE);
            glLoadIdentity();

            // Cache render state to pass to clients ...
            k3d::viewport::render_state state;
            state.draw_points = m_draw_points.property_value();
            state.draw_edges = m_draw_edges.property_value();
            state.draw_faces = m_draw_faces.property_value();
            state.draw_linear_curves = m_draw_linear_curves.property_value();
            state.draw_cubic_curves = m_draw_cubic_curves.property_value();
            state.draw_nucurves = m_draw_nucurves.property_value();
            state.draw_bilinear_patches = m_draw_bilinear_patches.property_value();
            state.draw_bicubic_patches = m_draw_bicubic_patches.property_value();
            state.draw_nupatches = m_draw_nupatches.property_value();
            state.draw_blobbies = m_draw_blobbies.property_value();
            state.draw_face_orientations = m_draw_face_orientations.property_value();
            state.draw_two_sided = m_draw_two_sided.property_value();
            state.gl_ascii_font_list_base = FontListBase;

            // Setup viewport ...
            glViewport(0, 0, PixelWidth, PixelHeight);
            glGetIntegerv(GL_VIEWPORT, state.gl_viewport);

            // Setup projection ...
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            setup_projection();
            glGetFloatv(GL_PROJECTION_MATRIX, state.gl_projection_matrix);

            // Setup lights ...
            glEnable(GL_LIGHTING);
            glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
            glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

            // Make sure all lights are turned off initially ...
            GLint maxlights = 0;
            glGetIntegerv(GL_MAX_LIGHTS, &maxlights);
            for(GLint i = 0; i < maxlights; ++i)
                  glDisable(GLenum(GL_LIGHT0 + i));

            // Setup modelview matrix
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();

            // Setup the headlight ...
            if(m_headlight.property_value())
                  {
                        // Enable the light ...
                        glEnable(GL_LIGHT0);

                        // Setup color ...
                        const GLfloat color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
                        glLightfv(GL_LIGHT0, GL_AMBIENT, color);
                        glLightfv(GL_LIGHT0, GL_DIFFUSE, color);
                        glLightfv(GL_LIGHT0, GL_SPECULAR, color);

                        // Setup light direction ...
                        const GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
                        glLightfv(GL_LIGHT0, GL_POSITION, position);
                  }

            const k3d::matrix4 transform_matrix(matrix());
            const k3d::angle_axis orientation(k3d::euler_angles(transform_matrix, k3d::euler_angles::ZXYstatic));
            const k3d::vector3 position(k3d::extractTranslation(transform_matrix));

            glScaled(1.0, 1.0, -1.0);
            glRotated(-k3d::degrees(orientation.angle), orientation.axis[0], orientation.axis[1], orientation.axis[2]);
            glTranslated(-position[0], -position[1], -position[2]);

            // Setup fog ...
            if(m_fog.property_value())
                  {
                        const k3d::color background_color(m_background_color.property_value());

                        GLfloat fogdata[4];
                        fogdata[0] = background_color.red;
                        fogdata[1] = background_color.green;
                        fogdata[2] = background_color.blue;
                        fogdata[3] = 1.0f;

                        glFogfv(GL_FOG_COLOR, fogdata);
                        glFogf(GL_FOG_START, static_cast<GLfloat>(m_fog_near.property_value()));
                        glFogf(GL_FOG_END, static_cast<GLfloat>(m_fog_far.property_value()));
                        glHint(GL_FOG_HINT, GL_NICEST);
                        glFogi(GL_FOG_MODE, GL_LINEAR);
                        glEnable(GL_FOG);
                  }
            else
                  {
                        glDisable(GL_FOG);
                  }

            if(m_show_lights.property_value())
                  std::for_each(document().objects().collection().begin(), document().objects().collection().end(), detail::light_setup());

            std::for_each(document().objects().collection().begin(), document().objects().collection().end(), detail::draw(state));

            // Optionally draw a safe zone for video ...
            if(m_draw_safe_zone.property_value())
                  {
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        glOrtho(-1, 1, -1, 1, -1, 1);

                        glMatrixMode(GL_MODELVIEW);
                        glLoadIdentity();

                        glColor4d(0, 0, 0, 1);
                        glDisable(GL_LIGHTING);

                        glBegin(GL_LINE_LOOP);
                        glVertex2d(-0.8, 0.8);
                        glVertex2d(0.8, 0.8);
                        glVertex2d(0.8, -0.8);
                        glVertex2d(-0.8, -0.8);
                        glEnd();
                  }

/* I really hate to lose this feedback, but the GLU NURBS routines generate large numbers of errors, which ruins its utility :-(
            for(GLenum gl_error = glGetError(); gl_error != GL_NO_ERROR; gl_error = glGetError())
                  std::cerr << error << "OpenGL error: " << reinterpret_cast<const char*>(gluErrorString(gl_error)) << std::endl;
*/
      }

      redraw_request_signal_t& redraw_request_signal()
      {
            return m_redraw_request_signal;
      }

      void select(const unsigned long PixelWidth, const unsigned long PixelHeight, const unsigned long FontListBase, const k3d::rectangle& Region)
      {
            // If width or height are zero, we're done ...
            if(!PixelWidth || !PixelHeight)
                  return;

            // Setup culling ...
            glFrontFace(GL_CW);
            glCullFace(GL_BACK);

            // Setup dithering ...
            glDisable(GL_DITHER);

            // Setup antialiasing ...
            glDisable(GL_LINE_SMOOTH);
            glDisable(GL_BLEND);
            glLineWidth(1.0f);

            // Disable stencil ...
            glDisable(GL_STENCIL_TEST);
            glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
            glStencilMask(0x00);

            // Set Z buffer options ...
            glDepthMask(GL_TRUE);
            glDepthFunc(GL_LESS);
            glEnable(GL_DEPTH_TEST);

            // Normalization ...
            glShadeModel(GL_FLAT);
            glDisable(GL_NORMALIZE);
            glDisable(GL_AUTO_NORMAL);

            glDisable(GL_TEXTURE_2D);

            // Cache render state to pass to clients ...
            k3d::viewport::render_state state;
            state.draw_points = m_draw_points.property_value();
            state.draw_edges = m_draw_edges.property_value();
            state.draw_faces = m_draw_faces.property_value();
            state.draw_linear_curves = m_draw_linear_curves.property_value();
            state.draw_cubic_curves = m_draw_cubic_curves.property_value();
            state.draw_nucurves = m_draw_nucurves.property_value();
            state.draw_bilinear_patches = m_draw_bilinear_patches.property_value();
            state.draw_bicubic_patches = m_draw_bicubic_patches.property_value();
            state.draw_nupatches = m_draw_nupatches.property_value();
            state.draw_blobbies = m_draw_blobbies.property_value();
            state.draw_face_orientations = m_draw_face_orientations.property_value();
            state.draw_two_sided = m_draw_two_sided.property_value();
            state.gl_ascii_font_list_base = FontListBase;

            // Setup viewport ...
            glViewport(0, 0, PixelWidth, PixelHeight);
            glGetIntegerv(GL_VIEWPORT, state.gl_viewport);

            // First, setup the projection matrix as we would normally do for drawing (so we can store the projection matrix, unaffected by the pick matrix) ...
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            setup_projection();
            glGetFloatv(GL_PROJECTION_MATRIX, state.gl_projection_matrix);

            // Second, setup the projection matrix with the pick matrix ...
            glLoadIdentity();

            const double width  = Region.Width();
            const double height = Region.Height();
            gluPickMatrix(Region.Left() + (width * 0.5), state.gl_viewport[3] - (Region.Top() + (height * 0.5)), width, height, state.gl_viewport);
            setup_projection();

            // Setup modelview matrix
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();

            const k3d::matrix4 transform_matrix(matrix());
            const k3d::angle_axis orientation(k3d::euler_angles(transform_matrix, k3d::euler_angles::ZXYstatic));
            const k3d::vector3 position(k3d::extractTranslation(transform_matrix));

            glScaled(1.0, 1.0, -1.0);
            glRotated(-k3d::degrees(orientation.angle), orientation.axis[0], orientation.axis[1], orientation.axis[2]);
            glTranslated(-position[0], -position[1], -position[2]);

            // Clear background ...
            glClear(GL_DEPTH_BUFFER_BIT);

            glDisable(GL_LIGHTING);

            std::for_each(document().objects().collection().begin(), document().objects().collection().end(), detail::draw_selection(state));

/* I really hate to lose this feedback, but the glu NURBS routines generate large numbers of errors, which ruins its utility :-(
            for(GLenum gl_error = glGetError(); gl_error != GL_NO_ERROR; gl_error = glGetError())
                  std::cerr << error << "OpenGL error: " << reinterpret_cast<const char*>(gluErrorString(gl_error)) << std::endl;
*/
      }

private:
      redraw_request_signal_t m_redraw_request_signal;

      k3d_measurement_property(unsigned long, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_point_size;
      k3d_data_property(k3d::color, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_background_color;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_fog;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::with_constraint) m_fog_near;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::with_constraint) m_fog_far;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_orthographic;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_left;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_right;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_top;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_bottom;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::with_constraint) m_near;
      k3d_measurement_property(double, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::with_constraint) m_far;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_headlight;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_show_lights;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_points;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_edges;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_faces;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_linear_curves;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_cubic_curves;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_nucurves;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_bilinear_patches;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_bicubic_patches;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_nupatches;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_blobbies;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_face_orientations;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_two_sided;
      k3d_data_property(bool, k3d::immutable_name, k3d::change_signal, k3d::no_undo, k3d::local_storage, k3d::no_constraint) m_draw_safe_zone;

      k3d_object_property(k3d::iviewport_host, k3d::immutable_name, k3d::with_undo, k3d::local_storage) m_host;

      class perspective_projection :
            public k3d::iperspective
      {
      public:
            perspective_projection(k3d::iproperty& Left, k3d::iproperty& Right, k3d::iproperty& Top, k3d::iproperty& Bottom, k3d::iproperty& Near, k3d::iproperty& Far) :
                  m_left(Left),
                  m_right(Right),
                  m_top(Top),
                  m_bottom(Bottom),
                  m_near(Near),
                  m_far(Far)
            {
            }

            k3d::iproperty& left()
            {
                  return m_left;
            }

            k3d::iproperty& right()
            {
                  return m_right;
            }

            k3d::iproperty& top()
            {
                  return m_top;
            }

            k3d::iproperty& bottom()
            {
                  return m_bottom;
            }

            k3d::iproperty& near()
            {
                  return m_near;
            }

            k3d::iproperty& far()
            {
                  return m_far;
            }

      private:
            k3d::iproperty& m_left;
            k3d::iproperty& m_right;
            k3d::iproperty& m_top;
            k3d::iproperty& m_bottom;
            k3d::iproperty& m_near;
            k3d::iproperty& m_far;
      };

      class orthographic_projection :
            public k3d::iorthographic
      {
      public:
            orthographic_projection(k3d::iproperty& Left, k3d::iproperty& Right, k3d::iproperty& Top, k3d::iproperty& Bottom, k3d::iproperty& Near, k3d::iproperty& Far) :
                  m_left(Left),
                  m_right(Right),
                  m_top(Top),
                  m_bottom(Bottom),
                  m_near(Near),
                  m_far(Far)
            {
            }

            k3d::iproperty& left()
            {
                  return m_left;
            }

            k3d::iproperty& right()
            {
                  return m_right;
            }

            k3d::iproperty& top()
            {
                  return m_top;
            }

            k3d::iproperty& bottom()
            {
                  return m_bottom;
            }

            k3d::iproperty& near()
            {
                  return m_near;
            }

            k3d::iproperty& far()
            {
                  return m_far;
            }

      private:
            k3d::iproperty& m_left;
            k3d::iproperty& m_right;
            k3d::iproperty& m_top;
            k3d::iproperty& m_bottom;
            k3d::iproperty& m_near;
            k3d::iproperty& m_far;
      };

      perspective_projection m_perspective_projection;
      orthographic_projection m_orthographic_projection;

      SigC::Connection m_host_aspect_ratio_changed_connection;

      aspect_ratio_changed_signal_t m_aspect_ratio_changed_signal;
};

/////////////////////////////////////////////////////////////////////////////
// viewport_factory

k3d::iplugin_factory& viewport_factory()
{
      return viewport_implementation::get_factory();
}

} // namespace libk3dviewport

K3D_MODULE_START(k3d::uuid(0xae701c78, 0x095243c1, 0xa3739654, 0xe43c80db), Registry)
      Registry.register_factory(libk3dviewport::viewport_factory());
K3D_MODULE_END

Generated by  Doxygen 1.6.0   Back to index