GPv3: Draw Tool: Outline setting

This ports the outline setting from GPv2 to GPv3.

Internally, the same code as for the outline modifier is used.

Pull Request: https://projects.blender.org/blender/blender/pulls/123440
This commit is contained in:
Falk David
2024-06-20 12:07:24 +02:00
committed by Falk David
parent 4353b7ffba
commit 6daf139694
3 changed files with 143 additions and 21 deletions

View File

@@ -163,36 +163,37 @@ void DrawingPlacement::set_origin_to_nearest_stroke(const float2 co)
plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
}
float3 DrawingPlacement::project_depth(const float2 co) const
{
float3 proj_point;
float depth;
if (depth_cache_ != nullptr && ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth)) {
ED_view3d_depth_unproject_v3(region_, int2(co), depth, proj_point);
float3 normal;
ED_view3d_depth_read_cached_normal(region_, depth_cache_, int2(co), normal);
proj_point += normal * surface_offset_;
}
else {
/* Fallback to `View` placement. */
ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
}
return proj_point;
}
float3 DrawingPlacement::project(const float2 co) const
{
float3 proj_point;
if (depth_ == DrawingPlacementDepth::Surface) {
/* Project using the viewport depth cache. */
float depth;
if (depth_cache_ != nullptr && ED_view3d_depth_read_cached(depth_cache_, int2(co), 4, &depth))
{
ED_view3d_depth_unproject_v3(region_, int2(co), depth, proj_point);
float3 normal;
ED_view3d_depth_read_cached_normal(region_, depth_cache_, int2(co), normal);
proj_point += normal * surface_offset_;
}
else {
/* Fallback to `View` placement. */
ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
}
proj_point = this->project_depth(co);
}
else {
if (ELEM(plane_,
DrawingPlacementPlane::Front,
DrawingPlacementPlane::Side,
DrawingPlacementPlane::Top,
DrawingPlacementPlane::Cursor))
{
ED_view3d_win_to_3d_on_plane(region_, placement_plane_, co, false, proj_point);
}
else if (plane_ == DrawingPlacementPlane::View) {
if (plane_ == DrawingPlacementPlane::View) {
ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
}
else {
ED_view3d_win_to_3d_on_plane(region_, placement_plane_, co, false, proj_point);
}
}
return math::transform_point(world_space_to_layer_space_, proj_point);
}
@@ -206,6 +207,55 @@ void DrawingPlacement::project(const Span<float2> src, MutableSpan<float3> dst)
});
}
float3 DrawingPlacement::reproject(const float3 pos) const
{
float3 proj_point;
if (depth_ == DrawingPlacementDepth::Surface) {
/* First project the position into view space. */
float2 co;
if (ED_view3d_project_float_global(region_,
math::transform_point(layer_space_to_world_space_, pos),
co,
V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK)
{
/* Can't reproject the point. */
return pos;
}
/* Project using the viewport depth cache. */
proj_point = this->project_depth(co);
}
else {
if (plane_ != DrawingPlacementPlane::View) {
/* Reproject the point onto the `placement_plane_` from the current view. */
RegionView3D *rv3d = static_cast<RegionView3D *>(region_->regiondata);
float3 ray_co, ray_no;
if (rv3d->is_persp) {
ray_co = float3(rv3d->viewinv[3]);
ray_no = math::normalize(ray_co - math::transform_point(layer_space_to_world_space_, pos));
}
else {
ray_co = math::transform_point(layer_space_to_world_space_, pos);
ray_no = -float3(rv3d->viewinv[2]);
}
float lambda;
if (isect_ray_plane_v3(ray_co, ray_no, placement_plane_, &lambda, false)) {
proj_point = ray_co + ray_no * lambda;
}
}
}
return math::transform_point(world_space_to_layer_space_, proj_point);
}
void DrawingPlacement::reproject(const Span<float3> src, MutableSpan<float3> dst) const
{
threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
dst[i] = this->reproject(src[i]);
}
});
}
float4x4 DrawingPlacement::to_world_space() const
{
return layer_space_to_world_space_;

View File

@@ -119,7 +119,16 @@ class DrawingPlacement {
float3 project(float2 co) const;
void project(Span<float2> src, MutableSpan<float3> dst) const;
/**
* Projects a 3D position (in local space) to the drawing plane.
*/
float3 reproject(float3 pos) const;
void reproject(Span<float3> src, MutableSpan<float3> dst) const;
float4x4 to_world_space() const;
private:
float3 project_depth(float2 co) const;
};
void set_selected_frames_type(bke::greasepencil::Layer &layer,

View File

@@ -7,6 +7,7 @@
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_material.h"
#include "BKE_paint.hh"
@@ -26,6 +27,7 @@
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "GEO_join_geometries.hh"
#include "GEO_simplify_curves.hh"
#include "GEO_smooth_curves.hh"
@@ -822,6 +824,46 @@ static void simplify_stroke(bke::greasepencil::Drawing &drawing,
}
}
static void outline_stroke(bke::greasepencil::Drawing &drawing,
const int active_curve,
const float4x4 &viewmat,
const ed::greasepencil::DrawingPlacement &placement,
const float outline_radius,
const int material_index,
const bool on_back)
{
/* Get the outline stroke (single curve). */
bke::CurvesGeometry outline = ed::greasepencil::create_curves_outline(
drawing,
IndexRange::from_single(active_curve),
viewmat,
3,
outline_radius,
0.0f,
material_index);
/* Reproject the outline onto the drawing placement. */
placement.reproject(outline.positions(), outline.positions_for_write());
/* Remove the original stroke. */
drawing.strokes_for_write().remove_curves(IndexRange::from_single(active_curve), {});
/* Join the outline stroke into the drawing. */
Curves *outline_curve = bke::curves_new_nomain(std::move(outline));
Curves *other_curves = bke::curves_new_nomain(std::move(drawing.strokes_for_write()));
std::array<bke::GeometrySet, 2> geometry_sets;
if (on_back) {
geometry_sets = {bke::GeometrySet::from_curves(outline_curve),
bke::GeometrySet::from_curves(other_curves)};
}
else {
geometry_sets = {bke::GeometrySet::from_curves(other_curves),
bke::GeometrySet::from_curves(outline_curve)};
}
drawing.strokes_for_write() = std::move(
geometry::join_geometries(geometry_sets, {}).get_curves_for_write()->geometry.wrap());
}
static int trim_end_points(bke::greasepencil::Drawing &drawing,
const float epsilon,
const bool on_back,
@@ -918,6 +960,7 @@ void PaintOperation::on_stroke_done(const bContext &C)
using namespace blender::bke;
Scene *scene = CTX_data_scene(&C);
Object *object = CTX_data_active_object(&C);
RegionView3D *rv3d = CTX_wm_region_view3d(&C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
Paint *paint = &scene->toolsettings->gp_paint->paint;
@@ -948,6 +991,26 @@ void PaintOperation::on_stroke_done(const bContext &C)
settings->simplify_px,
active_curve);
}
if ((settings->flag & GP_BRUSH_OUTLINE_STROKE) != 0) {
const float outline_radius = float(brush->unprojected_radius) * settings->outline_fac * 0.5f;
const int material_index = [&]() {
Material *material = BKE_grease_pencil_object_material_ensure_from_active_input_brush(
CTX_data_main(&C), object, brush);
const int active_index = BKE_object_material_index_get(object, material);
if (settings->material_alt == nullptr) {
return active_index;
}
const int alt_index = BKE_object_material_slot_find_index(object, settings->material_alt);
return (alt_index > -1) ? alt_index - 1 : active_index;
}();
outline_stroke(drawing,
active_curve,
float4x4(rv3d->viewmat),
placement_,
outline_radius,
material_index,
on_back);
}
}
drawing.set_texture_matrices({texture_space_}, IndexRange::from_single(active_curve));
drawing.tag_topology_changed();