Workbench Next: Sculpt Support #109294

Merged
Miguel Pozo merged 6 commits from pragma37/blender:pull-workbench-next-sculpt into main 2023-06-26 11:56:17 +02:00
4 changed files with 382 additions and 58 deletions

View File

@ -100,6 +100,7 @@ set(SRC
intern/draw_pbvh.cc
intern/draw_pointcloud.cc
intern/draw_resource.cc
intern/draw_sculpt.cc
intern/draw_select_buffer.c
intern/draw_shader.cc
intern/draw_texture_pool.cc
@ -261,6 +262,7 @@ set(SRC
intern/draw_pass.hh
intern/draw_pbvh.h
intern/draw_resource.hh
intern/draw_sculpt.hh
intern/draw_shader.h
intern/draw_shader_shared.h
intern/draw_state.h

View File

@ -15,6 +15,8 @@
#include "ED_view3d.h"
#include "GPU_capabilities.h"
#include "draw_sculpt.hh"
#include "workbench_private.hh"
#include "workbench_engine.h" /* Own include. */
@ -128,6 +130,7 @@ class Instance {
* when switching from eevee to workbench).
*/
if (ob_ref.object->sculpt && ob_ref.object->sculpt->pbvh) {
/* TODO(Miguel Pozo): Could this me moved to sculpt_batches_get()? */
BKE_pbvh_is_drawing_set(ob_ref.object->sculpt->pbvh, object_state.sculpt_pbvh);
}
@ -180,7 +183,10 @@ class Instance {
return;
}
if (ELEM(ob->type, OB_MESH, OB_POINTCLOUD)) {
if (object_state.sculpt_pbvh) {
sculpt_sync(manager, ob_ref, object_state);
}
else if (ELEM(ob->type, OB_MESH, OB_POINTCLOUD)) {
mesh_sync(manager, ob_ref, object_state);
}
else if (ob->type == OB_CURVES) {
@ -206,72 +212,65 @@ class Instance {
ResourceHandle handle = manager.resource_handle(ob_ref);
bool has_transparent_material = false;
if (object_state.sculpt_pbvh) {
#if 0 /* TODO(@pragma37): */
workbench_cache_sculpt_populate(wpd, ob, object_state.color_type);
#endif
if (object_state.use_per_material_batches) {
const int material_count = DRW_cache_object_material_count_get(ob_ref.object);
GPUBatch **batches;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batches = DRW_cache_mesh_surface_texpaint_get(ob_ref.object);
}
else {
batches = DRW_cache_object_surface_material_get(
ob_ref.object, get_dummy_gpu_materials(material_count), material_count);
}
if (batches) {
for (auto i : IndexRange(material_count)) {
if (batches[i] == nullptr) {
continue;
}
Material mat = get_material(ob_ref, object_state.color_type, i);
has_transparent_material = has_transparent_material || mat.is_transparent();
::Image *image = nullptr;
ImageUser *iuser = nullptr;
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
get_material_image(ob_ref.object, i + 1, image, iuser, sampler_state);
}
draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser);
}
}
}
else {
if (object_state.use_per_material_batches) {
const int material_count = DRW_cache_object_material_count_get(ob_ref.object);
GPUBatch **batches;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batches = DRW_cache_mesh_surface_texpaint_get(ob_ref.object);
GPUBatch *batch;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batch = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object);
}
else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) {
if (ob_ref.object->mode & OB_MODE_VERTEX_PAINT) {
batch = DRW_cache_mesh_surface_vertpaint_get(ob_ref.object);
}
else {
batches = DRW_cache_object_surface_material_get(
ob_ref.object, get_dummy_gpu_materials(material_count), material_count);
}
if (batches) {
for (auto i : IndexRange(material_count)) {
if (batches[i] == nullptr) {
continue;
}
Material mat = get_material(ob_ref, object_state.color_type, i);
has_transparent_material = has_transparent_material || mat.is_transparent();
::Image *image = nullptr;
ImageUser *iuser = nullptr;
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
get_material_image(ob_ref.object, i + 1, image, iuser, sampler_state);
}
draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser);
}
batch = DRW_cache_mesh_surface_sculptcolors_get(ob_ref.object);
}
}
else {
GPUBatch *batch;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batch = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object);
}
else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) {
if (ob_ref.object->mode & OB_MODE_VERTEX_PAINT) {
batch = DRW_cache_mesh_surface_vertpaint_get(ob_ref.object);
}
else {
batch = DRW_cache_mesh_surface_sculptcolors_get(ob_ref.object);
}
}
else {
batch = DRW_cache_object_surface_get(ob_ref.object);
}
batch = DRW_cache_object_surface_get(ob_ref.object);
}
if (batch) {
Material mat = get_material(ob_ref, object_state.color_type);
has_transparent_material = has_transparent_material || mat.is_transparent();
if (batch) {
Material mat = get_material(ob_ref, object_state.color_type);
has_transparent_material = has_transparent_material || mat.is_transparent();
draw_mesh(ob_ref,
mat,
batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
draw_mesh(ob_ref,
mat,
batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
}
@ -320,6 +319,54 @@ class Instance {
}
}
void sculpt_sync(Manager &manager, ObjectRef &ob_ref, const ObjectState &object_state)
{
/* Disable frustum culling for sculpt meshes. */
ResourceHandle handle = manager.resource_handle(float4x4(ob_ref.object->object_to_world));
if (object_state.use_per_material_batches) {
const int material_count = DRW_cache_object_material_count_get(ob_ref.object);
for (SculptBatch &batch : sculpt_batches_per_material_get(
ob_ref.object, {get_dummy_gpu_materials(material_count), material_count}))
{
Material mat = get_material(ob_ref, object_state.color_type, batch.material_slot);
if (SCULPT_DEBUG_DRAW) {
mat.base_color = batch.debug_color();
}
draw_mesh(ob_ref,
mat,
batch.batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
}
else {
Material mat = get_material(ob_ref, object_state.color_type);
SculptBatchFeature features = SCULPT_BATCH_DEFAULT;
if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) {
features = SCULPT_BATCH_VERTEX_COLOR;
}
else if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
features = SCULPT_BATCH_UV;
}
for (SculptBatch &batch : sculpt_batches_get(ob_ref.object, features)) {
if (SCULPT_DEBUG_DRAW) {
mat.base_color = batch.debug_color();
}
draw_mesh(ob_ref,
mat,
batch.batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
}
}
void draw(Manager &manager, GPUTexture *depth_tx, GPUTexture *color_tx)
{
view.sync(DRW_view_default_get());

View File

@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#include "draw_sculpt.hh"
#include "draw_attributes.hh"
#include "draw_pbvh.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "DRW_pbvh.hh"
namespace blender::draw {
float3 SculptBatch::debug_color()
{
static float3 colors[9] = {
{1.0f, 0.2f, 0.2f},
{0.2f, 1.0f, 0.2f},
{0.2f, 0.2f, 1.0f},
{1.0f, 1.0f, 0.2f},
{0.2f, 1.0f, 1.0f},
{1.0f, 0.2f, 1.0f},
{1.0f, 0.7f, 0.2f},
{0.2f, 1.0f, 0.7f},
{0.7f, 0.2f, 1.0f},
};
return colors[debug_index % 9];
}
struct SculptCallbackData {
bool use_wire;
bool fast_mode;
PBVHAttrReq *attrs;
int attrs_len;
Vector<SculptBatch> batches;
};
static void sculpt_draw_cb(SculptCallbackData *data,
PBVHBatches *batches,
PBVH_GPU_Args *pbvh_draw_args)
{
if (!batches) {
return;
}
SculptBatch batch = {};
int primcount;
if (data->use_wire) {
batch.batch = DRW_pbvh_lines_get(
batches, data->attrs, data->attrs_len, pbvh_draw_args, &primcount, data->fast_mode);
}
else {
batch.batch = DRW_pbvh_tris_get(
batches, data->attrs, data->attrs_len, pbvh_draw_args, &primcount, data->fast_mode);
}
batch.material_slot = drw_pbvh_material_index_get(batches);
batch.debug_index = data->batches.size();
pragma37 marked this conversation as resolved

These kind of comments are better left on the PR itself. They are not really useful once the initial code gets removed.

These kind of comments are better left on the PR itself. They are not really useful once the initial code gets removed.
data->batches.append(batch);
}
static Vector<SculptBatch> sculpt_batches_get_ex(
Object *ob, bool use_wire, bool use_materials, PBVHAttrReq *attrs, int attrs_len)
{
/* PBVH should always exist for non-empty meshes, created by depsgraph eval. */
PBVH *pbvh = ob->sculpt ? ob->sculpt->pbvh : nullptr;
if (!pbvh) {
return {};
}
/* TODO(Miguel Pozo): Don't use global context. */
const DRWContextState *drwctx = DRW_context_state_get();
RegionView3D *rv3d = drwctx->rv3d;
const bool navigating = rv3d && (rv3d->rflag & RV3D_NAVIGATING);
Paint *paint = nullptr;
if (drwctx->evil_C != nullptr) {
paint = BKE_paint_get_active_from_context(drwctx->evil_C);
}
/* Frustum planes to show only visible PBVH nodes. */
float4 draw_planes[6];
PBVHFrustumPlanes draw_frustum = {reinterpret_cast<float(*)[4]>(draw_planes), 6};
float4 update_planes[6];
PBVHFrustumPlanes update_frustum = {reinterpret_cast<float(*)[4]>(update_planes), 6};
/* TODO: take into account partial redraw for clipping planes. */
DRW_view_frustum_planes_get(DRW_view_default_get(), draw_frustum.planes);
/* Transform clipping planes to object space. Transforming a plane with a
* 4x4 matrix is done by multiplying with the transpose inverse.
* The inverse cancels out here since we transform by inverse(obmat). */
float4x4 tmat = math::transpose(float4x4(ob->object_to_world));
for (int i : IndexRange(6)) {
draw_planes[i] = tmat * draw_planes[i];
update_planes[i] = draw_planes[i];
}
if (paint && (paint->flags & PAINT_SCULPT_DELAY_UPDATES)) {
if (navigating) {
BKE_pbvh_get_frustum_planes(pbvh, &update_frustum);
}
else {
BKE_pbvh_set_frustum_planes(pbvh, &update_frustum);
}
}
/* Fast mode to show low poly multires while navigating. */
bool fast_mode = false;
if (paint && (paint->flags & PAINT_FAST_NAVIGATE)) {
fast_mode = navigating;
}
/* Update draw buffers only for visible nodes while painting.
* But do update them otherwise so navigating stays smooth. */
bool update_only_visible = rv3d && !(rv3d->rflag & RV3D_PAINTING);
if (paint && (paint->flags & PAINT_SCULPT_DELAY_UPDATES)) {
update_only_visible = true;
}
Mesh *mesh = static_cast<Mesh *>(ob->data);
BKE_pbvh_update_normals(pbvh, mesh->runtime->subdiv_ccg);
SculptCallbackData data;
data.use_wire = use_wire;
data.fast_mode = fast_mode;
data.attrs = attrs;
data.attrs_len = attrs_len;
BKE_pbvh_draw_cb(pbvh,
update_only_visible,
pragma37 marked this conversation as resolved

Use functional cast for all eCustomDataType.

Use functional cast for all `eCustomDataType`.

Use functional cast for all eCustomDataType.

Use functional cast for all `eCustomDataType`.
&update_frustum,
&draw_frustum,
(void (*)(void *, PBVHBatches *, PBVH_GPU_Args *))sculpt_draw_cb,
&data,
use_materials,
attrs,
attrs_len);
return data.batches;
}
Vector<SculptBatch> sculpt_batches_get(Object *ob, SculptBatchFeature features)
{
PBVHAttrReq attrs[16] = {0};
int attrs_len = 0;
/* NOTE: these are NOT #eCustomDataType, they are extended values, ASAN may warn about this. */
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_CO_TYPE);
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_NO_TYPE);
if (features & SCULPT_BATCH_MASK) {
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_MASK_TYPE);
}
if (features & SCULPT_BATCH_FACE_SET) {
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_FSET_TYPE);
}
Mesh *mesh = BKE_object_get_original_mesh(ob);
if (features & SCULPT_BATCH_VERTEX_COLOR) {
const CustomDataLayer *layer = BKE_id_attributes_color_find(&mesh->id,
mesh->active_color_attribute);
if (layer) {
attrs[attrs_len].type = eCustomDataType(layer->type);
fclem marked this conversation as resolved Outdated

The feature masking here is not obvious nor safe (bool could be different from 0/1). Prefer defining bool use_wire = features & SCULPT_BATCH_WIREFRAME != 0 and use as sculpt_batches_get_ex argument.

The feature masking here is not obvious nor safe (`bool` could be different from 0/1). Prefer defining `bool use_wire = features & SCULPT_BATCH_WIREFRAME != 0` and use as `sculpt_batches_get_ex` argument.

I personally find flags & mask more readable than (flags & mask) != 0.
This idiom is used all over the place, including the original functions from draw_mamager_data.
Not sure why this one triggers your spider sense. 🤔

Regarding safety, it's guaranteed by the standard to be true.
AFAIK, the binary representation of a true boolean is not guaranteed in any case, but that's only relevant for serialization.
In any case, all the compilers we support do "the right thing".
https://godbolt.org/z/xbv5e6hcd

I personally find `flags & mask` more readable than `(flags & mask) != 0`. This idiom is used all over the place, including the original functions from `draw_mamager_data`. Not sure why this one triggers your spider sense. 🤔 Regarding safety, it's guaranteed by the standard to be `true`. AFAIK, the binary representation of a `true` boolean is not guaranteed in any case, but that's only relevant for serialization. In any case, all the compilers we support do "the right thing". https://godbolt.org/z/xbv5e6hcd

Might have been some gidelines we had when code was mostly C. Disregard that then.

Might have been some gidelines we had when code was mostly C. Disregard that then.
attrs[attrs_len].domain = BKE_id_attribute_domain(&mesh->id, layer);
STRNCPY(attrs[attrs_len].name, layer->name);
attrs_len++;
}
}
if (features & SCULPT_BATCH_UV) {
int layer_i = CustomData_get_active_layer_index(&mesh->ldata, CD_PROP_FLOAT2);
if (layer_i != -1) {
CustomDataLayer *layer = mesh->ldata.layers + layer_i;
attrs[attrs_len].type = CD_PROP_FLOAT2;
attrs[attrs_len].domain = ATTR_DOMAIN_CORNER;
STRNCPY(attrs[attrs_len].name, layer->name);
attrs_len++;
}
}
return sculpt_batches_get_ex(ob, features & SCULPT_BATCH_WIREFRAME, false, attrs, attrs_len);
}
Vector<SculptBatch> sculpt_batches_per_material_get(Object *ob,
MutableSpan<GPUMaterial *> materials)
{
BLI_assert(ob->type == OB_MESH);
Mesh *mesh = (Mesh *)ob->data;
DRW_Attributes draw_attrs;
DRW_MeshCDMask cd_needed;
DRW_mesh_get_attributes(ob, mesh, materials.data(), materials.size(), &draw_attrs, &cd_needed);
PBVHAttrReq attrs[16] = {0};
int attrs_len = 0;
/* NOTE: these are NOT #eCustomDataType, they are extended values, ASAN may warn about this. */
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_CO_TYPE);
attrs[attrs_len++].type = eCustomDataType(CD_PBVH_NO_TYPE);
for (int i = 0; i < draw_attrs.num_requests; i++) {
DRW_AttributeRequest *req = draw_attrs.requests + i;
attrs[attrs_len].type = req->cd_type;
attrs[attrs_len].domain = req->domain;
STRNCPY(attrs[attrs_len].name, req->attribute_name);
attrs_len++;
}
/* UV maps are not in attribute requests. */
for (uint i = 0; i < 32; i++) {
if (cd_needed.uv & (1 << i)) {
int layer_i = CustomData_get_layer_index_n(&mesh->ldata, CD_PROP_FLOAT2, i);
CustomDataLayer *layer = layer_i != -1 ? mesh->ldata.layers + layer_i : nullptr;
if (layer) {
attrs[attrs_len].type = CD_PROP_FLOAT2;
attrs[attrs_len].domain = ATTR_DOMAIN_CORNER;
STRNCPY(attrs[attrs_len].name, layer->name);
attrs_len++;
}
}
}
return sculpt_batches_get_ex(ob, false, true, attrs, attrs_len);
}
} // namespace blender::draw

View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "draw_manager.hh"
namespace blender::draw {
#define SCULPT_DEBUG_DRAW (G.debug_value == 889)
struct SculptBatch {
GPUBatch *batch;
int material_slot;
int debug_index;
float3 debug_color();
};
enum SculptBatchFeature {
pragma37 marked this conversation as resolved

Avoid contraction. SCULPT_BATCH_FACE_SET.

Avoid contraction. SCULPT_BATCH_FACE_SET.
SCULPT_BATCH_DEFAULT = 0,
SCULPT_BATCH_WIREFRAME = 1 << 0,
SCULPT_BATCH_MASK = 1 << 1,
SCULPT_BATCH_FACE_SET = 1 << 2,
SCULPT_BATCH_VERTEX_COLOR = 1 << 3,
SCULPT_BATCH_UV = 1 << 4
};
ENUM_OPERATORS(SculptBatchFeature, SCULPT_BATCH_UV);
Vector<SculptBatch> sculpt_batches_get(Object *ob, SculptBatchFeature features);
Vector<SculptBatch> sculpt_batches_per_material_get(Object *ob,
MutableSpan<GPUMaterial *> materials);
} // namespace blender::draw