Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2020-03-26 23:03:42 +0300
committerJulian Eisel <julian@blender.org>2020-03-26 23:18:45 +0300
commitc94b6209861ca7cc3985b53474feed7d94c0221a (patch)
tree752054f0dca1338cda5cf8ad4f6d18573fcca3b9 /source/blender/blenkernel/intern/multires.c
parent357ed79cb93f9d655501a828c6cddd68282de62d (diff)
parentafb1a64ccb81b7ed792f64151986f40f53af8da5 (diff)
Merge branch 'master' into wm-drag-drop-rewrite
Diffstat (limited to 'source/blender/blenkernel/intern/multires.c')
-rw-r--r--source/blender/blenkernel/intern/multires.c381
1 files changed, 173 insertions, 208 deletions
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 23fa8dd60d5..b40dfcd3b7f 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -34,22 +34,22 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
#include "BLI_task.h"
+#include "BLI_utildefines.h"
-#include "BKE_pbvh.h"
#include "BKE_ccg.h"
#include "BKE_cdderivedmesh.h"
+#include "BKE_editmesh.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_paint.h"
+#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subsurf.h"
-#include "BKE_editmesh.h"
#include "BKE_object.h"
@@ -57,6 +57,8 @@
#include "DEG_depsgraph_query.h"
+#include "multires_reshape.h"
+
#include <math.h>
#include <string.h>
@@ -88,9 +90,14 @@ void multires_customdata_delete(Mesh *me)
* as non-external for further free-ing, so zero element count
* looks safer than em->totface */
CustomData_external_remove(&em->bm->ldata, &me->id, CD_MDISPS, 0);
- BM_data_layer_free(em->bm, &em->bm->ldata, CD_MDISPS);
- BM_data_layer_free(em->bm, &em->bm->ldata, CD_GRID_PAINT_MASK);
+ if (CustomData_has_layer(&em->bm->ldata, CD_MDISPS)) {
+ BM_data_layer_free(em->bm, &em->bm->ldata, CD_MDISPS);
+ }
+
+ if (CustomData_has_layer(&em->bm->ldata, CD_GRID_PAINT_MASK)) {
+ BM_data_layer_free(em->bm, &em->bm->ldata, CD_GRID_PAINT_MASK);
+ }
}
else {
CustomData_external_remove(&me->ldata, &me->id, CD_MDISPS, me->totloop);
@@ -121,7 +128,7 @@ static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden,
return MEM_dupallocN(lo_hidden);
}
- subd = BLI_BITMAP_NEW(SQUARE(hi_gridsize), "MDisps.hidden upsample");
+ subd = BLI_BITMAP_NEW(square_i(hi_gridsize), "MDisps.hidden upsample");
factor = BKE_ccg_factor(lo_level, hi_level);
offset = 1 << (hi_level - lo_level - 1);
@@ -179,7 +186,7 @@ static BLI_bitmap *multires_mdisps_downsample_hidden(BLI_bitmap *old_hidden,
BLI_assert(new_level <= old_level);
factor = BKE_ccg_factor(new_level, old_level);
- new_hidden = BLI_BITMAP_NEW(SQUARE(new_gridsize), "downsample hidden");
+ new_hidden = BLI_BITMAP_NEW(square_i(new_gridsize), "downsample hidden");
for (y = 0; y < new_gridsize; y++) {
for (x = 0; x < new_gridsize; x++) {
@@ -238,7 +245,7 @@ static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level)
{
MDisps *mdisps = CustomData_add_layer(&me->ldata, CD_MDISPS, CD_CALLOC, NULL, me->totloop);
int gridsize = BKE_ccg_gridsize(level);
- int gridarea = SQUARE(gridsize);
+ int gridarea = square_i(gridsize);
int i, j;
for (i = 0; i < me->totpoly; i++) {
@@ -269,15 +276,16 @@ static MDisps *multires_mdisps_initialize_hidden(Mesh *me, int level)
}
Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph,
- Scene *scene,
- MultiresModifierData *mmd,
- Object *ob)
+ Object *object,
+ MultiresModifierData *mmd)
{
- Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
- Mesh *deformed_mesh = mesh_get_eval_deform(depsgraph, scene, ob_eval, &CD_MASK_BAREMESH);
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Mesh *deformed_mesh = mesh_get_eval_deform(
+ depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
ModifierEvalContext modifier_ctx = {
.depsgraph = depsgraph,
- .object = ob_eval,
+ .object = object_eval,
.flag = MOD_APPLY_USECACHE | MOD_APPLY_IGNORE_SIMPLIFY,
};
@@ -290,6 +298,57 @@ Mesh *BKE_multires_create_mesh(struct Depsgraph *depsgraph,
return result;
}
+float (*BKE_multires_create_deformed_base_mesh_vert_coords(struct Depsgraph *depsgraph,
+ struct Object *object,
+ struct MultiresModifierData *mmd,
+ int *r_num_deformed_verts))[3]
+{
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
+
+ Object object_for_eval = *object_eval;
+ object_for_eval.data = object->data;
+
+ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ ModifierEvalContext mesh_eval_context = {depsgraph, &object_for_eval, 0};
+ if (use_render) {
+ mesh_eval_context.flag |= MOD_APPLY_RENDER;
+ }
+ const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
+
+ VirtualModifierData virtual_modifier_data;
+ ModifierData *first_md = modifiers_getVirtualModifierList(&object_for_eval,
+ &virtual_modifier_data);
+
+ Mesh *base_mesh = object->data;
+
+ int num_deformed_verts;
+ float(*deformed_verts)[3] = BKE_mesh_vert_coords_alloc(base_mesh, &num_deformed_verts);
+
+ for (ModifierData *md = first_md; md != NULL; md = md->next) {
+ const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+
+ if (md == &mmd->modifier) {
+ break;
+ }
+
+ if (!modifier_isEnabled(scene_eval, md, required_mode)) {
+ continue;
+ }
+
+ if (mti->type != eModifierTypeType_OnlyDeform) {
+ break;
+ }
+
+ modwrap_deformVerts(md, &mesh_eval_context, base_mesh, deformed_verts, num_deformed_verts);
+ }
+
+ if (r_num_deformed_verts != NULL) {
+ *r_num_deformed_verts = num_deformed_verts;
+ }
+ return deformed_verts;
+}
+
MultiresModifierData *find_multires_modifier_before(Scene *scene, ModifierData *lastmd)
{
ModifierData *md;
@@ -406,47 +465,66 @@ void multires_mark_as_modified(Depsgraph *depsgraph, Object *object, MultiresMod
multires_ccg_mark_as_modified(subdiv_ccg, flags);
}
-void multires_flush_sculpt_updates(Object *ob)
+void multires_flush_sculpt_updates(Object *object)
{
- if (ob && ob->sculpt && ob->sculpt->pbvh != NULL) {
- SculptSession *sculpt_session = ob->sculpt;
- if (BKE_pbvh_type(sculpt_session->pbvh) == PBVH_GRIDS) {
- Mesh *mesh = ob->data;
- multiresModifier_reshapeFromCCG(
- sculpt_session->multires->totlvl, mesh, sculpt_session->subdiv_ccg);
- }
+ if (object == NULL || object->sculpt == NULL || object->sculpt->pbvh == NULL) {
+ return;
+ }
+
+ SculptSession *sculpt_session = object->sculpt;
+ if (BKE_pbvh_type(sculpt_session->pbvh) != PBVH_GRIDS || sculpt_session->multires == NULL) {
+ return;
}
+
+ SubdivCCG *subdiv_ccg = sculpt_session->subdiv_ccg;
+ if (subdiv_ccg == NULL) {
+ return;
+ }
+
+ if (!subdiv_ccg->dirty.coords && !subdiv_ccg->dirty.hidden) {
+ return;
+ }
+
+ Mesh *mesh = object->data;
+ multiresModifier_reshapeFromCCG(
+ sculpt_session->multires->totlvl, mesh, sculpt_session->subdiv_ccg);
+
+ subdiv_ccg->dirty.coords = false;
+ subdiv_ccg->dirty.hidden = false;
}
-void multires_force_sculpt_rebuild(Object *ob)
+void multires_force_sculpt_rebuild(Object *object)
{
- multires_flush_sculpt_updates(ob);
+ multires_flush_sculpt_updates(object);
- if (ob && ob->sculpt) {
- SculptSession *ss = ob->sculpt;
- if (ss->pbvh) {
- BKE_pbvh_free(ss->pbvh);
- ob->sculpt->pbvh = NULL;
- }
+ if (object == NULL || object->sculpt == NULL) {
+ return;
+ }
- if (ss->pmap) {
- MEM_freeN(ss->pmap);
- ss->pmap = NULL;
- }
+ SculptSession *ss = object->sculpt;
- if (ss->pmap_mem) {
- MEM_freeN(ss->pmap_mem);
- ss->pmap_mem = NULL;
- }
+ if (ss->pbvh != NULL) {
+ BKE_pbvh_free(ss->pbvh);
+ object->sculpt->pbvh = NULL;
+ }
+
+ if (ss->pmap != NULL) {
+ MEM_freeN(ss->pmap);
+ ss->pmap = NULL;
+ }
+
+ if (ss->pmap_mem != NULL) {
+ MEM_freeN(ss->pmap_mem);
+ ss->pmap_mem = NULL;
}
}
-void multires_force_external_reload(Object *ob)
+void multires_force_external_reload(Object *object)
{
- Mesh *me = BKE_mesh_from_object(ob);
+ Mesh *mesh = BKE_mesh_from_object(object);
- CustomData_external_reload(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop);
- multires_force_sculpt_rebuild(ob);
+ CustomData_external_reload(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop);
+ multires_force_sculpt_rebuild(object);
}
/* reset the multires levels to match the number of mdisps */
@@ -603,7 +681,7 @@ static void multires_grid_paint_mask_downsample(GridPaintMask *gpm, int level)
if (level < gpm->level) {
int gridsize = BKE_ccg_gridsize(level);
float *data = MEM_calloc_arrayN(
- SQUARE(gridsize), sizeof(float), "multires_grid_paint_mask_downsample");
+ square_i(gridsize), sizeof(float), "multires_grid_paint_mask_downsample");
int x, y;
for (y = 0; y < gridsize; y++) {
@@ -626,7 +704,7 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
GridPaintMask *gpm;
multires_set_tot_mdisps(me, mmd->totlvl);
- CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop);
+ multiresModifier_ensure_external_read(me, mmd);
mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
gpm = CustomData_get_layer(&me->ldata, CD_GRID_PAINT_MASK);
@@ -692,7 +770,7 @@ void multiresModifier_del_levels(MultiresModifierData *mmd,
MDisps *mdisps;
multires_set_tot_mdisps(me, mmd->totlvl);
- CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop);
+ multiresModifier_ensure_external_read(me, mmd);
mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
multires_force_sculpt_rebuild(ob);
@@ -770,158 +848,7 @@ static DerivedMesh *subsurf_dm_create_local(Scene *scene,
return subsurf_make_derived_from_derived(dm, &smd, scene, NULL, flags);
}
-/* assumes no is normalized; return value's sign is negative if v is on
- * the other side of the plane */
-static float v3_dist_from_plane(float v[3], float center[3], float no[3])
-{
- float s[3];
- sub_v3_v3v3(s, v, center);
- return dot_v3v3(s, no);
-}
-
-void multiresModifier_base_apply(MultiresModifierData *mmd, Scene *scene, Object *ob)
-{
- DerivedMesh *cddm, *dispdm, *origdm;
- Mesh *me;
- const MeshElemMap *pmap;
- float(*origco)[3];
- int i, j, k, offset, totlvl;
-
- multires_force_sculpt_rebuild(ob);
-
- me = BKE_mesh_from_object(ob);
- totlvl = mmd->totlvl;
-
- /* nothing to do */
- if (!totlvl) {
- return;
- }
-
- /* XXX - probably not necessary to regenerate the cddm so much? */
-
- /* generate highest level with displacements */
- cddm = CDDM_from_mesh(me);
- DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
- dispdm = multires_dm_create_local(
- scene, ob, cddm, totlvl, totlvl, 0, 0, MULTIRES_IGNORE_SIMPLIFY);
- cddm->release(cddm);
-
- /* copy the new locations of the base verts into the mesh */
- offset = dispdm->getNumVerts(dispdm) - me->totvert;
- for (i = 0; i < me->totvert; i++) {
- dispdm->getVertCo(dispdm, offset + i, me->mvert[i].co);
- }
-
- /* heuristic to produce a better-fitting base mesh */
-
- cddm = CDDM_from_mesh(me);
- pmap = cddm->getPolyMap(ob, cddm);
- origco = MEM_calloc_arrayN(me->totvert, 3 * sizeof(float), "multires apply base origco");
- for (i = 0; i < me->totvert; i++) {
- copy_v3_v3(origco[i], me->mvert[i].co);
- }
-
- for (i = 0; i < me->totvert; i++) {
- float avg_no[3] = {0, 0, 0}, center[3] = {0, 0, 0}, push[3];
- float dist;
- int tot = 0;
-
- /* don't adjust verts not used by at least one poly */
- if (!pmap[i].count) {
- continue;
- }
-
- /* find center */
- for (j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &me->mpoly[pmap[i].indices[j]];
-
- /* this double counts, not sure if that's bad or good */
- for (k = 0; k < p->totloop; k++) {
- int vndx = me->mloop[p->loopstart + k].v;
- if (vndx != i) {
- add_v3_v3(center, origco[vndx]);
- tot++;
- }
- }
- }
- mul_v3_fl(center, 1.0f / tot);
-
- /* find normal */
- for (j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &me->mpoly[pmap[i].indices[j]];
- MPoly fake_poly;
- MLoop *fake_loops;
- float(*fake_co)[3];
- float no[3];
-
- /* set up poly, loops, and coords in order to call
- * BKE_mesh_calc_poly_normal_coords() */
- fake_poly.totloop = p->totloop;
- fake_poly.loopstart = 0;
- fake_loops = MEM_malloc_arrayN(p->totloop, sizeof(MLoop), "fake_loops");
- fake_co = MEM_malloc_arrayN(p->totloop, 3 * sizeof(float), "fake_co");
-
- for (k = 0; k < p->totloop; k++) {
- int vndx = me->mloop[p->loopstart + k].v;
-
- fake_loops[k].v = k;
-
- if (vndx == i) {
- copy_v3_v3(fake_co[k], center);
- }
- else {
- copy_v3_v3(fake_co[k], origco[vndx]);
- }
- }
-
- BKE_mesh_calc_poly_normal_coords(&fake_poly, fake_loops, (const float(*)[3])fake_co, no);
- MEM_freeN(fake_loops);
- MEM_freeN(fake_co);
-
- add_v3_v3(avg_no, no);
- }
- normalize_v3(avg_no);
-
- /* push vertex away from the plane */
- dist = v3_dist_from_plane(me->mvert[i].co, center, avg_no);
- copy_v3_v3(push, avg_no);
- mul_v3_fl(push, dist);
- add_v3_v3(me->mvert[i].co, push);
- }
-
- MEM_freeN(origco);
- cddm->release(cddm);
-
- /* Vertices were moved around, need to update normals after all the vertices are updated
- * Probably this is possible to do in the loop above, but this is rather tricky because
- * we don't know all needed vertices' coordinates there yet.
- */
- BKE_mesh_calc_normals(me);
-
- /* subdivide the mesh to highest level without displacements */
- cddm = CDDM_from_mesh(me);
- DM_set_only_copy(cddm, &CD_MASK_BAREMESH);
- origdm = subsurf_dm_create_local(scene,
- ob,
- cddm,
- totlvl,
- 0,
- 0,
- mmd->uv_smooth == SUBSURF_UV_SMOOTH_NONE,
- 0,
- false,
- SUBSURF_IGNORE_SIMPLIFY);
- cddm->release(cddm);
-
- /* calc disps */
- multiresModifier_disp_run(
- dispdm, me, NULL, CALC_DISPLACEMENTS, origdm->getGridData(origdm), totlvl);
-
- origdm->release(origdm);
- dispdm->release(dispdm);
-}
-
-static void multires_subdivide(
+static void multires_subdivide_legacy(
MultiresModifierData *mmd, Scene *scene, Object *ob, int totlvl, int updateblock, int simple)
{
Mesh *me = ob->data;
@@ -1019,10 +946,10 @@ static void multires_subdivide(
multires_set_tot_level(ob, mmd, totlvl);
}
-void multiresModifier_subdivide(
+void multiresModifier_subdivide_legacy(
MultiresModifierData *mmd, Scene *scene, Object *ob, int updateblock, int simple)
{
- multires_subdivide(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple);
+ multires_subdivide_legacy(mmd, scene, ob, mmd->totlvl + 1, updateblock, simple);
}
static void grid_tangent(const CCGKey *key, int x, int y, int axis, CCGElem *grid, float t[3])
@@ -1274,7 +1201,7 @@ void multires_modifier_update_mdisps(struct DerivedMesh *dm, Scene *scene)
me = ccgdm->multires.ob->data;
mmd = ccgdm->multires.mmd;
multires_set_tot_mdisps(me, mmd->totlvl);
- CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop);
+ multiresModifier_ensure_external_read(me, mmd);
mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
if (mdisps) {
@@ -1502,7 +1429,7 @@ DerivedMesh *multires_make_derived_from_derived(
}
multires_set_tot_mdisps(me, mmd->totlvl);
- CustomData_external_read(&me->ldata, &me->id, CD_MASK_MDISPS, me->totloop);
+ multiresModifier_ensure_external_read(me, mmd);
/*run displacement*/
multiresModifier_disp_run(result, ob->data, dm, APPLY_DISPLACEMENTS, subGridData, mmd->totlvl);
@@ -2258,7 +2185,8 @@ void multires_load_old(Object *ob, Mesh *me)
multires_load_old_vcols(me);
multires_load_old_face_flags(me);
- /* multiresModifier_subdivide (actually, multires_subdivide) expects polys, not tessfaces! */
+ /* multiresModifier_subdivide_legacy (actually, multires_subdivide_legacy) expects polys, not
+ * tessfaces! */
BKE_mesh_convert_mfaces_to_mpolys(me);
/* Add a multires modifier to the object */
@@ -2270,7 +2198,7 @@ void multires_load_old(Object *ob, Mesh *me)
BLI_insertlinkbefore(&ob->modifiers, md, mmd);
for (i = 0; i < me->mr->level_count - 1; i++) {
- multiresModifier_subdivide(mmd, NULL, ob, 1, 0);
+ multiresModifier_subdivide_legacy(mmd, NULL, ob, 1, 0);
}
mmd->lvl = mmd->totlvl;
@@ -2295,8 +2223,7 @@ void multires_load_old(Object *ob, Mesh *me)
/* If 'ob_src' and 'ob_dst' both have multires modifiers, synchronize them
* such that 'ob_dst' has the same total number of levels as 'ob_src'. */
-void multiresModifier_sync_levels_ex(Scene *scene,
- Object *ob_dst,
+void multiresModifier_sync_levels_ex(Object *ob_dst,
MultiresModifierData *mmd_src,
MultiresModifierData *mmd_dst)
{
@@ -2305,7 +2232,7 @@ void multiresModifier_sync_levels_ex(Scene *scene,
}
if (mmd_src->totlvl > mmd_dst->totlvl) {
- multires_subdivide(mmd_dst, scene, ob_dst, mmd_src->totlvl, false, mmd_dst->simple);
+ multiresModifier_subdivide_to_level(ob_dst, mmd_dst, mmd_src->totlvl);
}
else {
multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl);
@@ -2327,7 +2254,7 @@ static void multires_sync_levels(Scene *scene, Object *ob_src, Object *ob_dst)
}
if (mmd_src && mmd_dst) {
- multiresModifier_sync_levels_ex(scene, ob_dst, mmd_src, mmd_dst);
+ multiresModifier_sync_levels_ex(ob_dst, mmd_src, mmd_dst);
}
}
@@ -2354,7 +2281,7 @@ static void multires_apply_smat(struct Depsgraph *UNUSED(depsgraph),
}
/* Make sure layer present. */
Mesh *mesh = (Mesh *)object->data;
- CustomData_external_read(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop);
+ multiresModifier_ensure_external_read(mesh, mmd);
if (!CustomData_get_layer(&mesh->ldata, CD_MDISPS)) {
return;
}
@@ -2448,6 +2375,44 @@ void multires_topology_changed(Mesh *me)
}
}
+/* Makes sure data from an external file is fully read.
+ *
+ * Since the multires data files only contain displacement vectors without knowledge about
+ * subdivision level some extra work is needed. Namely make is to all displacement grids have
+ * proper level and number of displacement vectors set. */
+void multires_ensure_external_read(struct Mesh *mesh, int top_level)
+{
+ if (!CustomData_external_test(&mesh->ldata, CD_MDISPS)) {
+ return;
+ }
+
+ MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
+ if (mdisps == NULL) {
+ mdisps = CustomData_add_layer(&mesh->ldata, CD_MDISPS, CD_DEFAULT, NULL, mesh->totloop);
+ }
+
+ const int totloop = mesh->totloop;
+
+ for (int i = 0; i < totloop; ++i) {
+ if (mdisps[i].level != top_level) {
+ MEM_SAFE_FREE(mdisps[i].disps);
+ }
+
+ /* NOTE: CustomData_external_read will take care of allocation of displacement vectors if
+ * they are missing. */
+
+ const int totdisp = multires_grid_tot[top_level];
+ mdisps[i].totdisp = totdisp;
+ mdisps[i].level = top_level;
+ }
+
+ CustomData_external_read(&mesh->ldata, &mesh->id, CD_MASK_MDISPS, mesh->totloop);
+}
+void multiresModifier_ensure_external_read(struct Mesh *mesh, const MultiresModifierData *mmd)
+{
+ multires_ensure_external_read(mesh, mmd->totlvl);
+}
+
/***************** Multires interpolation stuff *****************/
/* Find per-corner coordinate with given per-face UV coord */