diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 5e99debeb2f..e9a91e473c0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -294,6 +294,7 @@ set(SRC engines/overlay/overlay_next_bounds.hh engines/overlay/overlay_next_empty.hh engines/overlay/overlay_next_camera.hh + engines/overlay/overlay_next_force_field.hh engines/overlay/overlay_next_grid.hh engines/overlay/overlay_next_instance.hh engines/overlay/overlay_next_lattice.hh diff --git a/source/blender/draw/engines/overlay/overlay_next_force_field.hh b/source/blender/draw/engines/overlay/overlay_next_force_field.hh new file mode 100644 index 00000000000..68e2e02454a --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_next_force_field.hh @@ -0,0 +1,169 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup overlay + */ + +#pragma once + +#include "BKE_anim_path.h" + +#include "BLI_math_rotation.h" + +#include "DNA_object_force_types.h" + +#include "overlay_next_private.hh" + +namespace blender::draw::overlay { + +class ForceFields { + using ForceFieldsInstanceBuf = ShapeInstanceBuf; + + private: + PassSimple ps_ = {"ForceFields"}; + + struct CallBuffers { + const SelectionType selection_type_; + + ForceFieldsInstanceBuf field_force_buf = {selection_type_, "field_force_buf"}; + ForceFieldsInstanceBuf field_wind_buf = {selection_type_, "field_wind_buf"}; + ForceFieldsInstanceBuf field_vortex_buf = {selection_type_, "field_vortex_buf"}; + ForceFieldsInstanceBuf field_curve_buf = {selection_type_, "field_curve_buf"}; + ForceFieldsInstanceBuf field_sphere_limit_buf = {selection_type_, "field_sphere_limit_buf"}; + ForceFieldsInstanceBuf field_tube_limit_buf = {selection_type_, "field_tube_limit_buf"}; + ForceFieldsInstanceBuf field_cone_limit_buf = {selection_type_, "field_cone_limit_buf"}; + } call_buffers_; + + public: + ForceFields(const SelectionType selection_type) : call_buffers_{selection_type} {} + + void begin_sync() + { + call_buffers_.field_force_buf.clear(); + call_buffers_.field_wind_buf.clear(); + call_buffers_.field_vortex_buf.clear(); + call_buffers_.field_curve_buf.clear(); + call_buffers_.field_sphere_limit_buf.clear(); + call_buffers_.field_tube_limit_buf.clear(); + call_buffers_.field_cone_limit_buf.clear(); + } + + void object_sync(const ObjectRef &ob_ref, Resources &res, const State &state) + { + const select::ID select_id = res.select_id(ob_ref); + const Object *ob = ob_ref.object; + PartDeflect *pd = ob->pd; + Curve *cu = (ob->type == OB_CURVES_LEGACY) ? static_cast(ob->data) : nullptr; + + ExtraInstanceData data( + ob->object_to_world(), res.object_background_blend_color(ob_ref, state), 1.0f); + float4x4 &matrix = data.object_to_world_; + float &size_x = matrix[0][3]; + float &size_y = matrix[1][3]; + float &size_z = matrix[2][3]; + + size_x = size_y = size_z = ob->empty_drawsize; + + switch (pd->forcefield) { + case PFIELD_FORCE: + call_buffers_.field_force_buf.append(data, select_id); + break; + case PFIELD_WIND: + size_z = pd->f_strength; + call_buffers_.field_wind_buf.append(data, select_id); + break; + case PFIELD_VORTEX: + size_y = (pd->f_strength < 0.0f) ? -size_y : size_y; + call_buffers_.field_vortex_buf.append(data, select_id); + break; + case PFIELD_GUIDE: + if (cu && (cu->flag & CU_PATH) && ob->runtime->curve_cache->anim_path_accum_length) { + size_x = size_y = size_z = pd->f_strength; + float4 pos; + BKE_where_on_path(ob, 0.0f, pos, nullptr, nullptr, nullptr, nullptr); + matrix.location() = ob->object_to_world().location(); + matrix = math::translate(matrix, pos.xyz()); + call_buffers_.field_curve_buf.append(data, select_id); + + BKE_where_on_path(ob, 1.0f, pos, nullptr, nullptr, nullptr, nullptr); + matrix.location() = ob->object_to_world().location(); + matrix = math::translate(matrix, pos.xyz()); + call_buffers_.field_sphere_limit_buf.append(data, select_id); + /* Restore */ + matrix.location() = ob->object_to_world().location(); + } + break; + } + + if (pd->falloff == PFIELD_FALL_TUBE) { + if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { + size_z = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; + size_x = (pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f; + size_y = size_x; + call_buffers_.field_tube_limit_buf.append(data, select_id); + } + if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { + size_z = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; + size_x = (pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f; + size_y = size_x; + call_buffers_.field_tube_limit_buf.append(data, select_id); + } + } + else if (pd->falloff == PFIELD_FALL_CONE) { + if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { + float radius = DEG2RADF((pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f); + float distance = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; + size_x = distance * sinf(radius); + size_z = distance * cosf(radius); + size_y = size_x; + call_buffers_.field_cone_limit_buf.append(data, select_id); + } + if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { + float radius = DEG2RADF((pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f); + float distance = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; + size_x = distance * sinf(radius); + size_z = distance * cosf(radius); + size_y = size_x; + call_buffers_.field_cone_limit_buf.append(data, select_id); + } + } + else if (pd->falloff == PFIELD_FALL_SPHERE) { + if (pd->flag & PFIELD_USEMAX) { + size_x = size_y = size_z = pd->maxdist; + call_buffers_.field_sphere_limit_buf.append(data, select_id); + } + if (pd->flag & PFIELD_USEMIN) { + size_x = size_y = size_z = pd->mindist; + call_buffers_.field_sphere_limit_buf.append(data, select_id); + } + } + } + + void end_sync(Resources &res, ShapeCache &shapes, const State &state) + { + ps_.init(); + res.select_bind(ps_); + ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + state.clipping_state); + ps_.shader_set(res.shaders.extra_shape.get()); + ps_.bind_ubo("globalsBlock", &res.globals_buf); + + call_buffers_.field_force_buf.end_sync(ps_, shapes.field_force.get()); + call_buffers_.field_wind_buf.end_sync(ps_, shapes.field_wind.get()); + call_buffers_.field_vortex_buf.end_sync(ps_, shapes.field_vortex.get()); + call_buffers_.field_curve_buf.end_sync(ps_, shapes.field_curve.get()); + call_buffers_.field_sphere_limit_buf.end_sync(ps_, shapes.field_sphere_limit.get()); + call_buffers_.field_tube_limit_buf.end_sync(ps_, shapes.field_tube_limit.get()); + call_buffers_.field_cone_limit_buf.end_sync(ps_, shapes.field_cone_limit.get()); + } + + void draw(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + manager.submit(ps_, view); + } +}; + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index 09536d7156a..cb734c532ba 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -82,6 +82,7 @@ void Instance::begin_sync() layer.bounds.begin_sync(); layer.cameras.begin_sync(); layer.empties.begin_sync(); + layer.force_fields.begin_sync(); layer.lattices.begin_sync(resources, state); layer.lights.begin_sync(); layer.light_probes.begin_sync(resources, state); @@ -172,6 +173,9 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) layer.speakers.object_sync(ob_ref, resources, state); break; } + if (ob_ref.object->pd && ob_ref.object->pd->forcefield) { + layer.force_fields.object_sync(ob_ref, resources, state); + } layer.bounds.object_sync(ob_ref, resources, state); layer.relations.object_sync(ob_ref, resources, state); } @@ -185,6 +189,7 @@ void Instance::end_sync() layer.bounds.end_sync(resources, shapes, state); layer.cameras.end_sync(resources, shapes, state); layer.empties.end_sync(resources, shapes, state); + layer.force_fields.end_sync(resources, shapes, state); layer.lights.end_sync(resources, shapes, state); layer.light_probes.end_sync(resources, shapes, state); layer.metaballs.end_sync(resources, shapes, state); @@ -279,6 +284,7 @@ void Instance::draw(Manager &manager) layer.bounds.draw(framebuffer, manager, view); layer.cameras.draw(framebuffer, manager, view); layer.empties.draw(framebuffer, manager, view); + layer.force_fields.draw(framebuffer, manager, view); layer.lights.draw(framebuffer, manager, view); layer.light_probes.draw(framebuffer, manager, view); layer.speakers.draw(framebuffer, manager, view); diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.hh b/source/blender/draw/engines/overlay/overlay_next_instance.hh index 5545d2174ab..a9df9d4d88d 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.hh +++ b/source/blender/draw/engines/overlay/overlay_next_instance.hh @@ -15,6 +15,7 @@ #include "overlay_next_bounds.hh" #include "overlay_next_camera.hh" #include "overlay_next_empty.hh" +#include "overlay_next_force_field.hh" #include "overlay_next_grid.hh" #include "overlay_next_lattice.hh" #include "overlay_next_light.hh" @@ -52,6 +53,7 @@ class Instance { Bounds bounds = {selection_type_}; Cameras cameras = {selection_type_}; Empties empties = {selection_type_}; + ForceFields force_fields = {selection_type_}; Lattices lattices; Lights lights = {selection_type_}; LightProbes light_probes = {selection_type_}; diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index f2ba298e4de..d0116d40798 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -110,6 +110,14 @@ class ShapeCache { BatchPtr light_area_square_lines; BatchPtr light_spot_volume; + BatchPtr field_force; + BatchPtr field_wind; + BatchPtr field_vortex; + BatchPtr field_curve; + BatchPtr field_sphere_limit; + BatchPtr field_tube_limit; + BatchPtr field_cone_limit; + BatchPtr lightprobe_cube; BatchPtr lightprobe_planar; BatchPtr lightprobe_grid; @@ -281,6 +289,19 @@ struct Resources : public select::SelectMap { ThemeColorID theme_id = object_wire_theme_id(ob_ref, state); return object_wire_color(ob_ref, theme_id); } + + float4 background_blend_color(ThemeColorID theme_id) const + { + float4 color; + UI_GetThemeColorBlendShade4fv(theme_id, TH_BACK, 0.5, 0, color); + return color; + } + + float4 object_background_blend_color(const ObjectRef &ob_ref, const State &state) const + { + ThemeColorID theme_id = object_wire_theme_id(ob_ref, state); + return background_blend_color(theme_id); + } }; /** diff --git a/source/blender/draw/engines/overlay/overlay_next_shape.cc b/source/blender/draw/engines/overlay/overlay_next_shape.cc index e0446e2792d..00af68b94fd 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shape.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shape.cc @@ -778,6 +778,133 @@ ShapeCache::ShapeCache() light_area_square_lines = BatchPtr( GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); } + /* field_force */ + { + constexpr int circle_resol = 32; + constexpr int flag = VCLASS_EMPTY_SIZE | VCLASS_SCREENALIGNED; + constexpr std::array scales{2.0f, 0.75}; + Vector ring = ring_vertices(1.0f, circle_resol); + + Vector verts; + + append_line_loop(verts, ring, 0.0f, flag); + for (const float scale : scales) { + for (float2 &point : ring) { + point *= scale; + } + append_line_loop(verts, ring, 0.0f, flag); + } + + field_force = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_wind */ + { + constexpr int circle_resol = 32; + const Vector ring = ring_vertices(1.0f, circle_resol); + + Vector verts; + + for (const int i : IndexRange(4)) { + const float z = 0.05f * float(i); + append_line_loop(verts, ring, z, VCLASS_EMPTY_SIZE); + } + + field_wind = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_vortex */ + { + constexpr int spiral_resol = 32; + const Vector ring = ring_vertices(1.0f, spiral_resol); + + Vector verts; + + for (const int i : IndexRange(ring.size() * 2 + 1)) { + /* r: [-1, .., 0, .., 1] */ + const float r = (i - spiral_resol) / float(spiral_resol); + /* index: [9, spiral_resol - 1, spiral_resol - 2, .., 2, 1, 0, 1, 2, .., spiral_resol - 1, 0] + */ + const float2 point = ring[abs(spiral_resol - i) % spiral_resol] * r; + verts.append({float3(point.y, point.x, 0.0f), VCLASS_EMPTY_SIZE}); + } + field_vortex = BatchPtr(GPU_batch_create_ex( + GPU_PRIM_LINE_STRIP, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_curve */ + { + constexpr int circle_resol = 32; + const Vector ring = ring_vertices(1.0f, circle_resol); + + Vector verts; + + append_line_loop(verts, ring, 0.0f, VCLASS_EMPTY_SIZE | VCLASS_SCREENALIGNED); + + field_curve = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_sphere_limit */ + { + constexpr int circle_resol = 32 * 2; + const Vector ring = ring_vertices(1.0f, circle_resol); + + Vector verts; + + append_line_loop(verts, ring, 0.0f, VCLASS_EMPTY_SIZE | VCLASS_SCREENALIGNED, true); + + field_sphere_limit = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_tube_limit */ + { + constexpr int circle_resol = 32; + constexpr int side_stipple = 32; + const Vector ring = ring_vertices(1.0f, circle_resol); + const Vector diamond = ring_vertices(1.0f, 4); + + Vector verts; + + /* Caps */ + for (const int i : IndexRange(2)) { + const float z = i * 2.0f - 1.0f; + append_line_loop(verts, ring, z, VCLASS_EMPTY_SIZE, true); + } + /* Side Edges */ + for (const float2 &point : diamond) { + for (const int i : IndexRange(side_stipple)) { + const float z = (i / float(side_stipple)) * 2.0f - 1.0f; + verts.append({float3(point.y, point.x, z), VCLASS_EMPTY_SIZE}); + } + } + + field_tube_limit = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } + /* field_cone_limit */ + { + constexpr int circle_resol = 32; + constexpr int side_stipple = 32; + const Vector ring = ring_vertices(1.0f, circle_resol); + const Vector diamond = ring_vertices(1.0f, 4); + + Vector verts; + + /* Caps */ + for (const int i : IndexRange(2)) { + const float z = i * 2.0f - 1.0f; + append_line_loop(verts, ring, z, VCLASS_EMPTY_SIZE, true); + } + /* Side Edges */ + for (const float2 &point : diamond) { + for (const int i : IndexRange(side_stipple)) { + const float z = (i / float(side_stipple)) * 2.0f - 1.0f; + verts.append({float3(point.y * z, point.x * z, z), VCLASS_EMPTY_SIZE}); + } + } + + field_cone_limit = BatchPtr( + GPU_batch_create_ex(GPU_PRIM_LINES, vbo_from_vector(verts), nullptr, GPU_BATCH_OWNS_VBO)); + } /* lightprobe_cube */ { constexpr float r = 14.0f;