1 | ## Demonstrates some techniques for working with "faces", and |
---|
2 | ## shows how to build a height field (a common feature request) |
---|
3 | ## with it. |
---|
4 | ## David Scherer July 2001 |
---|
5 | |
---|
6 | from visual import * |
---|
7 | |
---|
8 | class Model: |
---|
9 | def __init__(self): |
---|
10 | self.frame = frame() |
---|
11 | self.model = faces(frame=self.frame) |
---|
12 | self.twoSided = true # add every face twice with opposite normals |
---|
13 | |
---|
14 | def FacetedTriangle(self, v1, v2, v3, color=None): |
---|
15 | """Add a triangle to the model, apply faceted shading automatically""" |
---|
16 | try: |
---|
17 | normal = norm( cross(v2-v1, v3-v1) ) |
---|
18 | except: |
---|
19 | normal = vector(0,0,0) |
---|
20 | for v in (v1,v2,v3): |
---|
21 | self.model.append( pos=v, color=color, normal=normal ) |
---|
22 | if self.twoSided: |
---|
23 | for v in (v1,v3,v2): |
---|
24 | self.model.append( pos=v, color=color, normal=-normal ) |
---|
25 | |
---|
26 | def FacetedPolygon(self, *v): |
---|
27 | """Appends a planar polygon of any number of vertices to the model, |
---|
28 | applying faceted shading automatically.""" |
---|
29 | for t in range(len(v)-2): |
---|
30 | self.FacetedTriangle( v[0], v[t+1], v[t+2] ) |
---|
31 | |
---|
32 | def DoSmoothShading(self): |
---|
33 | """Change a faceted model to smooth shaded, by averaging normals at |
---|
34 | coinciding vertices. |
---|
35 | |
---|
36 | This is a very slow and simple smooth shading |
---|
37 | implementation which has to figure out the connectivity of the |
---|
38 | model and does not attempt to detect sharp edges. |
---|
39 | |
---|
40 | It attempts to work even in two-sided mode where there are two |
---|
41 | opposite normals at each vertex. It may fail somehow in pathological |
---|
42 | cases. """ |
---|
43 | |
---|
44 | pos = self.model.pos |
---|
45 | normal = self.model.normal |
---|
46 | |
---|
47 | vertex_map = {} # vertex position -> vertex normal |
---|
48 | vertex_map_backface = {} |
---|
49 | for i in range( len(pos) ): |
---|
50 | tp = tuple(pos[i]) |
---|
51 | old_normal = vertex_map.get( tp, (0,0,0) ) |
---|
52 | if dot(old_normal, normal[i]) >= 0: |
---|
53 | vertex_map[tp] = normal[i] + old_normal |
---|
54 | else: |
---|
55 | vertex_map_backface[tp] = normal[i] + vertex_map_backface.get(tp, (0,0,0)) |
---|
56 | |
---|
57 | for i in range( len(pos) ): |
---|
58 | tp = tuple(pos[i]) |
---|
59 | if dot(vertex_map[tp], normal[i]) >= 0: |
---|
60 | normal[i] = vertex_map[tp] and norm( vertex_map[ tp ] ) |
---|
61 | else: |
---|
62 | normal[i] = vertex_map_backface[tp] and norm(vertex_map_backface[tp] ) |
---|
63 | |
---|
64 | def DrawNormal(self, scale): |
---|
65 | pos = self.model.pos |
---|
66 | normal = self.model.normal |
---|
67 | for i in range(len(pos)): |
---|
68 | arrow(pos=pos[i], axis=normal[i]*scale) |
---|
69 | |
---|
70 | class Mesh (Model): |
---|
71 | def __init__(self, xvalues, yvalues, zvalues): |
---|
72 | Model.__init__(self) |
---|
73 | |
---|
74 | points = zeros( xvalues.shape + (3,), Float ) |
---|
75 | points[...,0] = xvalues |
---|
76 | points[...,1] = yvalues |
---|
77 | points[...,2] = zvalues |
---|
78 | |
---|
79 | for i in range(zvalues.shape[0]-1): |
---|
80 | for j in range(zvalues.shape[1]-1): |
---|
81 | self.FacetedPolygon( points[i,j], points[i,j+1], |
---|
82 | points[i+1,j+1], points[i+1,j] ) |
---|
83 | |
---|
84 | ## Graph a function of two variables (a height field) |
---|
85 | x = arange(-1,1,2./60) |
---|
86 | y = arange(-1,1,2./60) |
---|
87 | |
---|
88 | z = zeros( (len(x),len(y)), Float ) |
---|
89 | x,y = x[:,NewAxis]+z, y+z |
---|
90 | |
---|
91 | m = Mesh( x, (sin(x*pi)+sin(y*pi))*0.2, y ) |
---|
92 | m.DoSmoothShading() |
---|
93 | #m.DrawNormal(0.05) |
---|