Fix #90641: wireframe does not hide edges of coplanar faces #105352

Merged
Germano Cavalcante merged 1 commits from mano-wii/blender:drw_edges_factor_new into main 2023-04-03 16:22:51 +02:00
6 changed files with 176 additions and 96 deletions

View File

@ -21,6 +21,7 @@
#include "BKE_particle.h"
#include "BLI_hash.h"
#include "BLI_math_base.hh"
#include "DRW_render.h"
#include "GPU_shader.h"
@ -29,6 +30,8 @@
#include "overlay_private.hh"
using namespace blender::math;
void OVERLAY_wireframe_init(OVERLAY_Data *vedata)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
@ -47,7 +50,13 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
View3DShading *shading = &draw_ctx->v3d->shading;
pd->shdata.wire_step_param = pd->overlay.wireframe_threshold - 254.0f / 255.0f;
/* Use `sqrt` since the value stored in the edge is a variation of the cosine, so its square
* becomes more proportional with a variation of angle. */
pd->shdata.wire_step_param = sqrt(abs(pd->overlay.wireframe_threshold));
/* The maximum value (255 in the vbo) is used to force hide the edge. */
pd->shdata.wire_step_param = interpolate(0.0f, 1.0f - (1.0f / 255), pd->shdata.wire_step_param);
pd->shdata.wire_opacity = pd->overlay.wireframe_opacity;
bool is_wire_shmode = (shading->type == OB_WIRE);

View File

@ -1,9 +1,9 @@
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
float get_edge_sharpness(float wd)
bool is_edge_sharpness_visible(float wd)
{
return ((wd == 0.0) ? -1.5 : wd) + wireStepParam;
return wd <= wireStepParam;
}
void wire_color_get(out vec3 rim_col, out vec3 wire_col)
@ -129,7 +129,7 @@ void main()
#endif
/* Cull flat edges below threshold. */
if (!no_attr && (get_edge_sharpness(wd) < 0.0)) {
if (!no_attr && !is_edge_sharpness_visible(wd)) {
edgeStart = vec2(-1.0);
}

View File

@ -1881,6 +1881,7 @@ void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache *cache,
void draw_subdiv_build_edge_fac_buffer(const DRWSubdivCache *cache,
GPUVertBuf *pos_nor,
GPUVertBuf *edge_draw_flag,
GPUVertBuf *poly_other_map,
GPUVertBuf *edge_fac)
{
GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_EDGE_FAC);
@ -1889,6 +1890,7 @@ void draw_subdiv_build_edge_fac_buffer(const DRWSubdivCache *cache,
int binding_point = 0;
GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++);
GPU_vertbuf_bind_as_ssbo(edge_draw_flag, binding_point++);
GPU_vertbuf_bind_as_ssbo(poly_other_map, binding_point++);
GPU_vertbuf_bind_as_ssbo(edge_fac, binding_point++);
BLI_assert(binding_point <= MAX_GPU_SUBDIV_SSBOS);

View File

@ -259,6 +259,7 @@ void draw_subdiv_extract_uvs(const DRWSubdivCache *cache,
void draw_subdiv_build_edge_fac_buffer(const DRWSubdivCache *cache,
struct GPUVertBuf *pos_nor,
struct GPUVertBuf *edge_draw_flag,
struct GPUVertBuf *poly_other_map,
struct GPUVertBuf *edge_fac);
void draw_subdiv_build_tris_buffer(const DRWSubdivCache *cache,

View File

@ -7,11 +7,14 @@
#include "MEM_guardedalloc.h"
#include "BLI_math_vector.hh"
#include "GPU_capabilities.h"
#include "draw_subdivision.h"
#include "extract_mesh.hh"
#define FORCE_HIDE 255
namespace blender::draw {
/* ---------------------------------------------------------------------- */
@ -19,27 +22,38 @@ namespace blender::draw {
* Defines how much an edge is visible.
* \{ */
struct MEdgeDataPrev {
int corner_a;
/* Data that represents:
* - the index of the polygon of `corner_a` before the 2nd loop is found
* - the index of the next radial corner after the 2nd loop is found */
int data;
};
struct MeshExtract_EdgeFac_Data {
uint8_t *vbo_data;
bool use_edge_render;
/* Number of loop per edge. */
uint8_t *edge_loop_count;
MEdgeDataPrev *edge_pdata;
};
static float loop_edge_factor_get(const float f_no[3],
const float v_co[3],
const float v_no[3],
const float v_next_co[3])
/**
* Calculates a factor that is used to identify the minimum angle in the shader to display an edge.
* NOTE: Keep in sync with `common_subdiv_vbo_edge_fac_comp.glsl`.
*/
BLI_INLINE uint8_t loop_edge_factor_get(const float3 &fa_no, const float3 &fb_no)
{
float enor[3], evec[3];
sub_v3_v3v3(evec, v_next_co, v_co);
cross_v3_v3v3(enor, v_no, evec);
normalize_v3(enor);
float d = fabsf(dot_v3v3(enor, f_no));
const float cosine = math::dot(fa_no, fb_no);
/* Re-scale to the slider range. */
d *= (1.0f / 0.065f);
CLAMP(d, 0.0f, 1.0f);
return d;
float fac = (200 * (cosine - 1.0f)) + 1.0f;
CLAMP(fac, 0.0f, 1.0f);
/* 255 is a reserved value to force hide the wire. */
return uint8_t(fac * 254);
}
static void extract_edge_fac_init(const MeshRenderData *mr,
@ -59,10 +73,10 @@ static void extract_edge_fac_init(const MeshRenderData *mr,
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(tls_data);
if (mr->extract_type == MR_EXTRACT_MESH) {
data->use_edge_render = !mr->me->runtime->subsurf_optimal_display_edges.is_empty();
data->edge_loop_count = MEM_cnew_array<uint8_t>(mr->edge_len, __func__);
if (!mr->me->runtime->subsurf_optimal_display_edges.is_empty()) {
data->use_edge_render = true;
}
data->edge_pdata = (MEdgeDataPrev *)MEM_malloc_arrayN(
mr->edge_len, sizeof(MEdgeDataPrev), __func__);
}
else {
/* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
@ -84,14 +98,12 @@ static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
const int l_index = BM_elem_index_get(l_iter);
if (BM_edge_is_manifold(l_iter->e)) {
float ratio = loop_edge_factor_get(bm_face_no_get(mr, f),
bm_vert_co_get(mr, l_iter->v),
bm_vert_no_get(mr, l_iter->v),
bm_vert_co_get(mr, l_iter->next->v));
data->vbo_data[l_index] = ratio * 253 + 1;
BMFace *fb = l_iter->f != f ? l_iter->f : l_iter->radial_next->f;
data->vbo_data[l_index] = loop_edge_factor_get(float3(bm_face_no_get(mr, f)),
float3(bm_face_no_get(mr, fb)));
}
else {
data->vbo_data[l_index] = 255;
data->vbo_data[l_index] = 0;
}
} while ((l_iter = l_iter->next) != l_first);
}
@ -106,32 +118,45 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
const int ml_index_end = poly->loopstart + poly->totloop;
for (int ml_index = poly->loopstart; ml_index < ml_index_end; ml_index += 1) {
const int vert = mr->corner_verts[ml_index];
const int edge = mr->corner_edges[ml_index];
if (data->use_edge_render) {
data->vbo_data[ml_index] = optimal_display_edges[edge] ? 255 : 0;
if (data->use_edge_render && !optimal_display_edges[edge]) {
data->vbo_data[ml_index] = FORCE_HIDE;
}
else {
MEdgeDataPrev *medata = &data->edge_pdata[edge];
/* Count loop per edge to detect non-manifold. */
if (data->edge_loop_count[edge] < 3) {
data->edge_loop_count[edge]++;
}
if (data->edge_loop_count[edge] == 2) {
/* Manifold */
const int ml_index_last = poly->totloop + poly->loopstart - 1;
const int ml_index_other = (ml_index == ml_index_last) ? poly->loopstart : (ml_index + 1);
const int vert_next = mr->corner_verts[ml_index_other];
float ratio = loop_edge_factor_get(mr->poly_normals[poly_index],
mr->vert_positions[vert],
mr->vert_normals[vert],
mr->vert_positions[vert_next]);
data->vbo_data[ml_index] = ratio * 253 + 1;
}
else {
/* Non-manifold */
data->vbo_data[ml_index] = 255;
uint8_t corner_count = data->edge_loop_count[edge];
if (corner_count < 4) {
if (corner_count == 0) {
/* Prepare to calculate the factor. */
medata->corner_a = ml_index;
medata->data = poly_index;
/* Consider boundary edge while second corner is not detected. Always visible. */
data->vbo_data[ml_index] = 0;
}
else if (corner_count == 1) {
/* Calculate the factor for both corners. */
const int poly_index_a = medata->data;
uint8_t fac = loop_edge_factor_get(float3(mr->poly_normals[poly_index_a]),
float3(mr->poly_normals[poly_index]));
data->vbo_data[medata->corner_a] = fac;
data->vbo_data[ml_index] = fac;
/* If the count still changes, use this `data` member to inform the corner. */
medata->data = ml_index;
}
else {
/* Non-manifold edge. Always visible. */
const int corner_a = medata->corner_a;
const int corner_b = medata->data;
data->vbo_data[corner_a] = 0;
data->vbo_data[corner_b] = 0;
}
/* Increment the corner_count count. */
data->edge_loop_count[edge] = corner_count + 1;
}
}
}
@ -143,8 +168,8 @@ static void extract_edge_fac_iter_loose_edge_bm(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 0;
data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 0;
}
static void extract_edge_fac_iter_loose_edge_mesh(const MeshRenderData *mr,
@ -154,8 +179,8 @@ static void extract_edge_fac_iter_loose_edge_mesh(const MeshRenderData *mr,
{
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 0;
data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 0;
}
static void extract_edge_fac_finish(const MeshRenderData *mr,
@ -190,6 +215,7 @@ static void extract_edge_fac_finish(const MeshRenderData *mr,
MEM_freeN(data->vbo_data);
}
MEM_SAFE_FREE(data->edge_loop_count);
MEM_SAFE_FREE(data->edge_pdata);
}
/* Different function than the one used for the non-subdivision case, as we directly take care of
@ -208,39 +234,72 @@ static GPUVertFormat *get_subdiv_edge_fac_format()
return &format;
}
static GPUVertBuf *build_poly_other_map_vbo(const DRWSubdivCache *subdiv_cache)
{
GPUVertBuf *vbo = GPU_vertbuf_calloc();
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "poly_other", GPU_COMP_I32, 1, GPU_FETCH_INT);
}
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, subdiv_cache->num_subdiv_loops);
MutableSpan vbo_data{static_cast<int *>(GPU_vertbuf_get_data(vbo)),
subdiv_cache->num_subdiv_loops};
Array<MEdgeDataPrev> edge_data(subdiv_cache->num_subdiv_edges);
Array<int> tmp_edge_corner_count(subdiv_cache->num_subdiv_edges, 0);
int *subdiv_loop_subdiv_edge_index = subdiv_cache->subdiv_loop_subdiv_edge_index;
for (int corner : IndexRange(subdiv_cache->num_subdiv_loops)) {
const int edge = subdiv_loop_subdiv_edge_index[corner];
const int quad = corner / 4;
const int corner_count = tmp_edge_corner_count[edge]++;
vbo_data[corner] = -1;
if (corner_count == 0) {
edge_data[edge].corner_a = corner;
edge_data[edge].data = quad;
}
else if (corner_count == 1) {
const int corner_a = edge_data[edge].corner_a;
const int quad_a = edge_data[edge].data;
vbo_data[corner_a] = quad;
vbo_data[corner] = quad_a;
edge_data[edge].data = corner;
}
else if (corner_count == 2) {
const int corner_a = edge_data[edge].corner_a;
const int corner_b = edge_data[edge].data;
vbo_data[corner_a] = -1;
vbo_data[corner_b] = -1;
}
}
return vbo;
}
static void extract_edge_fac_init_subdiv(const DRWSubdivCache *subdiv_cache,
const MeshRenderData * /*mr*/,
MeshBatchCache *cache,
void *buffer,
void * /*data*/)
{
const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom;
GPUVertBuf *edge_idx = cache->final.buff.vbo.edge_idx;
GPUVertBuf *pos_nor = cache->final.buff.vbo.pos_nor;
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer);
const DRWSubdivLooseGeom &loose_geom = subdiv_cache->loose_geom;
GPU_vertbuf_init_build_on_device(
vbo, get_subdiv_edge_fac_format(), subdiv_cache->num_subdiv_loops + loose_geom.loop_len);
/* Create a temporary buffer for the edge original indices if it was not requested. */
const bool has_edge_idx = edge_idx != nullptr;
GPUVertBuf *loop_edge_draw_flag = nullptr;
if (has_edge_idx) {
loop_edge_draw_flag = edge_idx;
}
else {
loop_edge_draw_flag = GPU_vertbuf_calloc();
draw_subdiv_init_origindex_buffer(
loop_edge_draw_flag,
static_cast<int *>(GPU_vertbuf_get_data(subdiv_cache->edges_draw_flag)),
subdiv_cache->num_subdiv_loops,
0);
}
GPUVertBuf *pos_nor = cache->final.buff.vbo.pos_nor;
GPUVertBuf *poly_other_map = build_poly_other_map_vbo(subdiv_cache);
draw_subdiv_build_edge_fac_buffer(subdiv_cache, pos_nor, loop_edge_draw_flag, vbo);
draw_subdiv_build_edge_fac_buffer(
subdiv_cache, pos_nor, subdiv_cache->edges_draw_flag, poly_other_map, vbo);
if (!has_edge_idx) {
GPU_vertbuf_discard(loop_edge_draw_flag);
}
GPU_vertbuf_discard(poly_other_map);
}
static void extract_edge_fac_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,

View File

@ -11,7 +11,12 @@ layout(std430, binding = 1) readonly buffer inputEdgeDrawFlag
uint input_edge_draw_flag[];
};
layout(std430, binding = 2) writeonly buffer outputEdgeFactors
layout(std430, binding = 2) readonly buffer inputPolyOtherMap
{
int input_poly_other_map[];
};
layout(std430, binding = 3) writeonly buffer outputEdgeFactors
{
#ifdef GPU_AMD_DRIVER_BYTE_BUG
float output_edge_fac[];
@ -28,42 +33,46 @@ void write_vec4(uint index, vec4 edge_facs)
}
#else
/* Use same scaling as in extract_edge_fac_iter_poly_mesh. */
uint a = uint(clamp(edge_facs.x * 253.0 + 1.0, 0.0, 255.0));
uint b = uint(clamp(edge_facs.y * 253.0 + 1.0, 0.0, 255.0));
uint c = uint(clamp(edge_facs.z * 253.0 + 1.0, 0.0, 255.0));
uint d = uint(clamp(edge_facs.w * 253.0 + 1.0, 0.0, 255.0));
uint packed_edge_fac = a << 24 | b << 16 | c << 8 | d;
uint a = uint(edge_facs.x * 255);
uint b = uint(edge_facs.y * 255);
uint c = uint(edge_facs.z * 255);
uint d = uint(edge_facs.w * 255);
uint packed_edge_fac = d << 24 | c << 16 | b << 8 | a;
output_edge_fac[index] = packed_edge_fac;
#endif
}
/* From extract_mesh_vbo_edge_fac.cc, keep in sync! */
float loop_edge_factor_get(vec3 f_no, vec3 v_co, vec3 v_no, vec3 v_next_co)
float loop_edge_factor_get(vec3 fa_no, vec3 fb_no)
{
vec3 evec = v_next_co - v_co;
vec3 enor = normalize(cross(v_no, evec));
float d = abs(dot(enor, f_no));
float cosine = dot(fa_no, fb_no);
/* Re-scale to the slider range. */
d *= (1.0 / 0.065);
return clamp(d, 0.0, 1.0);
float fac = (200 * (cosine - 1.0)) + 1.0;
/* The maximum value (255) is unreachable through the UI. */
return clamp(fac, 0.0, 1.0) * (254.0 / 255.0);
}
float compute_line_factor(uint start_loop_index, uint corner_index, vec3 face_normal)
float compute_line_factor(uint corner_index, vec3 face_normal)
{
uint vertex_index = start_loop_index + corner_index;
uint edge_draw_flag = input_edge_draw_flag[vertex_index];
if (input_edge_draw_flag[corner_index] == 0) {
return 1.0;
}
if (edge_draw_flag == 0) {
int quad_other = input_poly_other_map[corner_index];
if (quad_other == -1) {
/* Boundary edge or non-manifold. */
return 0.0;
}
/* Mod 4 so we loop back at the first vertex on the last loop index (3), but only the corner
* index needs to be wrapped. */
uint next_vertex_index = start_loop_index + (corner_index + 1) % 4;
vec3 vertex_pos = get_vertex_pos(pos_nor[vertex_index]);
vec3 vertex_nor = get_vertex_nor(pos_nor[vertex_index]);
vec3 next_vertex_pos = get_vertex_pos(pos_nor[next_vertex_index]);
return loop_edge_factor_get(face_normal, vertex_pos, vertex_nor, next_vertex_pos);
uint start_coner_index_other = quad_other * 4;
vec3 v0 = get_vertex_pos(pos_nor[start_coner_index_other + 0]);
vec3 v1 = get_vertex_pos(pos_nor[start_coner_index_other + 1]);
vec3 v2 = get_vertex_pos(pos_nor[start_coner_index_other + 2]);
vec3 face_normal_other = normalize(cross(v1 - v0, v2 - v0));
return loop_edge_factor_get(face_normal, face_normal_other);
}
void main()
@ -84,8 +93,8 @@ void main()
vec3 face_normal = normalize(cross(v1 - v0, v2 - v0));
vec4 edge_facs = vec4(0.0);
for (int i = 0; i < 4; i++) {
edge_facs[i] = compute_line_factor(start_loop_index, i, face_normal);
for (uint i = 0; i < 4; i++) {
edge_facs[i] = compute_line_factor(start_loop_index + i, face_normal);
}
#ifdef GPU_AMD_DRIVER_BYTE_BUG