Mercurial > octave
view libinterp/corefcn/gl-render.cc @ 29569:29a1f8fd8ee6
move idx_vector classes inside octave namespace
* idx-vector.h, idx-vector.cc:
Move idx_vector classes inside octave namespace. Update all uses
outside of octave namespace to use octave:: tag.
* Sparse.h: Eliminate forward declaration of idx_vector.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 28 Apr 2021 13:46:02 -0400 |
parents | 2031c0f76d02 |
children | ebd1ac75e99f |
line wrap: on
line source
//////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2021 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or <https://octave.org/copyright/>. // // This file is part of Octave. // // Octave 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 3 of the License, or // (at your option) any later version. // // Octave 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 Octave; see the file COPYING. If not, see // <https://www.gnu.org/licenses/>. // //////////////////////////////////////////////////////////////////////// #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include <limits> #include <memory> #include <sstream> #if defined (HAVE_WINDOWS_H) # define WIN32_LEAN_AND_MEAN # include <windows.h> #endif #include "lo-mappers.h" #include "oct-locbuf.h" #include "errwarn.h" #include "gl-render.h" #include "interpreter-private.h" #include "oct-opengl.h" #include "text-renderer.h" namespace octave { #if defined (HAVE_OPENGL) static int next_power_of_2 (int n) { int m = 1; while (m < n && m < std::numeric_limits<int>::max ()) m <<= 1; return m; } #define LIGHT_MODE GL_FRONT_AND_BACK // Use symbolic names for axes enum { X_AXIS, Y_AXIS, Z_AXIS }; // Use symbolic names for color mode enum { UNIFORM, FLAT, INTERP, TEXTURE }; // Use symbolic names for lighting enum { NONE, //FLAT, // Already declared in anonymous enum for color mode GOURAUD = 2 }; // Win32 API requires the CALLBACK attributes for // GLU callback functions. Define it to empty on // other platforms. #if ! defined (CALLBACK) # define CALLBACK #endif class opengl_texture { private: class texture_rep { public: texture_rep (opengl_functions& glfcns) : m_glfcns (glfcns), m_id (), m_w (), m_h (), m_tw (), m_th (), m_tx (), m_ty (), m_valid (false) { } texture_rep (opengl_functions& glfcns, GLuint id, int w, int h, int tw, int th) : m_glfcns (glfcns), m_id (id), m_w (w), m_h (h), m_tw (tw), m_th (th), m_tx (double(m_w)/m_tw), m_ty (double(m_h)/m_th), m_valid (true) { } ~texture_rep (void) { if (m_valid) m_glfcns.glDeleteTextures (1, &m_id); } void bind (int mode) const { if (m_valid) m_glfcns.glBindTexture (mode, m_id); } void tex_coord (double q, double r) const { if (m_valid) m_glfcns.glTexCoord2d (q*m_tx, r*m_ty); } opengl_functions& m_glfcns; GLuint m_id; int m_w, m_h; int m_tw, m_th; double m_tx, m_ty; bool m_valid; }; public: opengl_texture (opengl_functions& glfcns) : m_rep (new texture_rep (glfcns)) { } opengl_texture (opengl_functions& glfcns, GLuint id, int w, int h, int tw, int th) : m_rep (new texture_rep (glfcns, id, w, h, tw, th)) { } opengl_texture (const opengl_texture&) = default; ~opengl_texture (void) = default; opengl_texture& operator = (const opengl_texture&) = default; static opengl_texture create (opengl_functions& glfcns, const octave_value& data); void bind (int mode = GL_TEXTURE_2D) const { m_rep->bind (mode); } void tex_coord (double q, double r) const { m_rep->tex_coord (q, r); } bool is_valid (void) const { return m_rep->m_valid; } private: opengl_texture (const std::shared_ptr<texture_rep>& new_rep) : m_rep (new_rep) { } std::shared_ptr<texture_rep> m_rep; }; opengl_texture opengl_texture::create (opengl_functions& glfcns, const octave_value& data) { opengl_texture retval (glfcns); dim_vector dv (data.dims ()); // Expect RGB data if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) { // FIXME: dim_vectors hold octave_idx_type values. // Should we check for dimensions larger than intmax? int h, w, tw, th; h = dv(0), w = dv(1); // Return early if the image data are larger than the texture // can hold int max_size; glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size); static bool warned = false; if (h > max_size || w > max_size) { if (! warned) { warning ("opengl_texture::create: the opengl library in use " "doesn't support images with either dimension larger " "than %d. Not rendering.", max_size); warned = true; } return opengl_texture (glfcns); } GLuint id; bool ok = true; tw = next_power_of_2 (w); th = next_power_of_2 (h); glfcns.glGenTextures (1, &id); glfcns.glBindTexture (GL_TEXTURE_2D, id); if (data.is_double_type ()) { const NDArray xdata = data.array_value (); OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); for (int i = 0; i < h; i++) { for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) { a[idx] = xdata(i,j,0); a[idx+1] = xdata(i,j,1); a[idx+2] = xdata(i,j,2); } } glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a); } else if (data.is_single_type ()) { const FloatNDArray xdata = data.float_array_value (); OCTAVE_LOCAL_BUFFER (GLfloat, a, (3*tw*th)); for (int i = 0; i < h; i++) { for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) { a[idx] = xdata(i,j,0); a[idx+1] = xdata(i,j,1); a[idx+2] = xdata(i,j,2); } } glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a); } else if (data.is_uint16_type ()) { const uint16NDArray xdata = data.uint16_array_value (); OCTAVE_LOCAL_BUFFER (GLushort, a, (3*tw*th)); for (int i = 0; i < h; i++) { for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) { a[idx] = xdata(i,j,0); a[idx+1] = xdata(i,j,1); a[idx+2] = xdata(i,j,2); } } glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT, a); } else if (data.is_uint8_type () && dv(2) == 3) { const uint8NDArray xdata = data.uint8_array_value (); OCTAVE_LOCAL_BUFFER (GLubyte, a, (3*tw*th)); for (int i = 0; i < h; i++) { for (int j = 0, idx = i*tw*3; j < w; j++, idx += 3) { a[idx] = xdata(i,j,0); a[idx+1] = xdata(i,j,1); a[idx+2] = xdata(i,j,2); } } glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, a); } else if (data.is_uint8_type () && dv(2) == 4) { const uint8NDArray xdata = data.uint8_array_value (); OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th)); for (int i = 0; i < h; i++) { for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4) { a[idx] = xdata(i,j,0); a[idx+1] = xdata(i,j,1); a[idx+2] = xdata(i,j,2); a[idx+3] = xdata(i,j,3); } } glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, a); } else { ok = false; warning ("opengl_texture::create: invalid image data type, expected double, single, uint8, or uint16"); } if (ok) { glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); if (glfcns.glGetError () != GL_NO_ERROR) warning ("opengl_texture::create: OpenGL error while generating texture data"); else retval = opengl_texture (glfcns, id, w, h, tw, th); } } else warning ("opengl_texture::create: invalid texture data size"); return retval; } class opengl_tessellator { public: #if defined (HAVE_FRAMEWORK_OPENGL) && defined (HAVE_GLUTESSCALLBACK_THREEDOTS) typedef GLvoid (CALLBACK *fcn) (...); #else typedef void (CALLBACK *fcn) (void); #endif public: opengl_tessellator (void) : glu_tess (nullptr), fill () { init (); } // No copying! opengl_tessellator (const opengl_tessellator&) = delete; opengl_tessellator operator = (const opengl_tessellator&) = delete; virtual ~opengl_tessellator (void) { if (glu_tess) gluDeleteTess (glu_tess); } void begin_polygon (bool filled = true) { gluTessProperty (glu_tess, GLU_TESS_BOUNDARY_ONLY, (filled ? GL_FALSE : GL_TRUE)); fill = filled; gluTessBeginPolygon (glu_tess, this); } void end_polygon (void) const { gluTessEndPolygon (glu_tess); } void begin_contour (void) const { gluTessBeginContour (glu_tess); } void end_contour (void) const { gluTessEndContour (glu_tess); } void add_vertex (double *loc, void *data) const { gluTessVertex (glu_tess, loc, data); } protected: virtual void begin (GLenum /*type*/) { } virtual void end (void) { } virtual void vertex (void * /*data*/) { } virtual void combine (GLdouble [3] /*c*/, void * [4] /*data*/, GLfloat [4] /*w*/, void ** /*out_data*/) { } virtual void edge_flag (GLboolean /*flag*/) { } virtual void error (GLenum err) { ::error ("OpenGL tessellation error (%d)", err); } virtual void init (void) { glu_tess = gluNewTess (); gluTessCallback (glu_tess, GLU_TESS_BEGIN_DATA, reinterpret_cast<fcn> (tess_begin)); gluTessCallback (glu_tess, GLU_TESS_END_DATA, reinterpret_cast<fcn> (tess_end)); gluTessCallback (glu_tess, GLU_TESS_VERTEX_DATA, reinterpret_cast<fcn> (tess_vertex)); gluTessCallback (glu_tess, GLU_TESS_COMBINE_DATA, reinterpret_cast<fcn> (tess_combine)); gluTessCallback (glu_tess, GLU_TESS_EDGE_FLAG_DATA, reinterpret_cast<fcn> (tess_edge_flag)); gluTessCallback (glu_tess, GLU_TESS_ERROR_DATA, reinterpret_cast<fcn> (tess_error)); } bool is_filled (void) const { return fill; } private: static void CALLBACK tess_begin (GLenum type, void *t) { reinterpret_cast<opengl_tessellator *> (t)->begin (type); } static void CALLBACK tess_end (void *t) { reinterpret_cast<opengl_tessellator *> (t)->end (); } static void CALLBACK tess_vertex (void *v, void *t) { reinterpret_cast<opengl_tessellator *> (t)->vertex (v); } static void CALLBACK tess_combine (GLdouble c[3], void *v[4], GLfloat w[4], void **out, void *t) { reinterpret_cast<opengl_tessellator *> (t)->combine (c, v, w, out); } static void CALLBACK tess_edge_flag (GLboolean flag, void *t) { reinterpret_cast<opengl_tessellator *> (t)->edge_flag (flag); } static void CALLBACK tess_error (GLenum err, void *t) { reinterpret_cast<opengl_tessellator *> (t)->error (err); } private: GLUtesselator *glu_tess; bool fill; }; class vertex_data { public: class vertex_data_rep { public: vertex_data_rep (void) : m_coords (), m_color (), m_vertex_normal (), m_face_normal (), m_alpha (), m_ambient (), m_diffuse (), m_specular (), m_specular_exp (), m_specular_color_refl () { } vertex_data_rep (const Matrix& c, const Matrix& col, const Matrix& vn, const Matrix& fn, double a, float as, float ds, float ss, float se, float scr) : m_coords (c), m_color (col), m_vertex_normal (vn), m_face_normal (fn), m_alpha (a), m_ambient (as), m_diffuse (ds), m_specular (ss), m_specular_exp (se), m_specular_color_refl (scr) { } Matrix m_coords; Matrix m_color; Matrix m_vertex_normal; Matrix m_face_normal; double m_alpha; float m_ambient; float m_diffuse; float m_specular; float m_specular_exp; float m_specular_color_refl; }; public: // Required to instantiate std::list<vertex_data> objects. vertex_data (void) : m_rep (nil_rep ()) { } vertex_data (const Matrix& c, const Matrix& col, const Matrix& vn, const Matrix& fn, double a, float as, float ds, float ss, float se, float scr) : m_rep (new vertex_data_rep (c, col, vn, fn, a, as, ds, ss, se, scr)) { } vertex_data (const vertex_data&) = default; ~vertex_data (void) = default; vertex_data& operator = (const vertex_data&) = default; vertex_data_rep * get_rep (void) const { return m_rep.get (); } private: static std::shared_ptr<vertex_data_rep> nil_rep (void) { static std::shared_ptr<vertex_data_rep> nr (new vertex_data_rep ()); return nr; } std::shared_ptr<vertex_data_rep> m_rep; }; class opengl_renderer::patch_tessellator : public opengl_tessellator { public: patch_tessellator (opengl_renderer *r, int cmode, int lmode, bool fl, float idx = 0.0) : opengl_tessellator (), renderer (r), color_mode (cmode), light_mode (lmode), face_lighting (fl), index (idx), first (true), tmp_vdata () { } protected: void begin (GLenum type) { opengl_functions& glfcns = renderer->get_opengl_functions (); //printf ("patch_tessellator::begin (%d)\n", type); first = true; if (color_mode == INTERP || light_mode == GOURAUD) glfcns.glShadeModel (GL_SMOOTH); else glfcns.glShadeModel (GL_FLAT); if (is_filled ()) renderer->set_polygon_offset (true, index); glfcns.glBegin (type); } void end (void) { opengl_functions& glfcns = renderer->get_opengl_functions (); //printf ("patch_tessellator::end\n"); glfcns.glEnd (); renderer->set_polygon_offset (false); } void vertex (void *data) { opengl_functions& glfcns = renderer->get_opengl_functions (); vertex_data::vertex_data_rep *v = reinterpret_cast<vertex_data::vertex_data_rep *> (data); //printf ("patch_tessellator::vertex (%g, %g, %g)\n", v->m_coords(0), v->m_coords(1), v->m_coords(2)); // NOTE: OpenGL can re-order vertices. For "flat" coloring of FaceColor // the first vertex must be identified in the draw_patch routine. if (color_mode == INTERP || (color_mode == FLAT && ! is_filled ())) { Matrix col = v->m_color; if (col.numel () == 3) { glfcns.glColor4d (col(0), col(1), col(2), v->m_alpha); if (light_mode > 0) { // edge lighting only uses ambient light float buf[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; if (face_lighting) for (int k = 0; k < 3; k++) buf[k] = (v->m_specular * (v->m_specular_color_refl + (1 - v->m_specular_color_refl) * col(k))); glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf); if (face_lighting) for (int k = 0; k < 3; k++) buf[k] = (v->m_diffuse * col(k)); glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf); for (int k = 0; k < 3; k++) buf[k] = (v->m_ambient * col(k)); glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf); } } } if (light_mode == FLAT && first) glfcns.glNormal3dv (v->m_face_normal.data ()); else if (light_mode == GOURAUD) glfcns.glNormal3dv (v->m_vertex_normal.data ()); glfcns.glVertex3dv (v->m_coords.data ()); first = false; } void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data) { vertex_data::vertex_data_rep *v[4]; int vmax = 4; for (int i = 0; i < 4; i++) { v[i] = reinterpret_cast<vertex_data::vertex_data_rep *> (data[i]); if (vmax == 4 && ! v[i]) vmax = i; } Matrix vv (1, 3, 0.0); Matrix cc; Matrix vnn (1, 3, 0.0); Matrix fnn (1, 3, 0.0); double aa = 0.0; vv(0) = xyz[0]; vv(1) = xyz[1]; vv(2) = xyz[2]; if (v[0]->m_color.numel ()) { cc.resize (1, 3, 0.0); for (int ic = 0; ic < 3; ic++) for (int iv = 0; iv < vmax; iv++) cc(ic) += (w[iv] * v[iv]->m_color (ic)); } if (v[0]->m_vertex_normal.numel () > 0) { for (int in = 0; in < 3; in++) for (int iv = 0; iv < vmax; iv++) vnn(in) += (w[iv] * v[iv]->m_vertex_normal (in)); } if (v[0]->m_face_normal.numel () > 0) { for (int in = 0; in < 3; in++) for (int iv = 0; iv < vmax; iv++) fnn(in) += (w[iv] * v[iv]->m_face_normal (in)); } for (int iv = 0; iv < vmax; iv++) aa += (w[iv] * v[iv]->m_alpha); vertex_data new_v (vv, cc, vnn, fnn, aa, v[0]->m_ambient, v[0]->m_diffuse, v[0]->m_specular, v[0]->m_specular_exp, v[0]->m_specular_color_refl); tmp_vdata.push_back (new_v); *out_data = new_v.get_rep (); } private: // No copying! patch_tessellator (const patch_tessellator&) = delete; patch_tessellator& operator = (const patch_tessellator&) = delete; opengl_renderer *renderer; int color_mode; int light_mode; bool face_lighting; int index; bool first; std::list<vertex_data> tmp_vdata; }; #else class opengl_renderer::patch_tessellator { // Dummy class. }; #endif opengl_renderer::opengl_renderer (opengl_functions& glfcns) : m_glfcns (glfcns), xmin (), xmax (), ymin (), ymax (), zmin (), zmax (), m_devpixratio (1.0), xform (), toolkit (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (), camera_pos (), camera_dir (), view_vector (), interpreter ("none"), txt_renderer (), m_current_light (0), m_max_lights (0), selecting (false), m_printing (false) { // This constructor will fail if we don't have OpenGL or if the data // types we assumed in our public interface aren't compatible with the // OpenGL types. #if defined (HAVE_OPENGL) // Ensure that we can't request an image larger than OpenGL can handle. // FIXME: should we check signed vs. unsigned? static bool ok = (sizeof (int) <= sizeof (GLsizei)); if (! ok) error ("the size of GLsizei is smaller than the size of int"); #else err_disabled_feature ("opengl_renderer", "OpenGL"); #endif } void opengl_renderer::draw (const graphics_object& go, bool toplevel) { if (! go.valid_object ()) return; const base_properties& props = go.get_properties (); if (! toolkit) toolkit = props.get_toolkit (); if (go.isa ("figure")) draw_figure (dynamic_cast<const figure::properties&> (props)); else if (go.isa ("axes")) draw_axes (dynamic_cast<const axes::properties&> (props)); else if (go.isa ("line")) draw_line (dynamic_cast<const line::properties&> (props)); else if (go.isa ("surface")) draw_surface (dynamic_cast<const surface::properties&> (props)); else if (go.isa ("patch")) draw_patch (dynamic_cast<const patch::properties&> (props)); else if (go.isa ("scatter")) draw_scatter (dynamic_cast<const scatter::properties&> (props)); else if (go.isa ("light")) draw_light (dynamic_cast<const light::properties&> (props)); else if (go.isa ("hggroup")) draw_hggroup (dynamic_cast<const hggroup::properties&> (props)); else if (go.isa ("text")) draw_text (dynamic_cast<const text::properties&> (props)); else if (go.isa ("image")) draw_image (dynamic_cast<const image::properties&> (props)); else if (go.isa ("uimenu") || go.isa ("uicontrol") || go.isa ("uicontextmenu") || go.isa ("uitoolbar") || go.isa ("uipushtool") || go.isa ("uitoggletool") || go.isa ("uitable")) ; // SKIP else if (go.isa ("uipanel")) { if (toplevel) draw_uipanel (dynamic_cast<const uipanel::properties&> (props), go); } else if (go.isa ("uibuttongroup")) { if (toplevel) draw_uibuttongroup (dynamic_cast<const uibuttongroup::properties&> (props), go); } else { warning ("opengl_renderer: cannot render object of type '%s'", props.graphics_object_name ().c_str ()); } #if defined (HAVE_OPENGL) GLenum gl_error = m_glfcns.glGetError (); if (gl_error) warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object", gluErrorString (gl_error), gl_error, props.graphics_object_name ().c_str ()); #endif } void opengl_renderer::draw_figure (const figure::properties& props) { m_printing = props.is___printing__ (); // Initialize OpenGL context init_gl_context (props.is_graphicssmoothing (), props.get_color_rgb ()); #if defined (HAVE_OPENGL) props.set___gl_extensions__ (get_string (GL_EXTENSIONS)); props.set___gl_renderer__ (get_string (GL_RENDERER)); props.set___gl_vendor__ (get_string (GL_VENDOR)); props.set___gl_version__ (get_string (GL_VERSION)); #endif // Draw children draw (props.get_all_children (), false); } void opengl_renderer::draw_uipanel (const uipanel::properties& props, const graphics_object& go) { graphics_object fig = go.get_ancestor ("figure"); const figure::properties& figProps = dynamic_cast<const figure::properties&> (fig.get_properties ()); // Initialize OpenGL context init_gl_context (figProps.is_graphicssmoothing (), props.get_backgroundcolor_rgb ()); // Draw children draw (props.get_all_children (), false); } void opengl_renderer::draw_uibuttongroup (const uibuttongroup::properties& props, const graphics_object& go) { graphics_object fig = go.get_ancestor ("figure"); const figure::properties& figProps = dynamic_cast<const figure::properties&> (fig.get_properties ()); // Initialize OpenGL context init_gl_context (figProps.is_graphicssmoothing (), props.get_backgroundcolor_rgb ()); // Draw children draw (props.get_all_children (), false); } void opengl_renderer::init_gl_context (bool enhanced, const Matrix& c) { #if defined (HAVE_OPENGL) // Initialize OpenGL context m_glfcns.glEnable (GL_DEPTH_TEST); m_glfcns.glDepthFunc (GL_LEQUAL); m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_glfcns.glAlphaFunc (GL_GREATER, 0.0f); m_glfcns.glEnable (GL_NORMALIZE); m_glfcns.glEnable (GL_BLEND); if (enhanced) { m_glfcns.glEnable (GL_MULTISAMPLE); bool has_multisample = false; if (! m_glfcns.glGetError ()) { GLint iMultiSample, iNumSamples; m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample); m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples); if (iMultiSample == GL_TRUE && iNumSamples > 0) has_multisample = true; } if (! has_multisample) { // MultiSample not implemented. Use old-style anti-aliasing m_glfcns.glDisable (GL_MULTISAMPLE); // Disabling GL_MULTISAMPLE will raise a gl error if it is not // implemented. Thus, call glGetError to reset the error state. m_glfcns.glGetError (); m_glfcns.glEnable (GL_LINE_SMOOTH); m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); } } else { m_glfcns.glDisable (GL_LINE_SMOOTH); } // Clear background if (c.numel () >= 3) { m_glfcns.glClearColor (c(0), c(1), c(2), 1); m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } GLenum gl_error = m_glfcns.glGetError (); if (gl_error) warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context", gluErrorString (gl_error), gl_error); #else octave_unused_parameter (enhanced); octave_unused_parameter (c); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::render_grid (const double linewidth, const std::string& gridstyle, const Matrix& gridcolor, const double gridalpha, const Matrix& ticks, double lim1, double lim2, double p1, double p1N, double p2, double p2N, int xyz, bool is_3D) { #if defined (HAVE_OPENGL) m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha); set_linestyle (gridstyle, true, linewidth); m_glfcns.glBegin (GL_LINES); for (int i = 0; i < ticks.numel (); i++) { double val = ticks(i); if (lim1 <= val && val <= lim2) { if (xyz == X_AXIS) { m_glfcns.glVertex3d (val, p1N, p2); m_glfcns.glVertex3d (val, p1, p2); if (is_3D) { m_glfcns.glVertex3d (val, p1, p2N); m_glfcns.glVertex3d (val, p1, p2); } } else if (xyz == Y_AXIS) { m_glfcns.glVertex3d (p1N, val, p2); m_glfcns.glVertex3d (p1, val, p2); if (is_3D) { m_glfcns.glVertex3d (p1, val, p2N); m_glfcns.glVertex3d (p1, val, p2); } } else if (xyz == Z_AXIS) { m_glfcns.glVertex3d (p1N, p2, val); m_glfcns.glVertex3d (p1, p2, val); m_glfcns.glVertex3d (p1, p2N, val); m_glfcns.glVertex3d (p1, p2, val); } } } m_glfcns.glEnd (); set_linestyle ("-"); // Disable LineStipple double black[3] = {0, 0, 0}; m_glfcns.glColor3dv (black); #else octave_unused_parameter (linewidth); octave_unused_parameter (gridstyle); octave_unused_parameter (gridcolor); octave_unused_parameter (gridalpha); octave_unused_parameter (ticks); octave_unused_parameter (lim1); octave_unused_parameter (lim2); octave_unused_parameter (p1); octave_unused_parameter (p1N); octave_unused_parameter (p2); octave_unused_parameter (p2N); octave_unused_parameter (xyz); octave_unused_parameter (is_3D); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::render_tickmarks (const Matrix& ticks, double lim1, double lim2, double p1, double p1N, double p2, double p2N, double dx, double dy, double dz, int xyz, bool mirror) { #if defined (HAVE_OPENGL) m_glfcns.glBegin (GL_LINES); for (int i = 0; i < ticks.numel (); i++) { double val = ticks(i); if (lim1 <= val && val <= lim2) { if (xyz == X_AXIS) { m_glfcns.glVertex3d (val, p1, p2); m_glfcns.glVertex3d (val, p1+dy, p2+dz); if (mirror) { m_glfcns.glVertex3d (val, p1N, p2N); m_glfcns.glVertex3d (val, p1N-dy, p2N-dz); } } else if (xyz == Y_AXIS) { m_glfcns.glVertex3d (p1, val, p2); m_glfcns.glVertex3d (p1+dx, val, p2+dz); if (mirror) { m_glfcns.glVertex3d (p1N, val, p2N); m_glfcns.glVertex3d (p1N-dx, val, p2N-dz); } } else if (xyz == Z_AXIS) { m_glfcns.glVertex3d (p1, p2, val); m_glfcns.glVertex3d (p1+dx, p2+dy, val); if (mirror) { m_glfcns.glVertex3d (p1N, p2N, val); m_glfcns.glVertex3d (p1N-dx, p2N-dy, val); } } } } m_glfcns.glEnd (); #else octave_unused_parameter (ticks); octave_unused_parameter (lim1); octave_unused_parameter (lim2); octave_unused_parameter (p1); octave_unused_parameter (p1N); octave_unused_parameter (p2); octave_unused_parameter (p2N); octave_unused_parameter (dx); octave_unused_parameter (dy); octave_unused_parameter (dz); octave_unused_parameter (xyz); octave_unused_parameter (mirror); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::render_ticktexts (const Matrix& ticks, const string_vector& ticklabels, double lim1, double lim2, double p1, double p2, int xyz, int ha, int va, int& wmax, int& hmax) { #if defined (HAVE_OPENGL) int nticks = ticks.numel (); int nlabels = ticklabels.numel (); if (nlabels == 0) return; for (int i = 0; i < nticks; i++) { double val = ticks(i); if (lim1 <= val && val <= lim2) { Matrix b; std::string label (ticklabels(i % nlabels)); label.erase (0, label.find_first_not_of (' ')); label = label.substr (0, label.find_last_not_of (' ')+1); // FIXME: As tick text is transparent, shouldn't it be // drawn after axes object, for correct rendering? if (xyz == X_AXIS) { b = render_text (label, val, p1, p2, ha, va); } else if (xyz == Y_AXIS) { b = render_text (label, p1, val, p2, ha, va); } else if (xyz == Z_AXIS) { b = render_text (label, p1, p2, val, ha, va); } wmax = std::max (wmax, static_cast<int> (b(2))); hmax = std::max (hmax, static_cast<int> (b(3))); } } #else octave_unused_parameter (ticks); octave_unused_parameter (ticklabels); octave_unused_parameter (lim1); octave_unused_parameter (lim2); octave_unused_parameter (p1); octave_unused_parameter (p2); octave_unused_parameter (xyz); octave_unused_parameter (ha); octave_unused_parameter (va); octave_unused_parameter (wmax); octave_unused_parameter (hmax); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_zoom_rect (int x1, int y1, int x2, int y2) { #if defined (HAVE_OPENGL) m_glfcns.glVertex2d (x1, y1); m_glfcns.glVertex2d (x2, y1); m_glfcns.glVertex2d (x2, y2); m_glfcns.glVertex2d (x1, y2); m_glfcns.glVertex2d (x1, y1); #else octave_unused_parameter (x1); octave_unused_parameter (x2); octave_unused_parameter (y1); octave_unused_parameter (y2); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_zoom_box (int width, int height, int x1, int y1, int x2, int y2, const Matrix& overlaycolor, double overlayalpha, const Matrix& bordercolor, double borderalpha, double borderwidth) { #if defined (HAVE_OPENGL) m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPushMatrix (); m_glfcns.glLoadIdentity (); m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPushMatrix (); m_glfcns.glLoadIdentity (); m_glfcns.glOrtho (0, width, height, 0, 1, -1); m_glfcns.glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT); m_glfcns.glDisable (GL_DEPTH_TEST); m_glfcns.glBegin (GL_POLYGON); m_glfcns.glColor4f (overlaycolor(0), overlaycolor(1), overlaycolor(2), overlayalpha); draw_zoom_rect (x1, y1, x2, y2); m_glfcns.glEnd (); m_glfcns.glLineWidth (borderwidth); m_glfcns.glBegin (GL_LINE_STRIP); m_glfcns.glColor4f (bordercolor(0), bordercolor(1), bordercolor(2), borderalpha); draw_zoom_rect (x1, y1, x2, y2); m_glfcns.glEnd (); m_glfcns.glPopAttrib (); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPopMatrix (); m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPopMatrix (); #else octave_unused_parameter (width); octave_unused_parameter (height); octave_unused_parameter (x1); octave_unused_parameter (x2); octave_unused_parameter (y1); octave_unused_parameter (y2); octave_unused_parameter (overlaycolor); octave_unused_parameter (overlayalpha); octave_unused_parameter (bordercolor); octave_unused_parameter (borderalpha); octave_unused_parameter (borderwidth); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } uint8NDArray opengl_renderer::get_pixels (int width, int height) { #if defined (HAVE_OPENGL) m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1); uint8NDArray pix(dim_vector (3, width, height), 0); m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pix.fortran_vec ()); // Permute and flip data Array<octave_idx_type> perm (dim_vector (3, 1)); perm(0) = 2; perm(1) = 1; perm(2) = 0; Array<idx_vector> idx (dim_vector (3, 1)); idx(0) = idx_vector::make_range (height - 1, -1, height); idx(1) = idx_vector::colon; idx(2) = idx_vector::colon; return pix.permute (perm).index (idx); #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. octave_unused_parameter (width); octave_unused_parameter (height); panic_impossible (); #endif } void opengl_renderer::finish (void) { #if defined (HAVE_OPENGL) m_glfcns.glFinish (); #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::setup_opengl_transformation (const axes::properties& props) { #if defined (HAVE_OPENGL) // setup OpenGL transformation Matrix x_zlim = props.get_transform_zlim (); // Expand the distance between the clipping planes symmetrically by // an arbitrary factor (see bug #54551). const double expansion_fac = 100.0; // Also make sure that the distance between the clipping planes // differs in single precision (see bug #58956). This factor is also // arbitrary. Different values (>2) might also work. const double single_prec_fac = 10.0; double avgZ = x_zlim(0) / 2.0 + x_zlim(1) / 2.0; double span = std::max (expansion_fac * (x_zlim(1)-x_zlim(0)), single_prec_fac * std::abs (avgZ) * std::numeric_limits<float>::epsilon ()); xZ1 = avgZ - span; xZ2 = avgZ + span; Matrix x_mat1 = props.get_opengl_matrix_1 (); Matrix x_mat2 = props.get_opengl_matrix_2 (); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glLoadIdentity (); m_glfcns.glScaled (1, 1, -1); m_glfcns.glMultMatrixd (x_mat1.data ()); m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glLoadIdentity (); Matrix vp = get_viewport_scaled (); m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); m_glfcns.glMultMatrixd (x_mat2.data ()); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glClear (GL_DEPTH_BUFFER_BIT); // store axes transformation data xform = props.get_transform (); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_planes (const axes::properties& props) { #if defined (HAVE_OPENGL) Matrix axe_color = props.get_color_rgb (); if (axe_color.isempty () || ! props.is_visible ()) return; double xPlane = props.get_xPlane (); double yPlane = props.get_yPlane (); double zPlane = props.get_zPlane (); double xPlaneN = props.get_xPlaneN (); double yPlaneN = props.get_yPlaneN (); double zPlaneN = props.get_zPlaneN (); bool is2D = props.get_is2D (); // Axes planes set_color (axe_color); set_polygon_offset (true, 9.0); m_glfcns.glBegin (GL_QUADS); if (! is2D) { // X plane m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlane, zPlane); m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); // Y plane m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlane, zPlane); m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); } // Z plane m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); m_glfcns.glVertex3d (xPlane, yPlane, zPlane); m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); m_glfcns.glEnd (); set_polygon_offset (false); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_boxes (const axes::properties& props) { #if defined (HAVE_OPENGL) if (! props.is_visible ()) return; bool xySym = props.get_xySym (); bool layer2Dtop = props.get_layer2Dtop (); bool is2D = props.get_is2D (); bool isXOrigin = props.xaxislocation_is ("origin") && ! props.yscale_is ("log"); bool isYOrigin = props.yaxislocation_is ("origin") && ! props.xscale_is ("log"); bool boxFull = (props.get_boxstyle () == "full"); double linewidth = props.get_linewidth (); double xPlane = props.get_xPlane (); double yPlane = props.get_yPlane (); double zPlane = props.get_zPlane (); double xPlaneN = props.get_xPlaneN (); double yPlaneN = props.get_yPlaneN (); double zPlaneN = props.get_zPlaneN (); double xpTick = props.get_xpTick (); double ypTick = props.get_ypTick (); double zpTick = props.get_zpTick (); double xpTickN = props.get_xpTickN (); double ypTickN = props.get_ypTickN (); double zpTickN = props.get_zpTickN (); bool plotyy = (props.has_property ("__plotyy_axes__")); // Axes box set_linecap ("square"); set_linestyle ("-", true, linewidth); m_glfcns.glBegin (GL_LINES); if (layer2Dtop) std::swap (zpTick, zpTickN); // X box Matrix color = props.get_xcolor_rgb (); if (! color.isempty ()) { set_color (color); if (! isXOrigin || props.is_box() || ! is2D) { m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick); m_glfcns.glVertex3d (xPlane, ypTick, zpTick); } if (props.is_box ()) { m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick); m_glfcns.glVertex3d (xPlane, ypTickN, zpTick); if (! is2D) { m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN); m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN); if (boxFull) { m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN); m_glfcns.glVertex3d (xPlane, ypTick, zpTickN); } } } } // Y box color = props.get_ycolor_rgb (); if (! color.isempty ()) { set_color (color); if (! isYOrigin || props.is_box() || ! is2D) { m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick); m_glfcns.glVertex3d (xpTick, yPlane, zpTick); } if (props.is_box () && ! plotyy) { m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick); m_glfcns.glVertex3d (xpTickN, yPlane, zpTick); if (! is2D) { m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN); m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN); if (boxFull) { m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN); m_glfcns.glVertex3d (xpTick, yPlane, zpTickN); } } } } // Z box color = props.get_zcolor_rgb (); if (! color.isempty () && ! is2D) { set_color (color); if (xySym) { m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); } else { m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); } if (props.is_box ()) { m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlane, zPlane); if (xySym) { m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN); m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane); } else { m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN); m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane); } if (boxFull) { m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN); m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane); } } } m_glfcns.glEnd (); set_linestyle ("-"); // Disable LineStipple #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_x_grid (const axes::properties& props) { #if defined (HAVE_OPENGL) gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_x_grid"); int xstate = props.get_xstate (); if (xstate != AXE_DEPTH_DIR && (props.is_visible () || (selecting && props.pickableparts_is ("all")))) { int zstate = props.get_zstate (); bool x2Dtop = props.get_x2Dtop (); bool layer2Dtop = props.get_layer2Dtop (); bool xyzSym = props.get_xyzSym (); bool nearhoriz = props.get_nearhoriz (); double xticklen = props.get_xticklen (); double xtickoffset = props.get_xtickoffset (); double fy = props.get_fy (); double fz = props.get_fz (); double x_min = props.get_x_min (); double x_max = props.get_x_max (); double y_min = props.get_y_min (); double y_max = props.get_y_max (); double yPlane = props.get_yPlane (); double yPlaneN = props.get_yPlaneN (); double ypTick = props.get_ypTick (); double ypTickN = props.get_ypTickN (); double zPlane = props.get_zPlane (); double zPlaneN = props.get_zPlaneN (); double zpTick = props.get_zpTick (); double zpTickN = props.get_zpTickN (); // X ticks and grid properties Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); Matrix xmticks = xform.xscale (props.get_xminortickvalues ().matrix_value ()); bool do_xminortick = props.is_xminortick () && ! xticks.isempty (); string_vector xticklabels = props.get_xticklabel ().string_vector_value (); int wmax = 0; int hmax = 0; bool tick_along_z = nearhoriz || math::isinf (fy); double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); Matrix gridcolor = props.get_gridcolor_rgb (); Matrix minorgridcolor = props.get_minorgridcolor_rgb (); double gridalpha = props.get_gridalpha (); double minorgridalpha = props.get_minorgridalpha (); bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none") && ! xticks.isempty ()); bool is_origin = props.xaxislocation_is ("origin") && props.get_is2D () && ! props.yscale_is ("log"); bool is_origin_low = is_origin && (y_min + y_max) < 0; bool mirror = props.is_box () && xstate != AXE_ANY_DIR; // X grid // possibly use axis color for gridcolor & minorgridcolor if (props.gridcolormode_is ("auto")) if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) gridcolor = props.get_xcolor_rgb (); if (props.minorgridcolormode_is ("auto")) if (props.xcolormode_is ("manual") && ! props.xcolor_is ("none")) minorgridcolor = props.get_xcolor_rgb (); if (gridcolor.isempty ()) do_xgrid = false; if (minorgridcolor.isempty ()) do_xminorgrid = false; // set styles when drawing only minor grid if (do_xminorgrid && ! do_xgrid) { gridstyle = minorgridstyle; gridcolor = minorgridcolor; gridalpha = minorgridalpha; do_xgrid = true; } // minor grid lines if (do_xminorgrid) render_grid (linewidth, minorgridstyle, minorgridcolor, minorgridalpha, xmticks, x_min, x_max, yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); // grid lines if (do_xgrid) render_grid (linewidth, gridstyle, gridcolor, gridalpha, xticks, x_min, x_max, yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 0, (zstate != AXE_DEPTH_DIR)); // Skip drawing axis, ticks, and ticklabels when color is "none" if (props.xcolor_is ("none")) return; set_color (props.get_xcolor_rgb ()); // axis line double y_axis_pos = 0.; if (is_origin) { y_axis_pos = math::max (math::min (0., y_max), y_min); m_glfcns.glBegin (GL_LINES); set_color (props.get_xcolor_rgb ()); m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick); m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick); m_glfcns.glEnd (); } // minor tick marks if (do_xminortick) { if (tick_along_z) render_tickmarks (xmticks, x_min, x_max, is_origin ? y_axis_pos : ypTick, ypTick, zpTick, zpTickN, 0., 0., (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*xticklen/2, 0, ! is_origin && mirror); else render_tickmarks (xmticks, x_min, x_max, is_origin ? y_axis_pos : ypTick, ypTickN, zpTick, zpTick, 0., (is_origin_low ? -1. : 1.) * math::signum (ypTick-ypTickN)*fy*xticklen/2, 0., 0, ! is_origin && mirror); } // tick marks if (tick_along_z) render_tickmarks (xticks, x_min, x_max, is_origin ? y_axis_pos : ypTick, ypTick, zpTick, zpTickN, 0., 0., (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*xticklen, 0, ! is_origin && mirror); else render_tickmarks (xticks, x_min, x_max, is_origin ? y_axis_pos : ypTick, ypTickN, zpTick, zpTick, 0., (is_origin_low ? -1. : 1.) * math::signum (ypTick-ypTickN)*fy*xticklen, 0., 0, ! is_origin && mirror); // tick texts if (xticklabels.numel () > 0) { int halign = (xstate == AXE_HORZ_DIR ? 1 : (xyzSym || is_origin_low ? 0 : 2)); int valign = (xstate == AXE_VERT_DIR ? 1 : (x2Dtop || is_origin_low ? 0 : 2)); if (tick_along_z) render_ticktexts (xticks, xticklabels, x_min, x_max, is_origin ? y_axis_pos : ypTick, zpTick + (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*xtickoffset, 0, halign, valign, wmax, hmax); else render_ticktexts (xticks, xticklabels, x_min, x_max, (is_origin ? y_axis_pos : ypTick) + (is_origin_low ? -1. : 1.) * math::signum (ypTick-ypTickN)*fy*xtickoffset, zpTick, 0, halign, valign, wmax, hmax); } gh_mgr.get_object (props.get_xlabel ()).set ("visible", "on"); } else gh_mgr.get_object (props.get_xlabel ()).set ("visible", "off"); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_y_grid (const axes::properties& props) { #if defined (HAVE_OPENGL) gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_y_grid"); int ystate = props.get_ystate (); if (ystate != AXE_DEPTH_DIR && props.is_visible () && (props.is_visible () || (selecting && props.pickableparts_is ("all")))) { int zstate = props.get_zstate (); bool y2Dright = props.get_y2Dright (); bool layer2Dtop = props.get_layer2Dtop (); bool xyzSym = props.get_xyzSym (); bool nearhoriz = props.get_nearhoriz (); double yticklen = props.get_yticklen (); double ytickoffset = props.get_ytickoffset (); double fx = props.get_fx (); double fz = props.get_fz (); double xPlane = props.get_xPlane (); double xPlaneN = props.get_xPlaneN (); double xpTick = props.get_xpTick (); double xpTickN = props.get_xpTickN (); double y_min = props.get_y_min (); double y_max = props.get_y_max (); double x_min = props.get_x_min (); double x_max = props.get_x_max (); double zPlane = props.get_zPlane (); double zPlaneN = props.get_zPlaneN (); double zpTick = props.get_zpTick (); double zpTickN = props.get_zpTickN (); // Y ticks and grid properties Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); Matrix ymticks = xform.yscale (props.get_yminortickvalues ().matrix_value ()); bool do_yminortick = props.is_yminortick () && ! yticks.isempty (); string_vector yticklabels = props.get_yticklabel ().string_vector_value (); int wmax = 0; int hmax = 0; bool tick_along_z = nearhoriz || math::isinf (fx); double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); Matrix gridcolor = props.get_gridcolor_rgb (); Matrix minorgridcolor = props.get_minorgridcolor_rgb (); double gridalpha = props.get_gridalpha (); double minorgridalpha = props.get_minorgridalpha (); bool do_ygrid = (props.is_ygrid () && (gridstyle != "none")); bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none") && ! yticks.isempty ()); bool is_origin = props.yaxislocation_is ("origin") && props.get_is2D () && ! props.xscale_is ("log"); bool is_origin_low = is_origin && (x_min + x_max) < 0; bool mirror = props.is_box () && ystate != AXE_ANY_DIR && (! props.has_property ("__plotyy_axes__")); // Y grid // possibly use axis color for gridcolor & minorgridcolor if (props.gridcolormode_is ("auto")) if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) gridcolor = props.get_ycolor_rgb (); if (props.minorgridcolormode_is ("auto")) if (props.ycolormode_is ("manual") && ! props.ycolor_is ("none")) minorgridcolor = props.get_ycolor_rgb (); if (gridcolor.isempty ()) do_ygrid = false; if (minorgridcolor.isempty ()) do_yminorgrid = false; // set styles when drawing only minor grid if (do_yminorgrid && ! do_ygrid) { gridstyle = minorgridstyle; gridcolor = minorgridcolor; gridalpha = minorgridalpha; do_ygrid = true; } // minor grid lines if (do_yminorgrid) render_grid (linewidth, minorgridstyle, minorgridcolor, minorgridalpha, ymticks, y_min, y_max, xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); // grid lines if (do_ygrid) render_grid (linewidth, gridstyle, gridcolor, gridalpha, yticks, y_min, y_max, xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN, 1, (zstate != AXE_DEPTH_DIR)); // Skip drawing axis, ticks, and ticklabels when color is "none" if (props.ycolor_is ("none")) return; set_color (props.get_ycolor_rgb ()); // axis line double x_axis_pos = 0.; if (is_origin) { x_axis_pos = math::max (math::min (0., x_max), x_min); m_glfcns.glBegin (GL_LINES); set_color (props.get_ycolor_rgb ()); m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick); m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick); m_glfcns.glEnd (); } // minor tick marks if (do_yminortick) { if (tick_along_z) render_tickmarks (ymticks, y_min, y_max, is_origin ? x_axis_pos : xpTick, xpTick, zpTick, zpTickN, 0., 0., (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*yticklen/2, 1, ! is_origin && mirror); else render_tickmarks (ymticks, y_min, y_max, is_origin ? x_axis_pos : xpTick, xpTickN, zpTick, zpTick, (is_origin_low ? -1. : 1.) * math::signum (xpTick-xpTickN)*fx*yticklen/2, 0., 0., 1, ! is_origin && mirror); } // tick marks if (tick_along_z) render_tickmarks (yticks, y_min, y_max, is_origin ? x_axis_pos : xpTick, xpTick, zpTick, zpTickN, 0., 0., (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*yticklen, 1, ! is_origin && mirror); else render_tickmarks (yticks, y_min, y_max, is_origin ? x_axis_pos : xpTick, xpTickN, zpTick, zpTick, (is_origin_low ? -1. : 1.) * math::signum (xPlaneN-xPlane)*fx*yticklen, 0., 0., 1, ! is_origin && mirror); // tick texts if (yticklabels.numel () > 0) { int halign = (ystate == AXE_HORZ_DIR ? 1 : (! xyzSym || y2Dright || is_origin_low ? 0 : 2)); int valign = (ystate == AXE_VERT_DIR ? 1 : (is_origin_low ? 0 : 2)); if (tick_along_z) render_ticktexts (yticks, yticklabels, y_min, y_max, is_origin ? x_axis_pos : xpTick, zpTick + (is_origin_low ? -1. : 1.) * math::signum (zpTick-zpTickN)*fz*ytickoffset, 1, halign, valign, wmax, hmax); else render_ticktexts (yticks, yticklabels, y_min, y_max, (is_origin ? x_axis_pos : xpTick) + (is_origin_low ? -1. : 1.) * math::signum (xpTick-xpTickN)*fx*ytickoffset, zpTick, 1, halign, valign, wmax, hmax); } gh_mgr.get_object (props.get_ylabel ()).set ("visible", "on"); } else gh_mgr.get_object (props.get_ylabel ()).set ("visible", "off"); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_z_grid (const axes::properties& props) { gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_z_grid"); int zstate = props.get_zstate (); if (zstate != AXE_DEPTH_DIR && props.is_visible () && (props.is_visible () || (selecting && props.pickableparts_is ("all")))) { bool xySym = props.get_xySym (); bool zSign = props.get_zSign (); double zticklen = props.get_zticklen (); double ztickoffset = props.get_ztickoffset (); double fx = props.get_fx (); double fy = props.get_fy (); double xPlane = props.get_xPlane (); double xPlaneN = props.get_xPlaneN (); double yPlane = props.get_yPlane (); double yPlaneN = props.get_yPlaneN (); double z_min = props.get_z_min (); double z_max = props.get_z_max (); // Z ticks and grid properties Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); Matrix zmticks = xform.zscale (props.get_zminortickvalues ().matrix_value ()); bool do_zminortick = props.is_zminortick () && ! zticks.isempty (); string_vector zticklabels = props.get_zticklabel ().string_vector_value (); int wmax = 0; int hmax = 0; double linewidth = props.get_linewidth (); std::string gridstyle = props.get_gridlinestyle (); std::string minorgridstyle = props.get_minorgridlinestyle (); Matrix gridcolor = props.get_gridcolor_rgb (); Matrix minorgridcolor = props.get_minorgridcolor_rgb (); double gridalpha = props.get_gridalpha (); double minorgridalpha = props.get_minorgridalpha (); bool do_zgrid = (props.is_zgrid () && (gridstyle != "none")); bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none") && ! zticks.isempty ()); bool mirror = props.is_box () && zstate != AXE_ANY_DIR; // Z grid // possibly use axis color for gridcolor & minorgridcolor if (props.gridcolormode_is ("auto")) if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) gridcolor = props.get_zcolor_rgb (); if (props.minorgridcolormode_is ("auto")) if (props.zcolormode_is ("manual") && ! props.zcolor_is ("none")) minorgridcolor = props.get_zcolor_rgb (); if (gridcolor.isempty ()) do_zgrid = false; if (minorgridcolor.isempty ()) do_zminorgrid = false; // set styles when drawing only minor grid if (do_zminorgrid && ! do_zgrid) { gridstyle = minorgridstyle; gridcolor = minorgridcolor; gridalpha = minorgridalpha; do_zgrid = true; } // minor grid lines if (do_zminorgrid) render_grid (linewidth, minorgridstyle, minorgridcolor, minorgridalpha, zmticks, z_min, z_max, xPlane, xPlaneN, yPlane, yPlaneN, 2, true); // grid lines if (do_zgrid) render_grid (linewidth, gridstyle, gridcolor, gridalpha, zticks, z_min, z_max, xPlane, xPlaneN, yPlane, yPlaneN, 2, true); // Skip drawing axis, ticks, and ticklabels when color is "none" if (props.zcolor_is ("none")) return; set_color (props.get_zcolor_rgb ()); // minor tick marks if (do_zminortick) { if (xySym) { if (math::isinf (fy)) render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlane, yPlane, yPlane, math::signum (xPlaneN-xPlane)*fx*zticklen/2, 0., 0., 2, mirror); else render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, yPlane, yPlane, 0., math::signum (yPlane-yPlaneN)*fy*zticklen/2, 0., 2, false); } else { if (math::isinf (fx)) render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlane, 0., math::signum (yPlaneN-yPlane)*fy*zticklen/2, 0., 2, mirror); else render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlaneN, math::signum (xPlane-xPlaneN)*fx*zticklen/2, 0., 0., 2, false); } } // tick marks if (xySym) { if (math::isinf (fy)) render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, yPlane, yPlane, math::signum (xPlaneN-xPlane)*fx*zticklen, 0., 0., 2, mirror); else render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, yPlane, yPlane, 0., math::signum (yPlane-yPlaneN)*fy*zticklen, 0., 2, false); } else { if (math::isinf (fx)) render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, yPlaneN, yPlane, 0., math::signum (yPlaneN-yPlane)*fy*zticklen, 0., 2, mirror); else render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlane, math::signum (xPlane-xPlaneN)*fx*zticklen, 0., 0., 2, false); } // tick texts if (zticklabels.numel () > 0) { int halign = 2; int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2)); if (xySym) { if (math::isinf (fy)) render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN + math::signum (xPlaneN-xPlane)*fx*ztickoffset, yPlane, 2, halign, valign, wmax, hmax); else render_ticktexts (zticks, zticklabels, z_min, z_max, xPlaneN, yPlane + math::signum (yPlane-yPlaneN)*fy*ztickoffset, 2, halign, valign, wmax, hmax); } else { if (math::isinf (fx)) render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane, yPlaneN + math::signum (yPlaneN-yPlane)*fy*ztickoffset, 2, halign, valign, wmax, hmax); else render_ticktexts (zticks, zticklabels, z_min, z_max, xPlane + math::signum (xPlane-xPlaneN)*fx*ztickoffset, yPlaneN, 2, halign, valign, wmax, hmax); } } gh_mgr.get_object (props.get_zlabel ()).set ("visible", "on"); } else gh_mgr.get_object (props.get_zlabel ()).set ("visible", "off"); } void opengl_renderer::draw_axes_grids (const axes::properties& props) { #if defined (HAVE_OPENGL) // Disable line smoothing for axes GLboolean antialias; m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias); if (antialias == GL_TRUE) m_glfcns.glDisable (GL_LINE_SMOOTH); set_linecap ("butt"); set_linewidth (props.get_linewidth ()); set_font (props); set_interpreter (props.get_ticklabelinterpreter ()); draw_axes_x_grid (props); draw_axes_y_grid (props); draw_axes_z_grid (props); if (antialias == GL_TRUE) m_glfcns.glEnable (GL_LINE_SMOOTH); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_all_lights (const base_properties& props, std::list<graphics_object>& obj_list) { #if defined (HAVE_OPENGL) gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_axes_all_lights"); Matrix children = props.get_all_children (); for (octave_idx_type i = children.numel () - 1; i >= 0; i--) { graphics_object go = gh_mgr.get_object (children(i)); base_properties p = go.get_properties (); if (p.is_visible () || (selecting && p.pickableparts_is ("all"))) { if (go.isa ("light") && ! selecting) { if (m_current_light-GL_LIGHT0 < m_max_lights) { set_clipping (p.is_clipping ()); draw (go); m_current_light++; } } else if (go.isa ("hggroup") && ! (selecting && p.pickableparts_is ("none"))) draw_all_lights (go.get_properties (), obj_list); else if (! (selecting && p.pickableparts_is ("none"))) obj_list.push_back (go); } } #else octave_unused_parameter (props); octave_unused_parameter (obj_list); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes_children (const axes::properties& props) { #if defined (HAVE_OPENGL) // list for non-light child objects std::list<graphics_object> obj_list; std::list<graphics_object>::iterator it; // 1st pass: draw light objects // FIXME: max_lights only needs to be set once. // It would be better if this could be in the constructor for gl_renderer // but this seems to lead to calls of OpenGL functions before the context // is actually initialized. See bug #48669. // Check actual maximum number of lights possible init_maxlights (); // Start with the last element of the array of child objects to // display them in the order they were added to the array. if (props.get_num_lights () > m_max_lights) warning_with_id ("Octave:max-lights-exceeded", "light: Maximum number of lights (%d) in these axes is " "exceeded.", m_max_lights); m_current_light = GL_LIGHT0; draw_all_lights (props, obj_list); // disable other OpenGL lights for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++) m_glfcns.glDisable (GL_LIGHT0 + i); // save camera position and set ambient light color before drawing // other objects view_vector = props.get_cameraposition ().matrix_value (); float cb[4] = { 1.0, 1.0, 1.0, 1.0 }; ColumnVector ambient_color = props.get_ambientlightcolor_rgb (); for (int i = 0; i < 3; i++) cb[i] = ambient_color(i); m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb); // 2nd pass: draw other objects (with units set to "data") it = obj_list.begin (); while (it != obj_list.end ()) { graphics_object go = (*it); // FIXME: check whether object has "units" property and it is set // to "data" if (! go.isa ("text") || go.get ("units").string_value () == "data") { set_clipping (go.get_properties ().is_clipping ()); draw (go); it = obj_list.erase (it); } else it++; } // 3rd pass: draw remaining objects m_glfcns.glDisable (GL_DEPTH_TEST); for (it = obj_list.begin (); it != obj_list.end (); it++) { graphics_object go = (*it); set_clipping (go.get_properties ().is_clipping ()); draw (go); } set_clipping (false); // FIXME: finalize rendering (transparency processing) // FIXME: draw zoom box, if needed #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_axes (const axes::properties& props) { #if defined (HAVE_OPENGL) // Legends are not drawn when "visible" is "off". if (! props.is_visible () && props.get_tag () == "legend") return; // Don't draw the axes and its children if we are in selection and // pickable parts is "none". if (selecting && props.pickableparts_is ("none")) return; static double floatmax = std::numeric_limits<float>::max (); double x_min = props.get_x_min (); double x_max = props.get_x_max (); double y_min = props.get_y_min (); double y_max = props.get_y_max (); double z_min = props.get_z_min (); double z_max = props.get_z_max (); if (x_max > floatmax || y_max > floatmax || z_max > floatmax || x_min < -floatmax || y_min < -floatmax || z_min < -floatmax) { warning ("opengl_renderer: data values greater than float capacity. (1) Scale data, or (2) Use gnuplot"); return; } setup_opengl_transformation (props); // For 2D axes with only 2D primitives, draw from back to front without // depth sorting bool is2D = props.get_is2D (true); if (is2D) m_glfcns.glDisable (GL_DEPTH_TEST); else m_glfcns.glEnable (GL_DEPTH_TEST); draw_axes_planes (props); if (! is2D || props.layer_is ("bottom")) { draw_axes_grids (props); if (props.get_tag () != "legend" || props.get_box () != "off") draw_axes_boxes (props); } set_clipbox (x_min, x_max, y_min, y_max, z_min, z_max); draw_axes_children (props); if (is2D && props.layer_is ("top")) { draw_axes_grids (props); if (props.get_tag () != "legend" || props.get_box () != "off") draw_axes_boxes (props); } #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_line (const line::properties& props) { #if defined (HAVE_OPENGL) bool draw_all = selecting && props.pickableparts_is ("all"); Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); bool has_z = (z.numel () > 0); int n = static_cast<int> (std::min (std::min (x.numel (), y.numel ()), (has_z ? z.numel () : std::numeric_limits<int>::max ()))); uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); uint8_t clip_ok = 0x40; std::vector<uint8_t> clip (n); if (has_z) for (int i = 0; i < n; i++) clip[i] = (clip_code (x(i), y(i), z(i)) & clip_mask); else { double z_mid = (zmin+zmax)/2; for (int i = 0; i < n; i++) clip[i] = (clip_code (x(i), y(i), z_mid) & clip_mask); } if (! props.linestyle_is ("none") && ! props.color_is ("none")) { set_color (props.get_color_rgb ()); set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); set_linewidth (props.get_linewidth ()); set_linecap ("butt"); set_linejoin (props.get_linejoin ()); if (has_z) { bool flag = false; for (int i = 1; i < n; i++) { if ((clip[i-1] & clip[i]) == clip_ok) { if (! flag) { flag = true; m_glfcns.glBegin (GL_LINE_STRIP); m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1)); } m_glfcns.glVertex3d (x(i), y(i), z(i)); } else if (flag) { flag = false; m_glfcns.glEnd (); } } if (flag) m_glfcns.glEnd (); } else { bool flag = false; for (int i = 1; i < n; i++) { if ((clip[i-1] & clip[i]) == clip_ok) { if (! flag) { flag = true; m_glfcns.glBegin (GL_LINE_STRIP); m_glfcns.glVertex2d (x(i-1), y(i-1)); } m_glfcns.glVertex2d (x(i), y(i)); } else if (flag) { flag = false; m_glfcns.glEnd (); } } if (flag) m_glfcns.glEnd (); } set_linewidth (0.5f); set_linestyle ("-"); } set_clipping (false); if (! props.marker_is ("none") && ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none"))) { Matrix lc, fc; if (draw_all) lc = Matrix (1, 3, 0.0); else if (props.markeredgecolor_is ("auto")) lc = props.get_color_rgb (); else if (! props.markeredgecolor_is ("none")) lc = props.get_markeredgecolor_rgb (); if (draw_all) fc = Matrix (1, 3, 0.0); if (props.markerfacecolor_is ("auto")) fc = props.get_color_rgb (); else if (! props.markerfacecolor_is ("none")) fc = props.get_markerfacecolor_rgb (); init_marker (props.get_marker (), props.get_markersize (), props.get_linewidth ()); for (int i = 0; i < n; i++) { if (clip[i] == clip_ok) draw_marker (x(i), y(i), has_z ? z(i) : 0.0, lc, fc); } end_marker (); } set_clipping (props.is_clipping ()); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_surface (const surface::properties& props) { #if defined (HAVE_OPENGL) bool draw_all = selecting && props.pickableparts_is ("all"); const Matrix x = xform.xscale (props.get_xdata ().matrix_value ()); const Matrix y = xform.yscale (props.get_ydata ().matrix_value ()); const Matrix z = xform.zscale (props.get_zdata ().matrix_value ()); int zr = z.rows (); int zc = z.columns (); NDArray c; const NDArray vn = props.get_vertexnormals ().array_value (); dim_vector vn_dims = vn.dims (); bool has_vertex_normals = (vn_dims(0) == zr && vn_dims(1) == zc && vn_dims(2) == 3); const NDArray fn = props.get_facenormals ().array_value (); dim_vector fn_dims = fn.dims (); bool has_face_normals = (fn_dims(0) == zr - 1 && fn_dims(1) == zc - 1 && fn_dims(2) == 3); // FIXME: handle transparency Matrix a; int fc_mode = (props.facecolor_is_rgb () ? 0 : (props.facecolor_is ("flat") ? 1 : (props.facecolor_is ("interp") ? 2 : (props.facecolor_is ("texturemap") ? 3 : -1)))); int fl_mode = (props.facelighting_is ("none") ? 0 : (props.facelighting_is ("flat") ? (has_face_normals ? 1 : 0) : (has_vertex_normals ? 2 : 0))); int fa_mode = (props.facealpha_is_double () ? 0 : (props.facealpha_is ("flat") ? 1 : 2)); int ec_mode = (props.edgecolor_is_rgb () ? 0 : (props.edgecolor_is ("flat") ? 1 : (props.edgecolor_is ("interp") ? 2 : -1))); int el_mode = (props.edgelighting_is ("none") ? 0 : (props.edgelighting_is ("flat") ? (has_face_normals ? 1 : 0) : (has_vertex_normals ? 2 : 0))); int ea_mode = (props.edgealpha_is_double () ? 0 : (props.edgealpha_is ("flat") ? 1 : 2)); int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : (props.backfacelighting_is ("reverselit") ? 1 : 2)); bool do_lighting = props.get_do_lighting (); Matrix fcolor = (fc_mode == TEXTURE ? Matrix (1, 3, 1.0) : props.get_facecolor_rgb ()); Matrix ecolor = props.get_edgecolor_rgb (); double fa = 1.0; float as = props.get_ambientstrength (); float ds = props.get_diffusestrength (); float ss = props.get_specularstrength (); float se = props.get_specularexponent () * 5; // to fit Matlab float scr = props.get_specularcolorreflectance (); float cb[4] = { 0.0, 0.0, 0.0, 1.0 }; opengl_texture tex (m_glfcns); int i1, i2, j1, j2; bool x_mat = (x.rows () == z.rows ()); bool y_mat = (y.columns () == z.columns ()); i1 = i2 = j1 = j2 = 0; if ((fc_mode > 0 && fc_mode < 3) || ec_mode > 0) c = props.get_color_data ().array_value (); boolMatrix clip (z.dims (), false); for (int i = 0; i < zr; i++) { if (x_mat) i1 = i; for (int j = 0; j < zc; j++) { if (y_mat) j1 = j; clip(i,j) = is_nan_or_inf (x(i1,j), y(i,j1), z(i,j)); } } if (fa_mode > 0 || ea_mode > 0) { // FIXME: implement alphadata conversion //a = props.get_alpha_data (); } if (fl_mode > 0 || el_mode > 0) m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); // FIXME: good candidate for caching, // transferring pixel data to OpenGL is time consuming. if (fc_mode == TEXTURE) tex = opengl_texture::create (m_glfcns, props.get_color_data ()); if (draw_all || ! props.facecolor_is ("none")) { if (fa_mode == 0) { fa = props.get_facealpha_double (); cb[3] = fa; if (fc_mode == UNIFORM || fc_mode == TEXTURE) { m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); if (fl_mode > 0) { for (int i = 0; i < 3; i++) cb[i] = as * fcolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int i = 0; i < 3; i++) cb[i] = ds * fcolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int i = 0; i < 3; i++) cb[i] = ss * (scr + (1-scr) * fcolor(i)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if ((fl_mode > 0) && do_lighting) m_glfcns.glEnable (GL_LIGHTING); m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD) ? GL_SMOOTH : GL_FLAT); set_polygon_offset (true, 1.0); if (fc_mode == TEXTURE) m_glfcns.glEnable (GL_TEXTURE_2D); for (int i = 1; i < zc; i++) { if (y_mat) { i1 = i-1; i2 = i; } for (int j = 1; j < zr; j++) { if (clip(j-1, i-1) || clip(j, i-1) || clip(j-1, i) || clip(j, i)) continue; if (fc_mode == FLAT) { // "flat" only needs color at lower-left vertex if (! math::isfinite (c(j-1,i-1))) continue; } else if (fc_mode == INTERP) { // "interp" needs valid color at all 4 vertices if (! (math::isfinite (c(j-1, i-1)) && math::isfinite (c(j, i-1)) && math::isfinite (c(j-1, i)) && math::isfinite (c(j, i)))) continue; } if (x_mat) { j1 = j-1; j2 = j; } m_glfcns.glBegin (GL_QUADS); // Vertex 1 if (fc_mode == TEXTURE) tex.tex_coord (double (i-1) / (zc-1), double (j-1) / (zr-1)); else if (fc_mode > 0) { // FIXME: is there a smarter way to do this? for (int k = 0; k < 3; k++) cb[k] = c(j-1, i-1, k); m_glfcns.glColor4fv (cb); if (fl_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j-1, i-1, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (fl_mode > 0) set_normal (bfl_mode, (fl_mode == GOURAUD ? vn : fn), j-1, i-1); m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1)); // Vertex 2 if (fc_mode == TEXTURE) tex.tex_coord (double (i) / (zc-1), double (j-1) / (zr-1)); else if (fc_mode == INTERP) { for (int k = 0; k < 3; k++) cb[k] = c(j-1, i, k); m_glfcns.glColor4fv (cb); if (fl_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j-1, i, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (fl_mode == GOURAUD) set_normal (bfl_mode, vn, j-1, i); m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); // Vertex 3 if (fc_mode == TEXTURE) tex.tex_coord (double (i) / (zc-1), double (j) / (zr-1)); else if (fc_mode == INTERP) { for (int k = 0; k < 3; k++) cb[k] = c(j, i, k); m_glfcns.glColor4fv (cb); if (fl_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j, i, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j, i, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (fl_mode == GOURAUD) set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); // Vertex 4 if (fc_mode == TEXTURE) tex.tex_coord (double (i-1) / (zc-1), double (j) / (zr-1)); else if (fc_mode == INTERP) { for (int k = 0; k < 3; k++) cb[k] = c(j, i-1, k); m_glfcns.glColor4fv (cb); if (fl_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j, i-1, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (fl_mode == GOURAUD) set_normal (bfl_mode, vn, j, i-1); m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); m_glfcns.glEnd (); } } set_polygon_offset (false); if (fc_mode == TEXTURE) m_glfcns.glDisable (GL_TEXTURE_2D); if ((fl_mode > 0) && do_lighting) m_glfcns.glDisable (GL_LIGHTING); } else { // FIXME: implement flat, interp and texturemap transparency } } if (! props.edgecolor_is ("none") && ! props.linestyle_is ("none")) { if (props.get_edgealpha_double () == 1) { cb[3] = 1.0; // edgealpha isn't implemented yet if (ec_mode == UNIFORM) { m_glfcns.glColor3dv (ecolor.data ()); if (el_mode > 0) { for (int i = 0; i < 3; i++) cb[i] = as * ecolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int i = 0; i < 3; i++) cb[i] = ds * ecolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int i = 0; i < 3; i++) cb[i] = ss * (scr + (1-scr) * ecolor(i)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if ((el_mode > 0) && do_lighting) m_glfcns.glEnable (GL_LIGHTING); m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) ? GL_SMOOTH : GL_FLAT); set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); set_linewidth (props.get_linewidth ()); set_linecap ("butt"); set_linejoin ("miter"); // Mesh along Y-axis if (props.meshstyle_is ("both") || props.meshstyle_is ("column")) { for (int i = 0; i < zc; i++) { if (y_mat) { i1 = i-1; i2 = i; } for (int j = 1; j < zr; j++) { if (clip(j-1,i) || clip(j,i)) continue; if (ec_mode == FLAT) { // "flat" only needs color at lower-left vertex if (! math::isfinite (c(j-1,i))) continue; } else if (ec_mode == INTERP) { // "interp" needs valid color at both vertices if (! (math::isfinite (c(j-1, i)) && math::isfinite (c(j, i)))) continue; } if (x_mat) { j1 = j-1; j2 = j; } m_glfcns.glBegin (GL_LINES); // Vertex 1 if (ec_mode > 0) { for (int k = 0; k < 3; k++) cb[k] = c(j-1, i, k); m_glfcns.glColor3fv (cb); if (el_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j-1, i, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j-1, i, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (el_mode > 0) { if (el_mode == GOURAUD) set_normal (bfl_mode, vn, j-1, i); else set_normal (bfl_mode, fn, j-1, std::min (i, zc-2)); } m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); // Vertex 2 if (ec_mode == INTERP) { for (int k = 0; k < 3; k++) cb[k] = c(j, i, k); m_glfcns.glColor3fv (cb); if (el_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j, i, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j, i, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (el_mode == GOURAUD) set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); m_glfcns.glEnd (); } } } // Mesh along X-axis if (props.meshstyle_is ("both") || props.meshstyle_is ("row")) { for (int j = 0; j < zr; j++) { if (x_mat) { j1 = j-1; j2 = j; } for (int i = 1; i < zc; i++) { if (clip(j,i-1) || clip(j,i)) continue; if (ec_mode == FLAT) { // "flat" only needs color at lower-left vertex if (! math::isfinite (c(j,i-1))) continue; } else if (ec_mode == INTERP) { // "interp" needs valid color at both vertices if (! (math::isfinite (c(j, i-1)) && math::isfinite (c(j, i)))) continue; } if (y_mat) { i1 = i-1; i2 = i; } m_glfcns.glBegin (GL_LINES); // Vertex 1 if (ec_mode > 0) { for (int k = 0; k < 3; k++) cb[k] = c(j, i-1, k); m_glfcns.glColor3fv (cb); if (el_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j, i-1, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j, i-1, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (el_mode > 0) { if (el_mode == GOURAUD) set_normal (bfl_mode, vn, j, i-1); else set_normal (bfl_mode, fn, std::min (j, zr-2), i-1); } m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); // Vertex 2 if (ec_mode == INTERP) { for (int k = 0; k < 3; k++) cb[k] = c(j, i, k); m_glfcns.glColor3fv (cb); if (el_mode > 0) { for (int k = 0; k < 3; k++) cb[k] *= as; m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = ds * c(j, i, k); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = ss * (scr + (1-scr) * c(j, i, k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if (el_mode == GOURAUD) set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); m_glfcns.glEnd (); } } } set_linestyle ("-"); // Disable LineStipple set_linewidth (0.5f); if ((el_mode > 0) && do_lighting) m_glfcns.glDisable (GL_LIGHTING); } else { // FIXME: implement transparency } } if (! props.marker_is ("none") && ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none"))) { // FIXME: check how transparency should be handled in markers // FIXME: check what to do with marker facecolor set to auto // and facecolor set to none. bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); bool do_face = draw_all || ! props.markerfacecolor_is ("none"); Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markeredgecolor_rgb ()); Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markerfacecolor_rgb ()); Matrix cc (1, 3, 0.0); if (mecolor.isempty () && props.markeredgecolor_is ("auto")) { mecolor = props.get_edgecolor_rgb (); do_edge = ! props.edgecolor_is ("none"); } if (mfcolor.isempty () && props.markerfacecolor_is ("auto")) { mfcolor = props.get_facecolor_rgb (); do_face = ! props.facecolor_is ("none"); } if ((mecolor.isempty () || mfcolor.isempty ()) && c.isempty ()) c = props.get_color_data ().array_value (); init_marker (props.get_marker (), props.get_markersize (), props.get_linewidth ()); uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); uint8_t clip_ok = 0x40; for (int i = 0; i < zc; i++) { if (y_mat) i1 = i; for (int j = 0; j < zr; j++) { if (x_mat) j1 = j; if ((clip_code (x(j1,i), y(j,i1), z(j,i)) & clip_mask) != clip_ok) continue; if ((do_edge && mecolor.isempty ()) || (do_face && mfcolor.isempty ())) { if (! math::isfinite (c(j,i))) continue; // Skip NaNs in color data for (int k = 0; k < 3; k++) cc(k) = c(j,i,k); } Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) : Matrix ()); Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) : Matrix ()); draw_marker (x(j1,i), y(j,i1), z(j,i), lc, fc); } } end_marker (); } #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } // FIXME: global optimization (rendering, data structures...), // there is probably a smarter/faster/less-memory-consuming way to do this. void opengl_renderer::draw_patch (const patch::properties& props) { #if defined (HAVE_OPENGL) // Do not render if the patch has incoherent data std::string msg; if (props.has_bad_data (msg)) { warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); return; } bool draw_all = selecting && props.pickableparts_is ("all"); const Matrix f = props.get_faces ().matrix_value (); const Matrix v = xform.scale (props.get_vertices ().matrix_value ()); Matrix c; Matrix a; double fa = 1.0; int nv = v.rows (); int nf = f.rows (); int fcmax = f.columns (); bool has_z = (v.columns () > 2); bool has_facecolor = false; bool has_facealpha = false; int fc_mode = ((props.facecolor_is ("none") || props.facecolor_is_rgb () || draw_all) ? 0 : (props.facecolor_is ("flat") ? 1 : 2)); int fl_mode = (props.facelighting_is ("none") ? 0 : (props.facelighting_is ("flat") ? 1 : 2)); int fa_mode = (props.facealpha_is_double () ? 0 : (props.facealpha_is ("flat") ? 1 : 2)); int ec_mode = ((props.edgecolor_is ("none") || props.edgecolor_is_rgb ()) ? 0 : (props.edgecolor_is ("flat") ? 1 : 2)); int el_mode = (props.edgelighting_is ("none") ? 0 : (props.edgelighting_is ("flat") ? 1 : 2)); int ea_mode = (props.edgealpha_is_double () ? 0 : (props.edgealpha_is ("flat") ? 1 : 2)); int bfl_mode = (props.backfacelighting_is ("lit") ? 0 : (props.backfacelighting_is ("reverselit") ? 1 : 2)); bool do_lighting = props.get_do_lighting (); Matrix fcolor = props.get_facecolor_rgb (); Matrix ecolor = props.get_edgecolor_rgb (); float as = props.get_ambientstrength (); float ds = props.get_diffusestrength (); float ss = props.get_specularstrength (); float se = props.get_specularexponent () * 5; // to fit Matlab float scr = props.get_specularcolorreflectance (); const Matrix vn = props.get_vertexnormals ().matrix_value (); bool has_vertex_normals = (vn.rows () == nv); const Matrix fn = props.get_facenormals ().matrix_value (); bool has_face_normals = (fn.rows () == nf); boolMatrix clip (1, nv, false); if (has_z) for (int i = 0; i < nv; i++) clip(i) = is_nan_or_inf (v(i,0), v(i,1), v(i,2)); else for (int i = 0; i < nv; i++) clip(i) = is_nan_or_inf (v(i,0), v(i,1), 0); boolMatrix clip_f (1, nf, false); Array<int> count_f (dim_vector (nf, 1), 0); for (int i = 0; i < nf; i++) { bool fclip = false; int count = 0; for (int j = 0; j < fcmax && ! math::isnan (f(i,j)); j++, count++) fclip = (fclip || clip(int (f(i,j) - 1))); clip_f(i) = fclip; count_f(i) = count; } if (draw_all || fc_mode > 0 || ec_mode > 0) { if (draw_all) c = Matrix (1, 3, 0.0); else c = props.get_color_data ().matrix_value (); if (c.rows () == 1) { // Single color specifications, we can simplify a little bit if (draw_all || fc_mode > 0) { fcolor = c; fc_mode = UNIFORM; } if (draw_all || ec_mode > 0) { ecolor = c; ec_mode = UNIFORM; } c = Matrix (); } else has_facecolor = ((c.numel () > 0) && (c.rows () == f.rows ())); } if (fa_mode > 0 || ea_mode > 0) { // FIXME: retrieve alpha data from patch object //a = props.get_alpha_data (); has_facealpha = ((a.numel () > 0) && (a.rows () == f.rows ())); } if (fa_mode == 0) fa = props.get_facealpha_double (); octave_idx_type fr = f.rows (); std::vector<vertex_data> vdata (f.numel ()); for (int i = 0; i < nf; i++) for (int j = 0; j < count_f(i); j++) { int idx = int (f(i,j) - 1); Matrix vv (1, 3, 0.0); Matrix cc; Matrix vnn (1, 3, 0.0); Matrix fnn (1, 3, 0.0); double aa = 1.0; vv(0) = v(idx,0); vv(1) = v(idx,1); if (has_z) vv(2) = v(idx,2); if (((fl_mode == FLAT) || (el_mode == FLAT)) && has_face_normals) { double dir = 1.0; if (bfl_mode > 0) dir = ((fn(i,0) * view_vector(0) + fn(i,1) * view_vector(1) + fn(i,2) * view_vector(2) < 0) ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); fnn(0) = dir * fn(i,0); fnn(1) = dir * fn(i,1); fnn(2) = dir * fn(i,2); } if ((fl_mode == GOURAUD || el_mode == GOURAUD) && has_vertex_normals) { double dir = 1.0; if (bfl_mode > 0) dir = ((vn(idx,0) * view_vector(0) + vn(idx,1) * view_vector(1) + vn(idx,2) * view_vector(2) < 0) ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); vnn(0) = dir * vn(idx,0); vnn(1) = dir * vn(idx,1); vnn(2) = dir * vn(idx,2); } if (c.numel () > 0) { cc.resize (1, 3); if (has_facecolor) cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); else cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); } if (fa_mode == 0) aa = fa; else if (a.numel () > 0) { if (has_facealpha) aa = a(i); else aa = a(idx); } vdata[i+j*fr] = vertex_data (vv, cc, vnn, fnn, aa, as, ds, ss, se, scr); } if (fl_mode > 0 || el_mode > 0) m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se); if (draw_all || ! props.facecolor_is ("none")) { // FIXME: adapt to double-radio property if (fa_mode == 0) { if (fc_mode == UNIFORM) { m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa); if (fl_mode > 0) { float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f };; for (int i = 0; i < 3; i++) cb[i] = as * fcolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int i = 0; i < 3; i++) cb[i] = ds * fcolor(i); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int i = 0; i < 3; i++) cb[i] = ss * (scr + (1-scr) * fcolor(i)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } if ((fl_mode > 0) && do_lighting) m_glfcns.glEnable (GL_LIGHTING); // NOTE: Push filled part of patch backwards to avoid Z-fighting // with tessellator outline. A value of 1.0 seems to work fine. // Value can't be too large or the patch will be pushed below the // axes planes at +2.5. patch_tessellator tess (this, fc_mode, fl_mode, true, 1.0); std::vector<octave_idx_type>::const_iterator it; octave_idx_type i_start, i_end; for (int i = 0; i < nf; i++) { if (clip_f(i)) continue; bool is_non_planar = false; if (props.coplanar_last_idx.size () > 0 && props.coplanar_last_idx[i].size () > 1) { is_non_planar = true; it = props.coplanar_last_idx[i].end (); it--; } // loop over planar subsets of face do { if (is_non_planar) { i_end = *it; if (it == props.coplanar_last_idx[i].begin ()) i_start = 0; else { it--; i_start = *it - 1; } } else { i_end = count_f(i) - 1; i_start = 0; } tess.begin_polygon (true); tess.begin_contour (); // Add vertices in reverse order for Matlab compatibility for (int j = i_end; j > i_start; j--) { vertex_data::vertex_data_rep *vv = vdata[i+j*fr].get_rep (); tess.add_vertex (vv->m_coords.fortran_vec (), vv); } if (count_f(i) > 0) { vertex_data::vertex_data_rep *vv = vdata[i].get_rep (); if (fc_mode == FLAT) { // For "flat" shading, use color of 1st vertex. Matrix col = vv->m_color; if (col.numel () == 3) { m_glfcns.glColor4d (col(0), col(1), col(2), fa); if (fl_mode > 0) { float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; for (int k = 0; k < 3; k++) cb[k] = (vv->m_ambient * col(k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); for (int k = 0; k < 3; k++) cb[k] = (vv->m_diffuse * col(k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int k = 0; k < 3; k++) cb[k] = vv->m_specular * (vv->m_specular_color_refl + (1-vv->m_specular_color_refl) * col(k)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); } } } tess.add_vertex (vv->m_coords.fortran_vec (), vv); } tess.end_contour (); tess.end_polygon (); } while (i_start > 0); } if ((fl_mode > 0) && do_lighting) m_glfcns.glDisable (GL_LIGHTING); } else { // FIXME: implement flat and interp transparency } } if (draw_all || (! props.edgecolor_is ("none") && ! props.linestyle_is ("none"))) { // FIXME: adapt to double-radio property if (props.get_edgealpha_double () == 1) { if (ec_mode == UNIFORM) { m_glfcns.glColor3dv (ecolor.data ()); if (el_mode > 0) { // edge lighting only uses ambient light float cb[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb); m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb); for (int i = 0; i < 3; i++) cb[i] = (as * ecolor(i)); m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb); } } if ((el_mode > 0) && do_lighting) m_glfcns.glEnable (GL_LIGHTING); double linewidth = props.get_linewidth (); set_linestyle (props.get_linestyle (), false, linewidth); set_linewidth (linewidth); set_linecap ("butt"); set_linejoin ("miter"); // NOTE: patch contour cannot be offset. Offset must occur with the // filled portion of the patch above. The tessellator uses // GLU_TESS_BOUNDARY_ONLY to get the outline of the patch and OpenGL // automatically sets the glType to GL_LINE_LOOP. This primitive is // not supported by glPolygonOffset which is used to do Z offsets. patch_tessellator tess (this, ec_mode, el_mode, false); for (int i = 0; i < nf; i++) { bool is_non_planar = false; if (props.coplanar_last_idx.size () > 0 && props.coplanar_last_idx[i].size () > 1) is_non_planar = true; if (clip_f(i) || is_non_planar) { // This is an unclosed contour or a non-planar face. // Draw it as a line. bool flag = false; m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD) ? GL_SMOOTH : GL_FLAT); // Add vertices in reverse order for Matlab compatibility for (int j = count_f(i)-1; j >= 0; j--) { if (! clip(int (f(i,j) - 1))) { vertex_data::vertex_data_rep *vv = vdata[i+j*fr].get_rep (); const Matrix m = vv->m_coords; if (! flag) { flag = true; m_glfcns.glBegin (GL_LINE_STRIP); } if (ec_mode != UNIFORM) { Matrix col = vv->m_color; if (col.numel () == 3) m_glfcns.glColor3dv (col.data ()); } m_glfcns.glVertex3d (m(0), m(1), m(2)); } else if (flag) { flag = false; m_glfcns.glEnd (); } } // Do loop body with vertex N to "close" GL_LINE_STRIP // from vertex 0 to vertex N. int j = count_f(i)-1; if (flag && ! clip(int (f(i,j) - 1))) { vertex_data::vertex_data_rep *vv = vdata[i+j*fr].get_rep (); const Matrix m = vv->m_coords; if (ec_mode != UNIFORM) { Matrix col = vv->m_color; if (col.numel () == 3) m_glfcns.glColor3dv (col.data ()); } m_glfcns.glVertex3d (m(0), m(1), m(2)); } if (flag) m_glfcns.glEnd (); } else // Normal edge contour drawn with tessellator { tess.begin_polygon (false); tess.begin_contour (); for (int j = count_f(i)-1; j >= 0; j--) { vertex_data::vertex_data_rep *vv = vdata[i+j*fr].get_rep (); tess.add_vertex (vv->m_coords.fortran_vec (), vv); } tess.end_contour (); tess.end_polygon (); } } set_linestyle ("-"); // Disable LineStipple set_linewidth (0.5f); if ((el_mode > 0) && do_lighting) m_glfcns.glDisable (GL_LIGHTING); } else { // FIXME: implement transparency } } if (! props.marker_is ("none") && ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none"))) { bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); bool do_face = draw_all || ! props.markerfacecolor_is ("none"); Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markeredgecolor_rgb ()); Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markerfacecolor_rgb ()); bool has_markerfacecolor = draw_all || false; if ((mecolor.isempty () && ! props.markeredgecolor_is ("none")) || (mfcolor.isempty () && ! props.markerfacecolor_is ("none"))) { Matrix mc = props.get_color_data ().matrix_value (); if (mc.rows () == 1) { // Single color specifications, we can simplify a little bit if (mfcolor.isempty () && ! props.markerfacecolor_is ("none")) mfcolor = mc; if (mecolor.isempty () && ! props.markeredgecolor_is ("none")) mecolor = mc; } else { if (c.isempty ()) c = props.get_color_data ().matrix_value (); has_markerfacecolor = ((c.numel () > 0) && (c.rows () == f.rows ())); } } init_marker (props.get_marker (), props.get_markersize (), props.get_linewidth ()); uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); uint8_t clip_ok = 0x40; for (int i = 0; i < nf; i++) for (int j = 0; j < count_f(i); j++) { int idx = int (f(i,j) - 1); if ((clip_code (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0)) & clip_mask) != clip_ok) continue; Matrix cc; if (c.numel () > 0) { cc.resize (1, 3); if (has_markerfacecolor) cc(0) = c(i,0), cc(1) = c(i,1), cc(2) = c(i,2); else cc(0) = c(idx,0), cc(1) = c(idx,1), cc(2) = c(idx,2); } Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) : Matrix ()); Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) : Matrix ()); draw_marker (v(idx,0), v(idx,1), (has_z ? v(idx,2) : 0), lc, fc); } end_marker (); } #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_scatter (const scatter::properties& props) { #if defined (HAVE_OPENGL) // Do not render if the scatter object has incoherent data std::string msg; if (props.has_bad_data (msg)) { warning ("opengl_renderer: %s. Not rendering.", msg.c_str ()); return; } bool draw_all = selecting; if (draw_all || (! props.marker_is ("none") && ! (props.markeredgecolor_is ("none") && props.markerfacecolor_is ("none")))) { bool do_edge = draw_all || ! props.markeredgecolor_is ("none"); bool do_face = draw_all || ! props.markerfacecolor_is ("none"); const Matrix x = props.get_xdata ().matrix_value (); const Matrix y = props.get_ydata ().matrix_value (); const Matrix z = props.get_zdata ().matrix_value (); const Matrix c = props.get_color_data ().matrix_value (); const Matrix s = props.get_sizedata ().matrix_value (); int np = x.rows (); bool has_z = ! z.isempty (); // If markeredgecolor is "flat", mecolor is empty Matrix mecolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markeredgecolor_rgb ()); Matrix mfcolor = (draw_all ? Matrix (1, 3, 0.0) : props.get_markerfacecolor_rgb ()); const double mea = props.get_markeredgealpha (); const double mfa = props.get_markerfacealpha (); if (props.markerfacecolor_is ("auto")) { gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw_scatter"); graphics_object go = gh_mgr.get_object (props.get___myhandle__ ()); graphics_object ax = go.get_ancestor ("axes"); const axes::properties& ax_props = dynamic_cast<const axes::properties&> (ax.get_properties ()); mfcolor = ax_props.get_color ().matrix_value (); } init_marker (props.get_marker (), std::sqrt (s(0)), props.get_linewidth ()); uint8_t clip_mask = (props.is_clipping () ? 0x7F : 0x40); uint8_t clip_ok = 0x40; Matrix cc; if (! c.isempty ()) { if (c.rows () == 1) cc = c; else { cc.resize (1, 3); cc(0) = c(0,0); cc(1) = c(0,1); cc(2) = c(0,2); } } for (int i = 0; i < np; i++) { if ((clip_code (x(i), y(i), (has_z ? z(i) : 0.0)) & clip_mask) != clip_ok) continue; if (c.rows () > 1) { cc(0) = c(i,0); cc(1) = c(i,1); cc(2) = c(i,2); } Matrix lc = (do_edge ? (mecolor.isempty () ? cc : mecolor) : Matrix ()); Matrix fc = (do_face ? (mfcolor.isempty () ? cc : mfcolor) : Matrix ()); if (s.numel () > 1) change_marker (props.get_marker (), std::sqrt (s(i))); draw_marker (x(i), y(i), (has_z ? z(i) : 0.0), lc, fc, mea, mfa); } end_marker (); } #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_light (const light::properties& props) { #if defined (HAVE_OPENGL) // enable light source m_glfcns.glEnable (m_current_light); // light position float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local Matrix lpos = props.get_position ().matrix_value (); for (int i = 0; i < 3; i++) pos[i] = lpos(i); if (props.style_is ("local")) pos[3] = 1; m_glfcns.glLightfv (m_current_light, GL_POSITION, pos); // light color float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning) Matrix lcolor = props.get_color ().matrix_value (); for (int i = 0; i < 3; i++) col[i] = lcolor(i); m_glfcns.glLightfv (m_current_light, GL_DIFFUSE, col); m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_hggroup (const hggroup::properties& props) { draw (props.get_children ()); } void opengl_renderer::set_ortho_coordinates (void) { #if defined (HAVE_OPENGL) m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPushMatrix (); m_glfcns.glLoadIdentity (); Matrix vp = get_viewport_scaled (); m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPushMatrix (); m_glfcns.glLoadIdentity (); #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::restore_previous_coordinates (void) { #if defined (HAVE_OPENGL) // Restore previous coordinate system m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPopMatrix(); m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPopMatrix(); #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_text (const text::properties& props) { #if defined (HAVE_OPENGL) if (props.get_string ().isempty () || props.color_is ("none")) return; Matrix pos = xform.scale (props.get_data_position ()); // Handle clipping manually when drawing text in ortho coordinates if (! props.is_clipping () || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40)) { set_clipping (false); draw_text_background (props); set_font (props); render_text (props.get_pixels (), props.get_extent_matrix (), pos(0), pos(1), pos(2), props.get_rotation ()); set_clipping (props.is_clipping ()); } #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_text_background (const text::properties& props, bool /*do_rotate*/) { #if defined (HAVE_OPENGL) Matrix bgcol = props.get_backgroundcolor_rgb (); Matrix ecol = props.get_edgecolor_rgb (); if (bgcol.isempty () && ecol.isempty ()) return; Matrix pos = props.get_data_position (); ColumnVector pixpos = get_transform ().transform (pos(0), pos(1), pos(2), true); // Save current transform matrices and set orthogonal window coordinates set_ortho_coordinates (); // Translate coordinates so that the text anchor is (0,0) m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); // FIXME: Only multiples of 90° are handled by the text renderer. // Handle others here. double rotation = props.get_rotation (); m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); double m = points_to_pixels (props.get_margin ()); const Matrix bbox = props.get_extent_matrix (); double x0 = bbox (0) / m_devpixratio - m; double x1 = x0 + bbox(2) / m_devpixratio + 2 * m; double y0 = -(bbox (1) / m_devpixratio - m); double y1 = y0 - (bbox(3) / m_devpixratio + 2 * m); if (! bgcol.isempty ()) { m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2)); bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST); if (depth_test) set_polygon_offset (true, 4.0); m_glfcns.glBegin (GL_QUADS); m_glfcns.glVertex2d (x0, y0); m_glfcns.glVertex2d (x1, y0); m_glfcns.glVertex2d (x1, y1); m_glfcns.glVertex2d (x0, y1); m_glfcns.glEnd (); if (depth_test) set_polygon_offset (false); } if (! ecol.isempty ()) { m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2)); set_linestyle (props.get_linestyle (), false, props.get_linewidth ()); set_linewidth (props.get_linewidth ()); m_glfcns.glBegin (GL_LINE_STRIP); m_glfcns.glVertex2d (x0, y0); m_glfcns.glVertex2d (x1, y0); m_glfcns.glVertex2d (x1, y1); m_glfcns.glVertex2d (x0, y1); m_glfcns.glVertex2d (x0, y0); m_glfcns.glEnd (); set_linestyle ("-"); } restore_previous_coordinates (); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_image (const image::properties& props) { #if defined (HAVE_OPENGL) octave_value cdata = props.get_color_data (); Matrix x = props.get_xdata ().matrix_value (); Matrix y = props.get_ydata ().matrix_value (); draw_texture_image (cdata, x, y); #else octave_unused_parameter (props); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x, Matrix y, bool ortho) { #if defined (HAVE_OPENGL) dim_vector dv (cdata.dims ()); int h = dv(0); int w = dv(1); double x0, x1, y0, y1; double dx = 1.0; if (w > 1) dx = (x(1) - x(0)) / (w - 1); x0 = x(0)-dx/2; x1 = x(1)+dx/2; double dy = 1.0; if (h > 1) dy = (y(1) - y(0)) / (h - 1); y0 = y(0)-dy/2; y1 = y(1)+dy/2; // Expect RGB data if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4)) { opengl_texture tex = opengl_texture::create (m_glfcns, cdata); if (tex.is_valid ()) { m_glfcns.glColor4d (1.0, 1.0, 1.0, 1.0); m_glfcns.glEnable (GL_TEXTURE_2D); m_glfcns.glBegin (GL_QUADS); tex.tex_coord (0.0, 0.0); if (ortho) m_glfcns.glVertex2d (x0, y0); else m_glfcns.glVertex3d (x0, y0, 0.0); tex.tex_coord (1.0, 0.0); if (ortho) m_glfcns.glVertex2d (x1, y0); else m_glfcns.glVertex3d (x1, y0, 0.0); tex.tex_coord (1.0, 1.0); if (ortho) m_glfcns.glVertex2d (x1, y1); else m_glfcns.glVertex3d (x1, y1, 0.0); tex.tex_coord (0.0, 1.0); if (ortho) m_glfcns.glVertex2d (x0, y1); else m_glfcns.glVertex3d (x0, y1, 0.0); m_glfcns.glEnd (); m_glfcns.glDisable (GL_TEXTURE_2D); } } else warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)"); #else octave_unused_parameter (cdata); octave_unused_parameter (x); octave_unused_parameter (y); octave_unused_parameter (ortho); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw (const Matrix& hlist, bool toplevel) { int len = hlist.numel (); gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::draw"); for (int i = len-1; i >= 0; i--) { graphics_object obj = gh_mgr.get_object (hlist(i)); if (obj) draw (obj, toplevel); } } void opengl_renderer::set_viewport (int w, int h) { #if defined (HAVE_OPENGL) m_glfcns.glViewport (0, 0, w, h); #else octave_unused_parameter (w); octave_unused_parameter (h); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } Matrix opengl_renderer::get_viewport_scaled (void) const { Matrix retval (1, 4, 0.0); #if defined (HAVE_OPENGL) #if defined (HAVE_FRAMEWORK_OPENGL) GLint vp[4]; #else int vp[4]; #endif m_glfcns.glGetIntegerv (GL_VIEWPORT, vp); for (int i = 0; i < 4; i++) retval(i) = static_cast<double> (vp[i]) / m_devpixratio; #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif return retval; } void opengl_renderer::set_color (const Matrix& c) { #if defined (HAVE_OPENGL) m_glfcns.glColor3dv (c.data ()); if (! c.isempty ()) txt_renderer.set_color (c); #else octave_unused_parameter (c); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::set_font (const base_properties& props) { bool do_anti_alias = props.get ("fontsmoothing").string_value () == "on"; txt_renderer.set_anti_aliasing (do_anti_alias); txt_renderer.set_font (props.get ("fontname").string_value (), props.get ("fontweight").string_value (), props.get ("fontangle").string_value (), props.get ("__fontsize_points__").double_value () * m_devpixratio); } void opengl_renderer::set_polygon_offset (bool on, float offset) { #if defined (HAVE_OPENGL) if (on) { m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL); m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE); m_glfcns.glPolygonOffset (offset, offset); } else { m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL); m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE); } #else octave_unused_parameter (on); octave_unused_parameter (offset); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::set_linewidth (float w) { #if defined (HAVE_OPENGL) // Measure LineWidth in points. See bug #53056. m_glfcns.glLineWidth (points_to_pixels (w) * m_devpixratio); #else octave_unused_parameter (w); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::set_linestyle (const std::string& s, bool use_stipple, double linewidth) { #if defined (HAVE_OPENGL) // Measure LineWidth in points. See bug #53056. int factor = math::round (points_to_pixels (linewidth) * m_devpixratio); if (factor < 1) factor = 1; uint16_t pattern = 0xFFFF; bool solid = false; if (s == "-") solid = true; else if (s == ":") { if (factor > 1) pattern = 0x5555; else pattern = 0x1111; } else if (s == "--") { if (factor > 1) pattern = 0x0F0F; else pattern = 0x01FF; } else if (s == "-.") { if (factor > 1) pattern = 0x6F6F; else pattern = 0x18FF; } else pattern = 0x0000; m_glfcns.glLineStipple (factor, pattern); if (solid && ! use_stipple) m_glfcns.glDisable (GL_LINE_STIPPLE); else m_glfcns.glEnable (GL_LINE_STIPPLE); #else octave_unused_parameter (s); octave_unused_parameter (use_stipple); octave_unused_parameter (linewidth); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::set_clipbox (double x1, double x2, double y1, double y2, double z1, double z2) { #if defined (HAVE_OPENGL) double dx = (x2-x1); double dy = (y2-y1); double dz = (z2-z1); x1 -= 0.001*dx; x2 += 0.001*dx; y1 -= 0.001*dy; y2 += 0.001*dy; z1 -= 0.001*dz; z2 += 0.001*dz; ColumnVector p (4, 0.0); p(0) = -1; p(3) = x2; m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ()); p(0) = 1; p(3) = -x1; m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ()); p(0) = 0; p(1) = -1; p(3) = y2; m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ()); p(1) = 1; p(3) = -y1; m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ()); p(1) = 0; p(2) = -1; p(3) = z2; m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ()); p(2) = 1; p(3) = -z1; m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ()); xmin = x1; xmax = x2; ymin = y1; ymax = y2; zmin = z1; zmax = z2; #else octave_unused_parameter (x1); octave_unused_parameter (x2); octave_unused_parameter (y1); octave_unused_parameter (y2); octave_unused_parameter (z1); octave_unused_parameter (z2); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::set_clipping (bool enable) { #if defined (HAVE_OPENGL) bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE); if (enable != has_clipping) { if (enable) for (int i = 0; i < 6; i++) m_glfcns.glEnable (GL_CLIP_PLANE0+i); else for (int i = 0; i < 6; i++) m_glfcns.glDisable (GL_CLIP_PLANE0+i); } #else octave_unused_parameter (enable); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::init_marker (const std::string& m, double size, float width) { #if defined (HAVE_OPENGL) m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPushMatrix (); m_glfcns.glLoadIdentity (); Matrix vp = get_viewport_scaled (); m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPushMatrix (); set_clipping (false); set_linewidth (width); marker_id = make_marker_list (m, size, false); filled_marker_id = make_marker_list (m, size, true); #else octave_unused_parameter (m); octave_unused_parameter (size); octave_unused_parameter (width); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::change_marker (const std::string& m, double size) { #if defined (HAVE_OPENGL) marker_id = make_marker_list (m, size, false); filled_marker_id = make_marker_list (m, size, true); #else octave_unused_parameter (m); octave_unused_parameter (size); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::end_marker (void) { #if defined (HAVE_OPENGL) m_glfcns.glDeleteLists (marker_id, 1); m_glfcns.glDeleteLists (filled_marker_id, 1); m_glfcns.glMatrixMode (GL_MODELVIEW); m_glfcns.glPopMatrix (); m_glfcns.glMatrixMode (GL_PROJECTION); m_glfcns.glPopMatrix (); set_linewidth (0.5f); #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::draw_marker (double x, double y, double z, const Matrix& lc, const Matrix& fc, const double la, const double fa) { #if defined (HAVE_OPENGL) ColumnVector tmp = xform.transform (x, y, z, false); m_glfcns.glLoadIdentity (); m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2)); if (filled_marker_id > 0 && fc.numel () > 0) { m_glfcns.glColor4d (fc(0), fc(1), fc(2), fa); set_polygon_offset (true, -1.0); m_glfcns.glCallList (filled_marker_id); if (lc.numel () > 0) { m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); m_glfcns.glEdgeFlag (GL_TRUE); set_polygon_offset (true, -2.0); m_glfcns.glCallList (filled_marker_id); m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); } set_polygon_offset (false); } else if (marker_id > 0 && lc.numel () > 0) { m_glfcns.glColor4d (lc(0), lc(1), lc(2), la); m_glfcns.glCallList (marker_id); } #else octave_unused_parameter (x); octave_unused_parameter (y); octave_unused_parameter (z); octave_unused_parameter (lc); octave_unused_parameter (fc); octave_unused_parameter (la); octave_unused_parameter (fa); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::init_maxlights (void) { #if defined (HAVE_OPENGL) // Check actual maximum number of lights possible if (m_max_lights == 0) { GLint max_lights; m_glfcns.glGetIntegerv (GL_MAX_LIGHTS, &max_lights); m_max_lights = max_lights; } #else // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } std::string opengl_renderer::get_string (unsigned int id) const { #if defined (HAVE_OPENGL) // This is kind of ugly, but glGetString returns a pointer to GLubyte // and there is no std::string constructor that matches. Is there a // better way? std::ostringstream buf; buf << m_glfcns.glGetString (static_cast<GLenum> (id)); return std::string (buf.str ()); #else octave_unused_parameter (id); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); return std::string (); #endif } void opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i) { #if defined (HAVE_OPENGL) double x = n(j,i,0); double y = n(j,i,1); double z = n(j,i,2); double d = sqrt (x*x + y*y + z*z); double dir = 1.0; if (bfl_mode > 0) dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0) ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0); m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d); #else octave_unused_parameter (bfl_mode); octave_unused_parameter (n); octave_unused_parameter (j); octave_unused_parameter (i); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } double opengl_renderer::points_to_pixels (const double val) const { gh_manager& gh_mgr = __get_gh_manager__ ("opengl_renderer::points_to_pixels"); // FIXME: Does making this static cause problems if figure is moved to a // 2nd monitor with a different value for "screenpixelsperinch"? static const double pix_per_pts = gh_mgr.get_object (0).get ("screenpixelsperinch").double_value () / 72.0; double retval = val; if (! m_printing) retval *= pix_per_pts; return retval; } unsigned int opengl_renderer::make_marker_list (const std::string& marker, double size, bool filled) const { #if defined (HAVE_OPENGL) char c = marker[0]; if (filled && (c == '+' || c == 'x' || c == '*' || c == '.')) return 0; unsigned int ID = m_glfcns.glGenLists (1); // FIXME: See bug #53056 (measure LineWidth in points). double sz = points_to_pixels (size); // constants for the * marker const double sqrt2d4 = 0.35355339059327; double tt = sz*sqrt2d4; m_glfcns.glNewList (ID, GL_COMPILE); switch (marker[0]) { case '+': m_glfcns.glBegin (GL_LINES); m_glfcns.glVertex2d (-sz/2, 0); m_glfcns.glVertex2d (sz/2, 0); m_glfcns.glVertex2d (0, -sz/2); m_glfcns.glVertex2d (0, sz/2); m_glfcns.glEnd (); break; case 'x': m_glfcns.glBegin (GL_LINES); m_glfcns.glVertex2d (-sz/2, -sz/2); m_glfcns.glVertex2d (sz/2, sz/2); m_glfcns.glVertex2d (-sz/2, sz/2); m_glfcns.glVertex2d (sz/2, -sz/2); m_glfcns.glEnd (); break; case '*': m_glfcns.glBegin (GL_LINES); m_glfcns.glVertex2d (-sz/2, 0); m_glfcns.glVertex2d (sz/2, 0); m_glfcns.glVertex2d (0, -sz/2); m_glfcns.glVertex2d (0, sz/2); m_glfcns.glVertex2d (-tt, -tt); m_glfcns.glVertex2d (+tt, +tt); m_glfcns.glVertex2d (-tt, +tt); m_glfcns.glVertex2d (+tt, -tt); m_glfcns.glEnd (); break; case '.': { // The dot marker is special and is drawn at 1/3rd the specified size // Ensure that something is drawn even at very small markersizes if (sz > 0 && sz < 3) sz = 3; int div = static_cast<int> (M_PI * sz / 12); if (! (div % 2)) div += 1; // ensure odd number for left/right symmetry div = std::max (div, 3); // ensure at least a few vertices are drawn double ang_step = M_PI / div; m_glfcns.glBegin (GL_POLYGON); for (double ang = 0; ang < 2*M_PI; ang += ang_step) m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang)); m_glfcns.glEnd (); } break; case 's': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (-sz/2, -sz/2); m_glfcns.glVertex2d (-sz/2, sz/2); m_glfcns.glVertex2d (sz/2, sz/2); m_glfcns.glVertex2d (sz/2, -sz/2); m_glfcns.glEnd (); break; case 'o': { int div = static_cast<int> (M_PI * sz / 4); if (! (div % 2)) div += 1; // ensure odd number for left/right symmetry div = std::max (div, 5); // ensure at least a few vertices are drawn double ang_step = M_PI / div; m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); for (double ang = 0; ang < 2*M_PI; ang += ang_step) m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang)); m_glfcns.glEnd (); } break; case 'd': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (0, -sz/2); m_glfcns.glVertex2d (sz/2, 0); m_glfcns.glVertex2d (0, sz/2); m_glfcns.glVertex2d (-sz/2, 0); m_glfcns.glEnd (); break; case 'v': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (0, sz/2); m_glfcns.glVertex2d (sz/2, -sz/2); m_glfcns.glVertex2d (-sz/2, -sz/2); m_glfcns.glEnd (); break; case '^': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (0, -sz/2); m_glfcns.glVertex2d (-sz/2, sz/2); m_glfcns.glVertex2d (sz/2, sz/2); m_glfcns.glEnd (); break; case '>': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (sz/2, 0); m_glfcns.glVertex2d (-sz/2, sz/2); m_glfcns.glVertex2d (-sz/2, -sz/2); m_glfcns.glEnd (); break; case '<': m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); m_glfcns.glVertex2d (-sz/2, 0); m_glfcns.glVertex2d (sz/2, -sz/2); m_glfcns.glVertex2d (sz/2, sz/2); m_glfcns.glEnd (); break; case 'p': { double ang, r, dr; dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02; m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); for (int i = 0; i < 2*5; i++) { ang = (-0.5 + double (i+1) / 5) * M_PI; r = 1.0 - (dr * fmod (double (i+1), 2.0)); m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); } m_glfcns.glEnd (); } break; case 'h': { double ang, r, dr; dr = 1.0 - 0.5/sin (M_PI/3)*1.02; m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP); for (int i = 0; i < 2*6; i++) { ang = (0.5 + double (i+1) / 6.0) * M_PI; r = 1.0 - (dr * fmod (double (i+1), 2.0)); m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang)); } m_glfcns.glEnd (); } break; default: warning ("opengl_renderer: unsupported marker '%s'", marker.c_str ()); break; } m_glfcns.glEndList (); return ID; #else octave_unused_parameter (marker); octave_unused_parameter (size); octave_unused_parameter (filled); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::text_to_pixels (const std::string& txt, uint8NDArray& pixels, Matrix& bbox, int halign, int valign, double rotation) { txt_renderer.text_to_pixels (txt, pixels, bbox, halign, valign, rotation, interpreter); } void opengl_renderer::text_to_strlist (const std::string& txt, std::list<text_renderer::string>& lst, Matrix& bbox, int halign, int valign, double rotation) { txt_renderer.text_to_strlist (txt, lst, bbox, halign, valign, rotation, interpreter); } Matrix opengl_renderer::render_text (const std::string& txt, double x, double y, double z, int halign, int valign, double rotation) { #if defined (HAVE_OPENGL) Matrix bbox (1, 4, 0.0); if (txt.empty ()) return bbox; if (txt_renderer.ok ()) { uint8NDArray pixels; text_to_pixels (txt, pixels, bbox, halign, valign, rotation); render_text (pixels, bbox, x, y, z, rotation); } return bbox; #else octave_unused_parameter (txt); octave_unused_parameter (x); octave_unused_parameter (y); octave_unused_parameter (z); octave_unused_parameter (halign); octave_unused_parameter (valign); octave_unused_parameter (rotation); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } void opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox, double x, double y, double z, double rotation) { #if defined (HAVE_OPENGL) // Transform data coordinates to screen pixel ortho coordinates ColumnVector pixpos = get_transform ().transform (x, y, z, false); Matrix xdata(1, 2, bbox(0) / m_devpixratio); xdata(1) += (bbox(2) - 1) / m_devpixratio; Matrix ydata(1, 2, -bbox(1) / m_devpixratio); ydata(1) -= (bbox(3) - 1) / m_devpixratio; bool blend = m_glfcns.glIsEnabled (GL_BLEND); m_glfcns.glEnable (GL_BLEND); m_glfcns.glEnable (GL_ALPHA_TEST); set_ortho_coordinates (); // Translate coordinates so that the text anchor is (0,0) m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2)); m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0); // Permute pixels returned by freetype Array<octave_idx_type> perm (dim_vector (3, 1)); perm(0) = 2; perm(1) = 1; perm(2) = 0; draw_texture_image (pixels.permute (perm), xdata, ydata, true); restore_previous_coordinates (); m_glfcns.glDisable (GL_ALPHA_TEST); if (! blend) m_glfcns.glDisable (GL_BLEND); #else octave_unused_parameter (pixels); octave_unused_parameter (bbox); octave_unused_parameter (x); octave_unused_parameter (y); octave_unused_parameter (z); octave_unused_parameter (rotation); // This shouldn't happen because construction of opengl_renderer // objects is supposed to be impossible if OpenGL is not available. panic_impossible (); #endif } }