/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Nathan Letwory. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/collada/MeshImporter.cpp * \ingroup collada */ #include #if !defined(WIN32) #include #endif /* COLLADABU_ASSERT, may be able to remove later */ #include "COLLADABUPlatform.h" #include "COLLADAFWMeshPrimitive.h" #include "COLLADAFWMeshVertexData.h" #include "COLLADAFWPolygons.h" extern "C" { #include "BKE_customdata.h" #include "BKE_displist.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_object.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" #include "BLI_edgehash.h" #include "MEM_guardedalloc.h" } #include "ArmatureImporter.h" #include "MeshImporter.h" #include "collada_utils.h" // get node name, or fall back to original id if not present (name is optional) template static const std::string bc_get_dae_name(T *node) { return node->getName().size() ? node->getName(): node->getOriginalId(); } static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type) { switch (type) { case COLLADAFW::MeshPrimitive::LINES: return "LINES"; case COLLADAFW::MeshPrimitive::LINE_STRIPS: return "LINESTRIPS"; case COLLADAFW::MeshPrimitive::POLYGONS: return "POLYGONS"; case COLLADAFW::MeshPrimitive::POLYLIST: return "POLYLIST"; case COLLADAFW::MeshPrimitive::TRIANGLES: return "TRIANGLES"; case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: return "TRIANGLE_FANS"; case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS: return "TRIANGLE_STRIPS"; case COLLADAFW::MeshPrimitive::POINTS: return "POINTS"; case COLLADAFW::MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE: return "UNDEFINED_PRIMITIVE_TYPE"; } return "UNKNOWN"; } static const char *bc_geomTypeToStr(COLLADAFW::Geometry::GeometryType type) { switch (type) { case COLLADAFW::Geometry::GEO_TYPE_MESH: return "MESH"; case COLLADAFW::Geometry::GEO_TYPE_SPLINE: return "SPLINE"; case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH: return "CONVEX_MESH"; case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN: default: return "UNKNOWN"; } } UVDataWrapper::UVDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata) { } #ifdef COLLADA_DEBUG void WVDataWrapper::print() { fprintf(stderr, "UVs:\n"); switch (mVData->getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); if (values->getCount()) { for (int i = 0; i < values->getCount(); i += 2) { fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i + 1]); } } } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); if (values->getCount()) { for (int i = 0; i < values->getCount(); i += 2) { fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i + 1]); } } } break; } fprintf(stderr, "\n"); } #endif void UVDataWrapper::getUV(int uv_index, float *uv) { int stride = mVData->getStride(0); if (stride == 0) stride = 2; switch (mVData->getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); if (values->empty()) return; uv[0] = (*values)[uv_index * stride]; uv[1] = (*values)[uv_index * stride + 1]; } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); if (values->empty()) return; uv[0] = (float)(*values)[uv_index * stride]; uv[1] = (float)(*values)[uv_index * stride + 1]; } break; case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: default: fprintf(stderr, "MeshImporter.getUV(): unknown data type\n"); } } VCOLDataWrapper::VCOLDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata) {} void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol) { int stride = mVData->getStride(0); if (stride == 0) stride = 3; switch (mVData->getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); if (values->empty() || values->getCount() <= (v_index * stride + 2)) return; // xxx need to create an eror instead mloopcol->r = FTOCHAR((*values)[v_index * stride]); mloopcol->g = FTOCHAR((*values)[v_index * stride + 1]); mloopcol->b = FTOCHAR((*values)[v_index * stride + 2]); } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); if (values->empty() || values->getCount() <= (v_index * stride + 2)) return; // xxx need to create an eror instead mloopcol->r = FTOCHAR((*values)[v_index * stride]); mloopcol->g = FTOCHAR((*values)[v_index * stride + 1]); mloopcol->b = FTOCHAR((*values)[v_index * stride + 2]); } break; default: fprintf(stderr, "VCOLDataWrapper.getvcol(): unknown data type\n"); } } MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) { } bool MeshImporter::set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) { mpoly->loopstart = loop_index; mpoly->totloop = loop_count; bool broken_loop = false; for (int index=0; index < loop_count; index++) { /* Test if loop defines a hole */ if (!broken_loop) { for (int i = 0; i < index; i++) { if (indices[i] == indices[index]) { // duplicate index -> not good broken_loop = true; } } } mloop->v = indices[index]; mloop++; } return broken_loop; } void MeshImporter::set_vcol(MLoopCol *mlc, VCOLDataWrapper &vob, int loop_index, COLLADAFW::IndexList &index_list, int count) { int index; for (index = 0; index < count; index++, mlc++) { int v_index = index_list.getIndex(index + loop_index); vob.get_vcol(v_index,mlc); } } void MeshImporter::set_face_uv(MLoopUV *mloopuv, UVDataWrapper &uvs, int start_index, COLLADAFW::IndexList& index_list, int count) { // per face vertex indices, this means for quad we have 4 indices, not 8 COLLADAFW::UIntValuesArray& indices = index_list.getIndices(); for (int index = 0; index < count; index++) { int uv_index = indices[index+start_index]; uvs.getUV(uv_index, mloopuv[index].uv); } } #ifdef COLLADA_DEBUG void MeshImporter::print_index_list(COLLADAFW::IndexList& index_list) { fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str()); for (int i = 0; i < index_list.getIndicesCount(); i += 2) { fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1)); } fprintf(stderr, "\n"); } #endif bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) // checks if mesh has supported primitive types: lines, polylist, triangles, triangle_fans { COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); const std::string &name = bc_get_dae_name(mesh); for (unsigned i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType(); const char *type_str = bc_primTypeToStr(type); // OpenCollada passes POLYGONS type for if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; COLLADAFW::Polygons::VertexCountArray& vca = mpvc->getGroupedVerticesVertexCountArray(); int hole_count = 0; int nonface_count = 0; for (unsigned int j = 0; j < vca.getCount(); j++) { int count = vca[j]; if (abs(count) < 3) { nonface_count++; } if (count < 0) { hole_count ++; } } if (hole_count > 0) { fprintf(stderr, "WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n", type_str, name.c_str(), hole_count); } if (nonface_count > 0) { fprintf(stderr, "WARNING: Primitive %s in %s: %d faces with vertex count < 3 (rejected)\n", type_str, name.c_str(), nonface_count); } } else if (type == COLLADAFW::MeshPrimitive::LINES) { // TODO: Add Checker for line syntax here } else if (type != COLLADAFW::MeshPrimitive::TRIANGLES && type != COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str); return false; } } return true; } void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) { // vertices COLLADAFW::MeshVertexData& pos = mesh->getPositions(); if (pos.empty()) { return; } int stride = pos.getStride(0); if (stride == 0) stride = 3; me->totvert = pos.getFloatValues()->getCount() / stride; me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); MVert *mvert; int i; for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { get_vector(mvert->co, pos, i, stride); } } // ===================================================================== // condition 1: The Primitive has normals // condition 2: The number of normals equals the number of faces. // return true if both conditions apply. // return false otherwise. // ===================================================================== bool MeshImporter::primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp) { bool has_useable_normals = false; int normals_count = mp->getNormalIndices().getCount(); if (normals_count > 0) { int index_count = mp->getPositionIndices().getCount(); if (index_count == normals_count) has_useable_normals = true; else { fprintf(stderr, "Warning: Number of normals %d is different from the number of vertices %d, skipping normals\n", normals_count, index_count); } } return has_useable_normals; } // ===================================================================== // Assume that only TRIANGLES, TRIANGLE_FANS, POLYLIST and POLYGONS // have faces. (to be verified) // ===================================================================== bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp) { bool has_faces = false; int type = mp->getPrimitiveType(); switch (type) { case COLLADAFW::MeshPrimitive::TRIANGLES: case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: case COLLADAFW::MeshPrimitive::POLYLIST: case COLLADAFW::MeshPrimitive::POLYGONS: { has_faces = true; break; } default: { has_faces = false; break; } } return has_faces; } static std::string extract_vcolname(const COLLADAFW::String &collada_id) { std::string colname = collada_id; int spos = colname.find("-mesh-colors-"); if (spos != std::string::npos) { colname = colname.substr(spos+13); } return colname; } // ================================================================= // Return the number of faces by summing up // the facecounts of the parts. // hint: This is done because mesh->getFacesCount() does // count loose edges as extra faces, which is not what we want here. // ================================================================= void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) { COLLADAFW::MeshPrimitiveArray& prim_arr = collada_mesh->getMeshPrimitives(); int total_poly_count = 0; int total_loop_count = 0; // collect edge_count and face_count from all parts for (int i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; int type = mp->getPrimitiveType(); switch (type) { case COLLADAFW::MeshPrimitive::TRIANGLES: case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: case COLLADAFW::MeshPrimitive::POLYLIST: case COLLADAFW::MeshPrimitive::POLYGONS: { COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; size_t prim_poly_count = mpvc->getFaceCount(); size_t prim_loop_count = 0; for (int index=0; index < prim_poly_count; index++) { int vcount = get_vertex_count(mpvc, index); if (vcount > 0) { prim_loop_count += vcount; total_poly_count++; } else { // TODO: this is a hole and not another polygon! } } total_loop_count += prim_loop_count; break; } default: break; } } // Add the data containers if (total_poly_count > 0) { me->totpoly = total_poly_count; me->totloop = total_loop_count; me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, me->totpoly); me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, me->totloop); unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount(); for (int i = 0; i < totuvset; i++) { if (collada_mesh->getUVCoords().getLength(i) == 0) { totuvset = 0; break; } } if (totuvset > 0) { for (int i = 0; i < totuvset; i++) { COLLADAFW::MeshVertexData::InputInfos *info = collada_mesh->getUVCoords().getInputInfosArray()[i]; COLLADAFW::String &uvname = info->mName; // Allocate space for UV_data CustomData_add_layer_named(&me->pdata, CD_MTEXPOLY, CD_DEFAULT, NULL, me->totpoly, uvname.c_str()); CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, uvname.c_str()); } // activate the first uv map me->mtpoly = (MTexPoly *)CustomData_get_layer_n(&me->pdata, CD_MTEXPOLY, 0); me->mloopuv = (MLoopUV *) CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0); } int totcolset = collada_mesh->getColors().getInputInfosArray().getCount(); if (totcolset > 0) { for (int i = 0; i < totcolset; i++) { COLLADAFW::MeshVertexData::InputInfos *info = collada_mesh->getColors().getInputInfosArray()[i]; COLLADAFW::String colname = extract_vcolname(info->mName); CustomData_add_layer_named(&me->ldata,CD_MLOOPCOL,CD_DEFAULT,NULL,me->totloop, colname.c_str()); } me->mloopcol = (MLoopCol *) CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, 0); } } } unsigned int MeshImporter::get_vertex_count(COLLADAFW::Polygons *mp, int index) { int type = mp->getPrimitiveType(); int result; switch (type) { case COLLADAFW::MeshPrimitive::TRIANGLES: case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: { result = 3; break; } case COLLADAFW::MeshPrimitive::POLYLIST: case COLLADAFW::MeshPrimitive::POLYGONS: { result = mp->getGroupedVerticesVertexCountArray()[index]; break; } default: { result = -1; break; } } return result; } unsigned int MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh) { COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); int loose_edge_count = 0; // collect edge_count and face_count from all parts for (int i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; int type = mp->getPrimitiveType(); switch (type) { case COLLADAFW::MeshPrimitive::LINES: { size_t prim_totface = mp->getFaceCount(); loose_edge_count += prim_totface; break; } default: break; } } return loose_edge_count; } // ================================================================= // This functin is copied from source/blender/editors/mesh/mesh_data.c // // TODO: (As discussed with sergey-) : // Maybe move this function to blenderkernel/intern/mesh.c // and add definition to BKE_mesh.c // ================================================================= void MeshImporter::mesh_add_edges(Mesh *mesh, int len) { CustomData edata; MEdge *medge; int i, totedge; if (len == 0) return; totedge = mesh->totedge + len; /* update customdata */ CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH, CD_DEFAULT, totedge); CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); if (!CustomData_has_layer(&edata, CD_MEDGE)) CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); CustomData_free(&mesh->edata, mesh->totedge); mesh->edata = edata; BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */ /* set default flags */ medge = &mesh->medge[mesh->totedge]; for (i = 0; i < len; i++, medge++) medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT; mesh->totedge = totedge; } // ================================================================= // Read all loose edges. // Important: This function assumes that all edges from existing // faces have allready been generated and added to me->medge // So this function MUST be called after read_faces() (see below) // ================================================================= void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) { unsigned int loose_edge_count = get_loose_edge_count(mesh); if (loose_edge_count > 0) { unsigned int face_edge_count = me->totedge; /* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */ mesh_add_edges(me, loose_edge_count); MEdge *med = me->medge + face_edge_count; COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives(); for (int i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; int type = mp->getPrimitiveType(); if (type == COLLADAFW::MeshPrimitive::LINES) { unsigned int edge_count = mp->getFaceCount(); unsigned int *indices = mp->getPositionIndices().getData(); for (int i = 0; i < edge_count; i++, med++) { med->bweight = 0; med->crease = 0; med->flag |= ME_LOOSEEDGE; med->v1 = indices[2 * i]; med->v2 = indices[2 * i + 1]; } } } } } // ======================================================================= // Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON // Important: This function MUST be called before read_lines() // Otherwise we will loose all edges from faces (see read_lines() above) // // TODO: import uv set names // ======================================================================== void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) { unsigned int i; allocate_poly_data(collada_mesh, me); UVDataWrapper uvs(collada_mesh->getUVCoords()); VCOLDataWrapper vcol(collada_mesh->getColors()); MPoly *mpoly = me->mpoly; MLoop *mloop = me->mloop; int loop_index = 0; MaterialIdPrimitiveArrayMap mat_prim_map; COLLADAFW::MeshPrimitiveArray& prim_arr = collada_mesh->getMeshPrimitives(); COLLADAFW::MeshVertexData& nor = collada_mesh->getNormals(); for (i = 0; i < prim_arr.getCount(); i++) { COLLADAFW::MeshPrimitive *mp = prim_arr[i]; // faces size_t prim_totpoly = mp->getFaceCount(); unsigned int *position_indices = mp->getPositionIndices().getData(); unsigned int *normal_indices = mp->getNormalIndices().getData(); bool mp_has_normals = primitive_has_useable_normals(mp); bool mp_has_faces = primitive_has_faces(mp); int collada_meshtype = mp->getPrimitiveType(); // since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive Primitive prim = {mpoly, 0}; // If MeshPrimitive is TRIANGLE_FANS we split it into triangles // The first trifan vertex will be the first vertex in every triangle // XXX The proper function of TRIANGLE_FANS is not tested!!! // XXX In particular the handling of the normal_indices looks very wrong to me if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { unsigned grouped_vertex_count = mp->getGroupedVertexElementsCount(); for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) { unsigned int first_vertex = position_indices[0]; // Store first trifan vertex unsigned int first_normal = normal_indices[0]; // Store first trifan vertex normal unsigned int vertex_count = mp->getGroupedVerticesVertexCount(group_index); for (unsigned int vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) { // For each triangle store indeces of its 3 vertices unsigned int triangle_vertex_indices[3] = {first_vertex, position_indices[1], position_indices[2]}; set_poly_indices(mpoly, mloop, loop_index, triangle_vertex_indices, 3); if (mp_has_normals) { // vertex normals, same inplementation as for the triangles // the same for vertces normals unsigned int vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]}; if (!is_flat_face(vertex_normal_indices, nor, 3)) mpoly->flag |= ME_SMOOTH; normal_indices++; } mpoly++; mloop += 3; loop_index += 3; prim.totpoly++; } // Moving cursor to the next triangle fan. if (mp_has_normals) normal_indices += 2; position_indices += 2; } } if (collada_meshtype == COLLADAFW::MeshPrimitive::POLYLIST || collada_meshtype == COLLADAFW::MeshPrimitive::POLYGONS || collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLES) { COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; unsigned int start_index = 0; COLLADAFW::IndexListArray& index_list_array_uvcoord = mp->getUVCoordIndicesArray(); COLLADAFW::IndexListArray& index_list_array_vcolor = mp->getColorIndicesArray(); int invalid_loop_holes = 0; for (unsigned int j = 0; j < prim_totpoly; j++) { // Vertices in polygon: int vcount = get_vertex_count(mpvc, j); if (vcount < 0) { continue; // TODO: add support for holes } bool broken_loop = set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); if (broken_loop) { invalid_loop_holes += 1; } for (unsigned int uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount(); uvset_index++) { // get mtface by face index and uv set index COLLADAFW::IndexList& index_list = *index_list_array_uvcoord[uvset_index]; MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, index_list.getName().c_str()); if (mloopuv == NULL) { fprintf(stderr, "Collada import: Mesh [%s] : Unknown reference to TEXCOORD [#%s].\n", me->id.name, index_list.getName().c_str() ); } else { set_face_uv(mloopuv+loop_index, uvs, start_index, *index_list_array_uvcoord[uvset_index], vcount); } } if (mp_has_normals) { if (!is_flat_face(normal_indices, nor, vcount)) mpoly->flag |= ME_SMOOTH; } if (mp->hasColorIndices()) { int vcolor_count = index_list_array_vcolor.getCount(); for (unsigned int vcolor_index = 0; vcolor_index < vcolor_count; vcolor_index++) { COLLADAFW::IndexList& color_index_list = *mp->getColorIndices(vcolor_index); COLLADAFW::String colname = extract_vcolname(color_index_list.getName()); MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named(&me->ldata, CD_MLOOPCOL, colname.c_str()); if (mloopcol == NULL) { fprintf(stderr, "Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n", me->id.name, color_index_list.getName().c_str()); } else { set_vcol(mloopcol + loop_index, vcol, start_index, color_index_list, vcount); } } } mpoly++; mloop += vcount; loop_index += vcount; start_index += vcount; prim.totpoly++; if (mp_has_normals) normal_indices += vcount; position_indices += vcount; } if (invalid_loop_holes > 0) { fprintf(stderr, "Collada import: Mesh [%s] : contains %d unsupported loops (holes).\n", me->id.name, invalid_loop_holes); } } else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) { continue; // read the lines later after all the rest is done } if (mp_has_faces) mat_prim_map[mp->getMaterialId()].push_back(prim); } geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map; } void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData& arr, int i, int stride) { i *= stride; switch (arr.getType()) { case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { COLLADAFW::ArrayPrimitiveType *values = arr.getFloatValues(); if (values->empty()) return; v[0] = (*values)[i++]; v[1] = (*values)[i++]; if (stride>=3) { v[2] = (*values)[i]; } else { v[2] = 0.0f; } } break; case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { COLLADAFW::ArrayPrimitiveType *values = arr.getDoubleValues(); if (values->empty()) return; v[0] = (float)(*values)[i++]; v[1] = (float)(*values)[i++]; if (stride >= 3) { v[2] = (float)(*values)[i]; } else { v[2] = 0.0f; } } break; default: break; } } bool MeshImporter::is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData& nor, int count) { float a[3], b[3]; get_vector(a, nor, *nind, 3); normalize_v3(a); nind++; for (int i = 1; i < count; i++, nind++) { get_vector(b, nor, *nind, 3); normalize_v3(b); float dp = dot_v3v3(a, b); if (dp < 0.99999f || dp > 1.00001f) return false; } return true; } Object *MeshImporter::get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) { if (uid_object_map.find(geom_uid) != uid_object_map.end()) return uid_object_map[geom_uid]; return NULL; } Mesh *MeshImporter::get_mesh_by_geom_uid(const COLLADAFW::UniqueId& mesh_uid) { if (uid_mesh_map.find(mesh_uid) != uid_mesh_map.end()) return uid_mesh_map[mesh_uid]; return NULL; } std::string *MeshImporter::get_geometry_name(const std::string &mesh_name) { if (this->mesh_geom_map.find(mesh_name) != this->mesh_geom_map.end()) return &this->mesh_geom_map[mesh_name]; return NULL; } MTex *MeshImporter::assign_textures_to_uvlayer(COLLADAFW::TextureCoordinateBinding &ctexture, Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map, MTex *color_texture) { const COLLADAFW::TextureMapId texture_index = ctexture.getTextureMapId(); size_t setindex = ctexture.getSetIndex(); std::string uvname = ctexture.getSemantic(); if (setindex == -1) return NULL; const CustomData *data = &me->fdata; int layer_index = CustomData_get_layer_index(data, CD_MTFACE); if (layer_index == -1) return NULL; CustomDataLayer *cdl = &data->layers[layer_index + setindex]; /* set uvname to bind_vertex_input semantic */ BLI_strncpy(cdl->name, uvname.c_str(), sizeof(cdl->name)); if (texindex_texarray_map.find(texture_index) == texindex_texarray_map.end()) { fprintf(stderr, "Cannot find texture array by texture index.\n"); return color_texture; } std::vector textures = texindex_texarray_map[texture_index]; std::vector::iterator it; for (it = textures.begin(); it != textures.end(); it++) { MTex *texture = *it; if (texture) { BLI_strncpy(texture->uvname, uvname.c_str(), sizeof(texture->uvname)); if (texture->mapto == MAP_COL) color_texture = texture; } } return color_texture; } /** * this function checks if both objects have the same * materials assigned to Object (in the same order) * returns true if condition matches, otherwise false; **/ static bool bc_has_same_material_configuration(Object *ob1, Object *ob2) { if (ob1->totcol != ob2->totcol) return false; // not same number of materials if (ob1->totcol == 0) return false; // no material at all for (int index=0; index < ob1->totcol; index++) { if (ob1->matbits[index] != ob2->matbits[index]) return false; // shouldn't happen if (ob1->matbits[index] == 0) return false; // shouldn't happen if (ob1->mat[index] != ob2->mat[index]) return false; // different material assignment } return true; } /** * * Caution here: This code assumes tha all materials are assigned to Object * and no material is assigned to Data. * That is true right after the objects have been imported. * **/ static void bc_copy_materials_to_data(Object *ob, Mesh *me) { for (int index = 0; index < ob->totcol; index++) { ob->matbits[index] = 0; me->mat[index] = ob->mat[index]; } } /** * * Remove all references to materials from the object * **/ static void bc_remove_materials_from_object(Object *ob, Mesh *me) { for (int index = 0; index < ob->totcol; index++) { ob->matbits[index] = 0; ob->mat[index] = NULL; } } /** * Returns the list of Users of the given Mesh object. * Note: This function uses the object user flag to control * which objects have already been processed. **/ std::vector MeshImporter::get_all_users_of(Mesh *reference_mesh) { std::vector mesh_users; for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); ++it) { Object *ob = (*it); if (bc_is_marked(ob)) { bc_remove_mark(ob); Mesh *me = (Mesh *) ob->data; if (me == reference_mesh) mesh_users.push_back(ob); } } return mesh_users; } /** * * During import all materials have been assigned to Object. * Now we iterate over the imported objects and optimize * the assignments as follows: * * for each imported geometry: * if number of users is 1: * get the user (object) * move the materials from Object to Data * else: * determine which materials are assigned to the first user * check if all other users have the same materials in the same order * if the check is positive: * Add the materials of the first user to the geometry * adjust all other users accordingly. * **/ void MeshImporter::optimize_material_assignements() { for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); ++it) { Object *ob = (*it); Mesh *me = (Mesh *) ob->data; if (me->id.us==1) { bc_copy_materials_to_data(ob, me); bc_remove_materials_from_object(ob, me); bc_remove_mark(ob); } else if (me->id.us > 1) { bool can_move = true; std::vector mesh_users = get_all_users_of(me); if (mesh_users.size() > 1) { Object *ref_ob = mesh_users[0]; for (int index = 1; index < mesh_users.size(); index++) { if (!bc_has_same_material_configuration(ref_ob, mesh_users[index])) { can_move = false; break; } } if (can_move) { bc_copy_materials_to_data(ref_ob, me); for (int index = 0; index < mesh_users.size(); index++) { Object *object = mesh_users[index]; bc_remove_materials_from_object(object, me); bc_remove_mark(object); } } } } } } /** * We do not know in advance which objects will share geometries. * And we do not know either if the objects which share geometries * come along with different materials. So we first create the objects * and assign the materials to Object, then in a later cleanup we decide * which materials shall be moved to the created geometries. Also see * optimize_material_assignements() above. */ MTFace *MeshImporter::assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, std::map& uid_material_map, Object *ob, const COLLADAFW::UniqueId *geom_uid, char *layername, MTFace *texture_face, std::map& material_texture_mapping_map, short mat_index) { MTex *color_texture = NULL; Mesh *me = (Mesh *)ob->data; const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial(); // do we know this material? if (uid_material_map.find(ma_uid) == uid_material_map.end()) { fprintf(stderr, "Cannot find material by UID.\n"); return NULL; } // first time we get geom_uid, ma_uid pair. Save for later check. materials_mapped_to_geom.insert(std::pair(*geom_uid, ma_uid)); Material *ma = uid_material_map[ma_uid]; // Attention! This temporaly assigns material to object on purpose! // See note above. ob->actcol=0; assign_material(ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT); COLLADAFW::TextureCoordinateBindingArray& tex_array = cmaterial.getTextureCoordinateBindingArray(); TexIndexTextureArrayMap texindex_texarray_map = material_texture_mapping_map[ma]; unsigned int i; // loop through for (i = 0; i < tex_array.getCount(); i++) { color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map, color_texture); } // set texture face if (color_texture && strlen((color_texture)->uvname) && !STREQ(layername, color_texture->uvname)) { texture_face = (MTFace *)CustomData_get_layer_named(&me->fdata, CD_MTFACE, color_texture->uvname); strcpy(layername, color_texture->uvname); } MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId(); // assign material indices to mesh faces if (mat_prim_map.find(mat_id) != mat_prim_map.end()) { std::vector& prims = mat_prim_map[mat_id]; std::vector::iterator it; for (it = prims.begin(); it != prims.end(); it++) { Primitive& prim = *it; MPoly *mpoly = prim.mpoly; for (i = 0; i < prim.totpoly; i++, mpoly++) { mpoly->mat_nr = mat_index; // bind texture images to faces if (texture_face && color_texture) { texture_face->tpage = (Image *)color_texture->tex->ima; texture_face++; } } } } return texture_face; } Object *MeshImporter::create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom, bool isController, std::map& uid_material_map, std::map& material_texture_mapping_map) { const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId(); // check if node instanciates controller or geometry if (isController) { geom_uid = armature_importer->get_geometry_uid(*geom_uid); if (!geom_uid) { fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n"); return NULL; } } else { if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) { // this could happen if a mesh was not created // (e.g. if it contains unsupported geometry) fprintf(stderr, "Couldn't find a mesh by UID.\n"); return NULL; } } if (!uid_mesh_map[*geom_uid]) return NULL; // name Object const std::string& id = node->getName().size() ? node->getName() : node->getOriginalId(); const char *name = (id.length()) ? id.c_str() : NULL; // add object Object *ob = bc_add_object(scene, OB_MESH, name); bc_set_mark(ob); // used later for material assignement optimization // store object pointer for ArmatureImporter uid_object_map[*geom_uid] = ob; imported_objects.push_back(ob); // replace ob->data freeing the old one Mesh *old_mesh = (Mesh *)ob->data; Mesh *new_mesh = uid_mesh_map[*geom_uid]; BKE_mesh_assign_object(ob, new_mesh); BKE_mesh_calc_normals(new_mesh); id_us_plus(&old_mesh->id); /* Because BKE_mesh_assign_object would have already decreased it... */ BKE_libblock_free_us(G.main, old_mesh); char layername[100]; layername[0] = '\0'; MTFace *texture_face = NULL; COLLADAFW::MaterialBindingArray& mat_array = geom->getMaterialBindings(); // loop through geom's materials for (unsigned int i = 0; i < mat_array.getCount(); i++) { if (mat_array[i].getReferencedMaterial().isValid()) { texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, layername, texture_face, material_texture_mapping_map, i); } else { fprintf(stderr, "invalid referenced material for %s\n", mat_array[i].getName().c_str()); } } return ob; } // create a mesh storing a pointer in a map so it can be retrieved later by geometry UID bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) { if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { // TODO: report warning fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType())); return true; } COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh *)geom; if (!is_nice_mesh(mesh)) { fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh).c_str()); return true; } const std::string& str_geom_id = mesh->getName().size() ? mesh->getName() : mesh->getOriginalId(); Mesh *me = BKE_mesh_add(G.main, (char *)str_geom_id.c_str()); id_us_min(&me->id); // is already 1 here, but will be set later in BKE_mesh_assign_object // store the Mesh pointer to link it later with an Object // mesh_geom_map needed to map mesh to its geometry name (for shape key naming) this->uid_mesh_map[mesh->getUniqueId()] = me; this->mesh_geom_map[std::string(me->id.name)] = str_geom_id; read_vertices(mesh, me); read_polys(mesh, me); // must validate before calculating edges BKE_mesh_calc_normals(me); BKE_mesh_validate(me, false, false); // validation does this // BKE_mesh_calc_edges(me, false, false); // read_lines() must be called after the face edges have been generated. // Oterwise the loose edges will be silently deleted again. read_lines(mesh, me); return true; }