tornavis/source/blender/draw/intern/draw_cache_extract_mesh_ren...

644 lines
21 KiB
C++

/* SPDX-FileCopyrightText: 2021 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*
* \brief Extraction of Mesh data into VBO to feed to GPU.
*/
#include "MEM_guardedalloc.h"
#include "BLI_array.hh"
#include "BLI_array_utils.hh"
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_index_mask.hh"
#include "BLI_math_matrix.h"
#include "BLI_task.hh"
#include "BLI_virtual_array.hh"
#include "BKE_attribute.hh"
#include "BKE_editmesh.hh"
#include "BKE_editmesh_cache.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_runtime.hh"
#include "GPU_batch.h"
#include "ED_mesh.hh"
#include "mesh_extractors/extract_mesh.hh"
/* ---------------------------------------------------------------------- */
/** \name Update Loose Geometry
* \{ */
static void extract_set_bits(const blender::BitSpan bits, blender::MutableSpan<int> indices)
{
int count = 0;
for (const int64_t i : bits.index_range()) {
if (bits[i]) {
indices[count] = int(i);
count++;
}
}
BLI_assert(count == indices.size());
}
static void mesh_render_data_loose_geom_mesh(const MeshRenderData &mr, MeshBufferCache &cache)
{
using namespace blender;
const Mesh &mesh = *mr.me;
const bool no_loose_vert_hint = mesh.runtime->loose_verts_cache.is_cached() &&
mesh.runtime->loose_verts_cache.data().count == 0;
const bool no_loose_edge_hint = mesh.runtime->loose_edges_cache.is_cached() &&
mesh.runtime->loose_edges_cache.data().count == 0;
threading::parallel_invoke(
mesh.totedge > 4096 && !no_loose_vert_hint && !no_loose_edge_hint,
[&]() {
const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
if (loose_edges.count > 0) {
cache.loose_geom.edges.reinitialize(loose_edges.count);
extract_set_bits(loose_edges.is_loose_bits, cache.loose_geom.edges);
}
},
[&]() {
const bke::LooseVertCache &loose_verts = mesh.loose_verts();
if (loose_verts.count > 0) {
cache.loose_geom.verts.reinitialize(loose_verts.count);
extract_set_bits(loose_verts.is_loose_bits, cache.loose_geom.verts);
}
});
}
static void mesh_render_data_loose_verts_bm(const MeshRenderData &mr,
MeshBufferCache &cache,
BMesh &bm)
{
using namespace blender;
int i;
BMIter iter;
BMVert *vert;
int count = 0;
Array<int> loose_verts(mr.vert_len);
BM_ITER_MESH_INDEX (vert, &iter, &bm, BM_VERTS_OF_MESH, i) {
if (vert->e == nullptr) {
loose_verts[count] = i;
count++;
}
}
if (count < mr.vert_len) {
cache.loose_geom.verts = loose_verts.as_span().take_front(count);
}
else {
cache.loose_geom.verts = std::move(loose_verts);
}
}
static void mesh_render_data_loose_edges_bm(const MeshRenderData &mr,
MeshBufferCache &cache,
BMesh &bm)
{
using namespace blender;
int i;
BMIter iter;
BMEdge *edge;
int count = 0;
Array<int> loose_edges(mr.edge_len);
BM_ITER_MESH_INDEX (edge, &iter, &bm, BM_EDGES_OF_MESH, i) {
if (edge->l == nullptr) {
loose_edges[count] = i;
count++;
}
}
if (count < mr.edge_len) {
cache.loose_geom.edges = loose_edges.as_span().take_front(count);
}
else {
cache.loose_geom.edges = std::move(loose_edges);
}
}
static void mesh_render_data_loose_geom_build(const MeshRenderData &mr, MeshBufferCache &cache)
{
if (mr.extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
mesh_render_data_loose_geom_mesh(mr, cache);
}
else {
/* #BMesh */
BMesh &bm = *mr.bm;
mesh_render_data_loose_verts_bm(mr, cache, bm);
mesh_render_data_loose_edges_bm(mr, cache, bm);
}
}
static void mesh_render_data_loose_geom_ensure(const MeshRenderData &mr, MeshBufferCache &cache)
{
/* Early exit: Are loose geometry already available.
* Only checking for loose verts as loose edges and verts are calculated at the same time. */
if (!cache.loose_geom.verts.is_empty()) {
return;
}
mesh_render_data_loose_geom_build(mr, cache);
}
void mesh_render_data_update_loose_geom(MeshRenderData &mr,
MeshBufferCache &cache,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
if ((iter_type & (MR_ITER_LOOSE_EDGE | MR_ITER_LOOSE_VERT)) || (data_flag & MR_DATA_LOOSE_GEOM))
{
mesh_render_data_loose_geom_ensure(mr, cache);
mr.loose_edges = cache.loose_geom.edges;
mr.loose_verts = cache.loose_geom.verts;
mr.vert_loose_len = cache.loose_geom.verts.size();
mr.edge_loose_len = cache.loose_geom.edges.size();
mr.loop_loose_len = mr.vert_loose_len + (mr.edge_loose_len * 2);
}
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Polygons sorted per material
*
* Contains face indices sorted based on their material.
* \{ */
namespace blender::draw {
static void accumululate_material_counts_bm(
const BMesh &bm, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
{
threading::parallel_for(IndexRange(bm.totface), 1024, [&](const IndexRange range) {
Array<int> &tri_counts = all_tri_counts.local();
const short last_index = tri_counts.size() - 1;
for (const int i : range) {
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), i);
if (!BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
const short mat = std::clamp<short>(face.mat_nr, 0, last_index);
tri_counts[mat] += face.len - 2;
}
}
});
}
static void accumululate_material_counts_mesh(
const MeshRenderData &mr, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
{
const OffsetIndices faces = mr.faces;
if (!mr.material_indices) {
if (mr.use_hide && mr.hide_poly) {
const Span hide_poly(mr.hide_poly, mr.face_len);
all_tri_counts.local().first() = threading::parallel_reduce(
faces.index_range(),
4096,
0,
[&](const IndexRange range, int count) {
for (const int face : range) {
if (!hide_poly[face]) {
count += bke::mesh::face_triangles_num(faces[face].size());
}
}
return count;
},
std::plus<int>());
}
else {
all_tri_counts.local().first() = poly_to_tri_count(mr.face_len, mr.loop_len);
}
return;
}
const Span material_indices(mr.material_indices, mr.face_len);
threading::parallel_for(material_indices.index_range(), 1024, [&](const IndexRange range) {
Array<int> &tri_counts = all_tri_counts.local();
const int last_index = tri_counts.size() - 1;
if (mr.use_hide && mr.hide_poly) {
for (const int i : range) {
if (!mr.hide_poly[i]) {
const int mat = std::clamp(material_indices[i], 0, last_index);
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
}
}
}
else {
for (const int i : range) {
const int mat = std::clamp(material_indices[i], 0, last_index);
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
}
}
});
}
/* Count how many triangles for each material. */
static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
{
threading::EnumerableThreadSpecific<Array<int>> all_tri_counts(
[&]() { return Array<int>(mr.mat_len, 0); });
if (mr.extract_type == MR_EXTRACT_BMESH) {
accumululate_material_counts_bm(*mr.bm, all_tri_counts);
}
else {
accumululate_material_counts_mesh(mr, all_tri_counts);
}
Array<int> &mat_tri_len = all_tri_counts.local();
for (const Array<int> &counts : all_tri_counts) {
if (&counts != &mat_tri_len) {
for (const int i : mat_tri_len.index_range()) {
mat_tri_len[i] += counts[i];
}
}
}
return std::move(mat_tri_len);
}
static void mesh_render_data_faces_sorted_build(MeshRenderData &mr, MeshBufferCache &cache)
{
cache.face_sorted.mat_tri_len = mesh_render_data_mat_tri_len_build(mr);
const Span<int> mat_tri_len = cache.face_sorted.mat_tri_len;
/* Apply offset. */
int visible_tri_len = 0;
Array<int, 32> mat_tri_offs(mr.mat_len);
{
for (int i = 0; i < mr.mat_len; i++) {
mat_tri_offs[i] = visible_tri_len;
visible_tri_len += mat_tri_len[i];
}
}
cache.face_sorted.visible_tri_len = visible_tri_len;
cache.face_sorted.tri_first_index.reinitialize(mr.face_len);
MutableSpan<int> tri_first_index = cache.face_sorted.tri_first_index;
/* Sort per material. */
int mat_last = mr.mat_len - 1;
if (mr.extract_type == MR_EXTRACT_BMESH) {
BMIter iter;
BMFace *f;
int i;
BM_ITER_MESH_INDEX (f, &iter, mr.bm, BM_FACES_OF_MESH, i) {
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
const int mat = clamp_i(f->mat_nr, 0, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += f->len - 2;
}
else {
tri_first_index[i] = -1;
}
}
}
else {
for (int i = 0; i < mr.face_len; i++) {
if (!(mr.use_hide && mr.hide_poly && mr.hide_poly[i])) {
const int mat = mr.material_indices ? clamp_i(mr.material_indices[i], 0, mat_last) : 0;
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mr.faces[i].size() - 2;
}
else {
tri_first_index[i] = -1;
}
}
}
}
static void mesh_render_data_faces_sorted_ensure(MeshRenderData &mr, MeshBufferCache &cache)
{
if (!cache.face_sorted.tri_first_index.is_empty()) {
return;
}
mesh_render_data_faces_sorted_build(mr, cache);
}
} // namespace blender::draw
void mesh_render_data_update_faces_sorted(MeshRenderData &mr,
MeshBufferCache &cache,
const eMRDataType data_flag)
{
if (data_flag & MR_DATA_POLYS_SORTED) {
blender::draw::mesh_render_data_faces_sorted_ensure(mr, cache);
mr.face_sorted = &cache.face_sorted;
}
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
* \{ */
void mesh_render_data_update_looptris(MeshRenderData &mr,
const eMRIterType iter_type,
const eMRDataType data_flag)
{
if (mr.extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
mr.looptris = mr.me->looptris();
mr.looptri_faces = mr.me->looptri_faces();
}
}
else {
/* #BMesh */
if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
/* Edit mode ensures this is valid, no need to calculate. */
BLI_assert((mr.bm->totloop == 0) || (mr.edit_bmesh->looptris != nullptr));
}
}
}
static bool bm_edge_is_sharp(const BMEdge *const &edge)
{
return !BM_elem_flag_test(edge, BM_ELEM_SMOOTH);
}
static bool bm_face_is_sharp(const BMFace *const &face)
{
return !BM_elem_flag_test(face, BM_ELEM_SMOOTH);
}
/**
* Returns whether loop normals are required because of mixed sharp and smooth flags.
* Similar to #Mesh::normals_domain().
*/
static bool bm_loop_normals_required(BMesh *bm)
{
using namespace blender;
using namespace blender::bke;
if (bm->totface == 0) {
return false;
}
if (CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
return true;
}
BM_mesh_elem_table_ensure(bm, BM_FACE);
const VArray<bool> sharp_faces = VArray<bool>::ForDerivedSpan<const BMFace *, bm_face_is_sharp>(
Span(bm->ftable, bm->totface));
const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces);
if (face_mix == array_utils::BooleanMix::AllTrue) {
return false;
}
BM_mesh_elem_table_ensure(bm, BM_EDGE);
const VArray<bool> sharp_edges = VArray<bool>::ForDerivedSpan<const BMEdge *, bm_edge_is_sharp>(
Span(bm->etable, bm->totedge));
const array_utils::BooleanMix edge_mix = array_utils::booleans_mix_calc(sharp_edges);
if (edge_mix == array_utils::BooleanMix::AllTrue) {
return false;
}
if (edge_mix == array_utils::BooleanMix::AllFalse &&
face_mix == array_utils::BooleanMix::AllFalse) {
return false;
}
return true;
}
void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_flag)
{
if (mr.extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
mr.vert_normals = mr.me->vert_normals();
if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
mr.face_normals = mr.me->face_normals();
}
if (((data_flag & MR_DATA_LOOP_NOR) && ELEM(mr.me->normals_domain(),
blender::bke::MeshNormalDomain::Corner,
blender::bke::MeshNormalDomain::Face)) ||
(data_flag & MR_DATA_TAN_LOOP_NOR))
{
mr.loop_normals = mr.me->corner_normals();
}
}
else {
/* #BMesh */
if (data_flag & MR_DATA_POLY_NOR) {
/* Use #BMFace.no instead. */
}
if (((data_flag & MR_DATA_LOOP_NOR) && bm_loop_normals_required(mr.bm)) ||
(data_flag & MR_DATA_TAN_LOOP_NOR))
{
const float(*vert_coords)[3] = nullptr;
const float(*vert_normals)[3] = nullptr;
const float(*face_normals)[3] = nullptr;
if (mr.edit_data && !mr.edit_data->vertexCos.is_empty()) {
vert_coords = reinterpret_cast<const float(*)[3]>(mr.bm_vert_coords.data());
vert_normals = reinterpret_cast<const float(*)[3]>(mr.bm_vert_normals.data());
face_normals = reinterpret_cast<const float(*)[3]>(mr.bm_face_normals.data());
}
mr.bm_loop_normals.reinitialize(mr.loop_len);
const int clnors_offset = CustomData_get_offset(&mr.bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(mr.bm,
vert_coords,
vert_normals,
face_normals,
true,
reinterpret_cast<float(*)[3]>(mr.bm_loop_normals.data()),
nullptr,
nullptr,
clnors_offset,
false);
mr.loop_normals = mr.bm_loop_normals;
}
}
}
static void retrieve_active_attribute_names(MeshRenderData &mr,
const Object &object,
const Mesh &mesh)
{
const Mesh *mesh_final = editmesh_final_or_this(&object, &mesh);
mr.active_color_name = mesh_final->active_color_attribute;
mr.default_color_name = mesh_final->default_color_attribute;
}
MeshRenderData *mesh_render_data_create(Object *object,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
const bool is_mode_active,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
const ToolSettings *ts)
{
MeshRenderData *mr = MEM_new<MeshRenderData>(__func__);
mr->toolsettings = ts;
mr->mat_len = mesh_render_mat_len_get(object, me);
copy_m4_m4(mr->obmat, obmat);
if (is_editmode) {
Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(object);
BLI_assert(editmesh_eval_cage && editmesh_eval_final);
mr->bm = me->edit_mesh->bm;
mr->edit_bmesh = me->edit_mesh;
mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage;
mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr;
/* If there is no distinct cage, hide unmapped edges that can't be selected. */
mr->hide_unmapped_edges = !do_final || editmesh_eval_final == editmesh_eval_cage;
if (mr->edit_data) {
blender::bke::EditMeshData *emd = mr->edit_data;
if (!emd->vertexCos.is_empty()) {
BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
BKE_editmesh_cache_ensure_face_normals(mr->edit_bmesh, emd);
}
mr->bm_vert_coords = mr->edit_data->vertexCos;
mr->bm_vert_normals = mr->edit_data->vertexNos;
mr->bm_face_normals = mr->edit_data->faceNos;
mr->bm_face_centers = mr->edit_data->faceCos;
}
int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
mr->eed_act = BM_mesh_active_edge_get(mr->bm);
mr->eve_act = BM_mesh_active_vert_get(mr->bm);
mr->vert_crease_ofs = CustomData_get_offset_named(
&mr->bm->vdata, CD_PROP_FLOAT, "crease_vert");
mr->edge_crease_ofs = CustomData_get_offset_named(
&mr->bm->edata, CD_PROP_FLOAT, "crease_edge");
mr->bweight_ofs = CustomData_get_offset_named(
&mr->bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
#ifdef WITH_FREESTYLE
mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
#endif
/* Use bmesh directly when the object is in edit mode unchanged by any modifiers.
* For non-final UVs, always use original bmesh since the UV editor does not support
* using the cage mesh with deformed coordinates. */
if ((is_mode_active && mr->me->runtime->is_original_bmesh &&
mr->me->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) ||
(do_uvedit && !do_final))
{
mr->extract_type = MR_EXTRACT_BMESH;
}
else {
mr->extract_type = MR_EXTRACT_MESH;
/* Use mapping from final to original mesh when the object is in edit mode. */
if (is_mode_active && do_final) {
mr->v_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->vert_data, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->edge_data, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->face_data, CD_ORIGINDEX));
}
else {
mr->v_origindex = nullptr;
mr->e_origindex = nullptr;
mr->p_origindex = nullptr;
}
}
}
else {
mr->me = me;
mr->edit_bmesh = nullptr;
mr->extract_type = MR_EXTRACT_MESH;
mr->hide_unmapped_edges = false;
if (is_paint_mode && mr->me) {
mr->v_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->vert_data, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->edge_data, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->face_data, CD_ORIGINDEX));
}
else {
mr->v_origindex = nullptr;
mr->e_origindex = nullptr;
mr->p_origindex = nullptr;
}
}
if (mr->extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
mr->vert_len = mr->me->totvert;
mr->edge_len = mr->me->totedge;
mr->loop_len = mr->me->totloop;
mr->face_len = mr->me->faces_num;
mr->tri_len = poly_to_tri_count(mr->face_len, mr->loop_len);
mr->vert_positions = mr->me->vert_positions();
mr->edges = mr->me->edges();
mr->faces = mr->me->faces();
mr->corner_verts = mr->me->corner_verts();
mr->corner_edges = mr->me->corner_edges();
mr->v_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->vert_data, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->edge_data, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->face_data, CD_ORIGINDEX));
mr->material_indices = static_cast<const int *>(
CustomData_get_layer_named(&mr->me->face_data, CD_PROP_INT32, "material_index"));
mr->hide_vert = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->vert_data, CD_PROP_BOOL, ".hide_vert"));
mr->hide_edge = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->edge_data, CD_PROP_BOOL, ".hide_edge"));
mr->hide_poly = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->face_data, CD_PROP_BOOL, ".hide_poly"));
mr->select_vert = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->vert_data, CD_PROP_BOOL, ".select_vert"));
mr->select_edge = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->edge_data, CD_PROP_BOOL, ".select_edge"));
mr->select_poly = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->face_data, CD_PROP_BOOL, ".select_poly"));
mr->sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&mr->me->face_data, CD_PROP_BOOL, "sharp_face"));
}
else {
/* #BMesh */
BMesh *bm = mr->bm;
mr->vert_len = bm->totvert;
mr->edge_len = bm->totedge;
mr->loop_len = bm->totloop;
mr->face_len = bm->totface;
mr->tri_len = poly_to_tri_count(mr->face_len, mr->loop_len);
}
retrieve_active_attribute_names(*mr, *object, *mr->me);
return mr;
}
void mesh_render_data_free(MeshRenderData *mr)
{
MEM_delete(mr);
}
/** \} */