## Copyright (C) 2016 M. Muetzel
##
## 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
## <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {} update_lighting (@var{h_axes})
##
## Update the lighting of patch objects in the axes @var{h_axes}.
##
## This function is only intented for demonstrating a possible lighting model
## (Phong) in Octave.
##
## @end deftypefn

function update_lighting (h_axes)

  h_lights = findobj (h_axes, "Type", "light");
  if (isempty (h_lights))
    return
  endif

  light_vector = get (h_lights(1), "Position");
  light_vector = light_vector / norm (light_vector);
  light_color = get (h_lights(1), "Color");
  ambient_light_color = get (h_axes, "AmbientLightColor");

  ## Get view vector.
  view_vector = get (h_axes, "CameraPosition");
  view_vector = view_vector / norm (view_vector);

  dar = get (h_axes, "DataAspectRatio");

  h_patches = findobj (h_axes, "Type", "patch");

  for (i_patch = 1:length(h_patches))
    h_object = h_patches(i_patch);
    vertex_normals = get (h_object, "VertexNormals");
    if (isempty (vertex_normals))
      vertices = get (h_object, "Vertices");
      faces = get (h_object, "Faces");
      if (isempty (faces))
        continue
      endif
      vec1 = vertices(faces(:,2),:) - vertices(faces(:,1),:);
      vec2 = vertices(faces(:,3),:) - vertices(faces(:,1),:);
      vertex_normals(:,1) = vec1(:,2).*vec2(:,3) - vec1(:,3).*vec2(:,2);
      vertex_normals(:,2) = vec1(:,3).*vec2(:,1) - vec1(:,1).*vec2(:,3);
      vertex_normals(:,3) = vec1(:,1).*vec2(:,2) - vec1(:,2).*vec2(:,1);
      ## Store normals in the patch for use on subsequent calls.
      set (h_object, "VertexNormals", vertex_normals);
    endif
    ## vertex_normals must point in direction of viewer.
    pointing = sum(vertex_normals .* view_vector, 2);
    vertex_normals = vertex_normals .* sign (pointing);

    ## Account for differing data aspect ratios.
    vertex_normals = vertex_normals .* dar;

    ## Normalize vertex_normals.
    vertex_normals = vertex_normals ./ norm (vertex_normals, "rows");

    ## Workaround: Save FaceVertexCData to "private" property __FaceVertexCData__
    ## before changing FaceVertexCData to color values including lighting
    ## FIXME: Remove workaround
    face_color = getappdata (h_object, "__FaceVertexCData__");
    if (isempty (face_color) || (size (face_color, 1) != size (vertex_normals, 1)))
      face_color = get (h_object, "FaceVertexCData");
      if (isempty (face_color) || (size (face_color, 1) != size (vertex_normals, 1)))
        face_color = repmat (get (h_object , "FaceColor"), size (vertex_normals, 1), 1);
      endif
      ## FaceVertexCData will be overwritten. Save original values.
      setappdata (h_object, "__FaceVertexCData__", face_color);
    endif

    ## Ambient, diffuse, and specular reflexes.
    ambient_coeff  = get (h_object, "AmbientStrength");
    diffuse_coeff  = get (h_object, "DiffuseStrength");
    specular_coeff = get (h_object, "SpecularStrength");
    specular_exp   = get (h_object, "SpecularExponent");

    ambient_reflex  = ambient_coeff  * ambient_light_color;
    diffuse_reflex  = diffuse_coeff  * light_color .* ...
        diffuse  (vertex_normals(:,1), vertex_normals(:,2), vertex_normals(:,3), ...
                  light_vector);
    specular_reflex = specular_coeff * light_color .* ...
        specular (vertex_normals(:,1), vertex_normals(:,2), vertex_normals(:,3), ...
                  light_vector, view_vector, specular_exp);

    rgb = face_color .* (ambient_reflex + diffuse_reflex) + specular_reflex;

    ## FIXME: A final patch must not overwrite FaceVertexCData.
    set (h_object, "FaceVertexCData", rgb, "FaceColor", "interp");

  endfor

endfunction
