Compositor: Allow inter-operation canceling #119917

Merged
Omar Emara merged 4 commits from OmarEmaraDev/blender:gpu-compositor-better-interactivity into main 2024-04-08 16:24:09 +02:00
94 changed files with 1412 additions and 1108 deletions
Showing only changes of commit 56d0989ae0 - Show all commits

View File

@ -45,12 +45,26 @@ CHECKER_ARGS = (
# Shows many pedantic issues, some are quite useful.
"--enable=all",
# Generates many warnings, CPPCHECK known about system includes without resolving them.
"--suppress=missingIncludeSystem",
# Also shows useful messages, even if some are false-positives.
"--inconclusive",
# Generates many warnings, CPPCHECK known about system includes without resolving them.
"--suppress=missingIncludeSystem",
# Not considered an error.
"--suppress=allocaCalled",
# Overly noisy, we could consider resolving all of these at some point.
"--suppress=cstyleCast",
# There are various classes which don't have copy or equal constructors (GHOST windows for e.g.)
"--suppress=noCopyConstructor",
# Similar for `noCopyConstructor`.
"--suppress=nonoOperatorEq",
# There seems to be many false positives here.
"--suppress=unusedFunction",
# May be interesting to handle but very noisy currently.
"--suppress=variableScope",
# Quiet output, otherwise all defines/includes are printed (overly verbose).
# Only enable this for troubleshooting (if defines are not set as expected for example).
*(() if USE_VERBOSE else ("--quiet",))

View File

@ -797,6 +797,11 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph)
* but that will need some API support first.
*/
for (BL::Object &b_ob : b_depsgraph.objects) {
/* Grease pencil render requires all evaluated objects available as-is after Cycles is done
* with its part. */
if (b_ob.type() == BL::Object::type_GREASEPENCIL || b_ob.type() == BL::Object::type_GPENCIL) {
continue;
}
b_ob.cache_release();
}
}

View File

@ -1533,7 +1533,7 @@ typedef struct KernelParticle {
float lifetime;
float size;
float4 rotation;
/* Only xyz are used of the following. float4 instead of float3 are used
/* Only XYZ are used of the following. float4 instead of float3 are used
* to ensure consistent padding/alignment across devices. */
float4 location;
float4 velocity;

View File

@ -177,14 +177,12 @@ GHOST_TSuccess GHOST_System::endFullScreen()
if (m_windowManager->getFullScreen()) {
// GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
// GHOST_PRINT("GHOST_System::endFullScreen(): leaving window manager full-screen mode\n");
success = m_windowManager->endFullScreen();
GHOST_ASSERT(m_displayManager, "GHOST_System::endFullScreen(): invalid display manager");
// GHOST_PRINT("GHOST_System::endFullScreen(): leaving full-screen mode\n");
success = m_displayManager->setCurrentDisplaySetting(GHOST_DisplayManager::kMainDisplay,
m_preFullScreenSetting);
}
else {
success = GHOST_kFailure;
if (m_windowManager->endFullScreen() == GHOST_kSuccess) {
GHOST_ASSERT(m_displayManager, "GHOST_System::endFullScreen(): invalid display manager");
// GHOST_PRINT("GHOST_System::endFullScreen(): leaving full-screen mode\n");
success = m_displayManager->setCurrentDisplaySetting(GHOST_DisplayManager::kMainDisplay,
m_preFullScreenSetting);
}
}
return success;
}

View File

@ -252,7 +252,7 @@ class TIME_PT_playback(TimelinePanelButtons, Panel):
col.prop(screen, "use_play_3d_editors", text="3D Viewport")
col.prop(screen, "use_play_animation_editors", text="Animation Editors")
col.prop(screen, "use_play_image_editors", text="Image Editor")
col.prop(screen, "use_play_properties_editors", text="Properties Editor")
col.prop(screen, "use_play_properties_editors", text="Properties and Sidebars")
col.prop(screen, "use_play_clip_editors", text="Movie Clip Editor")
col.prop(screen, "use_play_node_editors", text="Node Editors")
col.prop(screen, "use_play_sequence_editors", text="Video Sequencer")

View File

@ -1175,6 +1175,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_edit_curve_ctrlpoints")
layout.menu("VIEW3D_MT_edit_curve_segments")
elif mode_string in {'EDIT_CURVES', 'EDIT_POINT_CLOUD'}:
layout.menu("VIEW3D_MT_edit_curves_segments")
layout.template_node_operator_asset_root_items()
elif mode_string == 'EDIT_GREASE_PENCIL':
layout.menu("VIEW3D_MT_edit_greasepencil_stroke")
@ -5916,6 +5917,16 @@ class VIEW3D_MT_edit_curves(Menu):
layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label)
class VIEW3D_MT_edit_curves_segments(Menu):
bl_label = "Segments"
def draw(self, _context):
layout = self.layout
layout.operator("curves.subdivide")
layout.operator("curves.switch_direction")
class VIEW3D_MT_edit_pointcloud(Menu):
bl_label = "Point Cloud"
@ -9058,6 +9069,7 @@ classes = (
VIEW3D_MT_edit_armature_delete,
VIEW3D_MT_edit_gpencil_transform,
VIEW3D_MT_edit_curves,
VIEW3D_MT_edit_curves_segments,
VIEW3D_MT_edit_pointcloud,
VIEW3D_MT_object_mode_pie,
VIEW3D_MT_view_pie,

View File

@ -185,8 +185,8 @@ ENUM_OPERATORS(Layer::Flags, Layer::Flags::Enabled);
* to identify which F-Curves (and in the future other animation data) it will
* be animated by.
*
* This is called an 'binding' because it acts like an binding socket of the
* Animation data-block, into which an animatable ID can be noodled.
* This is called a 'binding' because it binds the animatable ID to the sub-set
* of animation data that should animate it.
*
* \see AnimData::binding_handle
*/

View File

@ -241,7 +241,7 @@ void initialize_bezt(BezTriple *beztr,
/* Set keyframe type value (supplied),
* which should come from the scene settings in most cases. */
BEZKEYTYPE(beztr) = settings.keyframe_type;
BEZKEYTYPE_LVALUE(beztr) = settings.keyframe_type;
/* Set default values for "easing" interpolation mode settings.
* NOTE: Even if these modes aren't currently used, if users switch

View File

@ -11,7 +11,7 @@
/* defines BLI_INLINE */
#include "BLI_compiler_compat.h"
/* declares fprintf() and abort(), needed for BLI_assert */
/* Declares `fprintf()` & `abort()`, needed for `BLI_assert`. */
#include <stdio.h>
#include <stdlib.h>

View File

@ -79,7 +79,7 @@ class Drawing : public ::GreasePencilDrawing {
/**
* Returns the matrices that transform from a 3D point in layer-space to a 2D point in
* texture-space.
* texture-space. This is stored per curve.
*/
Span<float4x2> texture_matrices() const;
/**
@ -103,11 +103,19 @@ class Drawing : public ::GreasePencilDrawing {
MutableSpan<float> opacities_for_write();
/**
* Vertex colors of the points. Default is black.
* Vertex colors of the points. Default is black. This is mixed on top of the base material
* stroke color.
*/
VArray<ColorGeometry4f> vertex_colors() const;
MutableSpan<ColorGeometry4f> vertex_colors_for_write();
/**
* Fill colors of the curves. Default is black and fully transparent. This is mixed on top of the
* base material fill color.
*/
VArray<ColorGeometry4f> fill_colors() const;
MutableSpan<ColorGeometry4f> fill_colors_for_write();
/**
* Add a user for this drawing. When a drawing has multiple users, both users are allowed to
* modify this drawings data.

View File

@ -186,7 +186,7 @@ const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
{
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
if (entry->name == name) {
if (!type || (type && RNA_struct_is_a(entry->ptr.type, type))) {
if (!type || RNA_struct_is_a(entry->ptr.type, type)) {
return &entry->ptr;
}
}

View File

@ -1969,7 +1969,7 @@ static void sample_mesh(FluidFlowSettings *ffs,
printf("adding flow object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]);
# endif
}
/* Convert xyz velocities flow settings from world to grid space. */
/* Convert XYZ velocities flow settings from world to grid space. */
float convert_vel[3];
copy_v3_v3(convert_vel, ffs->vel_coord);
float time_mult = 1.0 / (25.0f * DT_DEFAULT);

View File

@ -255,6 +255,7 @@ namespace blender::bke::greasepencil {
static const std::string ATTR_RADIUS = "radius";
static const std::string ATTR_OPACITY = "opacity";
static const std::string ATTR_VERTEX_COLOR = "vertex_color";
static const std::string ATTR_FILL_COLOR = "fill_color";
/* Curves attributes getters */
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
@ -680,6 +681,20 @@ MutableSpan<ColorGeometry4f> Drawing::vertex_colors_for_write()
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
VArray<ColorGeometry4f> Drawing::fill_colors() const
{
return *this->strokes().attributes().lookup_or_default<ColorGeometry4f>(
ATTR_FILL_COLOR, AttrDomain::Curve, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
MutableSpan<ColorGeometry4f> Drawing::fill_colors_for_write()
{
return get_mutable_attribute<ColorGeometry4f>(this->strokes_for_write(),
AttrDomain::Curve,
ATTR_FILL_COLOR,
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
void Drawing::tag_texture_matrices_changed()
{
this->runtime->curve_texture_matrices.tag_dirty();

View File

@ -389,8 +389,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
"delta_time", AttrDomain::Point);
SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
"rotation", AttrDomain::Point);
SpanAttributeWriter<ColorGeometry4f> vertex_colors =
attributes.lookup_or_add_for_write_span<ColorGeometry4f>("vertex_color", AttrDomain::Point);
MutableSpan<ColorGeometry4f> vertex_colors = drawing.vertex_colors_for_write();
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
".selection", AttrDomain::Point);
MutableSpan<MDeformVert> dverts = use_dverts ? curves.wrap().deform_verts_for_write() :
@ -410,12 +409,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
"hardness", AttrDomain::Curve);
SpanAttributeWriter<float> stroke_point_aspect_ratios =
attributes.lookup_or_add_for_write_span<float>("aspect_ratio", AttrDomain::Curve);
SpanAttributeWriter<ColorGeometry4f> stroke_fill_colors =
attributes.lookup_or_add_for_write_span<ColorGeometry4f>(
"fill_color",
AttrDomain::Curve,
bke::AttributeInitVArray(VArray<ColorGeometry4f>::ForSingle(
ColorGeometry4f(float4(0.0f)), curves.curves_num())));
MutableSpan<ColorGeometry4f> stroke_fill_colors = drawing.fill_colors_for_write();
SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
"material_index", AttrDomain::Curve);
@ -431,7 +425,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
stroke_hardnesses.span[stroke_i] = gps->hardness;
stroke_point_aspect_ratios.span[stroke_i] = gps->aspect_ratio[0] /
max_ff(gps->aspect_ratio[1], 1e-8);
stroke_fill_colors.span[stroke_i] = ColorGeometry4f(gps->vert_color_fill);
stroke_fill_colors[stroke_i] = ColorGeometry4f(gps->vert_color_fill);
stroke_materials.span[stroke_i] = gps->mat_nr;
IndexRange points = points_by_curve[stroke_i];
@ -458,7 +452,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
MutableSpan<float> dst_opacities = opacities.slice(points);
MutableSpan<float> dst_deltatimes = delta_times.span.slice(points);
MutableSpan<float> dst_rotations = rotations.span.slice(points);
MutableSpan<ColorGeometry4f> dst_vertex_colors = vertex_colors.span.slice(points);
MutableSpan<ColorGeometry4f> dst_vertex_colors = vertex_colors.slice(points);
MutableSpan<bool> dst_selection = selection.span.slice(points);
MutableSpan<MDeformVert> dst_dverts = use_dverts ? dverts.slice(points) :
MutableSpan<MDeformVert>();
@ -526,7 +520,6 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
delta_times.finish();
rotations.finish();
vertex_colors.finish();
selection.finish();
stroke_cyclic.finish();
@ -535,7 +528,6 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
stroke_end_caps.finish();
stroke_hardnesses.finish();
stroke_point_aspect_ratios.finish();
stroke_fill_colors.finish();
stroke_materials.finish();
}

View File

@ -975,17 +975,29 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
}
}
static void direct_link_node_socket_list(BlendDataReader *reader, ListBase *socket_list)
static void remove_unsupported_sockets(ListBase *sockets, ListBase *links)
{
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, socket_list) {
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, sockets) {
if (is_node_socket_supported(sock)) {
direct_link_node_socket(reader, sock);
continue;
}
else {
/* Remove unsupported sockets. */
BLI_remlink(socket_list, sock);
MEM_SAFE_FREE(sock);
/* First remove any link pointing to the socket. */
if (links) {
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, links) {
if (link->fromsock == sock || link->tosock == sock) {
BLI_remlink(links, link);
if (link->tosock) {
link->tosock->link = nullptr;
}
MEM_freeN(link);
}
}
}
BLI_remlink(sockets, sock);
MEM_delete(sock->runtime);
MEM_freeN(sock);
}
}
@ -1157,8 +1169,12 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
BLO_read_data_address(reader, &node->parent);
direct_link_node_socket_list(reader, &node->inputs);
direct_link_node_socket_list(reader, &node->outputs);
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
direct_link_node_socket(reader, sock);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) {
direct_link_node_socket(reader, sock);
}
/* Socket storage. */
if (node->type == CMP_NODE_OUTPUT_FILE) {
@ -1173,8 +1189,12 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
/* Read legacy interface socket lists for versioning. */
BLO_read_list(reader, &ntree->inputs_legacy);
BLO_read_list(reader, &ntree->outputs_legacy);
direct_link_node_socket_list(reader, &ntree->inputs_legacy);
direct_link_node_socket_list(reader, &ntree->outputs_legacy);
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs_legacy) {
direct_link_node_socket(reader, sock);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs_legacy) {
direct_link_node_socket(reader, sock);
}
ntree->tree_interface.read_data(reader);
@ -1185,6 +1205,13 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
BLO_read_data_address(reader, &link->tosock);
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
remove_unsupported_sockets(&node->inputs, &ntree->links);
remove_unsupported_sockets(&node->outputs, &ntree->links);
}
remove_unsupported_sockets(&ntree->inputs_legacy, nullptr);
remove_unsupported_sockets(&ntree->outputs_legacy, nullptr);
BLO_read_data_address(reader, &ntree->geometry_node_asset_traits);
BLO_read_data_address(reader, &ntree->nested_node_refs);

View File

@ -169,7 +169,7 @@ bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_sele
}
}
else {
if (!mesh->deform_verts().data()) {
if (mesh->deform_verts().data()) {
const bool *select_vert = (const bool *)CustomData_get_layer_named(
&mesh->vert_data, CD_PROP_BOOL, ".select_vert");
int i;

View File

@ -42,6 +42,16 @@ template<typename T>
return std::nullopt;
}
template<typename T>
[[nodiscard]] inline std::optional<Bounds<T>> min_max(const std::optional<Bounds<T>> &a,
const T &b)
{
if (a.has_value()) {
return merge(*a, {b, b});
}
return Bounds<T>{b, b};
}
/**
* Find the smallest and largest values element-wise in the span.
*/
@ -117,12 +127,32 @@ template<typename T, typename RadiusT>
[](const Bounds<T> &a, const Bounds<T> &b) { return merge(a, b); });
}
/**
* Returns a new bound that contains the intersection of the two given bound.
* Returns no box if there are no overlap.
*/
template<typename T>
[[nodiscard]] inline std::optional<Bounds<T>> intersect(const std::optional<Bounds<T>> &a,
const std::optional<Bounds<T>> &b)
{
if (!a.has_value() || !b.has_value()) {
return std::nullopt;
}
const Bounds<T> result{math::max(a.value().min, b.value().min),
math::min(a.value().max, b.value().max)};
if (result.is_empty()) {
return std::nullopt;
}
return result;
}
} // namespace bounds
namespace detail {
template<typename T, int Size>
[[nodiscard]] inline bool less_or_equal_than(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
[[nodiscard]] inline bool any_less_or_equal_than(const VecBase<T, Size> &a,
const VecBase<T, Size> &b)
{
for (int i = 0; i < Size; i++) {
if (a[i] <= b[i]) {
@ -140,7 +170,7 @@ template<typename T> inline bool Bounds<T>::is_empty() const
return this->max <= this->min;
}
else {
return detail::less_or_equal_than(this->max, this->min);
return detail::any_less_or_equal_than(this->max, this->min);
}
}

View File

@ -444,6 +444,14 @@ template<typename T>
template<typename T>
[[nodiscard]] MatBase<T, 4, 4> perspective_infinite(T left, T right, T bottom, T top, T near_clip);
/**
* \brief Translate a projection matrix after creation in the screen plane.
* Usually used for anti-aliasing jittering.
* `offset` is the translation vector in projected space.
*/
template<typename T>
[[nodiscard]] MatBase<T, 4, 4> translate(const MatBase<T, 4, 4> &mat, const VecBase<T, 2> &offset);
} // namespace projection
/** \} */
@ -1649,6 +1657,23 @@ template<typename T>
return mat;
}
template<typename T>
[[nodiscard]] MatBase<T, 4, 4> translate(const MatBase<T, 4, 4> &mat, const VecBase<T, 2> &offset)
{
MatBase<T, 4, 4> result = mat;
const bool is_perspective = mat[2][3] == -1.0f;
const bool is_perspective_infinite = mat[2][2] == -1.0f;
if (is_perspective | is_perspective_infinite) {
result[2][0] -= mat[0][0] * offset.x / math::length(float3(mat[0][0], mat[1][0], mat[2][0]));
result[2][1] -= mat[1][1] * offset.y / math::length(float3(mat[0][1], mat[1][1], mat[2][1]));
}
else {
result[3][0] += offset.x;
result[3][1] += offset.y;
}
return result;
}
extern template float4x4 orthographic(
float left, float right, float bottom, float top, float near_clip, float far_clip);
extern template float4x4 perspective(

View File

@ -46,4 +46,4 @@ void BLT_lang_locale_explode(const char *locale,
char **language_variant);
/* Get EnumPropertyItem's for translations menu. */
EnumPropertyItem *BLT_lang_RNA_enum_properties();
const EnumPropertyItem *BLT_lang_RNA_enum_properties();

View File

@ -171,7 +171,7 @@ static void fill_locales()
}
#endif /* WITH_INTERNATIONAL */
EnumPropertyItem *BLT_lang_RNA_enum_properties()
const EnumPropertyItem *BLT_lang_RNA_enum_properties()
{
#ifdef WITH_INTERNATIONAL
return locales_menu;

View File

@ -603,6 +603,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_surf_lib.glsl
engines/eevee_next/shaders/eevee_surf_occupancy_frag.glsl
engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl
engines/eevee_next/shaders/eevee_surf_volume_frag.glsl
engines/eevee_next/shaders/eevee_shadow_page_tile_vert.glsl
engines/eevee_next/shaders/eevee_shadow_page_tile_frag.glsl
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@ -618,7 +619,6 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_vertex_copy_comp.glsl
engines/eevee_next/shaders/eevee_volume_integration_comp.glsl
engines/eevee_next/shaders/eevee_volume_lib.glsl
engines/eevee_next/shaders/eevee_volume_material_comp.glsl
engines/eevee_next/shaders/eevee_volume_resolve_frag.glsl
engines/eevee_next/shaders/eevee_volume_scatter_comp.glsl

View File

@ -150,13 +150,13 @@ struct ShadowBlock {
BLI_STATIC_ASSERT_ALIGN(ShadowBlock, 16)
struct LightData {
vec4 position_influence; /* w : InfluenceRadius (inversed and squared) */
vec4 color_influence_volume; /* w : InfluenceRadius but for Volume power */
vec4 spotdata_radius_shadow; /* x : spot size, y : spot blend, z : radius, w: shadow id */
vec4 rightvec_sizex; /* xyz: Normalized up vector, w: area size X or spot scale X */
vec4 upvec_sizey; /* xyz: Normalized right vector, w: area size Y or spot scale Y */
vec4 forwardvec_type; /* xyz: Normalized forward vector, w: Light Type */
vec4 diff_spec_volume; /* xyz: Diffuse/Spec/Volume power, w: radius for volumetric. */
vec4 position_influence; /* W: InfluenceRadius (inversed and squared) */
vec4 color_influence_volume; /* W: InfluenceRadius but for Volume power */
vec4 spotdata_radius_shadow; /* X: spot size, y : spot blend, z : radius, w: shadow id */
vec4 rightvec_sizex; /* XYZ: Normalized up vector, w: area size X or spot scale X */
vec4 upvec_sizey; /* XYZ: Normalized right vector, w: area size Y or spot scale Y */
vec4 forwardvec_type; /* XYZ: Normalized forward vector, w: Light Type */
vec4 diff_spec_volume; /* XYZ: Diffuse/Spec/Volume power, w: radius for volumetric. */
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)

View File

@ -166,6 +166,7 @@ void Camera::sync()
data.persmat = data.winmat * data.viewmat;
data.persinv = math::invert(data.persmat);
is_camera_object_ = false;
if (camera_eval && camera_eval->type == OB_CAMERA) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
data.clip_near = cam->clip_start;
@ -187,6 +188,7 @@ void Camera::sync()
data.equirect_bias = float2(0.0f);
data.equirect_scale = float2(0.0f);
#endif
is_camera_object_ = true;
}
else if (inst_.drw_view) {
/* \note: Follow camera parameters where distances are positive in front of the camera. */

View File

@ -102,6 +102,8 @@ class Camera {
float overscan_;
bool overscan_changed_;
/** Whether or not the camera was synced from a camera object. */
bool is_camera_object_ = false;
public:
Camera(Instance &inst, CameraData &data) : inst_(inst), data_(data){};
@ -130,6 +132,10 @@ class Camera {
{
return data_.type == CAMERA_PERSP;
}
bool is_camera_object() const
{
return is_camera_object_;
}
const float3 &position() const
{
return data_.viewinv.location();

View File

@ -251,7 +251,7 @@ Material &MaterialModule::material_sync(Object *ob,
mat.volume_occupancy = material_pass_get(
ob, blender_mat, MAT_PIPE_VOLUME_OCCUPANCY, MAT_GEOM_VOLUME);
mat.volume_material = material_pass_get(
ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, MAT_GEOM_VOLUME_OBJECT);
ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, MAT_GEOM_VOLUME);
return mat;
});
@ -338,7 +338,7 @@ Material &MaterialModule::material_sync(Object *ob,
mat.volume_occupancy = material_pass_get(
ob, blender_mat, MAT_PIPE_VOLUME_OCCUPANCY, geometry_type);
mat.volume_material = material_pass_get(
ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, MAT_GEOM_VOLUME_OBJECT);
ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, geometry_type);
}
else {
mat.volume_occupancy = MaterialPass();

View File

@ -51,8 +51,6 @@ enum eMaterialGeometry {
MAT_GEOM_VOLUME,
/* These maps to special shader. */
MAT_GEOM_VOLUME_OBJECT,
MAT_GEOM_VOLUME_WORLD,
MAT_GEOM_WORLD,
};

View File

@ -10,10 +10,14 @@
* This file is only for shading passes. Other passes are declared in their own module.
*/
#include "eevee_pipeline.hh"
#include "BLI_bounds.hh"
#include "eevee_instance.hh"
#include "eevee_pipeline.hh"
#include "eevee_shadow.hh"
#include <iostream>
#include "draw_common.hh"
namespace blender::eevee {
@ -124,7 +128,8 @@ void WorldPipeline::render(View &view)
void WorldVolumePipeline::sync(GPUMaterial *gpumat)
{
is_valid_ = (gpumat != nullptr) && (GPU_material_status(gpumat) == GPU_MAT_SUCCESS);
is_valid_ = (gpumat != nullptr) && (GPU_material_status(gpumat) == GPU_MAT_SUCCESS) &&
GPU_material_has_volume_output(gpumat);
if (!is_valid_) {
/* Skip if the material has not compiled yet. */
return;
@ -138,9 +143,10 @@ void WorldVolumePipeline::sync(GPUMaterial *gpumat)
world_ps_.bind_resources(inst_.sampling);
world_ps_.material_set(*inst_.manager, gpumat);
/* Bind correct dummy texture for attributes defaults. */
volume_sub_pass(world_ps_, nullptr, nullptr, gpumat);
world_ps_.dispatch(math::divide_ceil(inst_.volume.grid_size(), int3(VOLUME_GROUP_SIZE)));
world_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
/* Sync with object property pass. */
world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
@ -926,15 +932,19 @@ void DeferredPipeline::render(View &main_view,
void VolumeLayer::sync()
{
object_bounds_.clear();
combined_screen_bounds_ = std::nullopt;
use_hit_list = false;
is_empty = true;
finalized = false;
has_scatter = false;
has_absorption = false;
draw::PassMain &layer_pass = volume_layer_ps_;
layer_pass.init();
layer_pass.clear_stencil(0x0u);
{
PassMain::Sub &pass = layer_pass.sub("occupancy_ps");
/* Double sided without depth test. */
/* Always double sided to let all fragments be invoked. */
pass.state_set(DRW_STATE_WRITE_DEPTH);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.volume.occupancy);
@ -943,6 +953,9 @@ void VolumeLayer::sync()
}
{
PassMain::Sub &pass = layer_pass.sub("material_ps");
/* Double sided with stencil equal to ensure only one fragment is invoked per pixel. */
pass.state_set(DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL);
pass.state_stencil(0x1u, 0x1u, 0x1u);
pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
pass.bind_resources(inst_.uniform_data);
@ -977,9 +990,36 @@ PassMain::Sub *VolumeLayer::material_add(const Object * /*ob*/,
"Only volume material should be added here");
PassMain::Sub *pass = &material_ps_->sub(GPU_material_get_name(gpumat));
pass->material_set(*inst_.manager, gpumat);
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_VOLUME_SCATTER)) {
has_scatter = true;
}
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_VOLUME_ABSORPTION)) {
has_absorption = true;
}
return pass;
}
bool VolumeLayer::bounds_overlaps(const VolumeObjectBounds &object_bounds) const
{
/* First check the biggest area. */
if (bounds::intersect(object_bounds.screen_bounds, combined_screen_bounds_)) {
return true;
}
/* Check against individual bounds to try to squeeze the new object between them. */
for (const std::optional<Bounds<float2>> &other_aabb : object_bounds_) {
if (bounds::intersect(object_bounds.screen_bounds, other_aabb)) {
return true;
}
}
return false;
}
void VolumeLayer::add_object_bound(const VolumeObjectBounds &object_bounds)
{
object_bounds_.append(object_bounds.screen_bounds);
combined_screen_bounds_ = bounds::merge(combined_screen_bounds_, object_bounds.screen_bounds);
}
void VolumeLayer::render(View &view, Texture &occupancy_tx)
{
if (is_empty) {
@ -1007,6 +1047,7 @@ void VolumeLayer::render(View &view, Texture &occupancy_tx)
void VolumePipeline::sync()
{
object_integration_range_ = std::nullopt;
enabled_ = false;
has_scatter_ = false;
has_absorption_ = false;
@ -1024,107 +1065,63 @@ void VolumePipeline::render(View &view, Texture &occupancy_tx)
}
}
GridAABB VolumePipeline::grid_aabb_from_object(Object *ob)
VolumeObjectBounds::VolumeObjectBounds(const Camera &camera, Object *ob)
{
const Camera &camera = inst_.camera;
const VolumesInfoData &data = inst_.volume.data_;
/* Returns the unified volume grid cell corner of a world space coordinate. */
auto to_global_grid_coords = [&](float3 wP) -> int3 {
/* TODO(fclem): Should we use the render view winmat and not the camera one? */
const float4x4 &view_matrix = camera.data_get().viewmat;
const float4x4 &projection_matrix = camera.data_get().winmat;
/* TODO(fclem): For panoramic camera, we will have to do this check for each cube-face. */
const float4x4 &view_matrix = camera.data_get().viewmat;
/* Note in practice we only care about the projection type since we only care about 2D overlap,
* and this is independent of FOV. */
const float4x4 &projection_matrix = camera.data_get().winmat;
float3 ndc_coords = math::project_point(projection_matrix * view_matrix, wP);
ndc_coords = (ndc_coords * 0.5f) + float3(0.5f);
float3 grid_coords = screen_to_volume(projection_matrix,
data.depth_near,
data.depth_far,
data.depth_distribution,
data.coord_scale,
ndc_coords);
/* Round to nearest grid corner. */
return int3(grid_coords * float3(data.tex_size) + 0.5);
};
const Bounds<float3> bounds = BKE_object_boundbox_get(ob).value_or(Bounds(float3(0)));
int3 min = int3(INT32_MAX);
int3 max = int3(INT32_MIN);
const Bounds<float3> bounds = BKE_object_boundbox_get(ob).value_or(Bounds(float3(0.0f)));
BoundBox bb;
BKE_boundbox_init_from_minmax(&bb, bounds.min, bounds.max);
for (float3 l_corner : bb.vec) {
float3 w_corner = math::transform_point(ob->object_to_world(), l_corner);
/* Note that this returns the nearest cell corner coordinate.
* So sub-froxel AABB will effectively return the same coordinate
* for each corner (making it empty and skipped) unless it
* cover the center of the froxel. */
math::min_max(to_global_grid_coords(w_corner), min, max);
}
return {min, max};
}
GridAABB VolumePipeline::grid_aabb_from_view()
{
return {int3(0), inst_.volume.data_.tex_size};
screen_bounds = std::nullopt;
z_range = std::nullopt;
for (float3 l_corner : bb.vec) {
float3 ws_corner = math::transform_point(ob->object_to_world(), l_corner);
/* Split view and projection for precision. */
float3 vs_corner = math::transform_point(view_matrix, ws_corner);
float3 ss_corner = math::project_point(projection_matrix, vs_corner);
z_range = bounds::min_max(z_range, vs_corner.z);
if (camera.is_perspective() && vs_corner.z >= 1.0e-8f) {
/* If the object is crossing the z=0 plane, we can't determine its 2D bounds easily.
* In this case, consider the object covering the whole screen.
* Still continue the loop for the Z range. */
screen_bounds = Bounds<float2>(float2(-1.0f), float2(1.0f));
}
else {
screen_bounds = bounds::min_max(screen_bounds, ss_corner.xy());
}
}
}
VolumeLayer *VolumePipeline::register_and_get_layer(Object *ob)
{
GridAABB object_aabb = grid_aabb_from_object(ob);
GridAABB view_aabb = grid_aabb_from_view();
if (object_aabb.intersection(view_aabb).is_empty()) {
/* Skip invisible object with respect to raster grid and bounds density. */
return nullptr;
}
VolumeObjectBounds object_bounds(inst_.camera, ob);
object_integration_range_ = bounds::merge(object_integration_range_, object_bounds.z_range);
enabled_ = true;
/* Do linear search in all layers in order. This can be optimized. */
for (auto &layer : layers_) {
if (!layer->bounds_overlaps(object_aabb)) {
layer->add_object_bound(object_aabb);
if (!layer->bounds_overlaps(object_bounds)) {
layer->add_object_bound(object_bounds);
return layer.get();
}
}
/* No non-overlapping layer found. Create new one. */
int64_t index = layers_.append_and_get_index(std::make_unique<VolumeLayer>(inst_));
(*layers_[index]).add_object_bound(object_aabb);
(*layers_[index]).add_object_bound(object_bounds);
return layers_[index].get();
}
void VolumePipeline::material_call(MaterialPass &volume_material_pass,
Object *ob,
ResourceHandle res_handle)
std::optional<Bounds<float>> VolumePipeline::object_integration_range() const
{
if (volume_material_pass.sub_pass == nullptr) {
/* Can happen if shader is not compiled, or if object has been culled. */
return;
}
/* TODO(fclem): This should be revisited, `volume_sub_pass()` should not decide on the volume
* visibility. Instead, we should query visibility upstream and not try to even compile the
* shader. */
PassMain::Sub *object_pass = volume_sub_pass(
*volume_material_pass.sub_pass, inst_.scene, ob, volume_material_pass.gpumat);
if (object_pass) {
/* Possible double work here. Should be relatively insignificant in practice. */
GridAABB object_aabb = grid_aabb_from_object(ob);
GridAABB view_aabb = grid_aabb_from_view();
GridAABB visible_aabb = object_aabb.intersection(view_aabb);
/* Invisible volumes should already have been clipped. */
BLI_assert(visible_aabb.is_empty() == false);
/* TODO(fclem): Use graphic pipeline instead of compute so we can leverage GPU culling,
* resource indexing and other further optimizations. */
object_pass->push_constant("drw_ResourceID", int(res_handle.resource_index()));
object_pass->push_constant("grid_coords_min", visible_aabb.min);
object_pass->dispatch(math::divide_ceil(visible_aabb.extent(), int3(VOLUME_GROUP_SIZE)));
/* Notify the volume module to enable itself. */
enabled_ = true;
if (GPU_material_flag_get(volume_material_pass.gpumat, GPU_MATFLAG_VOLUME_SCATTER)) {
has_scatter_ = true;
}
if (GPU_material_flag_get(volume_material_pass.gpumat, GPU_MATFLAG_VOLUME_ABSORPTION)) {
has_absorption_ = true;
}
}
return object_integration_range_;
}
bool VolumePipeline::use_hit_list() const

View File

@ -341,28 +341,13 @@ class DeferredPipeline {
*
* \{ */
struct GridAABB {
int3 min, max;
struct VolumeObjectBounds {
/* Screen 2D bounds for layer intersection check. */
std::optional<Bounds<float2>> screen_bounds;
/* Combined bounds in Z. Allow tighter integration bounds. */
std::optional<Bounds<float>> z_range;
GridAABB(int3 min_, int3 max_) : min(min_), max(max_){};
/** Returns the intersection between this AABB and the \a other AABB. */
GridAABB intersection(const GridAABB &other) const
{
return {math::max(this->min, other.min), math::min(this->max, other.max)};
}
/** Returns the extent of the volume. Undefined if AABB is empty. */
int3 extent() const
{
return max - min;
}
/** Returns true if volume covers nothing or is negative. */
bool is_empty() const
{
return math::reduce_min(max - min) <= 0;
}
VolumeObjectBounds(const Camera &camera, Object *ob);
};
/**
@ -373,6 +358,8 @@ class VolumeLayer {
bool use_hit_list = false;
bool is_empty = true;
bool finalized = false;
bool has_scatter = false;
bool has_absorption = false;
private:
Instance &inst_;
@ -382,7 +369,9 @@ class VolumeLayer {
PassMain::Sub *occupancy_ps_;
PassMain::Sub *material_ps_;
/* List of bounds from all objects contained inside this pass. */
Vector<GridAABB> object_bounds_;
Vector<std::optional<Bounds<float2>>> object_bounds_;
/* Combined bounds from object_bounds_. */
std::optional<Bounds<float2>> combined_screen_bounds_;
public:
VolumeLayer(Instance &inst) : inst_(inst)
@ -398,20 +387,9 @@ class VolumeLayer {
GPUMaterial *gpumat);
/* Return true if the given bounds overlaps any of the contained object in this layer. */
bool bounds_overlaps(const GridAABB &object_aabb) const
{
for (const GridAABB &other_aabb : object_bounds_) {
if (object_aabb.intersection(other_aabb).is_empty() == false) {
return true;
}
}
return false;
}
bool bounds_overlaps(const VolumeObjectBounds &object_aabb) const;
void add_object_bound(const GridAABB &object_aabb)
{
object_bounds_.append(object_aabb);
}
void add_object_bound(const VolumeObjectBounds &object_aabb);
void sync();
void render(View &view, Texture &occupancy_tx);
@ -423,6 +401,8 @@ class VolumePipeline {
Vector<std::unique_ptr<VolumeLayer>> layers_;
/* Combined bounds in Z. Allow tighter integration bounds. */
std::optional<Bounds<float>> object_integration_range_;
/* True if any volume (any object type) creates a volume draw-call. Enables the volume module. */
bool enabled_ = false;
/* Aggregated properties of all volume objects. */
@ -441,12 +421,7 @@ class VolumePipeline {
*/
VolumeLayer *register_and_get_layer(Object *ob);
/**
* Creates a volume material call.
* If any call to this function result in a valid draw-call, then the volume module will be
* enabled.
*/
void material_call(MaterialPass &volume_material_pass, Object *ob, ResourceHandle res_handle);
std::optional<Bounds<float>> object_integration_range() const;
bool is_enabled() const
{
@ -454,31 +429,25 @@ class VolumePipeline {
}
bool has_scatter() const
{
return has_scatter_;
for (auto &layer : layers_) {
if (layer->has_scatter) {
return true;
}
}
return false;
}
bool has_absorption() const
{
return has_absorption_;
for (auto &layer : layers_) {
if (layer->has_absorption) {
return true;
}
}
return false;
}
/* Returns true if any volume layer uses the hist list. */
bool use_hit_list() const;
private:
/**
* Returns Axis aligned bounding box in the volume grid.
* Used for frustum culling and volumes overlapping detection.
* Represents min and max grid corners covered by a volume.
* So a volume covering the first froxel will have min={0,0,0} and max={1,1,1}.
* A volume with min={0,0,0} and max={0,0,0} covers nothing.
*/
GridAABB grid_aabb_from_object(Object *ob);
/**
* Returns the view entire AABB. Used for clipping object bounds.
* Remember that these are cells corners, so this extents to `tex_size`.
*/
GridAABB grid_aabb_from_view();
};
/** \} */

View File

@ -149,7 +149,7 @@ void Sampling::step()
double3 r, offset = {0, 0, 0};
uint64_t leap = 13;
uint3 primes = {5, 7, 11};
BLI_halton_3d(primes, offset, sample_raytrace * leap, r);
BLI_halton_3d(primes, offset, sample_raytrace * leap + 1, r);
data_.dimensions[SAMPLING_SHADOW_U] = r[0];
data_.dimensions[SAMPLING_SHADOW_V] = r[1];
data_.dimensions[SAMPLING_SHADOW_W] = r[2];
@ -157,17 +157,26 @@ void Sampling::step()
data_.dimensions[SAMPLING_RAYTRACE_U] = r[0];
data_.dimensions[SAMPLING_RAYTRACE_V] = r[1];
data_.dimensions[SAMPLING_RAYTRACE_W] = r[2];
/* TODO de-correlate. */
data_.dimensions[SAMPLING_VOLUME_U] = r[0];
data_.dimensions[SAMPLING_VOLUME_V] = r[1];
data_.dimensions[SAMPLING_VOLUME_W] = r[2];
}
{
uint64_t sample_volume = sample_;
if (interactive_mode()) {
sample_volume = sample_volume % interactive_sample_volume_;
}
double3 r, offset = {0, 0, 0};
uint3 primes = {2, 3, 5};
BLI_halton_3d(primes, offset, sample_volume + 1, r);
/* WORKAROUND: We offset the distribution to make the first sample (0,0,0). */
data_.dimensions[SAMPLING_VOLUME_U] = fractf(r[0] + (1.0 / 2.0));
data_.dimensions[SAMPLING_VOLUME_V] = fractf(r[1] + (2.0 / 3.0));
data_.dimensions[SAMPLING_VOLUME_W] = fractf(r[2] + (4.0 / 5.0));
}
{
/* Using leaped Halton sequence so we can reused the same primes. */
double2 r, offset = {0, 0};
uint64_t leap = 5;
uint2 primes = {2, 3};
BLI_halton_2d(primes, offset, sample_ * leap, r);
BLI_halton_2d(primes, offset, sample_ * leap + 1, r);
data_.dimensions[SAMPLING_SHADOW_X] = r[0];
data_.dimensions[SAMPLING_SHADOW_Y] = r[1];
/* TODO de-correlate. */

View File

@ -32,8 +32,10 @@ class Sampling {
/* During interactive rendering, loop over the first few samples. */
static constexpr uint64_t interactive_sample_aa_ = 8;
static constexpr uint64_t interactive_sample_raytrace_ = 32;
static constexpr uint64_t interactive_sample_volume_ = 32;
static constexpr uint64_t interactive_sample_max_ = interactive_sample_aa_ *
interactive_sample_raytrace_;
interactive_sample_raytrace_ *
interactive_sample_volume_;
/** 0 based current sample. Might not increase sequentially in viewport. */
uint64_t sample_ = 0;

View File

@ -508,7 +508,22 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
std::stringstream global_vars;
switch (geometry_type) {
case MAT_GEOM_MESH:
/** Noop. */
if (pipeline_type == MAT_PIPE_VOLUME_MATERIAL) {
/* If mesh has a volume output, it can receive volume grid attributes from smoke
* simulation modifier. But the vertex shader might still need access to the vertex
* attribute for displacement. */
/* TODO(fclem): Eventually, we could add support for loading both. For now, remove the
* vertex inputs after conversion (avoid name collision). */
for (auto &input : info.vertex_inputs_) {
info.sampler(sampler_slot--, ImageType::FLOAT_3D, input.name, Frequency::BATCH);
}
info.vertex_inputs_.clear();
/* Volume materials require these for loading the grid attributes from smoke sims. */
info.additional_info("draw_volume_infos");
if (ob_info_index == -1) {
info.additional_info("draw_object_infos_new");
}
}
break;
case MAT_GEOM_POINT_CLOUD:
case MAT_GEOM_CURVES:
@ -525,6 +540,14 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
info.vertex_inputs_.clear();
break;
case MAT_GEOM_WORLD:
if (pipeline_type == MAT_PIPE_VOLUME_MATERIAL) {
/* Even if world do not have grid attributes, we use dummy texture binds to pass correct
* defaults. So we have to replace all attributes as samplers. */
for (auto &input : info.vertex_inputs_) {
info.sampler(sampler_slot--, ImageType::FLOAT_3D, input.name, Frequency::BATCH);
}
info.vertex_inputs_.clear();
}
/**
* Only orco layer is supported by world and it is procedurally generated. These are here to
* make the attribs_load function calls valid.
@ -542,8 +565,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
info.vertex_inputs_.clear();
break;
case MAT_GEOM_VOLUME:
case MAT_GEOM_VOLUME_OBJECT:
case MAT_GEOM_VOLUME_WORLD:
/** Volume grid attributes come from 3D textures. Transfer attributes to samplers. */
for (auto &input : info.vertex_inputs_) {
info.sampler(sampler_slot--, ImageType::FLOAT_3D, input.name, Frequency::BATCH);
@ -552,11 +573,8 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
break;
}
const bool do_vertex_attrib_load = !ELEM(geometry_type,
MAT_GEOM_WORLD,
MAT_GEOM_VOLUME_WORLD,
MAT_GEOM_VOLUME_OBJECT,
MAT_GEOM_VOLUME);
const bool do_vertex_attrib_load = !ELEM(geometry_type, MAT_GEOM_WORLD, MAT_GEOM_VOLUME) &&
(pipeline_type != MAT_PIPE_VOLUME_MATERIAL);
if (!do_vertex_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
/* Codegen outputs only one interface. */
@ -580,26 +598,19 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
std::stringstream vert_gen, frag_gen, comp_gen;
bool is_compute = pipeline_type == MAT_PIPE_VOLUME_MATERIAL;
if (do_vertex_attrib_load) {
vert_gen << global_vars.str() << attr_load.str();
}
else if (!is_compute) {
frag_gen << global_vars.str() << attr_load.str();
frag_gen << "void attrib_load() {}\n"; /* Placeholder. */
}
else {
comp_gen << global_vars.str() << attr_load.str();
vert_gen << "void attrib_load() {}\n"; /* Placeholder. */
frag_gen << global_vars.str() << attr_load.str();
}
if (!is_compute) {
{
const bool use_vertex_displacement = (!codegen.displacement.empty()) &&
(displacement_type != MAT_DISPLACEMENT_BUMP) &&
(!ELEM(geometry_type,
MAT_GEOM_WORLD,
MAT_GEOM_VOLUME_WORLD,
MAT_GEOM_VOLUME_OBJECT,
MAT_GEOM_VOLUME));
(!ELEM(geometry_type, MAT_GEOM_WORLD, MAT_GEOM_VOLUME));
vert_gen << "vec3 nodetree_displacement()\n";
vert_gen << "{\n";
@ -609,7 +620,7 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
info.vertex_source_generated = vert_gen.str();
}
if (!is_compute && pipeline_type != MAT_PIPE_VOLUME_OCCUPANCY) {
if (pipeline_type != MAT_PIPE_VOLUME_OCCUPANCY) {
frag_gen << ((!codegen.material_functions.empty()) ? codegen.material_functions : "\n");
if (!codegen.displacement.empty()) {
@ -634,32 +645,20 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
frag_gen << ((!codegen.thickness.empty()) ? codegen.thickness : "return 0.1;\n");
frag_gen << "}\n\n";
frag_gen << "Closure nodetree_volume()\n";
frag_gen << "{\n";
frag_gen << " closure_weights_reset(0.0);\n";
frag_gen << ((!codegen.volume.empty()) ? codegen.volume : "return Closure(0);\n");
frag_gen << "}\n\n";
info.fragment_source_generated = frag_gen.str();
}
if (is_compute) {
comp_gen << ((!codegen.material_functions.empty()) ? codegen.material_functions : "\n");
comp_gen << "Closure nodetree_volume()\n";
comp_gen << "{\n";
comp_gen << " closure_weights_reset(0.0);\n";
comp_gen << ((!codegen.volume.empty()) ? codegen.volume : "return Closure(0);\n");
comp_gen << "}\n\n";
info.compute_source_generated = comp_gen.str();
}
/* Geometry Info. */
switch (geometry_type) {
case MAT_GEOM_WORLD:
info.additional_info("eevee_geom_world");
break;
case MAT_GEOM_VOLUME_WORLD:
info.additional_info("eevee_volume_world");
break;
case MAT_GEOM_VOLUME_OBJECT:
info.additional_info("eevee_volume_object");
break;
case MAT_GEOM_GPENCIL:
info.additional_info("eevee_geom_gpencil");
break;
@ -679,10 +678,14 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
/* Pipeline Info. */
switch (geometry_type) {
case MAT_GEOM_WORLD:
info.additional_info("eevee_surf_world");
break;
case MAT_GEOM_VOLUME_OBJECT:
case MAT_GEOM_VOLUME_WORLD:
switch (pipeline_type) {
case MAT_PIPE_VOLUME_MATERIAL:
info.additional_info("eevee_surf_volume");
break;
default:
info.additional_info("eevee_surf_world");
break;
}
break;
default:
switch (pipeline_type) {
@ -715,6 +718,9 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
case MAT_PIPE_VOLUME_OCCUPANCY:
info.additional_info("eevee_surf_occupancy");
break;
case MAT_PIPE_VOLUME_MATERIAL:
info.additional_info("eevee_surf_volume");
break;
case MAT_PIPE_CAPTURE:
info.additional_info("eevee_surf_capture");
break;
@ -774,9 +780,7 @@ GPUMaterial *ShaderModule::world_shader_get(::World *blender_world,
bool is_volume = (pipeline_type == MAT_PIPE_VOLUME_MATERIAL);
bool defer_compilation = is_volume;
eMaterialGeometry geometry_type = is_volume ? MAT_GEOM_VOLUME_WORLD : MAT_GEOM_WORLD;
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, MAT_GEOM_WORLD);
return DRW_shader_from_world(blender_world,
nodetree,

View File

@ -501,74 +501,38 @@ BLI_STATIC_ASSERT_ALIGN(MotionBlurTileIndirection, 16)
* \{ */
struct VolumesInfoData {
float2 coord_scale;
float2 viewport_size_inv;
/* During object voxelization, we need to use an infinite projection matrix to avoid clipping
* faces. But they cannot be used for recovering the view position from froxel position as they
* are not invertible. We store the finite projection matrix and use it for this purpose. */
float4x4 winmat_finite;
float4x4 wininv_finite;
/* Convert volume frustum UV(+ linear Z) coordinates into previous frame UV(+ linear Z). */
float4x4 history_matrix;
/* Size of the froxel grid texture. */
packed_int3 tex_size;
/* Maximum light intensity during volume lighting evaluation. */
float light_clamp;
/* Inverse of size of the froxel grid. */
packed_float3 inv_tex_size;
int tile_size;
int tile_size_lod;
/* Maximum light intensity during volume lighting evaluation. */
float shadow_steps;
/* 2D scaling factor to make froxel squared. */
float2 coord_scale;
/* Extent and inverse extent of the main shading view (render extent, not film extent). */
float2 main_view_extent;
float2 main_view_extent_inv;
/* Size in main view pixels of one froxel in XY. */
int tile_size;
/* Hi-Z LOD to use during volume shadow tagging. */
int tile_size_lod;
/* Depth to froxel mapping. */
float depth_near;
float depth_far;
float depth_distribution;
float _pad0;
float _pad1;
float _pad2;
};
BLI_STATIC_ASSERT_ALIGN(VolumesInfoData, 16)
/* Volume slice to view space depth. */
static inline float volume_z_to_view_z(
float near, float far, float distribution, bool is_persp, float z)
{
if (is_persp) {
/* Exponential distribution. */
return (exp2(z / distribution) - near) / far;
}
else {
/* Linear distribution. */
return near + (far - near) * z;
}
}
static inline float view_z_to_volume_z(
float near, float far, float distribution, bool is_persp, float depth)
{
if (is_persp) {
/* Exponential distribution. */
return distribution * log2(depth * far + near);
}
else {
/* Linear distribution. */
return (depth - near) * distribution;
}
}
static inline float3 screen_to_volume(const float4x4 projection_matrix,
float near,
float far,
float distribution,
const float2 coord_scale,
float3 coord)
{
bool is_persp = projection_matrix[3][3] == 0.0;
/* get_view_z_from_depth */
float d = 2.0 * coord.z - 1.0;
if (is_persp) {
coord.z = -projection_matrix[3][2] / (d + projection_matrix[2][2]);
}
else {
coord.z = (d - projection_matrix[3][2]) / projection_matrix[2][2];
}
coord.z = view_z_to_volume_z(near, far, distribution, is_persp, coord.z);
coord.x *= coord_scale.x;
coord.y *= coord_scale.y;
return coord;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -79,6 +79,15 @@ static inline void geometry_call(PassMain::Sub *sub_pass,
}
}
static inline void volume_call(
MaterialPass &matpass, Scene *scene, Object *ob, gpu::Batch *geom, ResourceHandle res_handle)
{
if (matpass.sub_pass != nullptr) {
PassMain::Sub *object_pass = volume_sub_pass(*matpass.sub_pass, scene, ob, matpass.gpumat);
object_pass->draw(geom, res_handle);
}
}
/** \} */
/* -------------------------------------------------------------------- */
@ -125,13 +134,12 @@ void SyncModule::sync_mesh(Object *ob,
Material &material = material_array.materials[i];
GPUMaterial *gpu_material = material_array.gpu_materials[i];
if (material.has_volume && (i == 0)) {
/* Only support single volume material for now. */
geometry_call(material.volume_occupancy.sub_pass, geom, res_handle);
inst_.pipelines.volume.material_call(material.volume_material, ob, res_handle);
if (material.has_volume) {
volume_call(material.volume_occupancy, inst_.scene, ob, geom, res_handle);
volume_call(material.volume_material, inst_.scene, ob, geom, res_handle);
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
if (gpu_material && !GPU_material_has_surface_output(gpu_material)) {
if (!material.has_surface) {
continue;
}
}
@ -206,10 +214,9 @@ bool SyncModule::sync_sculpt(Object *ob,
Material &material = material_array.materials[batch.material_slot];
if (material.has_volume && (batch.material_slot == 0)) {
/* Only support single volume material for now. */
geometry_call(material.volume_occupancy.sub_pass, geom, res_handle);
inst_.pipelines.volume.material_call(material.volume_material, ob, res_handle);
if (material.has_volume) {
volume_call(material.volume_occupancy, inst_.scene, ob, geom, res_handle);
volume_call(material.volume_material, inst_.scene, ob, geom, res_handle);
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
if (material.has_surface == false) {
@ -286,7 +293,7 @@ void SyncModule::sync_point_cloud(Object *ob,
if (material.has_volume) {
/* Only support single volume material for now. */
drawcall_add(material.volume_occupancy);
inst_.pipelines.volume.material_call(material.volume_material, ob, res_handle);
drawcall_add(material.volume_material);
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
@ -346,9 +353,17 @@ void SyncModule::sync_volume(Object *ob, ObjectHandle & /*ob_handle*/, ResourceH
/* Use bounding box tag empty spaces. */
gpu::Batch *geom = DRW_cache_cube_get();
geometry_call(material.volume_occupancy.sub_pass, geom, res_handle);
auto drawcall_add = [&](MaterialPass &matpass, gpu::Batch *geom, ResourceHandle res_handle) {
if (matpass.sub_pass == nullptr) {
return;
}
PassMain::Sub *object_pass = volume_sub_pass(
*matpass.sub_pass, inst_.scene, ob, matpass.gpumat);
object_pass->draw(geom, res_handle);
};
inst_.pipelines.volume.material_call(material.volume_material, ob, res_handle);
drawcall_add(material.volume_occupancy, geom, res_handle);
drawcall_add(material.volume_material, geom, res_handle);
}
/** \} */
@ -542,7 +557,7 @@ void SyncModule::sync_curves(Object *ob,
if (material.has_volume) {
/* Only support single volume material for now. */
drawcall_add(material.volume_occupancy);
inst_.pipelines.volume.material_call(material.volume_material, ob, res_handle);
drawcall_add(material.volume_material);
/* Do not render surface if we are rendering a volume object
* and do not have a surface closure. */
if (material.has_surface == false) {

View File

@ -19,6 +19,7 @@
#include "eevee_pipeline.hh"
#include "eevee_volume.hh"
#include <iostream>
namespace blender::eevee {
@ -44,7 +45,8 @@ void VolumeModule::init()
tex_size = math::min(tex_size, max_size);
data_.coord_scale = float2(extent) / float2(tile_size * tex_size);
data_.viewport_size_inv = 1.0f / float2(extent);
data_.main_view_extent = float2(extent);
data_.main_view_extent_inv = 1.0f / float2(extent);
/* TODO: compute snap to maxZBuffer for clustered rendering. */
if (data_.tex_size != tex_size) {
@ -62,8 +64,12 @@ void VolumeModule::init()
data_.light_clamp = scene_eval->eevee.volumetric_light_clamp;
}
void VolumeModule::begin_sync()
void VolumeModule::begin_sync() {}
void VolumeModule::end_sync()
{
enabled_ = inst_.world.has_volume() || inst_.pipelines.volume.is_enabled();
const Scene *scene_eval = inst_.scene;
/* Negate clip values (View matrix forward vector is -Z). */
@ -72,30 +78,34 @@ void VolumeModule::begin_sync()
float integration_start = scene_eval->eevee.volumetric_start;
float integration_end = scene_eval->eevee.volumetric_end;
if (!inst_.camera.is_camera_object() && inst_.camera.is_orthographic()) {
integration_start = -integration_end;
}
std::optional<Bounds<float>> volume_bounds = inst_.pipelines.volume.object_integration_range();
if (volume_bounds && !inst_.world.has_volume()) {
/* Restrict integration range to the object volume range. This increases precision. */
integration_start = math::max(integration_start, -volume_bounds.value().max);
integration_end = math::min(integration_end, -volume_bounds.value().min);
}
float near = math::min(-integration_start, clip_start + 1e-4f);
float far = math::max(-integration_end, clip_end - 1e-4f);
if (inst_.camera.is_perspective()) {
float sample_distribution = scene_eval->eevee.volumetric_sample_distribution;
sample_distribution = 4.0f * std::max(1.0f - sample_distribution, 1e-2f);
float near = integration_start = std::min(-integration_start, clip_start - 1e-4f);
float far = integration_end = std::min(-integration_end, near - 1e-4f);
sample_distribution = 4.0f * math::max(1.0f - sample_distribution, 1e-2f);
data_.depth_near = (far - near * exp2(1.0f / sample_distribution)) / (far - near);
data_.depth_far = (1.0f - data_.depth_near) / near;
data_.depth_distribution = sample_distribution;
}
else {
integration_start = std::min(integration_end, clip_start);
integration_end = std::max(-integration_end, clip_end);
data_.depth_near = integration_start;
data_.depth_far = integration_end;
data_.depth_distribution = 1.0f / (integration_end - integration_start);
data_.depth_near = near;
data_.depth_far = far;
data_.depth_distribution = 0.0f; /* Unused. */
}
}
void VolumeModule::end_sync()
{
enabled_ = inst_.world.has_volume() || inst_.pipelines.volume.is_enabled();
if (!enabled_) {
occupancy_tx_.free();
@ -103,8 +113,10 @@ void VolumeModule::end_sync()
prop_extinction_tx_.free();
prop_emission_tx_.free();
prop_phase_tx_.free();
scatter_tx_.free();
extinction_tx_.free();
scatter_tx_.current().free();
scatter_tx_.previous().free();
extinction_tx_.current().free();
extinction_tx_.previous().free();
integrated_scatter_tx_.free();
integrated_transmit_tx_.free();
@ -160,18 +172,17 @@ void VolumeModule::end_sync()
}
}
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
/* Metal requires a dummy attachment. */
occupancy_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE_LAYER(prop_extinction_tx_, 0));
}
else {
/* Empty frame-buffer. */
occupancy_fb_.ensure(data_.tex_size.xy());
}
eGPUTextureUsage front_depth_usage = GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_ATTACHMENT;
front_depth_tx_.ensure_2d(GPU_DEPTH24_STENCIL8, data_.tex_size.xy(), front_depth_usage);
occupancy_fb_.ensure(GPU_ATTACHMENT_TEXTURE(front_depth_tx_));
scatter_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
extinction_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
scatter_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
extinction_tx_.current().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
scatter_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
extinction_tx_.previous().ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
data_.history_matrix = float4x4::identity();
integrated_scatter_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
integrated_transmit_tx_.ensure_3d(GPU_R11F_G11F_B10F, data_.tex_size, usage);
@ -188,21 +199,29 @@ void VolumeModule::end_sync()
occupancy.hit_depth_tx_ = hit_depth_tx_;
occupancy.hit_count_tx_ = hit_count_tx_;
/* Use custom sampler to simplify and speedup the shader.
* - Set extend mode to clamp to border color to reject samples with invalid re-projection.
* - Set filtering mode to none to avoid over-blur during re-projection. */
const GPUSamplerState history_sampler = {GPU_SAMPLER_FILTERING_DEFAULT,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER};
scatter_ps_.init();
scatter_ps_.shader_set(
inst_.shaders.static_shader_get(use_lights_ ? VOLUME_SCATTER_WITH_LIGHTS : VOLUME_SCATTER));
inst_.lights.bind_resources(scatter_ps_);
inst_.sphere_probes.bind_resources(scatter_ps_);
inst_.volume_probes.bind_resources(scatter_ps_);
inst_.shadows.bind_resources(scatter_ps_);
inst_.sampling.bind_resources(scatter_ps_);
scatter_ps_.bind_resources(inst_.lights);
scatter_ps_.bind_resources(inst_.sphere_probes);
scatter_ps_.bind_resources(inst_.volume_probes);
scatter_ps_.bind_resources(inst_.shadows);
scatter_ps_.bind_resources(inst_.sampling);
scatter_ps_.bind_image("in_scattering_img", &prop_scattering_tx_);
scatter_ps_.bind_image("in_extinction_img", &prop_extinction_tx_);
scatter_ps_.bind_texture("extinction_tx", &prop_extinction_tx_);
scatter_ps_.bind_image("in_emission_img", &prop_emission_tx_);
scatter_ps_.bind_image("in_phase_img", &prop_phase_tx_);
scatter_ps_.bind_image("out_scattering_img", &scatter_tx_);
scatter_ps_.bind_image("out_extinction_img", &extinction_tx_);
scatter_ps_.bind_texture("scattering_history_tx", &scatter_tx_.previous(), history_sampler);
scatter_ps_.bind_texture("extinction_history_tx", &extinction_tx_.previous(), history_sampler);
scatter_ps_.bind_image("out_scattering_img", &scatter_tx_.current());
scatter_ps_.bind_image("out_extinction_img", &extinction_tx_.current());
scatter_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
/* Sync with the property pass. */
scatter_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH);
@ -211,8 +230,9 @@ void VolumeModule::end_sync()
integration_ps_.init();
integration_ps_.shader_set(inst_.shaders.static_shader_get(VOLUME_INTEGRATION));
integration_ps_.bind_resources(inst_.uniform_data);
integration_ps_.bind_texture("in_scattering_tx", &scatter_tx_);
integration_ps_.bind_texture("in_extinction_tx", &extinction_tx_);
integration_ps_.bind_resources(inst_.sampling);
integration_ps_.bind_texture("in_scattering_tx", &scatter_tx_.current());
integration_ps_.bind_texture("in_extinction_tx", &extinction_tx_.current());
integration_ps_.bind_image("out_scattering_img", &integrated_scatter_tx_);
integration_ps_.bind_image("out_transmittance_img", &integrated_transmit_tx_);
/* Sync with the scatter pass. */
@ -243,15 +263,44 @@ void VolumeModule::draw_prepass(View &view)
inst_.pipelines.world_volume.render(view);
float left, right, bottom, top, near, far;
float4x4 winmat = view.winmat();
projmat_dimensions(winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
const float4x4 winmat_view = view.winmat();
projmat_dimensions(winmat_view.ptr(), &left, &right, &bottom, &top, &near, &far);
float4x4 winmat_infinite = view.is_persp() ?
math::projection::perspective_infinite(
left, right, bottom, top, near) :
math::projection::orthographic_infinite(left, right, bottom, top);
/* Just like up-sampling matrix computation, we have to be careful to where to put the bounds of
* our froxel volume so that a 2D pixel covers exactly the number of pixel in a tile. */
float2 render_size = float2(right - left, top - bottom);
float2 volume_size = render_size * float2(data_.tex_size.xy() * data_.tile_size) /
float2(inst_.film.render_extent_get());
/* Change to the padded extends. */
right = left + volume_size.x;
top = bottom + volume_size.y;
/* TODO(fclem): These new matrices are created from the jittered main view matrix. It should be
* better to create them from the non-jittered one to avoid over-blurring. */
float4x4 winmat_infinite, winmat_finite;
/* Create an infinite projection matrix to avoid far clipping plane clipping the object. This
* way, surfaces that are further away than the far clip plane will still be voxelized.*/
winmat_infinite = view.is_persp() ?
math::projection::perspective_infinite(left, right, bottom, top, near) :
math::projection::orthographic_infinite(left, right, bottom, top);
/* We still need a bounded projection matrix to get correct froxel location. */
winmat_finite = view.is_persp() ?
math::projection::perspective(left, right, bottom, top, near, far) :
math::projection::orthographic(left, right, bottom, top, near, far);
/* Anti-Aliasing / Super-Sampling jitter. */
float2 jitter = inst_.sampling.rng_2d_get(SAMPLING_VOLUME_U);
/* Wrap to keep first sample centered (0,0) and scale to convert to NDC. */
jitter = math::fract(jitter + 0.5f) * 2.0f - 1.0f;
/* Convert to pixel size. */
jitter *= data_.inv_tex_size.xy();
/* Apply jitter to both matrices. */
winmat_infinite = math::projection::translate(winmat_infinite, jitter);
winmat_finite = math::projection::translate(winmat_finite, jitter);
data_.winmat_finite = winmat_finite;
data_.wininv_finite = math::invert(winmat_finite);
inst_.uniform_data.push_update();
View volume_view = {"Volume View"};
volume_view.sync(view.viewmat(), winmat_infinite);
if (inst_.pipelines.volume.is_enabled()) {
@ -266,6 +315,8 @@ void VolumeModule::draw_compute(View &view)
if (!enabled_) {
return;
}
scatter_tx_.swap();
extinction_tx_.swap();
inst_.manager->submit(scatter_ps_, view);
inst_.manager->submit(integration_ps_, view);

View File

@ -68,7 +68,7 @@ class VolumeModule {
*/
Texture hit_count_tx_ = {"hit_count_tx"};
Texture hit_depth_tx_ = {"hit_depth_tx"};
/** Empty frame-buffer for occupancy pass. */
Texture front_depth_tx_ = {"front_depth_tx"};
Framebuffer occupancy_fb_ = {"occupancy_fb"};
/* Material Parameters */
@ -79,8 +79,8 @@ class VolumeModule {
/* Light Scattering. */
PassSimple scatter_ps_ = {"Volumes.Scatter"};
Texture scatter_tx_;
Texture extinction_tx_;
SwapChain<Texture, 2> scatter_tx_;
SwapChain<Texture, 2> extinction_tx_;
/* Volume Integration */
PassSimple integration_ps_ = {"Volumes.Integration"};
@ -94,6 +94,8 @@ class VolumeModule {
Texture dummy_scatter_tx_;
Texture dummy_transmit_tx_;
View volume_view = {"Volume View"};
public:
VolumeModule(Instance &inst, VolumesInfoData &data) : inst_(inst), data_(data)
{

View File

@ -13,7 +13,88 @@
#define EEVEE_ATTRIBUTE_LIB
#if defined(MAT_GEOM_MESH)
/* All attributes are loaded in order. This allow us to use a global counter to retrieve the
* correct grid xform. */
/* TODO(fclem): This is very dangerous as it requires a reset for each time `attrib_load` is
* called. Instead, the right attribute index should be passed to attr_load_* functions. */
int g_attr_id = 0;
/* Point clouds and curves are not compatible with volume grids.
* They will fallback to their own attributes loading. */
#if defined(MAT_VOLUME) && !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINT_CLOUD)
# if defined(OBINFO_LIB) && !defined(MAT_GEOM_WORLD)
# define GRID_ATTRIBUTES
# endif
/* -------------------------------------------------------------------- */
/** \name Volume
*
* Volume objects loads attributes from "grids" in the form of 3D textures.
* Per grid transform order is following loading order.
* \{ */
# ifdef GRID_ATTRIBUTES
vec3 g_lP = vec3(0.0);
# else
vec3 g_wP = vec3(0.0);
# endif
vec3 grid_coordinates()
{
# ifdef GRID_ATTRIBUTES
vec3 co = (drw_volume.grids_xform[g_attr_id] * vec4(g_lP, 1.0)).xyz;
# else
/* Only for test shaders. All the runtime shaders require `draw_object_infos` and
* `draw_volume_infos`. */
vec3 co = vec3(0.0);
# endif
g_attr_id += 1;
return co;
}
vec3 attr_load_orco(sampler3D tex)
{
g_attr_id += 1;
# ifdef GRID_ATTRIBUTES
return OrcoTexCoFactors[0].xyz + g_lP * OrcoTexCoFactors[1].xyz;
# else
return g_wP;
# endif
}
vec4 attr_load_tangent(sampler3D tex)
{
g_attr_id += 1;
return vec4(0);
}
vec4 attr_load_vec4(sampler3D tex)
{
return texture(tex, grid_coordinates());
}
vec3 attr_load_vec3(sampler3D tex)
{
return texture(tex, grid_coordinates()).rgb;
}
vec2 attr_load_vec2(sampler3D tex)
{
return texture(tex, grid_coordinates()).rg;
}
float attr_load_float(sampler3D tex)
{
return texture(tex, grid_coordinates()).r;
}
vec4 attr_load_color(sampler3D tex)
{
return texture(tex, grid_coordinates());
}
vec3 attr_load_uv(sampler3D attr)
{
g_attr_id += 1;
return vec3(0);
}
/** \} */
#elif defined(MAT_GEOM_MESH)
/* -------------------------------------------------------------------- */
/** \name Mesh
@ -233,67 +314,6 @@ float attr_load_float(samplerBuffer cd_buf)
/** \} */
#elif defined(MAT_GEOM_VOLUME) || defined(MAT_GEOM_VOLUME_OBJECT) || defined(MAT_GEOM_VOLUME_WORLD)
/* -------------------------------------------------------------------- */
/** \name Volume
*
* Volume objects loads attributes from "grids" in the form of 3D textures.
* Per grid transform order is following loading order.
* \{ */
vec3 g_lP = vec3(0.0);
vec3 g_orco = vec3(0.0);
int g_attr_id = 0;
vec3 grid_coordinates()
{
vec3 co = g_orco;
# ifdef MAT_GEOM_VOLUME_OBJECT
co = (drw_volume.grids_xform[g_attr_id] * vec4(g_lP, 1.0)).xyz;
# endif
g_attr_id += 1;
return co;
}
vec3 attr_load_orco(sampler3D tex)
{
g_attr_id += 1;
return g_orco;
}
vec4 attr_load_tangent(sampler3D tex)
{
g_attr_id += 1;
return vec4(0);
}
vec4 attr_load_vec4(sampler3D tex)
{
return texture(tex, grid_coordinates());
}
vec3 attr_load_vec3(sampler3D tex)
{
return texture(tex, grid_coordinates()).rgb;
}
vec2 attr_load_vec2(sampler3D tex)
{
return texture(tex, grid_coordinates()).rg;
}
float attr_load_float(sampler3D tex)
{
return texture(tex, grid_coordinates()).r;
}
vec4 attr_load_color(sampler3D tex)
{
return texture(tex, grid_coordinates());
}
vec3 attr_load_uv(sampler3D attr)
{
g_attr_id += 1;
return vec3(0);
}
/** \} */
#elif defined(MAT_GEOM_WORLD)
/* -------------------------------------------------------------------- */

View File

@ -388,7 +388,7 @@ float ambient_occlusion_eval(vec3 normal,
#ifndef GPU_METAL
void attrib_load();
Closure nodetree_surface(float closure_rand);
/* Closure nodetree_volume(); */
Closure nodetree_volume();
vec3 nodetree_displacement();
float nodetree_thickness();
vec4 closure_to_rgba(Closure cl);
@ -713,39 +713,35 @@ float film_scaling_factor_get()
*
* \{ */
#if defined(MAT_GEOM_VOLUME_OBJECT) || defined(MAT_GEOM_VOLUME_WORLD)
/* Point clouds and curves are not compatible with volume grids.
* They will fallback to their own attributes loading. */
#if defined(MAT_VOLUME) && !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINT_CLOUD)
# if defined(OBINFO_LIB) && !defined(MAT_GEOM_WORLD)
/* We could just check for GRID_ATTRIBUTES but this avoids for header dependency. */
# define GRID_ATTRIBUTES_LOAD_POST
# endif
#endif
float attr_load_temperature_post(float attr)
{
# ifdef MAT_GEOM_VOLUME_OBJECT
#ifdef GRID_ATTRIBUTES_LOAD_POST
/* Bring the into standard range without having to modify the grid values */
attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
# endif
#endif
return attr;
}
vec4 attr_load_color_post(vec4 attr)
{
# ifdef MAT_GEOM_VOLUME_OBJECT
#ifdef GRID_ATTRIBUTES_LOAD_POST
/* Density is premultiplied for interpolation, divide it out here. */
attr.rgb *= safe_rcp(attr.a);
attr.rgb *= drw_volume.color_mul.rgb;
attr.a = 1.0;
# endif
return attr;
}
#else /* NOP for any other surface. */
float attr_load_temperature_post(float attr)
{
return attr;
}
vec4 attr_load_color_post(vec4 attr)
{
return attr;
}
#endif
return attr;
}
#undef GRID_ATTRIBUTES_LOAD_POST
/** \} */

View File

@ -28,18 +28,21 @@ void main()
return;
}
vec3 jitter = sampling_rng_3D_get(SAMPLING_VOLUME_U);
vec3 volume_ndc = volume_to_screen((vec3(froxel) + jitter) * uniform_buf.volumes.inv_tex_size);
vec3 vP = drw_point_screen_to_view(vec3(volume_ndc.xy, volume_ndc.z));
float offset = sampling_rng_1D_get(SAMPLING_VOLUME_W);
float jitter = interlieved_gradient_noise(vec2(froxel.xy), 0.0, offset);
vec3 uvw = (vec3(froxel) + vec3(0.5, 0.5, jitter)) * uniform_buf.volumes.inv_tex_size;
vec3 ss_P = volume_resolve_to_screen(uvw);
vec3 vP = drw_point_screen_to_view(vec3(ss_P.xy, ss_P.z));
vec3 P = drw_point_view_to_world(vP);
float depth = texelFetch(hiz_tx, froxel.xy, uniform_buf.volumes.tile_size_lod).r;
if (depth < volume_ndc.z) {
if (depth < ss_P.z) {
return;
}
vec2 pixel = (vec2(froxel.xy) + vec2(0.5)) / vec2(uniform_buf.volumes.tex_size.xy) /
uniform_buf.volumes.viewport_size_inv;
vec2 pixel = ((vec2(froxel.xy) + 0.5) * uniform_buf.volumes.inv_tex_size.xy) *
uniform_buf.volumes.main_view_extent;
int bias = uniform_buf.volumes.tile_size_lod;
shadow_tag_usage(vP, P, drw_world_incident_vector(P), 0.01, length(vP), pixel, bias);

View File

@ -53,7 +53,7 @@ void main()
forward_lighting_eval(g_thickness, radiance, transmittance);
/* Volumetric resolve and compositing. */
vec2 uvs = gl_FragCoord.xy * uniform_buf.volumes.viewport_size_inv;
vec2 uvs = gl_FragCoord.xy * uniform_buf.volumes.main_view_extent_inv;
VolumeResolveSample vol = volume_resolve(
vec3(uvs, gl_FragCoord.z), volume_transmittance_tx, volume_scattering_tx);
/* Removes the part of the volume scattering that has

View File

@ -46,11 +46,10 @@ void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
float vPz = dot(drw_view_forward(), interp.P) - dot(drw_view_forward(), drw_view_position());
/* Apply jitter here instead of modifying the projection matrix.
* This is because the depth range and mapping function changes. */
/* TODO(fclem): Jitter the camera for the other 2 dimension. */
float jitter = sampling_rng_1D_get(SAMPLING_VOLUME_W) * uniform_buf.volumes.inv_tex_size.z;
float volume_z = view_z_to_volume_z(vPz) - jitter;
float offset = sampling_rng_1D_get(SAMPLING_VOLUME_W);
float jitter = volume_froxel_jitter(texel, offset) * uniform_buf.volumes.inv_tex_size.z;
float volume_z = view_z_to_volume_z(vPz) + jitter;
if (use_fast_method) {
OccupancyBits occupancy_bits = occupancy_from_depth(volume_z, uniform_buf.volumes.tex_size.z);

View File

@ -0,0 +1,145 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
/* Store volumetric properties into the froxel textures. */
#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
/* Needed includes for shader nodes. */
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_occupancy_lib.glsl)
GlobalData init_globals(vec3 wP)
{
GlobalData surf;
surf.P = wP;
surf.N = vec3(0.0);
surf.Ng = vec3(0.0);
surf.is_strand = false;
surf.hair_time = 0.0;
surf.hair_thickness = 0.0;
surf.hair_strand_id = 0;
surf.barycentric_coords = vec2(0.0);
surf.barycentric_dists = vec3(0.0);
surf.ray_type = RAY_TYPE_CAMERA;
surf.ray_depth = 0.0;
surf.ray_length = distance(surf.P, drw_view_position());
return surf;
}
struct VolumeProperties {
vec3 scattering;
vec3 absorption;
vec3 emission;
float anisotropy;
};
VolumeProperties eval_froxel(ivec3 froxel, float jitter)
{
vec3 uvw = (vec3(froxel) + vec3(0.5, 0.5, 0.5 - jitter)) * uniform_buf.volumes.inv_tex_size;
vec3 vP = volume_jitter_to_view(uvw);
vec3 wP = point_view_to_world(vP);
#if !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINT_CLOUD)
# ifdef GRID_ATTRIBUTES
g_lP = point_world_to_object(wP);
# else
g_wP = wP;
# endif
/* TODO(fclem): This is very dangerous as it requires a reset for each time `attrib_load` is
* called. Instead, the right attribute index should be passed to attr_load_* functions. */
g_attr_id = 0;
#endif
g_data = init_globals(wP);
attrib_load();
nodetree_volume();
#if defined(MAT_GEOM_VOLUME)
g_volume_scattering *= drw_volume.density_scale;
g_volume_absorption *= drw_volume.density_scale;
g_emission *= drw_volume.density_scale;
#endif
VolumeProperties prop;
prop.scattering = g_volume_scattering;
prop.absorption = g_volume_absorption;
prop.emission = g_emission;
prop.anisotropy = g_volume_anisotropy;
return prop;
}
void write_froxel(ivec3 froxel, VolumeProperties prop)
{
vec2 phase = vec2(prop.anisotropy, 1.0);
/* Do not add phase weight if there's no scattering. */
if (all(equal(prop.scattering, vec3(0.0)))) {
phase = vec2(0.0);
}
vec3 extinction = prop.scattering + prop.absorption;
#ifndef MAT_GEOM_WORLD
/* Additive Blending. No race condition since we have a barrier between each conflicting
* invocations. */
prop.scattering += imageLoad(out_scattering_img, froxel).rgb;
prop.emission += imageLoad(out_emissive_img, froxel).rgb;
extinction += imageLoad(out_extinction_img, froxel).rgb;
phase += imageLoad(out_phase_img, froxel).rg;
#endif
imageStore(out_scattering_img, froxel, prop.scattering.xyzz);
imageStore(out_extinction_img, froxel, extinction.xyzz);
imageStore(out_emissive_img, froxel, prop.emission.xyzz);
imageStore(out_phase_img, froxel, phase.xyyy);
}
void main()
{
ivec3 froxel = ivec3(ivec2(gl_FragCoord.xy), 0);
float offset = sampling_rng_1D_get(SAMPLING_VOLUME_W);
float jitter = volume_froxel_jitter(froxel.xy, offset);
#ifdef VOLUME_HOMOGENOUS
/* Homogenous volumes only evaluate properties at volume entrance and write the same values for
* each froxel. */
VolumeProperties prop = eval_froxel(froxel, jitter);
#endif
#ifndef MAT_GEOM_WORLD
OccupancyBits occupancy;
for (int j = 0; j < 8; j++) {
occupancy.bits[j] = imageLoad(occupancy_img, ivec3(froxel.xy, j)).r;
}
#endif
/* Check all occupancy bits. */
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 32; i++) {
froxel.z = j * 32 + i;
if (froxel.z >= imageSize(out_scattering_img).z) {
break;
}
#ifndef MAT_GEOM_WORLD
if (((occupancy.bits[j] >> i) & 1u) == 0) {
continue;
}
#endif
#ifndef VOLUME_HOMOGENOUS
/* Heterogenous volumes evaluate properties at every froxel position. */
VolumeProperties prop = eval_froxel(froxel, jitter);
#endif
write_froxel(froxel, prop);
}
}
}

View File

@ -14,9 +14,8 @@
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec3 tex_size = uniform_buf.volumes.tex_size;
if (any(greaterThanEqual(texel, tex_size.xy))) {
if (any(greaterThanEqual(texel, uniform_buf.volumes.tex_size.xy))) {
return;
}
@ -24,16 +23,14 @@ void main()
vec3 scattering = vec3(0.0);
vec3 transmittance = vec3(1.0);
/* Compute view ray. */
vec2 uvs = (vec2(texel) + vec2(0.5)) / vec2(tex_size.xy);
vec3 ss_cell = volume_to_screen(vec3(uvs, 1e-5));
vec3 view_cell = drw_point_screen_to_view(ss_cell);
/* Compute view ray. Note that jittering the position of the first voxel doesn't bring any
* benefit here. */
vec3 uvw = (vec3(vec2(texel), 0.0) + vec3(0.5, 0.5, 0.0)) * uniform_buf.volumes.inv_tex_size;
vec3 view_cell = volume_jitter_to_view(uvw);
float prev_ray_len;
float orig_ray_len;
bool is_persp = ProjectionMatrix[3][3] == 0.0;
if (is_persp) {
if (drw_view_is_perspective()) {
prev_ray_len = length(view_cell);
orig_ray_len = prev_ray_len / view_cell.z;
}
@ -42,13 +39,13 @@ void main()
orig_ray_len = 1.0;
}
for (int i = 0; i <= tex_size.z; i++) {
for (int i = 0; i <= uniform_buf.volumes.tex_size.z; i++) {
ivec3 froxel = ivec3(texel, i);
vec3 froxel_scattering = texelFetch(in_scattering_tx, froxel, 0).rgb;
vec3 extinction = texelFetch(in_extinction_tx, froxel, 0).rgb;
float cell_depth = volume_z_to_view_z((float(i) + 1.0) / tex_size.z);
float cell_depth = volume_z_to_view_z((float(i) + 1.0) * uniform_buf.volumes.inv_tex_size.z);
float ray_len = orig_ray_len * cell_depth;
/* Evaluate Scattering. */

View File

@ -7,6 +7,7 @@
* - uniform_buf.volumes
*/
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
@ -15,44 +16,109 @@
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
/* Volume slice to view space depth. */
/* Per froxel jitter to break slices and flickering.
* Wrapped so that changing it is easier. */
float volume_froxel_jitter(ivec2 froxel, float offset)
{
return interlieved_gradient_noise(vec2(froxel), 0.0, offset);
}
/* Volume froxel texture normalized linear Z to view space Z.
* Not dependent on projection matrix (as long as drw_view_is_perspective is consistent). */
float volume_z_to_view_z(float z)
{
float near = uniform_buf.volumes.depth_near;
float far = uniform_buf.volumes.depth_far;
float distribution = uniform_buf.volumes.depth_distribution;
bool is_persp = ProjectionMatrix[3][3] == 0.0;
/* Implemented in eevee_shader_shared.cc */
return volume_z_to_view_z(near, far, distribution, is_persp, z);
if (drw_view_is_perspective()) {
/* Exponential distribution. */
return (exp2(z / distribution) - near) / far;
}
else {
/* Linear distribution. */
return near + (far - near) * z;
}
}
/* View space Z to volume froxel texture normalized linear Z.
* Not dependent on projection matrix (as long as drw_view_is_perspective is consistent). */
float view_z_to_volume_z(float depth)
{
float near = uniform_buf.volumes.depth_near;
float far = uniform_buf.volumes.depth_far;
float distribution = uniform_buf.volumes.depth_distribution;
bool is_persp = ProjectionMatrix[3][3] == 0.0;
/* Implemented in eevee_shader_shared.cc */
return view_z_to_volume_z(near, far, distribution, is_persp, depth);
if (drw_view_is_perspective()) {
/* Exponential distribution. */
return distribution * log2(depth * far + near);
}
else {
/* Linear distribution. */
return (depth - near) / (far - near);
}
}
/* Volume texture normalized coordinates to screen UVs (special range [0, 1]). */
vec3 volume_to_screen(vec3 coord)
/* Jittered volume texture normalized coordinates to view space position. */
vec3 volume_jitter_to_view(vec3 coord)
{
/* Since we use an infinite projection matrix for rendering inside the jittered volumes,
* we need to use a different matrix to reconstruct positions as the infinite matrix is not
* always invertible. */
mat4x4 winmat = uniform_buf.volumes.winmat_finite;
mat4x4 wininv = uniform_buf.volumes.wininv_finite;
/* Input coordinates are in jittered volume texture space. */
float view_z = volume_z_to_view_z(coord.z);
/* We need to recover the NDC position for correct perspective divide. */
float ndc_z = drw_perspective_divide(winmat * vec4(0.0, 0.0, view_z, 1.0)).z;
vec2 ndc_xy = drw_screen_to_ndc(coord.xy);
/* NDC to view. */
return drw_perspective_divide(wininv * vec4(ndc_xy, ndc_z, 1.0)).xyz;
}
/* View space position to jittered volume texture normalized coordinates. */
vec3 volume_view_to_jitter(vec3 vP)
{
/* Since we use an infinite projection matrix for rendering inside the jittered volumes,
* we need to use a different matrix to reconstruct positions as the infinite matrix is not
* always invertible. */
mat4x4 winmat = uniform_buf.volumes.winmat_finite;
/* View to ndc. */
vec3 ndc_P = drw_perspective_divide(winmat * vec4(vP, 1.0));
/* Here, screen is the same as volume texture UVW space. */
return vec3(drw_ndc_to_screen(ndc_P.xy), view_z_to_volume_z(vP.z));
}
/* Volume texture normalized coordinates (UVW) to render screen (UV).
* Expect active view to be the main view. */
vec3 volume_resolve_to_screen(vec3 coord)
{
coord.z = volume_z_to_view_z(coord.z);
coord.z = drw_depth_view_to_screen(coord.z);
coord.xy /= uniform_buf.volumes.coord_scale;
return coord;
}
vec3 screen_to_volume(vec3 coord)
/* Render screen (UV) to volume texture normalized coordinates (UVW).
* Expect active view to be the main view. */
vec3 volume_screen_to_resolve(vec3 coord)
{
float near = uniform_buf.volumes.depth_near;
float far = uniform_buf.volumes.depth_far;
float distribution = uniform_buf.volumes.depth_distribution;
vec2 coord_scale = uniform_buf.volumes.coord_scale;
/* Implemented in eevee_shader_shared.cc */
return screen_to_volume(ProjectionMatrix, near, far, distribution, coord_scale, coord);
coord.xy *= uniform_buf.volumes.coord_scale;
coord.z = drw_depth_screen_to_view(coord.z);
coord.z = view_z_to_volume_z(coord.z);
return coord;
}
/* Returns the uvw (normalized coordinate) of a froxel in the previous frame.
* If no history exists, it will return out of bounds sampling coordinates. */
vec3 volume_history_position_get(ivec3 froxel)
{
/* We can't reproject by a simple matrix multiplication. We first need to remap to the view Z,
* then transform, then remap back to Volume range. */
vec3 uvw = (vec3(froxel) + 0.5) * uniform_buf.volumes.inv_tex_size;
uvw.z = volume_z_to_view_z(uvw.z);
vec3 uvw_history = transform_point(uniform_buf.volumes.history_matrix, uvw);
/* TODO(fclem): For now assume same distribution settings. */
uvw_history.z = view_z_to_volume_z(uvw_history.z);
return uvw_history;
}
float volume_phase_function_isotropic()
@ -155,10 +221,10 @@ vec3 volume_shadow(
for (float t = 1.0; t < VOLUMETRIC_SHADOW_MAX_STEP && t <= uniform_buf.volumes.shadow_steps;
t += 1.0)
{
vec3 pos = P + L * t;
vec3 w_pos = P + L * t;
vec3 ndc = drw_point_world_to_ndc(pos);
vec3 volume_co = screen_to_volume(drw_ndc_to_screen(ndc));
vec3 v_pos = drw_point_world_to_view(w_pos);
vec3 volume_co = volume_view_to_jitter(v_pos);
/* Let the texture be clamped to edge. This reduce visual glitches. */
vec3 s_extinction = texture(extinction_tx, volume_co).rgb;
@ -177,7 +243,13 @@ struct VolumeResolveSample {
VolumeResolveSample volume_resolve(vec3 ndc_P, sampler3D transmittance_tx, sampler3D scattering_tx)
{
vec3 coord = screen_to_volume(ndc_P);
vec3 coord = volume_screen_to_resolve(ndc_P);
/* Volumes objects have the same aliasing problems has shadow maps.
* To fix this we need a quantization bias (the size of a step in Z) and a slope bias
* (multiplied by the size of a froxel in 2D). */
coord.z -= uniform_buf.volumes.inv_tex_size.z;
/* TODO(fclem): Slope bias. */
VolumeResolveSample volume;
volume.scattering = texture(scattering_tx, coord).rgb;

View File

@ -1,120 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(eevee_volume_lib.glsl)
/* Needed includes for shader nodes. */
#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_attributes_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_occupancy_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
/* Store volumetric properties into the froxel textures. */
GlobalData init_globals(vec3 wP)
{
GlobalData surf;
surf.P = wP;
surf.N = vec3(0.0);
surf.Ng = vec3(0.0);
surf.is_strand = false;
surf.hair_time = 0.0;
surf.hair_thickness = 0.0;
surf.hair_strand_id = 0;
surf.barycentric_coords = vec2(0.0);
surf.barycentric_dists = vec3(0.0);
surf.ray_type = RAY_TYPE_CAMERA;
surf.ray_depth = 0.0;
surf.ray_length = distance(surf.P, drw_view_position());
return surf;
}
#ifndef GPU_METAL
Closure nodetree_volume();
void attrib_load();
#endif
void main()
{
ivec3 froxel = ivec3(gl_GlobalInvocationID);
#ifdef MAT_GEOM_VOLUME_OBJECT
froxel += grid_coords_min;
#endif
if (any(greaterThanEqual(froxel, uniform_buf.volumes.tex_size))) {
return;
}
#ifdef MAT_GEOM_VOLUME_OBJECT
/** Check occupancy map. Discard thread if froxel is empty. */
/* Shift for 32bits per layer. Avoid integer modulo and division. */
const int shift = 5;
const int mask = int(~(0xFFFFFFFFu << 5u));
/* Divide by 32. */
int occupancy_layer = froxel.z >> shift;
/* Modulo 32. */
uint occupancy_shift = froxel.z & mask;
uint occupancy_bits = imageLoad(occupancy_img, ivec3(froxel.xy, occupancy_layer)).r;
if (((occupancy_bits >> occupancy_shift) & 1u) == 0u) {
return;
}
#endif
vec3 jitter = sampling_rng_3D_get(SAMPLING_VOLUME_U);
vec3 ndc_cell = volume_to_screen((vec3(froxel) + jitter) * uniform_buf.volumes.inv_tex_size);
vec3 vP = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
vec3 wP = point_view_to_world(vP);
#ifdef MAT_GEOM_VOLUME_OBJECT
g_lP = point_world_to_object(wP);
g_orco = OrcoTexCoFactors[0].xyz + g_lP * OrcoTexCoFactors[1].xyz;
if (any(lessThan(g_orco, vec3(0.0))) || any(greaterThan(g_orco, vec3(1.0)))) {
return;
}
#else /* WORLD_SHADER */
g_orco = wP;
#endif
g_data = init_globals(wP);
attrib_load();
nodetree_volume();
vec3 scattering = g_volume_scattering;
float anisotropy = g_volume_anisotropy;
vec3 absorption = g_volume_absorption;
vec3 emission = g_emission;
vec2 phase = vec2(anisotropy, 1.0);
#ifdef MAT_GEOM_VOLUME_OBJECT
scattering *= drw_volume.density_scale;
absorption *= drw_volume.density_scale;
emission *= drw_volume.density_scale;
#endif
vec3 extinction = scattering + absorption;
/* Do not add phase weight if there's no scattering. */
if (all(equal(scattering, vec3(0.0)))) {
phase = vec2(0.0);
}
#ifdef MAT_GEOM_VOLUME_OBJECT
/* Additive Blending.
* No race condition since each invocation only handles its own froxel. */
scattering += imageLoad(out_scattering_img, froxel).rgb;
extinction += imageLoad(out_extinction_img, froxel).rgb;
emission += imageLoad(out_emissive_img, froxel).rgb;
phase += imageLoad(out_phase_img, froxel).rg;
#endif
imageStore(out_scattering_img, froxel, vec4(scattering, 1.0));
imageStore(out_extinction_img, froxel, vec4(extinction, 1.0));
imageStore(out_emissive_img, froxel, vec4(emission, 1.0));
imageStore(out_phase_img, froxel, vec4(phase, vec2(1.0)));
}

View File

@ -11,7 +11,7 @@
void main()
{
vec2 uvs = gl_FragCoord.xy * uniform_buf.volumes.viewport_size_inv;
vec2 uvs = gl_FragCoord.xy * uniform_buf.volumes.main_view_extent_inv;
float scene_depth = texelFetch(hiz_tx, ivec2(gl_FragCoord.xy), 0).r;
VolumeResolveSample vol = volume_resolve(

View File

@ -66,10 +66,10 @@ void main()
vec3 extinction = imageLoad(in_extinction_img, froxel).rgb;
vec3 s_scattering = imageLoad(in_scattering_img, froxel).rgb;
vec3 jitter = sampling_rng_3D_get(SAMPLING_VOLUME_U);
vec3 volume_screen = volume_to_screen((vec3(froxel) + jitter) *
uniform_buf.volumes.inv_tex_size);
vec3 vP = drw_point_screen_to_view(volume_screen);
float offset = sampling_rng_1D_get(SAMPLING_VOLUME_W);
float jitter = volume_froxel_jitter(froxel.xy, offset);
vec3 uvw = (vec3(froxel) + vec3(0.5, 0.5, 0.5 - jitter)) * uniform_buf.volumes.inv_tex_size;
vec3 vP = volume_jitter_to_view(uvw);
vec3 P = drw_point_view_to_world(vP);
vec3 V = drw_world_incident_vector(P);
@ -88,8 +88,8 @@ void main()
}
LIGHT_FOREACH_END
vec2 pixel = (vec2(froxel.xy) + vec2(0.5)) / vec2(uniform_buf.volumes.tex_size.xy) /
uniform_buf.volumes.viewport_size_inv;
vec2 pixel = ((vec2(froxel.xy) + 0.5) * uniform_buf.volumes.inv_tex_size.xy) *
uniform_buf.volumes.main_view_extent;
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
light_scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy);
@ -99,6 +99,18 @@ void main()
scattering += light_scattering * s_scattering;
#endif
#if 0 /* TODO */
{
/* Temporal reprojection. */
vec3 uvw_history = volume_history_position_get(froxel);
vec4 scattering_history = texture(scattering_history_tx, uvw_history);
vec4 extinction_history = texture(extinction_history_tx, uvw_history);
float history_opacity = 0.95 * scattering_history.a;
scattering = mix(scattering, scattering_history.rgb, history_opacity);
extinction = mix(extinction, extinction_history.rgb, history_opacity);
}
#endif
/* Catch NaNs. */
if (any(isnan(scattering)) || any(isnan(extinction))) {
scattering = vec3(0.0);

View File

@ -87,6 +87,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_volume)
.additional_info("draw_modelmat_new",
"draw_object_infos_new",
"draw_resource_id_varying",
"draw_volume_infos",
"draw_view");
GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
@ -279,22 +280,10 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow_tbdr)
/** \name Volume
* \{ */
GPU_SHADER_CREATE_INFO(eevee_volume_material_common)
.compute_source("eevee_volume_material_comp.glsl")
.local_group_size(VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE)
.define("VOLUMETRICS")
.additional_info("draw_modelmat_new_common",
/* TODO(fclem): Legacy API. To remove. */
"draw_resource_id_uniform",
"draw_view",
"eevee_shared",
"eevee_global_ubo",
"eevee_sampling_data",
"eevee_utility_texture");
GPU_SHADER_CREATE_INFO(eevee_volume_object)
.define("MAT_GEOM_VOLUME_OBJECT")
.push_constant(Type::IVEC3, "grid_coords_min")
GPU_SHADER_CREATE_INFO(eevee_surf_volume)
.define("MAT_VOLUME")
/* Only the front fragments have to be invoked. */
.early_fragment_test(true)
.image(VOLUME_PROP_SCATTERING_IMG_SLOT,
GPU_R11F_G11F_B10F,
Qualifier::READ_WRITE,
@ -320,34 +309,18 @@ GPU_SHADER_CREATE_INFO(eevee_volume_object)
Qualifier::READ,
ImageType::UINT_3D_ATOMIC,
"occupancy_img")
.additional_info("eevee_volume_material_common", "draw_object_infos_new", "draw_volume_infos");
GPU_SHADER_CREATE_INFO(eevee_volume_world)
.image(VOLUME_PROP_SCATTERING_IMG_SLOT,
GPU_R11F_G11F_B10F,
Qualifier::WRITE,
ImageType::FLOAT_3D,
"out_scattering_img")
.image(VOLUME_PROP_EXTINCTION_IMG_SLOT,
GPU_R11F_G11F_B10F,
Qualifier::WRITE,
ImageType::FLOAT_3D,
"out_extinction_img")
.image(VOLUME_PROP_EMISSION_IMG_SLOT,
GPU_R11F_G11F_B10F,
Qualifier::WRITE,
ImageType::FLOAT_3D,
"out_emissive_img")
.image(VOLUME_PROP_PHASE_IMG_SLOT,
GPU_RG16F,
Qualifier::WRITE,
ImageType::FLOAT_3D,
"out_phase_img")
.define("MAT_GEOM_VOLUME_WORLD")
.additional_info("eevee_volume_material_common");
.fragment_source("eevee_surf_volume_frag.glsl")
.additional_info("draw_modelmat_new_common",
"draw_view",
"eevee_shared",
"eevee_global_ubo",
"eevee_sampling_data",
"eevee_utility_texture");
GPU_SHADER_CREATE_INFO(eevee_surf_occupancy)
.define("MAT_OCCUPANCY")
/* All fragments need to be invoked even if we write to the depth buffer. */
.early_fragment_test(false)
.builtins(BuiltinBits::TEXTURE_ATOMIC)
.push_constant(Type::BOOL, "use_fast_method")
.image(VOLUME_HIT_DEPTH_SLOT, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_3D, "hit_depth_img")
@ -398,6 +371,7 @@ GPU_SHADER_CREATE_INFO(eevee_material_stub)
EEVEE_MAT_GEOM_VARIATIONS(name##_deferred, "eevee_surf_deferred", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_forward, "eevee_surf_forward", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_capture, "eevee_surf_capture", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_volume, "eevee_surf_volume", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_occupancy, "eevee_surf_occupancy", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_shadow_atomic, "eevee_surf_shadow_atomic", __VA_ARGS__) \
EEVEE_MAT_GEOM_VARIATIONS(name##_shadow_tbdr, "eevee_surf_shadow_tbdr", __VA_ARGS__)

View File

@ -36,6 +36,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_properties_data)
"in_phase_img");
GPU_SHADER_CREATE_INFO(eevee_volume_scatter)
.local_group_size(VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE)
.additional_info("eevee_shared")
.additional_info("eevee_global_ubo")
.additional_info("draw_resource_id_varying")
@ -45,11 +46,12 @@ GPU_SHADER_CREATE_INFO(eevee_volume_scatter)
.additional_info("eevee_shadow_data")
.additional_info("eevee_sampling_data")
.additional_info("eevee_utility_texture")
.compute_source("eevee_volume_scatter_comp.glsl")
.local_group_size(VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE)
.additional_info("eevee_volume_properties_data")
.sampler(0, ImageType::FLOAT_3D, "scattering_history_tx")
.sampler(1, ImageType::FLOAT_3D, "extinction_history_tx")
.image(4, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_3D, "out_scattering_img")
.image(5, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_3D, "out_extinction_img")
.compute_source("eevee_volume_scatter_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_volume_scatter_with_lights)
@ -57,7 +59,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_scatter_with_lights)
.define("VOLUME_LIGHTING")
.define("VOLUME_IRRADIANCE")
.define("VOLUME_SHADOW")
.sampler(0, ImageType::FLOAT_3D, "extinction_tx")
.sampler(9, ImageType::FLOAT_3D, "extinction_tx")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_volume_occupancy_convert)
@ -79,6 +81,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_occupancy_convert)
GPU_SHADER_CREATE_INFO(eevee_volume_integration)
.additional_info("eevee_shared", "eevee_global_ubo", "draw_view")
.additional_info("eevee_sampling_data")
.compute_source("eevee_volume_integration_comp.glsl")
.local_group_size(VOLUME_INTEGRATION_GROUP_SIZE, VOLUME_INTEGRATION_GROUP_SIZE, 1)
/* Inputs. */

View File

@ -533,9 +533,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
"hardness", bke::AttrDomain::Curve, 1.0f);
const VArray<float> stroke_point_aspect_ratios = *attributes.lookup_or_default<float>(
"aspect_ratio", bke::AttrDomain::Curve, 1.0f);
const VArray<ColorGeometry4f> stroke_fill_colors =
*attributes.lookup_or_default<ColorGeometry4f>(
"fill_color", bke::AttrDomain::Curve, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
const VArray<ColorGeometry4f> stroke_fill_colors = info.drawing.fill_colors();
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
const VArray<float> u_translations = *attributes.lookup_or_default<float>(

View File

@ -835,7 +835,7 @@ void ANIM_copy_as_driver(ID *target_id, const char *target_path, const char *var
/* Add Driver - Enum Defines ------------------------- */
EnumPropertyItem prop_driver_create_mapping_types[] = {
const EnumPropertyItem prop_driver_create_mapping_types[] = {
/* XXX: These names need reviewing. */
{CREATEDRIVER_MAPPING_1_N,
"SINGLE_MANY",
@ -873,7 +873,7 @@ static const EnumPropertyItem *driver_mapping_type_itemf(bContext *C,
PropertyRNA * /*owner_prop*/,
bool *r_free)
{
EnumPropertyItem *input = prop_driver_create_mapping_types;
const EnumPropertyItem *input = prop_driver_create_mapping_types;
EnumPropertyItem *item = nullptr;
PointerRNA ptr = {nullptr};

View File

@ -35,44 +35,42 @@
/* *************************** Keyframe Drawing *************************** */
void draw_keyframe_shape(float x,
float y,
void draw_keyframe_shape(const float x,
const float y,
float size,
bool sel,
short key_type,
short mode,
float alpha,
const bool sel,
const eBezTriple_KeyframeType key_type,
const short mode,
const float alpha,
const KeyframeShaderBindings *sh_bindings,
short handle_type,
short extreme_type)
const short handle_type,
const short extreme_type)
{
bool draw_fill = ELEM(mode, KEYFRAME_SHAPE_INSIDE, KEYFRAME_SHAPE_BOTH);
bool draw_outline = ELEM(mode, KEYFRAME_SHAPE_FRAME, KEYFRAME_SHAPE_BOTH);
BLI_assert(draw_fill || draw_outline);
/* tweak size of keyframe shape according to type of keyframe
* - 'proper' keyframes have key_type = 0, so get drawn at full size
*/
/* Adjust size of keyframe shape according to type of keyframe. */
switch (key_type) {
case BEZT_KEYTYPE_KEYFRAME: /* must be full size */
case BEZT_KEYTYPE_KEYFRAME:
break;
case BEZT_KEYTYPE_BREAKDOWN: /* slightly smaller than normal keyframe */
case BEZT_KEYTYPE_BREAKDOWN:
size *= 0.85f;
break;
case BEZT_KEYTYPE_MOVEHOLD: /* Slightly smaller than normal keyframes
* (but by less than for breakdowns). */
case BEZT_KEYTYPE_MOVEHOLD:
size *= 0.925f;
break;
case BEZT_KEYTYPE_EXTREME: /* slightly larger */
case BEZT_KEYTYPE_EXTREME:
size *= 1.2f;
break;
default:
size -= 0.8f * key_type;
case BEZT_KEYTYPE_JITTER:
size *= 0.8f;
break;
}
uchar fill_col[4];
@ -96,8 +94,8 @@ void draw_keyframe_shape(float x,
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_MOVEHOLD_SELECT : TH_KEYTYPE_MOVEHOLD, fill_col);
break;
case BEZT_KEYTYPE_KEYFRAME: /* traditional yellowish frames (default theme) */
default:
UI_GetThemeColor4ubv(sel ? TH_KEYTYPE_KEYFRAME_SELECT : TH_KEYTYPE_KEYFRAME, fill_col);
break;
}
/* NOTE: we don't use the straight alpha from the theme, or else effects such as
@ -236,7 +234,7 @@ static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx,
case BEZT_KEYTYPE_KEYFRAME:
size *= 0.8f;
break;
default:
case BEZT_KEYTYPE_EXTREME:
break;
}
@ -363,7 +361,7 @@ static void draw_keylist_keys(const DrawKeylistUIData *ctx,
ypos,
ctx->icon_size,
(ak->sel & SELECT),
ak->key_type,
eBezTriple_KeyframeType(ak->key_type),
KEYFRAME_SHAPE_BOTH,
ctx->alpha,
sh_bindings,

View File

@ -1371,7 +1371,7 @@ KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
static short set_keytype_keyframe(KeyframeEditData * /*ked*/, BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
BEZKEYTYPE(bezt) = BEZT_KEYTYPE_KEYFRAME;
BEZKEYTYPE_LVALUE(bezt) = BEZT_KEYTYPE_KEYFRAME;
}
return 0;
}
@ -1379,7 +1379,7 @@ static short set_keytype_keyframe(KeyframeEditData * /*ked*/, BezTriple *bezt)
static short set_keytype_breakdown(KeyframeEditData * /*ked*/, BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
BEZKEYTYPE(bezt) = BEZT_KEYTYPE_BREAKDOWN;
BEZKEYTYPE_LVALUE(bezt) = BEZT_KEYTYPE_BREAKDOWN;
}
return 0;
}
@ -1387,7 +1387,7 @@ static short set_keytype_breakdown(KeyframeEditData * /*ked*/, BezTriple *bezt)
static short set_keytype_extreme(KeyframeEditData * /*ked*/, BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
BEZKEYTYPE(bezt) = BEZT_KEYTYPE_EXTREME;
BEZKEYTYPE_LVALUE(bezt) = BEZT_KEYTYPE_EXTREME;
}
return 0;
}
@ -1395,7 +1395,7 @@ static short set_keytype_extreme(KeyframeEditData * /*ked*/, BezTriple *bezt)
static short set_keytype_jitter(KeyframeEditData * /*ked*/, BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
BEZKEYTYPE(bezt) = BEZT_KEYTYPE_JITTER;
BEZKEYTYPE_LVALUE(bezt) = BEZT_KEYTYPE_JITTER;
}
return 0;
}
@ -1403,30 +1403,32 @@ static short set_keytype_jitter(KeyframeEditData * /*ked*/, BezTriple *bezt)
static short set_keytype_moving_hold(KeyframeEditData * /*ked*/, BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
BEZKEYTYPE(bezt) = BEZT_KEYTYPE_MOVEHOLD;
BEZKEYTYPE_LVALUE(bezt) = BEZT_KEYTYPE_MOVEHOLD;
}
return 0;
}
KeyframeEditFunc ANIM_editkeyframes_keytype(short mode)
KeyframeEditFunc ANIM_editkeyframes_keytype(const eBezTriple_KeyframeType keyframe_type)
{
switch (mode) {
case BEZT_KEYTYPE_BREAKDOWN: /* breakdown */
switch (keyframe_type) {
case BEZT_KEYTYPE_BREAKDOWN:
return set_keytype_breakdown;
case BEZT_KEYTYPE_EXTREME: /* extreme keyframe */
case BEZT_KEYTYPE_EXTREME:
return set_keytype_extreme;
case BEZT_KEYTYPE_JITTER: /* jitter keyframe */
case BEZT_KEYTYPE_JITTER:
return set_keytype_jitter;
case BEZT_KEYTYPE_MOVEHOLD: /* moving hold */
case BEZT_KEYTYPE_MOVEHOLD:
return set_keytype_moving_hold;
case BEZT_KEYTYPE_KEYFRAME: /* proper keyframe */
default:
case BEZT_KEYTYPE_KEYFRAME:
return set_keytype_keyframe;
}
BLI_assert_unreachable();
return nullptr;
}
/* ------- */

View File

@ -518,7 +518,7 @@ static ActKeyColumn *nalloc_ak_cel(void *data)
/* Store settings based on state of BezTriple */
ak->cfra = cel.frame_number;
ak->sel = (cel.frame.flag & SELECT) != 0;
ak->key_type = cel.frame.type;
ak->key_type = eBezTriple_KeyframeType(cel.frame.type);
/* Count keyframes in this column */
ak->totkey = 1;
@ -559,7 +559,7 @@ static ActKeyColumn *nalloc_ak_gpframe(void *data)
/* store settings based on state of BezTriple */
ak->cfra = gpf->framenum;
ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
ak->key_type = gpf->key_type;
ak->key_type = eBezTriple_KeyframeType(gpf->key_type);
/* Count keyframes in this column. */
ak->totkey = 1;

View File

@ -65,6 +65,7 @@
#include "GEO_reverse_uv_sampler.hh"
#include "GEO_set_curve_type.hh"
#include "GEO_subdivide_curves.hh"
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
@ -1423,6 +1424,107 @@ static void CURVES_OT_curve_type_set(wmOperatorType *ot)
ot->srna, "type", rna_enum_curves_type_items, CURVE_TYPE_POLY, "Type", "Curve type");
}
namespace switch_direction {
static int exec(bContext *C, wmOperator * /*op*/)
{
for (Curves *curves_id : get_unique_editable_curves(*C)) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
IndexMaskMemory memory;
const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
if (selection.is_empty()) {
continue;
}
curves.reverse_curves(selection);
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
} // namespace switch_direction
static void CURVES_OT_switch_direction(wmOperatorType *ot)
{
ot->name = "Switch Direction";
ot->idname = __func__;
ot->description = "Reverse the direction of the selected curves";
ot->exec = switch_direction::exec;
ot->poll = editable_curves_in_edit_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
namespace subdivide {
static int exec(bContext *C, wmOperator *op)
{
const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
for (Curves *curves_id : get_unique_editable_curves(*C)) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const int points_num = curves.points_num();
IndexMaskMemory memory;
const IndexMask points_selection = retrieve_selected_points(*curves_id, memory);
if (points_selection.is_empty()) {
continue;
}
Array<bool> points_selection_span(points_num);
points_selection.to_bools(points_selection_span);
Array<int> segment_cuts(points_num, number_cuts);
const OffsetIndices points_by_curve = curves.points_by_curve();
threading::parallel_for(points_by_curve.index_range(), 512, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
if (points.size() <= 1) {
continue;
}
for (const int point_i : points.drop_back(1)) {
if (!points_selection_span[point_i] || !points_selection_span[point_i + 1]) {
segment_cuts[point_i] = 0;
}
}
/* Cyclic segment. Doesn't matter if it is computed even if the curve is not cyclic. */
if (!points_selection_span[points.last()] || !points_selection_span[points.first()]) {
segment_cuts[points.last()] = 0;
}
}
});
curves = geometry::subdivide_curves(
curves, curves.curves_range(), VArray<int>::ForSpan(segment_cuts), {});
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
}
return OPERATOR_FINISHED;
}
} // namespace subdivide
static void CURVES_OT_subdivide(wmOperatorType *ot)
{
ot->name = "Subdivide";
ot->idname = __func__;
ot->description = "Subdivide selected curve segments";
ot->exec = subdivide::exec;
ot->poll = editable_curves_in_edit_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
/* Avoid re-using last value because it can cause an unexpectedly high number of subdivisions. */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
void operatortypes_curves()
{
WM_operatortype_append(CURVES_OT_attribute_set);
@ -1444,6 +1546,8 @@ void operatortypes_curves()
WM_operatortype_append(CURVES_OT_tilt_clear);
WM_operatortype_append(CURVES_OT_cyclic_toggle);
WM_operatortype_append(CURVES_OT_curve_type_set);
WM_operatortype_append(CURVES_OT_switch_direction);
WM_operatortype_append(CURVES_OT_subdivide);
}
void operatormacros_curves()

View File

@ -10,6 +10,8 @@
#include "BLI_sys_types.h"
#include "DNA_curve_types.h"
struct AnimData;
struct ChannelDrawList;
struct FCurve;
@ -44,7 +46,7 @@ void draw_keyframe_shape(float x,
float y,
float size,
bool sel,
short key_type,
eBezTriple_KeyframeType key_type,
short mode,
float alpha,
const KeyframeShaderBindings *sh_bindings,

View File

@ -13,6 +13,8 @@
#include "ED_anim_api.hh" /* for enum eAnimFilter_Flags */
#include "DNA_curve_types.h"
struct BezTriple;
struct ButterworthCoefficients;
struct FCurve;
@ -355,7 +357,7 @@ KeyframeEditFunc ANIM_editkeyframes_handles(short mode);
* Set the interpolation type of the selected BezTriples in each F-Curve to the specified one.
*/
KeyframeEditFunc ANIM_editkeyframes_ipo(short mode);
KeyframeEditFunc ANIM_editkeyframes_keytype(short mode);
KeyframeEditFunc ANIM_editkeyframes_keytype(eBezTriple_KeyframeType keyframe_type);
KeyframeEditFunc ANIM_editkeyframes_easing(short mode);
/** \} */

View File

@ -11,6 +11,8 @@
#include "BLI_math_vector_types.hh"
#include "BLI_range.h"
#include "DNA_curve_types.h"
struct AnimData;
struct CacheFile;
struct FCurve;
@ -57,8 +59,7 @@ struct ActKeyColumn {
char tree_col;
/* keyframe info */
/** eBezTripe_KeyframeType */
char key_type;
eBezTriple_KeyframeType key_type;
/** eKeyframeHandleDrawOpts */
char handle_type;
/** eKeyframeExtremeDrawOpts */

View File

@ -299,7 +299,7 @@ enum eCreateDriver_MappingTypes {
* Mapping Types enum for operators.
* \note Used by #ANIM_OT_driver_button_add and #UI_OT_eyedropper_driver.
*/
extern EnumPropertyItem prop_driver_create_mapping_types[];
extern const EnumPropertyItem prop_driver_create_mapping_types[];
/* -------- */

View File

@ -184,9 +184,9 @@ enum eObClearParentTypes {
#ifdef __RNA_TYPES_H__
/** Operator Property: `OBJECT_OT_parent_clear`. */
extern EnumPropertyItem prop_clear_parent_types[];
extern const EnumPropertyItem prop_clear_parent_types[];
/** Operator Property: `OBJECT_OT_parent_set`. */
extern EnumPropertyItem prop_make_parent_types[];
extern const EnumPropertyItem prop_make_parent_types[];
#endif
/**

View File

@ -243,8 +243,13 @@ static void def_internal_vicon(int icon_id, VectorDrawFunc drawFunc)
/* Utilities */
static void vicon_keytype_draw_wrapper(
int x, int y, int w, int h, float alpha, short key_type, short handle_type)
static void vicon_keytype_draw_wrapper(const int x,
const int y,
const int w,
const int h,
const float alpha,
const eBezTriple_KeyframeType key_type,
const short handle_type)
{
/* Initialize dummy theme state for Action Editor - where these colors are defined
* (since we're doing this off-screen, free from any particular space_id). */

View File

@ -1582,8 +1582,8 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
* probably reduce this to one pixel if we consolidate text output with length measuring. But
* our text string lengths include the last character's right-side bearing anyway, so a string
* can be longer by that amount and still fit visibly in the required space. */
BLI_assert((strwidth <= (okwidth + 2)) || (okwidth <= 0.0f));
UNUSED_VARS_NDEBUG(okwidth);
return strwidth;
}
@ -2778,6 +2778,7 @@ static void widget_state_menu_item(uiWidgetType *wt,
/* Inactive. */
if (state->but_flag & UI_HOVER) {
color_blend_v3_v3(wt->wcol.inner, wt->wcol.text, 0.2f);
copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel);
wt->wcol.inner[3] = 255;
}
color_blend_v3_v3(wt->wcol.text, wt->wcol.inner, 0.5f);

View File

@ -895,8 +895,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
{
/* This disables the status bar text that is set when opening a menu. */
ED_workspace_status_text(C, nullptr);
bool is_submenu = false;
/* If this popup is created from a popover which does NOT have keep-open flag set,
* then close the popover too. We could extend this to other popup types too. */
@ -909,9 +908,18 @@ void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
uiPopupBlockHandle *menu = block->handle;
menu->menuretval = UI_RETURN_OK;
}
if (ui_block_is_menu(block)) {
is_submenu = true;
}
}
}
/* Clear the status bar text that is set when opening a menu. */
if (!is_submenu) {
ED_workspace_status_text(C, nullptr);
}
if (handle->popup_create_vars.arg_free) {
handle->popup_create_vars.arg_free(handle->popup_create_vars.arg);
}

View File

@ -300,8 +300,18 @@ void AbstractTreeViewItem::tree_row_click_fn(bContext *C, void *but_arg1, void *
void AbstractTreeViewItem::add_treerow_button(uiBlock &block)
{
/* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */
view_item_but_ = reinterpret_cast<uiButViewItem *>(uiDefBut(
&block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, ""));
view_item_but_ = reinterpret_cast<uiButViewItem *>(uiDefBut(&block,
UI_BTYPE_VIEW_ITEM,
0,
"",
0,
0,
UI_UNIT_X * 10,
padded_item_height(),
nullptr,
0,
0,
""));
view_item_but_->view_item = this;
view_item_but_->draw_height = unpadded_item_height();
@ -650,8 +660,6 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
if (!item.is_interactive_) {
uiLayoutSetActive(overlap, false);
}
/* Scale the layout for the padded height. Widgets will be vertically centered then. */
uiLayoutSetScaleY(overlap, float(padded_item_height()) / UI_UNIT_Y);
uiLayout *row = uiLayoutRow(overlap, false);
/* Enable emboss for mouse hover highlight. */
@ -660,9 +668,15 @@ void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const
item.add_treerow_button(block_);
/* After adding tree-row button (would disable hover highlighting). */
UI_block_emboss_set(&block_, UI_EMBOSS_NONE);
UI_block_emboss_set(&block_, UI_EMBOSS_NONE_OR_STATUS);
row = uiLayoutRow(overlap, true);
/* Add little margin to align actual contents vertically. */
uiLayout *content_col = uiLayoutColumn(overlap, true);
const int margin_top = (padded_item_height() - unpadded_item_height()) / 2;
if (margin_top > 0) {
uiDefBut(&block_, UI_BTYPE_LABEL, 0, "", 0, 0, UI_UNIT_X, margin_top, nullptr, 0, 0, "");
}
row = uiLayoutRow(content_col, true);
item.add_indent(*row);
item.add_collapse_chevron(block_);

View File

@ -125,7 +125,7 @@ enum eLattice_FlipAxes {
/**
* Flip midpoint value so that relative distances between midpoint and neighbor-pair is maintained.
* Assumes that uvw <=> xyz (i.e. axis-aligned index-axes with coordinate-axes).
* Assumes that UVW <=> XYZ (i.e. axis-aligned index-axes with coordinate-axes).
* - Helper for #lattice_flip_exec()
*/
static void lattice_flip_point_value(

View File

@ -309,7 +309,7 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot)
/** \name Clear Parent Operator
* \{ */
EnumPropertyItem prop_clear_parent_types[] = {
const EnumPropertyItem prop_clear_parent_types[] = {
{CLEAR_PARENT_ALL,
"CLEAR",
0,
@ -475,7 +475,7 @@ void parent_set(Object *ob, Object *par, const int type, const char *substr)
STRNCPY(ob->parsubstr, substr);
}
EnumPropertyItem prop_make_parent_types[] = {
const EnumPropertyItem prop_make_parent_types[] = {
{PAR_OBJECT, "OBJECT", 0, "Object", ""},
{PAR_ARMATURE, "ARMATURE", 0, "Armature Deform", ""},
{PAR_ARMATURE_NAME, "ARMATURE_NAME", 0, " With Empty Groups", ""},

View File

@ -4382,23 +4382,21 @@ static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
uiItemS(layout);
if (area->spacetype != SPACE_FILE) {
uiItemO(layout,
area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
ICON_NONE,
"SCREEN_OT_screen_full_area");
uiItemO(layout,
area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
ICON_NONE,
"SCREEN_OT_screen_full_area");
if (!area->full) {
uiItemFullO(layout,
"SCREEN_OT_screen_full_area",
IFACE_("Full Screen Area"),
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
RNA_boolean_set(&ptr, "use_hide_panels", true);
}
if (area->spacetype != SPACE_FILE && !area->full) {
uiItemFullO(layout,
"SCREEN_OT_screen_full_area",
IFACE_("Full Screen Area"),
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
RNA_boolean_set(&ptr, "use_hide_panels", true);
}
uiItemO(layout, nullptr, ICON_NONE, "SCREEN_OT_area_dupli");

View File

@ -166,13 +166,7 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
bke::CurvesGeometry &strokes = drawing.strokes_for_write();
MutableSpan<ColorGeometry4f> vertex_colors = drawing.vertex_colors_for_write();
bke::MutableAttributeAccessor stroke_attributes = strokes.attributes_for_write();
bke::SpanAttributeWriter<ColorGeometry4f> fill_colors =
stroke_attributes.lookup_or_add_for_write_span<ColorGeometry4f>(
"fill_color",
bke::AttrDomain::Curve,
bke::AttributeInitVArray(VArray<ColorGeometry4f>::ForSingle(
ColorGeometry4f(float4(0.0f)), strokes.curves_num())));
MutableSpan<ColorGeometry4f> fill_colors = drawing.fill_colors_for_write();
OffsetIndices<int> points_by_curve = strokes.points_by_curve();
const Span<float2> screen_space_positions =
@ -215,7 +209,7 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
}
}
}
if (tint_fills && !fill_colors.span.is_empty()) {
if (tint_fills && !fill_colors.is_empty()) {
/* Will tint fill color when either the brush being inside the fill region or touching
* the stroke. */
const bool fill_effective = stroke_touched ||
@ -225,12 +219,12 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
mouse_position);
if (fill_effective) {
float4 premultiplied;
straight_to_premul_v4_v4(premultiplied, fill_colors.span[curve]);
straight_to_premul_v4_v4(premultiplied, fill_colors[curve]);
float4 rgba = float4(
math::interpolate(float3(premultiplied), float3(color_), fill_strength),
fill_colors.span[curve][3]);
fill_colors[curve][3]);
rgba[3] = rgba[3] * (1.0f - fill_strength) + fill_strength;
premul_to_straight_v4_v4(fill_colors.span[curve], rgba);
premul_to_straight_v4_v4(fill_colors[curve], rgba);
stroke_touched = true;
}
}
@ -239,7 +233,6 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
}
}
});
fill_colors.finish();
};
threading::parallel_for_each(drawings_, [&](const MutableDrawingInfo &info) {

View File

@ -18,7 +18,9 @@
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
@ -26,6 +28,7 @@
#include "BLT_translation.hh"
#include "BKE_brush.hh"
#include "BKE_bvhutils.hh"
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_customdata.hh"
@ -33,6 +36,7 @@
#include "BKE_layer.hh"
#include "BKE_material.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_sample.hh"
#include "BKE_object.hh"
#include "BKE_paint.hh"
#include "BKE_report.hh"
@ -44,9 +48,6 @@
#include "RNA_define.hh"
#include "RNA_prototypes.h"
#include "GPU_matrix.hh"
#include "GPU_state.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
@ -59,8 +60,6 @@
#include "BLI_sys_types.h"
#include "ED_mesh.hh" /* for face mask functions */
#include "DRW_select_buffer.hh"
#include "WM_api.hh"
#include "WM_types.hh"
@ -202,168 +201,104 @@ void paint_stroke_operator_properties(wmOperatorType *ot)
/* 3D Paint */
static void imapaint_project(const float matrix[4][4], const float co[3], float pco[4])
{
copy_v3_v3(pco, co);
pco[3] = 1.0f;
mul_m4_v4(matrix, pco);
}
static void imapaint_tri_weights(float matrix[4][4],
const int view[4],
const float v1[3],
const float v2[3],
const float v3[3],
const float co[2],
float w[3])
{
float pv1[4], pv2[4], pv3[4], h[3], divw;
float wmat[3][3], invwmat[3][3];
/* compute barycentric coordinates */
/* project the verts */
imapaint_project(matrix, v1, pv1);
imapaint_project(matrix, v2, pv2);
imapaint_project(matrix, v3, pv3);
/* do inverse view mapping, see gluProject man page */
h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f;
h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f;
h[2] = 1.0f;
/* Solve for `(w1,w2,w3)/perspdiv` in:
* `h * perspdiv = Project * Model * (w1 * v1 + w2 * v2 + w3 * v3)`. */
wmat[0][0] = pv1[0];
wmat[1][0] = pv2[0];
wmat[2][0] = pv3[0];
wmat[0][1] = pv1[1];
wmat[1][1] = pv2[1];
wmat[2][1] = pv3[1];
wmat[0][2] = pv1[3];
wmat[1][2] = pv2[3];
wmat[2][2] = pv3[3];
invert_m3_m3(invwmat, wmat);
mul_m3_v3(invwmat, h);
copy_v3_v3(w, h);
/* w is still divided by `perspdiv`, make it sum to one */
divw = w[0] + w[1] + w[2];
if (divw != 0.0f) {
mul_v3_fl(w, 1.0f / divw);
}
}
/* compute uv coordinates of mouse in face */
static void imapaint_pick_uv(const Mesh *mesh_eval,
Scene *scene,
Object *ob_eval,
uint faceindex,
const int xy[2],
float uv[2])
static blender::float2 imapaint_pick_uv(const Mesh *mesh_eval,
Scene *scene,
Object *ob_eval,
const int tri_index,
const blender::float3 &bary_coord)
{
float p[2], w[3], absw, minabsw;
float matrix[4][4], proj[4][4];
int view[4];
const ePaintCanvasSource mode = ePaintCanvasSource(scene->toolsettings->imapaint.mode);
const blender::Span<blender::int3> tris = mesh_eval->corner_tris();
const blender::Span<int> tri_faces = mesh_eval->corner_tri_faces();
const blender::Span<blender::float3> positions = mesh_eval->vert_positions();
const blender::Span<int> corner_verts = mesh_eval->corner_verts();
const int *index_mp_to_orig = static_cast<const int *>(
CustomData_get_layer(&mesh_eval->face_data, CD_ORIGINDEX));
/* get the needed opengl matrices */
GPU_viewport_size_get_i(view);
GPU_matrix_model_view_get(matrix);
GPU_matrix_projection_get(proj);
view[0] = view[1] = 0;
mul_m4_m4m4(matrix, matrix, ob_eval->object_to_world().ptr());
mul_m4_m4m4(matrix, proj, matrix);
minabsw = 1e10;
uv[0] = uv[1] = 0.0;
const int *material_indices = (const int *)CustomData_get_layer_named(
&mesh_eval->face_data, CD_PROP_INT32, "material_index");
/* test all faces in the derivedmesh with the original index of the picked face */
/* face means poly here, not triangle, indeed */
for (const int i : tris.index_range()) {
const int face_i = tri_faces[i];
const int findex = index_mp_to_orig ? index_mp_to_orig[face_i] : face_i;
const int face_i = tri_faces[tri_index];
if (findex == faceindex) {
const float(*mloopuv)[2];
const float *tri_uv[3];
float tri_co[3][3];
const float(*mloopuv)[2];
for (int j = 3; j--;) {
copy_v3_v3(tri_co[j], positions[corner_verts[tris[i][j]]]);
}
if (mode == PAINT_CANVAS_SOURCE_MATERIAL) {
const Material *ma;
const TexPaintSlot *slot;
if (mode == PAINT_CANVAS_SOURCE_MATERIAL) {
const Material *ma;
const TexPaintSlot *slot;
ma = BKE_object_material_get(ob_eval,
material_indices == nullptr ? 1 : material_indices[face_i] + 1);
slot = &ma->texpaintslot[ma->paint_active_slot];
ma = BKE_object_material_get(
ob_eval, material_indices == nullptr ? 1 : material_indices[face_i] + 1);
slot = &ma->texpaintslot[ma->paint_active_slot];
if (!(slot && slot->uvname &&
(mloopuv = static_cast<const float(*)[2]>(CustomData_get_layer_named(
&mesh_eval->corner_data, CD_PROP_FLOAT2, slot->uvname)))))
{
mloopuv = static_cast<const float(*)[2]>(
CustomData_get_layer(&mesh_eval->corner_data, CD_PROP_FLOAT2));
}
}
else {
mloopuv = static_cast<const float(*)[2]>(
CustomData_get_layer(&mesh_eval->corner_data, CD_PROP_FLOAT2));
}
tri_uv[0] = mloopuv[tris[i][0]];
tri_uv[1] = mloopuv[tris[i][1]];
tri_uv[2] = mloopuv[tris[i][2]];
p[0] = xy[0];
p[1] = xy[1];
imapaint_tri_weights(matrix, view, UNPACK3(tri_co), p, w);
absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]);
if (absw < minabsw) {
uv[0] = tri_uv[0][0] * w[0] + tri_uv[1][0] * w[1] + tri_uv[2][0] * w[2];
uv[1] = tri_uv[0][1] * w[0] + tri_uv[1][1] * w[1] + tri_uv[2][1] * w[2];
minabsw = absw;
}
if (!(slot && slot->uvname &&
(mloopuv = static_cast<const float(*)[2]>(CustomData_get_layer_named(
&mesh_eval->corner_data, CD_PROP_FLOAT2, slot->uvname)))))
{
mloopuv = static_cast<const float(*)[2]>(
CustomData_get_layer(&mesh_eval->corner_data, CD_PROP_FLOAT2));
}
}
else {
mloopuv = static_cast<const float(*)[2]>(
CustomData_get_layer(&mesh_eval->corner_data, CD_PROP_FLOAT2));
}
return blender::bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
bary_coord,
tris[tri_index],
blender::Span(reinterpret_cast<const blender::float2 *>(mloopuv), mesh_eval->corners_num));
}
/* returns 0 if not found, otherwise 1 */
static int imapaint_pick_face(ViewContext *vc, const int mval[2], uint *r_index, uint faces_num)
static int imapaint_pick_face(ViewContext *vc,
const int mval[2],
int *r_tri_index,
int *r_face_index,
blender::float3 *r_bary_coord,
const Mesh &mesh)
{
if (faces_num == 0) {
using namespace blender;
if (mesh.faces_num == 0) {
return 0;
}
/* sample only on the exact position */
ED_view3d_select_id_validate(vc);
*r_index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, mval);
BVHTreeFromMesh mesh_bvh;
BKE_bvhtree_from_mesh_get(&mesh_bvh, &mesh, BVHTREE_FROM_CORNER_TRIS, 2);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&mesh_bvh); });
if ((*r_index) == 0 || (*r_index) > uint(faces_num)) {
float3 start_world, end_world;
ED_view3d_win_to_segment_clipped(
vc->depsgraph, vc->region, vc->v3d, float2(mval[0], mval[1]), start_world, end_world, true);
const float4x4 &world_to_object = vc->obact->world_to_object();
const float3 start_object = math::transform_point(world_to_object, start_world);
const float3 end_object = math::transform_point(world_to_object, end_world);
BVHTreeRayHit ray_hit;
ray_hit.dist = FLT_MAX;
ray_hit.index = -1;
BLI_bvhtree_ray_cast(mesh_bvh.tree,
start_object,
math::normalize(end_object - start_object),
0.0f,
&ray_hit,
mesh_bvh.raycast_callback,
&mesh_bvh);
if (ray_hit.index == -1) {
return 0;
}
(*r_index)--;
const Span<float3> positions = mesh.vert_positions();
const Span<int> corner_verts = mesh.corner_verts();
const Span<int3> corner_tris = mesh.corner_tris();
const int3 &tri = corner_tris[ray_hit.index];
interp_weights_tri_v3(*r_bary_coord,
positions[corner_verts[tri[0]]],
positions[corner_verts[tri[1]]],
positions[corner_verts[tri[2]]],
ray_hit.co);
*r_tri_index = ray_hit.index;
*r_face_index = mesh.corner_tri_faces()[ray_hit.index];
return 1;
}
@ -404,23 +339,18 @@ void paint_sample_color(
bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL);
if (ob) {
CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
cddata_masks.pmask |= CD_MASK_ORIGINDEX;
Mesh *mesh = (Mesh *)ob->data;
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
const int *material_indices = (const int *)CustomData_get_layer_named(
&mesh_eval->face_data, CD_PROP_INT32, "material_index");
const int mval[2] = {x, y};
uint faceindex;
uint faces_num = mesh->faces_num;
if (CustomData_has_layer(&mesh_eval->corner_data, CD_PROP_FLOAT2)) {
ViewContext vc = ED_view3d_viewcontext_init(C, depsgraph);
view3d_operator_needs_opengl(C);
if (imapaint_pick_face(&vc, mval, &faceindex, faces_num)) {
const int mval[2] = {x, y};
int tri_index;
float3 bary_coord;
int faceindex;
if (imapaint_pick_face(&vc, mval, &tri_index, &faceindex, &bary_coord, *mesh_eval)) {
Image *image = nullptr;
int interp = SHD_INTERP_LINEAR;
@ -449,8 +379,7 @@ void paint_sample_color(
BKE_imageuser_default(&iuser);
iuser.framenr = image->lastframe;
float uv[2];
imapaint_pick_uv(mesh_eval, scene, ob_eval, faceindex, mval, uv);
float2 uv = imapaint_pick_uv(mesh_eval, scene, ob_eval, tri_index, bary_coord);
if (image->source == IMA_SRC_TILED) {
float new_uv[2];

View File

@ -1673,7 +1673,7 @@ void ACTION_OT_handle_type(wmOperatorType *ot)
* \{ */
/* this function is responsible for setting keyframe type for keyframes */
static void setkeytype_action_keys(bAnimContext *ac, short mode)
static void setkeytype_action_keys(bAnimContext *ac, eBezTriple_KeyframeType mode)
{
ListBase anim_data = {nullptr, nullptr};
eAnimFilter_Flags filter;
@ -1722,7 +1722,6 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode)
static int actkeys_keytype_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
short mode;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0) {
@ -1734,11 +1733,8 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op)
return OPERATOR_PASS_THROUGH;
}
/* get handle setting mode */
mode = RNA_enum_get(op->ptr, "type");
/* set handle type */
setkeytype_action_keys(&ac, mode);
const int mode = RNA_enum_get(op->ptr, "type");
setkeytype_action_keys(&ac, eBezTriple_KeyframeType(mode));
/* set notifier that keyframe properties have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, nullptr);

View File

@ -115,7 +115,6 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
SpaceGraph *sipo = (SpaceGraph *)ac->sl;
Scene *scene = ac->scene;
ToolSettings *ts = scene->toolsettings;
eInsertKeyFlags flag = eInsertKeyFlags(0);
/* Filter data. */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
@ -146,7 +145,7 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
}
/* Init key-framing flag. */
flag = ANIM_get_keyframing_flags(scene);
eInsertKeyFlags flag = ANIM_get_keyframing_flags(scene);
KeyframeSettings settings = get_keyframe_settings(true);
settings.keyframe_type = eBezTriple_KeyframeType(ts->keyframe_type);
@ -1838,7 +1837,7 @@ static ListBase /*tEulerFilter*/ euler_filter_group_channels(
* saves another loop over the animation data. */
ale->update |= ANIM_UPDATE_DEFAULT;
/* Optimization: assume that xyz curves will always be stored consecutively,
/* Optimization: assume that XYZ curves will always be stored consecutively,
* so if the paths or the ID's don't match up, then a curve needs to be added
* to a new group.
*/

View File

@ -66,21 +66,6 @@
/* Own include. */
#include "sequencer_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Structs & Enums
* \{ */
struct TransSeq {
int start, machine;
int startofs, endofs;
int anim_startofs, anim_endofs;
// int final_left, final_right; /* UNUSED. */
int len;
float content_start;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public Context Checks
* \{ */
@ -1337,7 +1322,7 @@ static const EnumPropertyItem prop_split_types[] = {
{0, nullptr, 0, nullptr, nullptr},
};
EnumPropertyItem prop_side_types[] = {
const EnumPropertyItem prop_side_types[] = {
{SEQ_SIDE_MOUSE, "MOUSE", 0, "Mouse Position", ""},
{SEQ_SIDE_LEFT, "LEFT", 0, "Left", ""},
{SEQ_SIDE_RIGHT, "RIGHT", 0, "Right", ""},
@ -2617,7 +2602,7 @@ void SEQUENCER_OT_change_effect_input(wmOperatorType *ot)
/** \name Change Effect Type Operator
* \{ */
EnumPropertyItem sequencer_prop_effect_types[] = {
const EnumPropertyItem sequencer_prop_effect_types[] = {
{SEQ_TYPE_CROSS, "CROSS", 0, "Crossfade", "Crossfade effect strip type"},
{SEQ_TYPE_ADD, "ADD", 0, "Add", "Add effect strip type"},
{SEQ_TYPE_SUB, "SUBTRACT", 0, "Subtract", "Subtract effect strip type"},

View File

@ -190,8 +190,8 @@ blender::VectorSet<Sequence *> selected_strips_from_context(bContext *C);
/* Externals. */
extern EnumPropertyItem sequencer_prop_effect_types[];
extern EnumPropertyItem prop_side_types[];
extern const EnumPropertyItem sequencer_prop_effect_types[];
extern const EnumPropertyItem prop_side_types[];
/* Operators. */

View File

@ -1819,6 +1819,7 @@ static void view3d_buttons_region_listener(const wmRegionListenerParams *params)
case ND_LAYER:
case ND_LAYER_CONTENT:
case ND_TOOLSETTINGS:
case ND_TRANSFORM:
ED_region_tag_redraw(region);
break;
}

View File

@ -1884,7 +1884,12 @@ static void initSnapSpatial(TransInfo *t, float r_snap[3], float *r_snap_precisi
*r_snap_precision = 0.1f;
if (t->spacetype == SPACE_VIEW3D) {
/* Pass. Done in #ED_transform_snap_object_project_view3d_ex. */
/* Used by incremental snap. */
if (t->region->regiondata) {
View3D *v3d = static_cast<View3D *>(t->area->spacedata.first);
r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale(
t->scene, v3d, t->region, nullptr);
}
}
else if (t->spacetype == SPACE_IMAGE) {
SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);

View File

@ -967,7 +967,7 @@ static eSnapMode snapObjectsRay(SnapObjectContext *sctx)
static bool snap_grid(SnapObjectContext *sctx)
{
SnapData nearest2d(sctx);
nearest2d.clip_planes_enable(sctx, nullptr);
nearest2d.clip_planes_enable(sctx, nullptr, true);
/* Ignore the maximum pixel distance when snapping to grid.
* This avoids undesirable jumps of the element being snapped. */
@ -990,7 +990,7 @@ static bool snap_grid(SnapObjectContext *sctx)
sctx->grid.planes[i],
&ray_dist,
false) &&
IN_RANGE_INCL(ray_dist, 0.0f, sctx->ret.ray_depth_max))
(ray_dist > 0.0f))
{
float3 co = math::round((sctx->runtime.ray_start + sctx->runtime.ray_dir * ray_dist) /
grid_dist) *
@ -1323,7 +1323,7 @@ eSnapMode ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx,
bool use_occlusion_plane = false;
/* It is required `mval` to calculate the occlusion plane. */
if (mval) {
if (mval && (snap_to_flag & SCE_SNAP_TO_GEOM)) {
const bool is_allways_occluded = !params->use_occlusion_test;
use_occlusion_plane = is_allways_occluded || !XRAY_ENABLED(v3d);
}

View File

@ -341,14 +341,20 @@ intersection_test intersect2dSeg2dSegParametric(const Vec2r &p1,
(void)0
// This internal procedure is defined below.
bool overlapPlaneBox(Vec3r &normal, real d, Vec3r &maxbox);
bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox);
// Use separating axis theorem to test overlap between triangle and box need to test for overlap in
// these directions: 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle we
// do not even need to test these) 2) normal of the triangle 3) crossproduct(edge from tri,
// {x,y,z}-directin) this gives 3x3=9 more tests
bool overlapTriangleBox(Vec3r &boxcenter, Vec3r &boxhalfsize, Vec3r triverts[3])
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3])
{
/* Use separating axis theorem to test overlap between triangle and box need to test for overlap
* in these directions:
*
* 1) The {x,y,z}-directions
* (actually, since we use the AABB of the triangle we do not even need to test these).
* 2) Normal of the triangle.
* 3) `crossproduct(edge from tri, {x,y,z}-directin)` this gives 3x3=9 more tests.
*
* Adapted from Tomas Akenine-Möller code. */
Vec3r v0, v1, v2, normal, e0, e1, e2;
real min, max, d, p0, p1, p2, rad, fex, fey, fez;
@ -421,17 +427,6 @@ bool overlapTriangleBox(Vec3r &boxcenter, Vec3r &boxhalfsize, Vec3r triverts[3])
return true; // box and triangle overlaps
}
// Fast, Minimum Storage Ray-Triangle Intersection
//
// Tomas Möller
// Prosolvia Clarus AB
// Sweden
// <tompa@clarus.se>
//
// Ben Trumbore
// Cornell University
// Ithaca, New York
// <wbt@graphics.cornell.edu>
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
@ -442,6 +437,12 @@ bool intersectRayTriangle(const Vec3r &orig,
real &v,
const real epsilon)
{
/* Fast, Minimum Storage Ray-Triangle Intersection.
* Adapted from Tomas Möller and Ben Trumbore code.
*
* Tomas Möller, Prosolvia Clarus AB, Sweden, <tompa@clarus.se>.
* Ben Trumbore, Cornell University, Ithaca, New York <wbt@graphics.cornell.edu>. */
Vec3r edge1, edge2, tvec, pvec, qvec;
real det, inv_det;
@ -497,9 +498,6 @@ bool intersectRayTriangle(const Vec3r &orig,
return true;
}
// Intersection between plane and ray, adapted from Graphics Gems, Didier Badouel
// The plane is represented by a set of points P implicitly defined as dot(norm, P) + d = 0.
// The ray is represented as r(t) = orig + dir * t.
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &norm,
@ -507,6 +505,10 @@ intersection_test intersectRayPlane(const Vec3r &orig,
real &t,
const real epsilon)
{
/* Intersection between plane and ray, adapted from Graphics Gems, Didier Badouel
* The plane is represented by a set of points P implicitly defined as `dot(norm, P) + d = 0`.
* The ray is represented as `r(t) = orig + dir * t`. */
real denom = norm * dir;
if (fabs(denom) <= epsilon) { // plane and ray are parallel
@ -791,7 +793,7 @@ inline bool intersect2dSegPoly(Vec2r *seg, Vec2r *poly, uint n)
return true;
}
inline bool overlapPlaneBox(Vec3r &normal, real d, Vec3r &maxbox)
inline bool overlapPlaneBox(const Vec3r &normal, const real d, const Vec3r &maxbox)
{
Vec3r vmin, vmax;

View File

@ -91,11 +91,10 @@ bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, co
/** check whether a 2D segment is included in a 2D region or not */
bool include2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B);
/** Box-triangle overlap test, adapted from Tomas Akenine-Möller code */
bool overlapTriangleBox(Vec3r &boxcenter, Vec3r &boxhalfsize, Vec3r triverts[3]);
/** Box-triangle overlap test. */
bool overlapTriangleBox(const Vec3r &boxcenter, const Vec3r &boxhalfsize, const Vec3r triverts[3]);
/** Fast, Minimum Storage Ray-Triangle Intersection, adapted from Tomas Möller and Ben Trumbore
* code. */
/** Fast, Minimum Storage Ray-Triangle Intersection. */
bool intersectRayTriangle(const Vec3r &orig,
const Vec3r &dir,
const Vec3r &v0,
@ -106,7 +105,7 @@ bool intersectRayTriangle(const Vec3r &orig,
real &v, // I = (1 - u - v) * v0 + u * v1 + v * v2
const real epsilon = M_EPSILON); // the epsilon to use
/** Intersection between plane and ray adapted from Graphics Gems, Didier Badouel */
/** Intersection between plane and ray. */
intersection_test intersectRayPlane(const Vec3r &orig,
const Vec3r &dir, // ray origin and direction
// plane's normal and offset (plane = { P / P.N + d = 0 })

View File

@ -50,12 +50,12 @@ void NodeCamera::accept(SceneVisitor &v)
v.visitNodeCamera(*this);
}
void NodeCamera::setModelViewMatrix(double modelview_matrix[16])
void NodeCamera::setModelViewMatrix(const double modelview_matrix[16])
{
memcpy(modelview_matrix_, modelview_matrix, sizeof(double[16]));
}
void NodeCamera::setProjectionMatrix(double projection_matrix[16])
void NodeCamera::setProjectionMatrix(const double projection_matrix[16])
{
memcpy(projection_matrix_, projection_matrix, sizeof(double[16]));
}

View File

@ -43,10 +43,10 @@ class NodeCamera : public Node {
virtual void accept(SceneVisitor &v);
/** Matrix is copied */
void setModelViewMatrix(double modelview_matrix[16]);
void setModelViewMatrix(const double modelview_matrix[16]);
/** Matrix is copied */
void setProjectionMatrix(double projection_matrix[16]);
void setProjectionMatrix(const double projection_matrix[16]);
double *modelViewMatrix()
{

View File

@ -139,7 +139,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format,
assert(fetch_mode == GPU_FETCH_FLOAT);
break;
case GPU_COMP_I10:
/* 10_10_10 format intended for normals (xyz) or colors (rgb)
/* 10_10_10 format intended for normals (XYZ) or colors (RGB)
* extra component packed.w can be manually set to { -2, -1, 0, 1 } */
assert(ELEM(comp_len, 3, 4));

View File

@ -111,7 +111,8 @@ typedef struct BezTriple {
* Provide access to Keyframe Type info #eBezTriple_KeyframeType in #BezTriple::hide.
* \note this is so that we can change it to another location.
*/
#define BEZKEYTYPE(bezt) ((bezt)->hide)
#define BEZKEYTYPE(bezt) (eBezTriple_KeyframeType((bezt)->hide))
#define BEZKEYTYPE_LVALUE(bezt) ((bezt)->hide)
/**
* \note #BPoint.tilt location in struct is abused by Key system.

View File

@ -62,7 +62,7 @@ typedef struct RegionView3D {
/** Transform gizmo matrix. */
float twmat[4][4];
/** min/max dot product on twmat xyz axis. */
/** min/max dot product on `twmat` XYZ axis. */
float tw_axis_min[3], tw_axis_max[3];
float tw_axis_matrix[3][3];

View File

@ -111,25 +111,25 @@ PropertyRNA *RNA_def_boolean(StructOrFunctionRNA *cont,
PropertyRNA *RNA_def_boolean_array(StructOrFunctionRNA *cont,
const char *identifier,
int len,
bool *default_value,
const bool *default_value,
const char *ui_name,
const char *ui_description);
PropertyRNA *RNA_def_boolean_layer(StructOrFunctionRNA *cont,
const char *identifier,
int len,
bool *default_value,
const bool *default_value,
const char *ui_name,
const char *ui_description);
PropertyRNA *RNA_def_boolean_layer_member(StructOrFunctionRNA *cont,
const char *identifier,
int len,
bool *default_value,
const bool *default_value,
const char *ui_name,
const char *ui_description);
PropertyRNA *RNA_def_boolean_vector(StructOrFunctionRNA *cont,
const char *identifier,
int len,
bool *default_value,
const bool *default_value,
const char *ui_name,
const char *ui_description);

View File

@ -72,10 +72,10 @@ const EnumPropertyItem *RNA_action_itemf(bContext *C,
PropertyRNA *prop,
bool *r_free);
#if 0
EnumPropertyItem *RNA_action_local_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_free);
const EnumPropertyItem *RNA_action_local_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_free);
#endif
const EnumPropertyItem *RNA_collection_itemf(bContext *C,
PointerRNA *ptr,

View File

@ -463,8 +463,8 @@ const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/,
const EnumPropertyItem *items = blender::ed::asset::library_reference_to_rna_enum_itemf(true);
if (!items) {
*r_free = false;
return rna_enum_dummy_NULL_items;
}
*r_free = true;
return items;
}

View File

@ -3608,7 +3608,7 @@ void RNA_def_py_data(PropertyRNA *prop, void *py_data)
PropertyRNA *RNA_def_boolean(StructOrFunctionRNA *cont_,
const char *identifier,
bool default_value,
const bool default_value,
const char *ui_name,
const char *ui_description)
{
@ -3624,8 +3624,8 @@ PropertyRNA *RNA_def_boolean(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_boolean_array(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
bool *default_value,
const int len,
const bool *default_value,
const char *ui_name,
const char *ui_description)
{
@ -3646,8 +3646,8 @@ PropertyRNA *RNA_def_boolean_array(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_boolean_layer(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
bool *default_value,
const int len,
const bool *default_value,
const char *ui_name,
const char *ui_description)
{
@ -3668,8 +3668,8 @@ PropertyRNA *RNA_def_boolean_layer(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_boolean_layer_member(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
bool *default_value,
const int len,
const bool *default_value,
const char *ui_name,
const char *ui_description)
{
@ -3690,8 +3690,8 @@ PropertyRNA *RNA_def_boolean_layer_member(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_boolean_vector(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
bool *default_value,
const int len,
const bool *default_value,
const char *ui_name,
const char *ui_description)
{
@ -3712,13 +3712,13 @@ PropertyRNA *RNA_def_boolean_vector(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_int(StructOrFunctionRNA *cont_,
const char *identifier,
int default_value,
int hardmin,
int hardmax,
const int default_value,
const int hardmin,
const int hardmax,
const char *ui_name,
const char *ui_description,
int softmin,
int softmax)
const int softmin,
const int softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -3738,14 +3738,14 @@ PropertyRNA *RNA_def_int(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_int_vector(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const int *default_value,
int hardmin,
int hardmax,
const int hardmin,
const int hardmax,
const char *ui_name,
const char *ui_description,
int softmin,
int softmax)
const int softmin,
const int softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -3770,14 +3770,14 @@ PropertyRNA *RNA_def_int_vector(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_int_array(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const int *default_value,
int hardmin,
int hardmax,
const int hardmin,
const int hardmax,
const char *ui_name,
const char *ui_description,
int softmin,
int softmax)
const int softmin,
const int softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -3803,7 +3803,7 @@ PropertyRNA *RNA_def_int_array(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_string(StructOrFunctionRNA *cont_,
const char *identifier,
const char *default_value,
int maxlen,
const int maxlen,
const char *ui_name,
const char *ui_description)
{
@ -3827,7 +3827,7 @@ PropertyRNA *RNA_def_string(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_string_file_path(StructOrFunctionRNA *cont_,
const char *identifier,
const char *default_value,
int maxlen,
const int maxlen,
const char *ui_name,
const char *ui_description)
{
@ -3851,7 +3851,7 @@ PropertyRNA *RNA_def_string_file_path(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_string_dir_path(StructOrFunctionRNA *cont_,
const char *identifier,
const char *default_value,
int maxlen,
const int maxlen,
const char *ui_name,
const char *ui_description)
{
@ -3875,7 +3875,7 @@ PropertyRNA *RNA_def_string_dir_path(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_string_file_name(StructOrFunctionRNA *cont_,
const char *identifier,
const char *default_value,
int maxlen,
const int maxlen,
const char *ui_name,
const char *ui_description)
{
@ -3899,7 +3899,7 @@ PropertyRNA *RNA_def_string_file_name(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_enum(StructOrFunctionRNA *cont_,
const char *identifier,
const EnumPropertyItem *items,
int default_value,
const int default_value,
const char *ui_name,
const char *ui_description)
{
@ -3922,7 +3922,7 @@ PropertyRNA *RNA_def_enum(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_enum_flag(StructOrFunctionRNA *cont_,
const char *identifier,
const EnumPropertyItem *items,
int default_value,
const int default_value,
const char *ui_name,
const char *ui_description)
{
@ -3951,13 +3951,13 @@ void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
PropertyRNA *RNA_def_float(StructOrFunctionRNA *cont_,
const char *identifier,
float default_value,
float hardmin,
float hardmax,
const float default_value,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -3977,14 +3977,14 @@ PropertyRNA *RNA_def_float(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_vector(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4009,14 +4009,14 @@ PropertyRNA *RNA_def_float_vector(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_vector_xyz(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
PropertyRNA *prop;
@ -4037,14 +4037,14 @@ PropertyRNA *RNA_def_float_vector_xyz(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_color(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4069,15 +4069,15 @@ PropertyRNA *RNA_def_float_color(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont_,
const char *identifier,
int rows,
int columns,
const int rows,
const int columns,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4101,14 +4101,14 @@ PropertyRNA *RNA_def_float_matrix(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
PropertyRNA *prop;
@ -4131,14 +4131,14 @@ PropertyRNA *RNA_def_float_translation(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4167,13 +4167,13 @@ PropertyRNA *RNA_def_float_rotation(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_distance(StructOrFunctionRNA *cont_,
const char *identifier,
float default_value,
float hardmin,
float hardmax,
const float default_value,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
PropertyRNA *prop = RNA_def_float(cont_,
identifier,
@ -4191,14 +4191,14 @@ PropertyRNA *RNA_def_float_distance(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_array(StructOrFunctionRNA *cont_,
const char *identifier,
int len,
const int len,
const float *default_value,
float hardmin,
float hardmax,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4223,13 +4223,13 @@ PropertyRNA *RNA_def_float_array(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_percentage(StructOrFunctionRNA *cont_,
const char *identifier,
float default_value,
float hardmin,
float hardmax,
const float default_value,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;
@ -4259,13 +4259,13 @@ PropertyRNA *RNA_def_float_percentage(StructOrFunctionRNA *cont_,
PropertyRNA *RNA_def_float_factor(StructOrFunctionRNA *cont_,
const char *identifier,
float default_value,
float hardmin,
float hardmax,
const float default_value,
const float hardmin,
const float hardmax,
const char *ui_name,
const char *ui_description,
float softmin,
float softmax)
const float softmin,
const float softmax)
{
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
PropertyRNA *prop;

View File

@ -128,7 +128,13 @@ static void rna_DynamicPaintSurfaces_changeFormat(Main *bmain, Scene *scene, Poi
{
DynamicPaintSurface *surface = (DynamicPaintSurface *)ptr->data;
surface->type = MOD_DPAINT_SURFACE_T_PAINT;
/* Only #MOD_DPAINT_SURFACE_F_VERTEX supports #MOD_DPAINT_SURFACE_T_WEIGHT. */
if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ &&
surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)
{
surface->type = MOD_DPAINT_SURFACE_T_PAINT;
}
dynamicPaintSurface_updateType((DynamicPaintSurface *)ptr->data);
rna_DynamicPaintSurface_reset(bmain, scene, ptr);
}

View File

@ -136,14 +136,13 @@ static void modify_stroke_color(Object &ob,
static void modify_fill_color(Object &ob,
const GreasePencilColorModifierData &cmd,
bke::CurvesGeometry &curves,
Drawing &drawing,
const IndexMask &curves_mask)
{
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const bke::CurvesGeometry &curves = drawing.strokes();
const bke::AttributeAccessor attributes = curves.attributes();
/* Fill color per stroke. */
bke::SpanAttributeWriter<ColorGeometry4f> fill_colors =
attributes.lookup_or_add_for_write_span<ColorGeometry4f>("fill_color",
bke::AttrDomain::Curve);
MutableSpan<ColorGeometry4f> fill_colors = drawing.fill_colors_for_write();
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
@ -153,10 +152,8 @@ static void modify_fill_color(Object &ob,
const ColorGeometry4f material_color = (gp_style ? ColorGeometry4f(gp_style->fill_rgba) :
ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
apply_color_factor(fill_colors.span[curve_i], material_color, cmd.hsv);
apply_color_factor(fill_colors[curve_i], material_color, cmd.hsv);
});
fill_colors.finish();
}
static void modify_drawing(ModifierData &md, const ModifierEvalContext &ctx, Drawing &drawing)
@ -174,12 +171,12 @@ static void modify_drawing(ModifierData &md, const ModifierEvalContext &ctx, Dra
*ctx.object, cmd, curves, curves_mask, drawing.vertex_colors_for_write());
break;
case MOD_GREASE_PENCIL_COLOR_FILL:
modify_fill_color(*ctx.object, cmd, curves, curves_mask);
modify_fill_color(*ctx.object, cmd, drawing, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_BOTH:
modify_stroke_color(
*ctx.object, cmd, curves, curves_mask, drawing.vertex_colors_for_write());
modify_fill_color(*ctx.object, cmd, curves, curves_mask);
modify_fill_color(*ctx.object, cmd, drawing, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_HARDNESS:
BLI_assert_unreachable();

View File

@ -99,6 +99,7 @@ static void deform_drawing(const GreasePencilNoiseModifierData &mmd,
bke::greasepencil::Drawing &drawing)
{
bke::CurvesGeometry &strokes = drawing.strokes_for_write();
bke::MutableAttributeAccessor attributes = strokes.attributes_for_write();
if (strokes.points_num() == 0) {
return;
}
@ -208,7 +209,26 @@ static void deform_drawing(const GreasePencilNoiseModifierData &mmd,
});
}
// TODO: UV hasn't been implemented yet.
if (mmd.factor_uvs > 0.0f) {
bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
"rotation", bke::AttrDomain::Point);
filtered_strokes.foreach_index(GrainSize(512), [&](const int stroke_i) {
const IndexRange points = points_by_curve[stroke_i];
const int noise_len = math::ceil(points.size() * noise_scale) + 2;
const Array<float> table = noise_table(
noise_len, int(math::floor(mmd.noise_offset)), seed + 4 + stroke_i);
for (const int i : points.index_range()) {
const int point = points[i];
const float weight = get_weight(points, i);
const float noise = get_noise(table, i * noise_scale + math::fract(mmd.noise_offset));
const float delta_rot = (noise * 2.0f - 1.0f) * weight * mmd.factor_uvs * M_PI_2;
rotations.span[point] = math::clamp(
rotations.span[point] + delta_rot, float(-M_PI_2), float(M_PI_2));
}
});
rotations.finish();
}
}
static void modify_geometry_set(ModifierData *md,
@ -221,7 +241,7 @@ static void modify_geometry_set(ModifierData *md,
return;
}
if (!mmd->factor && !mmd->factor_strength && !mmd->factor_thickness) {
if (!mmd->factor && !mmd->factor_strength && !mmd->factor_thickness && !mmd->factor_uvs) {
return;
}

View File

@ -239,12 +239,13 @@ static void modify_stroke_color(Object &ob,
static void modify_fill_color(Object &ob,
const GreasePencilTintModifierData &tmd,
bke::CurvesGeometry &curves,
Drawing &drawing,
const IndexMask &curves_mask)
{
const bool use_weight_as_factor = (tmd.flag & MOD_GREASE_PENCIL_TINT_USE_WEIGHT_AS_FACTOR);
const bool invert_vertex_group = (tmd.influence.flag &
GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP);
const bke::CurvesGeometry &curves = drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const GreasePencilTintModifierMode tint_mode = GreasePencilTintModifierMode(tmd.tint_mode);
@ -253,14 +254,9 @@ static void modify_fill_color(Object &ob,
return;
}
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
/* Fill color per stroke. */
bke::SpanAttributeWriter<ColorGeometry4f> fill_colors =
attributes.lookup_or_add_for_write_span<ColorGeometry4f>(
"fill_color",
bke::AttrDomain::Curve,
bke::AttributeInitVArray(VArray<ColorGeometry4f>::ForSingle(
ColorGeometry4f(float4(0.0f)), curves.curves_num())));
MutableSpan<ColorGeometry4f> fill_colors = drawing.fill_colors_for_write();
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
const VArray<float> vgroup_weights = modifier::greasepencil::get_influence_vertex_weights(
@ -288,10 +284,8 @@ static void modify_fill_color(Object &ob,
case MOD_GREASE_PENCIL_TINT_UNIFORM: {
curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) {
const ColorGeometry4f material_color = get_material_color(curve_i);
fill_colors.span[curve_i] = apply_uniform_tint(
tmd,
get_base_color(fill_colors.span[curve_i], material_color),
get_curve_factor(curve_i));
fill_colors[curve_i] = apply_uniform_tint(
tmd, get_base_color(fill_colors[curve_i], material_color), get_curve_factor(curve_i));
});
break;
}
@ -308,18 +302,16 @@ static void modify_fill_color(Object &ob,
const float3 pos = points.is_empty() ? float3(0.0f, 0.0f, 0.0f) :
positions[points.first()];
fill_colors.span[curve_i] = apply_gradient_tint(
fill_colors[curve_i] = apply_gradient_tint(
tmd,
matrix,
pos,
get_base_color(fill_colors.span[curve_i], material_color),
get_base_color(fill_colors[curve_i], material_color),
get_curve_factor(curve_i));
});
break;
}
}
fill_colors.finish();
}
static void modify_opacity(const GreasePencilTintModifierData &tmd,
@ -365,12 +357,12 @@ static void modify_curves(ModifierData &md, const ModifierEvalContext &ctx, Draw
*ctx.object, tmd, curves, curves_mask, drawing.vertex_colors_for_write());
break;
case MOD_GREASE_PENCIL_COLOR_FILL:
modify_fill_color(*ctx.object, tmd, curves, curves_mask);
modify_fill_color(*ctx.object, tmd, drawing, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_BOTH:
modify_stroke_color(
*ctx.object, tmd, curves, curves_mask, drawing.vertex_colors_for_write());
modify_fill_color(*ctx.object, tmd, curves, curves_mask);
modify_fill_color(*ctx.object, tmd, drawing, curves_mask);
break;
case MOD_GREASE_PENCIL_COLOR_HARDNESS:
BLI_assert_unreachable();

View File

@ -221,7 +221,7 @@ static void panel_draw(const bContext * /*C*/, Panel *panel)
ModifierData *md = (ModifierData *)ptr->data;
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
uiItemL(layout, RPT_("Settings are in the particle tab"), ICON_NONE);
uiItemL(layout, RPT_("Settings are inside the Particles tab"), ICON_NONE);
if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) {

View File

@ -512,7 +512,7 @@ PyDoc_STRVAR(
static PyObject *app_translations_locales_get(PyObject * /*self*/, void * /*userdata*/)
{
PyObject *ret;
EnumPropertyItem *it, *items = BLT_lang_RNA_enum_properties();
const EnumPropertyItem *it, *items = BLT_lang_RNA_enum_properties();
int num_locales = 0, pos = 0;
if (items) {

View File

@ -1365,7 +1365,7 @@ static void WM_OT_xr_navigation_teleport(wmOperatorType *ot)
ot->poll = wm_xr_operator_sessionactive;
/* Properties. */
static bool default_teleport_axes[3] = {true, true, true};
static const bool default_teleport_axes[3] = {true, true, true};
RNA_def_boolean_vector(ot->srna,
"teleport_axes",

@ -1 +1 @@
Subproject commit 873d77afcc5dd6b147dae5c91d033b210925800f
Subproject commit 612e310bf7ee505a79dad7ea925916ec9a812995