diff options
-rw-r--r-- | source/blender/blenkernel/BKE_volume_render.h | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/volume_render.cc | 452 | ||||
-rw-r--r-- | source/blender/draw/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_engine.c | 6 | ||||
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_outline.c | 16 | ||||
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_private.h | 6 | ||||
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_volume.c | 67 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache.c | 6 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache.h | 1 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl.h | 1 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_cache_impl_volume.c | 47 |
11 files changed, 455 insertions, 158 deletions
diff --git a/source/blender/blenkernel/BKE_volume_render.h b/source/blender/blenkernel/BKE_volume_render.h index a42f24a5312..593f296135c 100644 --- a/source/blender/blenkernel/BKE_volume_render.h +++ b/source/blender/blenkernel/BKE_volume_render.h @@ -58,6 +58,16 @@ void BKE_volume_grid_wireframe(const struct Volume *volume, BKE_volume_wireframe_cb cb, void *cb_userdata); +/* Selection Surface */ + +typedef void (*BKE_volume_selection_surface_cb)( + void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris); + +void BKE_volume_grid_selection_surface(const struct Volume *volume, + struct VolumeGrid *volume_grid, + BKE_volume_selection_surface_cb cb, + void *cb_userdata); + /* Render */ float BKE_volume_density_scale(const struct Volume *volume, const float matrix[4][4]); diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc index 98d3617c822..b773452b6a8 100644 --- a/source/blender/blenkernel/intern/volume_render.cc +++ b/source/blender/blenkernel/intern/volume_render.cc @@ -20,8 +20,11 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" +#include "BLI_float3.hh" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" +#include "BLI_vector.hh" #include "DNA_volume_types.h" @@ -166,107 +169,223 @@ void BKE_volume_grid_dense_voxels(const Volume *volume, /* Wireframe */ #ifdef WITH_OPENVDB -struct VolumeWireframe { - std::vector<openvdb::Vec3f> verts; - std::vector<openvdb::Vec2I> edges; - - template<typename GridType> - void add_grid(openvdb::GridBase::ConstPtr gridbase, const bool points, const bool coarse) - { - using TreeType = typename GridType::TreeType; - using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType; - using NodeCIter = typename TreeType::NodeCIter; - using GridConstPtr = typename GridType::ConstPtr; - - GridConstPtr grid = openvdb::gridConstPtrCast<GridType>(gridbase); - const openvdb::math::Transform &transform = grid->transform(); - const int depth = (coarse) ? 2 : 3; - - NodeCIter iter = grid->tree().cbeginNode(); - iter.setMaxDepth(depth); - - for (; iter; ++iter) { - if (iter.getDepth() == depth) { - openvdb::CoordBBox coordbbox; - - if (depth == 2) { - /* Internal node at depth 2. */ - const Depth2Type *node = nullptr; - iter.getNode(node); - if (node) { - node->evalActiveBoundingBox(coordbbox, false); - } - else { - continue; - } - } - else { - /* Leaf node. */ - if (!iter.getBoundingBox(coordbbox)) { - continue; - } - } - - /* +1 to convert from exclusive to include bounds. */ - coordbbox.max() = coordbbox.max().offsetBy(1); - openvdb::BBoxd bbox = transform.indexToWorld(coordbbox); - - if (points) { - add_point(bbox); - } - else { - add_box(bbox); - } + +/** Returns bounding boxes that approximate the shape of the volume stored in the grid. */ +template<typename GridType> +static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(openvdb::GridBase::ConstPtr gridbase, + const bool coarse) +{ + using TreeType = typename GridType::TreeType; + using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType; + using NodeCIter = typename TreeType::NodeCIter; + using GridConstPtr = typename GridType::ConstPtr; + + GridConstPtr grid = openvdb::gridConstPtrCast<GridType>(gridbase); + blender::Vector<openvdb::CoordBBox> boxes; + const int depth = coarse ? 2 : 3; + + NodeCIter iter = grid->tree().cbeginNode(); + iter.setMaxDepth(depth); + + for (; iter; ++iter) { + if (iter.getDepth() != depth) { + continue; + } + + openvdb::CoordBBox box; + if (depth == 2) { + /* Internal node at depth 2. */ + const Depth2Type *node = nullptr; + iter.getNode(node); + if (node) { + node->evalActiveBoundingBox(box, false); } + else { + continue; + } + } + else { + /* Leaf node. */ + if (!iter.getBoundingBox(box)) { + continue; + } + } + + /* +1 to convert from exclusive to inclusive bounds. */ + box.max() = box.max().offsetBy(1); + + boxes.append(box); + } + + return boxes; +} + +static blender::Vector<openvdb::CoordBBox> get_bounding_boxes(VolumeGridType grid_type, + openvdb::GridBase::ConstPtr grid, + const bool coarse) +{ + switch (grid_type) { + case VOLUME_GRID_BOOLEAN: { + return get_bounding_boxes<openvdb::BoolGrid>(grid, coarse); + break; + } + case VOLUME_GRID_FLOAT: { + return get_bounding_boxes<openvdb::FloatGrid>(grid, coarse); + break; + } + case VOLUME_GRID_DOUBLE: { + return get_bounding_boxes<openvdb::DoubleGrid>(grid, coarse); + break; + } + case VOLUME_GRID_INT: { + return get_bounding_boxes<openvdb::Int32Grid>(grid, coarse); + break; + } + case VOLUME_GRID_INT64: { + return get_bounding_boxes<openvdb::Int64Grid>(grid, coarse); + break; + } + case VOLUME_GRID_MASK: { + return get_bounding_boxes<openvdb::MaskGrid>(grid, coarse); + break; + } + case VOLUME_GRID_VECTOR_FLOAT: { + return get_bounding_boxes<openvdb::Vec3fGrid>(grid, coarse); + break; + } + case VOLUME_GRID_VECTOR_DOUBLE: { + return get_bounding_boxes<openvdb::Vec3dGrid>(grid, coarse); + break; + } + case VOLUME_GRID_VECTOR_INT: { + return get_bounding_boxes<openvdb::Vec3IGrid>(grid, coarse); + break; + } + case VOLUME_GRID_STRING: { + return get_bounding_boxes<openvdb::StringGrid>(grid, coarse); + break; + } + case VOLUME_GRID_POINTS: + case VOLUME_GRID_UNKNOWN: { + break; + } + } + return {}; +} + +static void boxes_to_center_points(blender::Span<openvdb::CoordBBox> boxes, + const openvdb::math::Transform &transform, + blender::MutableSpan<blender::float3> r_verts) +{ + BLI_assert(boxes.size() == r_verts.size()); + for (const int i : boxes.index_range()) { + openvdb::Vec3d center = transform.indexToWorld(boxes[i].getCenter()); + r_verts[i] = blender::float3(center[0], center[1], center[2]); + } +} + +static void boxes_to_corner_points(blender::Span<openvdb::CoordBBox> boxes, + const openvdb::math::Transform &transform, + blender::MutableSpan<blender::float3> r_verts) +{ + BLI_assert(boxes.size() * 8 == r_verts.size()); + for (const int i : boxes.index_range()) { + const openvdb::CoordBBox &box = boxes[i]; + + /* The ordering of the corner points is lexicographic. */ + std::array<openvdb::Coord, 8> corners; + box.getCornerPoints(corners.data()); + + for (int j = 0; j < 8; j++) { + openvdb::Coord corner_i = corners[j]; + openvdb::Vec3d corner_d = transform.indexToWorld(corner_i); + r_verts[8 * i + j] = blender::float3(corner_d[0], corner_d[1], corner_d[2]); } } +} - void add_point(const openvdb::BBoxd &bbox) - { - verts.push_back(bbox.getCenter()); +static void boxes_to_edge_mesh(blender::Span<openvdb::CoordBBox> boxes, + const openvdb::math::Transform &transform, + blender::Vector<blender::float3> &r_verts, + blender::Vector<std::array<int, 2>> &r_edges) +{ + /* TODO: Deduplicate edges, hide flat edges? */ + + const int box_edges[12][2] = { + {0, 1}, + {0, 2}, + {0, 4}, + {1, 3}, + {1, 5}, + {2, 3}, + {2, 6}, + {3, 7}, + {4, 5}, + {4, 6}, + {5, 7}, + {6, 7}, + }; + + int vert_offset = r_verts.size(); + int edge_offset = r_edges.size(); + + const int vert_amount = 8 * boxes.size(); + const int edge_amount = 12 * boxes.size(); + + r_verts.resize(r_verts.size() + vert_amount); + r_edges.resize(r_edges.size() + edge_amount); + boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount)); + + for (int i = 0; i < boxes.size(); i++) { + for (int j = 0; j < 12; j++) { + r_edges[edge_offset + j] = {vert_offset + box_edges[j][0], vert_offset + box_edges[j][1]}; + } + vert_offset += 8; + edge_offset += 12; } +} - void add_box(const openvdb::BBoxd &bbox) - { - /* TODO: deduplicate edges, hide flat edges? */ - openvdb::Vec3f min = bbox.min(); - openvdb::Vec3f max = bbox.max(); - - const int vert_offset = verts.size(); - const int edge_offset = edges.size(); - - /* Create vertices. */ - verts.resize(vert_offset + 8); - verts[vert_offset + 0] = openvdb::Vec3f(min[0], min[1], min[2]); - verts[vert_offset + 1] = openvdb::Vec3f(max[0], min[1], min[2]); - verts[vert_offset + 2] = openvdb::Vec3f(max[0], max[1], min[2]); - verts[vert_offset + 3] = openvdb::Vec3f(min[0], max[1], min[2]); - verts[vert_offset + 4] = openvdb::Vec3f(min[0], min[1], max[2]); - verts[vert_offset + 5] = openvdb::Vec3f(max[0], min[1], max[2]); - verts[vert_offset + 6] = openvdb::Vec3f(max[0], max[1], max[2]); - verts[vert_offset + 7] = openvdb::Vec3f(min[0], max[1], max[2]); - - /* Create edges. */ - const int box_edges[12][2] = {{0, 1}, - {1, 2}, - {2, 3}, - {3, 0}, - {4, 5}, - {5, 6}, - {6, 7}, - {7, 4}, - {0, 4}, - {1, 5}, - {2, 6}, - {3, 7}}; - - edges.resize(edge_offset + 12); - for (int i = 0; i < 12; i++) { - edges[edge_offset + i] = openvdb::Vec2I(vert_offset + box_edges[i][0], - vert_offset + box_edges[i][1]); +static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes, + const openvdb::math::Transform &transform, + blender::Vector<blender::float3> &r_verts, + blender::Vector<std::array<int, 3>> &r_tris) +{ + const int box_tris[12][3] = { + {0, 1, 4}, + {4, 1, 5}, + {0, 2, 1}, + {1, 2, 3}, + {1, 3, 5}, + {5, 3, 7}, + {6, 4, 5}, + {7, 5, 6}, + {2, 0, 4}, + {2, 4, 6}, + {3, 7, 2}, + {6, 2, 7}, + }; + + int vert_offset = r_verts.size(); + int tri_offset = r_tris.size(); + + const int vert_amount = 8 * boxes.size(); + const int tri_amount = 12 * boxes.size(); + + r_verts.resize(r_verts.size() + vert_amount); + r_tris.resize(r_tris.size() + tri_amount); + boxes_to_corner_points(boxes, transform, r_verts.as_mutable_span().take_back(vert_amount)); + + for (int i = 0; i < boxes.size(); i++) { + for (int j = 0; j < 12; j++) { + r_tris[tri_offset + j] = {vert_offset + box_tris[j][0], + vert_offset + box_tris[j][1], + vert_offset + box_tris[j][2]}; } + vert_offset += 8; + tri_offset += 12; } -}; +} + #endif void BKE_volume_grid_wireframe(const Volume *volume, @@ -274,79 +393,96 @@ void BKE_volume_grid_wireframe(const Volume *volume, BKE_volume_wireframe_cb cb, void *cb_userdata) { -#ifdef WITH_OPENVDB - VolumeWireframe wireframe; - if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) { - /* Nothing. */ + cb(cb_userdata, NULL, NULL, 0, 0); + return; } - else if (volume->display.wireframe_type == VOLUME_WIREFRAME_BOUNDS) { - /* Bounding box. */ - float min[3], max[3]; - BKE_volume_grid_bounds(volume_grid, min, max); - openvdb::BBoxd bbox(min, max); - wireframe.add_box(bbox); +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + + if (volume->display.wireframe_type == VOLUME_WIREFRAME_BOUNDS) { + /* Bounding box. */ + openvdb::CoordBBox box; + blender::Vector<blender::float3> verts; + blender::Vector<std::array<int, 2>> edges; + if (grid->baseTree().evalLeafBoundingBox(box)) { + boxes_to_edge_mesh({box}, grid->transform(), verts, edges); + } + cb(cb_userdata, + (float(*)[3])verts.data(), + (int(*)[2])edges.data(), + verts.size(), + edges.size()); } else { - /* Tree nodes. */ - openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - const bool points = (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS); - const bool coarse = (volume->display.wireframe_detail == VOLUME_WIREFRAME_COARSE); - - switch (BKE_volume_grid_type(volume_grid)) { - case VOLUME_GRID_BOOLEAN: { - wireframe.add_grid<openvdb::BoolGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_FLOAT: { - wireframe.add_grid<openvdb::FloatGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_DOUBLE: { - wireframe.add_grid<openvdb::DoubleGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_INT: { - wireframe.add_grid<openvdb::Int32Grid>(grid, points, coarse); - break; - } - case VOLUME_GRID_INT64: { - wireframe.add_grid<openvdb::Int64Grid>(grid, points, coarse); - break; - } - case VOLUME_GRID_MASK: { - wireframe.add_grid<openvdb::MaskGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_VECTOR_FLOAT: { - wireframe.add_grid<openvdb::Vec3fGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_VECTOR_DOUBLE: { - wireframe.add_grid<openvdb::Vec3dGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_VECTOR_INT: { - wireframe.add_grid<openvdb::Vec3IGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_STRING: { - wireframe.add_grid<openvdb::StringGrid>(grid, points, coarse); - break; - } - case VOLUME_GRID_POINTS: - case VOLUME_GRID_UNKNOWN: { - break; - } + blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes( + BKE_volume_grid_type(volume_grid), + grid, + volume->display.wireframe_detail == VOLUME_WIREFRAME_COARSE); + + blender::Vector<blender::float3> verts; + blender::Vector<std::array<int, 2>> edges; + + if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) { + verts.resize(boxes.size()); + boxes_to_center_points(boxes, grid->transform(), verts); } + else { + boxes_to_edge_mesh(boxes, grid->transform(), verts, edges); + } + + cb(cb_userdata, + (float(*)[3])verts.data(), + (int(*)[2])edges.data(), + verts.size(), + edges.size()); + } + +#else + UNUSED_VARS(volume, volume_grid); + cb(cb_userdata, NULL, NULL, 0, 0); +#endif +} + +static void grow_triangles(blender::MutableSpan<blender::float3> verts, + blender::Span<std::array<int, 3>> tris, + const float factor) +{ + /* Compute the offset for every vertex based on the connected edges. + * This formula simply tries increases the length of all edges. */ + blender::Array<blender::float3> offsets(verts.size(), {0, 0, 0}); + for (const std::array<int, 3> &tri : tris) { + offsets[tri[0]] += factor * (2 * verts[tri[0]] - verts[tri[1]] - verts[tri[2]]); + offsets[tri[1]] += factor * (2 * verts[tri[1]] - verts[tri[0]] - verts[tri[2]]); + offsets[tri[2]] += factor * (2 * verts[tri[2]] - verts[tri[0]] - verts[tri[1]]); + } + /* Apply the computed offsets. */ + for (const int i : verts.index_range()) { + verts[i] += offsets[i]; } +} + +void BKE_volume_grid_selection_surface(const Volume *volume, + VolumeGrid *volume_grid, + BKE_volume_selection_surface_cb cb, + void *cb_userdata) +{ +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + blender::Vector<openvdb::CoordBBox> boxes = get_bounding_boxes( + BKE_volume_grid_type(volume_grid), grid, true); + + blender::Vector<blender::float3> verts; + blender::Vector<std::array<int, 3>> tris; + boxes_to_cube_mesh(boxes, grid->transform(), verts, tris); + + /* By slightly scaling the individual boxes up, we can avoid some artifacts when drawing the + * selection outline. */ + const float offset_factor = 0.01f; + grow_triangles(verts, tris, offset_factor); - cb(cb_userdata, - (float(*)[3])wireframe.verts.data(), - (int(*)[2])wireframe.edges.data(), - wireframe.verts.size(), - wireframe.edges.size()); + cb(cb_userdata, (float(*)[3])verts.data(), (int(*)[3])tris.data(), verts.size(), tris.size()); #else UNUSED_VARS(volume, volume_grid); cb(cb_userdata, NULL, NULL, 0, 0); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 11aea544c65..4e72e89ae99 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC engines/overlay/overlay_particle.c engines/overlay/overlay_sculpt.c engines/overlay/overlay_shader.c + engines/overlay/overlay_volume.c engines/overlay/overlay_wireframe.c DRW_engine.h diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index fb235a1ff57..5a87af7238e 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -207,6 +207,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_outline_cache_init(vedata); OVERLAY_particle_cache_init(vedata); OVERLAY_wireframe_cache_init(vedata); + OVERLAY_volume_cache_init(vedata); } BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bool *do_init) @@ -355,6 +356,10 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_pose_cache_populate(vedata, ob); } + if (ob->type == OB_VOLUME) { + OVERLAY_volume_cache_populate(vedata, ob); + } + if (in_edit_mode && !pd->hide_overlays) { switch (ob->type) { case OB_MESH: @@ -551,6 +556,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_particle_draw(vedata); OVERLAY_metaball_draw(vedata); OVERLAY_gpencil_draw(vedata); + OVERLAY_volume_draw(vedata); OVERLAY_extra_draw(vedata); if (DRW_state_is_fbo()) { diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c index e904066248f..f1467ff9794 100644 --- a/source/blender/draw/engines/overlay/overlay_outline.c +++ b/source/blender/draw/engines/overlay/overlay_outline.c @@ -272,6 +272,17 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob) pd->cfra); } +static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob) +{ + struct GPUBatch *geom = DRW_cache_volume_selection_surface_get(ob); + if (geom == NULL) { + return; + } + + DRWShadingGroup *shgroup = pd->outlines_grp; + DRW_shgroup_call(shgroup, geom, ob); +} + void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, Object *ob, OVERLAY_DupliData *dupli, @@ -293,6 +304,11 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, return; } + if (ob->type == OB_VOLUME) { + OVERLAY_outline_volume(pd, ob); + return; + } + if (ob->type == OB_POINTCLOUD && pd->wireframe_mode) { /* Looks bad in this case. Could be relaxed if we draw a * wireframe of some sort in the future. */ diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 2cf2ca34801..ada2c2c8d1f 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -119,6 +119,7 @@ typedef struct OVERLAY_PassList { DRWPass *particle_ps; DRWPass *pointcloud_ps; DRWPass *sculpt_mask_ps; + DRWPass *volume_ps; DRWPass *wireframe_ps; DRWPass *wireframe_xray_ps; DRWPass *xray_fade_ps; @@ -285,6 +286,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *particle_shapes_grp; DRWShadingGroup *pointcloud_dots_grp; DRWShadingGroup *sculpt_mask_grp; + DRWShadingGroup *volume_selection_surface_grp; DRWShadingGroup *wires_grp[2][2]; /* With and without coloring. */ DRWShadingGroup *wires_all_grp[2][2]; /* With and without coloring. */ DRWShadingGroup *wires_hair_grp[2][2]; /* With and without coloring. */ @@ -511,6 +513,10 @@ void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata); void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_edit_text_draw(OVERLAY_Data *vedata); +void OVERLAY_volume_cache_init(OVERLAY_Data *vedata); +void OVERLAY_volume_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_volume_draw(OVERLAY_Data *vedata); + void OVERLAY_edit_mesh_init(OVERLAY_Data *vedata); void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata); void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob); diff --git a/source/blender/draw/engines/overlay/overlay_volume.c b/source/blender/draw/engines/overlay/overlay_volume.c new file mode 100644 index 00000000000..ffa664c90d5 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_volume.c @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DNA_volume_types.h" + +#include "DRW_render.h" +#include "GPU_shader.h" + +#include "overlay_private.h" + +void OVERLAY_volume_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const bool is_select = DRW_state_is_select(); + + if (is_select) { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->volume_ps, state | pd->clipping_state); + GPUShader *sh = OVERLAY_shader_depth_only(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->volume_ps); + pd->volume_selection_surface_grp = grp; + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } +} + +void OVERLAY_volume_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const bool is_select = DRW_state_is_select(); + + if (is_select) { + struct GPUBatch *geom = DRW_cache_volume_selection_surface_get(ob); + if (geom != NULL) { + DRW_shgroup_call(pd->volume_selection_surface_grp, geom, ob); + } + } +} + +void OVERLAY_volume_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_FramebufferList *fbl = vedata->fbl; + + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(fbl->overlay_default_fb); + } + + DRW_draw_pass(psl->volume_ps); +} diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 0fbc9e2ed82..52e7e91a995 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -3279,6 +3279,12 @@ GPUBatch *DRW_cache_volume_face_wireframe_get(Object *ob) return DRW_volume_batch_cache_get_wireframes_face(ob->data); } +GPUBatch *DRW_cache_volume_selection_surface_get(Object *ob) +{ + BLI_assert(ob->type == OB_VOLUME); + return DRW_volume_batch_cache_get_selection_surface(ob->data); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 7164a477d8a..5da9f4b7964 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -236,6 +236,7 @@ typedef struct DRWVolumeGrid { DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume, struct VolumeGrid *grid); struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob); +struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob); /* GPencil */ struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index 9e7a2e2916c..e5cc18f6e09 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -152,6 +152,7 @@ struct GPUBatch **DRW_cache_pointcloud_surface_shaded_get(struct Object *ob, int DRW_volume_material_count_get(struct Volume *volume); struct GPUBatch *DRW_volume_batch_cache_get_wireframes_face(struct Volume *volume); +struct GPUBatch *DRW_volume_batch_cache_get_selection_surface(struct Volume *volume); /* Mesh */ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c index e39c4976e38..7b03070e32b 100644 --- a/source/blender/draw/intern/draw_cache_impl_volume.c +++ b/source/blender/draw/intern/draw_cache_impl_volume.c @@ -62,6 +62,9 @@ typedef struct VolumeBatchCache { GPUBatch *batch; } face_wire; + /* Surface for selection */ + GPUBatch *selection_surface; + /* settings to determine if cache is invalid */ bool is_dirty; } VolumeBatchCache; @@ -132,6 +135,7 @@ static void volume_batch_cache_clear(Volume *volume) GPU_VERTBUF_DISCARD_SAFE(cache->face_wire.pos_nor_in_order); GPU_BATCH_DISCARD_SAFE(cache->face_wire.batch); + GPU_BATCH_DISCARD_SAFE(cache->selection_surface); } void DRW_volume_batch_cache_free(Volume *volume) @@ -209,6 +213,49 @@ GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume) return cache->face_wire.batch; } +static void drw_volume_selection_surface_cb( + void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris) +{ + Volume *volume = userdata; + VolumeBatchCache *cache = volume->batch_cache; + + static GPUVertFormat format = {0}; + static uint pos_id; + if (format.attr_len == 0) { + pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + /* Create vertex buffer. */ + GPUVertBuf *vbo_surface = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo_surface, totvert); + GPU_vertbuf_attr_fill(vbo_surface, pos_id, verts); + + /* Create index buffer. */ + GPUIndexBufBuilder elb; + GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tottris, totvert); + for (int i = 0; i < tottris; i++) { + GPU_indexbuf_add_tri_verts(&elb, UNPACK3(tris[i])); + } + GPUIndexBuf *ibo_surface = GPU_indexbuf_build(&elb); + + cache->selection_surface = GPU_batch_create_ex( + GPU_PRIM_TRIS, vbo_surface, ibo_surface, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); +} + +GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume) +{ + VolumeBatchCache *cache = volume_batch_cache_get(volume); + if (cache->selection_surface == NULL) { + VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume); + if (volume_grid == NULL) { + return NULL; + } + BKE_volume_grid_selection_surface( + volume, volume_grid, drw_volume_selection_surface_cb, volume); + } + return cache->selection_surface; +} + static DRWVolumeGrid *volume_grid_cache_get(Volume *volume, VolumeGrid *grid, VolumeBatchCache *cache) |