X-Git-Url: https://git.ao2.it/vrm.git/blobdiff_plain/58b487476c46b70b9900d613bd875a3d9d42a515..d2402688723bb3a348e7991869e45145db77a959:/vrm.py diff --git a/vrm.py b/vrm.py index 0338065..e6ac4d4 100755 --- a/vrm.py +++ b/vrm.py @@ -44,8 +44,6 @@ __bpydoc__ = """\ # Things TODO for a next release: # - FIX the issue with negative scales in object tranformations! # - Use a better depth sorting algorithm -# - Implement clipping of primitives and do handle object intersections. -# (for now only clipping away whole objects is supported). # - Review how selections are made (this script uses selection states of # primitives to represent visibility infos) # - Use a data structure other than Mesh to represent the 2D image? @@ -80,9 +78,13 @@ __bpydoc__ = """\ # recalculated at each frame! # * PDF output (using reportlab) # * Fixed another problem in the animation code the current frame was off -# by one +# by one in the case of camera movement. # * Use fps as specified in blender when VectorWriter handles animation # * Remove the real file opening in the abstract VectorWriter +# * View frustum clipping +# * Scene clipping done using bounding box instead of object center +# * Fix camera type selection for blender>2.43 (Thanks to Thomas Lachmann) +# * Compatibility with python 2.3 # # --------------------------------------------------------------------- @@ -92,6 +94,13 @@ from Blender.Mathutils import * from math import * import sys, time +def uniq(alist): + tmpdict = dict() + return [tmpdict.setdefault(e,e) for e in alist if e not in tmpdict] + # in python > 2.4 we ca use the following + #return [ u for u in alist if u not in locals()['_[1]'] ] + + # Constants EPS = 10e-5 @@ -105,7 +114,7 @@ class config: polygons = dict() polygons['SHOW'] = True polygons['SHADING'] = 'FLAT' # FLAT or TOON - polygons['HSR'] = 'NEWELL' # PAINTER or NEWELL + polygons['HSR'] = 'PAINTER' # PAINTER or NEWELL # Hidden to the user for now polygons['EXPANSION_TRICK'] = True @@ -683,9 +692,13 @@ class HSR: negVertList.append(V1) - # uniq - posVertList = [ u for u in posVertList if u not in locals()['_[1]'] ] - negVertList = [ u for u in negVertList if u not in locals()['_[1]'] ] + # uniq for python > 2.4 + #posVertList = [ u for u in posVertList if u not in locals()['_[1]'] ] + #negVertList = [ u for u in negVertList if u not in locals()['_[1]'] ] + + # a more portable way + posVertList = uniq(posVertList) + negVertList = uniq(negVertList) # If vertex are all on the same half-space, return @@ -887,13 +900,22 @@ class Projector: fovy = atan(0.5/aspect/(camera.lens/32)) fovy = fovy * 360.0/pi - + + + if Blender.Get('version') < 243: + camPersp = 0 + camOrtho = 1 + else: + camPersp = 'persp' + camOrtho = 'ortho' + # What projection do we want? - if camera.type == 0: + if camera.type == camPersp: mP = self._calcPerspectiveMatrix(fovy, aspect, near, far) - elif camera.type == 1: - mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale) + elif camera.type == camOrtho: + mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale) + # View transformation cam = Matrix(cameraObj.getInverseMatrix()) cam.transpose() @@ -1644,12 +1666,8 @@ class SWFVectorWriter(VectorWriter): p1 = self._calcCanvasCoord(e.v1) p2 = self._calcCanvasCoord(e.v2) - # FIXME: this is just a qorkaround, remove that after the - # implementation of propoer Viewport clipping - if abs(p1[0]) < 3000 and abs(p2[0]) < 3000 and abs(p1[1]) < 3000 and abs(p1[2]) < 3000: - s.movePenTo(p1[0], p1[1]) - s.drawLineTo(p2[0], p2[1]) - + s.movePenTo(p1[0], p1[1]) + s.drawLineTo(p2[0], p2[1]) s.end() sprite.add(s) @@ -1808,10 +1826,7 @@ class PDFVectorWriter(VectorWriter): p1 = self._calcCanvasCoord(e.v1) p2 = self._calcCanvasCoord(e.v2) - # FIXME: this is just a workaround, remove that after the - # implementation of propoer Viewport clipping - if abs(p1[0]) < 3000 and abs(p2[0]) < 3000 and abs(p1[1]) < 3000 and abs(p1[2]) < 3000: - self.canvas.line(p1[0], p1[1], p2[0], p2[1]) + self.canvas.line(p1[0], p1[1], p2[0], p2[1]) @@ -2114,9 +2129,7 @@ class Renderer: for o in Objects: if o.getType() != 'Mesh': continue; - # TODO: use the object bounding box (that is already in WorldSpace) - # bb = o.getBoundBox() and then: for point in bb: ... - + """ obj_vect = Vector(cam_pos) - self._getObjPosition(o) d = obj_vect*view_vect @@ -2125,6 +2138,30 @@ class Renderer: # if the object is outside the view frustum, clip it away if (d < near) or (d > far) or (theta > fovy): scene.unlink(o) + """ + + # Use the object bounding box + # (whose points are already in WorldSpace Coordinate) + + bb = o.getBoundBox() + + points_outside = 0 + for p in bb: + p_vect = Vector(cam_pos) - Vector(p) + + d = p_vect * view_vect + theta = AngleBetweenVecs(p_vect, view_vect) + + # Is this point outside the view frustum? + if (d < near) or (d > far) or (theta > fovy): + points_outside += 1 + + # If the bb is all outside the view frustum we clip the whole + # object away + if points_outside == len(bb): + scene.unlink(o) + + def _doConvertGeometricObjsToMesh(self, scene): """Convert all "geometric" objects to mesh ones. @@ -2162,18 +2199,25 @@ class Renderer: c = self._getObjPosition(self.cameraObj) - by_center_pos = (lambda o1, o2: + by_obj_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 + # Implement sorting by bounding box, the object with the bb + # nearest to the camera should be drawn as last. + by_nearest_bbox_point = (lambda o1, o2: + (o1.getType() == 'Mesh' and o2.getType() == 'Mesh') and + cmp( min( [(Vector(p) - Vector(c)).length for p in o1.getBoundBox()] ), + min( [(Vector(p) - Vector(c)).length for p in o2.getBoundBox()] ) + ) + ) + Objects = scene.getChildren() - Objects.sort(by_center_pos) + #Objects.sort(by_obj_center_pos) + Objects.sort(by_nearest_bbox_point) # update the scene for o in Objects: @@ -2441,16 +2485,17 @@ class Renderer: for clipface in cvv: clippedfaces = [] + for f in facelist: newfaces = HSR.splitOn(clipface, f, return_positive_faces=False) if not newfaces: - # Check if the face is inside the view rectangle + # Check if the face is all outside the view frustum # TODO: Do this test before, it is more efficient points_outside = 0 for v in f: - if abs(v[0]) > 1-EPS or abs(v[1]) > 1-EPS: + if abs(v[0]) > 1-EPS or abs(v[1]) > 1-EPS or abs(v[2]) > 1-EPS: points_outside += 1 if points_outside != len(f): @@ -2465,9 +2510,9 @@ class Renderer: nf.col = [f.col[0]] * len(nf.v) clippedfaces.append(nf) - facelist = clippedfaces[:] + nmesh.faces = facelist nmesh.update()