Subdiv: Initial implementation of CCG
Attempts to substitude CCGDM with an OpenSubdiv based structure which has less abstraction levels. The missing part in this substitude is a face pointers which old CCGDM/multires code was using to stitch faces (averaging boundaries). Another curial bit missing: "reshaping" of multires CD_MDISPS to the state of new PBVH grids. The new code is only available when OpenSubdiv modifier is enabled (WITH_OPENSUBDIV_MODIFIER=ON) and with debug value of 128. This is so this WIP code is not interfering with current production machines in the studio. Reviewers: brecht Reviewed By: brecht Differential Revision: https://developer.blender.org/D3685
This commit is contained in:
@@ -261,7 +261,7 @@ int BKE_sculpt_mask_layers_ensure(struct Object *ob,
|
||||
struct MultiresModifierData *mmd);
|
||||
void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene);
|
||||
|
||||
struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Object *ob, struct Mesh *me_eval_deform);
|
||||
struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Depsgraph *depsgraph, struct Object *ob);
|
||||
|
||||
enum {
|
||||
SCULPT_MASK_LAYER_CALC_VERT = (1 << 0),
|
||||
|
@@ -66,6 +66,8 @@ typedef enum eSubdivStatsValue {
|
||||
SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY,
|
||||
SUBDIV_STATS_EVALUATOR_CREATE,
|
||||
SUBDIV_STATS_EVALUATOR_REFINE,
|
||||
SUBDIV_STATS_SUBDIV_TO_CCG,
|
||||
SUBDIV_STATS_SUBDIV_TO_CCG_ELEMENTS,
|
||||
|
||||
NUM_SUBDIV_STATS_VALUES,
|
||||
} eSubdivStatsValue;
|
||||
@@ -86,6 +88,10 @@ typedef struct SubdivStats {
|
||||
double evaluator_creation_time;
|
||||
/* Time spent on evaluator->refine(). */
|
||||
double evaluator_refine_time;
|
||||
/* Total time spent on whole CCG creation. */
|
||||
double subdiv_to_ccg_time;
|
||||
/* Time spent on CCG elements evaluation/initialization. */
|
||||
double subdiv_to_ccg_elements_time;
|
||||
};
|
||||
double values_[NUM_SUBDIV_STATS_VALUES];
|
||||
};
|
||||
|
@@ -33,10 +33,12 @@
|
||||
#define __BKE_SUBDIV_CCG_H__
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
struct CCGElem;
|
||||
struct CCGKey;
|
||||
struct DMFlagMat;
|
||||
struct Mesh;
|
||||
struct Subdiv;
|
||||
|
||||
@@ -67,7 +69,12 @@ typedef struct SubdivCCG {
|
||||
* corresponding to face-corners of coarse mesh, each grid has
|
||||
* grid_size^2 elements.
|
||||
*/
|
||||
/* Indexed by a grid index, points to a grid data which is stored in
|
||||
* grids_storage.
|
||||
*/
|
||||
struct CCGElem **grids;
|
||||
/* Flat array of all grids' data. */
|
||||
unsigned char *grids_storage;
|
||||
int num_grids;
|
||||
/* Loose edges, each array element contains grid_size elements
|
||||
* corresponding to vertices created by subdividing coarse edges.
|
||||
@@ -91,6 +98,9 @@ typedef struct SubdivCCG {
|
||||
int normal_offset;
|
||||
int mask_offset;
|
||||
|
||||
struct DMFlagMat *grid_flag_mats;
|
||||
BLI_bitmap **grid_hidden;
|
||||
|
||||
/* TODO(sergey): Consider adding some accessors to a "decoded" geometry,
|
||||
* to make integration with draw manager and such easy.
|
||||
*/
|
||||
@@ -106,9 +116,18 @@ struct SubdivCCG *BKE_subdiv_to_ccg(
|
||||
const SubdivToCCGSettings *settings,
|
||||
const struct Mesh *coarse_mesh);
|
||||
|
||||
|
||||
/* Destroy CCG representation of subdivision surface. */
|
||||
void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg);
|
||||
|
||||
/* Helper function, creates Mesh structure which is properly setup to use
|
||||
* grids.
|
||||
*/
|
||||
struct Mesh *BKE_subdiv_to_ccg_mesh(
|
||||
struct Subdiv *subdiv,
|
||||
const SubdivToCCGSettings *settings,
|
||||
const struct Mesh *coarse_mesh);
|
||||
|
||||
/* Create a key for accessing grid elements at a given level. */
|
||||
void BKE_subdiv_ccg_key(
|
||||
struct CCGKey *key, const SubdivCCG *subdiv_ccg, int level);
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include "BKE_bvhutils.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_subdiv_ccg.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mesh Runtime Struct Utils
|
||||
@@ -196,6 +197,11 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
|
||||
{
|
||||
bvhcache_free(&mesh->runtime.bvh_cache);
|
||||
MEM_SAFE_FREE(mesh->runtime.looptris.array);
|
||||
/* TODO(sergey): Does this really belong here? */
|
||||
if (mesh->runtime.subsurf_ccg != NULL) {
|
||||
BKE_subdiv_ccg_destroy(mesh->runtime.subsurf_ccg);
|
||||
mesh->runtime.subsurf_ccg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -55,6 +55,7 @@
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_ccg.h"
|
||||
#include "BKE_colortools.h"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_main.h"
|
||||
@@ -72,6 +73,7 @@
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_pbvh.h"
|
||||
#include "BKE_subdiv_ccg.h"
|
||||
#include "BKE_subsurf.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@@ -910,7 +912,6 @@ void BKE_sculpt_update_mesh_elements(
|
||||
ss->kb = (mmd == NULL) ? BKE_keyblock_from_object(ob) : NULL;
|
||||
|
||||
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob, CD_MASK_BAREMESH);
|
||||
Mesh *me_eval_deform = mesh_get_eval_deform(depsgraph, scene, ob, CD_MASK_BAREMESH);
|
||||
|
||||
/* VWPaint require mesh info for loop lookup, so require sculpt mode here */
|
||||
if (mmd && ob->mode & OB_MODE_SCULPT) {
|
||||
@@ -931,7 +932,7 @@ void BKE_sculpt_update_mesh_elements(
|
||||
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
|
||||
}
|
||||
|
||||
PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(ob, me_eval_deform);
|
||||
PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
|
||||
BLI_assert(pbvh == ss->pbvh);
|
||||
UNUSED_VARS_NDEBUG(pbvh);
|
||||
MEM_SAFE_FREE(ss->pmap);
|
||||
@@ -1139,7 +1140,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
|
||||
return pbvh;
|
||||
}
|
||||
|
||||
static PBVH *build_regular_mesh_pbvh(Object *ob, Mesh *me_eval_deform)
|
||||
static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform)
|
||||
{
|
||||
Mesh *me = BKE_object_get_original_mesh(ob);
|
||||
const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop);
|
||||
@@ -1174,7 +1175,24 @@ static PBVH *build_regular_mesh_pbvh(Object *ob, Mesh *me_eval_deform)
|
||||
return pbvh;
|
||||
}
|
||||
|
||||
PBVH *BKE_sculpt_object_pbvh_ensure(Object *ob, Mesh *me_eval_deform)
|
||||
static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg)
|
||||
{
|
||||
CCGKey key;
|
||||
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
|
||||
PBVH *pbvh = BKE_pbvh_new();
|
||||
BKE_pbvh_build_grids(
|
||||
pbvh,
|
||||
subdiv_ccg->grids, subdiv_ccg->num_grids,
|
||||
&key,
|
||||
NULL,
|
||||
subdiv_ccg->grid_flag_mats,
|
||||
subdiv_ccg->grid_hidden);
|
||||
pbvh_show_diffuse_color_set(pbvh, ob->sculpt->show_diffuse_color);
|
||||
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
|
||||
return pbvh;
|
||||
}
|
||||
|
||||
PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
|
||||
{
|
||||
if (ob == NULL || ob->sculpt == NULL) {
|
||||
return NULL;
|
||||
@@ -1189,8 +1207,17 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Object *ob, Mesh *me_eval_deform)
|
||||
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
|
||||
pbvh = build_pbvh_for_dynamic_topology(ob);
|
||||
}
|
||||
else {
|
||||
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
|
||||
Mesh *mesh_eval = object_eval->data;
|
||||
if (mesh_eval->runtime.subsurf_ccg != NULL) {
|
||||
pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime.subsurf_ccg);
|
||||
}
|
||||
else if (ob->type == OB_MESH) {
|
||||
pbvh = build_regular_mesh_pbvh(ob, me_eval_deform);
|
||||
Mesh *me_eval_deform = mesh_get_eval_deform(
|
||||
depsgraph, DEG_get_evaluated_scene(depsgraph), ob, CD_MASK_BAREMESH);
|
||||
pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform);
|
||||
}
|
||||
}
|
||||
|
||||
ob->sculpt->pbvh = pbvh;
|
||||
|
@@ -34,24 +34,59 @@
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_task.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_ccg.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_eval.h"
|
||||
|
||||
/* =============================================================================
|
||||
* Generally useful internal helpers.
|
||||
*/
|
||||
|
||||
/* For a given subdivision level (NOT the refinement level) get resolution
|
||||
* of grid.
|
||||
*/
|
||||
static int grid_size_for_level_get(const SubdivCCG *subdiv_ccg, int level)
|
||||
{
|
||||
BLI_assert(level >= 1);
|
||||
BLI_assert(level <= subdiv_ccg->level);
|
||||
UNUSED_VARS_NDEBUG(subdiv_ccg);
|
||||
return (1 << (level - 1)) + 1;
|
||||
}
|
||||
|
||||
/* Number of floats in per-vertex elements. */
|
||||
static int num_element_float_get(const SubdivCCG *subdiv_ccg)
|
||||
{
|
||||
/* We always have 3 floats for coordinate. */
|
||||
int num_floats = 3;
|
||||
if (subdiv_ccg->has_normal) {
|
||||
num_floats += 3;
|
||||
}
|
||||
if (subdiv_ccg->has_mask) {
|
||||
num_floats += 1;
|
||||
}
|
||||
return num_floats;
|
||||
}
|
||||
|
||||
/* Per-vertex element size in bytes. */
|
||||
static int element_size_bytes_get(const SubdivCCG *subdiv_ccg)
|
||||
{
|
||||
return sizeof(float) * num_element_float_get(subdiv_ccg);
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* Internal helpers for CCG creation.
|
||||
*/
|
||||
|
||||
static void subdiv_ccg_init_layers(SubdivCCG *subdiv_ccg,
|
||||
const SubdivToCCGSettings *settings)
|
||||
{
|
||||
/* CCG always contains coordinates. Rest of layers are coming after them. */
|
||||
int layer_offset = sizeof(float) * 3;
|
||||
/* Normals. */
|
||||
if (settings->need_normal) {
|
||||
subdiv_ccg->has_normal = true;
|
||||
subdiv_ccg->normal_offset = layer_offset;
|
||||
layer_offset += sizeof(float) * 3;
|
||||
}
|
||||
else {
|
||||
subdiv_ccg->has_normal = false;
|
||||
subdiv_ccg->normal_offset = -1;
|
||||
}
|
||||
/* Mask. */
|
||||
if (settings->need_mask) {
|
||||
subdiv_ccg->has_mask = true;
|
||||
@@ -62,55 +97,278 @@ static void subdiv_ccg_init_layers(SubdivCCG *subdiv_ccg,
|
||||
subdiv_ccg->has_mask = false;
|
||||
subdiv_ccg->mask_offset = -1;
|
||||
}
|
||||
/* Normals.
|
||||
*
|
||||
* NOTE: Keep them at the end, matching old CCGDM. Doesn't really matter
|
||||
* here, but some other area might in theory depend memory layout.
|
||||
*/
|
||||
if (settings->need_normal) {
|
||||
subdiv_ccg->has_normal = true;
|
||||
subdiv_ccg->normal_offset = layer_offset;
|
||||
layer_offset += sizeof(float) * 3;
|
||||
}
|
||||
else {
|
||||
subdiv_ccg->has_normal = false;
|
||||
subdiv_ccg->normal_offset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int grid_size_for_level_get(const SubdivCCG *subdiv_ccg, int level)
|
||||
/* NOTE: Grid size and layer flags are to be filled in before calling this
|
||||
* function.
|
||||
*/
|
||||
static void subdiv_ccg_alloc_elements(SubdivCCG *subdiv_ccg,
|
||||
const Mesh *coarse_mesh)
|
||||
{
|
||||
BLI_assert(level >= 1);
|
||||
BLI_assert(level <= subdiv_ccg->level);
|
||||
UNUSED_VARS_NDEBUG(subdiv_ccg);
|
||||
return (1 << (level - 1)) + 1;
|
||||
const int element_size = element_size_bytes_get(subdiv_ccg);
|
||||
/* Allocate memory for surface grids. */
|
||||
const int num_grids = coarse_mesh->totloop;
|
||||
const int grid_size = grid_size_for_level_get(
|
||||
subdiv_ccg, subdiv_ccg->level);
|
||||
const int grid_area = grid_size * grid_size;
|
||||
subdiv_ccg->num_grids = num_grids;
|
||||
subdiv_ccg->grids =
|
||||
MEM_calloc_arrayN(num_grids, sizeof(CCGElem *), "subdiv ccg grids");
|
||||
subdiv_ccg->grids_storage = MEM_calloc_arrayN(
|
||||
num_grids, ((size_t)grid_area) * element_size,
|
||||
"subdiv ccg grids storage");
|
||||
const size_t grid_size_in_bytes = (size_t)grid_area * element_size;
|
||||
for (int grid_index = 0; grid_index < num_grids; grid_index++) {
|
||||
const size_t grid_offset = grid_size_in_bytes * grid_index;
|
||||
subdiv_ccg->grids[grid_index] =
|
||||
(CCGElem *)&subdiv_ccg->grids_storage[grid_offset];
|
||||
}
|
||||
/* Grid material flags. */
|
||||
subdiv_ccg->grid_flag_mats = MEM_calloc_arrayN(
|
||||
num_grids, sizeof(DMFlagMat), "ccg grid material flags");
|
||||
/* Grid hidden flags. */
|
||||
subdiv_ccg->grid_hidden = MEM_calloc_arrayN(
|
||||
num_grids, sizeof(BLI_bitmap *), "ccg grid material flags");
|
||||
for (int grid_index = 0; grid_index < num_grids; grid_index++) {
|
||||
subdiv_ccg->grid_hidden[grid_index] =
|
||||
BLI_BITMAP_NEW(grid_area, "ccg grid hidden");
|
||||
}
|
||||
/* TOOD(sergey): Allocate memory for loose elements. */
|
||||
}
|
||||
|
||||
/* Per-vertex element size in bytes. */
|
||||
static int element_size_get(const SubdivCCG *subdiv_ccg)
|
||||
/* =============================================================================
|
||||
* Grids evaluation.
|
||||
*/
|
||||
|
||||
typedef struct CCGEvalGridsData {
|
||||
SubdivCCG *subdiv_ccg;
|
||||
Subdiv *subdiv;
|
||||
const Mesh *coarse_mesh;
|
||||
int *face_petx_offset;
|
||||
} CCGEvalGridsData;
|
||||
|
||||
static void subdiv_ccg_eval_grid_element(
|
||||
CCGEvalGridsData *data,
|
||||
const int ptex_face_index,
|
||||
const float u, const float v,
|
||||
unsigned char *element)
|
||||
{
|
||||
/* We always have 3 floats for coordinate. */
|
||||
int num_floats = 3;
|
||||
if (subdiv_ccg->has_normal) {
|
||||
num_floats += 3;
|
||||
/* TODO(sergey): Support displacement. */
|
||||
if (data->subdiv_ccg->has_normal) {
|
||||
BKE_subdiv_eval_limit_point_and_normal(
|
||||
data->subdiv, ptex_face_index, u, v,
|
||||
(float *)element,
|
||||
(float *)(element + data->subdiv_ccg->normal_offset));
|
||||
}
|
||||
if (subdiv_ccg->has_mask) {
|
||||
num_floats += 1;
|
||||
else {
|
||||
BKE_subdiv_eval_limit_point(
|
||||
data->subdiv, ptex_face_index, u, v, (float *)element);
|
||||
}
|
||||
return sizeof(float) * num_floats;
|
||||
}
|
||||
|
||||
BLI_INLINE void rotate_corner_to_quad(const int corner,
|
||||
const float u, const float v,
|
||||
float *r_u, float *r_v)
|
||||
{
|
||||
if (corner == 0) {
|
||||
*r_u = 0.5f - v * 0.5f;
|
||||
*r_v = 0.5f - u * 0.5f;
|
||||
}
|
||||
else if (corner == 1) {
|
||||
*r_u = 0.5f + u * 0.5f;
|
||||
*r_v = 0.5f - v * 0.5f;
|
||||
}
|
||||
else if (corner == 2) {
|
||||
*r_u = 0.5f + v * 0.5f;
|
||||
*r_v = 0.5f + u * 0.5f;
|
||||
}
|
||||
else if (corner == 3) {
|
||||
*r_u = 0.5f - u * 0.5f;
|
||||
*r_v = 0.5f + v * 0.5f;
|
||||
}
|
||||
else {
|
||||
BLI_assert(!"Unexpected corner configuration");
|
||||
}
|
||||
}
|
||||
|
||||
static void subdiv_ccg_eval_regular_grid(CCGEvalGridsData *data,
|
||||
const MPoly *coarse_poly)
|
||||
{
|
||||
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
|
||||
const int coarse_poly_index = coarse_poly - data->coarse_mesh->mpoly;
|
||||
const int ptex_face_index = data->face_petx_offset[coarse_poly_index];
|
||||
const int grid_size = subdiv_ccg->grid_size;
|
||||
const float grid_size_1_inv = 1.0f / (float)(grid_size - 1);
|
||||
const int element_size = element_size_bytes_get(subdiv_ccg);
|
||||
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
|
||||
unsigned char *grid = (unsigned char *)subdiv_ccg->grids[
|
||||
coarse_poly->loopstart + corner];
|
||||
for (int y = 0; y < grid_size; y++) {
|
||||
const float grid_v = (float)y * grid_size_1_inv;
|
||||
for (int x = 0; x < grid_size; x++) {
|
||||
const float grid_u = (float)x * grid_size_1_inv;
|
||||
float u, v;
|
||||
rotate_corner_to_quad(corner, grid_u, grid_v, &u, &v);
|
||||
const size_t grid_element_index = (size_t)y * grid_size + x;
|
||||
const size_t grid_element_offset =
|
||||
grid_element_index * element_size;
|
||||
subdiv_ccg_eval_grid_element(
|
||||
data,
|
||||
ptex_face_index, u, v,
|
||||
&grid[grid_element_offset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void subdiv_ccg_eval_special_grid(CCGEvalGridsData *data,
|
||||
const MPoly *coarse_poly)
|
||||
{
|
||||
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
|
||||
const int coarse_poly_index = coarse_poly - data->coarse_mesh->mpoly;
|
||||
const int grid_size = subdiv_ccg->grid_size;
|
||||
const float grid_size_1_inv = 1.0f / (float)(grid_size - 1);
|
||||
const int element_size = element_size_bytes_get(subdiv_ccg);
|
||||
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
|
||||
unsigned char *grid = (unsigned char *)subdiv_ccg->grids[
|
||||
coarse_poly->loopstart + corner];
|
||||
for (int y = 0; y < grid_size; y++) {
|
||||
const float u = 1.0f - ((float)y * grid_size_1_inv);
|
||||
for (int x = 0; x < grid_size; x++) {
|
||||
const float v = 1.0f - ((float)x * grid_size_1_inv);
|
||||
const int ptex_face_index =
|
||||
data->face_petx_offset[coarse_poly_index] + corner;
|
||||
const size_t grid_element_index = (size_t)y * grid_size + x;
|
||||
const size_t grid_element_offset =
|
||||
grid_element_index * element_size;
|
||||
subdiv_ccg_eval_grid_element(
|
||||
data,
|
||||
ptex_face_index, u, v,
|
||||
&grid[grid_element_offset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void subdiv_ccg_eval_grids_task(
|
||||
void *__restrict userdata_v,
|
||||
const int coarse_poly_index,
|
||||
const ParallelRangeTLS *__restrict UNUSED(tls))
|
||||
{
|
||||
CCGEvalGridsData *data = userdata_v;
|
||||
const Mesh *coarse_mesh = data->coarse_mesh;
|
||||
const MPoly *coarse_mpoly = coarse_mesh->mpoly;
|
||||
const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
|
||||
if (coarse_poly->totloop == 4) {
|
||||
subdiv_ccg_eval_regular_grid(data, coarse_poly);
|
||||
}
|
||||
else {
|
||||
subdiv_ccg_eval_special_grid(data, coarse_poly);
|
||||
}
|
||||
}
|
||||
|
||||
static bool subdiv_ccg_evaluate_grids(SubdivCCG *subdiv_ccg,
|
||||
Subdiv *subdiv,
|
||||
const Mesh *coarse_mesh)
|
||||
{
|
||||
/* Make sure evaluator is ready. */
|
||||
if (!BKE_subdiv_eval_update_from_mesh(subdiv, coarse_mesh)) {
|
||||
if (coarse_mesh->totpoly) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Initialize data passed to all the tasks. */
|
||||
CCGEvalGridsData data;
|
||||
data.subdiv_ccg = subdiv_ccg;
|
||||
data.subdiv = subdiv;
|
||||
data.coarse_mesh = coarse_mesh;
|
||||
data.face_petx_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
|
||||
/* Threaded grids evaluation/ */
|
||||
ParallelRangeSettings parallel_range_settings;
|
||||
BLI_parallel_range_settings_defaults(¶llel_range_settings);
|
||||
BLI_task_parallel_range(0, coarse_mesh->totpoly,
|
||||
&data,
|
||||
subdiv_ccg_eval_grids_task,
|
||||
¶llel_range_settings);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* Public API.
|
||||
*/
|
||||
|
||||
SubdivCCG *BKE_subdiv_to_ccg(
|
||||
Subdiv *UNUSED(subdiv),
|
||||
Subdiv *subdiv,
|
||||
const SubdivToCCGSettings *settings,
|
||||
const Mesh *UNUSED(coarse_mesh))
|
||||
const Mesh *coarse_mesh)
|
||||
{
|
||||
SubdivCCG *subdiv_ccg = MEM_callocN(sizeof(SubdivCCG *), "subdiv ccg");
|
||||
subdiv_ccg->level = settings->resolution >> 1;
|
||||
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
||||
SubdivCCG *subdiv_ccg = MEM_callocN(sizeof(SubdivCCG), "subdiv ccg");
|
||||
subdiv_ccg->level = bitscan_forward_i(settings->resolution - 1);
|
||||
subdiv_ccg->grid_size =
|
||||
grid_size_for_level_get(subdiv_ccg, subdiv_ccg->level);
|
||||
subdiv_ccg_init_layers(subdiv_ccg, settings);
|
||||
subdiv_ccg_alloc_elements(subdiv_ccg, coarse_mesh);
|
||||
if (!subdiv_ccg_evaluate_grids(subdiv_ccg, subdiv, coarse_mesh)) {
|
||||
BKE_subdiv_ccg_destroy(subdiv_ccg);
|
||||
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
||||
return NULL;
|
||||
}
|
||||
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
||||
return subdiv_ccg;
|
||||
}
|
||||
|
||||
Mesh *BKE_subdiv_to_ccg_mesh(
|
||||
Subdiv *subdiv,
|
||||
const SubdivToCCGSettings *settings,
|
||||
const Mesh *coarse_mesh)
|
||||
{
|
||||
SubdivCCG *subdiv_ccg = BKE_subdiv_to_ccg(
|
||||
subdiv, settings, coarse_mesh);
|
||||
if (subdiv_ccg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
Mesh *result = BKE_mesh_new_nomain_from_template(
|
||||
coarse_mesh, 0, 0, 0, 0, 0);
|
||||
result->runtime.subsurf_ccg = subdiv_ccg;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg)
|
||||
{
|
||||
const int num_grids = subdiv_ccg->num_grids;
|
||||
MEM_SAFE_FREE(subdiv_ccg->grids);
|
||||
MEM_SAFE_FREE(subdiv_ccg->grids_storage);
|
||||
MEM_SAFE_FREE(subdiv_ccg->edges);
|
||||
MEM_SAFE_FREE(subdiv_ccg->vertices);
|
||||
MEM_SAFE_FREE(subdiv_ccg->grid_flag_mats);
|
||||
if (subdiv_ccg->grid_hidden != NULL) {
|
||||
for (int grid_index = 0; grid_index < num_grids; grid_index++) {
|
||||
MEM_freeN(subdiv_ccg->grid_hidden[grid_index]);
|
||||
}
|
||||
MEM_freeN(subdiv_ccg->grid_hidden);
|
||||
}
|
||||
MEM_freeN(subdiv_ccg);
|
||||
}
|
||||
|
||||
void BKE_subdiv_ccg_key(CCGKey *key, const SubdivCCG *subdiv_ccg, int level)
|
||||
{
|
||||
key->level = level;
|
||||
key->elem_size = element_size_get(subdiv_ccg);
|
||||
key->elem_size = element_size_bytes_get(subdiv_ccg);
|
||||
key->grid_size = grid_size_for_level_get(subdiv_ccg, level);
|
||||
key->grid_area = key->grid_size * key->grid_size;
|
||||
key->grid_bytes = key->elem_size * key->grid_area;
|
||||
|
@@ -40,6 +40,8 @@ void BKE_subdiv_stats_init(SubdivStats *stats)
|
||||
stats->subdiv_to_mesh_geometry_time = 0.0;
|
||||
stats->evaluator_creation_time = 0.0;
|
||||
stats->evaluator_refine_time = 0.0;
|
||||
stats->subdiv_to_ccg_time = 0.0;
|
||||
stats->subdiv_to_ccg_elements_time = 0.0;
|
||||
}
|
||||
|
||||
void BKE_subdiv_stats_begin(SubdivStats *stats, eSubdivStatsValue value)
|
||||
@@ -79,6 +81,12 @@ void BKE_subdiv_stats_print(const SubdivStats *stats)
|
||||
STATS_PRINT_TIME(stats,
|
||||
evaluator_refine_time,
|
||||
"Evaluator refine time");
|
||||
STATS_PRINT_TIME(stats,
|
||||
subdiv_to_ccg_time,
|
||||
"Subdivision to CCG time");
|
||||
STATS_PRINT_TIME(stats,
|
||||
subdiv_to_ccg_elements_time,
|
||||
" Elements time");
|
||||
|
||||
#undef STATS_PRINT_TIME
|
||||
}
|
||||
|
@@ -391,8 +391,7 @@ static int hide_show_exec(bContext *C, wmOperator *op)
|
||||
|
||||
clip_planes_from_rect(C, clip_planes, &rect);
|
||||
|
||||
Mesh *me_eval_deform = mesh_get_eval_deform(depsgraph, CTX_data_scene(C), ob, CD_MASK_BAREMESH);
|
||||
pbvh = BKE_sculpt_object_pbvh_ensure(ob, me_eval_deform);
|
||||
pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
|
||||
BLI_assert(ob->sculpt->pbvh == pbvh);
|
||||
|
||||
get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area);
|
||||
|
@@ -53,6 +53,7 @@ struct MVert;
|
||||
struct Material;
|
||||
struct Mesh;
|
||||
struct Multires;
|
||||
struct SubdivCCG;
|
||||
|
||||
#
|
||||
#
|
||||
@@ -83,6 +84,8 @@ struct MLoopTri_Store {
|
||||
typedef struct Mesh_Runtime {
|
||||
struct EditMeshData *edit_data;
|
||||
void *batch_cache;
|
||||
struct SubdivCCG *subsurf_ccg;
|
||||
void *pad1;
|
||||
|
||||
int64_t cd_dirty_vert;
|
||||
int64_t cd_dirty_edge;
|
||||
|
@@ -42,10 +42,12 @@
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_multires.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_ccg.h"
|
||||
#include "BKE_subdiv_mesh.h"
|
||||
#include "BKE_subsurf.h"
|
||||
|
||||
@@ -143,25 +145,74 @@ static DerivedMesh *applyModifier(
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENSUBDIV_MODIFIER
|
||||
static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
|
||||
/* Subdivide into fully qualified mesh. */
|
||||
|
||||
static Mesh *multires_as_mesh(MultiresModifierData *mmd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh)
|
||||
Mesh *mesh,
|
||||
Subdiv *subdiv)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER);
|
||||
const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY);
|
||||
const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
|
||||
Object *object = ctx->object;
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
BKE_multires_subdiv_mesh_settings_init(
|
||||
&mesh_settings, scene, object, mmd, use_render_params, ignore_simplify);
|
||||
if (mesh_settings.resolution < 3) {
|
||||
return result;
|
||||
}
|
||||
BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd);
|
||||
result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Subdivide into CCG. */
|
||||
|
||||
static void multires_ccg_settings_init(SubdivToCCGSettings *settings,
|
||||
const MultiresModifierData *mmd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh)
|
||||
{
|
||||
const bool has_mask =
|
||||
CustomData_has_layer(&mesh->ldata, CD_GRID_PAINT_MASK);
|
||||
const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER);
|
||||
const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY);
|
||||
const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
|
||||
Object *object = ctx->object;
|
||||
const int level = multires_get_level(
|
||||
scene, object, mmd, use_render_params, ignore_simplify);
|
||||
settings->resolution = (1 << level) + 1;
|
||||
settings->need_normal = true;
|
||||
settings->need_mask = has_mask;
|
||||
}
|
||||
|
||||
static Mesh *multires_as_ccg(MultiresModifierData *mmd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh,
|
||||
Subdiv *subdiv)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
SubdivToCCGSettings ccg_settings;
|
||||
multires_ccg_settings_init(&ccg_settings, mmd, ctx, mesh);
|
||||
if (ccg_settings.resolution < 3) {
|
||||
return result;
|
||||
}
|
||||
result = BKE_subdiv_to_ccg_mesh(subdiv, &ccg_settings, mesh);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
MultiresModifierData *mmd = (MultiresModifierData *)md;
|
||||
SubdivSettings subdiv_settings;
|
||||
BKE_multires_subdiv_settings_init(&subdiv_settings, mmd);
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
BKE_multires_subdiv_mesh_settings_init(
|
||||
&mesh_settings, scene, object, mmd, use_render_params, ignore_simplify);
|
||||
if (subdiv_settings.level == 0 || mesh_settings.resolution < 3) {
|
||||
/* NOTE: Shouldn't really happen, is supposed to be catched by
|
||||
* isDisabled() callback.
|
||||
*/
|
||||
if (subdiv_settings.level == 0) {
|
||||
return result;
|
||||
}
|
||||
/* TODO(sergey): Try to re-use subdiv when possible. */
|
||||
@@ -170,8 +221,17 @@ static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
/* Happens on bad topology, ut also on empty input mesh. */
|
||||
return result;
|
||||
}
|
||||
BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd);
|
||||
result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
/* TODO(sergey): Some of production machines are using OpenSubdiv already.
|
||||
* so better not enable semi-finished multires sculpting for now. Will give
|
||||
* a wrong impression that things do work, eben though crucial areas are
|
||||
* styill missing in implementation.
|
||||
*/
|
||||
if ((ctx->object->mode & OB_MODE_SCULPT) && G.debug_value == 128) {
|
||||
result = multires_as_ccg(mmd, ctx, mesh, subdiv);
|
||||
}
|
||||
else {
|
||||
result = multires_as_mesh(mmd, ctx, mesh, subdiv);
|
||||
}
|
||||
/* TODO(sergey): Cache subdiv somehow. */
|
||||
// BKE_subdiv_stats_print(&subdiv->stats);
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
@@ -44,6 +44,7 @@
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_subdiv.h"
|
||||
#include "BKE_subdiv_ccg.h"
|
||||
#include "BKE_subdiv_mesh.h"
|
||||
#include "BKE_subsurf.h"
|
||||
|
||||
@@ -170,6 +171,8 @@ static void subdiv_settings_init(SubdivSettings *settings,
|
||||
BKE_subdiv_fvar_interpolation_from_uv_smooth(smd->uv_smooth);
|
||||
}
|
||||
|
||||
/* Subdivide into fully qualified mesh. */
|
||||
|
||||
static void subdiv_mesh_settings_init(SubdivToMeshSettings *settings,
|
||||
const SubsurfModifierData *smd,
|
||||
const ModifierEvalContext *ctx)
|
||||
@@ -178,6 +181,50 @@ static void subdiv_mesh_settings_init(SubdivToMeshSettings *settings,
|
||||
settings->resolution = (1 << level) + 1;
|
||||
}
|
||||
|
||||
static Mesh *subdiv_as_mesh(SubsurfModifierData *smd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh,
|
||||
Subdiv *subdiv)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
subdiv_mesh_settings_init(&mesh_settings, smd, ctx);
|
||||
if (mesh_settings.resolution < 3) {
|
||||
return result;
|
||||
}
|
||||
result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Subdivide into CCG. */
|
||||
|
||||
static void subdiv_ccg_settings_init(SubdivToCCGSettings *settings,
|
||||
const SubsurfModifierData *smd,
|
||||
const ModifierEvalContext *ctx)
|
||||
{
|
||||
const int level = subdiv_levels_for_modifier_get(smd, ctx);
|
||||
settings->resolution = (1 << level) + 1;
|
||||
settings->need_normal = true;
|
||||
settings->need_mask = false;
|
||||
}
|
||||
|
||||
static Mesh *subdiv_as_ccg(SubsurfModifierData *smd,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh,
|
||||
Subdiv *subdiv)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
SubdivToCCGSettings ccg_settings;
|
||||
subdiv_ccg_settings_init(&ccg_settings, smd, ctx);
|
||||
if (ccg_settings.resolution < 3) {
|
||||
return result;
|
||||
}
|
||||
result = BKE_subdiv_to_ccg_mesh(subdiv, &ccg_settings, mesh);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Modifier itself. */
|
||||
|
||||
static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
const ModifierEvalContext *ctx,
|
||||
Mesh *mesh)
|
||||
@@ -186,12 +233,7 @@ static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
SubsurfModifierData *smd = (SubsurfModifierData *) md;
|
||||
SubdivSettings subdiv_settings;
|
||||
subdiv_settings_init(&subdiv_settings, smd);
|
||||
SubdivToMeshSettings mesh_settings;
|
||||
subdiv_mesh_settings_init(&mesh_settings, smd, ctx);
|
||||
if (subdiv_settings.level == 0 || mesh_settings.resolution < 3) {
|
||||
/* NOTE: Shouldn't really happen, is supposed to be catched by
|
||||
* isDisabled() callback.
|
||||
*/
|
||||
if (subdiv_settings.level == 0) {
|
||||
return result;
|
||||
}
|
||||
/* TODO(sergey): Try to re-use subdiv when possible. */
|
||||
@@ -200,7 +242,15 @@ static Mesh *applyModifier_subdiv(ModifierData *md,
|
||||
/* Happens on bad topology, ut also on empty input mesh. */
|
||||
return result;
|
||||
}
|
||||
result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
|
||||
/* TODO(sergey): Decide whether we ever want to use CCG for subsurf,
|
||||
* maybe when it is a last modifier in the stack?
|
||||
*/
|
||||
if (true) {
|
||||
result = subdiv_as_mesh(smd, ctx, mesh, subdiv);
|
||||
}
|
||||
else {
|
||||
result = subdiv_as_ccg(smd, ctx, mesh, subdiv);
|
||||
}
|
||||
/* TODO(sergey): Cache subdiv somehow. */
|
||||
// BKE_subdiv_stats_print(&subdiv->stats);
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
Reference in New Issue
Block a user