Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2008-03-14 21:53:51 +0300
committerCampbell Barton <ideasman42@gmail.com>2008-03-14 21:53:51 +0300
commit8e126216b0f070660bd0c4eda688bc3383c11ac1 (patch)
treee19eeb6dfda9cdffa31973b865d9e53d94afed74
parent7f556ba4ef840bb2883fba5213ce856301e4b5aa (diff)
added mesh.getTangents() to the python api
-rw-r--r--source/blender/blenlib/BLI_arithb.h10
-rw-r--r--source/blender/blenlib/intern/arithb.c73
-rw-r--r--source/blender/python/api2_2x/Mesh.c176
-rw-r--r--source/blender/python/api2_2x/doc/Mesh.py38
-rw-r--r--source/blender/render/intern/source/convertblender.c71
5 files changed, 296 insertions, 72 deletions
diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h
index 99e30bc91c0..3055f62ae3d 100644
--- a/source/blender/blenlib/BLI_arithb.h
+++ b/source/blender/blenlib/BLI_arithb.h
@@ -400,6 +400,16 @@ void DQuatNormalize(DualQuat *dq, float totweight);
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
diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c
index cd7de83b9c0..3514e695f59 100644
--- a/source/blender/blenlib/intern/arithb.c
+++ b/source/blender/blenlib/intern/arithb.c
@@ -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 @@ void LocQuatSizeToMat4(float mat[][4], float loc[3], float quat[4], float size[3
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);
+}
diff --git a/source/blender/python/api2_2x/Mesh.c b/source/blender/python/api2_2x/Mesh.c
index 803c0b96ef3..e070793f6cf 100644
--- a/source/blender/python/api2_2x/Mesh.c
+++ b/source/blender/python/api2_2x/Mesh.c
@@ -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 @@ static PyObject *Mesh_pointInside( BPy_Mesh * self, PyObject * args, PyObject *k
}
+/* 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 @@ static struct PyMethodDef BPy_Mesh_methods[] = {
"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"},
diff --git a/source/blender/python/api2_2x/doc/Mesh.py b/source/blender/python/api2_2x/doc/Mesh.py
index 0690b94712b..8b49ce29d54 100644
--- a/source/blender/python/api2_2x/doc/Mesh.py
+++ b/source/blender/python/api2_2x/doc/Mesh.py
@@ -844,6 +844,44 @@ class Mesh:
@note: Only returns a valid result for mesh data that has no holes.
@note: Bubbles in the mesh work as expect.
"""
+ def getTangents():
+ """
+ Calculates tangents for this mesh, returning a list of tuples,
+ each with 3 or 4 tangent vectors, these are alligned with the meshes faces.
+
+ Example::
+ # Display the tangents as edges over a the active mesh object
+ from Blender import *
+ sce = Scene.GetCurrent()
+ ob = sce.objects.active
+
+ me = ob.getData(mesh=1)
+ ts = me.getTangents()
+ me_disp = Mesh.New()
+
+ verts = []
+ edges = []
+ for i, f in enumerate(me.faces):
+ ft = ts[i]
+ for j, v in enumerate(f):
+ tan = ft[j]
+ print tan
+ co = v.co
+
+ verts.append(co)
+ verts.append(co+tan)
+
+ i = len(verts)
+ edges.append((i-1, i-2))
+
+ me_disp.verts.extend( verts )
+ me_disp.edges.extend( edges )
+
+ sce.objects.new( me_disp )
+
+ @note: The tangents are computed using the active UV layer, if there are no UV layers, orco coords are used.
+ """
+
def transform(matrix, recalc_normals = False, selected_only=False):
"""
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index aaa9cbbb104..42191ff35ad 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -451,77 +451,6 @@ static void calc_edge_stress(Render *re, ObjectRen *obr, Mesh *me)
MEM_freeN(accum);
}
-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);
-}
-
-/* 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 */
-
-typedef struct VertexTangent {
- float tang[3], uv[2];
- struct VertexTangent *next;
-} VertexTangent;
-
-static void sum_or_add_vertex_tangent(MemArena *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) {
- VECADD(vt->tang, vt->tang, tang);
- return;
- }
- }
-
- /* if not found, append a new one */
- vt= BLI_memarena_alloc(arena, sizeof(VertexTangent));
- VECCOPY(vt->tang, tang);
- vt->uv[0]= uv[0];
- vt->uv[1]= uv[1];
-
- if(*vtang)
- vt->next= *vtang;
- *vtang= vt;
-}
-
-static 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 */
-}
-
/* gets tangent from tface or orco */
static void calc_tangent_vector(ObjectRen *obr, VertexTangent **vtangents, MemArena *arena, VlakRen *vlr, int do_nmap_tangent, int do_tangent)
{