#!BPY
"""
Name: 'Select_frontfacing_faces'
Blender: 248
Group: 'Mesh'
Tooltip: 'Select faces frontfacing the current camera.'
"""

__author__ = "Antonio Ospite"
__url__ = ["blender"]
__version__ = "1.0"

__bpydoc__ = """\
    This script select faces frontfacing the current camera.
"""

# ---------------------------------------------------------------------
#    Copyright (c) 2006 Antonio Ospite
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# ---------------------------------------------------------------------

import Blender
from Blender import Window, Scene, Object, Mesh
from Blender.Mathutils import *
from math import *


def is_face_visible(face, obj, camObj):
    """Determine if a face of an object frontfaces a given camera.
    
    The normals need to be transformed, but note that we should apply only the
    rotation part of the tranformation matrix, since the normals are
    normalized and they can be intended as starting from the origin.

    The view vector is calculated from the camera location and one of the
    vertices of the face (expressed in World coordinates, after applying
    modelview transformations).

    After those transformations we determine if a face is visible by computing
    the angle between the face normal and the view vector, this angle has to be
    between -90 and 90 degrees for the face to be interpreted as frontfacing.
    This corresponds somehow to the dot product between the two,
    if it results > 0 then the face is visible.

    There is no need to normalize those vectors since we are only interested in
    the sign of the cross product and not in the product value.
    """

    # The transformation matrix of the object
    mObj = Matrix(obj.getMatrix())
    mObj.transpose()

    # The normal after applying the current object rotation
    normal = mObj.rotationPart() * Vector(face.no)

    # View vector in orthographics projections can be considered simply as the
    # camera position
    #view_vect = Vector(camObj.loc)

    # View vector as in perspective projections
    # it is the difference between the camera position and
    # one point of the face, we choose the furthest point.
    # TODO: make the code more pythonic :)
    max_len = 0
    for vect in face:
        point = Vector(vect.co)
        point = mObj * point.resize4D()
        point.resize3D()
        vv = Vector(camObj.loc) - point
        if vv.length > max_len:
            max_len = vv.length
            view_vect = vv

    # A possible variaton can be to pick the view vector accordigly to the
    # current 3D view and not from the camera position.
    #view_vect = Vector(Window.GetViewVector())


    # if d > 0 the face is visible from the camera
    d = view_vect * normal
    
    if d > 0:
        return True
    else:
        return False
    

def select_frontfacing_faces():
    """Select frontfacing faces of the currently active object.

    The selection is done accordigly to the current active camera.
    """

    editmode = Window.EditMode()
    if editmode: Window.EditMode(0)

    # Face select mode
    oldmode = Mesh.Mode()
    Mesh.Mode(Mesh.SelectModes['FACE'])

    scene = Scene.GetCurrent()
    camObj = scene.objects.camera

    # get the current Object
    object = scene.objects.active

    if (object.getType() != "Mesh"):
        print "Type:", object.getType(), "\tSorry, only mesh Object supported!"
        return


    mesh = object.data
    
    # Loop on faces
    for f in mesh.faces:
        f.sel = 0
        if is_face_visible(f, object, camObj):
            f.sel = 1

    mesh.update()

    #Mesh.Mode(oldmode)
    if editmode: Window.EditMode(1) 
    Blender.Redraw()
    

# The main program
if __name__ == "__main__":
    select_frontfacing_faces()
