911 lines
32 KiB
C
911 lines
32 KiB
C
|
|
/*
|
||
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
||
|
|
*
|
||
|
|
* This program is free software; you can redistribute it and/or
|
||
|
|
* modify it under the terms of the GNU General Public License
|
||
|
|
* as published by the Free Software Foundation; either version 2
|
||
|
|
* of the License, or (at your option) any later version.
|
||
|
|
*
|
||
|
|
* This program is distributed in the hope that it will be useful,
|
||
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
|
* GNU General Public License for more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU General Public License
|
||
|
|
* along with this program; if not, write to the Free Software Foundation,
|
||
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
|
*
|
||
|
|
* The Original Code is Copyright (C) 2018 by Blender Foundation.
|
||
|
|
* All rights reserved.
|
||
|
|
*
|
||
|
|
* Contributor(s): Sergey Sharybin.
|
||
|
|
*
|
||
|
|
* ***** END GPL LICENSE BLOCK *****
|
||
|
|
*/
|
||
|
|
|
||
|
|
/** \file blender/blenkernel/intern/subdiv_mesh.c
|
||
|
|
* \ingroup bke
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "BKE_subdiv.h"
|
||
|
|
|
||
|
|
#include "DNA_mesh_types.h"
|
||
|
|
#include "DNA_meshdata_types.h"
|
||
|
|
|
||
|
|
#include "BLI_alloca.h"
|
||
|
|
#include "BLI_math_vector.h"
|
||
|
|
#include "BLI_task.h"
|
||
|
|
|
||
|
|
#include "BKE_mesh.h"
|
||
|
|
|
||
|
|
/* TODO(sergey): Somehow move this to subdiv code? */
|
||
|
|
static int mpoly_ptex_faces_count_get(const MPoly *mp)
|
||
|
|
{
|
||
|
|
if (mp->totloop == 4) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return mp->totloop;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int num_edges_per_ptex_get(const int resolution)
|
||
|
|
{
|
||
|
|
return 2 * (resolution - 1) * resolution;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int num_polys_per_ptex_get(const int resolution)
|
||
|
|
{
|
||
|
|
return (resolution - 1) * (resolution - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
typedef struct SubdivMeshContext {
|
||
|
|
const Mesh *coarse_mesh;
|
||
|
|
Subdiv *subdiv;
|
||
|
|
Mesh *subdiv_mesh;
|
||
|
|
const SubdivToMeshSettings *settings;
|
||
|
|
/* Cached custom data arrays for fastter access. */
|
||
|
|
int *vert_origindex;
|
||
|
|
int *edge_origindex;
|
||
|
|
int *loop_origindex;
|
||
|
|
int *poly_origindex;
|
||
|
|
/* UV layers interpolation. */
|
||
|
|
int num_uv_layers;
|
||
|
|
MLoopUV *uv_layers[MAX_MTFACE];
|
||
|
|
} SubdivMeshContext;
|
||
|
|
|
||
|
|
typedef struct LoopsOfPtex {
|
||
|
|
/* First loop of the ptex, starts at ptex (0, 0) and goes in u direction. */
|
||
|
|
const MLoop *first_loop;
|
||
|
|
/* Last loop of the ptex, starts at ptex (0, 0) and goes in v direction. */
|
||
|
|
const MLoop *last_loop;
|
||
|
|
/* For quad coarse faces only. */
|
||
|
|
const MLoop *second_loop;
|
||
|
|
const MLoop *third_loop;
|
||
|
|
} LoopsOfPtex;
|
||
|
|
|
||
|
|
static void loops_of_ptex_get(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
LoopsOfPtex *loops_of_ptex,
|
||
|
|
const MPoly *coarse_poly,
|
||
|
|
const int ptex_face_index)
|
||
|
|
{
|
||
|
|
const MLoop *coarse_mloop = ctx->coarse_mesh->mloop;
|
||
|
|
const int first_ptex_loop_index = coarse_poly->loopstart + ptex_face_index;
|
||
|
|
/* Loop which look in the (opposite) V direction of the current
|
||
|
|
* ptex face.
|
||
|
|
*
|
||
|
|
* TOOD(sergey): Get rid of using module on every iteration.
|
||
|
|
*/
|
||
|
|
const int last_ptex_loop_index =
|
||
|
|
coarse_poly->loopstart +
|
||
|
|
(ptex_face_index + coarse_poly->totloop - 1) % coarse_poly->totloop;
|
||
|
|
loops_of_ptex->first_loop = &coarse_mloop[first_ptex_loop_index];
|
||
|
|
loops_of_ptex->last_loop = &coarse_mloop[last_ptex_loop_index];
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
loops_of_ptex->second_loop = loops_of_ptex->first_loop + 1;
|
||
|
|
loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
loops_of_ptex->second_loop = NULL;
|
||
|
|
loops_of_ptex->third_loop = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
typedef struct EdgesOfPtex {
|
||
|
|
/* First edge of the ptex, starts at ptex (0, 0) and goes in u direction. */
|
||
|
|
const MEdge *first_edge;
|
||
|
|
/* Last edge of the ptex, starts at ptex (0, 0) and goes in v direction. */
|
||
|
|
const MEdge *last_edge;
|
||
|
|
/* For quad coarse faces only. */
|
||
|
|
const MEdge *second_edge;
|
||
|
|
const MEdge *third_edge;
|
||
|
|
} EdgesOfPtex;
|
||
|
|
|
||
|
|
static void edges_of_ptex_get(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
EdgesOfPtex *edges_of_ptex,
|
||
|
|
const MPoly *coarse_poly,
|
||
|
|
const int ptex_face_index)
|
||
|
|
{
|
||
|
|
const MEdge *coarse_medge = ctx->coarse_mesh->medge;
|
||
|
|
LoopsOfPtex loops_of_ptex;
|
||
|
|
loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, ptex_face_index);
|
||
|
|
edges_of_ptex->first_edge = &coarse_medge[loops_of_ptex.first_loop->e];
|
||
|
|
edges_of_ptex->last_edge = &coarse_medge[loops_of_ptex.last_loop->e];
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
edges_of_ptex->second_edge =
|
||
|
|
&coarse_medge[loops_of_ptex.second_loop->e];
|
||
|
|
edges_of_ptex->third_edge =
|
||
|
|
&coarse_medge[loops_of_ptex.third_loop->e];
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
edges_of_ptex->second_edge = NULL;
|
||
|
|
edges_of_ptex->third_edge = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* TODO(sergey): Somehow de-duplicate with loops storage, without too much
|
||
|
|
* exception cases all over the code.
|
||
|
|
*/
|
||
|
|
|
||
|
|
typedef struct VerticesForInterpolation {
|
||
|
|
/* This field points to a vertex data which is to be used for interpolation.
|
||
|
|
* The idea is to avoid unnecessary allocations for regular faces, where
|
||
|
|
* we can simply
|
||
|
|
*/
|
||
|
|
const CustomData *vertex_data;
|
||
|
|
/* Vertices data calculated for ptex corners. There are always 4 elements
|
||
|
|
* in this custom data, aligned the following way:
|
||
|
|
*
|
||
|
|
* index 0 -> uv (0, 0)
|
||
|
|
* index 1 -> uv (0, 1)
|
||
|
|
* index 2 -> uv (1, 1)
|
||
|
|
* index 3 -> uv (1, 0)
|
||
|
|
*
|
||
|
|
* Is allocated for non-regular faces (triangles and n-gons).
|
||
|
|
*/
|
||
|
|
CustomData vertex_data_storage;
|
||
|
|
bool vertex_data_storage_allocated;
|
||
|
|
/* Infices within vertex_data to interpolate for. The indices are aligned
|
||
|
|
* with uv coordinates in a similar way as indices in loop_data_storage.
|
||
|
|
*/
|
||
|
|
int vertex_indices[4];
|
||
|
|
} VerticesForInterpolation;
|
||
|
|
|
||
|
|
static void vertex_interpolation_init(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
VerticesForInterpolation *vertex_interpolation,
|
||
|
|
const MPoly *coarse_poly)
|
||
|
|
{
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MLoop *coarse_mloop = coarse_mesh->mloop;
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
vertex_interpolation->vertex_data = &coarse_mesh->vdata;
|
||
|
|
vertex_interpolation->vertex_indices[0] =
|
||
|
|
coarse_mloop[coarse_poly->loopstart + 0].v;
|
||
|
|
vertex_interpolation->vertex_indices[1] =
|
||
|
|
coarse_mloop[coarse_poly->loopstart + 1].v;
|
||
|
|
vertex_interpolation->vertex_indices[2] =
|
||
|
|
coarse_mloop[coarse_poly->loopstart + 2].v;
|
||
|
|
vertex_interpolation->vertex_indices[3] =
|
||
|
|
coarse_mloop[coarse_poly->loopstart + 3].v;
|
||
|
|
vertex_interpolation->vertex_data_storage_allocated = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
vertex_interpolation->vertex_data =
|
||
|
|
&vertex_interpolation->vertex_data_storage;
|
||
|
|
/* Allocate storage for loops corresponding to ptex corners. */
|
||
|
|
CustomData_copy(&ctx->coarse_mesh->vdata,
|
||
|
|
&vertex_interpolation->vertex_data_storage,
|
||
|
|
CD_MASK_EVERYTHING,
|
||
|
|
CD_CALLOC,
|
||
|
|
4);
|
||
|
|
/* Initialize indices. */
|
||
|
|
vertex_interpolation->vertex_indices[0] = 0;
|
||
|
|
vertex_interpolation->vertex_indices[1] = 1;
|
||
|
|
vertex_interpolation->vertex_indices[2] = 2;
|
||
|
|
vertex_interpolation->vertex_indices[3] = 3;
|
||
|
|
vertex_interpolation->vertex_data_storage_allocated = true;
|
||
|
|
/* Interpolate center of poly right away, it stays unchanged for all
|
||
|
|
* ptex faces.
|
||
|
|
*/
|
||
|
|
const float weight = 1.0f / (float)coarse_poly->totloop;
|
||
|
|
float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
|
||
|
|
int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
|
||
|
|
for (int i = 0; i < coarse_poly->totloop; ++i) {
|
||
|
|
weights[i] = weight;
|
||
|
|
indices[i] = coarse_poly->loopstart + i;
|
||
|
|
}
|
||
|
|
CustomData_interp(&coarse_mesh->vdata,
|
||
|
|
&vertex_interpolation->vertex_data_storage,
|
||
|
|
indices,
|
||
|
|
weights, NULL,
|
||
|
|
coarse_poly->totloop,
|
||
|
|
2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void vertex_interpolation_from_ptex(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
VerticesForInterpolation *vertex_interpolation,
|
||
|
|
const MPoly *coarse_poly,
|
||
|
|
const int ptex_face_index)
|
||
|
|
{
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
/* Nothing to do, all indices and data is already assigned. */
|
||
|
|
} else {
|
||
|
|
const CustomData *vertex_data = &ctx->coarse_mesh->vdata;
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MLoop *coarse_mloop = coarse_mesh->mloop;
|
||
|
|
LoopsOfPtex loops_of_ptex;
|
||
|
|
loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, ptex_face_index);
|
||
|
|
/* Ptex face corner corresponds to a poly loop with same index. */
|
||
|
|
CustomData_copy_data(
|
||
|
|
vertex_data,
|
||
|
|
&vertex_interpolation->vertex_data_storage,
|
||
|
|
coarse_mloop[coarse_poly->loopstart + ptex_face_index].v,
|
||
|
|
0,
|
||
|
|
1);
|
||
|
|
/* Interpolate remaining ptex face corners, which hits loops
|
||
|
|
* middle points.
|
||
|
|
*
|
||
|
|
* TODO(sergey): Re-use one of interpolation results from previous
|
||
|
|
* iteration.
|
||
|
|
*/
|
||
|
|
const float weights[2] = {0.5f, 0.5f};
|
||
|
|
const int first_indices[2] = {
|
||
|
|
coarse_mloop[loops_of_ptex.first_loop - coarse_mloop].v,
|
||
|
|
coarse_mloop[(loops_of_ptex.first_loop + 1 - coarse_mloop) %
|
||
|
|
coarse_poly->totloop].v};
|
||
|
|
const int last_indices[2] = {
|
||
|
|
coarse_mloop[loops_of_ptex.last_loop - coarse_mloop].v,
|
||
|
|
coarse_mloop[loops_of_ptex.first_loop - coarse_mloop].v};
|
||
|
|
CustomData_interp(vertex_data,
|
||
|
|
&vertex_interpolation->vertex_data_storage,
|
||
|
|
first_indices,
|
||
|
|
weights, NULL,
|
||
|
|
2,
|
||
|
|
1);
|
||
|
|
CustomData_interp(vertex_data,
|
||
|
|
&vertex_interpolation->vertex_data_storage,
|
||
|
|
last_indices,
|
||
|
|
weights, NULL,
|
||
|
|
2,
|
||
|
|
3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void vertex_interpolation_end(
|
||
|
|
VerticesForInterpolation *vertex_interpolation)
|
||
|
|
{
|
||
|
|
if (vertex_interpolation->vertex_data_storage_allocated) {
|
||
|
|
CustomData_free(&vertex_interpolation->vertex_data_storage, 4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
typedef struct LoopsForInterpolation {
|
||
|
|
/* This field points to a loop data which is to be used for interpolation.
|
||
|
|
* The idea is to avoid unnecessary allocations for regular faces, where
|
||
|
|
* we can simply
|
||
|
|
*/
|
||
|
|
const CustomData *loop_data;
|
||
|
|
/* Loops data calculated for ptex corners. There are always 4 elements
|
||
|
|
* in this custom data, aligned the following way:
|
||
|
|
*
|
||
|
|
* index 0 -> uv (0, 0)
|
||
|
|
* index 1 -> uv (0, 1)
|
||
|
|
* index 2 -> uv (1, 1)
|
||
|
|
* index 3 -> uv (1, 0)
|
||
|
|
*
|
||
|
|
* Is allocated for non-regular faces (triangles and n-gons).
|
||
|
|
*/
|
||
|
|
CustomData loop_data_storage;
|
||
|
|
bool loop_data_storage_allocated;
|
||
|
|
/* Infices within loop_data to interpolate for. The indices are aligned with
|
||
|
|
* uv coordinates in a similar way as indices in loop_data_storage.
|
||
|
|
*/
|
||
|
|
int loop_indices[4];
|
||
|
|
} LoopsForInterpolation;
|
||
|
|
|
||
|
|
static void loop_interpolation_init(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
LoopsForInterpolation *loop_interpolation,
|
||
|
|
const MPoly *coarse_poly)
|
||
|
|
{
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
loop_interpolation->loop_data = &coarse_mesh->ldata;
|
||
|
|
loop_interpolation->loop_indices[0] = coarse_poly->loopstart + 0;
|
||
|
|
loop_interpolation->loop_indices[1] = coarse_poly->loopstart + 1;
|
||
|
|
loop_interpolation->loop_indices[2] = coarse_poly->loopstart + 2;
|
||
|
|
loop_interpolation->loop_indices[3] = coarse_poly->loopstart + 3;
|
||
|
|
loop_interpolation->loop_data_storage_allocated = false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
loop_interpolation->loop_data = &loop_interpolation->loop_data_storage;
|
||
|
|
/* Allocate storage for loops corresponding to ptex corners. */
|
||
|
|
CustomData_copy(&ctx->coarse_mesh->ldata,
|
||
|
|
&loop_interpolation->loop_data_storage,
|
||
|
|
CD_MASK_EVERYTHING,
|
||
|
|
CD_CALLOC,
|
||
|
|
4);
|
||
|
|
/* Initialize indices. */
|
||
|
|
loop_interpolation->loop_indices[0] = 0;
|
||
|
|
loop_interpolation->loop_indices[1] = 1;
|
||
|
|
loop_interpolation->loop_indices[2] = 2;
|
||
|
|
loop_interpolation->loop_indices[3] = 3;
|
||
|
|
loop_interpolation->loop_data_storage_allocated = true;
|
||
|
|
/* Interpolate center of poly right away, it stays unchanged for all
|
||
|
|
* ptex faces.
|
||
|
|
*/
|
||
|
|
const float weight = 1.0f / (float)coarse_poly->totloop;
|
||
|
|
float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
|
||
|
|
int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
|
||
|
|
for (int i = 0; i < coarse_poly->totloop; ++i) {
|
||
|
|
weights[i] = weight;
|
||
|
|
indices[i] = coarse_poly->loopstart + i;
|
||
|
|
}
|
||
|
|
CustomData_interp(&coarse_mesh->ldata,
|
||
|
|
&loop_interpolation->loop_data_storage,
|
||
|
|
indices,
|
||
|
|
weights, NULL,
|
||
|
|
coarse_poly->totloop,
|
||
|
|
2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void loop_interpolation_from_ptex(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
LoopsForInterpolation *loop_interpolation,
|
||
|
|
const MPoly *coarse_poly,
|
||
|
|
const int ptex_face_index)
|
||
|
|
{
|
||
|
|
if (coarse_poly->totloop == 4) {
|
||
|
|
/* Nothing to do, all indices and data is already assigned. */
|
||
|
|
} else {
|
||
|
|
const CustomData *loop_data = &ctx->coarse_mesh->ldata;
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MLoop *coarse_mloop = coarse_mesh->mloop;
|
||
|
|
LoopsOfPtex loops_of_ptex;
|
||
|
|
loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, ptex_face_index);
|
||
|
|
/* Ptex face corner corresponds to a poly loop with same index. */
|
||
|
|
CustomData_copy_data(loop_data,
|
||
|
|
&loop_interpolation->loop_data_storage,
|
||
|
|
coarse_poly->loopstart + ptex_face_index,
|
||
|
|
0,
|
||
|
|
1);
|
||
|
|
/* Interpolate remaining ptex face corners, which hits loops
|
||
|
|
* middle points.
|
||
|
|
*
|
||
|
|
* TODO(sergey): Re-use one of interpolation results from previous
|
||
|
|
* iteration.
|
||
|
|
*/
|
||
|
|
const float weights[2] = {0.5f, 0.5f};
|
||
|
|
const int first_indices[2] = {
|
||
|
|
loops_of_ptex.first_loop - coarse_mloop,
|
||
|
|
(loops_of_ptex.first_loop + 1 - coarse_mloop) %
|
||
|
|
coarse_poly->totloop};
|
||
|
|
const int last_indices[2] = {
|
||
|
|
loops_of_ptex.last_loop - coarse_mloop,
|
||
|
|
loops_of_ptex.first_loop - coarse_mloop};
|
||
|
|
CustomData_interp(loop_data,
|
||
|
|
&loop_interpolation->loop_data_storage,
|
||
|
|
first_indices,
|
||
|
|
weights, NULL,
|
||
|
|
2,
|
||
|
|
1);
|
||
|
|
CustomData_interp(loop_data,
|
||
|
|
&loop_interpolation->loop_data_storage,
|
||
|
|
last_indices,
|
||
|
|
weights, NULL,
|
||
|
|
2,
|
||
|
|
3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation)
|
||
|
|
{
|
||
|
|
if (loop_interpolation->loop_data_storage_allocated) {
|
||
|
|
CustomData_free(&loop_interpolation->loop_data_storage, 4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_copy_vertex_data(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
MVert *subdiv_vertex,
|
||
|
|
const VerticesForInterpolation *vertex_interpolation,
|
||
|
|
const float u, const float v)
|
||
|
|
{
|
||
|
|
const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_mesh->mvert;
|
||
|
|
const float weights[4] = {(1.0f - u) * (1.0f - v),
|
||
|
|
u * (1.0f - v),
|
||
|
|
u * v,
|
||
|
|
(1.0f - u) * v};
|
||
|
|
CustomData_interp(vertex_interpolation->vertex_data,
|
||
|
|
&ctx->subdiv_mesh->vdata,
|
||
|
|
vertex_interpolation->vertex_indices,
|
||
|
|
weights, NULL,
|
||
|
|
4,
|
||
|
|
subdiv_vertex_index);
|
||
|
|
/* TODO(sergey): Set ORIGINDEX. */
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_evaluate_vertices(SubdivMeshContext *ctx,
|
||
|
|
const int poly_index)
|
||
|
|
{
|
||
|
|
Subdiv *subdiv = ctx->subdiv;
|
||
|
|
const int resolution = ctx->settings->resolution;
|
||
|
|
const int resolution2 = resolution * resolution;
|
||
|
|
const float inv_resolution_1 = 1.0f / (float)(resolution - 1);
|
||
|
|
/* Base/coarse mesh information. */
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MPoly *coarse_polyoly = coarse_mesh->mpoly;
|
||
|
|
const MPoly *coarse_poly = &coarse_polyoly[poly_index];
|
||
|
|
const int num_poly_ptex_faces = mpoly_ptex_faces_count_get(coarse_poly);
|
||
|
|
/* Hi-poly subdivided mesh. */
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
MVert *subdiv_vertert = subdiv_mesh->mvert;
|
||
|
|
const int ptex_face_index = subdiv->face_ptex_offset[poly_index];
|
||
|
|
/* Actual evaluation. */
|
||
|
|
VerticesForInterpolation vertex_interpolation;
|
||
|
|
vertex_interpolation_init(ctx, &vertex_interpolation, coarse_poly);
|
||
|
|
MVert *subdiv_vert = &subdiv_vertert[ptex_face_index * resolution2];
|
||
|
|
for (int ptex_of_poly_index = 0;
|
||
|
|
ptex_of_poly_index < num_poly_ptex_faces;
|
||
|
|
ptex_of_poly_index++)
|
||
|
|
{
|
||
|
|
vertex_interpolation_from_ptex(ctx,
|
||
|
|
&vertex_interpolation,
|
||
|
|
coarse_poly,
|
||
|
|
ptex_of_poly_index);
|
||
|
|
const int current_ptex_face_index =
|
||
|
|
ptex_face_index + ptex_of_poly_index;
|
||
|
|
BKE_subdiv_eval_limit_patch_resolution_point_and_short_normal(
|
||
|
|
subdiv,
|
||
|
|
current_ptex_face_index,
|
||
|
|
resolution,
|
||
|
|
subdiv_vert, offsetof(MVert, co), sizeof(MVert),
|
||
|
|
subdiv_vert, offsetof(MVert, no), sizeof(MVert));
|
||
|
|
for (int y = 0; y < resolution; y++) {
|
||
|
|
const float v = y * inv_resolution_1;
|
||
|
|
for (int x = 0; x < resolution; x++, subdiv_vert++) {
|
||
|
|
const float u = x * inv_resolution_1;
|
||
|
|
subdiv_copy_vertex_data(ctx,
|
||
|
|
subdiv_vert,
|
||
|
|
&vertex_interpolation,
|
||
|
|
u, v);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
vertex_interpolation_end(&vertex_interpolation);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_copy_edge_data(SubdivMeshContext *ctx,
|
||
|
|
MEdge *subdiv_edge,
|
||
|
|
const MEdge *coarse_edge)
|
||
|
|
{
|
||
|
|
if (coarse_edge == NULL) {
|
||
|
|
subdiv_edge->crease = 0;
|
||
|
|
subdiv_edge->bweight = 0;
|
||
|
|
subdiv_edge->flag = 0;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const int coarse_edge_index = coarse_edge - ctx->coarse_mesh->medge;
|
||
|
|
const int subdiv_edge_index = subdiv_edge - ctx->subdiv_mesh->medge;
|
||
|
|
CustomData_copy_data(&ctx->coarse_mesh->edata,
|
||
|
|
&ctx->subdiv_mesh->edata,
|
||
|
|
coarse_edge_index,
|
||
|
|
subdiv_edge_index,
|
||
|
|
1);
|
||
|
|
if (ctx->edge_origindex != NULL) {
|
||
|
|
ctx->edge_origindex[subdiv_edge_index] = coarse_edge_index;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static MEdge *subdiv_create_edges_row(SubdivMeshContext *ctx,
|
||
|
|
MEdge *subdiv_edge,
|
||
|
|
const MEdge *coarse_edge,
|
||
|
|
const int start_vertex_index,
|
||
|
|
const int resolution)
|
||
|
|
{
|
||
|
|
int vertex_index = start_vertex_index;
|
||
|
|
for (int edge_index = 0;
|
||
|
|
edge_index < resolution - 1;
|
||
|
|
edge_index++, subdiv_edge++)
|
||
|
|
{
|
||
|
|
subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge);
|
||
|
|
subdiv_edge->v1 = vertex_index;
|
||
|
|
subdiv_edge->v2 = vertex_index + 1;
|
||
|
|
vertex_index += 1;
|
||
|
|
}
|
||
|
|
return subdiv_edge;
|
||
|
|
}
|
||
|
|
|
||
|
|
static MEdge *subdiv_create_edges_column(SubdivMeshContext *ctx,
|
||
|
|
MEdge *subdiv_edge,
|
||
|
|
const MEdge *coarse_start_edge,
|
||
|
|
const MEdge *coarse_end_edge,
|
||
|
|
const int start_vertex_index,
|
||
|
|
const int resolution)
|
||
|
|
{
|
||
|
|
int vertex_index = start_vertex_index;
|
||
|
|
for (int edge_index = 0;
|
||
|
|
edge_index < resolution;
|
||
|
|
edge_index++, subdiv_edge++)
|
||
|
|
{
|
||
|
|
const MEdge *coarse_edge = NULL;
|
||
|
|
if (edge_index == 0) {
|
||
|
|
coarse_edge = coarse_start_edge;
|
||
|
|
}
|
||
|
|
else if (edge_index == resolution - 1) {
|
||
|
|
coarse_edge = coarse_end_edge;
|
||
|
|
}
|
||
|
|
subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge);
|
||
|
|
subdiv_edge->v1 = vertex_index;
|
||
|
|
subdiv_edge->v2 = vertex_index + resolution;
|
||
|
|
vertex_index += 1;
|
||
|
|
}
|
||
|
|
return subdiv_edge;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_create_edges(SubdivMeshContext *ctx, int poly_index)
|
||
|
|
{
|
||
|
|
Subdiv *subdiv = ctx->subdiv;
|
||
|
|
const int resolution = ctx->settings->resolution;
|
||
|
|
const int resolution2 = resolution * resolution;
|
||
|
|
const int ptex_face_index = subdiv->face_ptex_offset[poly_index];
|
||
|
|
const int num_edges_per_ptex = num_edges_per_ptex_get(resolution);
|
||
|
|
const int start_edge_index = ptex_face_index * num_edges_per_ptex;
|
||
|
|
/* Base/coarse mesh information. */
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MPoly *coarse_polyoly = coarse_mesh->mpoly;
|
||
|
|
const MPoly *coarse_poly = &coarse_polyoly[poly_index];
|
||
|
|
const int num_poly_ptex_faces = mpoly_ptex_faces_count_get(coarse_poly);
|
||
|
|
/* Hi-poly subdivided mesh. */
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
MEdge *subdiv_medge = subdiv_mesh->medge;
|
||
|
|
MEdge *subdiv_edge = &subdiv_medge[start_edge_index];
|
||
|
|
const int start_poly_vertex_index = ptex_face_index * resolution2;
|
||
|
|
/* Consider a subdivision of base face at level 1:
|
||
|
|
*
|
||
|
|
* y
|
||
|
|
* ^
|
||
|
|
* | (6) ---- (7) ---- (8)
|
||
|
|
* | | | |
|
||
|
|
* | (3) ---- (4) ---- (5)
|
||
|
|
* | | | |
|
||
|
|
* | (0) ---- (1) ---- (2)
|
||
|
|
* o---------------------------> x
|
||
|
|
*
|
||
|
|
* This is illustrate which parts of geometry is created by code below.
|
||
|
|
*/
|
||
|
|
for (int i = 0; i < num_poly_ptex_faces; i++) {
|
||
|
|
const int start_ptex_face_vertex_index =
|
||
|
|
start_poly_vertex_index + i * resolution2;
|
||
|
|
EdgesOfPtex edges_of_ptex;
|
||
|
|
edges_of_ptex_get(ctx, &edges_of_ptex, coarse_poly, i);
|
||
|
|
/* Create bottom row of edges (0-1, 1-2). */
|
||
|
|
subdiv_edge = subdiv_create_edges_row(ctx,
|
||
|
|
subdiv_edge,
|
||
|
|
edges_of_ptex.first_edge,
|
||
|
|
start_ptex_face_vertex_index,
|
||
|
|
resolution);
|
||
|
|
/* Create remaining edges. */
|
||
|
|
for (int row = 0; row < resolution - 1; row++) {
|
||
|
|
const int start_row_vertex_index =
|
||
|
|
start_ptex_face_vertex_index + row * resolution;
|
||
|
|
/* Create vertical columns.
|
||
|
|
*
|
||
|
|
* At first iteration it will be edges (0-3. 1-4, 2-5), then it
|
||
|
|
* will be (3-6, 4-7, 5-8) and so on.
|
||
|
|
*/
|
||
|
|
subdiv_edge = subdiv_create_edges_column(
|
||
|
|
ctx,
|
||
|
|
subdiv_edge,
|
||
|
|
edges_of_ptex.last_edge,
|
||
|
|
edges_of_ptex.second_edge,
|
||
|
|
start_row_vertex_index,
|
||
|
|
resolution);
|
||
|
|
/* Create horizontal edge row.
|
||
|
|
*
|
||
|
|
* At first iteration it will be edges (3-4, 4-5), then it will be
|
||
|
|
* (6-7, 7-8) and so on.
|
||
|
|
*/
|
||
|
|
subdiv_edge = subdiv_create_edges_row(
|
||
|
|
ctx,
|
||
|
|
subdiv_edge,
|
||
|
|
(row == resolution - 2) ? edges_of_ptex.third_edge
|
||
|
|
: NULL,
|
||
|
|
start_row_vertex_index + resolution,
|
||
|
|
resolution);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_copy_loop_data(
|
||
|
|
const SubdivMeshContext *ctx,
|
||
|
|
MLoop *subdiv_loop,
|
||
|
|
const LoopsForInterpolation *loop_interpolation,
|
||
|
|
const float u, const float v)
|
||
|
|
{
|
||
|
|
const int subdiv_loop_index = subdiv_loop - ctx->subdiv_mesh->mloop;
|
||
|
|
const float weights[4] = {(1.0f - u) * (1.0f - v),
|
||
|
|
u * (1.0f - v),
|
||
|
|
u * v,
|
||
|
|
(1.0f - u) * v};
|
||
|
|
CustomData_interp(loop_interpolation->loop_data,
|
||
|
|
&ctx->subdiv_mesh->ldata,
|
||
|
|
loop_interpolation->loop_indices,
|
||
|
|
weights, NULL,
|
||
|
|
4,
|
||
|
|
subdiv_loop_index);
|
||
|
|
/* TODO(sergey): Set ORIGINDEX. */
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_eval_uv_layer(SubdivMeshContext *ctx,
|
||
|
|
MLoop *subdiv_loop,
|
||
|
|
const int ptex_face_index,
|
||
|
|
const float u, const float v,
|
||
|
|
const float inv_resolution_1)
|
||
|
|
{
|
||
|
|
if (ctx->num_uv_layers == 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
Subdiv *subdiv = ctx->subdiv;
|
||
|
|
const int mloop_index = subdiv_loop - ctx->subdiv_mesh->mloop;
|
||
|
|
const float du = inv_resolution_1;
|
||
|
|
const float dv = inv_resolution_1;
|
||
|
|
for (int layer_index = 0; layer_index < ctx->num_uv_layers; layer_index++) {
|
||
|
|
MLoopUV *subdiv_loopuv = &ctx->uv_layers[layer_index][mloop_index];
|
||
|
|
BKE_subdiv_eval_face_varying(subdiv,
|
||
|
|
ptex_face_index,
|
||
|
|
u, v,
|
||
|
|
subdiv_loopuv[0].uv);
|
||
|
|
BKE_subdiv_eval_face_varying(subdiv,
|
||
|
|
ptex_face_index,
|
||
|
|
u + du, v,
|
||
|
|
subdiv_loopuv[1].uv);
|
||
|
|
BKE_subdiv_eval_face_varying(subdiv,
|
||
|
|
ptex_face_index,
|
||
|
|
u + du, v + dv,
|
||
|
|
subdiv_loopuv[2].uv);
|
||
|
|
BKE_subdiv_eval_face_varying(subdiv,
|
||
|
|
ptex_face_index,
|
||
|
|
u, v + dv,
|
||
|
|
subdiv_loopuv[3].uv);
|
||
|
|
/* TODO(sergey): Currently evaluator only has single UV layer, so can
|
||
|
|
* not evaluate more than that. Need to be solved.
|
||
|
|
*/
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_create_loops(SubdivMeshContext *ctx, int poly_index)
|
||
|
|
{
|
||
|
|
Subdiv *subdiv = ctx->subdiv;
|
||
|
|
const int resolution = ctx->settings->resolution;
|
||
|
|
const int resolution2 = resolution * resolution;
|
||
|
|
const float inv_resolution_1 = 1.0f / (float)(resolution - 1);
|
||
|
|
const int ptex_face_index = subdiv->face_ptex_offset[poly_index];
|
||
|
|
const int num_edges_per_ptex = num_edges_per_ptex_get(resolution);
|
||
|
|
const int start_edge_index = ptex_face_index * num_edges_per_ptex;
|
||
|
|
const int num_polys_per_ptex = num_polys_per_ptex_get(resolution);
|
||
|
|
const int start_poly_index = ptex_face_index * num_polys_per_ptex;
|
||
|
|
const int start_loop_index = 4 * start_poly_index;
|
||
|
|
const int start_vert_index = ptex_face_index * resolution2;
|
||
|
|
const float du = inv_resolution_1;
|
||
|
|
const float dv = inv_resolution_1;
|
||
|
|
/* Base/coarse mesh information. */
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MPoly *coarse_polyoly = coarse_mesh->mpoly;
|
||
|
|
const MPoly *coarse_poly = &coarse_polyoly[poly_index];
|
||
|
|
const int num_poly_ptex_faces = mpoly_ptex_faces_count_get(coarse_poly);
|
||
|
|
/* Hi-poly subdivided mesh. */
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
MLoop *subdiv_loopoop = subdiv_mesh->mloop;
|
||
|
|
MLoop *subdiv_loop = &subdiv_loopoop[start_loop_index];
|
||
|
|
LoopsForInterpolation loop_interpolation;
|
||
|
|
loop_interpolation_init(ctx, &loop_interpolation, coarse_poly);
|
||
|
|
for (int ptex_of_poly_index = 0;
|
||
|
|
ptex_of_poly_index < num_poly_ptex_faces;
|
||
|
|
ptex_of_poly_index++)
|
||
|
|
{
|
||
|
|
loop_interpolation_from_ptex(ctx,
|
||
|
|
&loop_interpolation,
|
||
|
|
coarse_poly,
|
||
|
|
ptex_of_poly_index);
|
||
|
|
const int current_ptex_face_index =
|
||
|
|
ptex_face_index + ptex_of_poly_index;
|
||
|
|
for (int y = 0; y < resolution - 1; y++) {
|
||
|
|
const float v = y * inv_resolution_1;
|
||
|
|
for (int x = 0; x < resolution - 1; x++, subdiv_loop += 4) {
|
||
|
|
const float u = x * inv_resolution_1;
|
||
|
|
/* Vertex indicies ordered counter-clockwise. */
|
||
|
|
const int v0 = start_vert_index +
|
||
|
|
(ptex_of_poly_index * resolution2) +
|
||
|
|
(y * resolution + x);
|
||
|
|
const int v1 = v0 + 1;
|
||
|
|
const int v2 = v0 + resolution + 1;
|
||
|
|
const int v3 = v0 + resolution;
|
||
|
|
/* Edge indicies ordered counter-clockwise. */
|
||
|
|
const int e0 = start_edge_index +
|
||
|
|
(ptex_of_poly_index * num_edges_per_ptex) +
|
||
|
|
(y * (2 * resolution - 1) + x);
|
||
|
|
const int e1 = e0 + resolution;
|
||
|
|
const int e2 = e0 + (2 * resolution - 1);
|
||
|
|
const int e3 = e0 + resolution - 1;
|
||
|
|
/* Initialize 4 loops of corresponding hi-poly poly. */
|
||
|
|
/* TODO(sergey): For ptex boundaries we should use loops from
|
||
|
|
* coarse mesh.
|
||
|
|
*/
|
||
|
|
subdiv_copy_loop_data(ctx,
|
||
|
|
&subdiv_loop[0],
|
||
|
|
&loop_interpolation,
|
||
|
|
u, v);
|
||
|
|
subdiv_loop[0].v = v0;
|
||
|
|
subdiv_loop[0].e = e0;
|
||
|
|
subdiv_copy_loop_data(ctx,
|
||
|
|
&subdiv_loop[1],
|
||
|
|
&loop_interpolation,
|
||
|
|
u + du, v);
|
||
|
|
subdiv_loop[1].v = v1;
|
||
|
|
subdiv_loop[1].e = e1;
|
||
|
|
subdiv_copy_loop_data(ctx,
|
||
|
|
&subdiv_loop[2],
|
||
|
|
&loop_interpolation,
|
||
|
|
u + du, v + dv);
|
||
|
|
subdiv_loop[2].v = v2;
|
||
|
|
subdiv_loop[2].e = e2;
|
||
|
|
subdiv_copy_loop_data(ctx,
|
||
|
|
&subdiv_loop[3],
|
||
|
|
&loop_interpolation,
|
||
|
|
u, v + dv);
|
||
|
|
subdiv_loop[3].v = v3;
|
||
|
|
subdiv_loop[3].e = e3;
|
||
|
|
/* Interpolate UV layers using OpenSubdiv. */
|
||
|
|
subdiv_eval_uv_layer(ctx,
|
||
|
|
subdiv_loop,
|
||
|
|
current_ptex_face_index,
|
||
|
|
u, v,
|
||
|
|
inv_resolution_1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
loop_interpolation_end(&loop_interpolation);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_copy_poly_data(const SubdivMeshContext *ctx,
|
||
|
|
MPoly *subdiv_poly,
|
||
|
|
const MPoly *coarse_poly)
|
||
|
|
{
|
||
|
|
const int coarse_poly_index = coarse_poly - ctx->coarse_mesh->mpoly;
|
||
|
|
const int subdiv_poly_index = subdiv_poly - ctx->subdiv_mesh->mpoly;
|
||
|
|
CustomData_copy_data(&ctx->coarse_mesh->pdata,
|
||
|
|
&ctx->subdiv_mesh->pdata,
|
||
|
|
coarse_poly_index,
|
||
|
|
subdiv_poly_index,
|
||
|
|
1);
|
||
|
|
if (ctx->poly_origindex != NULL) {
|
||
|
|
ctx->poly_origindex[subdiv_poly_index] = coarse_poly_index;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_create_polys(SubdivMeshContext *ctx, int poly_index)
|
||
|
|
{
|
||
|
|
Subdiv *subdiv = ctx->subdiv;
|
||
|
|
const int resolution = ctx->settings->resolution;
|
||
|
|
const int ptex_face_index = subdiv->face_ptex_offset[poly_index];
|
||
|
|
const int num_polys_per_ptex = num_polys_per_ptex_get(resolution);
|
||
|
|
const int num_loops_per_ptex = 4 * num_polys_per_ptex;
|
||
|
|
const int start_poly_index = ptex_face_index * num_polys_per_ptex;
|
||
|
|
const int start_loop_index = 4 * start_poly_index;
|
||
|
|
/* Base/coarse mesh information. */
|
||
|
|
const Mesh *coarse_mesh = ctx->coarse_mesh;
|
||
|
|
const MPoly *coarse_polyoly = coarse_mesh->mpoly;
|
||
|
|
const MPoly *coarse_poly = &coarse_polyoly[poly_index];
|
||
|
|
const int num_poly_ptex_faces = mpoly_ptex_faces_count_get(coarse_poly);
|
||
|
|
/* Hi-poly subdivided mesh. */
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
MPoly *subdiv_mpoly = subdiv_mesh->mpoly;
|
||
|
|
MPoly *subdiv_mp = &subdiv_mpoly[start_poly_index];
|
||
|
|
for (int ptex_of_poly_index = 0;
|
||
|
|
ptex_of_poly_index < num_poly_ptex_faces;
|
||
|
|
ptex_of_poly_index++)
|
||
|
|
{
|
||
|
|
for (int subdiv_poly_index = 0;
|
||
|
|
subdiv_poly_index < num_polys_per_ptex;
|
||
|
|
subdiv_poly_index++, subdiv_mp++)
|
||
|
|
{
|
||
|
|
subdiv_copy_poly_data(ctx, subdiv_mp, coarse_poly);
|
||
|
|
subdiv_mp->loopstart = start_loop_index +
|
||
|
|
(ptex_of_poly_index * num_loops_per_ptex) +
|
||
|
|
(subdiv_poly_index * 4);
|
||
|
|
subdiv_mp->totloop = 4;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void subdiv_eval_task(
|
||
|
|
void *__restrict userdata,
|
||
|
|
const int poly_index,
|
||
|
|
const ParallelRangeTLS *__restrict UNUSED(tls))
|
||
|
|
{
|
||
|
|
SubdivMeshContext *data = userdata;
|
||
|
|
/* Evaluate hi-poly vertex coordinates and normals. */
|
||
|
|
subdiv_evaluate_vertices(data, poly_index);
|
||
|
|
/* Create mesh geometry for the given base poly index. */
|
||
|
|
subdiv_create_edges(data, poly_index);
|
||
|
|
subdiv_create_loops(data, poly_index);
|
||
|
|
subdiv_create_polys(data, poly_index);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void cache_uv_layers(SubdivMeshContext *ctx)
|
||
|
|
{
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
ctx->num_uv_layers =
|
||
|
|
CustomData_number_of_layers(&subdiv_mesh->ldata, CD_MLOOPUV);
|
||
|
|
for (int layer_index = 0; layer_index < ctx->num_uv_layers; ++layer_index) {
|
||
|
|
ctx->uv_layers[layer_index] = CustomData_get_layer_n(
|
||
|
|
&subdiv_mesh->ldata, CD_MLOOPUV, layer_index);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void cache_custom_data_layers(SubdivMeshContext *ctx)
|
||
|
|
{
|
||
|
|
Mesh *subdiv_mesh = ctx->subdiv_mesh;
|
||
|
|
/* Pointers to original indices layers. */
|
||
|
|
ctx->vert_origindex = CustomData_get_layer(
|
||
|
|
&subdiv_mesh->vdata, CD_ORIGINDEX);
|
||
|
|
ctx->edge_origindex = CustomData_get_layer(
|
||
|
|
&subdiv_mesh->edata, CD_ORIGINDEX);
|
||
|
|
ctx->loop_origindex = CustomData_get_layer(
|
||
|
|
&subdiv_mesh->ldata, CD_ORIGINDEX);
|
||
|
|
ctx->poly_origindex = CustomData_get_layer(
|
||
|
|
&subdiv_mesh->pdata, CD_ORIGINDEX);
|
||
|
|
/* UV layers interpolation. */
|
||
|
|
cache_uv_layers(ctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
Mesh *BKE_subdiv_to_mesh(
|
||
|
|
Subdiv *subdiv,
|
||
|
|
const SubdivToMeshSettings *settings,
|
||
|
|
const Mesh *coarse_mesh)
|
||
|
|
{
|
||
|
|
/* Make sure evaluator is up to date with possible new topology, and that
|
||
|
|
* is is refined for the new positions of coarse vertices.
|
||
|
|
*/
|
||
|
|
BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh);
|
||
|
|
const int resolution = settings->resolution;
|
||
|
|
const int resolution2 = resolution * resolution;
|
||
|
|
const int num_result_verts = subdiv->num_ptex_faces * resolution2;
|
||
|
|
const int num_result_edges =
|
||
|
|
subdiv->num_ptex_faces * num_edges_per_ptex_get(resolution);
|
||
|
|
const int num_result_polys =
|
||
|
|
subdiv->num_ptex_faces * num_polys_per_ptex_get(resolution);
|
||
|
|
const int num_result_loops = 4 * num_result_polys;
|
||
|
|
/* Create mesh and its arrays. */
|
||
|
|
Mesh *result = BKE_mesh_new_nomain_from_template(
|
||
|
|
coarse_mesh,
|
||
|
|
num_result_verts,
|
||
|
|
num_result_edges,
|
||
|
|
0,
|
||
|
|
num_result_loops,
|
||
|
|
num_result_polys);
|
||
|
|
/* Evaluate subdivisions of base faces in threads. */
|
||
|
|
SubdivMeshContext ctx;
|
||
|
|
ctx.coarse_mesh = coarse_mesh;
|
||
|
|
ctx.subdiv = subdiv;
|
||
|
|
ctx.subdiv_mesh = result;
|
||
|
|
ctx.settings = settings;
|
||
|
|
cache_custom_data_layers(&ctx);
|
||
|
|
/* Multi-threaded evaluation. */
|
||
|
|
ParallelRangeSettings parallel_range_settings;
|
||
|
|
BLI_parallel_range_settings_defaults(¶llel_range_settings);
|
||
|
|
BLI_task_parallel_range(0, coarse_mesh->totpoly,
|
||
|
|
&ctx,
|
||
|
|
subdiv_eval_task,
|
||
|
|
¶llel_range_settings);
|
||
|
|
return result;
|
||
|
|
}
|