diff options
-rw-r--r-- | source/blender/blenlib/BLI_arithb.h | 10 | ||||
-rw-r--r-- | source/blender/blenlib/intern/arithb.c | 73 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Mesh.c | 176 | ||||
-rw-r--r-- | source/blender/python/api2_2x/doc/Mesh.py | 38 | ||||
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 71 |
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) { |