Compositor: Allow inter-operation canceling #119917
|
@ -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",))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)));
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* ------- */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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[];
|
||||
|
||||
/* -------- */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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", ""},
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue