diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2014-01-11 14:31:44 +0400 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2014-01-11 14:51:19 +0400 |
commit | 8952f58375385a6e8a636aa1e86763e88fc68fc0 (patch) | |
tree | e0f82a77ed1e969b155517dd427505776dc6f2c1 /source | |
parent | 274b2590fb0eedaadbd5e588f629df7cb69ba0d8 (diff) |
Add tangent space computation/access from RNA (i.e. python).
This simply mimics code used for loopnormals, to enable py scripts to generate and access (temporary)
a tangent 3D vector and bitangent sign for each loop. Together with the split normals, this allow
to recreate a complete tangent space for normal mapping (bitangent = bitangent_sign * cross(normal, tangent)).
Expects all faces to be tri or quads.
Reviewed By: Brecht, campbellbarton
Differential Revision: https://developer.blender.org/D185
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/BKE_mesh.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/DerivedMesh.c | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/customdata.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_evaluate.c | 145 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_customdata_types.h | 7 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_mesh.c | 43 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_mesh_api.c | 40 |
7 files changed, 243 insertions, 7 deletions
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 500b3d8cab5..e82689ec339 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -58,6 +58,7 @@ struct UvVertMap; struct UvMapVert; struct UvElementMap; struct UvElement; +struct ReportList; #ifdef __cplusplus extern "C" { @@ -172,6 +173,12 @@ void BKE_mesh_normals_loop_split( struct MVert *mverts, const int numVerts, struct MEdge *medges, const int numEdges, struct MLoop *mloops, float (*r_loopnors)[3], const int numLoops, struct MPoly *mpolys, float (*polynors)[3], const int numPolys, float split_angle); +void BKE_mesh_loop_tangents_ex( + struct MVert *mverts, const int numVerts, struct MLoop *mloops, float (*r_looptangent)[4], float (*loopnors)[3], + struct MLoopUV *loopuv, const int numLoops, struct MPoly *mpolys, const int numPolys, + struct ReportList *reports); +void BKE_mesh_loop_tangents( + struct Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], struct ReportList *reports); void BKE_mesh_calc_poly_normal( struct MPoly *mpoly, struct MLoop *loopstart, diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 341e8e671c7..8796bd54bd3 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2612,6 +2612,7 @@ static void GetNormal(const SMikkTSpaceContext *pContext, float r_no[3], const i normal_short_to_float_v3(r_no, no); } } + static void SetTSpace(const SMikkTSpaceContext *pContext, const float fvTangent[3], const float fSign, const int face_num, const int iVert) { //assert(vert_index >= 0 && vert_index < 4); @@ -2621,7 +2622,6 @@ static void SetTSpace(const SMikkTSpaceContext *pContext, const float fvTangent[ pRes[3] = fSign; } - void DM_add_tangent_layer(DerivedMesh *dm) { /* mesh vars */ diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 441a2b410ad..39b1a946720 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1168,7 +1168,9 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 37: CD_FREESTYLE_EDGE */ {sizeof(FreestyleEdge), "FreestyleEdge", 1, NULL, NULL, NULL, NULL, NULL, NULL}, /* 38: CD_FREESTYLE_FACE */ - {sizeof(FreestyleFace), "FreestyleFace", 1, NULL, NULL, NULL, NULL, NULL, NULL} + {sizeof(FreestyleFace), "FreestyleFace", 1, NULL, NULL, NULL, NULL, NULL, NULL}, + /* 39: CD_MLOOPTANGENT */ + {sizeof(float[4]), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, }; /* note, numbers are from trunk and need updating for bmesh */ @@ -1184,7 +1186,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { /* 25-29 */ "CDMPoly", "CDMLoop", "CDShapeKeyIndex", "CDShapeKey", "CDBevelWeight", /* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask", /* 35-36 */ "CDGridPaintMask", "CDMVertSkin", - /* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace" + /* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace", "CDMLoopTangent", }; diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 24362c1a817..9bafc9c4cb1 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -50,9 +50,11 @@ #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_multires.h" +#include "BKE_report.h" #include "BLI_strict_flags.h" +#include "mikktspace.h" // #define DEBUG_TIME @@ -577,6 +579,149 @@ void BKE_mesh_normals_loop_split(MVert *mverts, const int UNUSED(numVerts), MEdg /* -------------------------------------------------------------------- */ +/** \name Mesh Tangent Calculations + * \{ */ + +/* Tangent space utils. */ + +/* User data. */ +typedef struct { + MPoly *mpolys; /* faces */ + MLoop *mloops; /* faces's vertices */ + MVert *mverts; /* vertices */ + MLoopUV *luvs; /* texture coordinates */ + float (*lnors)[3]; /* loops' normals */ + float (*tangents)[4]; /* output tangents */ + int num_polys; /* number of polygons */ +} BKEMeshToTangent; + +/* Mikktspace's API */ +static int get_num_faces(const SMikkTSpaceContext *pContext) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + return p_mesh->num_polys; +} + +static int get_num_verts_of_face(const SMikkTSpaceContext *pContext, const int face_idx) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + return p_mesh->mpolys[face_idx].totloop; +} + +static void get_position(const SMikkTSpaceContext *pContext, float r_co[3], const int face_idx, const int vert_idx) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + const int loop_idx = p_mesh->mpolys[face_idx].loopstart + vert_idx; + copy_v3_v3(r_co, p_mesh->mverts[p_mesh->mloops[loop_idx].v].co); +} + +static void get_texture_coordinate(const SMikkTSpaceContext *pContext, float r_uv[2], const int face_idx, + const int vert_idx) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + copy_v2_v2(r_uv, p_mesh->luvs[p_mesh->mpolys[face_idx].loopstart + vert_idx].uv); +} + +static void get_normal(const SMikkTSpaceContext *pContext, float r_no[3], const int face_idx, const int vert_idx) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + copy_v3_v3(r_no, p_mesh->lnors[p_mesh->mpolys[face_idx].loopstart + vert_idx]); +} + +static void set_tspace(const SMikkTSpaceContext *pContext, const float fv_tangent[3], const float face_sign, + const int face_idx, const int vert_idx) +{ + BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData; + float *p_res = p_mesh->tangents[p_mesh->mpolys[face_idx].loopstart + vert_idx]; + copy_v3_v3(p_res, fv_tangent); + p_res[3] = face_sign; +} + +/** + * Compute simplified tangent space normals, i.e. tangent vector + sign of bi-tangent one, which combined with + * split normals can be used to recreate the full tangent space. + * Note: * The mesh should be made of only tris and quads! + */ +void BKE_mesh_loop_tangents_ex(MVert *mverts, const int UNUSED(numVerts), MLoop *mloops, + float (*r_looptangent)[4], float (*loopnors)[3], MLoopUV *loopuvs, + const int UNUSED(numLoops), MPoly *mpolys, const int numPolys, ReportList *reports) +{ + BKEMeshToTangent mesh_to_tangent = {NULL}; + SMikkTSpaceContext s_context = {NULL}; + SMikkTSpaceInterface s_interface = {NULL}; + + MPoly *mp; + int mp_index; + + /* First check we do have a tris/quads only mesh. */ + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + if (mp->totloop > 4) { + BKE_report(reports, RPT_ERROR, "Tangent space can only be computed for tris/quads, aborting...\n"); + return; + } + } + + /* Compute Mikktspace's tangent normals. */ + mesh_to_tangent.mpolys = mpolys; + mesh_to_tangent.mloops = mloops; + mesh_to_tangent.mverts = mverts; + mesh_to_tangent.luvs = loopuvs; + mesh_to_tangent.lnors = loopnors; + mesh_to_tangent.tangents = r_looptangent; + mesh_to_tangent.num_polys = numPolys; + + s_context.m_pUserData = &mesh_to_tangent; + s_context.m_pInterface = &s_interface; + s_interface.m_getNumFaces = get_num_faces; + s_interface.m_getNumVerticesOfFace = get_num_verts_of_face; + s_interface.m_getPosition = get_position; + s_interface.m_getTexCoord = get_texture_coordinate; + s_interface.m_getNormal = get_normal; + s_interface.m_setTSpaceBasic = set_tspace; + + /* 0 if failed */ + if (genTangSpaceDefault(&s_context) == false) { + BKE_report(reports, RPT_ERROR, "Mikktspace failed to generate tangents for this mesh!\n"); + } +} + +/** + * Wrapper around BKE_mesh_loop_tangents_ex, which takes care of most boiling code. + * Note: * There must be a valid loop's CD_NORMALS available. + * * The mesh should be made of only tris and quads! + */ +void BKE_mesh_loop_tangents(Mesh *mesh, const char *uvmap, float (*r_looptangents)[4], ReportList *reports) +{ + MLoopUV *loopuvs; + float (*loopnors)[3]; + + /* Check we have valid texture coordinates first! */ + if (uvmap) { + loopuvs = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap); + } + else { + loopuvs = CustomData_get_layer(&mesh->ldata, CD_MLOOPUV); + } + if (!loopuvs) { + BKE_reportf(reports, RPT_ERROR, "Tangent space computation needs an UVMap, \"%s\" not found, aborting.\n", uvmap); + return; + } + + loopnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); + if (!loopnors) { + BKE_report(reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting.\n"); + return; + } + + BKE_mesh_loop_tangents_ex(mesh->mvert, mesh->totvert, mesh->mloop, r_looptangents, + loopnors, loopuvs, mesh->totloop, mesh->mpoly, mesh->totpoly, reports); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + /** \name Polygon Calculations * \{ */ diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index f6516492b8f..325c2a8df54 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -63,9 +63,10 @@ typedef struct CustomDataExternal { * layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */ typedef struct CustomData { CustomDataLayer *layers; /* CustomDataLayers, ordered by type */ - int typemap[39]; /* runtime only! - maps types to indices of first layer of that type, + int typemap[40]; /* runtime only! - maps types to indices of first layer of that type, * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert() */ + int pad[1]; int totlayer, maxlayer; /* number of layers, size of layers array */ int totsize; /* in editmode, total size of all data layers */ void *pool; /* Bmesh: Memory pool for allocation of blocks */ @@ -117,7 +118,8 @@ enum { CD_MVERT_SKIN = 36, CD_FREESTYLE_EDGE = 37, CD_FREESTYLE_FACE = 38, - CD_NUMTYPES = 39, + CD_MLOOPTANGENT = 39, + CD_NUMTYPES = 40, }; /* Bits for CustomDataMask */ @@ -162,6 +164,7 @@ enum { #define CD_MASK_MVERT_SKIN (1LL << CD_MVERT_SKIN) #define CD_MASK_FREESTYLE_EDGE (1LL << CD_FREESTYLE_EDGE) #define CD_MASK_FREESTYLE_FACE (1LL << CD_FREESTYLE_FACE) +#define CD_MASK_MLOOPTANGENT (1LL << CD_MLOOPTANGENT) /* CustomData.flag */ enum { diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index fe6f33abc8c..cf634f9c7d4 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -338,6 +338,29 @@ static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values) } } +static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values) +{ + Mesh *me = rna_mesh(ptr); + MLoop *ml = (MLoop *)ptr->data; + const float (*vec)[4] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_MLOOPTANGENT); + + if (!vec) { + zero_v3(values); + } + else { + copy_v3_v3(values, (const float *)vec); + } +} + +static float rna_MeshLoop_bitangent_sign_get(PointerRNA *ptr) +{ + Mesh *me = rna_mesh(ptr); + MLoop *ml = (MLoop *)ptr->data; + const float (*vec)[4] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_MLOOPTANGENT); + + return (vec) ? (*vec)[3] : 0.0f; +} + static void rna_MeshPolygon_normal_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); @@ -1870,10 +1893,26 @@ static void rna_def_mloop(BlenderRNA *brna) RNA_def_property_range(prop, -1.0f, 1.0f); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_float_funcs(prop, "rna_MeshLoop_normal_get", NULL, NULL); - RNA_def_property_ui_text(prop, "Loop Normal", + RNA_def_property_ui_text(prop, "Normal", "Local space unit length split normal vector of this vertex for this polygon " - "(only computed on demand!)"); + "(must be computed beforehand using calc_normals_split or calc_tangents)"); + prop = RNA_def_property(srna, "tangent", PROP_FLOAT, PROP_DIRECTION); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, -1.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_MeshLoop_tangent_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Tangent", + "Local space unit length tangent vector of this vertex for this polygon " + "(must be computed beforehand using calc_tangents)"); + + prop = RNA_def_property(srna, "bitangent_sign", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, -1.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_MeshLoop_bitangent_sign_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Bitangent Sign", + "Sign of the bitangent vector of this vertex for this polygon (must be computed " + "beforehand using calc_tangents, bitangent = bitangent_sign * cross(normal, tangent))"); } static void rna_def_mpolygon(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 34f21046a92..76097b04c99 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -34,6 +34,8 @@ #include "RNA_define.h" +#include "DNA_customdata_types.h" + #include "BLI_sys_types.h" #include "BLI_utildefines.h" @@ -100,6 +102,32 @@ static void rna_Mesh_free_normals_split(Mesh *mesh) CustomData_free_layers(&mesh->ldata, CD_NORMAL, mesh->totloop); } +static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char *uvmap) +{ + float (*r_looptangents)[4]; + + if (CustomData_has_layer(&mesh->ldata, CD_MLOOPTANGENT)) { + r_looptangents = CustomData_get_layer(&mesh->ldata, CD_MLOOPTANGENT); + memset(r_looptangents, 0, sizeof(float[4]) * mesh->totloop); + } + else { + r_looptangents = CustomData_add_layer(&mesh->ldata, CD_MLOOPTANGENT, CD_CALLOC, NULL, mesh->totloop); + CustomData_set_layer_flag(&mesh->ldata, CD_MLOOPTANGENT, CD_FLAG_TEMPORARY); + } + + /* Compute loop normals if needed. */ + if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { + rna_Mesh_calc_normals_split(mesh, (float)M_PI); + } + + BKE_mesh_loop_tangents(mesh, uvmap, r_looptangents, reports); +} + +static void rna_Mesh_free_tangents(Mesh *mesh) +{ + CustomData_free_layers(&mesh->ldata, CD_MLOOPTANGENT, mesh->totloop); +} + static void rna_Mesh_calc_smooth_groups(Mesh *mesh, int use_bitflags, int *r_poly_group_len, int **r_poly_group, int *r_group_total) { @@ -141,6 +169,18 @@ void RNA_api_mesh(StructRNA *srna) func = RNA_def_function(srna, "free_normals_split", "rna_Mesh_free_normals_split"); RNA_def_function_ui_description(func, "Free split vertex normals"); + func = RNA_def_function(srna, "calc_tangents", "rna_Mesh_calc_tangents"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, + "Compute tangents and bitangent signs, to be used together with the split normals " + "to get a complete tangent space for normal mapping " + "(split normals are also computed if not yet present)"); + parm = RNA_def_string(func, "uvmap", "", MAX_CUSTOMDATA_LAYER_NAME, "", + "Name of the UV map to use for tangent space computation"); + + func = RNA_def_function(srna, "free_tangents", "rna_Mesh_free_tangents"); + RNA_def_function_ui_description(func, "Free tangents"); + func = RNA_def_function(srna, "calc_tessface", "ED_mesh_calc_tessface"); RNA_def_function_ui_description(func, "Calculate face tessellation (supports editmode too)"); |