Files
test2/source/blender/blenkernel/intern/subdiv_deform.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

235 lines
8.4 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "BKE_subdiv_deform.hh"
#include <cstring>
#include "DNA_mesh_types.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BKE_customdata.hh"
#include "BKE_subdiv.hh"
#include "BKE_subdiv_eval.hh"
#include "BKE_subdiv_foreach.hh"
#include "BKE_subdiv_mesh.hh"
#include "MEM_guardedalloc.h"
/* -------------------------------------------------------------------- */
/** \name Subdivision context
* \{ */
struct SubdivDeformContext {
const Mesh *coarse_mesh;
Subdiv *subdiv;
float (*vertex_cos)[3];
int num_verts;
/* Accumulated values.
*
* Averaging is happening for vertices which correspond to the coarse ones.
* This is needed for displacement.
*
* Displacement is being accumulated to a vertices coordinates, since those
* are not needed during traversal of face-vertices vertices. */
/* Per-subdivided vertex counter of averaged values. */
int *accumulated_counters;
bool have_displacement;
};
static void subdiv_mesh_prepare_accumulator(SubdivDeformContext *ctx, int num_vertices)
{
if (!ctx->have_displacement) {
return;
}
ctx->accumulated_counters = static_cast<int *>(
MEM_calloc_arrayN(num_vertices, sizeof(*ctx->accumulated_counters), __func__));
}
static void subdiv_mesh_context_free(SubdivDeformContext *ctx)
{
MEM_SAFE_FREE(ctx->accumulated_counters);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Accumulation helpers
* \{ */
static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx,
const int ptex_face_index,
const float u,
const float v,
int vertex_index)
{
Subdiv *subdiv = ctx->subdiv;
float dummy_P[3], dPdu[3], dPdv[3], D[3];
BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
/* Accumulate displacement if needed. */
if (ctx->have_displacement) {
BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
/* NOTE: The storage for vertex coordinates is coming from an external world, not necessarily
* initialized to zeroes. */
if (ctx->accumulated_counters[vertex_index] == 0) {
copy_v3_v3(ctx->vertex_cos[vertex_index], D);
}
else {
add_v3_v3(ctx->vertex_cos[vertex_index], D);
}
}
++ctx->accumulated_counters[vertex_index];
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subdivision callbacks
* \{ */
static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context,
const int /*num_vertices*/,
const int /*num_edges*/,
const int /*num_loops*/,
const int /*num_faces*/,
const int * /*subdiv_face_offset*/)
{
SubdivDeformContext *subdiv_context = static_cast<SubdivDeformContext *>(
foreach_context->user_data);
subdiv_mesh_prepare_accumulator(subdiv_context, subdiv_context->coarse_mesh->verts_num);
return true;
}
static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context,
void * /*tls*/,
const int ptex_face_index,
const float u,
const float v,
const int coarse_vertex_index,
const int /*coarse_face_index*/,
const int /*coarse_corner*/,
const int /*subdiv_vertex_index*/)
{
SubdivDeformContext *ctx = static_cast<SubdivDeformContext *>(foreach_context->user_data);
subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, coarse_vertex_index);
}
static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_context,
void * /*tls*/,
const int ptex_face_index,
const float u,
const float v,
const int coarse_vertex_index,
const int /*coarse_face_index*/,
const int /*coarse_corner*/,
const int /*subdiv_vertex_index*/)
{
SubdivDeformContext *ctx = static_cast<SubdivDeformContext *>(foreach_context->user_data);
BLI_assert(coarse_vertex_index != ORIGINDEX_NONE);
BLI_assert(coarse_vertex_index < ctx->num_verts);
float inv_num_accumulated = 1.0f;
if (ctx->accumulated_counters != nullptr) {
inv_num_accumulated = 1.0f / ctx->accumulated_counters[coarse_vertex_index];
}
/* Displacement is accumulated in subdiv vertex position.
* Needs to be backed up before copying data from original vertex. */
float D[3] = {0.0f, 0.0f, 0.0f};
float *vertex_co = ctx->vertex_cos[coarse_vertex_index];
if (ctx->have_displacement) {
copy_v3_v3(D, vertex_co);
mul_v3_fl(D, inv_num_accumulated);
}
/* Copy custom data and evaluate position. */
BKE_subdiv_eval_limit_point(ctx->subdiv, ptex_face_index, u, v, vertex_co);
/* Apply displacement. */
add_v3_v3(vertex_co, D);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Initialization
* \{ */
static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context,
SubdivForeachContext *foreach_context)
{
memset(foreach_context, 0, sizeof(*foreach_context));
/* General information. */
foreach_context->topology_info = subdiv_mesh_topology_info;
/* Every boundary geometry. Used for displacement and normals averaging. */
if (subdiv_context->have_displacement) {
foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
}
foreach_context->vertex_corner = subdiv_mesh_vertex_corner;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public entry point
* \{ */
void BKE_subdiv_deform_coarse_vertices(Subdiv *subdiv,
const Mesh *coarse_mesh,
float (*vertex_cos)[3],
int num_verts)
{
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Make sure evaluator is up to date with possible new topology, and that
2019-11-25 00:55:11 +11:00
* is refined for the new positions of coarse vertices. */
OpenSubDiv: add support for an OpenGL evaluator This evaluator is used in order to evaluate subdivision at render time, allowing for faster renders of meshes with a subdivision surface modifier placed at the last position in the modifier list. When evaluating the subsurf modifier, we detect whether we can delegate evaluation to the draw code. If so, the subdivision is first evaluated on the GPU using our own custom evaluator (only the coarse data needs to be initially sent to the GPU), then, buffers for the final `MeshBufferCache` are filled on the GPU using a set of compute shaders. However, some buffers are still filled on the CPU side, if doing so on the GPU is impractical (e.g. the line adjacency buffer used for x-ray, whose logic is hardly GPU compatible). This is done at the mesh buffer extraction level so that the result can be readily used in the various OpenGL engines, without having to write custom geometry or tesselation shaders. We use our own subdivision evaluation shaders, instead of OpenSubDiv's vanilla one, in order to control the data layout, and interpolation. For example, we store vertex colors as compressed 16-bit integers, while OpenSubDiv's default evaluator only work for float types. In order to still access the modified geometry on the CPU side, for use in modifiers or transform operators, a dedicated wrapper type is added `MESH_WRAPPER_TYPE_SUBD`. Subdivision will be lazily evaluated via `BKE_object_get_evaluated_mesh` which will create such a wrapper if possible. If the final subdivision surface is not needed on the CPU side, `BKE_object_get_evaluated_mesh_no_subsurf` should be used. Enabling or disabling GPU subdivision can be done through the user preferences (under Viewport -> Subdivision). See patch description for benchmarks. Reviewed By: campbellbarton, jbakker, fclem, brecht, #eevee_viewport Differential Revision: https://developer.blender.org/D12406
2021-12-27 16:34:47 +01:00
if (!BKE_subdiv_eval_begin_from_mesh(
subdiv, coarse_mesh, vertex_cos, SUBDIV_EVALUATOR_TYPE_CPU, nullptr))
{
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
* topology.
* In either way, we can't safely continue. */
if (coarse_mesh->faces_num) {
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
return;
}
}
/* Initialize subdivision mesh creation context. */
2023-08-03 19:14:53 +10:00
SubdivDeformContext subdiv_context = {nullptr};
subdiv_context.coarse_mesh = coarse_mesh;
subdiv_context.subdiv = subdiv;
subdiv_context.vertex_cos = vertex_cos;
subdiv_context.num_verts = num_verts;
subdiv_context.have_displacement = (subdiv->displacement_evaluator != nullptr);
SubdivForeachContext foreach_context;
setup_foreach_callbacks(&subdiv_context, &foreach_context);
foreach_context.user_data = &subdiv_context;
/* Dummy mesh rasterization settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = 1;
mesh_settings.use_optimal_display = false;
/* Multi-threaded traversal/evaluation. */
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
BKE_subdiv_foreach_subdiv_geometry(subdiv, &foreach_context, &mesh_settings, coarse_mesh);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
// BKE_mesh_validate(result, true, true);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
/* Free used memory. */
subdiv_mesh_context_free(&subdiv_context);
}
/** \} */