+ 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 visible.
+ 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.
+
+ NOTE: here we assume the face vertices are in WorldCoordinates, so
+ please transform the object _before_ doing the test.
+ """
+
+ normal = Vector(face.no)
+ camPos = self._getObjPosition(self.cameraObj)
+ view_vect = None
+
+ # View Vector in orthographics projections is the view Direction of
+ # the camera
+ if self.cameraObj.data.getType() == 1:
+ view_vect = self._cameraViewDirection()
+
+ # View vector in perspective projections can be considered as
+ # the difference between the camera position and one point of
+ # the face, we choose the farthest point from the camera.
+ if self.cameraObj.data.getType() == 0:
+ vv = max( [ ((camPos - Vector(v.co)).length, (camPos - Vector(v.co))) for v in face] )
+ view_vect = vv[1]
+
+ # if d > 0 the face is visible from the camera
+ d = view_vect * normal
+
+ if d > 0:
+ return True
+ else:
+ return False
+
+
+ # Scene methods
+
+ def _doConvertGeometricObjToMesh(self, scene):
+ """Convert all "geometric" objects to mesh ones.
+ """
+ geometricObjTypes = ['Mesh', 'Surf', 'Curve', 'Text']
+
+ Objects = scene.getChildren()
+ objList = [ o for o in Objects if o.getType() in geometricObjTypes ]
+ for obj in objList:
+ old_obj = obj
+ obj = self._convertToRawMeshObj(obj)
+ scene.link(obj)
+ scene.unlink(old_obj)
+
+
+ # XXX Workaround for Text and Curve which have some normals
+ # inverted when they are converted to Mesh, REMOVE that when
+ # blender will fix that!!
+ if old_obj.getType() in ['Curve', 'Text']:
+ me = obj.getData(mesh=1)
+ for f in me.faces: f.sel = 1;
+ for v in me.verts: v.sel = 1;
+ me.remDoubles(0)
+ me.triangleToQuad()
+ me.recalcNormals()
+ me.update()
+
+
+ def _doSceneClipping(self, scene):
+ """Clip objects against the View Frustum.
+
+ For now clip away only objects according to their center position.
+ """
+
+ cpos = self._getObjPosition(self.cameraObj)
+ view_vect = self._cameraViewDirection()
+
+ near = self.cameraObj.data.clipStart
+ far = self.cameraObj.data.clipEnd
+
+ aspect = float(self.canvasRatio[0])/float(self.canvasRatio[1])
+ fovy = atan(0.5/aspect/(self.cameraObj.data.lens/32))
+ fovy = fovy * 360.0/pi
+
+ Objects = scene.getChildren()
+ for o in Objects:
+ if o.getType() != 'Mesh': continue;
+
+ obj_vect = Vector(cpos) - self._getObjPosition(o)
+
+ d = obj_vect*view_vect
+ theta = AngleBetweenVecs(obj_vect, view_vect)
+
+ # if the object is outside the view frustum, clip it away
+ if (d < near) or (d > far) or (theta > fovy):
+ scene.unlink(o)
+
+ def _doSceneDepthSorting(self, scene):
+ """Sort objects in the scene.
+
+ The object sorting is done accordingly to the object centers.
+ """
+
+ c = self._getObjPosition(self.cameraObj)
+
+ by_center_pos = (lambda o1, o2:
+ (o1.getType() == 'Mesh' and o2.getType() == 'Mesh') and
+ cmp((self._getObjPosition(o1) - Vector(c)).length,
+ (self._getObjPosition(o2) - Vector(c)).length)
+ )
+
+ # TODO: implement sorting by bounding box, if obj1.bb is inside obj2.bb,
+ # then ob1 goes farther than obj2, useful when obj2 has holes
+ by_bbox = None