Package pyffi :: Package utils :: Module tangentspace
[hide private]
[frames] | no frames]

Source Code for Module pyffi.utils.tangentspace

  1  """A module for tangent space calculation.""" 
  2   
  3  # ***** BEGIN LICENSE BLOCK ***** 
  4  # 
  5  # Copyright (c) 2007-2011, Python File Format Interface 
  6  # All rights reserved. 
  7  # 
  8  # Redistribution and use in source and binary forms, with or without 
  9  # modification, are permitted provided that the following conditions 
 10  # are met: 
 11  # 
 12  #    * Redistributions of source code must retain the above copyright 
 13  #      notice, this list of conditions and the following disclaimer. 
 14  # 
 15  #    * Redistributions in binary form must reproduce the above 
 16  #      copyright notice, this list of conditions and the following 
 17  #      disclaimer in the documentation and/or other materials provided 
 18  #      with the distribution. 
 19  # 
 20  #    * Neither the name of the Python File Format Interface 
 21  #      project nor the names of its contributors may be used to endorse 
 22  #      or promote products derived from this software without specific 
 23  #      prior written permission. 
 24  # 
 25  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 26  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 27  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 28  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 29  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 30  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 31  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 32  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 33  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 34  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 35  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 36  # POSSIBILITY OF SUCH DAMAGE. 
 37  # 
 38  # ***** END LICENSE BLOCK ***** 
 39   
 40  from pyffi.utils.mathutils import * 
 41   
42 -def getTangentSpace(vertices = None, normals = None, uvs = None, 43 triangles = None, orientation = False, 44 orthogonal = True):
45 """Calculate tangent space data. 46 47 >>> vertices = [(0,0,0), (0,1,0), (1,0,0)] 48 >>> normals = [(0,0,1), (0,0,1), (0,0,1)] 49 >>> uvs = [(0,0), (0,1), (1,0)] 50 >>> triangles = [(0,1,2)] 51 >>> getTangentSpace(vertices = vertices, normals = normals, uvs = uvs, triangles = triangles) 52 ([(0.0, 1.0, 0.0), (0.0, 1.0, 0.0), (0.0, 1.0, 0.0)], [(1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 0.0, 0.0)]) 53 54 :param vertices: A list of vertices (triples of floats/ints). 55 :param normals: A list of normals (triples of floats/ints). 56 :param uvs: A list of uvs (pairs of floats/ints). 57 :param triangles: A list of triangle indices (triples of ints). 58 :param orientation: Set to ``True`` to return orientation (this is used by 59 for instance Crysis). 60 :return: Two lists of vectors, tangents and binormals. If C{orientation} 61 is ``True``, then returns an extra list with orientations (containing 62 floats which describe the total signed surface of all faces sharing 63 the particular vertex). 64 """ 65 66 # validate input 67 if len(vertices) != len(normals) or len(vertices) != len(uvs): 68 raise ValueError( 69 "lists of vertices, normals, and uvs must have the same length") 70 71 bin = [(0,0,0) for i in xrange(len(vertices)) ] 72 tan = [(0,0,0) for i in xrange(len(vertices)) ] 73 orientations = [0 for i in xrange(len(vertices))] 74 75 # calculate tangents and binormals from vertex and texture coordinates 76 for t1, t2, t3 in triangles: 77 # skip degenerate triangles 78 if t1 == t2 or t2 == t3 or t3 == t1: 79 continue 80 81 # get vertices, uvs, and directions of the triangle 82 v1 = vertices[t1] 83 v2 = vertices[t2] 84 v3 = vertices[t3] 85 w1 = uvs[t1] 86 w2 = uvs[t2] 87 w3 = uvs[t3] 88 v2v1 = vecSub(v2, v1) 89 v3v1 = vecSub(v3, v1) 90 w2w1 = vecSub(w2, w1) 91 w3w1 = vecSub(w3, w1) 92 93 # surface of triangle in texture space 94 r = w2w1[0] * w3w1[1] - w3w1[0] * w2w1[1] 95 96 # sign of surface 97 r_sign = (1 if r >= 0 else -1) 98 99 # contribution of this triangle to tangents and binormals 100 sdir = ( 101 r_sign * (w3w1[1] * v2v1[0] - w2w1[1] * v3v1[0]), 102 r_sign * (w3w1[1] * v2v1[1] - w2w1[1] * v3v1[1]), 103 r_sign * (w3w1[1] * v2v1[2] - w2w1[1] * v3v1[2])) 104 try: 105 sdir = vecNormalized(sdir) 106 except ZeroDivisionError: # catches zero vector 107 continue # skip triangle 108 except ValueError: # catches invalid data 109 continue # skip triangle 110 111 tdir = ( 112 r_sign * (w2w1[0] * v3v1[0] - w3w1[0] * v2v1[0]), 113 r_sign * (w2w1[0] * v3v1[1] - w3w1[0] * v2v1[1]), 114 r_sign * (w2w1[0] * v3v1[2] - w3w1[0] * v2v1[2])) 115 try: 116 tdir = vecNormalized(tdir) 117 except ZeroDivisionError: # catches zero vector 118 continue # skip triangle 119 except ValueError: # catches invalid data 120 continue # skip triangle 121 122 # vector combination algorithm could possibly be improved 123 for i in (t1, t2, t3): 124 tan[i] = vecAdd(tan[i], tdir) 125 bin[i] = vecAdd(bin[i], sdir) 126 orientations[i] += r 127 128 # convert into orthogonal space 129 xvec = (1, 0, 0) 130 yvec = (0, 1, 0) 131 for i, norm in enumerate(normals): 132 if abs(1-vecNorm(norm)) > 0.01: 133 raise ValueError( 134 "tangentspace: unnormalized normal in list of normals (%s, norm is %f)" % (norm, vecNorm(norm))) 135 try: 136 # turn norm, bin, tan into a base via Gram-Schmidt 137 bin[i] = vecSub(bin[i], 138 vecscalarMul( 139 norm, 140 vecDotProduct(norm, bin[i]))) 141 bin[i] = vecNormalized(bin[i]) 142 tan[i] = vecSub(tan[i], 143 vecscalarMul( 144 norm, 145 vecDotProduct(norm, tan[i]))) 146 tan[i] = vecSub(tan[i], 147 vecscalarMul( 148 bin[i], 149 vecDotProduct(norm, bin[i]))) 150 tan[i] = vecNormalized(tan[i]) 151 except ZeroDivisionError: 152 # insuffient data to set tangent space for this vertex 153 # in that case pick a space 154 bin[i] = vecCrossProduct(xvec, norm) 155 try: 156 bin[i] = vecNormalized(bin[i]) 157 except ZeroDivisionError: 158 bin[i] = vecCrossProduct(yvec, norm) 159 bin[i] = vecNormalized(bin[i]) 160 tan[i] = vecCrossProduct(norm, bin[i]) 161 162 # return result 163 if orientation: 164 return tan, bin, orientations 165 else: 166 return tan, bin
167 168 if __name__ == "__main__": 169 import doctest 170 doctest.testmod() 171