GPU subdiv: smoothly interpolate orco layer

This uses the recently introduced evaluator's vertex
data to smoothly interpolate original coordinates instead
of using linear interpolation.

The orcos are interpolated at the same time as positions
and as such, the specific subdivision routine for the
orco extractor has been removed. The patch evaluation
shader uses a definition to enable code specific to
orco evaluation.

Since the orco layer may not have been requested on first
render, and since orco data is now stored in the OpenSubDiv
evaluator, the evaluator needs to be recreated if an
orco layer is suddenly available. For this, a callback
to check if the evaluator has the data was added. This is
added to the evaluator as the `Subdiv` cache stored in the
modifier is invalidated less often than the Mesh batch cache
and so leads to fewer evaluator recreations.

Differential Revision: https://developer.blender.org/D14999
This commit is contained in:
Kévin Dietrich 2022-05-22 09:19:55 +02:00
parent 45ed325443
commit 9d9f2f1a03
14 changed files with 197 additions and 77 deletions

View File

@ -103,6 +103,10 @@ class EvalOutputAPI::EvalOutput {
{
}
virtual void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer * /*src_buffer*/)
{
}
virtual void fillFVarPatchArraysBuffer(const int /*face_varying_channel*/,
OpenSubdiv_Buffer * /*patch_arrays_buffer*/)
{
@ -122,6 +126,11 @@ class EvalOutputAPI::EvalOutput {
OpenSubdiv_Buffer * /*src_buffer*/)
{
}
virtual bool hasVertexData() const
{
return false;
}
};
namespace {
@ -437,6 +446,11 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput {
return face_varying_evaluators_.size() != 0;
}
bool hasVertexData() const override
{
return src_vertex_data_ != nullptr;
}
void refine() override
{
// Evaluate vertex positions.
@ -599,6 +613,11 @@ class VolatileEvalOutput : public EvalOutputAPI::EvalOutput {
return src_data_;
}
SRC_VERTEX_BUFFER *getSrcVertexDataBuffer() const
{
return src_vertex_data_;
}
PATCH_TABLE *getPatchTable() const
{
return patch_table_;

View File

@ -86,6 +86,12 @@ void GpuEvalOutput::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer)
src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO());
}
void GpuEvalOutput::wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer)
{
GLVertexBuffer *vertex_buffer = getSrcVertexDataBuffer();
src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO());
}
void GpuEvalOutput::fillFVarPatchArraysBuffer(const int face_varying_channel,
OpenSubdiv_Buffer *patch_arrays_buffer)
{

View File

@ -52,6 +52,8 @@ class GpuEvalOutput : public VolatileEvalOutput<GLVertexBuffer,
void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) override;
void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer) override;
void fillFVarPatchArraysBuffer(const int face_varying_channel,
OpenSubdiv_Buffer *patch_arrays_buffer) override;

View File

@ -191,6 +191,12 @@ void wrapSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, struct OpenSubdiv_Buf
evaluator->impl->eval_output->wrapSrcBuffer(src_buffer);
}
void wrapSrcVertexDataBuffer(struct OpenSubdiv_Evaluator *evaluator,
struct OpenSubdiv_Buffer *src_buffer)
{
evaluator->impl->eval_output->wrapSrcVertexDataBuffer(src_buffer);
}
void fillFVarPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator,
const int face_varying_channel,
struct OpenSubdiv_Buffer *patch_array_buffer)
@ -220,6 +226,11 @@ void wrapFVarSrcBuffer(struct OpenSubdiv_Evaluator *evaluator,
evaluator->impl->eval_output->wrapFVarSrcBuffer(face_varying_channel, src_buffer);
}
bool hasVertexData(struct OpenSubdiv_Evaluator *evaluator)
{
return evaluator->impl->eval_output->hasVertexData();
}
void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
{
evaluator->setCoarsePositions = setCoarsePositions;
@ -246,11 +257,14 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
evaluator->wrapPatchIndexBuffer = wrapPatchIndexBuffer;
evaluator->wrapPatchParamBuffer = wrapPatchParamBuffer;
evaluator->wrapSrcBuffer = wrapSrcBuffer;
evaluator->wrapSrcVertexDataBuffer = wrapSrcVertexDataBuffer;
evaluator->fillFVarPatchArraysBuffer = fillFVarPatchArraysBuffer;
evaluator->wrapFVarPatchIndexBuffer = wrapFVarPatchIndexBuffer;
evaluator->wrapFVarPatchParamBuffer = wrapFVarPatchParamBuffer;
evaluator->wrapFVarSrcBuffer = wrapFVarSrcBuffer;
evaluator->hasVertexData = hasVertexData;
}
} // namespace

View File

@ -383,6 +383,11 @@ void EvalOutputAPI::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer)
implementation_->wrapSrcBuffer(src_buffer);
}
void EvalOutputAPI::wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer)
{
implementation_->wrapSrcVertexDataBuffer(src_buffer);
}
void EvalOutputAPI::fillFVarPatchArraysBuffer(const int face_varying_channel,
OpenSubdiv_Buffer *patch_arrays_buffer)
{
@ -407,6 +412,11 @@ void EvalOutputAPI::wrapFVarSrcBuffer(const int face_varying_channel,
implementation_->wrapFVarSrcBuffer(face_varying_channel, src_buffer);
}
bool EvalOutputAPI::hasVertexData() const
{
return implementation_->hasVertexData();
}
} // namespace opensubdiv
} // namespace blender

View File

@ -163,6 +163,9 @@ class EvalOutputAPI {
// Wrap the buffer used by OpenSubDiv for the source data with the given buffer.
void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer);
// Wrap the buffer used by OpenSubDiv for the extra source data with the given buffer.
void wrapSrcVertexDataBuffer(OpenSubdiv_Buffer *src_buffer);
// Copy the patch arrays buffer used by OpenSubDiv for the face varying channel with the given
// buffer.
void fillFVarPatchArraysBuffer(const int face_varying_channel,
@ -181,6 +184,9 @@ class EvalOutputAPI {
// Wrap thebuffer used by OpenSubDiv for the face varying channel with the given buffer.
void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer);
// Return true if source vertex data has been set.
bool hasVertexData() const;
protected:
PatchMap *patch_map_;
EvalOutput *implementation_;

View File

@ -195,6 +195,10 @@ typedef struct OpenSubdiv_Evaluator {
void (*wrapSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator,
struct OpenSubdiv_Buffer *src_buffer);
// Fill the given buffer with data from the evaluator's extra source buffer.
void (*wrapSrcVertexDataBuffer)(struct OpenSubdiv_Evaluator *evaluator,
struct OpenSubdiv_Buffer *src_buffer);
// Fill the given buffer with data from the evaluator's face varying patch array buffer.
void (*fillFVarPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator,
const int face_varying_channel,
@ -215,6 +219,9 @@ typedef struct OpenSubdiv_Evaluator {
const int face_varying_channel,
struct OpenSubdiv_Buffer *src_buffer);
// Return true if the evaluator has source vertex data set.
bool (*hasVertexData)(struct OpenSubdiv_Evaluator *evaluator);
// Implementation of the evaluator.
struct OpenSubdiv_EvaluatorImpl *impl;

View File

@ -793,7 +793,12 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
/* The order in which extractors are added to the list matters somewhat, as some buffers are
* reused when building others. */
EXTRACT_ADD_REQUESTED(ibo, tris);
EXTRACT_ADD_REQUESTED(vbo, pos_nor);
/* Orcos are extracted at the same time as positions. */
if (DRW_vbo_requested(mbuflist->vbo.pos_nor) || DRW_vbo_requested(mbuflist->vbo.orco)) {
extractors.append(&extract_pos_nor);
}
EXTRACT_ADD_REQUESTED(vbo, lnor);
for (int i = 0; i < GPU_MAX_ATTR; i++) {
EXTRACT_ADD_REQUESTED(vbo, attr[i]);
@ -843,7 +848,6 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
EXTRACT_ADD_REQUESTED(vbo, orco);
EXTRACT_ADD_REQUESTED(vbo, vcol);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);

View File

@ -69,6 +69,7 @@ enum {
SHADER_PATCH_EVALUATION,
SHADER_PATCH_EVALUATION_FVAR,
SHADER_PATCH_EVALUATION_FACE_DOTS,
SHADER_PATCH_EVALUATION_ORCO,
SHADER_COMP_CUSTOM_DATA_INTERP_1D,
SHADER_COMP_CUSTOM_DATA_INTERP_2D,
SHADER_COMP_CUSTOM_DATA_INTERP_3D,
@ -107,7 +108,8 @@ static const char *get_shader_code(int shader_type)
}
case SHADER_PATCH_EVALUATION:
case SHADER_PATCH_EVALUATION_FVAR:
case SHADER_PATCH_EVALUATION_FACE_DOTS: {
case SHADER_PATCH_EVALUATION_FACE_DOTS:
case SHADER_PATCH_EVALUATION_ORCO: {
return datatoc_common_subdiv_patch_evaluation_comp_glsl;
}
case SHADER_COMP_CUSTOM_DATA_INTERP_1D:
@ -163,6 +165,9 @@ static const char *get_shader_name(int shader_type)
case SHADER_PATCH_EVALUATION_FACE_DOTS: {
return "subdiv patch evaluation face dots";
}
case SHADER_PATCH_EVALUATION_ORCO: {
return "subdiv patch evaluation orco";
}
case SHADER_COMP_CUSTOM_DATA_INTERP_1D: {
return "subdiv custom data interp 1D";
}
@ -206,6 +211,12 @@ static GPUShader *get_patch_evaluation_shader(int shader_type)
"#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n"
"#define FDOTS_EVALUATION\n";
}
else if (shader_type == SHADER_PATCH_EVALUATION_ORCO) {
defines =
"#define OSD_PATCH_BASIS_GLSL\n"
"#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n"
"#define ORCO_EVALUATION\n";
}
else {
defines =
"#define OSD_PATCH_BASIS_GLSL\n"
@ -236,7 +247,8 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines)
if (ELEM(shader_type,
SHADER_PATCH_EVALUATION,
SHADER_PATCH_EVALUATION_FVAR,
SHADER_PATCH_EVALUATION_FACE_DOTS)) {
SHADER_PATCH_EVALUATION_FACE_DOTS,
SHADER_PATCH_EVALUATION_ORCO)) {
return get_patch_evaluation_shader(shader_type);
}
if (g_subdiv_shaders[shader_type] == nullptr) {
@ -710,6 +722,23 @@ static DRWSubdivCache *mesh_batch_cache_ensure_subdiv_cache(MeshBatchCache *mbc)
return subdiv_cache;
}
static void draw_subdiv_invalidate_evaluator_for_orco(Subdiv *subdiv, Mesh *mesh)
{
const bool has_orco = CustomData_has_layer(&mesh->vdata, CD_ORCO);
if (has_orco && subdiv->evaluator && !subdiv->evaluator->hasVertexData(subdiv->evaluator)) {
/* If we suddenly have/need original coordinates, recreate the evaluator if the extra
* source was not created yet. The refiner also has to be recreated as refinement for source
* and vertex data is done only once. */
openSubdiv_deleteEvaluator(subdiv->evaluator);
subdiv->evaluator = nullptr;
if (subdiv->topology_refiner != nullptr) {
openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner);
subdiv->topology_refiner = nullptr;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1230,7 +1259,9 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache,
GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1);
}
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor)
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
GPUVertBuf *pos_nor,
GPUVertBuf *orco)
{
if (!draw_subdiv_cache_need_polygon_data(cache)) {
/* Happens on meshes with only loose geometry. */
@ -1245,6 +1276,14 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no
get_subdiv_vertex_format());
evaluator->wrapSrcBuffer(evaluator, &src_buffer_interface);
GPUVertBuf *src_extra_buffer = nullptr;
if (orco) {
OpenSubdiv_Buffer src_extra_buffer_interface;
src_extra_buffer = create_buffer_and_interface(&src_extra_buffer_interface,
get_subdiv_vertex_format());
evaluator->wrapSrcVertexDataBuffer(evaluator, &src_extra_buffer_interface);
}
OpenSubdiv_Buffer patch_arrays_buffer_interface;
GPUVertBuf *patch_arrays_buffer = create_buffer_and_interface(&patch_arrays_buffer_interface,
get_patch_array_format());
@ -1260,7 +1299,8 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no
get_patch_param_format());
evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface);
GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION);
GPUShader *shader = get_patch_evaluation_shader(orco ? SHADER_PATCH_EVALUATION_ORCO :
SHADER_PATCH_EVALUATION);
GPU_shader_bind(shader);
int binding_point = 0;
@ -1273,6 +1313,10 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no
GPU_vertbuf_bind_as_ssbo(patch_index_buffer, binding_point++);
GPU_vertbuf_bind_as_ssbo(patch_param_buffer, binding_point++);
GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++);
if (orco) {
GPU_vertbuf_bind_as_ssbo(src_extra_buffer, binding_point++);
GPU_vertbuf_bind_as_ssbo(orco, binding_point++);
}
BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS);
drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_quads);
@ -1289,6 +1333,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_no
GPU_vertbuf_discard(patch_param_buffer);
GPU_vertbuf_discard(patch_arrays_buffer);
GPU_vertbuf_discard(src_buffer);
GPU_VERTBUF_DISCARD_SAFE(src_extra_buffer);
}
void draw_subdiv_extract_uvs(const DRWSubdivCache *cache,
@ -1935,6 +1980,8 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
return false;
}
draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval);
if (!BKE_subdiv_eval_begin_from_mesh(
subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) {
/* This could happen in two situations:

View File

@ -235,7 +235,9 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache,
GPUVertBuf *src_custom_normals,
GPUVertBuf *pos_nor);
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor);
void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache,
struct GPUVertBuf *pos_nor,
struct GPUVertBuf *orco);
void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache,
struct GPUVertBuf *src_data,

View File

@ -236,7 +236,7 @@ static void extract_edituv_stretch_angle_init_subdiv(const DRWSubdivCache *subdi
draw_subdiv_get_pos_nor_format(),
subdiv_cache->num_subdiv_loops + loose_geom.loop_len);
draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor);
draw_subdiv_extract_pos_nor(subdiv_cache, pos_nor, nullptr);
}
/* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active

View File

@ -7,8 +7,6 @@
#include "extract_mesh.h"
#include "draw_subdivision.h"
namespace blender::draw {
/* ---------------------------------------------------------------------- */
@ -79,77 +77,12 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
}
}
static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache,
const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buffer,
void *UNUSED(data))
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
* attributes. This is a substantial waste of video-ram and should be done another way.
* Unfortunately, at the time of writing, I did not found any other "non disruptive"
* alternative. */
GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops);
GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc();
/* Dynamic as we upload and interpolate layers one at a time. */
GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC);
GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len);
float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo));
CustomData *cd_vdata = &mr->me->vdata;
const float(*orco)[3] = static_cast<const float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO));
if (mr->extract_type == MR_EXTRACT_MESH) {
const MLoop *mloop = mr->mloop;
const MPoly *mp = mr->mpoly;
int ml_index = 0;
for (int i = 0; i < mr->poly_len; i++, mp++) {
const MLoop *ml = &mloop[mp->loopstart];
for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) {
float *loop_orco = coarse_vbo_data[ml_index];
copy_v3_v3(loop_orco, orco[ml->v]);
loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
}
}
}
else {
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) {
BMLoop *l_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
const int l_index = BM_elem_index_get(l_iter);
float *loop_orco = coarse_vbo_data[l_index];
copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]);
loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
} while ((l_iter = l_iter->next) != l_first);
}
}
draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false);
GPU_vertbuf_discard(coarse_vbo);
}
constexpr MeshExtract create_extractor_orco()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_orco_init;
extractor.iter_poly_bm = extract_orco_iter_poly_bm;
extractor.iter_poly_mesh = extract_orco_iter_poly_mesh;
extractor.init_subdiv = extract_orco_init_subdiv;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Orco_Data);
extractor.use_threading = true;

View File

@ -201,7 +201,7 @@ static GPUVertFormat *get_custom_normals_format()
static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
const MeshRenderData *UNUSED(mr),
struct MeshBatchCache *UNUSED(cache),
struct MeshBatchCache *cache,
void *buffer,
void *UNUSED(data))
{
@ -216,7 +216,21 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache,
return;
}
draw_subdiv_extract_pos_nor(subdiv_cache, vbo);
GPUVertBuf *orco_vbo = cache->final.buff.vbo.orco;
if (orco_vbo) {
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
* attributes. This is a substantial waste of video-ram and should be done another way.
* Unfortunately, at the time of writing, I did not found any other "non disruptive"
* alternative. */
GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_build_on_device(orco_vbo, &format, subdiv_cache->num_subdiv_loops);
}
draw_subdiv_extract_pos_nor(subdiv_cache, vbo, orco_vbo);
if (subdiv_cache->use_custom_loop_normals) {
Mesh *coarse_mesh = subdiv_cache->mesh;

View File

@ -89,6 +89,16 @@ layout(std430, binding = 8) writeonly buffer outputVertexData
{
PosNorLoop output_verts[];
};
# if defined(ORCO_EVALUATION)
layout(std430, binding = 9) buffer src_extra_buffer
{
float srcExtraVertexBuffer[];
};
layout(std430, binding = 10) writeonly buffer outputOrcoData
{
vec4 output_orcos[];
};
# endif
#endif
vec2 read_vec2(int index)
@ -108,6 +118,17 @@ vec3 read_vec3(int index)
return result;
}
#if defined(ORCO_EVALUATION)
vec3 read_vec3_extra(int index)
{
vec3 result;
result.x = srcExtraVertexBuffer[index * 3];
result.y = srcExtraVertexBuffer[index * 3 + 1];
result.z = srcExtraVertexBuffer[index * 3 + 2];
return result;
}
#endif
OsdPatchArray GetPatchArray(int arrayIndex)
{
return patchArrayBuffer[arrayIndex];
@ -290,6 +311,31 @@ void evaluate_patches_limits(
dv += src_vertex * wDv[cv];
}
}
# if defined(ORCO_EVALUATION)
/* Evaluate the patches limits from the extra source vertex buffer. */
void evaluate_patches_limits_extra(int patch_index, float u, float v, inout vec3 dst)
{
OsdPatchCoord coord = GetPatchCoord(patch_index, u, v);
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
OsdPatchParam param = GetPatchParam(coord.patchIndex);
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
int nPoints = OsdEvaluatePatchBasis(
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
for (int cv = 0; cv < nPoints; ++cv) {
int index = patchIndexBuffer[indexBase + cv];
vec3 src_vertex = read_vec3_extra(index);
dst += src_vertex * wP[cv];
}
}
# endif
#endif
/* ------------------------------------------------------------------------------
@ -407,6 +453,16 @@ void main()
set_vertex_pos(vertex_data, pos);
set_vertex_nor(vertex_data, nor, flag);
output_verts[loop_index] = vertex_data;
# if defined(ORCO_EVALUATION)
pos = vec3(0.0);
evaluate_patches_limits_extra(patch_co.patch_index, uv.x, uv.y, pos);
/* Set w = 0.0 to indicate that this is not a generic attribute.
* See comments in `extract_mesh_vbo_orco.cc`. */
vec4 orco_data = vec4(pos, 0.0);
output_orcos[loop_index] = orco_data;
# endif
}
}
#endif