Mercurial > jwe > octave
changeset 25870:49ffa78f9243
Use "facenormals" for flat lighting on surfaces (bug #54024).
* graphics.in.h, graphics.cc (surface::properties::update_face_normals):
New function.
* gl-render.cc (draw_surface): Use face normals if lighting mode is "flat".
* genpropdoc.m (surface): Document "facenormals(mode)" and
"vertexnormals(mode)".
* light.m: Document usage of "facenormals" and "vertexnormals".
* NEWS: Announce changes.
author | Markus Mützel <markus.muetzel@gmx.de> |
---|---|
date | Sat, 08 Sep 2018 20:36:30 +0200 |
parents | cece80ddc264 |
children | daebd587961d |
files | NEWS doc/interpreter/genpropdoc.m libinterp/corefcn/gl-render.cc libinterp/corefcn/graphics.cc libinterp/corefcn/graphics.in.h scripts/plot/draw/light.m |
diffstat | 6 files changed, 167 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Sat Sep 08 17:23:57 2018 +0200 +++ b/NEWS Sat Sep 08 20:36:30 2018 +0200 @@ -51,6 +51,12 @@ However, if the property "IntegerHandle" has been set to "off" then the property will return an empty matrix ([]). + ** Patch and surface graphic objects now use the "FaceNormals" property for + flat lighting. + + ** "FaceNormals" and "EdgeNormals" for patch and surface graphic objects are + now calculated automatically if necessary. + ** Printing using the -dtiff output device will now create compressed images using lzw compression. This change was made for Matlab compatibility. To produce uncompressed images use the -dtiffn @@ -63,6 +69,9 @@ when using OpenGL graphics, the Qt QOFFSCREENSURFACE feature must be available and you must use the qt graphics toolkit. + ** It is now possible to use files and folders containing Unicode characters + in Windows. + ** New functions added in 5.0: isfile
--- a/doc/interpreter/genpropdoc.m Sat Sep 08 17:23:57 2018 +0200 +++ b/doc/interpreter/genpropdoc.m Sat Sep 08 20:36:30 2018 +0200 @@ -1236,6 +1236,17 @@ the vertices). @qcode{\"phong\"} is deprecated and has the same effect as \ @qcode{\"gouraud\"}."; + case "facenormals" + s.doc = "Face normals are used for lighting the edges or faces if the \ +@code{edgelighting} or @code{facelighting} properties are set to \ +@qcode{\"flat\"}. __modemsg__"; + + case "facenormalsmode" + s.doc = "If this property is set to @qcode{\"auto\"}, \ +@code{facenormals} are automatically calculated if the @code{edgelighting} or \ +@code{facelighting} property are set to @qcode{\"flat\"} and at least one \ +@code{light} object is present and visible in the same axes."; + case "interpreter" case "linestyle" s.doc = "@xref{Line Styles}."; @@ -1260,7 +1271,6 @@ s.valid = "scalar"; case "meshstyle" - case "normalmode" case "specularcolorreflectance" s.doc = "Reflectance for specular color. Value between 0.0 (color \ of underlying face) and 1.0 (color of light source)."; @@ -1277,6 +1287,16 @@ s.valid = "scalar"; case "vertexnormals" + s.doc = "Vertex normals are used for lighting the edges or faces if \ +the @code{edgelighting} or @code{facelighting} properties are set to \ +@qcode{\"gouraud\"}. __modemsg__"; + + case "vertexnormalsmode" + s.doc = "If this property is set to @qcode{\"auto\"}, \ +@code{vertexnormals} are automatically calculated if the @code{edgelighting} \ +or @code{facelighting} property are set to @qcode{\"gouraud\"} and at least \ +one @code{light} object is present and visible in the same axes."; + case "xdata" s.valid = "matrix";
--- a/libinterp/corefcn/gl-render.cc Sat Sep 08 17:23:57 2018 +0200 +++ b/libinterp/corefcn/gl-render.cc Sat Sep 08 20:36:30 2018 +0200 @@ -2391,7 +2391,8 @@ int zc = z.columns (); NDArray c; - const NDArray n = props.get_vertexnormals ().array_value (); + const NDArray vn = props.get_vertexnormals ().array_value (); + const NDArray fn = props.get_facenormals ().array_value (); // FIXME: handle transparency Matrix a; @@ -2565,7 +2566,8 @@ } } if (fl_mode > 0) - set_normal (bfl_mode, n, j-1, i-1); + 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)); @@ -2596,7 +2598,7 @@ } if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j-1, i); + set_normal (bfl_mode, vn, j-1, i); m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); @@ -2626,7 +2628,7 @@ } } if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); + set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); @@ -2656,7 +2658,7 @@ } } if (fl_mode == GOURAUD) - set_normal (bfl_mode, n, j, i-1); + set_normal (bfl_mode, vn, j, i-1); m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); @@ -2772,7 +2774,8 @@ } } if (el_mode > 0) - set_normal (bfl_mode, n, j-1, i); + set_normal (bfl_mode, (el_mode == GOURAUD ? vn : fn), + j-1, i); m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i)); @@ -2799,7 +2802,7 @@ } } if (el_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); + set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i)); @@ -2869,7 +2872,8 @@ } } if (el_mode > 0) - set_normal (bfl_mode, n, j, i-1); + set_normal (bfl_mode, (el_mode == GOURAUD ? vn : fn), + j, i-1); m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1)); @@ -2896,7 +2900,7 @@ } } if (el_mode == GOURAUD) - set_normal (bfl_mode, n, j, i); + set_normal (bfl_mode, vn, j, i); m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
--- a/libinterp/corefcn/graphics.cc Sat Sep 08 17:23:57 2018 +0200 +++ b/libinterp/corefcn/graphics.cc Sat Sep 08 20:36:30 2018 +0200 @@ -8677,8 +8677,7 @@ std::list<graphics_object> children_list; std::list<graphics_object>::iterator children_list_iter; get_children_of_type ("patch", false, true, children_list); - // FIXME: Un-comment when surface is ready: - // get_children_of_type ("surface", false, true, children_list); + get_children_of_type ("surface", false, true, children_list); // trigger normals calculation for these objects for (children_list_iter = children_list.begin (); @@ -8693,10 +8692,9 @@ } else { - // FIXME: Un-comment when surface is ready: - // surface::properties& surface_props = - // dynamic_cast<surface::properties&> (kid.get_properties ()); - // surface_props.update_normals (false); + surface::properties& surface_props = + dynamic_cast<surface::properties&> (kid.get_properties ()); + surface_props.update_normals (false); } } } @@ -9312,8 +9310,8 @@ { // more general for non-planar polygons - // calculate face normal with Newill method - // https://courses.cit.cornell.edu/cs417-land/SECTIONS/normals.html + // calculate face normal with Newell's method + // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method j1 = nc - 1; j2 = 0; i1 = f(i,j1) - 1; i2 = f(i,j2) - 1; @@ -9505,9 +9503,91 @@ } void -surface::properties::update_vertex_normals (void) -{ - if (vertexnormalsmode_is ("auto")) +surface::properties::update_face_normals (bool reset) +{ + if (! facenormalsmode_is ("auto")) + return; + + if ((facelighting_is ("flat") || edgelighting_is ("flat")) && + get_do_lighting ()) + { + Matrix x = get_xdata ().matrix_value (); + Matrix y = get_ydata ().matrix_value (); + Matrix z = get_zdata ().matrix_value (); + + int p = z.columns (); + int q = z.rows (); + + // FIXME: There might be a cleaner way to do this. When data is changed + // the update_xdata, update_ydata, update_zdata routines are called in a + // serial fashion. Until the final call to update_zdata the matrices + // will be of mismatched dimensions which can cause an out-of-bound + // indexing in the code below. This one-liner prevents calculating + // normals until dimensions match. + if (x.columns () != p || y.rows () != q) + return; + + NDArray n (dim_vector (q-1, p-1, 3), 0.0); + + bool x_mat = (x.rows () == q); + bool y_mat = (y.columns () == p); + + double dx = x(1,1) - x(0,0); + double dy = y(1,1) - y(0,0); + + int i1, i2, j1, j2; + i1 = i2 = 0; + j1 = j2 = 0; + + for (int i = 0; i < p-1; i++) + { + if (y_mat) + { + i1 = i; + i2 = i + 1; + } + + for (int j = 0; j < q-1; j++) + { + if (x_mat) + { + j1 = j; + j2 = j + 1; + } + + double& nx = n(j, i, 0); + double& ny = n(j, i, 1); + double& nz = n(j, i, 2); + + // calculate face normal with Newell's method + // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal#Newell.27s_Method + + nx = dy * (z(j1,i1) + z(j2,i1) - z(j1,i2) - z(j2,i2)); + ny = dx * (z(j1,i1) + z(j1,i2) - z(j2,i1) - z(j2,i2)); + nz = 2 * dx * dy; + + double d = std::max (std::max (fabs (nx), fabs (ny)), fabs (nz)); + + nx /= d; + ny /= d; + nz /= d; + } + } + facenormals = n; + } + else if (reset) + facenormals = Matrix (); +} + +void +surface::properties::update_vertex_normals (bool reset) +{ + if (! vertexnormalsmode_is ("auto")) + return; + + if ((facelighting_is ("gouraud") || facelighting_is ("phong") || + edgelighting_is ("gouraud") || edgelighting_is ("phong")) && + get_do_lighting ()) { Matrix x = get_xdata ().matrix_value (); Matrix y = get_ydata ().matrix_value (); @@ -9593,6 +9673,8 @@ } vertexnormals = n; } + else if (reset) + vertexnormals = Matrix (); } // ---------------------------------------------------------------------
--- a/libinterp/corefcn/graphics.in.h Sat Sep 08 17:23:57 2018 +0200 +++ b/libinterp/corefcn/graphics.in.h Sat Sep 08 20:36:30 2018 +0200 @@ -5118,13 +5118,13 @@ string_property displayname , "" double_radio_property edgealpha , double_radio_property (1.0, radio_values ("flat|interp")) color_property edgecolor , color_property (color_values (0, 0, 0), radio_values ("none|flat|interp")) - radio_property edgelighting , "{none}|flat|gouraud|phong" + radio_property edgelighting u , "{none}|flat|gouraud|phong" radio_property erasemode h , "{normal}|none|xor|background" double_radio_property facealpha , double_radio_property (1.0, radio_values ("flat|interp|texturemap")) color_property facecolor , color_property (radio_values ("none|{flat}|interp|texturemap"), color_values (0, 0, 0)) - radio_property facelighting , "none|{flat}|gouraud|phong" + radio_property facelighting u , "none|{flat}|gouraud|phong" array_property facenormals m , Matrix () - radio_property facenormalsmode , "{auto}|manual" + radio_property facenormalsmode u , "{auto}|manual" // FIXME: DEPRECATED: Remove interpreter property in version 6. radio_property interpreter hd , "{tex}|none|latex" radio_property linestyle , "{-}|--|:|-.|none" @@ -5190,6 +5190,14 @@ specularstrength.add_constraint ("max", 1.0, true); } + public: + void update_normals (bool reset) + { + update_face_normals (reset); + update_vertex_normals (reset); + } + + private: void update_alphadata (void) { @@ -5209,26 +5217,38 @@ void update_xdata (void) { - update_vertex_normals (); + update_normals (true); set_xlim (xdata.get_limits ()); } void update_ydata (void) { - update_vertex_normals (); + update_normals (true); set_ylim (ydata.get_limits ()); } void update_zdata (void) { - update_vertex_normals (); + update_normals (true); set_zlim (zdata.get_limits ()); } - void update_vertex_normals (void); + void update_face_normals (bool reset); + void update_vertex_normals (bool reset); + + void update_facenormalsmode (void) + { update_face_normals (false); } void update_vertexnormalsmode (void) - { update_vertex_normals (); } + { update_vertex_normals (false); } + + void update_edgelighting (void) + { update_normals (false); } + + void update_facelighting (void) + { update_normals (false); } + + }; private:
--- a/scripts/plot/draw/light.m Sat Sep 08 17:23:57 2018 +0200 +++ b/scripts/plot/draw/light.m Sat Sep 08 20:36:30 2018 +0200 @@ -29,7 +29,9 @@ ## objects are drawn with lighting effects. Supported values for Lighting ## properties are @qcode{"none"} (no lighting effects), @qcode{"flat"} (faceted ## look of the objects), and @qcode{"gouraud"} (linear interpolation of the -## lighting effects between the vertices). +## lighting effects between the vertices). If the lighting mode is set to +## @qcode{"flat"}, the @qcode{"FaceNormals"} property is used for lighting. +## For @qcode{"gouraud"}, the @qcode{"VertexNormals"} property is used. ## ## Up to eight light objects are supported per axes. (Implementation dependent) ##