diff --git a/intern/opensubdiv/opensubdiv_converter.cc b/intern/opensubdiv/opensubdiv_converter.cc index e6c8985e947..f637f514bf4 100644 --- a/intern/opensubdiv/opensubdiv_converter.cc +++ b/intern/opensubdiv/opensubdiv_converter.cc @@ -53,6 +53,11 @@ inline void reverse_face_verts(int *face_verts, int num_verts) face_verts[0] = last_vert; } +struct TopologyRefinerData { + const OpenSubdiv_Converter& conv; + std::vector *uvs; +}; + } /* namespace */ #endif /* OPENSUBDIV_ORIENT_TOPOLOGY */ @@ -141,10 +146,11 @@ inline void check_oriented_vert_connectivity(const int num_vert_edges, } /* namespace */ template <> -inline bool TopologyRefinerFactory::resizeComponentTopology( +inline bool TopologyRefinerFactory::resizeComponentTopology( TopologyRefiner& refiner, - const OpenSubdiv_Converter& conv) + const TopologyRefinerData& cb_data) { + const OpenSubdiv_Converter& conv = cb_data.conv; /* Faces and face-verts */ const int num_faces = conv.get_num_faces(&conv); setNumBaseFaces(refiner, num_faces); @@ -172,10 +178,11 @@ inline bool TopologyRefinerFactory::resizeComponentTopolog } template <> -inline bool TopologyRefinerFactory::assignComponentTopology( +inline bool TopologyRefinerFactory::assignComponentTopology( TopologyRefiner& refiner, - const OpenSubdiv_Converter& conv) + const TopologyRefinerData &cb_data) { + const OpenSubdiv_Converter& conv = cb_data.conv; using Far::IndexArray; /* Face relations. */ const int num_faces = conv.get_num_faces(&conv); @@ -430,10 +437,11 @@ inline bool TopologyRefinerFactory::assignComponentTopolog }; template <> -inline bool TopologyRefinerFactory::assignComponentTags( +inline bool TopologyRefinerFactory::assignComponentTags( TopologyRefiner& refiner, - const OpenSubdiv_Converter& conv) + const TopologyRefinerData& cb_data) { + const OpenSubdiv_Converter& conv = cb_data.conv; typedef OpenSubdiv::Sdc::Crease Crease; int num_edges = conv.get_num_edges(&conv); @@ -480,43 +488,48 @@ inline bool TopologyRefinerFactory::assignComponentTags( } template <> -inline void TopologyRefinerFactory::reportInvalidTopology( +inline void TopologyRefinerFactory::reportInvalidTopology( TopologyError /*errCode*/, const char *msg, - const OpenSubdiv_Converter& /*mesh*/) + const TopologyRefinerData& /*mesh*/) { printf("OpenSubdiv Error: %s\n", msg); } template <> -inline bool TopologyRefinerFactory::assignFaceVaryingTopology( +inline bool TopologyRefinerFactory::assignFaceVaryingTopology( TopologyRefiner& refiner, - const OpenSubdiv_Converter& conv) + const TopologyRefinerData& cb_data) { - if (conv.get_num_uv_layers(&conv) <= 0) { + const OpenSubdiv_Converter& conv = cb_data.conv; + const int num_layers = conv.get_num_uv_layers(&conv); + if (num_layers <= 0) { /* No UV maps, we can skip any face-varying data. */ return true; } - /* Count overall number of UV data. - * NOTE: We only do single UV layer here, and we don't "merge" loops - * together as it is done in OpenSubdiv examples.x - */ const int num_faces = getNumBaseFaces(refiner); - int num_uvs = 0; - for (int face = 0; face < num_faces; ++face) { - IndexArray face_verts = getBaseFaceVertices(refiner, face); - num_uvs += face_verts.size(); - } - /* Fill in actual UV offsets. */ - const int channel = createBaseFVarChannel(refiner, num_uvs); - for (int face = 0, offset = 0; face < num_faces; ++face) { - Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, - face, - channel); - for (int corner = 0; corner < dst_face_uvs.size(); ++corner) { - dst_face_uvs[corner] = offset; - ++offset; + for (int layer = 0; layer < num_layers; ++layer) { + conv.precalc_uv_layer(&conv, layer); + const int num_uvs = conv.get_num_uvs(&conv); + /* Fill in UV coordinates. */ + cb_data.uvs->resize(num_uvs * 2); + conv.get_uvs(&conv, &cb_data.uvs->at(0)); + /* Fill in per-corner index of the UV. */ + const int channel = createBaseFVarChannel(refiner, num_uvs); + for (int face = 0; face < num_faces; ++face) { + Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, + face, + channel); + for (int corner = 0; corner < dst_face_uvs.size(); ++corner) { + const int uv_index = conv.get_face_corner_uv_index(&conv, + face, + corner); + dst_face_uvs[corner] = uv_index; + } } + conv.finish_uv_layer(&conv); + /* TODO(sergey): Single layer only for now. */ + break; } return true; } @@ -541,33 +554,6 @@ OpenSubdiv::Sdc::SchemeType get_capi_scheme_type(OpenSubdiv_SchemeType type) return OpenSubdiv::Sdc::SCHEME_CATMARK; } -static void import_fvar_data(OpenSubdiv_TopologyRefinerDescr *result, - const OpenSubdiv_Converter& conv) -{ - const int num_layers = conv.get_num_uv_layers(&conv), - num_faces = conv.get_num_faces(&conv); - /* Pre-allocate values in one go. */ - int num_fvar_values = 0; - for (int layer = 0; layer < num_layers; ++layer) { - num_fvar_values = result->osd_refiner->GetNumFVarValuesTotal(); - } - result->uvs.resize(num_fvar_values * 2); - /* Fill in all channels. */ - for (int layer = 0, offset = 0; layer < num_layers; ++layer) { - for (int face = 0; face < num_faces; ++face) { - const int num_verts = conv.get_num_face_verts(&conv, face); - for (int vert = 0; vert < num_verts; ++vert) { - float uv[2]; - conv.get_face_corner_uv(&conv, face, vert, uv); - result->uvs[offset++] = uv[0]; - result->uvs[offset++] = uv[1]; - } - } - /* TODO(sergey): Currently we only support first layer only. */ - break; - } -} - } /* namespace */ struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr( @@ -581,33 +567,25 @@ struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr( Options options; options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY); options.SetCreasingMethod(Options::CREASE_UNIFORM); - options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL); + /* TODO(sergey): Get proper UV subdivide flag. */ + // options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL); + options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_CORNERS_ONLY); - TopologyRefinerFactory::Options + TopologyRefinerFactory::Options topology_options(scheme_type, options); #ifdef OPENSUBDIV_VALIDATE_TOPOLOGY topology_options.validateFullTopology = true; #endif OpenSubdiv_TopologyRefinerDescr *result = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerDescr); + TopologyRefinerData cb_data = {*converter, &result->uvs}; /* We don't use guarded allocation here so we can re-use the refiner * for GL mesh creation directly. */ result->osd_refiner = - TopologyRefinerFactory::Create( - *converter, + TopologyRefinerFactory::Create( + cb_data, topology_options); - if (result->osd_refiner->GetNumFVarChannels() > 0) { - /* Import face varrying data now since later we wouldn't have - * access to the converter. - * - * TODO(sergey): This is so-called "for now", for until we'll - * find better way to plug OSD to Blender or for until something - * happens inside of OSD API. - */ - import_fvar_data(result, *converter); - } - return result; } diff --git a/intern/opensubdiv/opensubdiv_converter_capi.h b/intern/opensubdiv/opensubdiv_converter_capi.h index 47c8dab49de..4448f108e8a 100644 --- a/intern/opensubdiv/opensubdiv_converter_capi.h +++ b/intern/opensubdiv/opensubdiv_converter_capi.h @@ -86,10 +86,16 @@ typedef struct OpenSubdiv_Converter { /* Face-varying data. */ int (*get_num_uv_layers)(const OpenSubdiv_Converter *converter); - void (*get_face_corner_uv)(const OpenSubdiv_Converter *converter, - int face, - int corner, - float r_uv[2]); + + void (*precalc_uv_layer)(const OpenSubdiv_Converter *converter, int layer); + void (*finish_uv_layer)(const OpenSubdiv_Converter *converter); + + int (*get_num_uvs)(const OpenSubdiv_Converter *converter); + void (*get_uvs)(const OpenSubdiv_Converter *converter, float *uvs); + + int (*get_face_corner_uv_index)(const OpenSubdiv_Converter *converter, + int face, + int corner); void (*free_user_data)(const OpenSubdiv_Converter *converter); void *user_data; diff --git a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c index 72e5f9f1346..9e8c97a9249 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c +++ b/source/blender/blenkernel/intern/CCGSubSurf_opensubdiv_converter.c @@ -63,10 +63,15 @@ typedef struct ConvDMStorage { *edge_poly_mem; #endif + MVert *mvert; MEdge *medge; MLoop *mloop; MPoly *mpoly; - MLoopUV *mloopuv; + + MeshIslandStore island_store; + int num_uvs; + float *uvs; + int *face_uvs; } ConvDMStorage; static OpenSubdiv_SchemeType conv_dm_get_type( @@ -295,23 +300,109 @@ static int conv_dm_get_num_uv_layers(const OpenSubdiv_Converter *converter) { ConvDMStorage *storage = converter->user_data; DerivedMesh *dm = storage->dm; - return CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV); + int num_uv_layers = CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV); + return num_uv_layers; } -static void conv_dm_get_face_corner_uv(const OpenSubdiv_Converter *converter, - int face, - int corner, - float r_uv[2]) +static void conv_dm_precalc_uv_layer(const OpenSubdiv_Converter *converter, + int layer) { ConvDMStorage *storage = converter->user_data; - MPoly *mpoly = &storage->mpoly[face]; - MLoopUV *mloopuv = &storage->mloopuv[mpoly->loopstart + corner]; - copy_v2_v2(r_uv, mloopuv->uv); + DerivedMesh *dm = storage->dm; + + const MLoopUV *mloopuv = CustomData_get_layer_n(&dm->loopData, CD_MLOOPUV, layer); + const int num_loops = dm->getNumLoops(dm); + + /* Initialize memory required for the operations. */ + if (storage->uvs == NULL) { + storage->uvs = MEM_mallocN(sizeof(float) * 2 * num_loops, "osd uvs"); + } + if (storage->face_uvs == NULL) { + storage->face_uvs = MEM_mallocN(sizeof(int) * num_loops, "osd face uvs"); + } + + /* Calculate islands connectivity of the UVs. */ + BKE_mesh_calc_islands_loop_poly_uv( + storage->mvert, dm->getNumVerts(dm), + storage->medge, dm->getNumEdges(dm), + storage->mpoly, dm->getNumPolys(dm), + storage->mloop, dm->getNumLoops(dm), + &storage->island_store); + + /* Here we "weld" duplicated vertices from island to the same UV value. + * The idea here is that we need to pass individual islands to OpenSubdiv. + */ + storage->num_uvs = 0; + for (int island = 0; island < storage->island_store.islands_num; ++island) { + MeshElemMap *island_poly_map = storage->island_store.islands[island]; + for (int poly = 0; poly < island_poly_map->count; ++poly) { + int poly_index = island_poly_map->indices[poly]; + /* Within the same UV island we should share UV points across + * loops. Otherwise each poly will be subdivided individually + * which we don't really want. + */ + const MPoly *mpoly = &storage->mpoly[poly_index]; + for (int loop = 0; loop < mpoly->totloop; ++loop) { + const MLoopUV *luv = &mloopuv[mpoly->loopstart + loop]; + bool found = false; + /* TODO(sergey): Quite bad loop, which gives us O(N^2) + * complexity here. But how can we do it smarter, hopefully + * without requiring lots of additional memory. + */ + for (int i = 0; i < storage->num_uvs; ++i) { + if (equals_v2v2(luv->uv, &storage->uvs[2 * i])) { + storage->face_uvs[mpoly->loopstart + loop] = i; + found = true; + break; + } + } + if (!found) { + copy_v2_v2(&storage->uvs[2 * storage->num_uvs], luv->uv); + storage->face_uvs[mpoly->loopstart + loop] = storage->num_uvs; + ++storage->num_uvs; + } + } + } + } +} + +static void conv_dm_finish_uv_layer(const OpenSubdiv_Converter *converter) +{ + ConvDMStorage *storage = converter->user_data; + BKE_mesh_loop_islands_free(&storage->island_store); +} + +static int conv_dm_get_num_uvs(const OpenSubdiv_Converter *converter) +{ + ConvDMStorage *storage = converter->user_data; + return storage->num_uvs; +} + +static void conv_dm_get_uvs(const OpenSubdiv_Converter *converter, float *uvs) +{ + ConvDMStorage *storage = converter->user_data; + memcpy(uvs, storage->uvs, sizeof(float) * 2 * storage->num_uvs); +} + +static int conv_dm_get_face_corner_uv_index(const OpenSubdiv_Converter *converter, + int face, + int corner) +{ + ConvDMStorage *storage = converter->user_data; + const MPoly *mpoly = &storage->mpoly[face]; + return storage->face_uvs[mpoly->loopstart + corner]; } static void conv_dm_free_user_data(const OpenSubdiv_Converter *converter) { ConvDMStorage *user_data = converter->user_data; + if (user_data->uvs != NULL) { + MEM_freeN(user_data->uvs); + } + if (user_data->face_uvs != NULL) { + MEM_freeN(user_data->face_uvs); + } + #ifdef USE_MESH_ELEMENT_MAPPING MEM_freeN(user_data->vert_edge_map); MEM_freeN(user_data->vert_edge_mem); @@ -351,16 +442,25 @@ void ccgSubSurf_converter_setup_from_derivedmesh( converter->get_vert_faces = conv_dm_get_vert_faces; converter->get_num_uv_layers = conv_dm_get_num_uv_layers; - converter->get_face_corner_uv = conv_dm_get_face_corner_uv; + converter->precalc_uv_layer = conv_dm_precalc_uv_layer; + converter->finish_uv_layer = conv_dm_finish_uv_layer; + converter->get_num_uvs = conv_dm_get_num_uvs; + converter->get_uvs = conv_dm_get_uvs; + converter->get_face_corner_uv_index = conv_dm_get_face_corner_uv_index; user_data = MEM_mallocN(sizeof(ConvDMStorage), __func__); user_data->ss = ss; user_data->dm = dm; + user_data->mvert = dm->getVertArray(dm); user_data->medge = dm->getEdgeArray(dm); user_data->mloop = dm->getLoopArray(dm); user_data->mpoly = dm->getPolyArray(dm); - user_data->mloopuv = DM_get_loop_data_layer(dm, CD_MLOOPUV); + + memset(&user_data->island_store, 0, sizeof(user_data->island_store)); + + user_data->uvs = NULL; + user_data->face_uvs = NULL; converter->free_user_data = conv_dm_free_user_data; converter->user_data = user_data; @@ -564,12 +664,30 @@ static int conv_ccg_get_num_uv_layers(const OpenSubdiv_Converter *UNUSED(convert return 0; } -static void conv_ccg_get_face_corner_uv(const OpenSubdiv_Converter * UNUSED(converter), - int UNUSED(face), - int UNUSED(corner), - float r_uv[2]) +static void conv_ccg_precalc_uv_layer(const OpenSubdiv_Converter * UNUSED(converter), + int UNUSED(layer)) { - zero_v2(r_uv); +} + +static void conv_ccg_finish_uv_layer(const OpenSubdiv_Converter *UNUSED(converter)) +{ +} + +static int conv_ccg_get_num_uvs(const OpenSubdiv_Converter *UNUSED(converter)) +{ + return 0; +} + +static void conv_ccg_get_uvs(const OpenSubdiv_Converter * UNUSED(converter), + float *UNUSED(uvs)) +{ +} + +static int conv_ccg_get_face_corner_uv_index(const OpenSubdiv_Converter *UNUSED(converter), + int UNUSED(face), + int UNUSED(corner_)) +{ + return 0; } void ccgSubSurf_converter_setup_from_ccg(CCGSubSurf *ss, @@ -596,7 +714,11 @@ void ccgSubSurf_converter_setup_from_ccg(CCGSubSurf *ss, converter->get_vert_faces = conv_ccg_get_vert_faces; converter->get_num_uv_layers = conv_ccg_get_num_uv_layers; - converter->get_face_corner_uv = conv_ccg_get_face_corner_uv; + converter->precalc_uv_layer = conv_ccg_precalc_uv_layer; + converter->finish_uv_layer = conv_ccg_finish_uv_layer; + converter->get_num_uvs = conv_ccg_get_num_uvs; + converter->get_uvs = conv_ccg_get_uvs; + converter->get_face_corner_uv_index = conv_ccg_get_face_corner_uv_index; converter->free_user_data = NULL; converter->user_data = ss;