The copied the material index and its smoothness to every grid, resulting in 4 bytes of memory per base mesh face corner. That's wasteful, since it's trivial to loop up the original data from the base mesh attributes as necessary. This way we can also avoid the dereference.
1678 lines
65 KiB
C++
1678 lines
65 KiB
C++
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include "BKE_subdiv_ccg.hh"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_enumerable_thread_specific.hh"
|
|
#include "BLI_math_bits.h"
|
|
#include "BLI_math_geom.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_task.hh"
|
|
#include "BLI_vector_set.hh"
|
|
|
|
#include "BKE_DerivedMesh.hh"
|
|
#include "BKE_ccg.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_mesh.hh"
|
|
#include "BKE_subdiv.hh"
|
|
#include "BKE_subdiv_eval.hh"
|
|
|
|
#include "opensubdiv_topology_refiner_capi.h"
|
|
|
|
using blender::Array;
|
|
using blender::float3;
|
|
using blender::GrainSize;
|
|
using blender::IndexMask;
|
|
using blender::IndexMaskMemory;
|
|
using blender::IndexRange;
|
|
using blender::MutableSpan;
|
|
using blender::OffsetIndices;
|
|
using blender::Span;
|
|
using blender::Vector;
|
|
using blender::VectorSet;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Various forward declarations
|
|
* \{ */
|
|
|
|
static void subdiv_ccg_average_inner_face_grids(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexRange face);
|
|
|
|
void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexMask &face_mask);
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generally useful internal helpers
|
|
* \{ */
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name 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]);
|
|
/* Mask. */
|
|
if (settings.need_mask) {
|
|
subdiv_ccg.has_mask = true;
|
|
subdiv_ccg.mask_offset = layer_offset;
|
|
layer_offset += sizeof(float);
|
|
}
|
|
else {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* TODO(sergey): Make it more accessible function. */
|
|
static int topology_refiner_count_face_corners(OpenSubdiv_TopologyRefiner *topology_refiner)
|
|
{
|
|
const int num_faces = topology_refiner->getNumFaces(topology_refiner);
|
|
int num_corners = 0;
|
|
for (int face_index = 0; face_index < num_faces; face_index++) {
|
|
num_corners += topology_refiner->getNumFaceVertices(topology_refiner, face_index);
|
|
}
|
|
return num_corners;
|
|
}
|
|
|
|
/* NOTE: Grid size and layer flags are to be filled in before calling this
|
|
* function. */
|
|
static void subdiv_ccg_alloc_elements(SubdivCCG &subdiv_ccg, Subdiv &subdiv)
|
|
{
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv.topology_refiner;
|
|
const int64_t element_size = element_size_bytes_get(subdiv_ccg);
|
|
/* Allocate memory for surface grids. */
|
|
const int64_t num_grids = topology_refiner_count_face_corners(topology_refiner);
|
|
const int64_t grid_size = BKE_subdiv_grid_size_from_level(subdiv_ccg.level);
|
|
const int64_t grid_area = grid_size * grid_size;
|
|
subdiv_ccg.grid_element_size = element_size;
|
|
subdiv_ccg.grids.reinitialize(num_grids);
|
|
subdiv_ccg.grids_storage.reinitialize(num_grids * grid_area * element_size);
|
|
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];
|
|
}
|
|
/* TODO(sergey): Allocate memory for loose elements. */
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Grids evaluation
|
|
* \{ */
|
|
|
|
static void subdiv_ccg_eval_grid_element_limit(Subdiv &subdiv,
|
|
SubdivCCG &subdiv_ccg,
|
|
const int ptex_face_index,
|
|
const float u,
|
|
const float v,
|
|
uchar *element)
|
|
{
|
|
if (subdiv.displacement_evaluator != nullptr) {
|
|
BKE_subdiv_eval_final_point(&subdiv, ptex_face_index, u, v, (float *)element);
|
|
}
|
|
else if (subdiv_ccg.has_normal) {
|
|
BKE_subdiv_eval_limit_point_and_normal(&subdiv,
|
|
ptex_face_index,
|
|
u,
|
|
v,
|
|
(float *)element,
|
|
(float *)(element + subdiv_ccg.normal_offset));
|
|
}
|
|
else {
|
|
BKE_subdiv_eval_limit_point(&subdiv, ptex_face_index, u, v, (float *)element);
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_eval_grid_element_mask(SubdivCCG &subdiv_ccg,
|
|
SubdivCCGMaskEvaluator *mask_evaluator,
|
|
const int ptex_face_index,
|
|
const float u,
|
|
const float v,
|
|
uchar *element)
|
|
{
|
|
if (!subdiv_ccg.has_mask) {
|
|
return;
|
|
}
|
|
float *mask_value_ptr = (float *)(element + subdiv_ccg.mask_offset);
|
|
if (mask_evaluator != nullptr) {
|
|
*mask_value_ptr = mask_evaluator->eval_mask(mask_evaluator, ptex_face_index, u, v);
|
|
}
|
|
else {
|
|
*mask_value_ptr = 0.0f;
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_eval_grid_element(Subdiv &subdiv,
|
|
SubdivCCG &subdiv_ccg,
|
|
SubdivCCGMaskEvaluator *mask_evaluator,
|
|
const int ptex_face_index,
|
|
const float u,
|
|
const float v,
|
|
uchar *element)
|
|
{
|
|
subdiv_ccg_eval_grid_element_limit(subdiv, subdiv_ccg, ptex_face_index, u, v, element);
|
|
subdiv_ccg_eval_grid_element_mask(subdiv_ccg, mask_evaluator, ptex_face_index, u, v, element);
|
|
}
|
|
|
|
static void subdiv_ccg_eval_regular_grid(Subdiv &subdiv,
|
|
SubdivCCG &subdiv_ccg,
|
|
const Span<int> face_ptex_offset,
|
|
SubdivCCGMaskEvaluator *mask_evaluator,
|
|
const int face_index)
|
|
{
|
|
const int ptex_face_index = face_ptex_offset[face_index];
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
const float grid_size_1_inv = 1.0f / (grid_size - 1);
|
|
const int element_size = element_size_bytes_get(subdiv_ccg);
|
|
const IndexRange face = subdiv_ccg.faces[face_index];
|
|
for (int corner = 0; corner < face.size(); corner++) {
|
|
const int grid_index = face.start() + corner;
|
|
uchar *grid = (uchar *)subdiv_ccg.grids[grid_index];
|
|
for (int y = 0; y < grid_size; y++) {
|
|
const float grid_v = y * grid_size_1_inv;
|
|
for (int x = 0; x < grid_size; x++) {
|
|
const float grid_u = x * grid_size_1_inv;
|
|
float u, v;
|
|
BKE_subdiv_rotate_grid_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(
|
|
subdiv, subdiv_ccg, mask_evaluator, ptex_face_index, u, v, &grid[grid_element_offset]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_eval_special_grid(Subdiv &subdiv,
|
|
SubdivCCG &subdiv_ccg,
|
|
const Span<int> face_ptex_offset,
|
|
SubdivCCGMaskEvaluator *mask_evaluator,
|
|
const int face_index)
|
|
{
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
const float grid_size_1_inv = 1.0f / (grid_size - 1);
|
|
const int element_size = element_size_bytes_get(subdiv_ccg);
|
|
const IndexRange face = subdiv_ccg.faces[face_index];
|
|
for (int corner = 0; corner < face.size(); corner++) {
|
|
const int grid_index = face.start() + corner;
|
|
const int ptex_face_index = face_ptex_offset[face_index] + corner;
|
|
uchar *grid = (uchar *)subdiv_ccg.grids[grid_index];
|
|
for (int y = 0; y < grid_size; y++) {
|
|
const float u = 1.0f - (y * grid_size_1_inv);
|
|
for (int x = 0; x < grid_size; x++) {
|
|
const float v = 1.0f - (x * grid_size_1_inv);
|
|
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(
|
|
subdiv, subdiv_ccg, mask_evaluator, ptex_face_index, u, v, &grid[grid_element_offset]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool subdiv_ccg_evaluate_grids(SubdivCCG &subdiv_ccg,
|
|
Subdiv &subdiv,
|
|
SubdivCCGMaskEvaluator *mask_evaluator)
|
|
{
|
|
using namespace blender;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv.topology_refiner;
|
|
const int num_faces = topology_refiner->getNumFaces(topology_refiner);
|
|
const Span<int> face_ptex_offset(BKE_subdiv_face_ptex_offset_get(&subdiv),
|
|
subdiv_ccg.faces.size());
|
|
threading::parallel_for(IndexRange(num_faces), 1024, [&](const IndexRange range) {
|
|
for (const int face_index : range) {
|
|
if (subdiv_ccg.faces[face_index].size() == 4) {
|
|
subdiv_ccg_eval_regular_grid(
|
|
subdiv, subdiv_ccg, face_ptex_offset, mask_evaluator, face_index);
|
|
}
|
|
else {
|
|
subdiv_ccg_eval_special_grid(
|
|
subdiv, subdiv_ccg, face_ptex_offset, mask_evaluator, face_index);
|
|
}
|
|
}
|
|
});
|
|
/* If displacement is used, need to calculate normals after all final
|
|
* coordinates are known. */
|
|
if (subdiv.displacement_evaluator != nullptr) {
|
|
BKE_subdiv_ccg_recalc_normals(subdiv_ccg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void subdiv_ccg_allocate_adjacent_edges(SubdivCCG &subdiv_ccg, const int num_edges)
|
|
{
|
|
subdiv_ccg.adjacent_edges = Array<SubdivCCGAdjacentEdge>(num_edges, SubdivCCGAdjacentEdge{});
|
|
}
|
|
|
|
static SubdivCCGCoord subdiv_ccg_coord(int grid_index, int x, int y)
|
|
{
|
|
SubdivCCGCoord coord{};
|
|
coord.grid_index = grid_index;
|
|
coord.x = x;
|
|
coord.y = y;
|
|
return coord;
|
|
}
|
|
|
|
static CCGElem *subdiv_ccg_coord_to_elem(const CCGKey &key,
|
|
const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
return CCG_grid_elem(&key, subdiv_ccg.grids[coord.grid_index], coord.x, coord.y);
|
|
}
|
|
|
|
/* Returns storage where boundary elements are to be stored. */
|
|
static SubdivCCGCoord *subdiv_ccg_adjacent_edge_add_face(SubdivCCG &subdiv_ccg,
|
|
SubdivCCGAdjacentEdge &adjacent_edge)
|
|
{
|
|
const int grid_size = subdiv_ccg.grid_size * 2;
|
|
const int adjacent_face_index = adjacent_edge.num_adjacent_faces;
|
|
++adjacent_edge.num_adjacent_faces;
|
|
/* Allocate memory for the boundary elements. */
|
|
adjacent_edge.boundary_coords = static_cast<SubdivCCGCoord **>(
|
|
MEM_reallocN(adjacent_edge.boundary_coords,
|
|
adjacent_edge.num_adjacent_faces * sizeof(*adjacent_edge.boundary_coords)));
|
|
adjacent_edge.boundary_coords[adjacent_face_index] = static_cast<SubdivCCGCoord *>(
|
|
MEM_malloc_arrayN(grid_size * 2, sizeof(SubdivCCGCoord), "ccg adjacent boundary"));
|
|
return adjacent_edge.boundary_coords[adjacent_face_index];
|
|
}
|
|
|
|
static void subdiv_ccg_init_faces_edge_neighborhood(SubdivCCG &subdiv_ccg)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
const OffsetIndices<int> faces = subdiv_ccg.faces;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
const int num_edges = topology_refiner->getNumEdges(topology_refiner);
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
if (num_edges == 0) {
|
|
/* Early output, nothing to do in this case. */
|
|
return;
|
|
}
|
|
subdiv_ccg_allocate_adjacent_edges(subdiv_ccg, num_edges);
|
|
|
|
Vector<int, 64> face_vertices;
|
|
Vector<int, 64> face_edges;
|
|
/* Store adjacency for all faces. */
|
|
for (const int face_index : faces.index_range()) {
|
|
const IndexRange face = faces[face_index];
|
|
const int num_face_grids = face.size();
|
|
face_vertices.reinitialize(num_face_grids);
|
|
topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices.data());
|
|
/* Note that order of edges is same as order of MLoops, which also
|
|
* means it's the same as order of grids. */
|
|
face_edges.reinitialize(num_face_grids);
|
|
topology_refiner->getFaceEdges(topology_refiner, face_index, face_edges.data());
|
|
/* Store grids adjacency for this edge. */
|
|
for (int corner = 0; corner < num_face_grids; corner++) {
|
|
const int vertex_index = face_vertices[corner];
|
|
const int edge_index = face_edges[corner];
|
|
int edge_vertices[2];
|
|
topology_refiner->getEdgeVertices(topology_refiner, edge_index, edge_vertices);
|
|
const bool is_edge_flipped = (edge_vertices[0] != vertex_index);
|
|
/* Grid which is adjacent to the current corner. */
|
|
const int current_grid_index = face.start() + corner;
|
|
/* Grid which is adjacent to the next corner. */
|
|
const int next_grid_index = face.start() + (corner + 1) % num_face_grids;
|
|
/* Add new face to the adjacent edge. */
|
|
SubdivCCGAdjacentEdge &adjacent_edge = subdiv_ccg.adjacent_edges[edge_index];
|
|
SubdivCCGCoord *boundary_coords = subdiv_ccg_adjacent_edge_add_face(subdiv_ccg,
|
|
adjacent_edge);
|
|
/* Fill CCG elements along the edge. */
|
|
int boundary_element_index = 0;
|
|
if (is_edge_flipped) {
|
|
for (int i = 0; i < grid_size; i++) {
|
|
boundary_coords[boundary_element_index++] = subdiv_ccg_coord(
|
|
next_grid_index, grid_size - i - 1, grid_size - 1);
|
|
}
|
|
for (int i = 0; i < grid_size; i++) {
|
|
boundary_coords[boundary_element_index++] = subdiv_ccg_coord(
|
|
current_grid_index, grid_size - 1, i);
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < grid_size; i++) {
|
|
boundary_coords[boundary_element_index++] = subdiv_ccg_coord(
|
|
current_grid_index, grid_size - 1, grid_size - i - 1);
|
|
}
|
|
for (int i = 0; i < grid_size; i++) {
|
|
boundary_coords[boundary_element_index++] = subdiv_ccg_coord(
|
|
next_grid_index, i, grid_size - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_allocate_adjacent_vertices(SubdivCCG &subdiv_ccg, const int num_vertices)
|
|
{
|
|
subdiv_ccg.adjacent_verts = Array<SubdivCCGAdjacentVertex>(num_vertices,
|
|
SubdivCCGAdjacentVertex{});
|
|
}
|
|
|
|
/* Returns storage where corner elements are to be stored. This is a pointer
|
|
* to the actual storage. */
|
|
static SubdivCCGCoord *subdiv_ccg_adjacent_vertex_add_face(
|
|
SubdivCCGAdjacentVertex &adjacent_vertex)
|
|
{
|
|
const int adjacent_face_index = adjacent_vertex.num_adjacent_faces;
|
|
++adjacent_vertex.num_adjacent_faces;
|
|
/* Allocate memory for the boundary elements. */
|
|
adjacent_vertex.corner_coords = static_cast<SubdivCCGCoord *>(
|
|
MEM_reallocN(adjacent_vertex.corner_coords,
|
|
adjacent_vertex.num_adjacent_faces * sizeof(*adjacent_vertex.corner_coords)));
|
|
return &adjacent_vertex.corner_coords[adjacent_face_index];
|
|
}
|
|
|
|
static void subdiv_ccg_init_faces_vertex_neighborhood(SubdivCCG &subdiv_ccg)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
const OffsetIndices<int> faces = subdiv_ccg.faces;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
const int num_vertices = topology_refiner->getNumVertices(topology_refiner);
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
if (num_vertices == 0) {
|
|
/* Early output, nothing to do in this case. */
|
|
return;
|
|
}
|
|
subdiv_ccg_allocate_adjacent_vertices(subdiv_ccg, num_vertices);
|
|
Vector<int, 64> face_vertices;
|
|
/* Store adjacency for all faces. */
|
|
for (const int face_index : faces.index_range()) {
|
|
const IndexRange face = faces[face_index];
|
|
const int num_face_grids = face.size();
|
|
face_vertices.reinitialize(num_face_grids);
|
|
topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices.data());
|
|
for (int corner = 0; corner < num_face_grids; corner++) {
|
|
const int vertex_index = face_vertices[corner];
|
|
/* Grid which is adjacent to the current corner. */
|
|
const int grid_index = face.start() + corner;
|
|
/* Add new face to the adjacent edge. */
|
|
SubdivCCGAdjacentVertex &adjacent_vertex = subdiv_ccg.adjacent_verts[vertex_index];
|
|
SubdivCCGCoord *corner_coord = subdiv_ccg_adjacent_vertex_add_face(adjacent_vertex);
|
|
*corner_coord = subdiv_ccg_coord(grid_index, grid_size - 1, grid_size - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_init_faces_neighborhood(SubdivCCG &subdiv_ccg)
|
|
{
|
|
subdiv_ccg_init_faces_edge_neighborhood(subdiv_ccg);
|
|
subdiv_ccg_init_faces_vertex_neighborhood(subdiv_ccg);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Creation / evaluation
|
|
* \{ */
|
|
|
|
std::unique_ptr<SubdivCCG> BKE_subdiv_to_ccg(Subdiv &subdiv,
|
|
const SubdivToCCGSettings &settings,
|
|
const Mesh &coarse_mesh,
|
|
SubdivCCGMaskEvaluator *mask_evaluator)
|
|
{
|
|
BKE_subdiv_stats_begin(&subdiv.stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
|
std::unique_ptr<SubdivCCG> subdiv_ccg = std::make_unique<SubdivCCG>();
|
|
subdiv_ccg->subdiv = &subdiv;
|
|
subdiv_ccg->level = bitscan_forward_i(settings.resolution - 1);
|
|
subdiv_ccg->grid_size = BKE_subdiv_grid_size_from_level(subdiv_ccg->level);
|
|
subdiv_ccg_init_layers(*subdiv_ccg, settings);
|
|
subdiv_ccg->faces = coarse_mesh.faces();
|
|
subdiv_ccg->grid_to_face_map = coarse_mesh.corner_to_face_map();
|
|
subdiv_ccg_alloc_elements(*subdiv_ccg, subdiv);
|
|
subdiv_ccg_init_faces_neighborhood(*subdiv_ccg);
|
|
if (!subdiv_ccg_evaluate_grids(*subdiv_ccg, subdiv, mask_evaluator)) {
|
|
BKE_subdiv_stats_end(&subdiv.stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
|
return nullptr;
|
|
}
|
|
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)
|
|
{
|
|
/* Make sure evaluator is ready. */
|
|
BKE_subdiv_stats_begin(&subdiv.stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
|
if (!BKE_subdiv_eval_begin_from_mesh(
|
|
&subdiv, &coarse_mesh, nullptr, SUBDIV_EVALUATOR_TYPE_CPU, nullptr))
|
|
{
|
|
if (coarse_mesh.faces_num) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
BKE_subdiv_stats_end(&subdiv.stats, SUBDIV_STATS_SUBDIV_TO_CCG);
|
|
SubdivCCGMaskEvaluator mask_evaluator;
|
|
bool has_mask = BKE_subdiv_ccg_mask_init_from_paint(&mask_evaluator, &coarse_mesh);
|
|
std::unique_ptr<SubdivCCG> subdiv_ccg = BKE_subdiv_to_ccg(
|
|
subdiv, settings, coarse_mesh, has_mask ? &mask_evaluator : nullptr);
|
|
if (has_mask) {
|
|
mask_evaluator.free(&mask_evaluator);
|
|
}
|
|
if (!subdiv_ccg) {
|
|
return nullptr;
|
|
}
|
|
Mesh *result = BKE_mesh_new_nomain_from_template(&coarse_mesh, 0, 0, 0, 0);
|
|
result->runtime->subdiv_ccg = std::move(subdiv_ccg);
|
|
return result;
|
|
}
|
|
|
|
SubdivCCG::~SubdivCCG()
|
|
{
|
|
if (this->subdiv != nullptr) {
|
|
BKE_subdiv_free(this->subdiv);
|
|
}
|
|
|
|
for (const int i : this->adjacent_edges.index_range()) {
|
|
SubdivCCGAdjacentEdge *adjacent_edge = &this->adjacent_edges[i];
|
|
for (int face_index = 0; face_index < adjacent_edge->num_adjacent_faces; face_index++) {
|
|
MEM_SAFE_FREE(adjacent_edge->boundary_coords[face_index]);
|
|
}
|
|
MEM_SAFE_FREE(adjacent_edge->boundary_coords);
|
|
}
|
|
|
|
for (const int i : this->adjacent_verts.index_range()) {
|
|
SubdivCCGAdjacentVertex *adjacent_vertex = &this->adjacent_verts[i];
|
|
MEM_SAFE_FREE(adjacent_vertex->corner_coords);
|
|
}
|
|
}
|
|
|
|
CCGKey BKE_subdiv_ccg_key(const SubdivCCG &subdiv_ccg, int level)
|
|
{
|
|
CCGKey key;
|
|
key.level = level;
|
|
key.elem_size = element_size_bytes_get(subdiv_ccg);
|
|
key.grid_size = BKE_subdiv_grid_size_from_level(level);
|
|
key.grid_area = key.grid_size * key.grid_size;
|
|
key.grid_bytes = key.elem_size * key.grid_area;
|
|
|
|
key.normal_offset = subdiv_ccg.normal_offset;
|
|
key.mask_offset = subdiv_ccg.mask_offset;
|
|
|
|
key.has_normals = subdiv_ccg.has_normal;
|
|
key.has_mask = subdiv_ccg.has_mask;
|
|
return key;
|
|
}
|
|
|
|
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
|
|
{
|
|
return BKE_subdiv_ccg_key(subdiv_ccg, subdiv_ccg.level);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Normals
|
|
* \{ */
|
|
|
|
/* Evaluate high-res face normals, for faces which corresponds to grid elements
|
|
*
|
|
* {(x, y), {x + 1, y}, {x + 1, y + 1}, {x, y + 1}}
|
|
*
|
|
* The result is stored in normals storage from TLS. */
|
|
static void subdiv_ccg_recalc_inner_face_normals(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
MutableSpan<float3> face_normals,
|
|
const int grid_index)
|
|
{
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
const int grid_size_1 = grid_size - 1;
|
|
CCGElem *grid = subdiv_ccg.grids[grid_index];
|
|
for (int y = 0; y < grid_size - 1; y++) {
|
|
for (int x = 0; x < grid_size - 1; x++) {
|
|
CCGElem *grid_elements[4] = {
|
|
CCG_grid_elem(&key, grid, x, y + 1),
|
|
CCG_grid_elem(&key, grid, x + 1, y + 1),
|
|
CCG_grid_elem(&key, grid, x + 1, y),
|
|
CCG_grid_elem(&key, grid, x, y),
|
|
};
|
|
float *co[4] = {
|
|
CCG_elem_co(&key, grid_elements[0]),
|
|
CCG_elem_co(&key, grid_elements[1]),
|
|
CCG_elem_co(&key, grid_elements[2]),
|
|
CCG_elem_co(&key, grid_elements[3]),
|
|
};
|
|
const int face_index = y * grid_size_1 + x;
|
|
float *face_normal = face_normals[face_index];
|
|
normal_quad_v3(face_normal, co[0], co[1], co[2], co[3]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Average normals at every grid element, using adjacent faces normals. */
|
|
static void subdiv_ccg_average_inner_face_normals(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const Span<float3> face_normals,
|
|
const int grid_index)
|
|
{
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
const int grid_size_1 = grid_size - 1;
|
|
CCGElem *grid = subdiv_ccg.grids[grid_index];
|
|
for (int y = 0; y < grid_size; y++) {
|
|
for (int x = 0; x < grid_size; x++) {
|
|
float normal_acc[3] = {0.0f, 0.0f, 0.0f};
|
|
int counter = 0;
|
|
/* Accumulate normals of all adjacent faces. */
|
|
if (x < grid_size_1 && y < grid_size_1) {
|
|
add_v3_v3(normal_acc, face_normals[y * grid_size_1 + x]);
|
|
counter++;
|
|
}
|
|
if (x >= 1) {
|
|
if (y < grid_size_1) {
|
|
add_v3_v3(normal_acc, face_normals[y * grid_size_1 + (x - 1)]);
|
|
counter++;
|
|
}
|
|
if (y >= 1) {
|
|
add_v3_v3(normal_acc, face_normals[(y - 1) * grid_size_1 + (x - 1)]);
|
|
counter++;
|
|
}
|
|
}
|
|
if (y >= 1 && x < grid_size_1) {
|
|
add_v3_v3(normal_acc, face_normals[(y - 1) * grid_size_1 + x]);
|
|
counter++;
|
|
}
|
|
/* Normalize and store. */
|
|
mul_v3_v3fl(CCG_grid_elem_no(&key, grid, x, y), normal_acc, 1.0f / counter);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Recalculate normals which corresponds to non-boundaries elements of grids. */
|
|
static void subdiv_ccg_recalc_inner_grid_normals(SubdivCCG &subdiv_ccg, const IndexMask &face_mask)
|
|
{
|
|
using namespace blender;
|
|
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
|
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
threading::EnumerableThreadSpecific<Array<float3>> face_normals_tls(
|
|
[&]() { return Array<float3>(grid_size_1 * grid_size_1); });
|
|
|
|
const OffsetIndices<int> faces = subdiv_ccg.faces;
|
|
face_mask.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
|
|
MutableSpan<float3> face_normals = face_normals_tls.local();
|
|
for (const int face_index : segment) {
|
|
const IndexRange face = faces[face_index];
|
|
for (const int grid_index : IndexRange(face.start(), face.size())) {
|
|
subdiv_ccg_recalc_inner_face_normals(subdiv_ccg, key, face_normals, grid_index);
|
|
subdiv_ccg_average_inner_face_normals(subdiv_ccg, key, face_normals, grid_index);
|
|
}
|
|
subdiv_ccg_average_inner_face_grids(subdiv_ccg, key, face);
|
|
}
|
|
});
|
|
}
|
|
|
|
void BKE_subdiv_ccg_recalc_normals(SubdivCCG &subdiv_ccg)
|
|
{
|
|
if (!subdiv_ccg.has_normal) {
|
|
/* Grids don't have normals, can do early output. */
|
|
return;
|
|
}
|
|
subdiv_ccg_recalc_inner_grid_normals(subdiv_ccg, subdiv_ccg.faces.index_range());
|
|
BKE_subdiv_ccg_average_grids(subdiv_ccg);
|
|
}
|
|
|
|
void BKE_subdiv_ccg_update_normals(SubdivCCG &subdiv_ccg, const IndexMask &face_mask)
|
|
{
|
|
if (!subdiv_ccg.has_normal) {
|
|
/* Grids don't have normals, can do early output. */
|
|
return;
|
|
}
|
|
if (face_mask.is_empty()) {
|
|
/* No faces changed, so nothing to do here. */
|
|
return;
|
|
}
|
|
subdiv_ccg_recalc_inner_grid_normals(subdiv_ccg, face_mask);
|
|
|
|
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
|
subdiv_ccg_average_faces_boundaries_and_corners(subdiv_ccg, key, face_mask);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Boundary averaging/stitching
|
|
* \{ */
|
|
|
|
static void average_grid_element_value_v3(float a[3], float b[3])
|
|
{
|
|
add_v3_v3(a, b);
|
|
mul_v3_fl(a, 0.5f);
|
|
copy_v3_v3(b, a);
|
|
}
|
|
|
|
static void average_grid_element(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
CCGElem *grid_element_a,
|
|
CCGElem *grid_element_b)
|
|
{
|
|
average_grid_element_value_v3(CCG_elem_co(&key, grid_element_a),
|
|
CCG_elem_co(&key, grid_element_b));
|
|
if (subdiv_ccg.has_normal) {
|
|
average_grid_element_value_v3(CCG_elem_no(&key, grid_element_a),
|
|
CCG_elem_no(&key, grid_element_b));
|
|
}
|
|
if (subdiv_ccg.has_mask) {
|
|
float mask = (*CCG_elem_mask(&key, grid_element_a) + *CCG_elem_mask(&key, grid_element_b)) *
|
|
0.5f;
|
|
*CCG_elem_mask(&key, grid_element_a) = mask;
|
|
*CCG_elem_mask(&key, grid_element_b) = mask;
|
|
}
|
|
}
|
|
|
|
/* Accumulator to hold data during averaging. */
|
|
struct GridElementAccumulator {
|
|
float co[3];
|
|
float no[3];
|
|
float mask;
|
|
};
|
|
|
|
static void element_accumulator_init(GridElementAccumulator &accumulator)
|
|
{
|
|
zero_v3(accumulator.co);
|
|
zero_v3(accumulator.no);
|
|
accumulator.mask = 0.0f;
|
|
}
|
|
|
|
static void element_accumulator_add(GridElementAccumulator &accumulator,
|
|
const SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
/*const*/ CCGElem &grid_element)
|
|
{
|
|
add_v3_v3(accumulator.co, CCG_elem_co(&key, &grid_element));
|
|
if (subdiv_ccg.has_normal) {
|
|
add_v3_v3(accumulator.no, CCG_elem_no(&key, &grid_element));
|
|
}
|
|
if (subdiv_ccg.has_mask) {
|
|
accumulator.mask += *CCG_elem_mask(&key, &grid_element);
|
|
}
|
|
}
|
|
|
|
static void element_accumulator_mul_fl(GridElementAccumulator &accumulator, const float f)
|
|
{
|
|
mul_v3_fl(accumulator.co, f);
|
|
mul_v3_fl(accumulator.no, f);
|
|
accumulator.mask *= f;
|
|
}
|
|
|
|
static void element_accumulator_copy(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
CCGElem &destination,
|
|
const GridElementAccumulator &accumulator)
|
|
{
|
|
copy_v3_v3(CCG_elem_co(&key, &destination), accumulator.co);
|
|
if (subdiv_ccg.has_normal) {
|
|
copy_v3_v3(CCG_elem_no(&key, &destination), accumulator.no);
|
|
}
|
|
if (subdiv_ccg.has_mask) {
|
|
*CCG_elem_mask(&key, &destination) = accumulator.mask;
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_average_inner_face_grids(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexRange face)
|
|
{
|
|
const Span<CCGElem *> grids = subdiv_ccg.grids;
|
|
const int num_face_grids = face.size();
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
CCGElem *prev_grid = grids[face.start() + num_face_grids - 1];
|
|
/* Average boundary between neighbor grid. */
|
|
for (int corner = 0; corner < num_face_grids; corner++) {
|
|
CCGElem *grid = grids[face.start() + corner];
|
|
for (int i = 1; i < grid_size; i++) {
|
|
CCGElem *prev_grid_element = CCG_grid_elem(&key, prev_grid, i, 0);
|
|
CCGElem *grid_element = CCG_grid_elem(&key, grid, 0, i);
|
|
average_grid_element(subdiv_ccg, key, prev_grid_element, grid_element);
|
|
}
|
|
prev_grid = grid;
|
|
}
|
|
/* Average all grids centers into a single accumulator, and share it.
|
|
* Guarantees correct and smooth averaging in the center. */
|
|
GridElementAccumulator center_accumulator;
|
|
element_accumulator_init(center_accumulator);
|
|
for (int corner = 0; corner < num_face_grids; corner++) {
|
|
CCGElem *grid = grids[face.start() + corner];
|
|
CCGElem *grid_center_element = CCG_grid_elem(&key, grid, 0, 0);
|
|
element_accumulator_add(center_accumulator, subdiv_ccg, key, *grid_center_element);
|
|
}
|
|
element_accumulator_mul_fl(center_accumulator, 1.0f / num_face_grids);
|
|
for (int corner = 0; corner < num_face_grids; corner++) {
|
|
CCGElem *grid = grids[face.start() + corner];
|
|
CCGElem *grid_center_element = CCG_grid_elem(&key, grid, 0, 0);
|
|
element_accumulator_copy(subdiv_ccg, key, *grid_center_element, center_accumulator);
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_average_grids_boundary(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
SubdivCCGAdjacentEdge &adjacent_edge,
|
|
MutableSpan<GridElementAccumulator> accumulators)
|
|
{
|
|
const int num_adjacent_faces = adjacent_edge.num_adjacent_faces;
|
|
const int grid_size2 = subdiv_ccg.grid_size * 2;
|
|
if (num_adjacent_faces == 1) {
|
|
/* Nothing to average with. */
|
|
return;
|
|
}
|
|
for (int i = 1; i < grid_size2 - 1; i++) {
|
|
element_accumulator_init(accumulators[i]);
|
|
}
|
|
for (int face_index = 0; face_index < num_adjacent_faces; face_index++) {
|
|
for (int i = 1; i < grid_size2 - 1; i++) {
|
|
CCGElem *grid_element = subdiv_ccg_coord_to_elem(
|
|
key, subdiv_ccg, adjacent_edge.boundary_coords[face_index][i]);
|
|
element_accumulator_add(accumulators[i], subdiv_ccg, key, *grid_element);
|
|
}
|
|
}
|
|
for (int i = 1; i < grid_size2 - 1; i++) {
|
|
element_accumulator_mul_fl(accumulators[i], 1.0f / num_adjacent_faces);
|
|
}
|
|
/* Copy averaged value to all the other faces. */
|
|
for (int face_index = 0; face_index < num_adjacent_faces; face_index++) {
|
|
for (int i = 1; i < grid_size2 - 1; i++) {
|
|
CCGElem *grid_element = subdiv_ccg_coord_to_elem(
|
|
key, subdiv_ccg, adjacent_edge.boundary_coords[face_index][i]);
|
|
element_accumulator_copy(subdiv_ccg, key, *grid_element, accumulators[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct AverageGridsCornerData {
|
|
SubdivCCG *subdiv_ccg;
|
|
CCGKey *key;
|
|
|
|
/* Optional lookup table. Maps task range index to index in `subdiv_ccg.adjacent_verts`. */
|
|
const int *adjacent_vert_index_map;
|
|
};
|
|
|
|
static void subdiv_ccg_average_grids_corners(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
SubdivCCGAdjacentVertex &adjacent_vertex)
|
|
{
|
|
const int num_adjacent_faces = adjacent_vertex.num_adjacent_faces;
|
|
if (num_adjacent_faces == 1) {
|
|
/* Nothing to average with. */
|
|
return;
|
|
}
|
|
GridElementAccumulator accumulator;
|
|
element_accumulator_init(accumulator);
|
|
for (int face_index = 0; face_index < num_adjacent_faces; face_index++) {
|
|
CCGElem *grid_element = subdiv_ccg_coord_to_elem(
|
|
key, subdiv_ccg, adjacent_vertex.corner_coords[face_index]);
|
|
element_accumulator_add(accumulator, subdiv_ccg, key, *grid_element);
|
|
}
|
|
element_accumulator_mul_fl(accumulator, 1.0f / num_adjacent_faces);
|
|
/* Copy averaged value to all the other faces. */
|
|
for (int face_index = 0; face_index < num_adjacent_faces; face_index++) {
|
|
CCGElem *grid_element = subdiv_ccg_coord_to_elem(
|
|
key, subdiv_ccg, adjacent_vertex.corner_coords[face_index]);
|
|
element_accumulator_copy(subdiv_ccg, key, *grid_element, accumulator);
|
|
}
|
|
}
|
|
|
|
static void subdiv_ccg_average_boundaries(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexMask &adjacent_edge_mask)
|
|
{
|
|
using namespace blender;
|
|
threading::EnumerableThreadSpecific<Array<GridElementAccumulator>> all_accumulators(
|
|
[&]() { return Array<GridElementAccumulator>(subdiv_ccg.grid_size * 2); });
|
|
|
|
adjacent_edge_mask.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
|
|
MutableSpan<GridElementAccumulator> accumulators = all_accumulators.local();
|
|
for (const int i : segment) {
|
|
SubdivCCGAdjacentEdge &adjacent_edge = subdiv_ccg.adjacent_edges[i];
|
|
subdiv_ccg_average_grids_boundary(subdiv_ccg, key, adjacent_edge, accumulators);
|
|
}
|
|
});
|
|
}
|
|
|
|
static void subdiv_ccg_average_corners(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexMask &adjacent_vert_mask)
|
|
{
|
|
using namespace blender;
|
|
adjacent_vert_mask.foreach_index(GrainSize(1024), [&](const int i) {
|
|
SubdivCCGAdjacentVertex &adjacent_vert = subdiv_ccg.adjacent_verts[i];
|
|
subdiv_ccg_average_grids_corners(subdiv_ccg, key, adjacent_vert);
|
|
});
|
|
}
|
|
|
|
void BKE_subdiv_ccg_average_grids(SubdivCCG &subdiv_ccg)
|
|
{
|
|
using namespace blender;
|
|
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
|
/* Average inner boundaries of grids (within one face), across faces
|
|
* from different face-corners. */
|
|
BKE_subdiv_ccg_average_stitch_faces(subdiv_ccg, subdiv_ccg.faces.index_range());
|
|
subdiv_ccg_average_boundaries(subdiv_ccg, key, subdiv_ccg.adjacent_edges.index_range());
|
|
subdiv_ccg_average_corners(subdiv_ccg, key, subdiv_ccg.adjacent_verts.index_range());
|
|
}
|
|
|
|
static void subdiv_ccg_affected_face_adjacency(SubdivCCG &subdiv_ccg,
|
|
const IndexMask &face_mask,
|
|
blender::Set<int> &adjacent_verts,
|
|
blender::Set<int> &adjacent_edges)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
|
|
Vector<int, 64> face_vertices;
|
|
Vector<int, 64> face_edges;
|
|
|
|
face_mask.foreach_index([&](const int face_index) {
|
|
const int num_face_grids = subdiv_ccg.faces[face_index].size();
|
|
face_vertices.reinitialize(num_face_grids);
|
|
topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices.data());
|
|
adjacent_verts.add_multiple(face_vertices);
|
|
|
|
face_edges.reinitialize(num_face_grids);
|
|
topology_refiner->getFaceEdges(topology_refiner, face_index, face_edges.data());
|
|
adjacent_edges.add_multiple(face_edges);
|
|
});
|
|
}
|
|
|
|
void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG &subdiv_ccg,
|
|
const CCGKey &key,
|
|
const IndexMask &face_mask)
|
|
{
|
|
blender::Set<int> adjacent_vert_set;
|
|
blender::Set<int> adjacent_edge_set;
|
|
subdiv_ccg_affected_face_adjacency(subdiv_ccg, face_mask, adjacent_vert_set, adjacent_edge_set);
|
|
|
|
Vector<int> adjacent_verts(adjacent_vert_set.begin(), adjacent_vert_set.end());
|
|
Vector<int> adjacent_edges(adjacent_edge_set.begin(), adjacent_edge_set.end());
|
|
|
|
std::sort(adjacent_verts.begin(), adjacent_verts.end());
|
|
std::sort(adjacent_edges.begin(), adjacent_edges.end());
|
|
|
|
IndexMaskMemory memory;
|
|
subdiv_ccg_average_boundaries(
|
|
subdiv_ccg, key, IndexMask::from_indices(adjacent_edges.as_span(), memory));
|
|
|
|
subdiv_ccg_average_corners(
|
|
subdiv_ccg, key, IndexMask::from_indices(adjacent_verts.as_span(), memory));
|
|
}
|
|
|
|
void BKE_subdiv_ccg_average_stitch_faces(SubdivCCG &subdiv_ccg, const IndexMask &face_mask)
|
|
{
|
|
using namespace blender;
|
|
const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
|
|
face_mask.foreach_index(GrainSize(512), [&](const int face_index) {
|
|
subdiv_ccg_average_inner_face_grids(subdiv_ccg, key, subdiv_ccg.faces[face_index]);
|
|
});
|
|
/* TODO(sergey): Only average elements which are adjacent to modified
|
|
* faces. */
|
|
subdiv_ccg_average_boundaries(subdiv_ccg, key, subdiv_ccg.adjacent_edges.index_range());
|
|
subdiv_ccg_average_corners(subdiv_ccg, key, subdiv_ccg.adjacent_verts.index_range());
|
|
}
|
|
|
|
void BKE_subdiv_ccg_topology_counters(const SubdivCCG &subdiv_ccg,
|
|
int &r_num_vertices,
|
|
int &r_num_edges,
|
|
int &r_num_faces,
|
|
int &r_num_loops)
|
|
{
|
|
const int num_grids = subdiv_ccg.grids.size();
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
const int grid_area = grid_size * grid_size;
|
|
const int num_edges_per_grid = 2 * (grid_size * (grid_size - 1));
|
|
r_num_vertices = num_grids * grid_area;
|
|
r_num_edges = num_grids * num_edges_per_grid;
|
|
r_num_faces = num_grids * (grid_size - 1) * (grid_size - 1);
|
|
r_num_loops = r_num_faces * 4;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Neighbors
|
|
* \{ */
|
|
|
|
void BKE_subdiv_ccg_print_coord(const char *message, const SubdivCCGCoord &coord)
|
|
{
|
|
printf("%s: grid index: %d, coord: (%d, %d)\n", message, coord.grid_index, coord.x, coord.y);
|
|
}
|
|
|
|
bool BKE_subdiv_ccg_check_coord_valid(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
if (coord.grid_index < 0 || coord.grid_index >= subdiv_ccg.grids.size()) {
|
|
return false;
|
|
}
|
|
const int grid_size = subdiv_ccg.grid_size;
|
|
if (coord.x < 0 || coord.x >= grid_size) {
|
|
return false;
|
|
}
|
|
if (coord.y < 0 || coord.y >= grid_size) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
BLI_INLINE void subdiv_ccg_neighbors_init(SubdivCCGNeighbors &neighbors,
|
|
const int num_unique,
|
|
const int num_duplicates)
|
|
{
|
|
const int size = num_unique + num_duplicates;
|
|
neighbors.size = size;
|
|
neighbors.num_duplicates = num_duplicates;
|
|
if (size < ARRAY_SIZE(neighbors.coords_fixed)) {
|
|
neighbors.coords = neighbors.coords_fixed;
|
|
}
|
|
else {
|
|
neighbors.coords = static_cast<SubdivCCGCoord *>(
|
|
MEM_mallocN(sizeof(*neighbors.coords) * size, "SubdivCCGNeighbors.coords"));
|
|
}
|
|
}
|
|
|
|
/* Check whether given coordinate belongs to a grid corner. */
|
|
BLI_INLINE bool is_corner_grid_coord(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
return (coord.x == 0 && coord.y == 0) || (coord.x == 0 && coord.y == grid_size_1) ||
|
|
(coord.x == grid_size_1 && coord.y == grid_size_1) ||
|
|
(coord.x == grid_size_1 && coord.y == 0);
|
|
}
|
|
|
|
/* Check whether given coordinate belongs to a grid boundary. */
|
|
BLI_INLINE bool is_boundary_grid_coord(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
return coord.x == 0 || coord.y == 0 || coord.x == grid_size_1 || coord.y == grid_size_1;
|
|
}
|
|
|
|
/* Check whether coordinate is at the boundary between two grids of the same face. */
|
|
BLI_INLINE bool is_inner_edge_grid_coordinate(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
if (coord.x == 0) {
|
|
return coord.y > 0 && coord.y < grid_size_1;
|
|
}
|
|
if (coord.y == 0) {
|
|
return coord.x > 0 && coord.x < grid_size_1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BLI_INLINE SubdivCCGCoord coord_at_prev_row(const SubdivCCG & /*subdiv_ccg*/,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
BLI_assert(coord.y > 0);
|
|
SubdivCCGCoord result = coord;
|
|
result.y -= 1;
|
|
return result;
|
|
}
|
|
BLI_INLINE SubdivCCGCoord coord_at_next_row(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
UNUSED_VARS_NDEBUG(subdiv_ccg);
|
|
BLI_assert(coord.y < subdiv_ccg.grid_size - 1);
|
|
SubdivCCGCoord result = coord;
|
|
result.y += 1;
|
|
return result;
|
|
}
|
|
|
|
BLI_INLINE SubdivCCGCoord coord_at_prev_col(const SubdivCCG & /*subdiv_ccg*/,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
BLI_assert(coord.x > 0);
|
|
SubdivCCGCoord result = coord;
|
|
result.x -= 1;
|
|
return result;
|
|
}
|
|
BLI_INLINE SubdivCCGCoord coord_at_next_col(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
UNUSED_VARS_NDEBUG(subdiv_ccg);
|
|
BLI_assert(coord.x < subdiv_ccg.grid_size - 1);
|
|
SubdivCCGCoord result = coord;
|
|
result.x += 1;
|
|
return result;
|
|
}
|
|
|
|
/* For the input coordinate which is at the boundary of the grid do one step inside. */
|
|
static SubdivCCGCoord coord_step_inside_from_boundary(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
|
|
{
|
|
SubdivCCGCoord result = coord;
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
if (result.x == grid_size_1) {
|
|
--result.x;
|
|
}
|
|
else if (result.y == grid_size_1) {
|
|
--result.y;
|
|
}
|
|
else if (result.x == 0) {
|
|
++result.x;
|
|
}
|
|
else if (result.y == 0) {
|
|
++result.y;
|
|
}
|
|
else {
|
|
BLI_assert_msg(0, "non-boundary element given");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BLI_INLINE
|
|
int next_grid_index_from_coord(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
const IndexRange face = subdiv_ccg.faces[subdiv_ccg.grid_to_face_map[coord.grid_index]];
|
|
const int face_grid_index = coord.grid_index;
|
|
int next_face_grid_index = face_grid_index + 1 - face.start();
|
|
if (next_face_grid_index == face.size()) {
|
|
next_face_grid_index = 0;
|
|
}
|
|
return face.start() + next_face_grid_index;
|
|
}
|
|
BLI_INLINE int prev_grid_index_from_coord(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
const IndexRange face = subdiv_ccg.faces[subdiv_ccg.grid_to_face_map[coord.grid_index]];
|
|
const int face_grid_index = coord.grid_index;
|
|
int prev_face_grid_index = face_grid_index - 1 - face.start();
|
|
if (prev_face_grid_index < 0) {
|
|
prev_face_grid_index = face.size() - 1;
|
|
}
|
|
return face.start() + prev_face_grid_index;
|
|
}
|
|
|
|
/* Simple case of getting neighbors of a corner coordinate: the corner is a face center, so
|
|
* can only iterate over grid of a single face, without looking into adjacency. */
|
|
static void neighbor_coords_corner_center_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
const IndexRange face = subdiv_ccg.faces[subdiv_ccg.grid_to_face_map[coord.grid_index]];
|
|
const int num_adjacent_grids = face.size();
|
|
|
|
subdiv_ccg_neighbors_init(
|
|
r_neighbors, num_adjacent_grids, (include_duplicates) ? num_adjacent_grids - 1 : 0);
|
|
|
|
int duplicate_face_grid_index = num_adjacent_grids;
|
|
for (int face_grid_index = 0; face_grid_index < num_adjacent_grids; ++face_grid_index) {
|
|
SubdivCCGCoord neighbor_coord;
|
|
neighbor_coord.grid_index = face.start() + face_grid_index;
|
|
neighbor_coord.x = 1;
|
|
neighbor_coord.y = 0;
|
|
r_neighbors.coords[face_grid_index] = neighbor_coord;
|
|
|
|
if (include_duplicates && neighbor_coord.grid_index != coord.grid_index) {
|
|
neighbor_coord.x = 0;
|
|
r_neighbors.coords[duplicate_face_grid_index++] = neighbor_coord;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get index within adjacent_verts array for the given CCG coordinate. */
|
|
static int adjacent_vertex_index_from_coord(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
|
|
const int face_index = subdiv_ccg.grid_to_face_map[coord.grid_index];
|
|
const IndexRange face = subdiv_ccg.faces[face_index];
|
|
const int face_grid_index = coord.grid_index - face.start();
|
|
const int num_face_grids = face.size();
|
|
|
|
Array<int, 64> face_vertices(num_face_grids);
|
|
topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices.data());
|
|
|
|
const int adjacent_vertex_index = face_vertices[face_grid_index];
|
|
return adjacent_vertex_index;
|
|
}
|
|
|
|
/* The corner is adjacent to a coarse vertex. */
|
|
static void neighbor_coords_corner_vertex_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
|
|
const int adjacent_vertex_index = adjacent_vertex_index_from_coord(subdiv_ccg, coord);
|
|
const int num_vertex_edges = topology_refiner->getNumVertexEdges(topology_refiner,
|
|
adjacent_vertex_index);
|
|
|
|
const SubdivCCGAdjacentVertex &adjacent_vert = subdiv_ccg.adjacent_verts[adjacent_vertex_index];
|
|
const int num_adjacent_faces = adjacent_vert.num_adjacent_faces;
|
|
|
|
subdiv_ccg_neighbors_init(
|
|
r_neighbors, num_vertex_edges, (include_duplicates) ? num_adjacent_faces - 1 : 0);
|
|
|
|
Array<int, 64> vertex_edges(num_vertex_edges);
|
|
topology_refiner->getVertexEdges(topology_refiner, adjacent_vertex_index, vertex_edges.data());
|
|
|
|
for (int i = 0; i < num_vertex_edges; ++i) {
|
|
const int edge_index = vertex_edges[i];
|
|
|
|
/* Use very first grid of every edge. */
|
|
const int edge_face_index = 0;
|
|
|
|
/* Depending edge orientation we use first (zero-based) or previous-to-last point. */
|
|
int edge_vertices_indices[2];
|
|
topology_refiner->getEdgeVertices(topology_refiner, edge_index, edge_vertices_indices);
|
|
int edge_point_index, duplicate_edge_point_index;
|
|
if (edge_vertices_indices[0] == adjacent_vertex_index) {
|
|
duplicate_edge_point_index = 0;
|
|
edge_point_index = duplicate_edge_point_index + 1;
|
|
}
|
|
else {
|
|
/* Edge "consists" of 2 grids, which makes it 2 * grid_size elements per edge.
|
|
* The index of last edge element is 2 * grid_size - 1 (due to zero-based indices),
|
|
* and we are interested in previous to last element. */
|
|
duplicate_edge_point_index = subdiv_ccg.grid_size * 2 - 1;
|
|
edge_point_index = duplicate_edge_point_index - 1;
|
|
}
|
|
|
|
const SubdivCCGAdjacentEdge &adjacent_edge = subdiv_ccg.adjacent_edges[edge_index];
|
|
r_neighbors.coords[i] = adjacent_edge.boundary_coords[edge_face_index][edge_point_index];
|
|
}
|
|
|
|
if (include_duplicates) {
|
|
/* Add duplicates of the current grid vertex in adjacent faces if requested. */
|
|
for (int i = 0, duplicate_i = num_vertex_edges; i < num_adjacent_faces; i++) {
|
|
SubdivCCGCoord neighbor_coord = adjacent_vert.corner_coords[i];
|
|
if (neighbor_coord.grid_index != coord.grid_index) {
|
|
r_neighbors.coords[duplicate_i++] = neighbor_coord;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int adjacent_edge_index_from_coord(const SubdivCCG &subdiv_ccg, const SubdivCCGCoord &coord)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
|
|
const int face_index = subdiv_ccg.grid_to_face_map[coord.grid_index];
|
|
const IndexRange face = subdiv_ccg.faces[face_index];
|
|
const int face_grid_index = coord.grid_index - face.start();
|
|
const int num_face_edges = topology_refiner->getNumFaceEdges(topology_refiner, face_index);
|
|
|
|
Array<int, 64> face_edges(num_face_edges);
|
|
topology_refiner->getFaceEdges(topology_refiner, face_index, face_edges.data());
|
|
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
int adjacent_edge_index = -1;
|
|
if (coord.x == grid_size_1) {
|
|
adjacent_edge_index = face_edges[face_grid_index];
|
|
}
|
|
else {
|
|
BLI_assert(coord.y == grid_size_1);
|
|
adjacent_edge_index = face_edges[face_grid_index == 0 ? face.size() - 1 : face_grid_index - 1];
|
|
}
|
|
|
|
return adjacent_edge_index;
|
|
}
|
|
|
|
static int adjacent_edge_point_index_from_coord(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const int adjacent_edge_index)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
|
|
const int adjacent_vertex_index = adjacent_vertex_index_from_coord(subdiv_ccg, coord);
|
|
int edge_vertices_indices[2];
|
|
topology_refiner->getEdgeVertices(topology_refiner, adjacent_edge_index, edge_vertices_indices);
|
|
|
|
/* Vertex index of an edge which is used to see whether edge points in the right direction.
|
|
* Tricky part here is that depending whether input coordinate is are maximum X or Y coordinate
|
|
* of the grid we need to use different edge direction.
|
|
* Basically, the edge adjacent to a previous loop needs to point opposite direction. */
|
|
int directional_edge_vertex_index = -1;
|
|
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
int adjacent_edge_point_index = -1;
|
|
if (coord.x == grid_size_1) {
|
|
adjacent_edge_point_index = subdiv_ccg.grid_size - coord.y - 1;
|
|
directional_edge_vertex_index = edge_vertices_indices[0];
|
|
}
|
|
else {
|
|
BLI_assert(coord.y == grid_size_1);
|
|
adjacent_edge_point_index = subdiv_ccg.grid_size + coord.x;
|
|
directional_edge_vertex_index = edge_vertices_indices[1];
|
|
}
|
|
|
|
/* Flip the index if the edge points opposite direction. */
|
|
if (adjacent_vertex_index != directional_edge_vertex_index) {
|
|
const int num_edge_points = subdiv_ccg.grid_size * 2;
|
|
adjacent_edge_point_index = num_edge_points - adjacent_edge_point_index - 1;
|
|
}
|
|
|
|
return adjacent_edge_point_index;
|
|
}
|
|
|
|
/* Adjacent edge has two points in the middle which corresponds to grid corners, but which are
|
|
* the same point in the final geometry.
|
|
* So need to use extra step when calculating next/previous points, so we don't go from a corner
|
|
* of one grid to a corner of adjacent grid. */
|
|
static int next_adjacent_edge_point_index(const SubdivCCG &subdiv_ccg, const int point_index)
|
|
{
|
|
if (point_index == subdiv_ccg.grid_size - 1) {
|
|
return point_index + 2;
|
|
}
|
|
return point_index + 1;
|
|
}
|
|
static int prev_adjacent_edge_point_index(const SubdivCCG &subdiv_ccg, const int point_index)
|
|
{
|
|
if (point_index == subdiv_ccg.grid_size) {
|
|
return point_index - 2;
|
|
}
|
|
return point_index - 1;
|
|
}
|
|
|
|
/* When the point index corresponds to a grid corner, returns the point index which corresponds to
|
|
* the corner of the adjacent grid, as the adjacent edge has two separate points for each grid
|
|
* corner at the middle of the edge. */
|
|
static int adjacent_grid_corner_point_index_on_edge(const SubdivCCG &subdiv_ccg,
|
|
const int point_index)
|
|
{
|
|
if (point_index == subdiv_ccg.grid_size) {
|
|
return point_index - 1;
|
|
}
|
|
return point_index + 1;
|
|
}
|
|
|
|
/* Common implementation of neighbor calculation when input coordinate is at the edge between two
|
|
* coarse faces, but is not at the coarse vertex. */
|
|
static void neighbor_coords_edge_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
|
|
{
|
|
const bool is_corner = is_corner_grid_coord(subdiv_ccg, coord);
|
|
const int adjacent_edge_index = adjacent_edge_index_from_coord(subdiv_ccg, coord);
|
|
const SubdivCCGAdjacentEdge *adjacent_edge = &subdiv_ccg.adjacent_edges[adjacent_edge_index];
|
|
|
|
/* 2 neighbor points along the edge, plus one inner point per every adjacent grid. */
|
|
const int num_adjacent_faces = adjacent_edge->num_adjacent_faces;
|
|
int num_duplicates = 0;
|
|
if (include_duplicates) {
|
|
num_duplicates += num_adjacent_faces - 1;
|
|
if (is_corner) {
|
|
/* When the coord is a grid corner, add an extra duplicate per adjacent grid in all adjacent
|
|
* faces to the edge. */
|
|
num_duplicates += num_adjacent_faces;
|
|
}
|
|
}
|
|
subdiv_ccg_neighbors_init(r_neighbors, num_adjacent_faces + 2, num_duplicates);
|
|
|
|
const int point_index = adjacent_edge_point_index_from_coord(
|
|
subdiv_ccg, coord, adjacent_edge_index);
|
|
const int point_index_duplicate = adjacent_grid_corner_point_index_on_edge(subdiv_ccg,
|
|
point_index);
|
|
|
|
const int next_point_index = next_adjacent_edge_point_index(subdiv_ccg, point_index);
|
|
const int prev_point_index = prev_adjacent_edge_point_index(subdiv_ccg, point_index);
|
|
|
|
int duplicate_i = num_adjacent_faces;
|
|
for (int i = 0; i < num_adjacent_faces; ++i) {
|
|
SubdivCCGCoord *boundary_coords = adjacent_edge->boundary_coords[i];
|
|
/* One step into the grid from the edge for each adjacent face. */
|
|
SubdivCCGCoord grid_coord = boundary_coords[point_index];
|
|
r_neighbors.coords[i + 2] = coord_step_inside_from_boundary(subdiv_ccg, grid_coord);
|
|
|
|
if (grid_coord.grid_index == coord.grid_index) {
|
|
/* Previous and next along the edge for the current grid. */
|
|
r_neighbors.coords[0] = boundary_coords[prev_point_index];
|
|
r_neighbors.coords[1] = boundary_coords[next_point_index];
|
|
}
|
|
else if (include_duplicates) {
|
|
/* Same coordinate on neighboring grids if requested. */
|
|
r_neighbors.coords[duplicate_i + 2] = grid_coord;
|
|
duplicate_i++;
|
|
}
|
|
|
|
/* When it is a corner, add the duplicate of the adjacent grid in the same face. */
|
|
if (include_duplicates && is_corner) {
|
|
SubdivCCGCoord duplicate_corner_grid_coord = boundary_coords[point_index_duplicate];
|
|
r_neighbors.coords[duplicate_i + 2] = duplicate_corner_grid_coord;
|
|
duplicate_i++;
|
|
}
|
|
}
|
|
BLI_assert(duplicate_i - num_adjacent_faces == num_duplicates);
|
|
}
|
|
|
|
/* The corner is at the middle of edge between faces. */
|
|
static void neighbor_coords_corner_edge_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
neighbor_coords_edge_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
|
|
/* Input coordinate is at one of 4 corners of its grid corners. */
|
|
static void neighbor_coords_corner_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
if (coord.x == 0 && coord.y == 0) {
|
|
neighbor_coords_corner_center_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
else {
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
if (coord.x == grid_size_1 && coord.y == grid_size_1) {
|
|
neighbor_coords_corner_vertex_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
else {
|
|
neighbor_coords_corner_edge_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Simple case of getting neighbors of a boundary coordinate: the input coordinate is at the
|
|
* boundary between two grids of the same face and there is no need to check adjacency with
|
|
* other faces. */
|
|
static void neighbor_coords_boundary_inner_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
subdiv_ccg_neighbors_init(r_neighbors, 4, (include_duplicates) ? 1 : 0);
|
|
|
|
if (coord.x == 0) {
|
|
r_neighbors.coords[0] = coord_at_prev_row(subdiv_ccg, coord);
|
|
r_neighbors.coords[1] = coord_at_next_row(subdiv_ccg, coord);
|
|
r_neighbors.coords[2] = coord_at_next_col(subdiv_ccg, coord);
|
|
|
|
r_neighbors.coords[3].grid_index = prev_grid_index_from_coord(subdiv_ccg, coord);
|
|
r_neighbors.coords[3].x = coord.y;
|
|
r_neighbors.coords[3].y = 1;
|
|
|
|
if (include_duplicates) {
|
|
r_neighbors.coords[4] = r_neighbors.coords[3];
|
|
r_neighbors.coords[4].y = 0;
|
|
}
|
|
}
|
|
else if (coord.y == 0) {
|
|
r_neighbors.coords[0] = coord_at_prev_col(subdiv_ccg, coord);
|
|
r_neighbors.coords[1] = coord_at_next_col(subdiv_ccg, coord);
|
|
r_neighbors.coords[2] = coord_at_next_row(subdiv_ccg, coord);
|
|
|
|
r_neighbors.coords[3].grid_index = next_grid_index_from_coord(subdiv_ccg, coord);
|
|
r_neighbors.coords[3].x = 1;
|
|
r_neighbors.coords[3].y = coord.x;
|
|
|
|
if (include_duplicates) {
|
|
r_neighbors.coords[4] = r_neighbors.coords[3];
|
|
r_neighbors.coords[4].x = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Input coordinate is on an edge between two faces. Need to check adjacency. */
|
|
static void neighbor_coords_boundary_outer_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
neighbor_coords_edge_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
|
|
/* Input coordinate is at one of 4 boundaries of its grid.
|
|
* It could either be an inner boundary (which connects face center to the face edge) or could be
|
|
* a part of coarse face edge. */
|
|
static void neighbor_coords_boundary_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
if (is_inner_edge_grid_coordinate(subdiv_ccg, coord)) {
|
|
neighbor_coords_boundary_inner_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
else {
|
|
neighbor_coords_boundary_outer_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
}
|
|
|
|
/* Input coordinate is inside of its grid, all the neighbors belong to the same grid. */
|
|
static void neighbor_coords_inner_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
subdiv_ccg_neighbors_init(r_neighbors, 4, 0);
|
|
|
|
r_neighbors.coords[0] = coord_at_prev_row(subdiv_ccg, coord);
|
|
r_neighbors.coords[1] = coord_at_next_row(subdiv_ccg, coord);
|
|
r_neighbors.coords[2] = coord_at_prev_col(subdiv_ccg, coord);
|
|
r_neighbors.coords[3] = coord_at_next_col(subdiv_ccg, coord);
|
|
}
|
|
|
|
void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const bool include_duplicates,
|
|
SubdivCCGNeighbors &r_neighbors)
|
|
{
|
|
BLI_assert(coord.grid_index >= 0);
|
|
BLI_assert(coord.grid_index < subdiv_ccg.grids.size());
|
|
BLI_assert(coord.x >= 0);
|
|
BLI_assert(coord.x < subdiv_ccg.grid_size);
|
|
BLI_assert(coord.y >= 0);
|
|
BLI_assert(coord.y < subdiv_ccg.grid_size);
|
|
|
|
if (is_corner_grid_coord(subdiv_ccg, coord)) {
|
|
neighbor_coords_corner_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
else if (is_boundary_grid_coord(subdiv_ccg, coord)) {
|
|
neighbor_coords_boundary_get(subdiv_ccg, coord, include_duplicates, r_neighbors);
|
|
}
|
|
else {
|
|
neighbor_coords_inner_get(subdiv_ccg, coord, r_neighbors);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
for (int i = 0; i < r_neighbors.size; i++) {
|
|
BLI_assert(BKE_subdiv_ccg_check_coord_valid(subdiv_ccg, r_neighbors.coords[i]));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG &subdiv_ccg)
|
|
{
|
|
if (subdiv_ccg.cache_.start_face_grid_index.is_empty()) {
|
|
const Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
|
|
if (topology_refiner == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
const int num_coarse_faces = topology_refiner->getNumFaces(topology_refiner);
|
|
|
|
subdiv_ccg.cache_.start_face_grid_index.reinitialize(num_coarse_faces);
|
|
|
|
int start_grid_index = 0;
|
|
for (int face_index = 0; face_index < num_coarse_faces; face_index++) {
|
|
const int num_face_grids = topology_refiner->getNumFaceVertices(topology_refiner,
|
|
face_index);
|
|
subdiv_ccg.cache_.start_face_grid_index[face_index] = start_grid_index;
|
|
start_grid_index += num_face_grids;
|
|
}
|
|
}
|
|
|
|
return subdiv_ccg.cache_.start_face_grid_index.data();
|
|
}
|
|
|
|
const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG &subdiv_ccg)
|
|
{
|
|
return subdiv_ccg.cache_.start_face_grid_index.data();
|
|
}
|
|
|
|
static void adjacent_vertices_index_from_adjacent_edge(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const blender::Span<int> corner_verts,
|
|
const blender::OffsetIndices<int> faces,
|
|
int &r_v1,
|
|
int &r_v2)
|
|
{
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord.grid_index);
|
|
const blender::IndexRange face = faces[face_index];
|
|
r_v1 = corner_verts[coord.grid_index];
|
|
|
|
const int corner = blender::bke::mesh::face_find_corner_from_vert(face, corner_verts, r_v1);
|
|
if (coord.x == grid_size_1) {
|
|
const int next = blender::bke::mesh::face_corner_next(face, corner);
|
|
r_v2 = corner_verts[next];
|
|
}
|
|
if (coord.y == grid_size_1) {
|
|
const int prev = blender::bke::mesh::face_corner_prev(face, corner);
|
|
r_v2 = corner_verts[prev];
|
|
}
|
|
}
|
|
|
|
SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
|
|
const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
const blender::Span<int> corner_verts,
|
|
const blender::OffsetIndices<int> faces,
|
|
int &r_v1,
|
|
int &r_v2)
|
|
{
|
|
|
|
const int grid_size_1 = subdiv_ccg.grid_size - 1;
|
|
if (is_corner_grid_coord(subdiv_ccg, coord)) {
|
|
if (coord.x == 0 && coord.y == 0) {
|
|
/* Grid corner in the center of a face. */
|
|
return SUBDIV_CCG_ADJACENT_NONE;
|
|
}
|
|
if (coord.x == grid_size_1 && coord.y == grid_size_1) {
|
|
/* Grid corner adjacent to a coarse mesh vertex. */
|
|
r_v1 = r_v2 = corner_verts[coord.grid_index];
|
|
return SUBDIV_CCG_ADJACENT_VERTEX;
|
|
}
|
|
/* Grid corner adjacent to the middle of a coarse mesh edge. */
|
|
adjacent_vertices_index_from_adjacent_edge(subdiv_ccg, coord, corner_verts, faces, r_v1, r_v2);
|
|
return SUBDIV_CCG_ADJACENT_EDGE;
|
|
}
|
|
|
|
if (is_boundary_grid_coord(subdiv_ccg, coord)) {
|
|
if (!is_inner_edge_grid_coordinate(subdiv_ccg, coord)) {
|
|
/* Grid boundary adjacent to a coarse mesh edge. */
|
|
adjacent_vertices_index_from_adjacent_edge(
|
|
subdiv_ccg, coord, corner_verts, faces, r_v1, r_v2);
|
|
return SUBDIV_CCG_ADJACENT_EDGE;
|
|
}
|
|
}
|
|
return SUBDIV_CCG_ADJACENT_NONE;
|
|
}
|
|
|
|
blender::BitGroupVector<> &BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg)
|
|
{
|
|
if (subdiv_ccg.grid_hidden.is_empty()) {
|
|
const int grid_area = subdiv_ccg.grid_size * subdiv_ccg.grid_size;
|
|
subdiv_ccg.grid_hidden = blender::BitGroupVector<>(subdiv_ccg.grids.size(), grid_area, false);
|
|
}
|
|
return subdiv_ccg.grid_hidden;
|
|
}
|
|
|
|
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg)
|
|
{
|
|
subdiv_ccg.grid_hidden = {};
|
|
}
|
|
|
|
static void subdiv_ccg_coord_to_ptex_coord(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
int *r_ptex_face_index,
|
|
float *r_u,
|
|
float *r_v)
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
|
|
const float grid_size = subdiv_ccg.grid_size;
|
|
const float grid_size_1_inv = 1.0f / (grid_size - 1);
|
|
|
|
const float grid_u = coord.x * grid_size_1_inv;
|
|
const float grid_v = coord.y * grid_size_1_inv;
|
|
|
|
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord.grid_index);
|
|
const OffsetIndices<int> faces = subdiv_ccg.faces;
|
|
const IndexRange face = faces[face_index];
|
|
const int *face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
|
|
*r_ptex_face_index = face_ptex_offset[face_index];
|
|
|
|
const float corner = coord.grid_index - face.start();
|
|
|
|
if (face.size() == 4) {
|
|
BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, r_u, r_v);
|
|
}
|
|
else {
|
|
*r_ptex_face_index += corner;
|
|
*r_u = 1.0f - grid_v;
|
|
*r_v = 1.0f - grid_u;
|
|
}
|
|
}
|
|
|
|
void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG &subdiv_ccg,
|
|
const SubdivCCGCoord &coord,
|
|
float r_point[3])
|
|
{
|
|
Subdiv *subdiv = subdiv_ccg.subdiv;
|
|
int ptex_face_index;
|
|
float u, v;
|
|
subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v);
|
|
BKE_subdiv_eval_limit_point(subdiv, ptex_face_index, u, v, r_point);
|
|
}
|
|
|
|
/** \} */
|