This commit will switch blender to use tangent space generated within

the two files mikktspace.h and mikktspace.c. These are standalone files
which can be redistributed into any other application and regenerate the
same tangent spaces. The implementation is independent of the ordering
of faces and the vertex ordering of faces.
This commit is contained in:
M.G. Kishalmi 2011-02-14 18:18:46 +00:00
parent 8b7482892b
commit 20553d4064
20 changed files with 3297 additions and 983 deletions

@ -533,7 +533,7 @@ typedef struct DMVertexAttribs {
} mcol[MAX_MCOL];
struct {
float (*array)[3];
float (*array)[4];
int emOffset, glIndex;
} tang;

@ -106,6 +106,7 @@ set(SRC
intern/mball.c
intern/mesh.c
intern/mesh_validate.c
intern/mikktspace.c
intern/modifier.c
intern/multires.c
intern/nla.c
@ -222,6 +223,7 @@ set(SRC
BKE_writeffmpeg.h
BKE_writeframeserver.h
depsgraph_private.h
mikktspace.h
intern/CCGSubSurf.h
intern/bmesh_private.h
nla_private.h

@ -948,7 +948,7 @@ static void emDM_drawMappedFacesGLSL(DerivedMesh *dm,
} \
if(attribs.tottang) { \
float *tang = attribs.tang.array[i*4 + vert]; \
glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \
glVertexAttrib4fvARB(attribs.tang.glIndex, tang); \
} \
}
@ -2481,6 +2481,111 @@ float *mesh_get_mapped_verts_nors(Scene *scene, Object *ob)
/* ******************* GLSL ******************** */
typedef struct
{
float * precomputedFaceNormals;
MTFace * mtface; // texture coordinates
MFace * mface; // indices
MVert * mvert; // vertices & normals
float (*orco)[3];
float (*tangent)[4]; // destination
int numFaces;
} SGLSLMeshToTangent;
// interface
#include "mikktspace.h"
static int GetNumFaces(const SMikkTSpaceContext * pContext)
{
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
return pMesh->numFaces;
}
static int GetNumVertsOfFace(const SMikkTSpaceContext * pContext, const int face_num)
{
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
return pMesh->mface[face_num].v4!=0 ? 4 : 3;
}
static void GetPosition(const SMikkTSpaceContext * pContext, float fPos[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
unsigned int indices[] = { pMesh->mface[face_num].v1, pMesh->mface[face_num].v2,
pMesh->mface[face_num].v3, pMesh->mface[face_num].v4 };
VECCOPY(fPos, pMesh->mvert[indices[vert_index]].co);
}
static void GetTextureCoordinate(const SMikkTSpaceContext * pContext, float fUV[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
if(pMesh->mtface!=NULL)
{
float * uv = pMesh->mtface[face_num].uv[vert_index];
fUV[0]=uv[0]; fUV[1]=uv[1];
}
else
{
unsigned int indices[] = { pMesh->mface[face_num].v1, pMesh->mface[face_num].v2,
pMesh->mface[face_num].v3, pMesh->mface[face_num].v4 };
map_to_sphere( &fUV[0], &fUV[1],pMesh->orco[indices[vert_index]][0], pMesh->orco[indices[vert_index]][1], pMesh->orco[indices[vert_index]][2]);
}
}
static void GetNormal(const SMikkTSpaceContext * pContext, float fNorm[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
unsigned int indices[] = { pMesh->mface[face_num].v1, pMesh->mface[face_num].v2,
pMesh->mface[face_num].v3, pMesh->mface[face_num].v4 };
const int smoothnormal = (pMesh->mface[face_num].flag & ME_SMOOTH);
if(!smoothnormal) // flat
{
if(pMesh->precomputedFaceNormals)
{
VECCOPY(fNorm, &pMesh->precomputedFaceNormals[3*face_num]);
}
else
{
float nor[3];
float * p0, * p1, * p2;
const int iGetNrVerts = pMesh->mface[face_num].v4!=0 ? 4 : 3;
p0 = pMesh->mvert[indices[0]].co; p1 = pMesh->mvert[indices[1]].co; p2 = pMesh->mvert[indices[2]].co;
if(iGetNrVerts==4)
{
float * p3 = pMesh->mvert[indices[3]].co;
normal_quad_v3( nor, p0, p1, p2, p3);
}
else {
normal_tri_v3(nor, p0, p1, p2);
}
VECCOPY(fNorm, nor);
}
}
else
{
int i=0;
short * no = pMesh->mvert[indices[vert_index]].no;
for(i=0; i<3; i++)
fNorm[i]=no[i]/32767.0f;
normalize_v3(fNorm);
}
}
static void SetTSpace(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int face_num, const int iVert)
{
//assert(vert_index>=0 && vert_index<4);
SGLSLMeshToTangent * pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
float * pRes = pMesh->tangent[4*face_num+iVert];
VECCOPY(pRes, fvTangent);
pRes[3]=fSign;
}
void DM_add_tangent_layer(DerivedMesh *dm)
{
/* mesh vars */
@ -2489,14 +2594,17 @@ void DM_add_tangent_layer(DerivedMesh *dm)
MVert *mvert, *v1, *v2, *v3, *v4;
MemArena *arena= NULL;
VertexTangent **vtangents= NULL;
float (*orco)[3]= NULL, (*tangent)[3];
float (*orco)[3]= NULL, (*tangent)[4];
float *uv1, *uv2, *uv3, *uv4, *vtang;
float fno[3], tang[3], uv[4][2];
int i, j, len, mf_vi[4], totvert, totface;
int i, j, len, mf_vi[4], totvert, totface, iCalcNewMethod;
float *nors;
if(CustomData_get_layer_index(&dm->faceData, CD_TANGENT) != -1)
return;
nors = dm->getFaceDataArray(dm, CD_NORMAL);
/* check we have all the needed layers */
totvert= dm->getNumVerts(dm);
totface= dm->getNumFaces(dm);
@ -2519,72 +2627,108 @@ void DM_add_tangent_layer(DerivedMesh *dm)
arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "tangent layer arena");
BLI_memarena_use_calloc(arena);
vtangents= MEM_callocN(sizeof(VertexTangent*)*totvert, "VertexTangent");
/* sum tangents at connected vertices */
for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++) {
v1= &mvert[mf->v1];
v2= &mvert[mf->v2];
v3= &mvert[mf->v3];
if (mf->v4) {
v4= &mvert[mf->v4];
normal_quad_v3( fno,v4->co, v3->co, v2->co, v1->co);
}
else {
v4= NULL;
normal_tri_v3( fno,v3->co, v2->co, v1->co);
}
if(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];
map_to_sphere( &uv[0][0], &uv[0][1],orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2]);
map_to_sphere( &uv[1][0], &uv[1][1],orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2]);
map_to_sphere( &uv[2][0], &uv[2][1],orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2]);
if(v4)
map_to_sphere( &uv[3][0], &uv[3][1],orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2]);
}
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= &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);
}
// new computation method
iCalcNewMethod = 1;
if(iCalcNewMethod!=0)
{
SGLSLMeshToTangent mesh2tangent;
SMikkTSpaceContext sContext;
SMikkTSpaceInterface sInterface;
memset(&mesh2tangent, 0, sizeof(SGLSLMeshToTangent));
memset(&sContext, 0, sizeof(SMikkTSpaceContext));
memset(&sInterface, 0, sizeof(SMikkTSpaceInterface));
mesh2tangent.precomputedFaceNormals = nors;
mesh2tangent.mtface = mtface;
mesh2tangent.mface = mface;
mesh2tangent.mvert = mvert;
mesh2tangent.orco = orco;
mesh2tangent.tangent = tangent;
mesh2tangent.numFaces = totface;
sContext.m_pUserData = &mesh2tangent;
sContext.m_pInterface = &sInterface;
sInterface.m_getNumFaces = GetNumFaces;
sInterface.m_getNumVerticesOfFace = GetNumVertsOfFace;
sInterface.m_getPosition = GetPosition;
sInterface.m_getTexCoord = GetTextureCoordinate;
sInterface.m_getNormal = GetNormal;
sInterface.m_setTSpaceBasic = SetTSpace;
// 0 if failed
iCalcNewMethod = genTangSpaceDefault(&sContext);
}
/* write tangent to layer */
for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++, tangent+=4) {
len= (mf->v4)? 4 : 3;
if(mtface == NULL) {
map_to_sphere( &uv[0][0], &uv[0][1],orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2]);
map_to_sphere( &uv[1][0], &uv[1][1],orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2]);
map_to_sphere( &uv[2][0], &uv[2][1],orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2]);
if(len==4)
map_to_sphere( &uv[3][0], &uv[3][1],orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2]);
if(!iCalcNewMethod)
{
/* sum tangents at connected vertices */
for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++) {
v1= &mvert[mf->v1];
v2= &mvert[mf->v2];
v3= &mvert[mf->v3];
if (mf->v4) {
v4= &mvert[mf->v4];
normal_quad_v3( fno,v4->co, v3->co, v2->co, v1->co);
}
else {
v4= NULL;
normal_tri_v3( fno,v3->co, v2->co, v1->co);
}
if(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];
map_to_sphere( &uv[0][0], &uv[0][1],orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2]);
map_to_sphere( &uv[1][0], &uv[1][1],orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2]);
map_to_sphere( &uv[2][0], &uv[2][1],orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2]);
if(v4)
map_to_sphere( &uv[3][0], &uv[3][1],orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2]);
}
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= &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);
}
}
/* write tangent to layer */
for(i=0, tf=mtface, mf=mface; i < totface; mf++, tf++, i++, tangent+=4) {
len= (mf->v4)? 4 : 3;
if(mtface == NULL) {
map_to_sphere( &uv[0][0], &uv[0][1],orco[mf->v1][0], orco[mf->v1][1], orco[mf->v1][2]);
map_to_sphere( &uv[1][0], &uv[1][1],orco[mf->v2][0], orco[mf->v2][1], orco[mf->v2][2]);
map_to_sphere( &uv[2][0], &uv[2][1],orco[mf->v3][0], orco[mf->v3][1], orco[mf->v3][2]);
if(len==4)
map_to_sphere( &uv[3][0], &uv[3][1],orco[mf->v4][0], orco[mf->v4][1], orco[mf->v4][2]);
}
mf_vi[0]= mf->v1;
mf_vi[1]= mf->v2;
mf_vi[2]= mf->v3;
mf_vi[3]= mf->v4;
mf_vi[0]= mf->v1;
mf_vi[1]= mf->v2;
mf_vi[2]= mf->v3;
mf_vi[3]= mf->v4;
for(j=0; j<len; j++) {
vtang= find_vertex_tangent(vtangents[mf_vi[j]], mtface ? tf->uv[j] : uv[j]);
normalize_v3_v3(tangent[j], vtang);
for(j=0; j<len; j++) {
vtang= find_vertex_tangent(vtangents[mf_vi[j]], mtface ? tf->uv[j] : uv[j]);
normalize_v3_v3(tangent[j], vtang);
((float *) tangent[j])[3]=1.0f;
}
}
}

@ -1066,7 +1066,7 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, vo
} \
if(attribs.tottang) { \
float *tang = attribs.tang.array[a*4 + vert]; \
glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \
glVertexAttrib4fvARB(attribs.tang.glIndex, tang); \
} \
if(smoothnormal) \
glNormal3sv(mvert[index].no); \
@ -1158,7 +1158,7 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, vo
}
if(attribs.tottang) {
datatypes[numdata].index = attribs.tang.glIndex;
datatypes[numdata].size = 3;
datatypes[numdata].size = 4;
datatypes[numdata].type = GL_FLOAT;
numdata++;
}
@ -1248,12 +1248,12 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, vo
}
if(attribs.tottang) {
float *tang = attribs.tang.array[a*4 + 0];
VECCOPY((float *)&varray[elementsize*curface*3+offset], tang);
QUATCOPY((float *)&varray[elementsize*curface*3+offset], tang);
tang = attribs.tang.array[a*4 + 1];
VECCOPY((float *)&varray[elementsize*curface*3+offset+elementsize], tang);
QUATCOPY((float *)&varray[elementsize*curface*3+offset+elementsize], tang);
tang = attribs.tang.array[a*4 + 2];
VECCOPY((float *)&varray[elementsize*curface*3+offset+elementsize*2], tang);
offset += sizeof(float)*3;
QUATCOPY((float *)&varray[elementsize*curface*3+offset+elementsize*2], tang);
offset += sizeof(float)*4;
}
}
curface++;
@ -1288,12 +1288,12 @@ static void cdDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, vo
}
if(attribs.tottang) {
float *tang = attribs.tang.array[a*4 + 2];
VECCOPY((float *)&varray[elementsize*curface*3+offset], tang);
QUATCOPY((float *)&varray[elementsize*curface*3+offset], tang);
tang = attribs.tang.array[a*4 + 3];
VECCOPY((float *)&varray[elementsize*curface*3+offset+elementsize], tang);
QUATCOPY((float *)&varray[elementsize*curface*3+offset+elementsize], tang);
tang = attribs.tang.array[a*4 + 0];
VECCOPY((float *)&varray[elementsize*curface*3+offset+elementsize*2], tang);
offset += sizeof(float)*3;
QUATCOPY((float *)&varray[elementsize*curface*3+offset+elementsize*2], tang);
offset += sizeof(float)*4;
}
}
curface++;

@ -829,7 +829,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(MTexPoly), "MTexPoly", 1, "Face Texture", NULL, NULL, NULL, NULL, NULL},
{sizeof(MLoopUV), "MLoopUV", 1, "UV coord", NULL, NULL, layerInterp_mloopuv, NULL, NULL},
{sizeof(MLoopCol), "MLoopCol", 1, "Col", NULL, NULL, layerInterp_mloopcol, NULL, layerDefault_mloopcol},
{sizeof(float)*3*4, "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
{sizeof(float)*4*4, "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
{sizeof(MDisps), "MDisps", 1, NULL, layerCopy_mdisps,
layerFree_mdisps, layerInterp_mdisps, layerSwap_mdisps, NULL, layerRead_mdisps, layerWrite_mdisps,
layerFilesize_mdisps, layerValidate_mdisps},

File diff suppressed because it is too large Load Diff

@ -1392,7 +1392,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, v
} \
if(attribs.tottang) { \
float *tang = attribs.tang.array[a*4 + vert]; \
glVertexAttrib3fvARB(attribs.tang.glIndex, tang); \
glVertexAttrib4fvARB(attribs.tang.glIndex, tang); \
} \
}

@ -0,0 +1,143 @@
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef __MIKKTSPACE_H__
#define __MIKKTSPACE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Author: Morten S. Mikkelsen
* Version: 1.0
*
* The files mikktspace.h and mikktspace.c are designed to be
* stand-alone files and it is important that they are kept this way.
* Not having dependencies on structures/classes/libraries specific
* to the program, in which they are used, allows them to be copied
* and used as is into any tool, program or plugin.
* The code is designed to consistently generate the same
* tangent spaces, for a given mesh, in any tool in which it is used.
* This is done by performing an internal welding step and subsequently an order-independent evaluation
* of tangent space for meshes consisting of triangles and quads.
* This means faces can be received in any order and the same is true for
* the order of vertices of each face. The generated result will not be affected
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
* primitives are present or not will not affect the generated results either.
* Once tangent space calculation is done the vertices of degenerate primitives will simply
* inherit tangent space from neighboring non degenerate primitives.
* The analysis behind this implementation can be found in my master's thesis
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
* by this implementation, the interpolated tangent space is still affected by which diagonal is
* chosen to split each quad. A sensible solution is to have your tools pipeline always
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
* If these have the same length then compare the diagonals defined by the texture coordinates.
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
* and also quad triangulator plugin.
*/
typedef int tbool;
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
typedef struct
{
// Returns the number of faces (triangles/quads) on the mesh to be processed.
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
// Returns the number of vertices on face number iFace
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
// either (or both) of the two setTSpace callbacks can be set.
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
// This function is used to return the tangent and fSign to the application.
// fvTangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
// This function is used to return tangent space results to the application.
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
// true magnitudes which can be used for relief mapping effects.
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
} SMikkTSpaceInterface;
struct SMikkTSpaceContext
{
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
};
// these are both thread safe!
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
// normal map sampler must use the exact inverse of the pixel shader transformation.
// The most efficient transformation we can possibly do in the pixel shader is
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
// pixel shader (fast transform out)
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// where vNt is the tangent space normal. The normal map sampler must likewise use the
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
// sampler does (exact inverse of pixel shader):
// float3 row0 = cross(vB, vN);
// float3 row1 = cross(vN, vT);
// float3 row2 = cross(vT, vB);
// float fSign = dot(vT, row0)<0 ? -1 : 1;
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
// where vNout is the sampled normal in some chosen 3D space.
//
// Should you choose to reconstruct the bitangent in the pixel shader instead
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
// However, this must be used both by the sampler and your tools/rendering pipeline.
#ifdef __cplusplus
}
#endif
#endif

@ -741,11 +741,12 @@ static char *code_generate_vertex(ListBase *nodes)
for (input=node->inputs.first; input; input=input->next)
if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
if(input->attribtype == CD_TANGENT) /* silly exception */
BLI_dynstr_printf(ds, "\tvar%d = gl_NormalMatrix * ", input->attribid);
{
BLI_dynstr_printf(ds, "\tvar%d.xyz = normalize((gl_ModelViewMatrix * vec4(att%d.xyz, 0)).xyz);\n", input->attribid, input->attribid);
BLI_dynstr_printf(ds, "\tvar%d.w = att%d.w;\n", input->attribid, input->attribid);
}
else
BLI_dynstr_printf(ds, "\tvar%d = ", input->attribid);
BLI_dynstr_printf(ds, "att%d;\n", input->attribid);
BLI_dynstr_printf(ds, "\tvar%d = att%d;\n", input->attribid, input->attribid);
}
BLI_dynstr_append(ds, "}\n\n");

@ -894,6 +894,7 @@ static void do_material_tex(GPUShadeInput *shi)
int tex_nr, rgbnor, talpha;
int init_done = 0, iBumpSpacePrev;
GPUNodeLink *vNorg, *vNacc, *fPrevMagnitude;
int iFirstTimeNMap=1;
GPU_link(mat, "set_value", GPU_uniform(&one), &stencil);
@ -902,7 +903,7 @@ static void do_material_tex(GPUShadeInput *shi)
GPU_link(mat, "texco_object", GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
GPU_builtin(GPU_INVERSE_OBJECT_MATRIX),
GPU_builtin(GPU_VIEW_POSITION), &texco_object);
GPU_link(mat, "texco_tangent", GPU_attribute(CD_TANGENT, ""), &texco_tangent);
//GPU_link(mat, "texco_tangent", GPU_attribute(CD_TANGENT, ""), &texco_tangent);
GPU_link(mat, "texco_global", GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
GPU_builtin(GPU_VIEW_POSITION), &texco_global);
@ -1037,7 +1038,20 @@ static void do_material_tex(GPUShadeInput *shi)
GPU_link(mat, "mtex_negate_texnormal", tnor, &tnor);
if(mtex->normapspace == MTEX_NSPACE_TANGENT)
GPU_link(mat, "mtex_nspace_tangent", GPU_attribute(CD_TANGENT, ""), shi->vn, tnor, &newnor);
{
if(iFirstTimeNMap!=0)
{
// use unnormalized normal (this is how we bake it - closer to gamedev)
GPUNodeLink *vNegNorm;
GPU_link(mat, "vec_math_negate", GPU_builtin(GPU_VIEW_NORMAL), &vNegNorm);
GPU_link(mat, "mtex_nspace_tangent", GPU_attribute(CD_TANGENT, ""), vNegNorm, tnor, &newnor);
iFirstTimeNMap = 0;
}
else // otherwise use accumulated perturbations
{
GPU_link(mat, "mtex_nspace_tangent", GPU_attribute(CD_TANGENT, ""), shi->vn, tnor, &newnor);
}
}
else
newnor = tnor;

@ -746,9 +746,9 @@ void texco_norm(vec3 normal, out vec3 outnormal)
outnormal = normalize(normal);
}
void texco_tangent(vec3 tangent, out vec3 outtangent)
void texco_tangent(vec4 tangent, out vec3 outtangent)
{
outtangent = normalize(tangent);
outtangent = normalize(tangent.xyz);
}
void texco_global(mat4 viewinvmat, vec3 co, out vec3 global)
@ -1250,12 +1250,11 @@ void mtex_negate_texnormal(vec3 normal, out vec3 outnormal)
outnormal = vec3(-normal.x, -normal.y, normal.z);
}
void mtex_nspace_tangent(vec3 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
void mtex_nspace_tangent(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
{
tangent = normalize(tangent);
vec3 B = cross(normal, tangent);
vec3 B = tangent.w * cross(normal, tangent.xyz);
outnormal = texnormal.x*tangent + texnormal.y*B + texnormal.z*normal;
outnormal = texnormal.x*tangent.xyz + texnormal.y*B + texnormal.z*normal;
outnormal = normalize(outnormal);
}

File diff suppressed because it is too large Load Diff

@ -7,6 +7,6 @@ void main()
vec4 co = gl_ModelViewMatrix * gl_Vertex;
varposition = co.xyz;
varnormal = gl_NormalMatrix * gl_Normal;
varnormal = normalize(gl_NormalMatrix * gl_Normal);
gl_Position = gl_ProjectionMatrix * co;

@ -1,13 +1,14 @@
/* DataToC output of file <gpu_shader_vertex_glsl> */
int datatoc_gpu_shader_vertex_glsl_size= 217;
int datatoc_gpu_shader_vertex_glsl_size= 228;
char datatoc_gpu_shader_vertex_glsl[]= {
10,118, 97,114,121,105,110,103, 32,118,101, 99, 51, 32,118, 97,114,112,111,115,105,116,105,111,110,
59, 10,118, 97,114,121,105,110,103, 32,118,101, 99, 51, 32,118, 97,114,110,111,114,109, 97,108, 59, 10, 10,118,111,105,100, 32,
109, 97,105,110, 40, 41, 10,123, 10, 9,118,101, 99, 52, 32, 99,111, 32, 61, 32,103,108, 95, 77,111,100,101,108, 86,105,101,119,
77, 97,116,114,105,120, 32, 42, 32,103,108, 95, 86,101,114,116,101,120, 59, 10, 10, 9,118, 97,114,112,111,115,105,116,105,111,
110, 32, 61, 32, 99,111, 46,120,121,122, 59, 10, 9,118, 97,114,110,111,114,109, 97,108, 32, 61, 32,103,108, 95, 78,111,114,109,
97,108, 77, 97,116,114,105,120, 32, 42, 32,103,108, 95, 78,111,114,109, 97,108, 59, 10, 9,103,108, 95, 80,111,115,105,116,105,
10,118, 97,114,
121,105,110,103, 32,118,101, 99, 51, 32,118, 97,114,112,111,115,105,116,105,111,110, 59, 10,118, 97,114,121,105,110,103, 32,118,
101, 99, 51, 32,118, 97,114,110,111,114,109, 97,108, 59, 10, 10,118,111,105,100, 32,109, 97,105,110, 40, 41, 10,123, 10, 9,118,
101, 99, 52, 32, 99,111, 32, 61, 32,103,108, 95, 77,111,100,101,108, 86,105,101,119, 77, 97,116,114,105,120, 32, 42, 32,103,108,
95, 86,101,114,116,101,120, 59, 10, 10, 9,118, 97,114,112,111,115,105,116,105,111,110, 32, 61, 32, 99,111, 46,120,121,122, 59,
10, 9,118, 97,114,110,111,114,109, 97,108, 32, 61, 32,110,111,114,109, 97,108,105,122,101, 40,103,108, 95, 78,111,114,109, 97,
108, 77, 97,116,114,105,120, 32, 42, 32,103,108, 95, 78,111,114,109, 97,108, 41, 59, 10, 9,103,108, 95, 80,111,115,105,116,105,
111,110, 32, 61, 32,103,108, 95, 80,114,111,106,101, 99,116,105,111,110, 77, 97,116,114,105,120, 32, 42, 32, 99,111, 59, 10, 10,
0};

@ -136,7 +136,7 @@ typedef struct ShadeInput
/* texture coordinates */
float lo[3], gl[3], ref[3], orn[3], winco[3], sticky[3], vcol[4];
float refcol[4], displace[3];
float strandco, tang[3], nmaptang[3], stress, winspeed[4];
float strandco, tang[3], nmapnorm[3], nmaptang[4], stress, winspeed[4];
float duplilo[3], dupliuv[3];
ShadeInputUV uv[8]; /* 8 = MAX_MTFACE */

@ -494,11 +494,82 @@ static void calc_tangent_vector(ObjectRen *obr, VertexTangent **vtangents, MemAr
}
typedef struct
{
ObjectRen *obr;
} SRenderMeshToTangent;
// interface
#include "mikktspace.h"
static int GetNumFaces(const SMikkTSpaceContext * pContext)
{
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
return pMesh->obr->totvlak;
}
static int GetNumVertsOfFace(const SMikkTSpaceContext * pContext, const int face_num)
{
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
return vlr->v4!=NULL ? 4 : 3;
}
static void GetPosition(const SMikkTSpaceContext * pContext, float fPos[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
VertRen * pVerts[] = {vlr->v1, vlr->v2, vlr->v3, vlr->v4};
VECCOPY(fPos, pVerts[vert_index]->co);
}
static void GetTextureCoordinate(const SMikkTSpaceContext * pContext, float fUV[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
MTFace *tface= RE_vlakren_get_tface(pMesh->obr, vlr, pMesh->obr->actmtface, NULL, 0);
if(tface!=NULL)
{
float * pTexCo = tface->uv[vert_index];
fUV[0]=pTexCo[0]; fUV[1]=pTexCo[1];
}
else
{
VertRen * pVerts[] = {vlr->v1, vlr->v2, vlr->v3, vlr->v4};
map_to_sphere(&fUV[0], &fUV[1], pVerts[vert_index]->orco[0], pVerts[vert_index]->orco[1], pVerts[vert_index]->orco[2]);
}
}
static void GetNormal(const SMikkTSpaceContext * pContext, float fNorm[], const int face_num, const int vert_index)
{
//assert(vert_index>=0 && vert_index<4);
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
VertRen * pVerts[] = {vlr->v1, vlr->v2, vlr->v3, vlr->v4};
VECCOPY(fNorm, pVerts[vert_index]->n);
}
static void SetTSpace(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int face_num, const int iVert)
{
//assert(vert_index>=0 && vert_index<4);
SRenderMeshToTangent * pMesh = (SRenderMeshToTangent *) pContext->m_pUserData;
VlakRen *vlr= RE_findOrAddVlak(pMesh->obr, face_num);
float * ftang= RE_vlakren_get_nmap_tangent(pMesh->obr, vlr, 1);
if(ftang!=NULL)
{
VECCOPY(&ftang[iVert*4+0], fvTangent);
ftang[iVert*4+3]=fSign;
}
}
static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent, int do_nmap_tangent)
{
MemArena *arena= NULL;
VertexTangent **vtangents= NULL;
int a;
int a, iCalcNewMethod;
if(do_nmap_tangent) {
arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "nmap tangent arena");
@ -594,22 +665,24 @@ static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent, int d
MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0);
if(tface) {
int k=0;
float *vtang, *ftang= RE_vlakren_get_nmap_tangent(obr, vlr, 1);
vtang= find_vertex_tangent(vtangents[v1->index], tface->uv[0]);
VECCOPY(ftang, vtang);
normalize_v3(ftang);
vtang= find_vertex_tangent(vtangents[v2->index], tface->uv[1]);
VECCOPY(ftang+3, vtang);
normalize_v3(ftang+3);
VECCOPY(ftang+4, vtang);
normalize_v3(ftang+4);
vtang= find_vertex_tangent(vtangents[v3->index], tface->uv[2]);
VECCOPY(ftang+6, vtang);
normalize_v3(ftang+6);
VECCOPY(ftang+8, vtang);
normalize_v3(ftang+8);
if(v4) {
vtang= find_vertex_tangent(vtangents[v4->index], tface->uv[3]);
VECCOPY(ftang+9, vtang);
normalize_v3(ftang+9);
VECCOPY(ftang+12, vtang);
normalize_v3(ftang+12);
}
for(k=0; k<4; k++) ftang[4*k+3]=1;
}
}
}
@ -631,6 +704,31 @@ static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent, int d
}
}
iCalcNewMethod = 1;
if(iCalcNewMethod!=0 && do_nmap_tangent!=0)
{
SRenderMeshToTangent mesh2tangent;
SMikkTSpaceContext sContext;
SMikkTSpaceInterface sInterface;
memset(&mesh2tangent, 0, sizeof(SRenderMeshToTangent));
memset(&sContext, 0, sizeof(SMikkTSpaceContext));
memset(&sInterface, 0, sizeof(SMikkTSpaceInterface));
mesh2tangent.obr = obr;
sContext.m_pUserData = &mesh2tangent;
sContext.m_pInterface = &sInterface;
sInterface.m_getNumFaces = GetNumFaces;
sInterface.m_getNumVerticesOfFace = GetNumVertsOfFace;
sInterface.m_getPosition = GetPosition;
sInterface.m_getTexCoord = GetTextureCoordinate;
sInterface.m_getNormal = GetNormal;
sInterface.m_setTSpaceBasic = SetTSpace;
// 0 if failed
iCalcNewMethod = genTangSpaceDefault(&sContext);
}
if(arena)
BLI_memarena_free(arena);

@ -2114,6 +2114,7 @@ void do_material_tex(ShadeInput *shi)
float texvec[3], dxt[3], dyt[3], tempvec[3], norvec[3], warpvec[3]={0.0f, 0.0f, 0.0f}, Tnor=1.0;
int tex_nr, rgbnor= 0, warpdone=0;
int use_compat_bump, use_ntap_bump;
int iFirstTimeNMap=1;
compatible_bump_init(&compat_bump);
ntap_bump_init(&ntap_bump);
@ -2427,14 +2428,17 @@ void do_material_tex(ShadeInput *shi)
if(mtex->normapspace == MTEX_NSPACE_TANGENT) {
/* qdn: tangent space */
float B[3], tv[3];
cross_v3_v3v3(B, shi->vn, shi->nmaptang); /* bitangent */
const float * no = iFirstTimeNMap!=0 ? shi->nmapnorm : shi->vn;
iFirstTimeNMap=0;
cross_v3_v3v3(B, no, shi->nmaptang); /* bitangent */
mul_v3_fl(B, shi->nmaptang[3]);
/* 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];
tv[0] = texres.nor[0]*shi->nmaptang[0] + texres.nor[1]*B[0] + texres.nor[2]*no[0];
tv[1] = texres.nor[0]*shi->nmaptang[1] + texres.nor[1]*B[1] + texres.nor[2]*no[1];
tv[2] = texres.nor[0]*shi->nmaptang[2] + texres.nor[1]*B[2] + texres.nor[2]*no[2];
shi->vn[0]= facm*no[0] + fact*tv[0];
shi->vn[1]= facm*no[1] + fact*tv[1];
shi->vn[2]= facm*no[2] + fact*tv[2];
}
else {
float nor[3];

@ -2160,12 +2160,14 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int
if(tvn && ttang) {
VECCOPY(mat[0], ttang);
cross_v3_v3v3(mat[1], tvn, ttang);
mul_v3_fl(mat[1], ttang[3]);
VECCOPY(mat[2], tvn);
}
else {
VECCOPY(mat[0], shi->nmaptang);
cross_v3_v3v3(mat[1], shi->vn, shi->nmaptang);
VECCOPY(mat[2], shi->vn);
cross_v3_v3v3(mat[1], shi->nmapnorm, shi->nmaptang);
mul_v3_fl(mat[1], shi->nmaptang[3]);
VECCOPY(mat[2], shi->nmapnorm);
}
invert_m3_m3(imat, mat);
@ -2347,7 +2349,7 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v)
VlakRen *vlr= bs->vlr;
ObjectInstanceRen *obi= bs->obi;
Object *ob= obi->obr->ob;
float l, *v1, *v2, *v3, tvn[3], ttang[3];
float l, *v1, *v2, *v3, tvn[3], ttang[4];
int quad;
ShadeSample *ssamp= &bs->ssamp;
ShadeInput *shi= ssamp->shi;
@ -2386,8 +2388,8 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v)
if(bs->type==RE_BAKE_NORMALS && R.r.bake_normal_space==R_BAKE_SPACE_TANGENT) {
shade_input_set_shade_texco(shi);
VECCOPY(tvn, shi->vn);
VECCOPY(ttang, shi->nmaptang);
VECCOPY(tvn, shi->nmapnorm);
QUATCOPY(ttang, shi->nmaptang);
}
/* if we are doing selected to active baking, find point on other face */

@ -109,7 +109,7 @@
#define RE_RADFACE_ELEMS 1
#define RE_SIMPLIFY_ELEMS 2
#define RE_FACE_ELEMS 1
#define RE_NMAP_TANGENT_ELEMS 12
#define RE_NMAP_TANGENT_ELEMS 16
float *RE_vertren_get_sticky(ObjectRen *obr, VertRen *ver, int verify)
{

@ -286,9 +286,9 @@ void shade_input_set_triangle_i(ShadeInput *shi, ObjectInstanceRen *obi, VlakRen
VECCOPY(shi->n3, shi->v3->n);
if(obi->flag & R_TRANSFORMED) {
mul_m3_v3(obi->nmat, shi->n1);
mul_m3_v3(obi->nmat, shi->n2);
mul_m3_v3(obi->nmat, shi->n3);
mul_m3_v3(obi->nmat, shi->n1); normalize_v3(shi->n1);
mul_m3_v3(obi->nmat, shi->n2); normalize_v3(shi->n2);
mul_m3_v3(obi->nmat, shi->n3); normalize_v3(shi->n3);
}
}
}
@ -819,11 +819,17 @@ void shade_input_set_normals(ShadeInput *shi)
shi->vn[0]= l*n3[0]-u*n1[0]-v*n2[0];
shi->vn[1]= l*n3[1]-u*n1[1]-v*n2[1];
shi->vn[2]= l*n3[2]-u*n1[2]-v*n2[2];
// use unnormalized normal (closer to games)
VECCOPY(shi->nmapnorm, shi->vn);
normalize_v3(shi->vn);
}
else
{
VECCOPY(shi->vn, shi->facenor);
VECCOPY(shi->nmapnorm, shi->vn);
}
/* used in nodes */
VECCOPY(shi->vno, shi->vn);
@ -849,6 +855,10 @@ void shade_input_flip_normals(ShadeInput *shi)
shi->vno[1]= -shi->vno[1];
shi->vno[2]= -shi->vno[2];
shi->nmapnorm[0] = -shi->nmapnorm[0];
shi->nmapnorm[1] = -shi->nmapnorm[1];
shi->nmapnorm[2] = -shi->nmapnorm[2];
shi->flippednor= !shi->flippednor;
}
@ -924,21 +934,32 @@ void shade_input_set_shade_texco(ShadeInput *shi)
if(tangent) {
int j1= shi->i1, j2= shi->i2, j3= shi->i3;
float c0[3], c1[3], c2[3];
vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3);
s1= &tangent[j1*3];
s2= &tangent[j2*3];
s3= &tangent[j3*3];
shi->nmaptang[0]= (tl*s3[0] - tu*s1[0] - tv*s2[0]);
shi->nmaptang[1]= (tl*s3[1] - tu*s1[1] - tv*s2[1]);
shi->nmaptang[2]= (tl*s3[2] - tu*s1[2] - tv*s2[2]);
VECCOPY(c0, &tangent[j1*4]);
VECCOPY(c1, &tangent[j2*4]);
VECCOPY(c2, &tangent[j3*4]);
// keeping tangents normalized at vertex level
// corresponds better to how it's done in game engines
if(obi->flag & R_TRANSFORMED)
mul_m3_v3(obi->nmat, shi->nmaptang);
{
mul_mat3_m4_v3(obi->mat, c0); normalize_v3(c0);
mul_mat3_m4_v3(obi->mat, c1); normalize_v3(c1);
mul_mat3_m4_v3(obi->mat, c2); normalize_v3(c2);
}
// we don't normalize the interpolated TBN tangent
// corresponds better to how it's done in game engines
shi->nmaptang[0]= (tl*c2[0] - tu*c0[0] - tv*c1[0]);
shi->nmaptang[1]= (tl*c2[1] - tu*c0[1] - tv*c1[1]);
shi->nmaptang[2]= (tl*c2[2] - tu*c0[2] - tv*c1[2]);
normalize_v3(shi->nmaptang);
// the sign is the same for all 3 vertices of any
// non degenerate triangle.
shi->nmaptang[3]= tangent[j1*4+3];
}
}
}