Barycentric weight calculation for face interpolation.
authorLukas Tönne <lukas.toenne@gmail.com>
Wed, 10 Dec 2014 09:19:10 +0000 (10:19 +0100)
committerLukas Tönne <lukas.toenne@gmail.com>
Wed, 10 Dec 2014 09:19:10 +0000 (10:19 +0100)
Ported over from C code in BLI_math_geom.

object_physics_meadow/blob.py
object_physics_meadow/duplimesh.py

index 463ed63..e4d7605 100644 (file)
@@ -28,7 +28,7 @@ import random
 
 from object_physics_meadow import settings as _settings
 from object_physics_meadow import duplimesh
-from object_physics_meadow.duplimesh import project_on_ground
+from object_physics_meadow.duplimesh import project_on_ground, interp_weights_face
 from object_physics_meadow.util import *
 
 _blob_object_name = "__MeadowBlob__"
@@ -213,12 +213,13 @@ def assign_sample_patches(groundob, blob):
     vgroup_samples[""] = [] # samples for unassigned patches
     for loc, nor, face_index in blob.samples:
         face = faces[face_index]
-        # XXX this throws an exception if testing for vertex outside the group
-        # but there seems to be no nice way to test beforehand ...
-        #weights = [ [vg.weight(i) for i in face.vertices] for vg in vgroups ]
+        verts = [vertices[i] for i in face.vertices]
+        assert(len(verts) in {3, 4})
+        
+        fweight, findex = interp_weights_face(tuple(v.co for v in verts[0:4]), loc)
+        
         weights = [ 0.0 for vg in vgroups ]
-        for i in face.vertices:
-            v = vertices[i]
+        for v in verts:
             for vg in v.groups:
                 weights[vg.group] += 0.25 * vg.weight # TODO
         
index 499e1c7..c6e4f37 100644 (file)
 
 # <pep8 compliant>
 
-import bpy
+import bpy, sys
+from math import *
 from mathutils import *
 
+def tri_signed_area(v1, v2, v3, i, j):
+    return 0.5 * ((v1[i] - v2[i]) * (v2[j] - v3[j]) + (v1[j] - v2[j]) * (v3[i] - v2[i]))
+
+# get the 2 dominant axis values, 0==X, 1==Y, 2==Z
+def axis_dominant(axis):
+    xn = fabs(axis[0])
+    yn = fabs(axis[1])
+    zn = fabs(axis[2])
+    if zn >= xn and zn >= yn:
+        return 0, 1
+    elif yn >= xn and yn >= zn:
+        return 0, 2
+    else:
+        return 1, 2
+
+def barycentric_weights(v1, v2, v3, co, n):
+    i, j = axis_dominant(n)
+    
+    w = (tri_signed_area(v2, v3, co, i, j),
+         tri_signed_area(v3, v1, co, i, j),
+         tri_signed_area(v1, v2, co, i, j))
+    wtot = w[0] + w[1] + w[2]
+    
+    if fabs(wtot) > sys.float_info.epsilon:
+        inv_w = 1.0 / wtot
+        return True, tuple(x*inv_w for x in w)
+    else:
+        return False, tuple(1.0/3.0 for x in w)
+
+def interp_weights_face(verts, co):
+    w = (0.0, 0.0, 0.0, 0.0)
+    
+    # OpenGL seems to split this way, so we do too
+    if len(verts) > 3:
+        n = (verts[0] - verts[2]).cross(verts[1] - verts[3])
+        
+        ok, w3 = barycentric_weights(verts[0], verts[1], verts[3], co, n)
+        w = (w3[0], w3[1], 0.0, w3[2])
+        idx = (0, 1, 3)
+        
+        if not ok or w[0] < 0.0:
+            # if w[1] is negative, co is on the other side of the v1-v3 edge,
+            # so we interpolate using the other triangle
+            ok, w3 = barycentric_weights(verts[1], verts[2], verts[3], co, n)
+            w = (0.0, w3[0], w3[1], w3[2])
+            idx = (1, 2, 3)
+        
+    else:
+        n = (verts[0] - verts[2]).cross(verts[1] - verts[2])
+        
+        ok, w3 = barycentric_weights(verts[0], verts[1], verts[2], co, n)
+        w = (w3[0], w3[1], w3[2], 0.0)
+        idx = (0, 1, 2)
+    
+    return w, idx
+
+
 def project_on_ground(groundob, co):
     groundmat4 = groundob.matrix_world
     groundmat3 = groundmat4.to_3x3()