OpenSubdiv: Lay down fundamentals to support multiple UV maps

This commit is contained in:
Sergey Sharybin 2016-07-22 14:46:13 +02:00
parent 98970f71fe
commit 48c4b700dc
7 changed files with 112 additions and 72 deletions

View 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;
}
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;
}
}
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;
}
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;

View 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);

View 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;
}

View 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);
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);
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++]);
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);
}

View 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.
*

View 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;
}

View 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);
}