diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-07-19 10:28:54 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-07-20 15:16:38 +0300 |
commit | f0f60d775def20ff3a3699704b33c66712d50c6a (patch) | |
tree | c9c4ec927a9eb447e1a0f9753a74eb99c470d5b5 /intern | |
parent | 9a0634a253421c02dbb6d2864db49e4d382ea2a3 (diff) |
OpenSubdiv: Initial work to support UV maps in textured OSD viewport
A bit work in progress, currently the following limitations:
- Texture shading only, Material shading will come later
- No UVs subdivision yet
- Always uses active UV and currently changing active UV will
not properly update the viewport.
Well, need to start somewhere :)
Diffstat (limited to 'intern')
-rw-r--r-- | intern/opensubdiv/opensubdiv_capi.cc | 73 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_capi.h | 13 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_converter.cc | 72 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_converter_capi.h | 8 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_gpu_capi.cc | 129 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_topology_refiner.h | 6 |
6 files changed, 264 insertions, 37 deletions
diff --git a/intern/opensubdiv/opensubdiv_capi.cc b/intern/opensubdiv/opensubdiv_capi.cc index 0463982fdf6..e04b23725b1 100644 --- a/intern/opensubdiv/opensubdiv_capi.cc +++ b/intern/opensubdiv/opensubdiv_capi.cc @@ -67,6 +67,7 @@ #include <opensubdiv/osd/glPatchTable.h> #include <opensubdiv/far/stencilTable.h> +#include <opensubdiv/far/primvarRefiner.h> #include "opensubdiv_intern.h" #include "opensubdiv_topology_refiner.h" @@ -143,11 +144,69 @@ typedef Mesh<GLVertexBuffer, GLPatchTable> OsdGLSLComputeMesh; #endif +namespace { + +struct FVarVertex { + float u, v; + void Clear() { + u = v = 0.0f; + } + void AddWithWeight(FVarVertex const & src, float weight) { + u += weight * src.u; + v += weight * src.v; + } +}; + +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; + + 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 primvarRefiner(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], uvs.size() * 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); + primvarRefiner.InterpolateFaceVarying(level, src, dst, channel); + src = dst; + } + FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[0]); + primvarRefiner.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], uvs.size() * sizeof(float)); + for (int level = 1; level <= max_level; ++level) { + FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel); + primvarRefiner.InterpolateFaceVarying(level, src, dst, channel); + src = dst; + } + } +} + +} // namespace + struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( OpenSubdiv_TopologyRefinerDescr *topology_refiner, int evaluator_type, - int level, - int /*subdivide_uvs*/) + int level) { using OpenSubdiv::Far::TopologyRefiner; @@ -213,11 +272,21 @@ struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh; gl_mesh->topology_refiner = topology_refiner; + 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]); + } + else { + gl_mesh->fvar_data = NULL; + } + return gl_mesh; } void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh) { + openSubdiv_osdGLDestroyFVar(gl_mesh); switch (gl_mesh->evaluator_type) { #define CHECK_EVALUATOR_TYPE(type, class) \ case OPENSUBDIV_EVALUATOR_ ## type: \ diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h index b40505b197d..0410083304e 100644 --- a/intern/opensubdiv/opensubdiv_capi.h +++ b/intern/opensubdiv/opensubdiv_capi.h @@ -32,16 +32,19 @@ extern "C" { // Types declaration. struct OpenSubdiv_GLMesh; +struct OpenSubdiv_GLMeshFVarData; struct OpenSubdiv_TopologyRefinerDescr; typedef struct OpenSubdiv_GLMesh OpenSubdiv_GLMesh; #ifdef __cplusplus struct OpenSubdiv_GLMeshDescr; + typedef struct OpenSubdiv_GLMesh { int evaluator_type; OpenSubdiv_GLMeshDescr *descriptor; OpenSubdiv_TopologyRefinerDescr *topology_refiner; + OpenSubdiv_GLMeshFVarData *fvar_data; } OpenSubdiv_GLMesh; #endif @@ -66,8 +69,7 @@ enum { OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner( struct OpenSubdiv_TopologyRefinerDescr *topology_refiner, int evaluator_type, - int level, - int subdivide_uvs); + int level); void openSubdiv_deleteOsdGLMesh(OpenSubdiv_GLMesh *gl_mesh); unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer( @@ -129,8 +131,7 @@ 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, - int active_uv_index); +void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl); /* Draw specified patches. */ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, @@ -138,6 +139,10 @@ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, int start_patch, int num_patches); +void openSubdiv_osdGLAllocFVar(OpenSubdiv_GLMesh *gl_mesh, + const float *fvar_data); +void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh); + /* ** Utility functions ** */ int openSubdiv_supportGPUDisplay(void); int openSubdiv_getAvailableEvaluators(void); diff --git a/intern/opensubdiv/opensubdiv_converter.cc b/intern/opensubdiv/opensubdiv_converter.cc index e718d6b4dc8..e6c8985e947 100644 --- a/intern/opensubdiv/opensubdiv_converter.cc +++ b/intern/opensubdiv/opensubdiv_converter.cc @@ -488,6 +488,39 @@ inline void TopologyRefinerFactory<OpenSubdiv_Converter>::reportInvalidTopology( printf("OpenSubdiv Error: %s\n", msg); } +template <> +inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignFaceVaryingTopology( + TopologyRefiner& refiner, + const OpenSubdiv_Converter& conv) +{ + if (conv.get_num_uv_layers(&conv) <= 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; + } + } + return true; +} + } /* namespace Far */ } /* namespace OPENSUBDIV_VERSION */ } /* namespace OpenSubdiv */ @@ -508,6 +541,33 @@ 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( @@ -536,6 +596,18 @@ struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr( TopologyRefinerFactory<OpenSubdiv_Converter>::Create( *converter, 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 1f09fa074d8..47c8dab49de 100644 --- a/intern/opensubdiv/opensubdiv_converter_capi.h +++ b/intern/opensubdiv/opensubdiv_converter_capi.h @@ -83,6 +83,14 @@ typedef struct OpenSubdiv_Converter { int vert, int *vert_faces); + /* 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 (*free_user_data)(const OpenSubdiv_Converter *converter); void *user_data; } OpenSubdiv_Converter; diff --git a/intern/opensubdiv/opensubdiv_gpu_capi.cc b/intern/opensubdiv/opensubdiv_gpu_capi.cc index 63cf390276f..84984e8bd7e 100644 --- a/intern/opensubdiv/opensubdiv_gpu_capi.cc +++ b/intern/opensubdiv/opensubdiv_gpu_capi.cc @@ -42,6 +42,10 @@ #include <opensubdiv/osd/cpuGLVertexBuffer.h> #include <opensubdiv/osd/cpuEvaluator.h> +#include "MEM_guardedalloc.h" + +#include "opensubdiv_capi.h" + using OpenSubdiv::Osd::GLMeshInterface; extern "C" char datatoc_gpu_shader_opensubd_display_glsl[]; @@ -78,7 +82,6 @@ typedef struct Transform { } Transform; static bool g_use_osd_glsl = false; -static int g_active_uv_index = -1; static GLuint g_flat_fill_solid_program = 0; static GLuint g_flat_fill_texture2d_program = 0; @@ -90,6 +93,59 @@ static GLuint g_lighting_ub = 0; static Lighting g_lighting_data; static Transform g_transform; +struct OpenSubdiv_GLMeshFVarData +{ + OpenSubdiv_GLMeshFVarData() : + texture_buffer(0) { + } + + ~OpenSubdiv_GLMeshFVarData() + { + Release(); + } + + void Release() + { + if (texture_buffer) { + glDeleteTextures(1, &texture_buffer); + } + texture_buffer = 0; + } + + void Create(const OpenSubdiv::Far::PatchTable *patch_table, + int fvarWidth, + const float *fvar_src_data) + { + Release(); + OpenSubdiv::Far::ConstIndexArray indices = patch_table->GetFVarValues(); + + // expand fvardata to per-patch array + std::vector<float> data; + data.reserve(indices.size() * fvarWidth); + + for (int fvert = 0; fvert < (int)indices.size(); ++fvert) { + int index = indices[fvert] * fvarWidth; + for (int i = 0; i < fvarWidth; ++i) { + data.push_back(fvar_src_data[index++]); + } + } + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, data.size()*sizeof(float), + &data[0], GL_STATIC_DRAW); + + glGenTextures(1, &texture_buffer); + glBindTexture(GL_TEXTURE_BUFFER, texture_buffer); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, buffer); + glBindTexture(GL_TEXTURE_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDeleteBuffers(1, &buffer); + } + GLuint texture_buffer; +}; + /* TODO(sergey): This is actually duplicated code from BLI. */ namespace { void copy_m3_m3(float m1[3][3], float m2[3][3]) @@ -307,8 +363,7 @@ GLuint linkProgram(const char *version, const char *define) return program; } -void bindProgram(GLMeshInterface * /*mesh*/, - int program) +void bindProgram(OpenSubdiv_GLMesh *gl_mesh, int program) { glUseProgram(program); @@ -352,23 +407,16 @@ void bindProgram(GLMeshInterface * /*mesh*/, glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color); } - /* TODO(sergey): Bring face varying back. */ -#if 0 /* Face-vertex data */ - if (mesh->GetDrawContext()->GetFvarDataTextureBuffer()) { + if (gl_mesh->fvar_data != NULL && gl_mesh->fvar_data->texture_buffer) { glActiveTexture(GL_TEXTURE31); - glBindTexture(GL_TEXTURE_BUFFER, - mesh->GetDrawContext()->GetFvarDataTextureBuffer()); + glBindTexture(GL_TEXTURE_BUFFER, gl_mesh->fvar_data->texture_buffer); glActiveTexture(GL_TEXTURE0); } -#endif - - /* TODO(sergey): Bring face varying back. */ - glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), - 0/* * mesh->GetFVarCount()*/); - glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), - g_active_uv_index * 2); + /* See notes below about why we use such values. */ + glUniform1i(glGetUniformLocation(program, "osd_fvar_count"), 2); + glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"), 0); } } /* namespace */ @@ -455,11 +503,9 @@ void openSubdiv_osdGLDisplayDeinit(void) } } -void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl, - int active_uv_index) +void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl) { g_use_osd_glsl = use_osd_glsl != 0; - g_active_uv_index = active_uv_index; /* Update transformation matrices. */ glGetFloatv(GL_PROJECTION_MATRIX, g_transform.projection_matrix); @@ -516,7 +562,7 @@ void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl, } } -static GLuint prepare_patchDraw(GLMeshInterface *mesh, +static GLuint prepare_patchDraw(OpenSubdiv_GLMesh *gl_mesh, bool fill_quads) { GLint program = 0; @@ -531,28 +577,31 @@ static GLuint prepare_patchDraw(GLMeshInterface *mesh, glUniform1i(location, model == GL_FLAT); } - /* TODO(sergey): Bring this back. */ -#if 0 /* Face-vertex data */ - if (mesh->GetDrawContext()->GetFvarDataTextureBuffer()) { + if (gl_mesh->fvar_data != NULL && + gl_mesh->fvar_data->texture_buffer) + { glActiveTexture(GL_TEXTURE31); glBindTexture(GL_TEXTURE_BUFFER, - mesh->GetDrawContext()->GetFvarDataTextureBuffer()); + gl_mesh->fvar_data->texture_buffer); glActiveTexture(GL_TEXTURE0); GLint location = glGetUniformLocation(program, "osd_fvar_count"); if (location != -1) { - glUniform1i(location, mesh->GetFVarCount()); + /* TODO(sergey): This is width of FVar data, which happened to be 2. */ + glUniform1i(location, 2); } location = glGetUniformLocation(program, "osd_active_uv_offset"); if (location != -1) { - glUniform1i(location, - g_active_uv_index * 2); + /* 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); } } -#endif - } return program; } @@ -584,7 +633,7 @@ static GLuint prepare_patchDraw(GLMeshInterface *mesh, program = g_wireframe_program; } - bindProgram(mesh, program); + bindProgram(gl_mesh, program); return program; } @@ -645,7 +694,7 @@ static void draw_partition_patches_range(GLMeshInterface *mesh, const int num_draw_patches = std::min(num_remained_patches, num_block_patches - start_draw_patch); perform_drawElements(program, - i, + i + start_draw_patch, num_draw_patches * num_control_verts, patch.GetIndexBase() + start_draw_patch * num_control_verts); num_remained_patches -= num_draw_patches; @@ -691,7 +740,7 @@ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, } /* Setup GLSL/OpenGL to draw patches in current context. */ - GLuint program = prepare_patchDraw(mesh, fill_quads != 0); + GLuint program = prepare_patchDraw(gl_mesh, fill_quads != 0); if (start_patch != -1) { draw_partition_patches_range(mesh, @@ -706,3 +755,21 @@ void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, /* Finish patch drawing by restoring all changes to the OpenGL context. */ finish_patchDraw(fill_quads != 0); } + +void openSubdiv_osdGLAllocFVar(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(), + 2, + fvar_data); +} + +void openSubdiv_osdGLDestroyFVar(OpenSubdiv_GLMesh *gl_mesh) +{ + if (gl_mesh->fvar_data != NULL) { + OBJECT_GUARDED_DELETE(gl_mesh->fvar_data, OpenSubdiv_GLMeshFVarData); + } +} diff --git a/intern/opensubdiv/opensubdiv_topology_refiner.h b/intern/opensubdiv/opensubdiv_topology_refiner.h index 5c299ac5df5..b00f6a54201 100644 --- a/intern/opensubdiv/opensubdiv_topology_refiner.h +++ b/intern/opensubdiv/opensubdiv_topology_refiner.h @@ -30,6 +30,12 @@ typedef struct OpenSubdiv_TopologyRefinerDescr { OpenSubdiv::Far::TopologyRefiner *osd_refiner; + + /* TODO(sergey): For now only, need to find better place + * after revisiting whole OSD drawing pipeline and Blender + * integration. + */ + std::vector<float> uvs; } OpenSubdiv_TopologyRefinerDescr; #endif /* __OPENSUBDIV_TOPOLOGY_REFINER_H__ */ |