[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [14113] trunk/blender/source/blender: added mesh.getTangents() to the python api

Campbell Barton ideasman42 at gmail.com
Fri Mar 14 19:54:03 CET 2008


Revision: 14113
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=14113
Author:   campbellbarton
Date:     2008-03-14 19:53:51 +0100 (Fri, 14 Mar 2008)

Log Message:
-----------
added mesh.getTangents() to the python api

Modified Paths:
--------------
    trunk/blender/source/blender/blenlib/BLI_arithb.h
    trunk/blender/source/blender/blenlib/intern/arithb.c
    trunk/blender/source/blender/python/api2_2x/Mesh.c
    trunk/blender/source/blender/python/api2_2x/doc/Mesh.py
    trunk/blender/source/blender/render/intern/source/convertblender.c

Modified: trunk/blender/source/blender/blenlib/BLI_arithb.h
===================================================================
--- trunk/blender/source/blender/blenlib/BLI_arithb.h	2008-03-14 18:21:06 UTC (rev 14112)
+++ trunk/blender/source/blender/blenlib/BLI_arithb.h	2008-03-14 18:53:51 UTC (rev 14113)
@@ -400,6 +400,16 @@
 void DQuatMulVecfl(DualQuat *dq, float *co, float mat[][3]);
 void DQuatCpyDQuat(DualQuat *dq1, DualQuat *dq2);
 			  
+/* Tangent stuff */
+typedef struct VertexTangent {
+	float tang[3], uv[2];
+	struct VertexTangent *next;
+} VertexTangent;
+
+void sum_or_add_vertex_tangent(void *arena, VertexTangent **vtang, float *tang, float *uv);
+float *find_vertex_tangent(VertexTangent *vtang, float *uv);
+void tangent_from_uv(float *uv1, float *uv2, float *uv3, float *co1, float *co2, float *co3, float *n, float *tang);
+
 #ifdef __cplusplus
 }
 #endif

Modified: trunk/blender/source/blender/blenlib/intern/arithb.c
===================================================================
--- trunk/blender/source/blender/blenlib/intern/arithb.c	2008-03-14 18:21:06 UTC (rev 14112)
+++ trunk/blender/source/blender/blenlib/intern/arithb.c	2008-03-14 18:53:51 UTC (rev 14113)
@@ -57,6 +57,7 @@
 
 #include <stdio.h>
 #include "BLI_arithb.h"
+#include "BLI_memarena.h"
 
 /* A few small defines. Keep'em local! */
 #define SMALL_NUMBER	1.e-8
@@ -4268,3 +4269,75 @@
 	mat[3][1] = loc[1];
 	mat[3][2] = loc[2];
 }
+
+/* Tangents */
+
+/* For normal map tangents we need to detect uv boundaries, and only average
+ * tangents in case the uvs are connected. Alternative would be to store 1 
+ * tangent per face rather than 4 per face vertex, but that's not compatible
+ * with games */
+
+
+/* from BKE_mesh.h */
+#define STD_UV_CONNECT_LIMIT	0.0001f
+
+void sum_or_add_vertex_tangent(void *arena, VertexTangent **vtang, float *tang, float *uv)
+{
+	VertexTangent *vt;
+
+	/* find a tangent with connected uvs */
+	for(vt= *vtang; vt; vt=vt->next) {
+		if(fabs(uv[0]-vt->uv[0]) < STD_UV_CONNECT_LIMIT && fabs(uv[1]-vt->uv[1]) < STD_UV_CONNECT_LIMIT) {
+			VecAddf(vt->tang, vt->tang, tang);
+			return;
+		}
+	}
+
+	/* if not found, append a new one */
+	vt= BLI_memarena_alloc((MemArena *)arena, sizeof(VertexTangent));
+	VecCopyf(vt->tang, tang);
+	vt->uv[0]= uv[0];
+	vt->uv[1]= uv[1];
+
+	if(*vtang)
+		vt->next= *vtang;
+	*vtang= vt;
+}
+
+float *find_vertex_tangent(VertexTangent *vtang, float *uv)
+{
+	VertexTangent *vt;
+	static float nulltang[3] = {0.0f, 0.0f, 0.0f};
+
+	for(vt= vtang; vt; vt=vt->next)
+		if(fabs(uv[0]-vt->uv[0]) < STD_UV_CONNECT_LIMIT && fabs(uv[1]-vt->uv[1]) < STD_UV_CONNECT_LIMIT)
+			return vt->tang;
+
+	return nulltang;	/* shouldn't happen, except for nan or so */
+}
+
+void tangent_from_uv(float *uv1, float *uv2, float *uv3, float *co1, float *co2, float *co3, float *n, float *tang)
+{
+	float tangv[3], ct[3], e1[3], e2[3], s1, t1, s2, t2, det;
+
+	s1= uv2[0] - uv1[0];
+	s2= uv3[0] - uv1[0];
+	t1= uv2[1] - uv1[1];
+	t2= uv3[1] - uv1[1];
+	det= 1.0f / (s1 * t2 - s2 * t1);
+	
+	/* normals in render are inversed... */
+	VecSubf(e1, co1, co2);
+	VecSubf(e2, co1, co3);
+	tang[0] = (t2*e1[0] - t1*e2[0])*det;
+	tang[1] = (t2*e1[1] - t1*e2[1])*det;
+	tang[2] = (t2*e1[2] - t1*e2[2])*det;
+	tangv[0] = (s1*e2[0] - s2*e1[0])*det;
+	tangv[1] = (s1*e2[1] - s2*e1[1])*det;
+	tangv[2] = (s1*e2[2] - s2*e1[2])*det;
+	Crossf(ct, tang, tangv);
+
+	/* check flip */
+	if ((ct[0]*n[0] + ct[1]*n[1] + ct[2]*n[2]) < 0.0f)
+		VecMulf(tang, -1.0f);
+}

Modified: trunk/blender/source/blender/python/api2_2x/Mesh.c
===================================================================
--- trunk/blender/source/blender/python/api2_2x/Mesh.c	2008-03-14 18:21:06 UTC (rev 14112)
+++ trunk/blender/source/blender/python/api2_2x/Mesh.c	2008-03-14 18:53:51 UTC (rev 14113)
@@ -75,6 +75,7 @@
 
 #include "BLI_arithb.h"
 #include "BLI_blenlib.h"
+#include "BLI_memarena.h"
 
 #include "blendef.h"
 #include "mydevice.h"
@@ -7494,6 +7495,177 @@
 }
 
 
+/* This is a bit nasty, Blenders tangents are computed for rendering, and this isnt compatible with a normal Mesh
+ * so we have to rewrite parts of it here, make sure these stay in sync */
+
+static PyObject *Mesh_getTangents( BPy_Mesh * self )
+{
+	/* python stuff */
+	PyObject *py_tanlist;
+	PyObject *py_tuple;
+	
+	
+	PyObject *py_vector;
+#if 0	/* BI-TANGENT */
+	PyObject *py_bivector;
+	PyObject *py_pair;
+	
+	float no[3];
+#endif
+	/* mesh vars */
+	Mesh *mesh = self->mesh;
+	MTFace *tf = mesh->mtface;
+	MFace *mf = mesh->mface;
+	MVert *v1, *v2, *v3, *v4;
+	int mf_vi[4];
+	
+	/* See convertblender.c */
+	float *uv1, *uv2, *uv3, *uv4;
+	float fno[3];
+	float tang[3];
+	float uv[4][2];
+	float *vtang;
+	
+	float (*orco)[3] = NULL;
+	
+	MemArena *arena= NULL;
+	VertexTangent **vtangents= NULL;
+	int i, j, len;
+	
+	
+	if(!mesh->mtface) {
+		if (!self->object)
+			return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cannot get tangents when there are not UV's, or the mesh has no link to an object");
+		
+		orco = (float(*)[3])get_mesh_orco_verts(self->object);
+		
+		if (!orco)
+			return EXPP_ReturnPyObjError( PyExc_RuntimeError, "cannot get orco's for this objects tangents");
+	}
+	
+	/* vertex normals */
+	arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
+	BLI_memarena_use_calloc(arena);
+	vtangents= MEM_callocN(sizeof(VertexTangent*)*mesh->totvert, "VertexTangent");
+	
+	for( i = 0, tf = mesh->mtface, mf = mesh->mface; i < mesh->totface; mf++, tf++, i++ ) {
+		v1 = &mesh->mvert[mf->v1];
+		v2 = &mesh->mvert[mf->v2];
+		v3 = &mesh->mvert[mf->v3];
+		if (mf->v4) {
+			v4 = &mesh->mvert[mf->v4];
+			
+			CalcNormFloat4( v1->co, v2->co, v3->co, v4->co, fno );
+		} else {
+			CalcNormFloat( v1->co, v2->co, v3->co, fno );
+		}
+		
+		if(mesh->mtface) {
+			uv1= tf->uv[0];
+			uv2= tf->uv[1];
+			uv3= tf->uv[2];
+			uv4= tf->uv[3];
+		} else {
+			uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3];
+			spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]);
+			spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]);
+			spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]);
+			if(v4)
+				spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]);
+		}
+		
+		tangent_from_uv(uv1, uv2, uv3, v1->co, v2->co, v3->co, fno, tang);
+		sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1);
+		sum_or_add_vertex_tangent(arena, &vtangents[mf->v2], tang, uv2);
+		sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3);
+		
+		if (mf->v4) {
+			v4 = &mesh->mvert[mf->v4];
+			
+			tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, fno, tang);
+			sum_or_add_vertex_tangent(arena, &vtangents[mf->v1], tang, uv1);
+			sum_or_add_vertex_tangent(arena, &vtangents[mf->v3], tang, uv3);
+			sum_or_add_vertex_tangent(arena, &vtangents[mf->v4], tang, uv4);
+		}
+	}
+	
+	
+	py_tanlist = PyList_New(mesh->totface);
+	
+	for( i = 0, tf = mesh->mtface, mf = mesh->mface; i < mesh->totface; mf++, tf++, i++ ) {
+		 
+		len = mf->v4 ? 4 : 3; 
+		
+		if(mesh->mtface) {
+			uv1= tf->uv[0];
+			uv2= tf->uv[1];
+			uv3= tf->uv[2];
+			uv4= tf->uv[3];
+		} else {
+			uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3];
+			spheremap(orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2], &uv[0][0], &uv[0][1]);
+			spheremap(orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2], &uv[1][0], &uv[1][1]);
+			spheremap(orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2], &uv[2][0], &uv[2][1]);
+			if(len==4)
+				spheremap(orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2], &uv[3][0], &uv[3][1]);
+		}
+		
+		mf_vi[0] = mf->v1;
+		mf_vi[1] = mf->v2;
+		mf_vi[2] = mf->v3;
+		mf_vi[3] = mf->v4;
+		
+#if 0	/* BI-TANGENT */
+		/* now calculate the bitangent */
+		if (mf->flag & ME_SMOOTH) {
+			no[0] = (float)(mesh->mvert[mf_vi[j]]->no[0] / 32767.0);
+			no[1] = (float)(mesh->mvert[mf_vi[j]]->no[1] / 32767.0);
+			no[2] = (float)(mesh->mvert[mf_vi[j]]->no[2] / 32767.0);
+		} else {
+			/* calc face normal */
+			if (len==4)		CalcNormFloat4( mesh->mvert[0]->co, mesh->mvert[1]->co, mesh->mvert[2]->co, mesh->mvert[3]->co, no );
+			else			CalcNormFloat4( mesh->mvert[0]->co, mesh->mvert[1]->co, mesh->mvert[2]->co, no );
+		}
+#endif
+		
+		py_tuple = PyTuple_New( len );
+		
+		for (j=0; j<len; j++) {
+			vtang= find_vertex_tangent(vtangents[mf_vi[j]], mesh->mtface ? tf->uv[j] : uv[j]);	/* mf_vi[j] == mf->v1,   uv[j] == tf->uv[0] */
+			
+			py_vector = newVectorObject( vtang, 3, Py_NEW );
+			Normalize(((VectorObject *)py_vector)->vec);
+			
+#if 0		/* BI-TANGENT */
+			py_pair = PyTuple_New( 2 );
+			PyTuple_SetItem( py_pair, 0, py_vector );
+			PyTuple_SetItem( py_pair, 1, py_bivector );
+			
+			/* qdn: tangent space */
+			/* copied from texture.c */
+			float B[3], tv[3];
+			Crossf(B, shi->vn, shi->nmaptang);	/* bitangent */
+			/* transform norvec from tangent space to object surface in camera space */
+			tv[0] = texres.nor[0]*shi->nmaptang[0] + texres.nor[1]*B[0] + texres.nor[2]*shi->vn[0];
+			tv[1] = texres.nor[0]*shi->nmaptang[1] + texres.nor[1]*B[1] + texres.nor[2]*shi->vn[1];
+			tv[2] = texres.nor[0]*shi->nmaptang[2] + texres.nor[1]*B[2] + texres.nor[2]*shi->vn[2];
+			shi->vn[0]= facm*shi->vn[0] + fact*tv[0];
+			shi->vn[1]= facm*shi->vn[1] + fact*tv[1];
+			shi->vn[2]= facm*shi->vn[2] + fact*tv[2];				
+			PyTuple_SetItem( py_tuple, j, py_pair );
+#else
+			PyTuple_SetItem( py_tuple, j, py_vector );
+#endif
+		}
+
+		PyList_SetItem( py_tanlist, i, py_tuple );
+	}
+	if (orco)
+		MEM_freeN( orco );
+	
+	return py_tanlist;
+}
+
 /*
  * "__copy__" return a copy of the mesh
  */
@@ -7571,7 +7743,9 @@
 		"Recalculates inside or outside normals (experimental)"},
 	{"pointInside", (PyCFunction)Mesh_pointInside, METH_VARARGS|METH_KEYWORDS,
 		"Recalculates inside or outside normals (experimental)"},
-	
+	{"getTangents", (PyCFunction)Mesh_getTangents, METH_VARARGS|METH_KEYWORDS,
+		"Return a list of face tangents"},
+		
 	/* mesh custom data layers */
 	{"addUVLayer", (PyCFunction)Mesh_addUVLayer, METH_VARARGS,
 		"adds a UV layer to this mesh"},

Modified: trunk/blender/source/blender/python/api2_2x/doc/Mesh.py
===================================================================
--- trunk/blender/source/blender/python/api2_2x/doc/Mesh.py	2008-03-14 18:21:06 UTC (rev 14112)
+++ trunk/blender/source/blender/python/api2_2x/doc/Mesh.py	2008-03-14 18:53:51 UTC (rev 14113)
@@ -844,7 +844,45 @@
 		@note: Only returns a valid result for mesh data that has no holes.

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list