OpenSubdiv: Lay down fundamentals to support multiple UV maps
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 22 Jul 2016 12:46:13 +0000 (14:46 +0200)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 22 Jul 2016 12:56:15 +0000 (14:56 +0200)
intern/opensubdiv/opensubdiv_capi.cc
intern/opensubdiv/opensubdiv_capi.h
intern/opensubdiv/opensubdiv_converter.cc
intern/opensubdiv/opensubdiv_gpu_capi.cc
source/blender/blenkernel/intern/CCGSubSurf.h
source/blender/blenkernel/intern/CCGSubSurf_opensubdiv.c
source/blender/blenkernel/intern/subsurf_ccg.c

index 9093fd87a446b5cf5dd1e62cc95892f2f1c33d23..ab904953c70d94b57c82066436642175edebd944 100644 (file)
@@ -160,45 +160,49 @@ struct FVarVertex {
 static void interpolate_fvar_data(OpenSubdiv::Far::TopologyRefiner& refiner,
                                   const std::vector<float> uvs,
                                   std::vector<float> &fvar_data) {
-       /* TODO(sergey): Support all FVar channels. */
-       const int channel = 0;
        /* TODO(sergey): Make it somehow more generic way. */
        const int fvar_width = 2;
-       const int num_uvs = refiner.GetLevel(0).GetNumFVarValues(0) * 2;
-       int max_level = refiner.GetMaxLevel(),
-           num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel),
-           num_values_total = refiner.GetNumFVarValuesTotal(channel);
-       if (num_values_total <= 0) {
-               return;
-       }
-       OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner);
-       if (refiner.IsUniform()) {
-               /* For uniform we only keep the highest level of refinement. */
-               fvar_data.resize(num_values_max * fvar_width);
-               std::vector<FVarVertex> buffer(num_values_total - num_values_max);
-               FVarVertex *src = &buffer[0];
-               memcpy(src, &uvs[0], num_uvs * sizeof(float));
-               /* Defer the last level to treat separately with its alternate
-                * destination.
-                */
-               for (int level = 1; level < max_level; ++level) {
-                       FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
-                       primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
-                       src = dst;
+       const int max_level = refiner.GetMaxLevel();
+       size_t fvar_data_offset = 0, values_offset = 0;
+       for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) {
+               const int num_values = refiner.GetLevel(0).GetNumFVarValues(0) * 2,
+                         num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel),
+                         num_values_total = refiner.GetNumFVarValuesTotal(channel);
+               if (num_values_total <= 0) {
+                       continue;
+               }
+               OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner);
+               if (refiner.IsUniform()) {
+                       /* For uniform we only keep the highest level of refinement. */
+                       fvar_data.resize(fvar_data.size() + num_values_max * fvar_width);
+                       std::vector<FVarVertex> buffer(num_values_total - num_values_max);
+                       FVarVertex *src = &buffer[0];
+                       memcpy(src, &uvs[values_offset], num_values * sizeof(float));
+                       /* Defer the last level to treat separately with its alternate
+                        * destination.
+                        */
+                       for (int level = 1; level < max_level; ++level) {
+                               FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
+                               primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
+                               src = dst;
+                       }
+                       FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
+                       primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel);
+                       fvar_data_offset += num_values_max * fvar_width;
+               } else {
+                       /* For adaptive we keep all levels. */
+                       fvar_data.resize(fvar_data.size() + num_values_total * fvar_width);
+                       FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
+                       memcpy(src, &uvs[values_offset], num_values * sizeof(float));
+                       for (int level = 1; level <= max_level; ++level) {
+                               FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
+                               primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
+                               src = dst;
+                       }
+                       fvar_data_offset += num_values_total * fvar_width;
                }
-               FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[0]);
-               primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel);
-       } else {
-               /* For adaptive we keep all levels. */
-               fvar_data.resize(num_values_total * fvar_width);
-               FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[0]);
-               memcpy(src, &uvs[0], num_uvs * sizeof(float));
-               for (int level = 1; level <= max_level; ++level) {
-                       FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
-                       primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
-                       src = dst;
-        }
-    }
+               values_offset += num_values;
+       }
 }
 
 }  // namespace
@@ -275,7 +279,7 @@ struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
        if (refiner->GetNumFVarChannels() > 0) {
                std::vector<float> fvar_data;
                interpolate_fvar_data(*refiner, topology_refiner->uvs, fvar_data);
-               openSubdiv_osdGLAllocFVar(gl_mesh, &fvar_data[0]);
+               openSubdiv_osdGLAllocFVar(topology_refiner, gl_mesh, &fvar_data[0]);
        }
        else {
                gl_mesh->fvar_data = NULL;
index 0410083304e81443857adc70a8cb4cf9d1ee79ee..c3a194813e65a10497d5d08bff0601adf12b2b16 100644 (file)
@@ -131,7 +131,8 @@ void openSubdiv_evaluateVarying(OpenSubdiv_EvaluatorDescr *evaluator_descr,
  *
  * TODO(sergey): Some of the stuff could be initialized once for all meshes.
  */
-void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl);
+void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
+                                        int active_uv_index);
 
 /* Draw specified patches. */
 void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
@@ -139,7 +140,8 @@ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
                                  int start_patch,
                                  int num_patches);
 
-void openSubdiv_osdGLAllocFVar(OpenSubdiv_GLMesh *gl_mesh,
+void openSubdiv_osdGLAllocFVar(struct OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+                               OpenSubdiv_GLMesh *gl_mesh,
                                const float *fvar_data);
 void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh);
 
index f637f514bf49578d40ceaf3583f363c57fa213e5..9b2fb19808c1e4a7b436778c0504423bdeeff8ce 100644 (file)
@@ -508,12 +508,14 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopolo
                return true;
        }
        const int num_faces = getNumBaseFaces(refiner);
+       size_t uvs_offset = 0;
        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));
+               cb_data.uvs->resize(cb_data.uvs->size() + num_uvs * 2);
+               conv.get_uvs(&conv, &cb_data.uvs->at(uvs_offset));
+               uvs_offset += num_uvs * 2;
                /* Fill in per-corner index of the UV. */
                const int channel = createBaseFVarChannel(refiner, num_uvs);
                for (int face = 0; face < num_faces; ++face) {
@@ -528,8 +530,6 @@ inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopolo
                        }
                }
                conv.finish_uv_layer(&conv);
-               /* TODO(sergey): Single layer only for now. */
-               break;
        }
        return true;
 }
index 8329062c4527c9309d60cb341c00eff37c9e4d66..a7c42aea07f39467f159edc6ed20b38b1f209956 100644 (file)
@@ -45,6 +45,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "opensubdiv_capi.h"
+#include "opensubdiv_topology_refiner.h"
 
 using OpenSubdiv::Osd::GLMeshInterface;
 
@@ -82,6 +83,7 @@ typedef struct Transform {
 } Transform;
 
 static bool g_use_osd_glsl = false;
+static int g_active_uv_index = 0;
 
 static GLuint g_flat_fill_solid_program = 0;
 static GLuint g_flat_fill_texture2d_program = 0;
@@ -110,25 +112,44 @@ struct OpenSubdiv_GLMeshFVarData
                        glDeleteTextures(1, &texture_buffer);
                }
                texture_buffer = 0;
+               channel_offsets.clear();
        }
 
-       void Create(const OpenSubdiv::Far::PatchTable *patch_table,
+       void Create(const OpenSubdiv::Far::TopologyRefiner *refiner,
+                   const OpenSubdiv::Far::PatchTable *patch_table,
                    int fvar_width,
                    const float *fvar_src_data)
        {
                Release();
-               OpenSubdiv::Far::ConstIndexArray indices = patch_table->GetFVarValues();
 
-               // expand fvardata to per-patch array
+               /* Expand fvar data to per-patch array */
+               const int max_level = refiner->GetMaxLevel();
+               const int num_channels = patch_table->GetNumFVarChannels();
                std::vector<float> data;
-               data.reserve(indices.size() * fvar_width);
-
-               for (int fvert = 0; fvert < (int)indices.size(); ++fvert) {
-                       int index = indices[fvert] * fvar_width;
-                       for (int i = 0; i < fvar_width; ++i) {
-                               data.push_back(fvar_src_data[index++]);
+               size_t fvar_data_offset = 0;
+               channel_offsets.resize(num_channels);
+               for (int channel = 0; channel < num_channels; ++channel) {
+                       OpenSubdiv::Far::ConstIndexArray indices =
+                               patch_table->GetFVarValues(channel);
+
+                       channel_offsets[channel] = data.size();
+                       data.reserve(data.size() + indices.size() * fvar_width);
+
+                       for (int fvert = 0; fvert < (int)indices.size(); ++fvert) {
+                               int index = indices[fvert] * fvar_width;
+                               for (int i = 0; i < fvar_width; ++i) {
+                                       data.push_back(fvar_src_data[fvar_data_offset + index++]);
+                               }
+                       }
+                       if (refiner->IsUniform()) {
+                               const int num_values_max = refiner->GetLevel(max_level).GetNumFVarValues(channel);
+                               fvar_data_offset += num_values_max * fvar_width;
+                       } else {
+                               const int num_values_total = refiner->GetNumFVarValuesTotal(channel);
+                               fvar_data_offset += num_values_total * fvar_width;
                        }
                }
+
                GLuint buffer;
                glGenBuffers(1, &buffer);
                glBindBuffer(GL_ARRAY_BUFFER, buffer);
@@ -144,6 +165,7 @@ struct OpenSubdiv_GLMeshFVarData
                glDeleteBuffers(1, &buffer);
        }
        GLuint texture_buffer;
+       std::vector<size_t> channel_offsets;
 };
 
 /* TODO(sergey): This is actually duplicated code from BLI. */
@@ -415,8 +437,14 @@ void bindProgram(OpenSubdiv_GLMesh *gl_mesh, int program)
        }
 
        /* See notes below about why we use such values. */
+       /* TOO(sergey): Get proper value for FVar width. */
        glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 2);
-       glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0);
+       if (gl_mesh->fvar_data->channel_offsets.size() > 0 && g_active_uv_index >= 0) {
+               glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"),
+                           gl_mesh->fvar_data->channel_offsets[g_active_uv_index]);
+       } else {
+               glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0);
+       }
 }
 
 }  /* namespace */
@@ -503,9 +531,11 @@ void openSubdiv_osdGLDisplayDeinit(void)
        }
 }
 
-void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl)
+void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
+                                        int active_uv_index)
 {
-       g_use_osd_glsl = use_osd_glsl != 0;
+       g_active_uv_index = active_uv_index;
+       g_use_osd_glsl = (use_osd_glsl != 0);
 
        /* Update transformation matrices. */
        glGetFloatv(GL_PROJECTION_MATRIX, g_transform.projection_matrix);
@@ -594,12 +624,12 @@ static GLuint prepare_patchDraw(OpenSubdiv_GLMesh *gl_mesh,
 
                                location = glGetUniformLocation(program, "osd_active_uv_offset");
                                if (location != -1) {
-                                       /* TODO(sergey): Since we only store single UV channel
-                                        * we can always suuppose offset is 0.
-                                        *
-                                        * Ideally it should be active UV index times 2.
-                                        */
-                                       glUniform1i(location, 0);
+                                       if (gl_mesh->fvar_data->channel_offsets.size() > 0 && g_active_uv_index >= 0) {
+                                               glUniform1i(location,
+                                                           gl_mesh->fvar_data->channel_offsets[g_active_uv_index]);
+                                       } else {
+                                               glUniform1i(location, 0);
+                                       }
                                }
                        }
                }
@@ -756,13 +786,15 @@ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
        finish_patchDraw(fill_quads != 0);
 }
 
-void openSubdiv_osdGLAllocFVar(OpenSubdiv_GLMesh *gl_mesh,
+void openSubdiv_osdGLAllocFVar(OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+                               OpenSubdiv_GLMesh *gl_mesh,
                                const float *fvar_data)
 {
        GLMeshInterface *mesh =
                (GLMeshInterface *)(gl_mesh->descriptor);
        gl_mesh->fvar_data = OBJECT_GUARDED_NEW(OpenSubdiv_GLMeshFVarData);
-       gl_mesh->fvar_data->Create(mesh->GetFarPatchTable(),
+       gl_mesh->fvar_data->Create(topology_refiner->osd_refiner,
+                                  mesh->GetFarPatchTable(),
                                   2,
                                   fvar_data);
 }
index a825cffe7a07c31e7138c66eff32e32d0c3ba616..72f386bf246051e5091b41bd960967c0c6e78533 100644 (file)
@@ -205,7 +205,7 @@ void ccgSubSurf_checkTopologyChanged(CCGSubSurf *ss, struct DerivedMesh *dm);
 void ccgSubSurf_prepareTopologyRefiner(CCGSubSurf *ss, struct DerivedMesh *dm);
 
 /* Make sure GL mesh exists, up to date and ready to draw. */
-bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl);
+bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl, int active_uv_index);
 
 /* Draw given partitions of the GL mesh.
  *
index c715c1fa1eead7b2ff0e19302a916677231e33a2..9dc1a71adab9348d7b069e16d7d3e1df50b45d88 100644 (file)
@@ -215,7 +215,9 @@ static void ccgSubSurf__updateGLMeshCoords(CCGSubSurf *ss)
                                               ss->osd_num_coarse_coords);
 }
 
-bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl)
+bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss,
+                              bool use_osd_glsl,
+                              int active_uv_index)
 {
        int compute_type;
 
@@ -288,7 +290,7 @@ bool ccgSubSurf_prepareGLMesh(CCGSubSurf *ss, bool use_osd_glsl)
                ss->osd_coarse_coords_invalid = false;
        }
 
-       openSubdiv_osdGLMeshDisplayPrepare(use_osd_glsl);
+       openSubdiv_osdGLMeshDisplayPrepare(use_osd_glsl, active_uv_index);
 
        return true;
 }
index b9efe23a4b3d650a731875f4a745c644be4a92be..c6e20b995ba1e9b6ba7d7d2852c8e65968b64cb6 100644 (file)
@@ -1801,7 +1801,7 @@ static void ccgDM_drawEdges(DerivedMesh *dm, bool drawLooseEdges, bool drawAllEd
 #ifdef WITH_OPENSUBDIV
        if (ccgdm->useGpuBackend) {
                /* TODO(sergey): We currently only support all edges drawing. */
-               if (ccgSubSurf_prepareGLMesh(ccgdm->ss, true)) {
+               if (ccgSubSurf_prepareGLMesh(ccgdm->ss, true, -1)) {
                        ccgSubSurf_drawGLMesh(ccgdm->ss, false, -1, -1);
                }
                return;
@@ -2638,7 +2638,7 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
                int mat_nr = -1;
                bool draw_smooth = false;
                int start_draw_patch = -1, num_draw_patches = 0;
-               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, setMaterial != NULL) == false)) {
+               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, setMaterial != NULL, -1) == false)) {
                        return;
                }
                if (setMaterial == NULL) {
@@ -2750,7 +2750,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm,
                bool draw_smooth = false;
                int start_draw_patch = -1, num_draw_patches = 0;
                GPU_draw_update_fvar_offset(dm);
-               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, false) == false)) {
+               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, false, -1) == false)) {
                        return;
                }
                for (i = 0; i < num_base_faces; ++i) {
@@ -3193,7 +3193,7 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm,
                int new_matnr;
                bool draw_smooth;
                GPU_draw_update_fvar_offset(dm);
-               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true) == false)) {
+               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true, -1) == false)) {
                        return;
                }
                /* TODO(sergey): Single matierial currently. */
@@ -3401,7 +3401,7 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
 
 #ifdef WITH_OPENSUBDIV
        if (ccgdm->useGpuBackend) {
-               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true) == false)) {
+               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true, -1) == false)) {
                        return;
                }
                if (drawParams == NULL) {
@@ -3639,7 +3639,7 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm,
                         */
                        glColor3f(0.8f, 0.8f, 0.8f);
                }
-               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true) == false)) {
+               if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true, -1) == false)) {
                        return;
                }
                if (faceFlags) {
@@ -3838,7 +3838,7 @@ static void ccgDM_drawMappedEdges(DerivedMesh *dm,
 #ifdef WITH_OPENSUBDIV
        if (ccgdm->useGpuBackend) {
                /* TODO(sergey): Only draw edges from base mesh. */
-               if (ccgSubSurf_prepareGLMesh(ccgdm->ss, true)) {
+               if (ccgSubSurf_prepareGLMesh(ccgdm->ss, true, -1)) {
                        if (!setDrawOptions || (setDrawOptions(userData, 0) != DM_DRAW_OPTION_SKIP)) {
                                ccgSubSurf_drawGLMesh(ccgdm->ss, false, -1, -1);
                        }