Expect input coordinates for the mesh projection function to be in world
[blender-addons-contrib.git] / object_physics_meadow / duplimesh.py
1 ### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy, sys
22 from math import *
23 from mathutils import *
24
25 def tri_signed_area(v1, v2, v3, i, j):
26     return 0.5 * ((v1[i] - v2[i]) * (v2[j] - v3[j]) + (v1[j] - v2[j]) * (v3[i] - v2[i]))
27
28 # get the 2 dominant axis values, 0==X, 1==Y, 2==Z
29 def axis_dominant(axis):
30     xn = fabs(axis[0])
31     yn = fabs(axis[1])
32     zn = fabs(axis[2])
33     if zn >= xn and zn >= yn:
34         return 0, 1
35     elif yn >= xn and yn >= zn:
36         return 0, 2
37     else:
38         return 1, 2
39
40 def barycentric_weights(v1, v2, v3, co, n):
41     i, j = axis_dominant(n)
42     
43     w = (tri_signed_area(v2, v3, co, i, j),
44          tri_signed_area(v3, v1, co, i, j),
45          tri_signed_area(v1, v2, co, i, j))
46     wtot = w[0] + w[1] + w[2]
47     
48     if fabs(wtot) > sys.float_info.epsilon:
49         inv_w = 1.0 / wtot
50         return True, tuple(x*inv_w for x in w)
51     else:
52         return False, tuple(1.0/3.0 for x in w)
53
54 def interp_weights_face(verts, co):
55     w = (0.0, 0.0, 0.0, 0.0)
56     
57     # OpenGL seems to split this way, so we do too
58     if len(verts) > 3:
59         n = (verts[0] - verts[2]).cross(verts[1] - verts[3])
60         
61         ok, w3 = barycentric_weights(verts[0], verts[1], verts[3], co, n)
62         w = (w3[0], w3[1], 0.0, w3[2])
63         idx = (0, 1, 3)
64         
65         if not ok or w[0] < 0.0:
66             # if w[1] is negative, co is on the other side of the v1-v3 edge,
67             # so we interpolate using the other triangle
68             ok, w3 = barycentric_weights(verts[1], verts[2], verts[3], co, n)
69             w = (0.0, w3[0], w3[1], w3[2])
70             idx = (1, 2, 3)
71         
72     else:
73         n = (verts[0] - verts[2]).cross(verts[1] - verts[2])
74         
75         ok, w3 = barycentric_weights(verts[0], verts[1], verts[2], co, n)
76         w = (w3[0], w3[1], w3[2], 0.0)
77         idx = (0, 1, 2)
78     
79     return w, idx
80
81
82 def project_on_ground(groundob, co):
83     groundmat4 = groundob.matrix_world
84     inv_groundmat4 = groundmat4.inverted()
85     groundmat3 = groundmat4.to_3x3()
86     
87     zmin = min(p[2] for p in groundob.bound_box) - 1.0
88     zmax = max(p[2] for p in groundob.bound_box) + 1.0
89     
90     obco = inv_groundmat4 * Vector(co[0:3] + (1.0,)) # co expected to be in world space
91     ray_start = (obco[0], obco[1], zmax)
92     ray_end = (obco[0], obco[1], zmin)
93     
94     hit, nor, index = groundob.ray_cast(ray_start, ray_end)
95     if index >= 0:
96         return True, groundmat4 * hit, groundmat3 * nor, index
97     else:
98         return False, co, (0.0, 0.0, 1.0), -1
99
100
101 def make_dupli_mesh(name, obmat, samples, scale):
102     tot = len(samples)
103     scalemat = Matrix()
104     scalemat[0][0] = scalemat[1][1] = scalemat[2][2] = scale
105     scalemat[3][3] = 1.0
106     
107     invobmat = obmat.inverted()
108     
109     def verts():
110         for loc, nor in samples:
111             mat = Matrix.Translation(loc) * invobmat * scalemat
112             yield ( mat * Vector((-0.86603, -0.5, 0.0)) )[:]
113             yield ( mat * Vector(( 0.86603, -0.5, 0.0)) )[:]
114             yield ( mat * Vector(( 0.0,      1.0, 0.0)) )[:]
115     
116     def edges():
117         for i in range(tot):
118             yield (i*3 + 0, i*3 + 1)
119             yield (i*3 + 1, i*3 + 2)
120             yield (i*3 + 2, i*3 + 0)
121     
122     def faces():
123         for i in range(tot):
124             yield (i*3 + 0, i*3 + 1, i*3 + 2)
125     
126     mesh = bpy.data.meshes.new(name)
127     # XXX edges somehow are broken, but can be calculated automatically
128     #mesh.from_pydata([v for v in verts()], [e for e in edges()], [f for f in faces()])
129     mesh.from_pydata([v for v in verts()], [], [f for f in faces()])
130     mesh.update()
131     return mesh