Geometry Nodes: add Inspection Index to Repeat Zone #112818

Merged
Jacques Lucke merged 7 commits from JacquesLucke/blender:repeat-viewer-iteration into main 2023-09-27 11:09:46 +02:00
190 changed files with 6917 additions and 844 deletions
Showing only changes of commit 2e494d6102 - Show all commits

View File

@ -379,6 +379,7 @@ endif()
if(WITH_USD)
find_package_wrapper(USD)
set_and_warn_library_found("USD" USD_FOUND WITH_USD)
set_and_warn_library_found("Hydra" USD_FOUND WITH_HYDRA)
endif()
add_bundled_libraries(usd/lib)

View File

@ -16,6 +16,9 @@ class CustomHydraRenderEngine(bpy.types.HydraRenderEngine):
# Name of the render plugin.
bl_delegate_id = "HdCustomRendererPlugin"
# Use MaterialX instead of UsdPreviewSurface for materials.
bl_use_materialx = True
# Register path to plugin.
@classmethod
def register(cls):

View File

@ -292,6 +292,23 @@ class PrincipledBSDFWrapper(ShaderWrapper):
specular = property(specular_get, specular_set)
# Will only be used as gray-scale one...
def specular_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return None
return ShaderImageTextureWrapper(
self, self.node_principled_bsdf,
self.node_principled_bsdf.inputs["Specular IOR Level"],
grid_row_diff=0,
colorspace_name='Non-Color',
)
specular_texture = property(specular_texture_get)
# --------------------------------------------------------------------
# Specular Tint.
def specular_tint_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return Color((0.0, 0.0, 0.0))
@ -306,19 +323,17 @@ class PrincipledBSDFWrapper(ShaderWrapper):
specular_tint = property(specular_tint_get, specular_tint_set)
# Will only be used as gray-scale one...
def specular_texture_get(self):
def specular_tint_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
print("NO NODES!")
return None
return ShaderImageTextureWrapper(
self, self.node_principled_bsdf,
self.node_principled_bsdf.inputs["Specular IOR Level"],
self.node_principled_bsdf.inputs["Specular Tint"],
grid_row_diff=0,
colorspace_name='Non-Color',
)
specular_texture = property(specular_texture_get)
specular_tint_texture = property(specular_tint_texture_get)
# --------------------------------------------------------------------
# Roughness (also sort of inverse of specular hardness...).

View File

@ -114,6 +114,7 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel):
col.prop(probe, "grid_bake_samples")
col.prop(probe, "surfel_density")
col.prop(probe, "clip_end", text="Capture Distance")
col.separator()
@ -143,12 +144,6 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel):
col.prop(probe, "grid_capture_indirect")
col.prop(probe, "grid_capture_emission")
col.separator()
row = col.row(align=True)
row.prop(probe, "visibility_collection")
row.prop(probe, "invert_visibility_collection", text="", icon='ARROW_LEFTRIGHT')
elif probe.type == 'CUBEMAP':
col = layout.column()
col.prop(probe, "resolution")

View File

@ -292,6 +292,8 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
draw_attribute_warnings(context, layout)
class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
bl_label = "Shape Keys"
@ -416,6 +418,8 @@ class DATA_PT_uv_texture(MeshButtonsPanel, Panel):
col.operator("mesh.uv_texture_add", icon='ADD', text="")
col.operator("mesh.uv_texture_remove", icon='REMOVE', text="")
draw_attribute_warnings(context, layout)
class DATA_PT_remesh(MeshButtonsPanel, Panel):
bl_label = "Remesh"
@ -562,34 +566,35 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
col.menu("MESH_MT_attribute_context_menu", icon='DOWNARROW_HLT', text="")
self.draw_attribute_warnings(context, layout)
draw_attribute_warnings(context, layout)
def draw_attribute_warnings(self, context, layout):
ob = context.object
mesh = context.mesh
unique_names = set()
colliding_names = []
for collection in (
# Built-in names.
{"crease": None},
mesh.attributes,
None if ob is None else ob.vertex_groups,
):
if collection is None:
colliding_names.append("Cannot check for object vertex groups when pinning mesh")
continue
for name in collection.keys():
unique_names_len = len(unique_names)
unique_names.add(name)
if len(unique_names) == unique_names_len:
colliding_names.append(name)
def draw_attribute_warnings(context, layout):
ob = context.object
mesh = context.mesh
if not colliding_names:
return
unique_names = set()
colliding_names = []
for collection in (
# Built-in names.
{"crease": None},
mesh.attributes,
None if ob is None else ob.vertex_groups,
):
if collection is None:
colliding_names.append("Cannot check for object vertex groups when pinning mesh")
continue
for name in collection.keys():
unique_names_len = len(unique_names)
unique_names.add(name)
if len(unique_names) == unique_names_len:
colliding_names.append(name)
layout.label(text=tip_("Name collisions: ") + ", ".join(set(colliding_names)),
icon='ERROR', translate=False)
if not colliding_names:
return
layout.label(text=tip_("Name collisions: ") + ", ".join(set(colliding_names)),
icon='ERROR', translate=False)
class ColorAttributesListBase():
@ -691,7 +696,7 @@ class DATA_PT_vertex_colors(DATA_PT_mesh_attributes, Panel):
col.menu("MESH_MT_color_attribute_context_menu", icon='DOWNARROW_HLT', text="")
self.draw_attribute_warnings(context, layout)
draw_attribute_warnings(context, layout)
classes = (

View File

@ -370,7 +370,7 @@ class OBJECT_PT_motion_paths_display(MotionPathButtonsPanel_display, Panel):
class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
bl_label = "Visibility"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'}
@classmethod
def poll(cls, context):
@ -388,6 +388,13 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
col = layout.column(heading="Show In")
col.prop(ob, "hide_viewport", text="Viewports", toggle=False, invert_checkbox=True)
col.prop(ob, "hide_render", text="Renders", toggle=False, invert_checkbox=True)
if context.engine == 'BLENDER_EEVEE_NEXT':
if ob.type in ('MESH','CURVE','SURFACE','META','FONT','CURVES','POINTCLOUD','VOLUME','LIGHT'):
layout.separator()
col = layout.column(heading="Light Probes")
col.prop(ob, "hide_probe_volume", text="Volume", toggle=False, invert_checkbox=True)
col.prop(ob, "hide_probe_cubemap", text="Cubemap", toggle=False, invert_checkbox=True)
if ob.type == 'GPENCIL':
col = layout.column(heading="Grease Pencil")
@ -398,6 +405,7 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
col.prop(ob, "is_holdout")
class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel, Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
_context_path = "object"

View File

@ -312,6 +312,24 @@ class SCENE_PT_physics(SceneButtonsPanel, Panel):
layout.prop(scene, "gravity")
class SCENE_PT_simulation(SceneButtonsPanel, Panel):
bl_label = "Simulation"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
scene = context.scene
col = layout.column()
col.prop(scene, "use_custom_simulation_range", text="Simulation Range")
subcol = col.column(align=True)
subcol.active = scene.use_custom_simulation_range
subcol.prop(scene, "simulation_frame_start", text="Start")
subcol.prop(scene, "simulation_frame_end", text="End")
class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
bl_label = "Rigid Body World"
bl_options = {'DEFAULT_CLOSED'}
@ -409,6 +427,7 @@ classes = (
SCENE_PT_scene,
SCENE_PT_unit,
SCENE_PT_physics,
SCENE_PT_simulation,
SCENE_PT_keying_sets,
SCENE_PT_keying_set_paths,
SCENE_PT_keyframing_settings,

View File

@ -282,8 +282,7 @@ class DOPESHEET_HT_editor_buttons:
sub = row.row(align=True)
sub.popover(
panel="DOPESHEET_PT_snapping",
icon='NONE',
text="Modes",
text="",
)
row = layout.row(align=True)

View File

@ -25,7 +25,7 @@ class FILEBROWSER_HT_header(Header):
layout.separator_spacer()
if params.asset_library_reference not in {'LOCAL', 'ESSENTIALS'}:
layout.prop(params, "import_type", text="")
layout.prop(params, "import_method", text="")
layout.separator_spacer()

View File

@ -54,8 +54,7 @@ class GRAPH_HT_header(Header):
sub = row.row(align=True)
sub.popover(
panel="GRAPH_PT_snapping",
icon='NONE',
text="Modes",
text="",
)
row = layout.row(align=True)

View File

@ -39,8 +39,7 @@ class NLA_HT_header(Header):
sub = row.row(align=True)
sub.popover(
panel="NLA_PT_snapping",
icon='NONE',
text="Modes",
text="",
)

View File

@ -161,8 +161,6 @@ class NODE_HT_header(Header):
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
else:
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
if snode.node_tree and snode.node_tree.asset_data:
layout.popover(panel="NODE_PT_geometry_node_modifier")
else:
layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool")
if snode.node_tree and snode.node_tree.asset_data:
@ -484,22 +482,6 @@ class NODE_PT_geometry_node_tool_mode(Panel):
row_label.label(text=name, icon=icon)
class NODE_PT_geometry_node_modifier(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Modifier"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
snode = context.space_data
group = snode.node_tree
layout.prop(group, "is_modifier")
class NODE_PT_node_color_presets(PresetPanel, Panel):
"""Predefined node color"""
bl_label = "Color Presets"
@ -976,6 +958,38 @@ class NODE_PT_node_tree_interface(Panel):
layout.use_property_split = False
class NODE_PT_node_tree_properties(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Group"
bl_label = "Properties"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
group = snode.edit_tree
if group is None:
return False
if group.is_embedded_data:
return False
if group.bl_idname != "GeometryNodeTree":
return False
return True
def draw(self, context):
layout = self.layout
snode = context.space_data
group = snode.edit_tree
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.prop(group, "is_modifier")
col.prop(group, "is_tool")
class NODE_UL_simulation_zone_items(bpy.types.UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
@ -1182,12 +1196,12 @@ classes = (
NODE_MT_context_menu,
NODE_MT_view_pie,
NODE_PT_material_slots,
NODE_PT_geometry_node_modifier,
NODE_PT_geometry_node_tool_object_types,
NODE_PT_geometry_node_tool_mode,
NODE_PT_node_color_presets,
NODE_MT_node_tree_interface_context_menu,
NODE_PT_node_tree_interface,
NODE_PT_node_tree_properties,
NODE_PT_active_node_generic,
NODE_PT_active_node_color,
NODE_PT_texture_mapping,

View File

@ -712,7 +712,6 @@ class VIEW3D_PT_slots_vertex_groups(Panel):
class VIEW3D_PT_mask(View3DPanel, Panel):
bl_category = "Tool"
bl_context = ".imagepaint" # dot on purpose (access from topbar)
bl_label = "Masking"
bl_options = {'DEFAULT_CLOSED'}

View File

@ -26,6 +26,7 @@ struct IDProperty;
struct PreviewImage;
typedef void (*PreSaveFn)(void *asset_ptr, struct AssetMetaData *asset_data);
typedef void (*OnMarkAssetFn)(void *asset_ptr, struct AssetMetaData *asset_data);
typedef struct AssetTypeInfo {
/**
@ -33,6 +34,7 @@ typedef struct AssetTypeInfo {
* saved.
*/
PreSaveFn pre_save_fn;
OnMarkAssetFn on_mark_asset_fn;
} AssetTypeInfo;
struct AssetMetaData *BKE_asset_metadata_create(void);

View File

@ -17,7 +17,6 @@
extern "C" {
#endif
struct bDeformGroup;
struct CustomData;
struct CustomDataLayer;
struct ID;
@ -118,12 +117,6 @@ void BKE_id_attributes_default_color_set(struct ID *id, const char *name);
const struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name);
typedef struct AttributeAndDefgroupUniqueNameData {
struct ID *id;
struct bDeformGroup *dg;
} AttributeAndDefgroupUniqueNameData;
bool BKE_id_attribute_and_defgroup_unique_name_check(void *arg, const char *name);
bool BKE_id_attribute_calc_unique_name(struct ID *id, const char *name, char *outname);
const char *BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer);

View File

@ -84,6 +84,10 @@ std::optional<BakePath> get_node_bake_path(const Main &bmain,
const Object &object,
const NodesModifierData &nmd,
int node_id);
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
const Object &object,
const NodesModifierData &nmd,
int node_id);
std::optional<std::string> get_modifier_bake_path(const Main &bmain,
const Object &object,
const NodesModifierData &nmd);

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 25
#define BLENDER_FILE_SUBVERSION 26
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -90,7 +90,6 @@ int *BKE_object_defgroup_flip_map_single(const struct Object *ob,
int BKE_object_defgroup_flip_index(const struct Object *ob, int index, bool use_default);
int BKE_object_defgroup_name_index(const struct Object *ob, const char *name);
void BKE_object_defgroup_unique_name(struct bDeformGroup *dg, struct Object *ob);
bool BKE_defgroup_unique_name_check(void *arg, const char *name);
struct MDeformWeight *BKE_defvert_find_index(const struct MDeformVert *dv, int defgroup);
/**

View File

@ -151,6 +151,38 @@ void BKE_nlatrack_remove(ListBase *tracks, struct NlaTrack *nlt);
*/
void BKE_nlatrack_remove_and_free(ListBase *tracks, struct NlaTrack *nlt, bool do_id_user);
/**
* Compute the length of the passed strip's clip, unless the clip length
* is zero in which case a non-zero value is returned.
*
* WARNING: this function is *very narrow* and special-cased in its
* application. It was introduced as part of the fix for issue #107030,
* as a way to collect a bunch of whac-a-mole inline applications of this
* logic in one place. The logic itself isn't principled in any way,
* and should almost certainly not be used anywhere that it isn't already,
* short of one of those whac-a-mole inline places being overlooked.
*
* The underlying purpose of this function is to ensure that the computed
* clip length for an NLA strip is (in certain places) never zero, in order to
* avoid the strip's scale having to be infinity. In other words, it's a
* hack. But at least now it's a hack collected in one place.
*
*/
float BKE_nla_clip_length_get_nonzero(const NlaStrip *strip);
/**
* Ensure the passed range has non-zero length, using the same logic as
* `BKE_nla_clip_length_get_nonzero` to determine the new non-zero length.
*
* See the documentation for `BKE_nla_clip_length_get_nonzero` for the
* reason this function exists and the issues around its use.
*
* Usage: both `actstart` and `r_actend` should already be set to the
* start/end values of a strip's clip. `r_actend` will be modified
* if necessary to ensure the range is non-zero in length.
*/
void BKE_nla_clip_length_ensure_nonzero(const float *actstart, float *r_actend);
/**
* Create a NLA Strip referencing the given Action.
*/

View File

@ -225,6 +225,9 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
struct bNodeExecData *execdata,
struct GPUNodeStack *in,
struct GPUNodeStack *out);
typedef void (*NodeMaterialXFunction)(void *data,
struct bNode *node,
struct bNodeSocket *out);
/**
* \brief Defines a node type.
@ -339,6 +342,8 @@ typedef struct bNodeType {
NodeExecFunction exec_fn;
/* gpu */
NodeGPUExecFunction gpu_fn;
/* MaterialX */
NodeMaterialXFunction materialx_fn;
/* Get an instance of this node's compositor operation. Freeing the instance is the
* responsibility of the caller. */

View File

@ -250,7 +250,7 @@ static IDProperty *action_asset_type_property(const bAction *action)
return property;
}
static void action_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
static void action_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_data)
{
bAction *action = (bAction *)asset_ptr;
BLI_assert(GS(action->id.name) == ID_AC);
@ -260,7 +260,8 @@ static void action_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
}
static AssetTypeInfo AssetType_AC = {
/*pre_save_fn*/ action_asset_pre_save,
/*pre_save_fn*/ action_asset_metadata_ensure,
/*on_mark_asset_fn*/ action_asset_metadata_ensure,
};
IDTypeInfo IDType_ID_AC = {
@ -1444,17 +1445,12 @@ void BKE_action_frame_range_calc(const bAction *act,
}
if (foundvert || foundmod) {
/* ensure that action is at least 1 frame long (for NLA strips to have a valid length) */
if (min == max) {
max += 1.0f;
}
*r_start = max_ff(min, MINAFRAMEF);
*r_end = min_ff(max, MAXFRAMEF);
}
else {
*r_start = 0.0f;
*r_end = 1.0f;
*r_end = 0.0f;
}
}
@ -1468,10 +1464,7 @@ void BKE_action_frame_range_get(const bAction *act, float *r_start, float *r_end
BKE_action_frame_range_calc(act, false, r_start, r_end);
}
/* Ensure that action is at least 1 frame long (for NLA strips to have a valid length). */
if (*r_start >= *r_end) {
*r_end = *r_start + 1.0f;
}
BLI_assert(*r_start <= *r_end);
}
bool BKE_action_is_cyclic(const bAction *act)

View File

@ -223,4 +223,66 @@ TEST(action_assets, BKE_action_has_single_frame)
}
}
TEST(action, BKE_action_frame_range_calc)
{
float start, end;
/* No FCurves. */
{
const bAction empty = {{nullptr}};
BKE_action_frame_range_calc(&empty, false, &start, &end);
EXPECT_FLOAT_EQ(start, 0.0f);
EXPECT_FLOAT_EQ(end, 0.0f);
}
/* One curve with one key. */
{
FCurve fcu = {nullptr};
std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 1);
add_keyframe(&fcu, 1.0f, 2.0f);
bAction action = {{nullptr}};
BLI_addtail(&action.curves, &fcu);
BKE_action_frame_range_calc(&action, false, &start, &end);
EXPECT_FLOAT_EQ(start, 1.0f);
EXPECT_FLOAT_EQ(end, 1.0f);
}
/* Two curves with one key each on different frames. */
{
FCurve fcu1 = {nullptr};
FCurve fcu2 = {nullptr};
std::unique_ptr<BezTriple[]> bezt1 = allocate_keyframes(&fcu1, 1);
std::unique_ptr<BezTriple[]> bezt2 = allocate_keyframes(&fcu2, 1);
add_keyframe(&fcu1, 1.0f, 2.0f);
add_keyframe(&fcu2, 1.5f, 2.0f);
bAction action = {{nullptr}};
BLI_addtail(&action.curves, &fcu1);
BLI_addtail(&action.curves, &fcu2);
BKE_action_frame_range_calc(&action, false, &start, &end);
EXPECT_FLOAT_EQ(start, 1.0f);
EXPECT_FLOAT_EQ(end, 1.5f);
}
/* One curve with two keys. */
{
FCurve fcu = {nullptr};
std::unique_ptr<BezTriple[]> bezt = allocate_keyframes(&fcu, 2);
add_keyframe(&fcu, 1.0f, 2.0f);
add_keyframe(&fcu, 1.5f, 2.0f);
bAction action = {{nullptr}};
BLI_addtail(&action.curves, &fcu);
BKE_action_frame_range_calc(&action, false, &start, &end);
EXPECT_FLOAT_EQ(start, 1.0f);
EXPECT_FLOAT_EQ(end, 1.5f);
}
/* TODO: action with fcurve modifiers. */
}
} // namespace blender::bke::tests

View File

@ -3223,10 +3223,9 @@ static void animsys_create_action_track_strip(const AnimData *adt,
* (which making new strips doesn't do due to the troublesome nature of that). */
BKE_action_frame_range_calc(
r_action_strip->act, true, &r_action_strip->actstart, &r_action_strip->actend);
BKE_nla_clip_length_ensure_nonzero(&r_action_strip->actstart, &r_action_strip->actend);
r_action_strip->start = r_action_strip->actstart;
r_action_strip->end = IS_EQF(r_action_strip->actstart, r_action_strip->actend) ?
(r_action_strip->actstart + 1.0f) :
(r_action_strip->actend);
r_action_strip->end = r_action_strip->actend;
r_action_strip->blendmode = adt->act_blendmode;
r_action_strip->extendmode = adt->act_extendmode;

View File

@ -31,7 +31,6 @@
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.h"
@ -224,14 +223,13 @@ bool BKE_id_attribute_rename(ID *id,
return true;
}
bool BKE_id_attribute_and_defgroup_unique_name_check(void *arg, const char *name)
{
AttributeAndDefgroupUniqueNameData *data = static_cast<AttributeAndDefgroupUniqueNameData *>(
arg);
struct AttrUniqueData {
ID *id;
};
if (BKE_id_supports_vertex_groups(data->id) && BKE_defgroup_unique_name_check(data, name)) {
return true;
}
static bool unique_name_cb(void *arg, const char *name)
{
AttrUniqueData *data = (AttrUniqueData *)arg;
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(data->id, info);
@ -256,7 +254,7 @@ bool BKE_id_attribute_and_defgroup_unique_name_check(void *arg, const char *name
bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
{
AttributeAndDefgroupUniqueNameData data{id, nullptr};
AttrUniqueData data{id};
const int name_maxncpy = CustomData_name_maxncpy_calc(name);
@ -264,10 +262,8 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
* NOTE: We only call IFACE_() if needed to avoid locale lookup overhead. */
BLI_strncpy_utf8(outname, (name && name[0]) ? name : IFACE_("Attribute"), name_maxncpy);
/* Avoid name collisions with vertex groups and other attributes. */
const char *defname = ""; /* Dummy argument, never used as `name` is never zero length. */
return BLI_uniquename_cb(
BKE_id_attribute_and_defgroup_unique_name_check, &data, defname, '.', outname, name_maxncpy);
return BLI_uniquename_cb(unique_name_cb, &data, defname, '.', outname, name_maxncpy);
}
CustomDataLayer *BKE_id_attribute_new(ID *id,

View File

@ -71,17 +71,52 @@ std::optional<bake::BakePath> get_node_bake_path(const Main &bmain,
const NodesModifierData &nmd,
int node_id)
{
const NodesModifierBake *bake = nmd.find_bake(node_id);
if (bake == nullptr) {
return std::nullopt;
}
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH) {
if (StringRef(bake->directory).is_empty()) {
return std::nullopt;
}
const char *base_path = ID_BLEND_PATH(&bmain, &object.id);
char absolute_bake_dir[FILE_MAX];
STRNCPY(absolute_bake_dir, bake->directory);
BLI_path_abs(absolute_bake_dir, base_path);
return bake::BakePath::from_single_root(absolute_bake_dir);
}
const std::optional<std::string> modifier_bake_path = get_modifier_bake_path(bmain, object, nmd);
if (!modifier_bake_path) {
return std::nullopt;
}
char bake_dir[FILE_MAX];
BLI_path_join(
bake_dir, sizeof(bake_dir), modifier_bake_path->c_str(), std::to_string(node_id).c_str());
return bake::BakePath::from_single_root(bake_dir);
}
char zone_bake_dir[FILE_MAX];
BLI_path_join(zone_bake_dir,
sizeof(zone_bake_dir),
modifier_bake_path->c_str(),
std::to_string(node_id).c_str());
return bake::BakePath::from_single_root(zone_bake_dir);
static IndexRange fix_frame_range(const int start, const int end)
{
const int num_frames = std::max(1, end - start + 1);
return IndexRange(start, num_frames);
}
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
const Object & /*object*/,
const NodesModifierData &nmd,
int node_id)
{
const NodesModifierBake *bake = nmd.find_bake(node_id);
if (bake == nullptr) {
return std::nullopt;
}
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE) {
return fix_frame_range(bake->frame_start, bake->frame_end);
}
if (scene.flag & SCE_CUSTOM_SIMULATION_RANGE) {
return fix_frame_range(scene.simulation_frame_start, scene.simulation_frame_end);
}
return fix_frame_range(scene.r.sfra, scene.r.efra);
}
/**

View File

@ -687,9 +687,14 @@ int BKE_object_defgroup_flip_index(const Object *ob, int index, const bool use_d
return (flip_index == -1 && use_default) ? index : flip_index;
}
static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, ID *id)
struct DeformGroupUniqueNameData {
Object *ob;
bDeformGroup *dg;
};
static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, Object *ob)
{
const ListBase *defbase = BKE_id_defgroup_list_get(id);
const ListBase *defbase = BKE_object_defgroup_list(ob);
LISTBASE_FOREACH (bDeformGroup *, curdef, defbase) {
if (dg != curdef) {
@ -702,32 +707,16 @@ static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, ID *id)
return false;
}
bool BKE_defgroup_unique_name_check(void *arg, const char *name)
static bool defgroup_unique_check(void *arg, const char *name)
{
AttributeAndDefgroupUniqueNameData *data = static_cast<AttributeAndDefgroupUniqueNameData *>(
arg);
return defgroup_find_name_dupe(name, data->dg, data->id);
DeformGroupUniqueNameData *data = static_cast<DeformGroupUniqueNameData *>(arg);
return defgroup_find_name_dupe(name, data->dg, data->ob);
}
void BKE_object_defgroup_unique_name(bDeformGroup *dg, Object *ob)
{
/* Avoid name collisions with other vertex groups and (mesh) attributes. */
if (ob->type == OB_MESH) {
Mesh *me = static_cast<Mesh *>(ob->data);
AttributeAndDefgroupUniqueNameData data{&me->id, dg};
BLI_uniquename_cb(BKE_id_attribute_and_defgroup_unique_name_check,
&data,
DATA_("Group"),
'.',
dg->name,
sizeof(dg->name));
}
else {
AttributeAndDefgroupUniqueNameData data{static_cast<ID *>(ob->data), dg};
BLI_uniquename_cb(
BKE_defgroup_unique_name_check, &data, DATA_("Group"), '.', dg->name, sizeof(dg->name));
}
DeformGroupUniqueNameData data{ob, dg};
BLI_uniquename_cb(defgroup_unique_check, &data, DATA_("Group"), '.', dg->name, sizeof(dg->name));
}
float BKE_defvert_find_weight(const MDeformVert *dvert, const int defgroup)

View File

@ -446,6 +446,21 @@ NlaTrack *BKE_nlatrack_new_tail(ListBase *nla_tracks, const bool is_liboverride)
return BKE_nlatrack_new_after(nla_tracks, (NlaTrack *)nla_tracks->last, is_liboverride);
}
float BKE_nla_clip_length_get_nonzero(const NlaStrip *strip)
{
if (strip->actend <= strip->actstart) {
return 1.0f;
}
return strip->actend - strip->actstart;
}
void BKE_nla_clip_length_ensure_nonzero(const float *actstart, float *r_actend)
{
if (*r_actend <= *actstart) {
*r_actend = *actstart + 1.0f;
}
}
NlaStrip *BKE_nlastrip_new(bAction *act)
{
NlaStrip *strip;
@ -478,13 +493,11 @@ NlaStrip *BKE_nlastrip_new(bAction *act)
strip->act = act;
id_us_plus(&act->id);
/* determine initial range
* - strip length cannot be 0... ever...
*/
/* determine initial range */
BKE_action_frame_range_get(strip->act, &strip->actstart, &strip->actend);
BKE_nla_clip_length_ensure_nonzero(&strip->actstart, &strip->actend);
strip->start = strip->actstart;
strip->end = IS_EQF(strip->actstart, strip->actend) ? (strip->actstart + 1.0f) : strip->actend;
strip->end = strip->actend;
/* strip should be referenced as-is */
strip->scale = 1.0f;
@ -606,7 +619,7 @@ void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, bool do_id_us
*/
static float nlastrip_get_frame_actionclip(NlaStrip *strip, float cframe, short mode)
{
float actlength, scale;
float scale;
// float repeat; // UNUSED
/* get number of repeats */
@ -624,10 +637,7 @@ static float nlastrip_get_frame_actionclip(NlaStrip *strip, float cframe, short
scale = fabsf(strip->scale);
/* length of referenced action */
actlength = strip->actend - strip->actstart;
if (IS_EQF(actlength, 0.0f)) {
actlength = 1.0f;
}
const float actlength = BKE_nla_clip_length_get_nonzero(strip);
/* reversed = play strip backwards */
if (strip->flag & NLASTRIP_FLAG_REVERSE) {
@ -1587,6 +1597,7 @@ void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
prev_actstart = strip->actstart;
BKE_action_frame_range_get(strip->act, &strip->actstart, &strip->actend);
BKE_nla_clip_length_ensure_nonzero(&strip->actstart, &strip->actend);
/* Set start such that key's do not visually move, to preserve the overall animation result. */
strip->start += (strip->actstart - prev_actstart) * strip->scale;
@ -1595,7 +1606,7 @@ void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
}
void BKE_nlastrip_recalculate_bounds(NlaStrip *strip)
{
float actlen, mapping;
float mapping;
/* sanity checks
* - must have a strip
@ -1606,10 +1617,7 @@ void BKE_nlastrip_recalculate_bounds(NlaStrip *strip)
}
/* calculate new length factors */
actlen = strip->actend - strip->actstart;
if (IS_EQF(actlen, 0.0f)) {
actlen = 1.0f;
}
const float actlen = BKE_nla_clip_length_get_nonzero(strip);
mapping = strip->scale * strip->repeat;

View File

@ -66,6 +66,52 @@ TEST(nla_strip, BKE_nlastrips_add_strip)
EXPECT_TRUE(BKE_nlastrips_add_strip(&strips, &strip2));
}
TEST(nla_strip, BKE_nla_clip_length_get_nonzero)
{
NlaStrip strip{};
strip.actstart = 2.0f;
strip.actend = 4.0f;
EXPECT_FLOAT_EQ(BKE_nla_clip_length_get_nonzero(&strip), 2.0f);
strip.actstart = 2.0f;
strip.actend = 3.0f;
EXPECT_FLOAT_EQ(BKE_nla_clip_length_get_nonzero(&strip), 1.0f);
strip.actstart = 2.0f;
strip.actend = 2.0625f;
EXPECT_FLOAT_EQ(BKE_nla_clip_length_get_nonzero(&strip), 0.0625f);
strip.actstart = 2.0f;
strip.actend = 2.0f;
EXPECT_FLOAT_EQ(BKE_nla_clip_length_get_nonzero(&strip), 1.0f);
}
TEST(nla_strip, BKE_nla_clip_length_ensure_nonzero)
{
float start, end;
start = 2.0f;
end = 4.0f;
BKE_nla_clip_length_ensure_nonzero(&start, &end);
EXPECT_FLOAT_EQ(end, 4.0f);
start = 2.0f;
end = 3.0f;
BKE_nla_clip_length_ensure_nonzero(&start, &end);
EXPECT_FLOAT_EQ(end, 3.0f);
start = 2.0f;
end = 2.0625f;
BKE_nla_clip_length_ensure_nonzero(&start, &end);
EXPECT_FLOAT_EQ(end, 2.0625f);
start = 2.0f;
end = 2.0f;
BKE_nla_clip_length_ensure_nonzero(&start, &end);
EXPECT_FLOAT_EQ(end, 3.0f);
}
TEST(nla_track, BKE_nlatrack_remove_strip)
{
NlaTrack track{};

View File

@ -1127,10 +1127,16 @@ static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData * /*asset_da
node_update_asset_metadata(*static_cast<bNodeTree *>(asset_ptr));
}
static void node_tree_asset_on_mark_asset(void *asset_ptr, AssetMetaData * /*asset_data*/)
{
node_update_asset_metadata(*static_cast<bNodeTree *>(asset_ptr));
}
} // namespace blender::bke
static AssetTypeInfo AssetType_NT = {
/*pre_save_fn*/ blender::bke::node_tree_asset_pre_save,
/*on_mark_asset_fn*/ blender::bke::node_tree_asset_on_mark_asset,
};
IDTypeInfo IDType_ID_NT = {

View File

@ -607,3 +607,24 @@ bool bNodeTree::node_id_path_from_nested_node_ref(const int32_t nested_node_id,
}
return group->node_id_path_from_nested_node_ref(ref->path.id_in_node, r_node_ids);
}
const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id) const
{
const bNestedNodeRef *ref = this->find_nested_node_ref(nested_node_id);
if (ref == nullptr) {
return nullptr;
}
const int32_t node_id = ref->path.node_id;
const bNode *node = this->node_by_id(node_id);
if (node == nullptr) {
return nullptr;
}
if (!node->is_group()) {
return node;
}
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
if (group == nullptr) {
return nullptr;
}
return group->find_nested_node(ref->path.id_in_node);
}

View File

@ -1032,7 +1032,7 @@ static IDProperty *object_asset_dimensions_property(Object *ob)
return property;
}
static void object_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
static void object_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_data)
{
Object *ob = (Object *)asset_ptr;
BLI_assert(GS(ob->id.name) == ID_OB);
@ -1045,7 +1045,8 @@ static void object_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
}
static AssetTypeInfo AssetType_OB = {
/*pre_save_fn*/ object_asset_pre_save,
/*pre_save_fn*/ object_asset_metadata_ensure,
/*on_mark_asset_fn*/ object_asset_metadata_ensure,
};
IDTypeInfo IDType_ID_OB = {

View File

@ -2476,14 +2476,14 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
FOREACH_NODETREE_END;
if (!DNA_struct_member_exists(fd->filesdna, "FileAssetSelectParams", "short", "import_type")) {
if (!DNA_struct_member_exists(fd->filesdna, "FileAssetSelectParams", "short", "import_method")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
if (sfile->asset_params) {
sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
sfile->asset_params->import_method = FILE_ASSET_IMPORT_APPEND;
}
}
}
@ -3035,9 +3035,9 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
FILE_PARAMS_FLAG_UNUSED_3 | FILE_PATH_TOKENS_ALLOW);
}
/* New default import type: Append with reuse. */
/* New default import method: Append with reuse. */
if (sfile->asset_params) {
sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE;
sfile->asset_params->import_method = FILE_ASSET_IMPORT_APPEND_REUSE;
}
break;
}
@ -4324,8 +4324,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
/* When an asset browser uses the default import method, make it follow the new
* preference setting. This means no effective default behavior change. */
if (sfile->asset_params->import_type == FILE_ASSET_IMPORT_APPEND_REUSE) {
sfile->asset_params->import_type = FILE_ASSET_IMPORT_FOLLOW_PREFS;
if (sfile->asset_params->import_method == FILE_ASSET_IMPORT_APPEND_REUSE) {
sfile->asset_params->import_method = FILE_ASSET_IMPORT_FOLLOW_PREFS;
}
}
}

View File

@ -977,6 +977,34 @@ static void version_node_group_split_socket(bNodeTreeInterface &tree_interface,
csocket->flag &= ~NODE_INTERFACE_SOCKET_OUTPUT;
}
static void enable_geometry_nodes_is_modifier(Main &bmain)
{
/* Any node group with a first socket geometry output can potentially be a modifier. Previously
* this wasn't an explicit option, so better to enable too many groups rather than too few. */
LISTBASE_FOREACH (bNodeTree *, group, &bmain.nodetrees) {
if (group->type != NTREE_GEOMETRY) {
continue;
}
group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (item.item_type != NODE_INTERFACE_SOCKET) {
return true;
}
const auto &socket = reinterpret_cast<const bNodeTreeInterfaceSocket &>(item);
if ((socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) == 0) {
return true;
}
if (!STREQ(socket.socket_type, "NodeSocketGeometry")) {
return true;
}
if (!group->geometry_node_asset_traits) {
group->geometry_node_asset_traits = MEM_new<GeometryNodeAssetTraits>(__func__);
}
group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
return false;
});
}
}
void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) {
@ -1443,6 +1471,15 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 26)) {
enable_geometry_nodes_is_modifier(*bmain);
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->simulation_frame_start = scene->r.sfra;
scene->simulation_frame_end = scene->r.efra;
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -865,6 +865,10 @@ void blo_do_versions_userdef(UserDef *userdef)
userdef->uiflag &= ~USER_UIFLAG_UNUSED_4;
}
if (!USER_VERSION_ATLEAST(400, 26)) {
userdef->animation_flag |= USER_ANIM_SHOW_CHANNEL_GROUP_COLORS;
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -561,7 +561,8 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl
engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl
engines/eevee_next/shaders/eevee_subsurface_eval_frag.glsl
engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl
engines/eevee_next/shaders/eevee_subsurface_setup_comp.glsl
engines/eevee_next/shaders/eevee_surf_capture_frag.glsl
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl

View File

@ -83,6 +83,11 @@
#define RAYTRACE_VARIANCE_FORMAT GPU_R16F
#define RAYTRACE_TILEMASK_FORMAT GPU_R8UI
/* Sub-Surface Scattering. */
#define SUBSURFACE_GROUP_SIZE RAYTRACE_GROUP_SIZE
#define SUBSURFACE_RADIANCE_FORMAT GPU_R11F_G11F_B10F
#define SUBSURFACE_OBJECT_ID_FORMAT GPU_R16UI
/* Minimum visibility size. */
#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16

View File

@ -25,6 +25,8 @@
#include "DNA_particle_types.h"
#include "draw_common.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
@ -258,12 +260,10 @@ void Instance::object_sync_render(void *instance_,
UNUSED_VARS(engine, depsgraph);
Instance &inst = *reinterpret_cast<Instance *>(instance_);
if (inst.visibility_collection != nullptr) {
bool object_part_of_group = BKE_collection_has_object(inst.visibility_collection, ob);
if (object_part_of_group == inst.visibility_collection_invert) {
return;
}
if (inst.is_baking() && ob->visibility_flag & OB_HIDE_PROBE_VOLUME) {
return;
}
inst.object_sync(ob);
}
@ -291,9 +291,20 @@ void Instance::render_sync()
manager->begin_sync();
draw::hair_init();
draw::curves_init();
begin_sync();
DRW_render_object_iter(this, render, depsgraph, object_sync_render);
draw::hair_update(*manager);
draw::curves_update(*manager);
draw::hair_free();
draw::curves_free();
velocity.geometry_steps_fill();
end_sync();
manager->end_sync();
@ -576,11 +587,6 @@ void Instance::light_bake_irradiance(
irradiance_cache.bake.init(probe);
custom_pipeline_wrapper([&]() {
const ::LightProbe *light_probe = static_cast<const ::LightProbe *>(probe.data);
visibility_collection = light_probe->visibility_grp;
visibility_collection_invert = (light_probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP) != 0;
manager->begin_sync();
render_sync();
manager->end_sync();

View File

@ -98,9 +98,6 @@ class Instance {
const DRWView *drw_view;
const View3D *v3d;
const RegionView3D *rv3d;
/** Only available when baking irradiance volume. */
Collection *visibility_collection = nullptr;
bool visibility_collection_invert = false;
/** True if the grease pencil engine might be running. */
bool gpencil_engine_enabled;

View File

@ -203,9 +203,21 @@ void IrradianceCache::set_view(View & /*view*/)
/* Volumes are identical. Any arbitrary criteria can be used to sort them.
* Use position to avoid unstable result caused by depsgraph non deterministic eval
* order. This could also become a priority parameter. */
return a->object_to_world.location()[0] < b->object_to_world.location()[0] ||
a->object_to_world.location()[1] < b->object_to_world.location()[1] ||
a->object_to_world.location()[2] < b->object_to_world.location()[2];
float3 _a = a->object_to_world.location();
float3 _b = b->object_to_world.location();
if (_a.x != _b.x) {
return _a.x < _b.x;
}
else if (_a.y != _b.y) {
return _a.y < _b.y;
}
else if (_a.z != _b.z) {
return _a.z < _b.z;
}
else {
/* Fallback to memory address, since there's no good alternative.*/
return a < b;
}
});
/* Insert grids in UBO in sorted order. */
@ -421,6 +433,10 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb)
LightProbeGridCacheFrame *cache = grid.cache->grid_static_cache;
if (cache == nullptr) {
continue;
}
switch (inst_.debug_mode) {
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_NORMAL:
case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_CLUSTER:
@ -618,9 +634,13 @@ void IrradianceBake::init(const Object &probe_object)
surfel_density_ = lightprobe->surfel_density;
min_distance_to_surface_ = lightprobe->grid_surface_bias;
max_virtual_offset_ = lightprobe->grid_escape_bias;
clip_distance_ = lightprobe->clipend;
capture_world_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_WORLD);
capture_indirect_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_INDIRECT);
capture_emission_ = (lightprobe->grid_flag & LIGHTPROBE_GRID_CAPTURE_EMISSION);
/* Initialize views data, since they're used by other modules.*/
surfel_raster_views_sync(float3(0.0f), float3(1.0f), float4x4::identity());
}
void IrradianceBake::sync()
@ -724,24 +744,61 @@ void IrradianceBake::sync()
}
}
void IrradianceBake::surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max)
void IrradianceBake::surfel_raster_views_sync(float3 scene_min,
float3 scene_max,
float4x4 probe_to_world)
{
using namespace blender::math;
grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (scene_max - scene_min)));
float3 location, scale;
Quaternion rotation;
to_loc_rot_scale(probe_to_world, location, rotation, scale);
/* Remove scale from view matrix. */
float4x4 viewinv = from_loc_rot_scale<float4x4>(location, rotation, float3(1.0f));
float4x4 viewmat = invert(viewinv);
/* Compute the intersection between the grid and the scene extents. */
float3 extent_min = float3(FLT_MAX);
float3 extent_max = float3(-FLT_MAX);
for (int x : {0, 1}) {
for (int y : {0, 1}) {
for (int z : {0, 1}) {
float3 ws_corner = scene_min + ((scene_max - scene_min) * float3(x, y, z));
float3 ls_corner = transform_point(viewmat, ws_corner);
extent_min = min(extent_min, ls_corner);
extent_max = max(extent_max, ls_corner);
}
}
}
/* Clip distance is added to every axis in both directions, not just Z. */
float3 target_extent = scale + clip_distance_;
extent_min = max(extent_min, -target_extent);
extent_max = min(extent_max, target_extent);
grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (extent_max - extent_min)));
grid_pixel_extent_ = min(grid_pixel_extent_, int3(16384));
float3 ls_midpoint = midpoint(extent_min, extent_max);
scene_bound_sphere_ = float4(transform_point(viewinv, ls_midpoint),
distance(extent_min, extent_max) / 2.0f);
/* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to
* make any difference. The bottleneck is still the light propagation loop. */
auto sync_view = [&](View &view, CartesianBasis basis) {
float3 extent_min = transform_point(invert(basis), scene_min);
float3 extent_max = transform_point(invert(basis), scene_max);
float4x4 winmat = projection::orthographic(
extent_min.x, extent_max.x, extent_min.y, extent_max.y, -extent_min.z, -extent_max.z);
float4x4 viewinv = from_rotation<float4x4>(to_quaternion<float>(basis));
float4x4 capture_viewinv = viewinv * from_rotation<float4x4>(basis);
float3 capture_extent_min = transform_point(invert(basis), extent_min);
float3 capture_extent_max = transform_point(invert(basis), extent_max);
float4x4 capture_winmat = projection::orthographic(capture_extent_min.x,
capture_extent_max.x,
capture_extent_min.y,
capture_extent_max.y,
-capture_extent_min.z,
-capture_extent_max.z);
view.visibility_test(false);
view.sync(invert(viewinv), winmat);
view.sync(invert(capture_viewinv), capture_winmat);
};
sync_view(view_x_, basis_x_);
@ -861,10 +918,7 @@ void IrradianceBake::surfels_create(const Object &probe_object)
float epsilon = 1.0f / surfel_density_;
scene_min -= epsilon;
scene_max += epsilon;
surfel_raster_views_sync(scene_min, scene_max);
scene_bound_sphere_ = float4(midpoint(scene_max, scene_min),
distance(scene_max, scene_min) / 2.0f);
surfel_raster_views_sync(scene_min, scene_max, float4x4(probe_object.object_to_world));
DRW_stats_group_end();

View File

@ -118,6 +118,10 @@ class IrradianceBake {
* Avoids samples to be too far from their actual origin.
*/
float max_virtual_offset_ = 0.1f;
/**
* Surfaces outside the Grid won't generate surfels above this distance.
*/
float clip_distance_;
/** True if world lighting is recorded during irradiance capture. */
bool capture_world_ = false;
@ -133,7 +137,7 @@ class IrradianceBake {
void sync();
/** Create the views used to rasterize the scene into surfel representation. */
void surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max);
void surfel_raster_views_sync(float3 scene_min, float3 scene_max, float4x4 probe_to_world);
/** Create a surfel representation of the scene from the probe using the capture pipeline. */
void surfels_create(const Object &probe_object);
/** Evaluate direct lighting (and also clear the surfels radiance). */

View File

@ -595,10 +595,6 @@ void DeferredLayer::render(View &main_view,
inst_.manager->submit(eval_light_ps_, render_view);
if (closure_bits_ & CLOSURE_SSS) {
inst_.subsurface.render(render_view, combined_fb, direct_diffuse_tx_);
}
RayTraceResult diffuse_result = inst_.raytracing.trace(rt_buffer,
radiance_feedback_tx_,
radiance_feedback_persmat_,
@ -619,6 +615,8 @@ void DeferredLayer::render(View &main_view,
indirect_reflect_tx_ = reflect_result.get();
indirect_refract_tx_ = refract_result.get();
inst_.subsurface.render(direct_diffuse_tx_, indirect_diffuse_tx_, closure_bits_, render_view);
GPU_framebuffer_bind(combined_fb);
inst_.manager->submit(combine_ps_);

View File

@ -230,8 +230,10 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_shadow_tag_usage_transparent";
case SHADOW_TILEMAP_TAG_USAGE_VOLUME:
return "eevee_shadow_tag_usage_volume";
case SUBSURFACE_EVAL:
return "eevee_subsurface_eval";
case SUBSURFACE_CONVOLVE:
return "eevee_subsurface_convolve";
case SUBSURFACE_SETUP:
return "eevee_subsurface_setup";
case SURFEL_CLUSTER_BUILD:
return "eevee_surfel_cluster_build";
case SURFEL_LIGHT:

View File

@ -114,7 +114,8 @@ enum eShaderType {
SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT,
SHADOW_TILEMAP_TAG_USAGE_VOLUME,
SUBSURFACE_EVAL,
SUBSURFACE_CONVOLVE,
SUBSURFACE_SETUP,
SURFEL_CLUSTER_BUILD,
SURFEL_LIGHT,

View File

@ -1402,6 +1402,7 @@ using LightDataBuf = draw::StorageArrayBuffer<LightData, LIGHT_CHUNK>;
using MotionBlurDataBuf = draw::UniformBuffer<MotionBlurData>;
using MotionBlurTileIndirectionBuf = draw::StorageBuffer<MotionBlurTileIndirection, true>;
using RayTraceTileBuf = draw::StorageArrayBuffer<uint, 1024, true>;
using SubsurfaceTileBuf = RayTraceTileBuf;
using ReflectionProbeDataBuf =
draw::UniformArrayBuffer<ReflectionProbeData, REFLECTION_PROBES_MAX>;
using SamplingDataBuf = draw::StorageBuffer<SamplingData>;

View File

@ -26,33 +26,83 @@ void SubsurfaceModule::end_sync()
/* Convert sample count from old implementation which was using a separable filter. */
/* TODO(fclem) better remapping. */
// data_.sample_len = square_f(1 + 2 * inst_.scene->eevee.sss_samples);
data_.sample_len = 55;
data_.sample_len = 16;
}
subsurface_ps_.init();
subsurface_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL |
DRW_STATE_BLEND_ADD_FULL);
subsurface_ps_.state_stencil(0x00u, 0xFFu, CLOSURE_SSS);
subsurface_ps_.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_EVAL));
inst_.bind_uniform_data(&subsurface_ps_);
inst_.hiz_buffer.bind_resources(&subsurface_ps_);
inst_.gbuffer.bind_resources(&subsurface_ps_);
subsurface_ps_.bind_texture("radiance_tx", &diffuse_light_tx_);
subsurface_ps_.bind_image(RBUFS_COLOR_SLOT, &inst_.render_buffers.rp_color_tx);
/** NOTE: Not used in the shader, but we bind it to avoid debug warnings. */
subsurface_ps_.bind_image(RBUFS_VALUE_SLOT, &inst_.render_buffers.rp_value_tx);
{
PassSimple &pass = setup_ps_;
pass.init();
pass.state_set(DRW_STATE_NO_DRAW);
pass.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_SETUP));
inst_.gbuffer.bind_resources(&pass);
pass.bind_texture("depth_tx", &inst_.render_buffers.depth_tx);
pass.bind_image("direct_light_img", &direct_light_tx_);
pass.bind_image("indirect_light_img", &indirect_light_tx_);
pass.bind_image("object_id_img", &object_id_tx_);
pass.bind_image("radiance_img", &radiance_tx_);
pass.bind_ssbo("convolve_tile_buf", &convolve_tile_buf_);
pass.bind_ssbo("convolve_dispatch_buf", &convolve_dispatch_buf_);
pass.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
pass.dispatch(&setup_dispatch_size_);
}
{
/* Clamping to border color allows to always load ID 0 for out of view samples and discard
* their influence. Also disable filtering to avoid light bleeding between different objects
* and loading invalid interpolated IDs. */
GPUSamplerState sampler = {GPU_SAMPLER_FILTERING_DEFAULT,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER,
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER,
GPU_SAMPLER_CUSTOM_COMPARE,
GPU_SAMPLER_STATE_TYPE_PARAMETERS};
subsurface_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
subsurface_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
PassSimple &pass = convolve_ps_;
pass.init();
pass.state_set(DRW_STATE_NO_DRAW);
pass.shader_set(inst_.shaders.static_shader_get(SUBSURFACE_CONVOLVE));
inst_.bind_uniform_data(&pass);
inst_.gbuffer.bind_resources(&pass);
pass.bind_texture("radiance_tx", &radiance_tx_, sampler);
pass.bind_texture("depth_tx", &inst_.render_buffers.depth_tx, sampler);
pass.bind_texture("object_id_tx", &object_id_tx_, sampler);
pass.bind_image("out_direct_light_img", &direct_light_tx_);
pass.bind_image("out_indirect_light_img", &indirect_light_tx_);
pass.bind_ssbo("tiles_coord_buf", &convolve_tile_buf_);
pass.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE);
pass.dispatch(convolve_dispatch_buf_);
}
}
void SubsurfaceModule::render(View &view, Framebuffer &fb, Texture &diffuse_light_tx)
void SubsurfaceModule::render(GPUTexture *direct_diffuse_light_tx,
GPUTexture *indirect_diffuse_light_tx,
eClosureBits active_closures,
View &view)
{
if (!(active_closures & CLOSURE_SSS)) {
return;
}
precompute_samples_location();
fb.bind();
diffuse_light_tx_ = *&diffuse_light_tx;
inst_.manager->submit(subsurface_ps_, view);
int2 render_extent = inst_.film.render_extent_get();
setup_dispatch_size_ = int3(math::divide_ceil(render_extent, int2(SUBSURFACE_GROUP_SIZE)), 1);
const int convolve_tile_count = setup_dispatch_size_.x * setup_dispatch_size_.y;
convolve_tile_buf_.resize(ceil_to_multiple_u(convolve_tile_count, 512));
direct_light_tx_ = direct_diffuse_light_tx;
indirect_light_tx_ = indirect_diffuse_light_tx;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
object_id_tx_.acquire(render_extent, SUBSURFACE_OBJECT_ID_FORMAT, usage);
radiance_tx_.acquire(render_extent, SUBSURFACE_RADIANCE_FORMAT, usage);
convolve_dispatch_buf_.clear_to_zero();
inst_.manager->submit(setup_ps_, view);
inst_.manager->submit(convolve_ps_, view);
object_id_tx_.release();
radiance_tx_.release();
}
void SubsurfaceModule::precompute_samples_location()

View File

@ -36,9 +36,19 @@ struct SubsurfaceModule {
/** Contains samples locations. */
SubsurfaceData &data_;
/** Scene diffuse irradiance. Pointer binded at sync time, set at render time. */
GPUTexture *diffuse_light_tx_;
/** Subsurface eval pass. Runs after the deferred pass. */
PassSimple subsurface_ps_ = {"Subsurface"};
GPUTexture *direct_light_tx_;
GPUTexture *indirect_light_tx_;
/** Input radiance packed with surface ID. */
TextureFromPool radiance_tx_;
TextureFromPool object_id_tx_;
/** Setup pass fill the radiance_id_tx_ for faster convolution. */
PassSimple setup_ps_ = {"Subsurface.Prepare"};
int3 setup_dispatch_size_ = {1, 1, 1};
/** Screen space convolution pass. */
PassSimple convolve_ps_ = {"Subsurface.Convolve"};
SubsurfaceTileBuf convolve_tile_buf_;
DispatchIndirectBuf convolve_dispatch_buf_;
public:
SubsurfaceModule(Instance &inst, SubsurfaceData &data) : inst_(inst), data_(data)
@ -51,7 +61,12 @@ struct SubsurfaceModule {
void end_sync();
void render(View &view, Framebuffer &fb, Texture &diffuse_light_tx);
/* Process the direct & indirect diffuse light buffers using screen space subsurface scattering.
* Result is stored in the direct light texture. */
void render(GPUTexture *direct_diffuse_light_tx,
GPUTexture *indirect_diffuse_light_tx,
eClosureBits active_closures,
View &view);
private:
void precompute_samples_location();

View File

@ -136,7 +136,7 @@ void SyncModule::sync_mesh(Object *ob,
bool is_shadow_caster = false;
bool is_alpha_blend = false;
bool do_probe_sync = inst_.do_probe_sync();
bool do_probe_sync = inst_.do_probe_sync() && !(ob->visibility_flag & OB_HIDE_PROBE_CUBEMAP);
for (auto i : material_array.gpu_materials.index_range()) {
GPUBatch *geom = mat_geom[i];
if (geom == nullptr) {
@ -209,7 +209,7 @@ bool SyncModule::sync_sculpt(Object *ob,
bool is_shadow_caster = false;
bool is_alpha_blend = false;
bool do_probe_sync = inst_.do_probe_sync();
bool do_probe_sync = inst_.do_probe_sync() && !(ob->visibility_flag & OB_HIDE_PROBE_CUBEMAP);
for (SculptBatch &batch :
sculpt_batches_per_material_get(ob_ref.object, material_array.gpu_materials))
{

View File

@ -27,6 +27,8 @@
#include "eevee_shader_shared.hh"
#include "eevee_velocity.hh"
#include "draw_common.hh"
namespace blender::eevee {
/* -------------------------------------------------------------------- */
@ -100,7 +102,17 @@ void VelocityModule::step_sync(eVelocityStep step, float time)
step_ = step;
object_steps_usage[step_] = 0;
step_camera_sync();
draw::hair_init();
draw::curves_init();
DRW_render_object_iter(&inst_, inst_.render, inst_.depsgraph, step_object_sync_render);
draw::hair_update(*inst_.manager);
draw::curves_update(*inst_.manager);
draw::hair_free();
draw::curves_free();
geometry_steps_fill();
}
@ -142,7 +154,7 @@ bool VelocityModule::step_object_sync(Object *ob,
VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key);
vel.obj.ofs[step_] = object_steps_usage[step_]++;
vel.obj.resource_id = resource_handle.resource_index();
vel.id = particle_sys ? &particle_sys->part->id : &ob->id;
vel.id = object_key.hash_value;
object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = float4x4_view(ob->object_to_world);
if (step_ == STEP_CURRENT) {
/* Replace invalid steps. Can happen if object was hidden in one of those steps. */
@ -163,12 +175,22 @@ bool VelocityModule::step_object_sync(Object *ob,
auto add_cb = [&]() {
VelocityGeometryData data;
if (particle_sys) {
data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
if (inst_.is_viewport()) {
data.pos_buf = DRW_hair_pos_buffer_get(ob, particle_sys, modifier_data);
}
else {
data.pos_buf = draw::hair_pos_buffer_get(inst_.scene, ob, particle_sys, modifier_data);
}
return data;
}
switch (ob->type) {
case OB_CURVES:
data.pos_buf = DRW_curves_pos_buffer_get(ob);
if (inst_.is_viewport()) {
data.pos_buf = DRW_curves_pos_buffer_get(ob);
}
else {
data.pos_buf = draw::curves_pos_buffer_get(inst_.scene, ob);
}
break;
case OB_POINTCLOUD:
data.pos_buf = DRW_pointcloud_position_and_radius_buffer_get(ob);
@ -251,7 +273,7 @@ void VelocityModule::geometry_steps_fill()
vel.geo.len[step_] = geom.len;
vel.geo.ofs[step_] = geom.ofs;
/* Avoid reuse. */
vel.id = nullptr;
vel.id = 0;
}
geometry_map.clear();
@ -263,7 +285,6 @@ void VelocityModule::geometry_steps_fill()
*/
void VelocityModule::step_swap()
{
auto swap_steps = [&](eVelocityStep step_a, eVelocityStep step_b) {
std::swap(object_steps[step_a], object_steps[step_b]);
std::swap(geometry_steps[step_a], geometry_steps[step_b]);

View File

@ -29,8 +29,8 @@ namespace blender::eevee {
class VelocityModule {
public:
struct VelocityObjectData : public VelocityIndex {
/** ID to retrieve the corresponding #VelocityGeometryData after copy. */
ID *id;
/** ID key to retrieve the corresponding #VelocityGeometryData after copy. */
uint64_t id;
};
struct VelocityGeometryData {
/** VertBuf not yet ready to be copied to the #VelocityGeometryBuf. */
@ -46,8 +46,8 @@ class VelocityModule {
* geometry offset.
*/
Map<ObjectKey, VelocityObjectData> velocity_map;
/** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID *. Empty after */
Map<ID *, VelocityGeometryData> geometry_map;
/** Geometry to be copied to VelocityGeometryBuf. Indexed by evaluated ID hash. Empty after */
Map<uint64_t, VelocityGeometryData> geometry_map;
/** Contains all objects matrices for each time step. */
std::array<VelocityObjectBuf *, 3> object_steps;
/** Contains all Geometry steps from deforming objects for each time step. */

View File

@ -14,18 +14,27 @@ void main()
{
ivec2 texel = ivec2(gl_FragCoord.xy);
vec3 diffuse_light = imageLoad(direct_diffuse_img, texel).rgb +
imageLoad(indirect_diffuse_img, texel).rgb;
vec3 reflect_light = imageLoad(direct_reflect_img, texel).rgb +
imageLoad(indirect_reflect_img, texel).rgb;
vec3 refract_light = imageLoad(direct_refract_img, texel).rgb +
imageLoad(indirect_refract_img, texel).rgb;
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
/* Mask invalid radiance. */
diffuse_light = gbuf.has_diffuse ? diffuse_light : vec3(0.0);
reflect_light = gbuf.has_reflection ? reflect_light : vec3(0.0);
refract_light = gbuf.has_refraction ? refract_light : vec3(0.0);
vec3 diffuse_light = vec3(0.0);
vec3 reflect_light = vec3(0.0);
vec3 refract_light = vec3(0.0);
if (gbuf.has_diffuse) {
diffuse_light = imageLoad(direct_diffuse_img, texel).rgb +
imageLoad(indirect_diffuse_img, texel).rgb;
}
if (gbuf.has_reflection) {
reflect_light = imageLoad(direct_reflect_img, texel).rgb +
imageLoad(indirect_reflect_img, texel).rgb;
}
if (gbuf.has_refraction) {
refract_light = imageLoad(direct_refract_img, texel).rgb +
imageLoad(indirect_refract_img, texel).rgb;
}
/* Light passes. */
vec3 specular_light = reflect_light + refract_light;
output_renderpass_color(uniform_buf.render_pass.diffuse_light_id, vec4(diffuse_light, 1.0));

View File

@ -47,11 +47,7 @@ void main()
output_renderpass_value(uniform_buf.render_pass.shadow_id, shadow);
/* Store lighting for next deferred pass. */
/* Output object ID for sub-surface screen space processing. */
float f_sss_id = gbuffer_object_id_f16_pack(gbuf.diffuse.sss_id);
imageStore(direct_diffuse_img, texel, vec4(diffuse_light, f_sss_id));
imageStore(direct_diffuse_img, texel, vec4(diffuse_light, 1.0));
imageStore(direct_reflect_img, texel, vec4(reflection_light, 1.0));
imageStore(direct_refract_img, texel, vec4(refraction_light, 1.0));
}

View File

@ -0,0 +1,132 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Process in screen space the diffuse radiance input to mimic subsurface transmission.
*
* This implementation follows the technique described in the siggraph presentation:
* "Efficient screen space subsurface scattering Siggraph 2018"
* by Evgenii Golubev
*
* But, instead of having all the precomputed weights for all three color primaries,
* we precompute a weight profile texture to be able to support per pixel AND per channel radius.
*/
#pragma BLENDER_REQUIRE(gpu_shader_math_rotation_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
shared vec3 cached_radiance[SUBSURFACE_GROUP_SIZE][SUBSURFACE_GROUP_SIZE];
shared uint cached_sss_id[SUBSURFACE_GROUP_SIZE][SUBSURFACE_GROUP_SIZE];
shared float cached_depth[SUBSURFACE_GROUP_SIZE][SUBSURFACE_GROUP_SIZE];
struct SubSurfaceSample {
vec3 radiance;
float depth;
uint sss_id;
};
void cache_populate(vec2 local_uv)
{
uvec2 texel = gl_LocalInvocationID.xy;
cached_radiance[texel.y][texel.x] = texture(radiance_tx, local_uv).rgb;
cached_sss_id[texel.y][texel.x] = texture(object_id_tx, local_uv).r;
cached_depth[texel.y][texel.x] = texture(depth_tx, local_uv).r;
}
bool cache_sample(uvec2 texel, out SubSurfaceSample samp)
{
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
/* This can underflow and allow us to only do one upper bound check. */
texel -= tile_coord * SUBSURFACE_GROUP_SIZE;
if (any(greaterThanEqual(texel, uvec2(SUBSURFACE_GROUP_SIZE)))) {
return false;
}
samp.radiance = cached_radiance[texel.y][texel.x];
samp.sss_id = cached_sss_id[texel.y][texel.x];
samp.depth = cached_depth[texel.y][texel.x];
return true;
}
SubSurfaceSample sample_neighborhood(vec2 sample_uv)
{
SubSurfaceSample samp;
uvec2 sample_texel = uvec2(sample_uv * vec2(textureSize(depth_tx, 0)));
if (cache_sample(sample_texel, samp)) {
return samp;
}
samp.depth = texture(depth_tx, sample_uv).r;
samp.sss_id = texture(object_id_tx, sample_uv).r;
samp.radiance = texture(radiance_tx, sample_uv).rgb;
return samp;
}
void main(void)
{
const uint tile_size = SUBSURFACE_GROUP_SIZE;
uvec2 tile_coord = unpackUvec2x16(tiles_coord_buf[gl_WorkGroupID.x]);
ivec2 texel = ivec2(gl_LocalInvocationID.xy + tile_coord * tile_size);
vec2 center_uv = (vec2(texel) + 0.5) / vec2(textureSize(gbuf_header_tx, 0));
cache_populate(center_uv);
float depth = texelFetch(depth_tx, texel, 0).r;
vec3 vP = get_view_space_from_depth(center_uv, depth);
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
if (gbuf.diffuse.sss_id == 0u) {
return;
}
float max_radius = max_v3(gbuf.diffuse.sss_radius);
float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3];
vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) *
(0.5 * max_radius / homcoord);
/* Avoid too small radii that have float imprecision. */
vec3 clamped_sss_radius = max(vec3(1e-4), gbuf.diffuse.sss_radius / max_radius) * max_radius;
/* Scale albedo because we can have HDR value caused by BSDF sampling. */
vec3 albedo = gbuf.diffuse.color / max(1e-6, max_v3(gbuf.diffuse.color));
vec3 d = burley_setup(clamped_sss_radius, albedo);
/* Do not rotate too much to avoid too much cache misses. */
float golden_angle = M_PI * (3.0 - sqrt(5.0));
float theta = interlieved_gradient_noise(vec2(texel), 0, 0.0) * golden_angle;
mat2 sample_space = from_scale(sample_scale) * from_rotation(Angle(theta));
vec3 accum_weight = vec3(0.0);
vec3 accum_radiance = vec3(0.0);
for (int i = 0; i < uniform_buf.subsurface.sample_len; i++) {
vec2 sample_uv = center_uv + sample_space * uniform_buf.subsurface.samples[i].xy;
float pdf_inv = uniform_buf.subsurface.samples[i].z;
SubSurfaceSample samp = sample_neighborhood(sample_uv);
/* Reject radiance from other surfaces. Avoids light leak between objects. */
if (samp.sss_id != gbuf.diffuse.sss_id) {
continue;
}
/* Slide 34. */
vec3 sample_vP = get_view_space_from_depth(sample_uv, samp.depth);
float r = distance(sample_vP, vP);
vec3 weight = burley_eval(d, r) * pdf_inv;
accum_radiance += samp.radiance * weight;
accum_weight += weight;
}
/* Normalize the sum (slide 34). */
accum_radiance *= safe_rcp(accum_weight);
/* Put result in direct diffuse. */
imageStore(out_direct_light_img, texel, vec4(accum_radiance, 0.0));
imageStore(out_indirect_light_img, texel, vec4(0.0, 0.0, 0.0, 0.0));
}

View File

@ -1,122 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Postprocess diffuse radiance output from the diffuse evaluation pass to mimic subsurface
* transmission.
*
* This implementation follows the technique described in the siggraph presentation:
* "Efficient screen space subsurface scattering Siggraph 2018"
* by Evgenii Golubev
*
* But, instead of having all the precomputed weights for all three color primaries,
* we precompute a weight profile texture to be able to support per pixel AND per channel radius.
*/
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
void main(void)
{
vec2 center_uv = uvcoordsvar.xy;
ivec2 texel = ivec2(gl_FragCoord.xy);
float gbuffer_depth = texelFetch(hiz_tx, texel, 0).r;
vec3 vP = get_view_space_from_depth(center_uv, gbuffer_depth);
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
if (gbuf.diffuse.sss_id == 0u) {
/* Normal diffuse is already in combined pass. */
/* Refraction also go into this case. */
out_combined = vec4(0.0);
return;
}
float max_radius = max_v3(gbuf.diffuse.sss_radius);
float homcoord = ProjectionMatrix[2][3] * vP.z + ProjectionMatrix[3][3];
vec2 sample_scale = vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) *
(0.5 * max_radius / homcoord);
float pixel_footprint = sample_scale.x * textureSize(hiz_tx, 0).x;
if (pixel_footprint <= 1.0) {
/* Early out. */
out_combined = vec4(0.0);
return;
}
/* Avoid too small radii that have float imprecision. */
vec3 clamped_sss_radius = max(vec3(1e-4), gbuf.diffuse.sss_radius / max_radius) * max_radius;
/* Scale albedo because we can have HDR value caused by BSDF sampling. */
vec3 albedo = gbuf.diffuse.color / max(1e-6, max_v3(gbuf.diffuse.color));
vec3 d = burley_setup(clamped_sss_radius, albedo);
/* Do not rotate too much to avoid too much cache misses. */
float golden_angle = M_PI * (3.0 - sqrt(5.0));
float theta = interlieved_gradient_noise(gl_FragCoord.xy, 0, 0.0) * golden_angle;
float cos_theta = cos(theta);
float sin_theta = sqrt(1.0 - sqr(cos_theta));
mat2 rot = mat2(cos_theta, sin_theta, -sin_theta, cos_theta);
mat2 scale = mat2(sample_scale.x, 0.0, 0.0, sample_scale.y);
mat2 sample_space = scale * rot;
vec3 accum_weight = vec3(0.0);
vec3 accum = vec3(0.0);
/* TODO/OPTI(fclem) Make separate sample set for lower radius. */
for (int i = 0; i < uniform_buf.subsurface.sample_len; i++) {
vec2 sample_uv = center_uv + sample_space * uniform_buf.subsurface.samples[i].xy;
float pdf_inv = uniform_buf.subsurface.samples[i].z;
float sample_depth = textureLod(hiz_tx, sample_uv * uniform_buf.hiz.uv_scale, 0.0).r;
vec3 sample_vP = get_view_space_from_depth(sample_uv, sample_depth);
vec4 sample_data = texture(radiance_tx, sample_uv);
vec3 sample_radiance = sample_data.rgb;
uint sample_sss_id = uint(sample_data.a);
if (sample_sss_id != gbuf.diffuse.sss_id) {
continue;
}
/* Discard out of bounds samples. */
if (any(lessThan(sample_uv, vec2(0.0))) || any(greaterThan(sample_uv, vec2(1.0)))) {
continue;
}
/* Slide 34. */
float r = distance(sample_vP, vP);
vec3 weight = burley_eval(d, r) * pdf_inv;
accum += sample_radiance * weight;
accum_weight += weight;
}
/* Normalize the sum (slide 34). */
accum /= accum_weight;
if (uniform_buf.render_pass.diffuse_light_id >= 0) {
imageStore(
rp_color_img, ivec3(texel, uniform_buf.render_pass.diffuse_light_id), vec4(accum, 1.0));
}
/* This pass uses additive blending.
* Subtract the surface diffuse radiance so it's not added twice. */
accum -= texelFetch(radiance_tx, texel, 0).rgb;
/* Apply surface color on final radiance. */
accum *= gbuf.diffuse.color;
/* Debug, detect NaNs. */
if (any(isnan(accum))) {
accum = vec3(1.0, 0.0, 1.0);
}
out_combined = vec4(accum, 0.0);
}

View File

@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Select tile that have visible SSS effect and prepare the intermediate buffers for faster
* processing.
*/
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
shared uint has_visible_sss;
void main(void)
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
has_visible_sss = 0u;
}
barrier();
GBufferData gbuf = gbuffer_read(gbuf_header_tx, gbuf_closure_tx, gbuf_color_tx, texel);
if (gbuf.has_diffuse && gbuf.diffuse.sss_id != 0u) {
vec3 radiance = imageLoad(direct_light_img, texel).rgb +
imageLoad(indirect_light_img, texel).rgb;
float max_radius = max_v3(gbuf.diffuse.sss_radius);
imageStore(radiance_img, texel, vec4(radiance, 0.0));
imageStore(object_id_img, texel, uvec4(gbuf.diffuse.sss_id));
vec2 center_uv = (vec2(texel) + 0.5) / vec2(textureSize(gbuf_header_tx, 0));
float depth = texelFetch(depth_tx, texel, 0).r;
/* TODO(fclem): Check if this simplifies. */
float vPz = get_view_z_from_depth(depth);
float homcoord = ProjectionMatrix[2][3] * vPz + ProjectionMatrix[3][3];
float sample_scale = ProjectionMatrix[0][0] * (0.5 * max_radius / homcoord);
float pixel_footprint = sample_scale * float(textureSize(gbuf_header_tx, 0).x);
if (pixel_footprint > 1.0) {
/* Race condition doesn't matter here. */
has_visible_sss = 1u;
}
}
else {
/* No need to write radiance_img since the radiance won't be used at all. */
imageStore(object_id_img, texel, uvec4(0));
}
barrier();
if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
if (has_visible_sss > 0u) {
uint tile_id = atomicAdd(convolve_dispatch_buf.num_groups_x, 1u);
convolve_tile_buf[tile_id] = packUvec2x16(gl_WorkGroupID.xy);
/* Race condition doesn't matter here. */
convolve_dispatch_buf.num_groups_y = 1;
convolve_dispatch_buf.num_groups_z = 1;
}
}
}

View File

@ -42,7 +42,8 @@ void main()
if (capture_info_buf.do_surfel_count) {
/* Generate a surfel only once. This check allow cases where no axis is dominant. */
bool is_surface_view_aligned = dominant_axis(g_data.Ng) == dominant_axis(cameraForward);
vec3 vNg = normal_world_to_view(g_data.Ng);
bool is_surface_view_aligned = dominant_axis(vNg) == 2;
if (is_surface_view_aligned) {
uint surfel_id = atomicAdd(capture_info_buf.surfel_len, 1u);
if (capture_info_buf.do_surfel_output) {

View File

@ -5,15 +5,28 @@
#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_subsurface_eval)
GPU_SHADER_CREATE_INFO(eevee_subsurface_setup)
.do_static_compilation(true)
.additional_info("eevee_shared",
"eevee_gbuffer_data",
"eevee_global_ubo",
"eevee_render_pass_out")
.local_group_size(SUBSURFACE_GROUP_SIZE, SUBSURFACE_GROUP_SIZE)
.typedef_source("draw_shader_shared.h")
.additional_info("draw_view", "eevee_shared", "eevee_gbuffer_data")
.sampler(2, ImageType::DEPTH_2D, "depth_tx")
.image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "direct_light_img")
.image(1, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "indirect_light_img")
.image(2, SUBSURFACE_OBJECT_ID_FORMAT, Qualifier::WRITE, ImageType::UINT_2D, "object_id_img")
.image(3, SUBSURFACE_RADIANCE_FORMAT, Qualifier::WRITE, ImageType::FLOAT_2D, "radiance_img")
.storage_buf(0, Qualifier::WRITE, "uint", "convolve_tile_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "DispatchCommand", "convolve_dispatch_buf")
.compute_source("eevee_subsurface_setup_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_subsurface_convolve)
.do_static_compilation(true)
.local_group_size(SUBSURFACE_GROUP_SIZE, SUBSURFACE_GROUP_SIZE)
.additional_info("draw_view", "eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo")
.sampler(2, ImageType::FLOAT_2D, "radiance_tx")
.early_fragment_test(true)
.fragment_out(0, Type::VEC4, "out_combined")
.fragment_source("eevee_subsurface_eval_frag.glsl")
/* TODO(fclem) Output to diffuse pass without feedback loop. */
.additional_info("draw_fullscreen", "draw_view", "eevee_hiz_data");
.sampler(3, ImageType::DEPTH_2D, "depth_tx")
.sampler(4, ImageType::UINT_2D, "object_id_tx")
.storage_buf(0, Qualifier::READ, "uint", "tiles_coord_buf[]")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_direct_light_img")
.image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_indirect_light_img")
.compute_source("eevee_subsurface_convolve_comp.glsl");

View File

@ -187,12 +187,30 @@ static void accumululate_material_counts_bm(
static void accumululate_material_counts_mesh(
const MeshRenderData &mr, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
{
const OffsetIndices faces = mr.faces;
if (!mr.material_indices) {
all_tri_counts.local().first() = poly_to_tri_count(mr.face_len, mr.loop_len);
if (mr.use_hide && mr.hide_poly) {
const Span hide_poly(mr.hide_poly, mr.face_len);
all_tri_counts.local().first() = threading::parallel_reduce(
faces.index_range(),
4096,
0,
[&](const IndexRange range, int count) {
for (const int face : range) {
if (!hide_poly[face]) {
count += bke::mesh::face_triangles_num(faces[face].size());
}
}
return count;
},
std::plus<int>());
}
else {
all_tri_counts.local().first() = poly_to_tri_count(mr.face_len, mr.loop_len);
}
return;
}
const OffsetIndices faces = mr.faces;
const Span material_indices(mr.material_indices, mr.face_len);
threading::parallel_for(material_indices.index_range(), 1024, [&](const IndexRange range) {
Array<int> &tri_counts = all_tri_counts.local();

View File

@ -14,6 +14,19 @@
namespace blender::draw {
/** Hair. */
void hair_init();
GPUVertBuf *hair_pos_buffer_get(Scene *scene,
Object *object,
ParticleSystem *psys,
ModifierData *md);
void hair_update(Manager &manager);
void hair_free();
GPUBatch *hair_sub_pass_setup(PassMain::Sub &sub_ps,
const Scene *scene,
Object *object,
@ -28,6 +41,16 @@ GPUBatch *hair_sub_pass_setup(PassSimple::Sub &sub_ps,
ModifierData *md,
GPUMaterial *gpu_material = nullptr);
/** Curves. */
void curves_init();
GPUVertBuf *curves_pos_buffer_get(Scene *scene, Object *object);
void curves_update(Manager &manager);
void curves_free();
GPUBatch *curves_sub_pass_setup(PassMain::Sub &ps,
const Scene *scene,
Object *ob,
@ -38,6 +61,8 @@ GPUBatch *curves_sub_pass_setup(PassSimple::Sub &ps,
Object *ob,
GPUMaterial *gpu_material = nullptr);
/* Point cloud. */
GPUBatch *point_cloud_sub_pass_setup(PassMain::Sub &sub_ps,
Object *object,
GPUMaterial *gpu_material = nullptr);
@ -46,6 +71,8 @@ GPUBatch *point_cloud_sub_pass_setup(PassSimple::Sub &sub_ps,
Object *object,
GPUMaterial *gpu_material = nullptr);
/** Volume. */
/**
* Add attribute bindings of volume grids to an existing pass.
* No draw call is added so the caller can decide how to use the data.

View File

@ -88,6 +88,25 @@ static GPUShader *curves_eval_shader_get(CurvesEvalShader type)
return DRW_shader_curves_refine_get(type, drw_curves_shader_type_get());
}
static void drw_curves_ensure_dummy_vbo()
{
if (g_dummy_vbo != nullptr) {
return;
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
}
void DRW_curves_init(DRWData *drw_data)
{
/* Initialize legacy hair too, to avoid verbosity in callers. */
@ -106,20 +125,7 @@ void DRW_curves_init(DRWData *drw_data)
g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR);
}
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
}
drw_curves_ensure_dummy_vbo();
}
void DRW_curves_ubos_pool_free(CurvesUniformBufPool *pool)
@ -553,12 +559,102 @@ void DRW_curves_free()
namespace blender::draw {
static PassSimple *g_pass = nullptr;
void curves_init()
{
if (!g_pass) {
g_pass = MEM_new<PassSimple>("drw_curves g_pass", "Update Curves Pass");
}
g_pass->init();
g_pass->state_set(DRW_STATE_NO_DRAW);
}
static CurvesEvalCache *curves_cache_get(Curves &curves,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
CurvesEvalCache *cache;
const bool update = curves_ensure_procedural_data(
&curves, &cache, gpu_material, subdiv, thickness_res);
if (!update) {
return cache;
}
const int strands_len = cache->strands_len;
const int final_points_len = cache->final[subdiv].strands_res * strands_len;
auto cache_update = [&](GPUVertBuf *output_buf, GPUVertBuf *input_buf) {
PassSimple::Sub &ob_ps = g_pass->sub("Object Pass");
ob_ps.shader_set(
DRW_shader_curves_refine_get(CURVES_EVAL_CATMULL_ROM, PART_REFINE_SHADER_COMPUTE));
ob_ps.bind_texture("hairPointBuffer", input_buf);
ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
ob_ps.push_constant("hairStrandsRes", &cache->final[subdiv].strands_res);
ob_ps.bind_ssbo("posTime", output_buf);
const int max_strands_per_call = GPU_max_work_group_count(0);
int strands_start = 0;
while (strands_start < strands_len) {
int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
sub_ps.push_constant("hairStrandOffset", strands_start);
sub_ps.dispatch(int3(batch_strands_len, cache->final[subdiv].strands_res, 1));
strands_start += batch_strands_len;
}
};
if (final_points_len > 0) {
cache_update(cache->final[subdiv].proc_buf, cache->proc_point_buf);
const DRW_Attributes &attrs = cache->final[subdiv].attr_used;
for (int i : IndexRange(attrs.num_requests)) {
/* Only refine point attributes. */
if (attrs.requests[i].domain != ATTR_DOMAIN_CURVE) {
cache_update(cache->final[subdiv].attributes_buf[i], cache->proc_attributes_buf[i]);
}
}
}
return cache;
}
GPUVertBuf *curves_pos_buffer_get(Scene *scene, Object *object)
{
const int subdiv = scene->r.hair_subdiv;
const int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
Curves &curves = *static_cast<Curves *>(object->data);
CurvesEvalCache *cache = curves_cache_get(curves, nullptr, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
void curves_update(Manager &manager)
{
manager.submit(*g_pass);
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
}
void curves_free()
{
MEM_delete(g_pass);
g_pass = nullptr;
}
template<typename PassT>
GPUBatch *curves_sub_pass_setup_implementation(PassT &sub_ps,
const Scene *scene,
Object *ob,
GPUMaterial *gpu_material)
{
/** NOTE: This still relies on the old DRW_curves implementation. */
CurvesUniformBufPool *pool = DST.vmempool->curves_ubos;
CurvesInfosBuf &curves_infos = pool->alloc();
BLI_assert(ob->type == OB_CURVES);

View File

@ -71,6 +71,30 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
return DRW_shader_hair_refine_get(refinement, drw_hair_shader_type_get());
}
static void drw_hair_ensure_vbo()
{
if (g_dummy_vbo != nullptr) {
return;
}
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create VBO immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
g_dummy_curves_info = MEM_new<blender::draw::UniformBuffer<CurvesInfos>>("g_dummy_curves_info");
memset(
g_dummy_curves_info->is_point_attribute, 0, sizeof(g_dummy_curves_info->is_point_attribute));
g_dummy_curves_info->push_update();
}
void DRW_hair_init()
{
if (GPU_transform_feedback_support() || GPU_compute_shader_support()) {
@ -80,27 +104,7 @@ void DRW_hair_init()
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
}
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
&format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
/* Create VBO immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
g_dummy_curves_info = MEM_new<blender::draw::UniformBuffer<CurvesInfos>>(
"g_dummy_curves_info");
memset(g_dummy_curves_info->is_point_attribute,
0,
sizeof(g_dummy_curves_info->is_point_attribute));
g_dummy_curves_info->push_update();
}
drw_hair_ensure_vbo();
}
static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp,
@ -437,6 +441,86 @@ void DRW_hair_free()
namespace blender::draw {
static PassSimple *g_pass = nullptr;
void hair_init()
{
if (!g_pass) {
g_pass = MEM_new<PassSimple>("drw_hair g_pass", "Update Hair Pass");
}
g_pass->init();
g_pass->state_set(DRW_STATE_NO_DRAW);
}
static ParticleHairCache *hair_particle_cache_get(Object *object,
ParticleSystem *psys,
ModifierData *md,
GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
ParticleHairCache *cache;
bool update = particles_ensure_procedural_data(
object, psys, md, &cache, gpu_material, subdiv, thickness_res);
if (!update) {
return cache;
}
const int strands_len = cache->strands_len;
const int final_points_len = cache->final[subdiv].strands_res * strands_len;
if (final_points_len > 0) {
PassSimple::Sub &ob_ps = g_pass->sub("Object Pass");
ob_ps.shader_set(
DRW_shader_hair_refine_get(PART_REFINE_CATMULL_ROM, PART_REFINE_SHADER_COMPUTE));
ob_ps.bind_texture("hairPointBuffer", cache->proc_point_buf);
ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
ob_ps.push_constant("hairStrandsRes", &cache->final[subdiv].strands_res);
ob_ps.bind_ssbo("posTime", cache->final[subdiv].proc_buf);
const int max_strands_per_call = GPU_max_work_group_count(0);
int strands_start = 0;
while (strands_start < strands_len) {
int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
sub_ps.push_constant("hairStrandOffset", strands_start);
sub_ps.dispatch(int3(batch_strands_len, cache->final[subdiv].strands_res, 1));
strands_start += batch_strands_len;
}
}
return cache;
}
GPUVertBuf *hair_pos_buffer_get(Scene *scene,
Object *object,
ParticleSystem *psys,
ModifierData *md)
{
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *cache = hair_particle_cache_get(
object, psys, md, nullptr, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
void hair_update(Manager &manager)
{
manager.submit(*g_pass);
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
}
void hair_free()
{
MEM_delete(g_pass);
g_pass = nullptr;
}
template<typename PassT>
GPUBatch *hair_sub_pass_setup_implementation(PassT &sub_ps,
const Scene *scene,
@ -445,6 +529,8 @@ GPUBatch *hair_sub_pass_setup_implementation(PassT &sub_ps,
ModifierData *md,
GPUMaterial *gpu_material)
{
/** NOTE: This still relies on the old DRW_hair implementation. */
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache = drw_hair_particle_cache_get(

View File

@ -174,58 +174,12 @@ static bool acf_show_channel_colors()
static void acf_generic_channel_color(bAnimContext *ac, bAnimListElem *ale, float r_color[3])
{
const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
bActionGroup *grp = nullptr;
short indent = (acf->get_indent_level) ? acf->get_indent_level(ac, ale) : 0;
bool showGroupColors = acf_show_channel_colors();
if (ale->type == ANIMTYPE_FCURVE) {
FCurve *fcu = (FCurve *)ale->data;
grp = fcu->grp;
}
/* set color for normal channels
* - use 3 shades of color group/standard color for 3 indentation level
* - only use group colors if allowed to, and if actually feasible
*/
if (showGroupColors && (grp) && (grp->customCol)) {
uchar cp[3];
if (indent == 2) {
copy_v3_v3_uchar(cp, grp->cs.solid);
}
else if (indent == 1) {
copy_v3_v3_uchar(cp, grp->cs.select);
}
else {
copy_v3_v3_uchar(cp, grp->cs.active);
}
/* copy the colors over, transforming from bytes to floats */
rgb_uchar_to_float(r_color, cp);
}
else {
/* FIXME: what happens when the indentation is 1 greater than what it should be
* (due to grouping)? */
int colOfs = 10 - 10 * indent;
UI_GetThemeColorShade3fv(TH_SHADE2, colOfs, r_color);
}
}
/* get backdrop color for grease pencil channels */
static void acf_gpencil_channel_color(bAnimContext *ac, bAnimListElem *ale, float r_color[3])
{
const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
short indent = (acf->get_indent_level) ? acf->get_indent_level(ac, ale) : 0;
bool showGroupColors = acf_show_channel_colors();
if ((showGroupColors) && (ale->type == ANIMTYPE_GPLAYER)) {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
copy_v3_v3(r_color, gpl->color);
}
else {
int colOfs = 10 - 10 * indent;
UI_GetThemeColorShade3fv(TH_SHADE2, colOfs, r_color);
}
/* FIXME: what happens when the indentation is 1 greater than what it should be
* (due to grouping)? */
const int colorOffset = 10 - 10 * indent;
UI_GetThemeColorShade3fv(TH_SHADE2, colorOffset, r_color);
}
/* backdrop for generic channels */
@ -519,6 +473,7 @@ static bAnimChannelType ACF_SUMMARY = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_summary_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_summary_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ nullptr,
@ -630,6 +585,7 @@ static bAnimChannelType ACF_SCENE = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_root_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_root_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ nullptr,
@ -810,6 +766,7 @@ static bAnimChannelType ACF_OBJECT = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_root_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_root_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ nullptr,
@ -828,31 +785,11 @@ static bAnimChannelType ACF_OBJECT = {
/* get backdrop color for group widget */
static void acf_group_color(bAnimContext * /*ac*/, bAnimListElem *ale, float r_color[3])
{
bActionGroup *agrp = (bActionGroup *)ale->data;
bool showGroupColors = acf_show_channel_colors();
if (showGroupColors && agrp->customCol) {
uchar cp[3];
/* highlight only for active */
if (ale->flag & AGRP_ACTIVE) {
copy_v3_v3_uchar(cp, agrp->cs.select);
}
else {
copy_v3_v3_uchar(cp, agrp->cs.solid);
}
/* copy the colors over, transforming from bytes to floats */
rgb_uchar_to_float(r_color, cp);
if (ale->flag & AGRP_ACTIVE) {
UI_GetThemeColor3fv(TH_GROUP_ACTIVE, r_color);
}
else {
/* highlight only for active */
if (ale->flag & AGRP_ACTIVE) {
UI_GetThemeColor3fv(TH_GROUP_ACTIVE, r_color);
}
else {
UI_GetThemeColor3fv(TH_GROUP, r_color);
}
UI_GetThemeColor3fv(TH_GROUP, r_color);
}
}
@ -977,12 +914,45 @@ static void *acf_group_setting_ptr(bAnimListElem *ale,
return GET_ACF_FLAG_PTR(agrp->flag, r_type);
}
static bool get_actiongroup_color(const bActionGroup *agrp, uint8_t r_color[3])
{
if (!agrp) {
return false;
}
const int8_t color_index = agrp->customCol;
if (color_index == 0) {
return false;
}
const ThemeWireColor *wire_color;
if (color_index < 0) {
wire_color = &agrp->cs;
}
else {
const bTheme *btheme = UI_GetTheme();
wire_color = &btheme->tarm[(color_index - 1)];
}
r_color[0] = wire_color->solid[0];
r_color[1] = wire_color->solid[1];
r_color[2] = wire_color->solid[2];
return true;
}
static bool acf_group_channel_color(const bAnimListElem *ale, uint8_t r_color[3])
{
const bActionGroup *agrp = static_cast<const bActionGroup *>(ale->data);
return get_actiongroup_color(agrp, r_color);
}
/** Group type define. */
static bAnimChannelType ACF_GROUP = {
/*channel_type_name*/ "Group",
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_group_color,
/*get_channel_color*/ acf_group_channel_color,
/*draw_backdrop*/ acf_group_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
@ -1090,12 +1060,19 @@ static void *acf_fcurve_setting_ptr(bAnimListElem *ale,
return GET_ACF_FLAG_PTR(fcu->flag, r_type);
}
static bool acf_fcurve_channel_color(const bAnimListElem *ale, uint8_t r_color[3])
{
const FCurve *fcu = static_cast<const FCurve *>(ale->data);
return get_actiongroup_color(fcu->grp, r_color);
}
/** F-Curve type define. */
static bAnimChannelType ACF_FCURVE = {
/*channel_type_name*/ "F-Curve",
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ acf_fcurve_channel_color,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
/* XXX rename this to f-curves only? */
@ -1212,6 +1189,7 @@ static bAnimChannelType ACF_NLACONTROLS = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_nla_controls_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_nla_controls_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
@ -1252,6 +1230,7 @@ static bAnimChannelType ACF_NLACURVE = {
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_group_offset,
@ -1342,6 +1321,7 @@ static bAnimChannelType ACF_FILLACTD = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1427,6 +1407,7 @@ static bAnimChannelType ACF_FILLDRIVERS = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1508,6 +1489,7 @@ static bAnimChannelType ACF_DSMAT = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1589,6 +1571,7 @@ static bAnimChannelType ACF_DSLIGHT = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1677,6 +1660,7 @@ static bAnimChannelType ACF_DSTEX = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_dstex_offset,
@ -1762,6 +1746,7 @@ static bAnimChannelType ACF_DSCACHEFILE = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1847,6 +1832,7 @@ static bAnimChannelType ACF_DSCAM = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -1938,6 +1924,7 @@ static bAnimChannelType ACF_DSCUR = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2038,6 +2025,7 @@ static bAnimChannelType ACF_DSSKEY = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2119,6 +2107,7 @@ static bAnimChannelType ACF_DSWOR = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2200,6 +2189,7 @@ static bAnimChannelType ACF_DSPART = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2281,6 +2271,7 @@ static bAnimChannelType ACF_DSMBALL = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2362,6 +2353,7 @@ static bAnimChannelType ACF_DSARM = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2454,6 +2446,7 @@ static bAnimChannelType ACF_DSNTREE = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_dsntree_offset,
@ -2535,6 +2528,7 @@ static bAnimChannelType ACF_DSLINESTYLE = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2616,6 +2610,7 @@ static bAnimChannelType ACF_DSMESH = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/* XXX: this only works for compositing. */
@ -2698,6 +2693,7 @@ static bAnimChannelType ACF_DSLAT = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/* XXX: this only works for compositing. */
@ -2780,6 +2776,7 @@ static bAnimChannelType ACF_DSSPK = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2861,6 +2858,7 @@ static bAnimChannelType ACF_DSCURVES = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -2941,6 +2939,7 @@ static bAnimChannelType ACF_DSPOINTCLOUD = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -3021,6 +3020,7 @@ static bAnimChannelType ACF_DSVOLUME = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -3101,6 +3101,7 @@ static bAnimChannelType ACF_DSGPENCIL = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -3182,6 +3183,7 @@ static bAnimChannelType ACF_DSMCLIP = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_generic_dataexpand_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_dataexpand_backdrop,
/*get_indent_level*/ acf_generic_indentation_1,
/*get_offset*/ acf_generic_basic_offset,
@ -3297,6 +3299,7 @@ static bAnimChannelType ACF_SHAPEKEY = {
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_basic_offset,
@ -3379,6 +3382,7 @@ static bAnimChannelType ACF_GPD_LEGACY = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_gpd_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_group_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
@ -3463,6 +3467,13 @@ static int acf_gpl_setting_flag_legacy(bAnimContext * /*ac*/,
}
}
static bool acf_gpl_channel_color(const bAnimListElem *ale, uint8_t r_color[3])
{
const bGPDlayer *gpl = static_cast<const bGPDlayer *>(ale->data);
rgb_float_to_uchar(r_color, gpl->color);
return true;
}
/* get pointer to the setting */
static void *acf_gpl_setting_ptr_legacy(bAnimListElem *ale,
eAnimChannel_Settings /*setting*/,
@ -3479,7 +3490,8 @@ static bAnimChannelType ACF_GPL_LEGACY = {
/*channel_type_name*/ "GPencil Layer",
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_gpencil_channel_color,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ acf_gpl_channel_color,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
/*get_offset*/ acf_generic_group_offset,
@ -3675,6 +3687,7 @@ static bAnimChannelType ACF_GPD = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_gpd_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_group_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
@ -3693,7 +3706,8 @@ static bAnimChannelType ACF_GPL = {
/*channel_type_name*/ "Grease Pencil Layer",
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_gpencil_channel_color,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ acf_gpl_channel_color,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
/*get_offset*/ greasepencil::layer_offset,
@ -3713,6 +3727,7 @@ static bAnimChannelType ACF_GPLGROUP = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ greasepencil::layer_group_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_group_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ greasepencil::layer_offset,
@ -3793,6 +3808,7 @@ static bAnimChannelType ACF_MASKDATA = {
/*channel_role*/ ACHANNEL_ROLE_EXPANDER,
/*get_backdrop_color*/ acf_mask_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_group_backdrop,
/*get_indent_level*/ acf_generic_indentation_0,
/*get_offset*/ acf_generic_group_offset,
@ -3889,6 +3905,7 @@ static bAnimChannelType ACF_MASKLAYER = {
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_generic_channel_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
/*get_offset*/ acf_generic_group_offset,
@ -4029,6 +4046,7 @@ static bAnimChannelType ACF_NLATRACK = {
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/*get_backdrop_color*/ acf_nlatrack_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_generic_channel_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
/*get_offset*/ acf_generic_group_offset, /* XXX? */
@ -4210,6 +4228,7 @@ static bAnimChannelType ACF_NLAACTION = {
/*channel_role*/ ACHANNEL_ROLE_CHANNEL,
/* NOTE: the backdrop handles this too, since it needs special hacks. */
/*get_backdrop_color*/ acf_nlaaction_color,
/*get_channel_color*/ nullptr,
/*draw_backdrop*/ acf_nlaaction_backdrop,
/*get_indent_level*/ acf_generic_indentation_flexible,
@ -4509,6 +4528,9 @@ void ANIM_channel_setting_set(bAnimContext *ac,
/** Extra offset for the visibility icons in the graph editor. */
#define GRAPH_ICON_VISIBILITY_OFFSET (GRAPH_COLOR_BAND_WIDTH * 1.5f)
#define CHANNEL_COLOR_RECT_WIDTH (0.5f * ICON_WIDTH)
#define CHANNEL_COLOR_RECT_MARGIN (2.0f * UI_SCALE_FAC)
/* Helper - Check if a channel needs renaming */
static bool achannel_is_being_renamed(const bAnimContext *ac,
const bAnimChannelType *acf,
@ -4774,6 +4796,14 @@ void ANIM_channel_draw(
/* check if there's enough space for the toggles if the sliders are drawn too */
if (!(draw_sliders) || (BLI_rcti_size_x(&v2d->mask) > ANIM_UI_get_channel_button_width() / 2))
{
/* NOTE: The comments here match the comments in ANIM_channel_draw_widgets(), as that
* function and this one are strongly coupled. */
/* Little channel color rectangle. */
if (acf_show_channel_colors()) {
offset += CHANNEL_COLOR_RECT_WIDTH + 2 * CHANNEL_COLOR_RECT_MARGIN;
}
/* solo... */
if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
/* A touch of padding because the star icon is so wide. */
@ -5607,6 +5637,32 @@ void ANIM_channel_draw_widgets(const bContext *C,
/* check if there's enough space for the toggles if the sliders are drawn too */
if (!(draw_sliders) || (BLI_rcti_size_x(&v2d->mask) > ANIM_UI_get_channel_button_width() / 2))
{
/* NOTE: The comments here match the comments in ANIM_channel_draw(), as that
* function and this one are strongly coupled. */
/* Little channel color rectangle. */
const bool show_group_colors = acf_show_channel_colors();
if (show_group_colors) {
const float rect_width = CHANNEL_COLOR_RECT_WIDTH;
const float rect_margin = CHANNEL_COLOR_RECT_MARGIN;
uint8_t color[3];
if (acf->get_channel_color && acf->get_channel_color(ale, color)) {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(color);
GPUVertFormat format = {0};
uint pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immRectf(pos,
rect->xmax - rect_width - rect_margin,
rect->ymin + rect_margin,
rect->xmax - rect_margin,
rect->ymax - rect_margin);
immUnbindProgram();
}
offset -= rect_width + 2 * rect_margin;
}
/* solo... */
if ((ac->spacetype == SPACE_NLA) && acf->has_setting(ac, ale, ACHANNEL_SETTING_SOLO)) {
offset -= ICON_WIDTH;

View File

@ -44,6 +44,7 @@ bool ED_asset_mark_id(ID *id)
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id);
id->asset_data = BKE_asset_metadata_create();
id->asset_data->local_type_info = id_type_info->asset_type_info;
id->asset_data->local_type_info->on_mark_asset_fn(id, id->asset_data);
/* Important for asset storage to update properly! */
ED_assetlist_storage_tag_main_data_dirty();

View File

@ -492,6 +492,7 @@ void ED_asset_shelf_header_region_init(wmWindowManager * /*wm*/, ARegion *region
{
ED_region_header_init(region);
region->alignment |= RGN_SPLIT_SCALE_PREV;
region->flag |= RGN_FLAG_RESIZE_RESPECT_BUTTON_SECTIONS;
}
void ED_asset_shelf_header_region(const bContext *C, ARegion *region)
@ -505,14 +506,17 @@ void ED_asset_shelf_header_region(const bContext *C, ARegion *region)
*main_shelf_region);
update_active_shelf(*C, *space_type, *shelf_regiondata);
ED_region_header(C, region);
ED_region_header_with_button_sections(C, region, uiButtonSectionsAlign::Bottom);
}
int ED_asset_shelf_header_region_size()
{
/* The asset shelf tends to look like a separate area. Making the shelf header smaller than a
* normal header helps a bit. */
return ED_area_headersize() * 0.85f;
/* Use a height that lets widgets sit just on top of the separator line drawn at the lower edge
* of the region (widgets will be centered).
*
* Note that this is usually a bit less than the header size. The asset shelf tends to look like
* a separate area, so making the shelf header smaller than a header helps. */
return UI_UNIT_Y + (UI_BUTTON_SECTION_SEPERATOR_LINE_WITH * 2);
}
void ED_asset_shelf_region_blend_read_data(BlendDataReader *reader, ARegion *region)

View File

@ -570,6 +570,12 @@ struct bAnimChannelType {
/* -- Drawing -- */
/** Get RGB color that is used to draw the majority of the backdrop. */
void (*get_backdrop_color)(bAnimContext *ac, bAnimListElem *ale, float r_color[3]);
/** Get RGB color that represents this channel.
* \return true when r_color was updated, false when there is no color for this channel.
*/
bool (*get_channel_color)(const bAnimListElem *ale, uint8_t r_color[3]);
/** Draw backdrop strip for channel. */
void (*draw_backdrop)(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc);
/** Get depth of indentation (relative to the depth channel is nested at). */

View File

@ -7,6 +7,7 @@
#include <optional>
#include "BLI_compute_context.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector_set.hh"
#include "ED_node_c.hh"
@ -14,10 +15,13 @@
struct SpaceNode;
struct ARegion;
struct Main;
struct bContext;
struct bNodeSocket;
struct bNodeTree;
struct Object;
struct rcti;
struct NodesModifierData;
struct uiLayout;
namespace blender::ed::space_node {
@ -38,6 +42,11 @@ void node_insert_on_link_flags_clear(bNodeTree &node_tree);
*/
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale);
/**
* Find the nested node id of a currently visible node in the root tree.
*/
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node);
struct ObjectAndModifier {
const Object *object;
const NodesModifierData *nmd;

View File

@ -118,6 +118,16 @@ void ED_region_header_init(ARegion *region);
void ED_region_header(const bContext *C, ARegion *region);
void ED_region_header_layout(const bContext *C, ARegion *region);
void ED_region_header_draw(const bContext *C, ARegion *region);
/* Forward declare enum. */
enum class uiButtonSectionsAlign : int8_t;
/** Version of #ED_region_header() that draws with button sections. */
void ED_region_header_with_button_sections(const bContext *C,
ARegion *region,
uiButtonSectionsAlign align);
/** Version of #ED_region_header_draw() that draws with button sections. */
void ED_region_header_draw_with_button_sections(const bContext *C,
const ARegion *region,
uiButtonSectionsAlign align);
void ED_region_cursor_set(wmWindow *win, ScrArea *area, ARegion *region);
/**

View File

@ -839,6 +839,25 @@ void UI_block_region_set(uiBlock *block, ARegion *region);
void UI_block_lock_set(uiBlock *block, bool val, const char *lockstr);
void UI_block_lock_clear(uiBlock *block);
#define UI_BUTTON_SECTION_MERGE_DISTANCE (UI_UNIT_X * 3)
/* Separator line between regions if the #uiButtonSectionsAlign is not #None. */
#define UI_BUTTON_SECTION_SEPERATOR_LINE_WITH (U.pixelsize * 2)
enum class uiButtonSectionsAlign : int8_t { None = 1, Top, Bottom };
/**
* Draw a background with rounded corners behind each visual group of buttons. The visual groups
* are separated by spacer buttons (#uiItemSpacer()). Button groups that are closer than
* #UI_BUTTON_SECTION_MERGE_DISTANCE will be merged into one visual section. If the group is closer
* than that to a region edge, it will also be extended to that, and the rounded corners will be
* removed on that edge.
*
* \note This currently only works well for horizontal, header like regions.
*/
void UI_region_button_sections_draw(const ARegion *region,
int /*THemeColorID*/ colorid,
uiButtonSectionsAlign align);
bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x);
/**
* Automatic aligning, horizontal or vertical.
*/
@ -1872,7 +1891,7 @@ void UI_but_drag_attach_image(uiBut *but, const ImBuf *imb, float scale);
*/
void UI_but_drag_set_asset(uiBut *but,
const blender::asset_system::AssetRepresentation *asset,
int import_type, /* eAssetImportType */
int import_method, /* eAssetImportMethod */
int icon,
const ImBuf *imb,
float scale);

View File

@ -42,6 +42,7 @@ set(SRC
interface_align.cc
interface_anim.cc
interface_button_group.cc
interface_button_sections.cc
interface_context_menu.cc
interface_context_path.cc
interface_drag.cc

View File

@ -0,0 +1,225 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*
* Calculating and drawing of bounding boxes for "button sections". That is, each group of buttons
* separated by a separator spacer button.
*/
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
#include "BLI_vector.hh"
#include "DNA_screen_types.h"
#include "GPU_immediate.h"
#include "interface_intern.hh"
using namespace blender;
/**
* Calculate a bounding box for each section. Sections will be merged if they are closer than
* #UI_BUTTON_SECTION_MERGE_DISTANCE.
*
* If a section is closer than #UI_BUTTON_SECTION_MERGE_DISTANCE to a region edge, it will be
* extended to the edge.
*
* \return the bounding boxes in region space.
*/
static Vector<rcti> button_section_bounds_calc(const ARegion *region, const bool add_padding)
{
Vector<rcti> section_bounds;
const auto finish_section_fn = [&](const rcti cur_section_bounds) {
if (!section_bounds.is_empty() &&
std::abs(section_bounds.last().xmax - cur_section_bounds.xmin) <
UI_BUTTON_SECTION_MERGE_DISTANCE)
{
section_bounds.last().xmax = cur_section_bounds.xmax;
}
else {
section_bounds.append(cur_section_bounds);
}
rcti &last_bounds = section_bounds.last();
/* Extend to region edge if close enough. */
if (last_bounds.xmin <= UI_BUTTON_SECTION_MERGE_DISTANCE) {
last_bounds.xmin = 0;
}
if (last_bounds.xmax >= (region->winx - UI_BUTTON_SECTION_MERGE_DISTANCE)) {
last_bounds.xmax = region->winx;
}
};
{
bool has_section_content = false;
rcti cur_section_bounds;
BLI_rcti_init_minmax(&cur_section_bounds);
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
if (but->type == UI_BTYPE_SEPR_SPACER) {
/* Start a new section. */
if (has_section_content) {
finish_section_fn(cur_section_bounds);
/* Reset for next section. */
BLI_rcti_init_minmax(&cur_section_bounds);
has_section_content = false;
}
continue;
}
rcti but_pixelrect;
ui_but_to_pixelrect(&but_pixelrect, region, block, but);
BLI_rcti_do_minmax_rcti(&cur_section_bounds, &but_pixelrect);
has_section_content = true;
}
}
/* Finish last section in case the last button is not a spacer. */
if (has_section_content) {
finish_section_fn(cur_section_bounds);
}
}
if (add_padding) {
const uiStyle *style = UI_style_get_dpi();
for (rcti &bounds : section_bounds) {
BLI_rcti_pad(&bounds, style->buttonspacex, style->buttonspacey);
/* Clamp, important for the rounded-corners to draw correct. */
CLAMP_MIN(bounds.xmin, 0);
CLAMP_MAX(bounds.xmax, region->winx);
CLAMP_MIN(bounds.ymin, 0);
CLAMP_MAX(bounds.ymax, region->winy);
}
}
return section_bounds;
}
static void ui_draw_button_sections_background(const ARegion *region,
const Span<rcti> section_bounds,
const ThemeColorID colorid,
const uiButtonSectionsAlign align,
const float corner_radius)
{
float bg_color[4];
UI_GetThemeColor4fv(colorid, bg_color);
for (const rcti &bounds : section_bounds) {
int roundbox_corners = [align]() -> int {
switch (align) {
case uiButtonSectionsAlign::Top:
return UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT;
case uiButtonSectionsAlign::Bottom:
return UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
case uiButtonSectionsAlign::None:
return UI_CNR_ALL;
}
return UI_CNR_ALL;
}();
/* No rounded corners at the region edge. */
if (bounds.xmin == 0) {
roundbox_corners &= ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
}
if (bounds.xmax >= region->winx) {
roundbox_corners &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
}
rctf bounds_float;
BLI_rctf_rcti_copy(&bounds_float, &bounds);
UI_draw_roundbox_corner_set(roundbox_corners);
UI_draw_roundbox_4fv(&bounds_float, true, corner_radius, bg_color);
}
}
static void ui_draw_button_sections_alignment_separator(const ARegion *region,
const Span<rcti> section_bounds,
const ThemeColorID colorid,
const uiButtonSectionsAlign align,
const float corner_radius)
{
const int separator_line_width = UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
float bg_color[4];
UI_GetThemeColor4fv(colorid, bg_color);
GPU_blend(GPU_BLEND_ALPHA);
/* Separator line. */
{
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(
format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(bg_color);
if (align == uiButtonSectionsAlign::Top) {
immRecti(pos, 0, region->winy - separator_line_width, region->winx, region->winy);
}
else if (align == uiButtonSectionsAlign::Bottom) {
immRecti(pos, 0, 0, region->winx, separator_line_width);
}
else {
BLI_assert_unreachable();
}
immUnbindProgram();
}
int prev_xmax = 0;
for (const rcti &bounds : section_bounds) {
if (prev_xmax != 0) {
const rcti rounded_corner_rect = {
prev_xmax, bounds.xmin, separator_line_width, region->winy - separator_line_width};
UI_draw_roundbox_corner_set(align == uiButtonSectionsAlign::Top ?
(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) :
(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT));
ui_draw_rounded_corners_inverted(rounded_corner_rect, corner_radius, bg_color);
}
prev_xmax = bounds.xmax;
}
GPU_blend(GPU_BLEND_NONE);
}
void UI_region_button_sections_draw(const ARegion *region,
const int /*ThemeColorID*/ colorid,
const uiButtonSectionsAlign align)
{
const float aspect = BLI_rctf_size_x(&region->v2d.cur) /
(BLI_rcti_size_x(&region->v2d.mask) + 1);
const float corner_radius = 4.0f * UI_SCALE_FAC / aspect;
const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
ui_draw_button_sections_background(
region, section_bounds, ThemeColorID(colorid), align, corner_radius);
if (align != uiButtonSectionsAlign::None) {
ui_draw_button_sections_alignment_separator(region,
section_bounds,
ThemeColorID(colorid),
align,
/* Slightly bigger corner radius, looks better. */
corner_radius + 1);
}
}
bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
{
const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
for (const rcti &bounds : section_bounds) {
if (BLI_rcti_isect_x(&bounds, mval_x)) {
return true;
}
}
return false;
}

View File

@ -31,12 +31,12 @@ void UI_but_drag_attach_image(uiBut *but, const ImBuf *imb, const float scale)
void UI_but_drag_set_asset(uiBut *but,
const blender::asset_system::AssetRepresentation *asset,
int import_type,
int import_method,
int icon,
const ImBuf *imb,
float scale)
{
wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, import_type);
wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, import_method);
but->dragtype = WM_DRAG_ASSET;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */

View File

@ -149,6 +149,73 @@ void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float
UI_draw_roundbox_4fv_ex(rect, (filled) ? col : nullptr, nullptr, 1.0f, col, U.pixelsize, rad);
}
void ui_draw_rounded_corners_inverted(const rcti &rect,
const float rad,
const blender::float4 color)
{
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
float vec[4][2] = {
{0.195, 0.02},
{0.55, 0.169},
{0.831, 0.45},
{0.98, 0.805},
};
for (int a = 0; a < 4; a++) {
mul_v2_fl(vec[a], rad);
}
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
if (roundboxtype & UI_CNR_TOP_LEFT) {
immBegin(GPU_PRIM_TRI_FAN, 7);
immVertex2f(pos, rect.xmin, rect.ymax);
immVertex2f(pos, rect.xmin, rect.ymax - rad);
for (int a = 0; a < 4; a++) {
immVertex2f(pos, rect.xmin + vec[a][1], rect.ymax - rad + vec[a][0]);
}
immVertex2f(pos, rect.xmin + rad, rect.ymax);
immEnd();
}
if (roundboxtype & UI_CNR_TOP_RIGHT) {
immBegin(GPU_PRIM_TRI_FAN, 7);
immVertex2f(pos, rect.xmax, rect.ymax);
immVertex2f(pos, rect.xmax - rad, rect.ymax);
for (int a = 0; a < 4; a++) {
immVertex2f(pos, rect.xmax - rad + vec[a][0], rect.ymax - vec[a][1]);
}
immVertex2f(pos, rect.xmax, rect.ymax - rad);
immEnd();
}
if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
immBegin(GPU_PRIM_TRI_FAN, 7);
immVertex2f(pos, rect.xmax, rect.ymin);
immVertex2f(pos, rect.xmax, rect.ymin + rad);
for (int a = 0; a < 4; a++) {
immVertex2f(pos, rect.xmax - vec[a][1], rect.ymin + rad - vec[a][0]);
}
immVertex2f(pos, rect.xmax - rad, rect.ymin);
immEnd();
}
if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
immBegin(GPU_PRIM_TRI_FAN, 7);
immVertex2f(pos, rect.xmin, rect.ymin);
immVertex2f(pos, rect.xmin + rad, rect.ymin);
for (int a = 0; a < 4; a++) {
immVertex2f(pos, rect.xmin + rad - vec[a][0], rect.ymin + vec[a][1]);
}
immVertex2f(pos, rect.xmin, rect.ymin + rad);
immEnd();
}
immUnbindProgram();
}
void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4])
{
const int ofs_y = 4 * U.pixelsize;

View File

@ -11,6 +11,7 @@
#include <functional>
#include "BLI_compiler_attrs.h"
#include "BLI_math_vector_types.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
@ -1015,6 +1016,16 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
*/
void ui_draw_gradient(const rcti *rect, const float hsv[3], eButGradientType type, float alpha);
/**
* Draws rounded corner segments but inverted. Imagine each corner like a filled right triangle,
* just that the hypotenuse is nicely curved inwards (towards the right angle of the triangle).
*
* Useful for connecting orthogonal shapes with a rounded corner, which can look quite nice.
*/
void ui_draw_rounded_corners_inverted(const rcti &rect,
const float rad,
const blender::float4 color);
/* based on UI_draw_roundbox_gl_mode,
* check on making a version which allows us to skip some sides */
void ui_draw_but_TAB_outline(const rcti *rect,

View File

@ -1304,8 +1304,7 @@ void UI_view2d_dot_grid_draw(const View2D *v2d,
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
/* Scaling the dots fully with the zoom looks too busy, but a bit of size variation is nice. */
const float min_point_size = 2.0f * U.pixelsize;
@ -1368,7 +1367,8 @@ void UI_view2d_dot_grid_draw(const View2D *v2d,
continue;
}
GPU_point_size(point_size_draw);
immUniform1f("size", point_size_draw);
immUniform4fv("color", color);
immBegin(GPU_PRIM_POINTS, count_x * count_y);
/* Theoretically drawing on top of lower grid levels could be avoided, but it would also
@ -1377,7 +1377,6 @@ void UI_view2d_dot_grid_draw(const View2D *v2d,
const float y = start_y + step * i_y;
for (int i_x = 0; i_x < count_x; i_x++) {
const float x = start_x + step * i_x;
immAttr4fv(color_id, color);
immVertex2f(pos, x + point_size_offset, y + point_size_offset);
}
}

View File

@ -18,6 +18,7 @@
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_screen.hh"
@ -95,7 +96,7 @@ static void catalog_assets_draw(const bContext *C, Menu *menu)
uiLayout *layout = menu->layout;
uiItemS(layout);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
for (const asset_system::AssetRepresentation *asset : assets) {
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
@ -121,11 +122,29 @@ static void catalog_assets_draw(const bContext *C, Menu *menu)
});
}
static void unassigned_assets_draw(const bContext * /*C*/, Menu *menu)
static bool unassigned_local_poll(const Main &bmain)
{
LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group->id.library_weak_reference || group->id.asset_data) {
continue;
}
if (!group->geometry_node_asset_traits ||
!(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
return true;
}
return false;
}
static void unassigned_assets_draw(const bContext *C, Menu *menu)
{
Main &bmain = *CTX_data_main(C);
asset::AssetItemTree &tree = *get_static_item_tree();
uiLayout *layout = menu->layout;
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true);
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
@ -138,6 +157,37 @@ static void unassigned_assets_draw(const bContext * /*C*/, Menu *menu)
&props_ptr);
asset::operator_asset_reference_props_set(*asset, props_ptr);
}
bool add_separator = !tree.unassigned_assets.is_empty();
LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group->id.library_weak_reference || group->id.asset_data) {
continue;
}
if (!group->geometry_node_asset_traits ||
!(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
if (add_separator) {
uiItemS(layout);
uiItemL(layout, IFACE_("Non-Assets"), ICON_NONE);
add_separator = false;
}
PointerRNA props_ptr;
uiItemFullO_ptr(layout,
ot,
group->id.name + 2,
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&props_ptr);
WM_operator_properties_id_lookup_set_from_id(&props_ptr, &group->id);
}
}
static void root_catalogs_draw(const bContext *C, Menu *menu)
@ -193,7 +243,7 @@ static void root_catalogs_draw(const bContext *C, Menu *menu)
}
});
if (!tree.unassigned_assets.is_empty()) {
if (!tree.unassigned_assets.is_empty() || unassigned_local_poll(*CTX_data_main(C))) {
uiItemS(layout);
uiItemM(layout,
"OBJECT_MT_add_modifier_unassigned_assets",
@ -202,16 +252,28 @@ static void root_catalogs_draw(const bContext *C, Menu *menu)
}
}
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
static bNodeTree *get_asset_or_local_node_group(const bContext &C,
PointerRNA &ptr,
ReportList *reports)
{
Main &bmain = *CTX_data_main(&C);
if (bNodeTree *group = reinterpret_cast<bNodeTree *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(&bmain, &ptr, ID_NT)))
{
return group;
}
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports);
if (!asset) {
return nullptr;
}
Main &bmain = *CTX_data_main(&C);
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
asset::asset_local_id_ensure_imported(bmain, *asset));
return reinterpret_cast<bNodeTree *>(asset::asset_local_id_ensure_imported(bmain, *asset));
}
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
{
bNodeTree *node_group = get_asset_or_local_node_group(C, ptr, reports);
if (!node_group) {
return nullptr;
}
@ -270,11 +332,11 @@ static std::string modifier_add_asset_get_description(bContext *C,
return TIP_(asset->get_metadata().description);
}
static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot)
static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
{
ot->name = "Add Modifier";
ot->description = "Add a procedural operation/effect to the active object";
ot->idname = "OBJECT_OT_modifier_add_asset";
ot->idname = "OBJECT_OT_modifier_add_node_group";
ot->exec = modifier_add_asset_exec;
ot->poll = ED_operator_object_active_editable;
@ -283,6 +345,7 @@ static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
asset::operator_asset_reference_props_register(*ot->srna);
WM_operator_properties_id_lookup(ot, false);
}
static MenuType modifier_add_unassigned_assets_menu_type()
@ -291,7 +354,6 @@ static MenuType modifier_add_unassigned_assets_menu_type()
STRNCPY(type.idname, "OBJECT_MT_add_modifier_unassigned_assets");
type.draw = unassigned_assets_draw;
type.listener = asset::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
type.description = N_(
"Modifier node group assets not assigned to a catalog.\n"
"Catalogs can be assigned in the Asset Browser");
@ -323,7 +385,7 @@ void object_modifier_add_asset_register()
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_catalog_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_unassigned_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_root_catalogs_menu_type()));
WM_operatortype_append(OBJECT_OT_modifier_add_asset);
WM_operatortype_append(OBJECT_OT_modifier_add_node_group);
}
void ui_template_modifier_asset_menu_items(uiLayout &layout,

View File

@ -38,6 +38,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.hh"
#include "BKE_modifier.h"
#include "BKE_node_runtime.hh"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
@ -97,15 +98,16 @@ static void calculate_simulation_job_startjob(void *customdata,
continue;
}
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->runtime->cache) {
continue;
}
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
if (item.value->cache_status != bake::CacheStatus::Baked) {
item.value->reset();
}
if (md->type != eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->runtime->cache) {
continue;
}
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
if (item.value->cache_status != bake::CacheStatus::Baked) {
item.value->reset();
}
}
}
@ -222,15 +224,17 @@ static bool bake_simulation_poll(bContext *C)
return true;
}
struct ZoneBakeData {
int zone_id;
struct NodeBakeData {
int id;
bake::BakePath path;
int frame_start;
int frame_end;
std::unique_ptr<bake::BlobSharing> blob_sharing;
};
struct ModifierBakeData {
NodesModifierData *nmd;
Vector<ZoneBakeData> zones;
Vector<NodeBakeData> nodes;
};
struct ObjectBakeData {
@ -243,7 +247,7 @@ struct BakeSimulationJob {
Main *bmain;
Depsgraph *depsgraph;
Scene *scene;
Vector<Object *> objects;
Vector<ObjectBakeData> objects;
};
static void bake_simulation_job_startjob(void *customdata,
@ -256,57 +260,29 @@ static void bake_simulation_job_startjob(void *customdata,
G.is_break = false;
WM_set_locked_interface(job.wm, true);
Vector<ObjectBakeData> objects_to_bake;
for (Object *object : job.objects) {
if (!BKE_id_is_editable(job.bmain, &object->id)) {
continue;
}
int global_bake_start_frame = INT32_MAX;
int global_bake_end_frame = INT32_MIN;
ObjectBakeData bake_data;
bake_data.object = object;
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->node_group) {
continue;
}
if (!nmd->runtime->cache) {
continue;
}
ModifierBakeData modifier_bake_data;
modifier_bake_data.nmd = nmd;
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
item.value->reset();
}
for (const bNestedNodeRef &nested_node_ref : nmd->node_group->nested_node_refs_span()) {
ZoneBakeData zone_bake_data;
zone_bake_data.zone_id = nested_node_ref.id;
zone_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
if (std::optional<bake::BakePath> path = bake::get_node_bake_path(
*job.bmain, *object, *nmd, nested_node_ref.id))
{
zone_bake_data.path = std::move(*path);
modifier_bake_data.zones.append(std::move(zone_bake_data));
}
}
bake_data.modifiers.append(std::move(modifier_bake_data));
for (ObjectBakeData &object_bake : job.objects) {
for (ModifierBakeData &modifier_bake : object_bake.modifiers) {
for (NodeBakeData &node_bake : modifier_bake.nodes) {
global_bake_start_frame = std::min(global_bake_start_frame, node_bake.frame_start);
global_bake_end_frame = std::max(global_bake_end_frame, node_bake.frame_end);
}
}
objects_to_bake.append(std::move(bake_data));
}
*progress = 0.0f;
*do_update = true;
const int frames_to_bake = global_bake_end_frame - global_bake_start_frame + 1;
const float frame_step_size = 1.0f;
const float progress_per_frame = 1.0f / (float(job.scene->r.efra - job.scene->r.sfra + 1) /
frame_step_size);
const float progress_per_frame = frame_step_size / frames_to_bake;
const int old_frame = job.scene->r.cfra;
for (float frame_f = job.scene->r.sfra; frame_f <= job.scene->r.efra; frame_f += frame_step_size)
for (float frame_f = global_bake_start_frame; frame_f <= global_bake_end_frame;
frame_f += frame_step_size)
{
const SubFrame frame{frame_f};
@ -321,16 +297,16 @@ static void bake_simulation_job_startjob(void *customdata,
const std::string frame_file_name = bake::frame_to_file_name(frame);
for (ObjectBakeData &object_bake_data : objects_to_bake) {
for (ObjectBakeData &object_bake_data : job.objects) {
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
NodesModifierData &nmd = *modifier_bake_data.nmd;
const bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
for (ZoneBakeData &zone_bake_data : modifier_bake_data.zones) {
if (!modifier_cache.cache_by_id.contains(zone_bake_data.zone_id)) {
for (NodeBakeData &node_bake_data : modifier_bake_data.nodes) {
if (!modifier_cache.cache_by_id.contains(node_bake_data.id)) {
continue;
}
const bake::NodeCache &node_cache = *modifier_cache.cache_by_id.lookup(
zone_bake_data.zone_id);
node_bake_data.id);
if (node_cache.frame_caches.is_empty()) {
continue;
}
@ -339,7 +315,7 @@ static void bake_simulation_job_startjob(void *customdata,
continue;
}
const bake::BakePath path = zone_bake_data.path;
const bake::BakePath path = node_bake_data.path;
const std::string blob_file_name = frame_file_name + ".blob";
@ -357,7 +333,7 @@ static void bake_simulation_job_startjob(void *customdata,
bake::DiskBlobWriter blob_writer{blob_file_name, blob_file, 0};
fstream meta_file{meta_path, std::ios::out};
bake::serialize_bake(
frame_cache.state, blob_writer, *zone_bake_data.blob_sharing, meta_file);
frame_cache.state, blob_writer, *node_bake_data.blob_sharing, meta_file);
}
}
}
@ -366,15 +342,18 @@ static void bake_simulation_job_startjob(void *customdata,
*do_update = true;
}
for (ObjectBakeData &object_bake_data : objects_to_bake) {
for (ObjectBakeData &object_bake_data : job.objects) {
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
NodesModifierData &nmd = *modifier_bake_data.nmd;
for (ZoneBakeData &zone_bake_data : modifier_bake_data.zones) {
if (std::unique_ptr<bake::NodeCache> &node_cache = nmd.runtime->cache->cache_by_id.lookup(
zone_bake_data.zone_id))
for (NodeBakeData &node_bake_data : modifier_bake_data.nodes) {
if (std::unique_ptr<bake::NodeCache> *node_cache_ptr =
nmd.runtime->cache->cache_by_id.lookup_ptr(node_bake_data.id))
{
/* Tag the caches as being baked so that they are not changed anymore. */
node_cache->cache_status = bake::CacheStatus::Baked;
bake::NodeCache &node_cache = **node_cache_ptr;
if (!node_cache.frame_caches.is_empty()) {
/* Tag the caches as being baked so that they are not changed anymore. */
node_cache.cache_status = bake::CacheStatus::Baked;
}
}
}
}
@ -396,34 +375,18 @@ static void bake_simulation_job_endjob(void *customdata)
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
}
static int bake_simulation_exec(bContext *C, wmOperator *op)
static int start_bake_job(bContext *C, Vector<ObjectBakeData> objects_to_bake, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Main *bmain = CTX_data_main(C);
BakeSimulationJob *job = MEM_new<BakeSimulationJob>(__func__);
job->wm = wm;
job->bmain = bmain;
job->depsgraph = depsgraph;
job->scene = scene;
job->wm = CTX_wm_manager(C);
job->bmain = CTX_data_main(C);
job->depsgraph = CTX_data_depsgraph_pointer(C);
job->scene = CTX_data_scene(C);
job->objects = std::move(objects_to_bake);
if (RNA_boolean_get(op->ptr, "selected")) {
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
job->objects.append(object);
}
CTX_DATA_END;
}
else {
if (Object *object = CTX_data_active_object(C)) {
job->objects.append(object);
}
}
wmJob *wm_job = WM_jobs_get(wm,
wmJob *wm_job = WM_jobs_get(job->wm,
CTX_wm_window(C),
CTX_data_scene(C),
job->scene,
"Bake Simulation Nodes",
WM_JOB_PROGRESS,
WM_JOB_TYPE_BAKE_SIMULATION_NODES);
@ -439,6 +402,91 @@ static int bake_simulation_exec(bContext *C, wmOperator *op)
return OPERATOR_RUNNING_MODAL;
}
static Vector<ObjectBakeData> collect_nodes_to_bake(Main &bmain,
Scene &scene,
const Span<Object *> objects)
{
Vector<ObjectBakeData> objects_to_bake;
for (Object *object : objects) {
if (!BKE_id_is_editable(&bmain, &object->id)) {
continue;
}
ObjectBakeData bake_data;
bake_data.object = object;
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->node_group) {
continue;
}
if (!nmd->runtime->cache) {
continue;
}
ModifierBakeData modifier_bake_data;
modifier_bake_data.nmd = nmd;
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
item.value->reset();
}
for (const bNestedNodeRef &nested_node_ref : nmd->node_group->nested_node_refs_span()) {
NodeBakeData node_bake_data;
node_bake_data.id = nested_node_ref.id;
node_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
std::optional<bake::BakePath> path = bake::get_node_bake_path(
bmain, *object, *nmd, nested_node_ref.id);
if (!path) {
continue;
}
std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
scene, *object, *nmd, nested_node_ref.id);
if (!frame_range) {
continue;
}
node_bake_data.path = std::move(*path);
node_bake_data.frame_start = frame_range->first();
node_bake_data.frame_end = frame_range->last();
modifier_bake_data.nodes.append(std::move(node_bake_data));
}
if (modifier_bake_data.nodes.is_empty()) {
continue;
}
bake_data.modifiers.append(std::move(modifier_bake_data));
}
if (bake_data.modifiers.is_empty()) {
continue;
}
objects_to_bake.append(std::move(bake_data));
}
return objects_to_bake;
}
static int bake_simulation_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Main *bmain = CTX_data_main(C);
Vector<Object *> objects;
if (RNA_boolean_get(op->ptr, "selected")) {
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
objects.append(object);
}
CTX_DATA_END;
}
else {
if (Object *object = CTX_data_active_object(C)) {
objects.append(object);
}
}
Vector<ObjectBakeData> objects_to_bake = collect_nodes_to_bake(*bmain, *scene, objects);
return start_bake_job(C, std::move(objects_to_bake), op);
}
struct PathStringHash {
uint64_t operator()(const StringRef s) const
{
@ -601,10 +649,52 @@ static int bake_simulation_modal(bContext *C, wmOperator * /*op*/, const wmEvent
return OPERATOR_PASS_THROUGH;
}
static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
static void try_delete_bake(
bContext *C, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
{
Main *bmain = CTX_data_main(C);
if (!nmd.runtime->cache) {
return;
}
bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
std::lock_guard lock{modifier_cache.mutex};
if (!modifier_cache.cache_by_id.contains(bake_id)) {
return;
}
bake::NodeCache &node_cache = *modifier_cache.cache_by_id.lookup(bake_id);
node_cache.reset();
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
*bmain, object, nmd, bake_id);
if (!bake_path) {
return;
}
const char *meta_dir = bake_path->meta_dir.c_str();
if (BLI_exists(meta_dir)) {
if (BLI_delete(meta_dir, true, true)) {
BKE_reportf(reports, RPT_ERROR, "Failed to remove metadata directory %s", meta_dir);
}
}
const char *blobs_dir = bake_path->blobs_dir.c_str();
if (BLI_exists(blobs_dir)) {
if (BLI_delete(blobs_dir, true, true)) {
BKE_reportf(reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
}
}
if (bake_path->bake_dir.has_value()) {
const char *zone_bake_dir = bake_path->bake_dir->c_str();
/* Try to delete zone bake directory if it is empty. */
BLI_delete(zone_bake_dir, true, false);
}
if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
*bmain, object, nmd))
{
/* Try to delete modifier bake directory if it is empty. */
BLI_delete(modifier_bake_dir->c_str(), true, false);
}
}
static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
{
Vector<Object *> objects;
if (RNA_boolean_get(op->ptr, "selected")) {
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
@ -626,42 +716,8 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
if (!nmd->runtime->cache) {
continue;
}
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
item.value->reset();
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
*bmain, *object, *nmd, item.key);
if (!bake_path) {
continue;
}
const char *meta_dir = bake_path->meta_dir.c_str();
if (BLI_exists(meta_dir)) {
if (BLI_delete(meta_dir, true, true)) {
BKE_reportf(op->reports, RPT_ERROR, "Failed to remove meta directory %s", meta_dir);
}
}
const char *blobs_dir = bake_path->blobs_dir.c_str();
if (BLI_exists(blobs_dir)) {
if (BLI_delete(blobs_dir, true, true)) {
BKE_reportf(
op->reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
}
}
if (bake_path->bake_dir.has_value()) {
const char *zone_bake_dir = bake_path->bake_dir->c_str();
/* Try to delete zone bake directory if it is empty. */
BLI_delete(zone_bake_dir, true, false);
}
}
if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
*bmain, *object, *nmd))
{
/* Try to delete modifier bake directory if it is empty. */
BLI_delete(modifier_bake_dir->c_str(), true, false);
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
try_delete_bake(C, *object, *nmd, bake.id, op->reports);
}
}
}
@ -674,6 +730,102 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static int bake_single_simulation_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *object = reinterpret_cast<Object *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
if (object == nullptr) {
return OPERATOR_CANCELLED;
}
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
if (modifier_name == nullptr) {
return OPERATOR_CANCELLED;
}
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
if (md == nullptr) {
return OPERATOR_CANCELLED;
}
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
if (StringRef(nmd.simulation_bake_directory).is_empty()) {
const std::string directory = bake::get_default_modifier_bake_directory(*bmain, *object, nmd);
nmd.simulation_bake_directory = BLI_strdup(directory.c_str());
}
const int bake_id = RNA_int_get(op->ptr, "bake_id");
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
*bmain, *object, nmd, bake_id);
if (!bake_path.has_value()) {
return OPERATOR_CANCELLED;
}
const std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
*scene, *object, nmd, bake_id);
if (!frame_range.has_value()) {
return OPERATOR_CANCELLED;
}
if (frame_range->is_empty()) {
return OPERATOR_CANCELLED;
}
NodeBakeData node_bake_data;
node_bake_data.id = bake_id;
node_bake_data.path = std::move(*bake_path);
node_bake_data.frame_start = frame_range->first();
node_bake_data.frame_end = frame_range->last();
node_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
ModifierBakeData modifier_bake_data;
modifier_bake_data.nmd = &nmd;
modifier_bake_data.nodes.append(std::move(node_bake_data));
ObjectBakeData object_bake_data;
object_bake_data.object = object;
object_bake_data.modifiers.append(std::move(modifier_bake_data));
Vector<ObjectBakeData> objects_to_bake;
objects_to_bake.append(std::move(object_bake_data));
return start_bake_job(C, std::move(objects_to_bake), op);
}
static int bake_single_simulation_modal(bContext *C,
wmOperator * /*op*/,
const wmEvent * /*event*/)
{
if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_BAKE_SIMULATION_NODES)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_PASS_THROUGH;
}
static int delete_baked_simulation_single_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *object = reinterpret_cast<Object *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
if (object == nullptr) {
return OPERATOR_CANCELLED;
}
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
if (modifier_name == nullptr) {
return OPERATOR_CANCELLED;
}
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
if (md == nullptr) {
return OPERATOR_CANCELLED;
}
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
const int bake_id = RNA_int_get(op->ptr, "bake_id");
try_delete_bake(C, *object, nmd, bake_id, op->reports);
return OPERATOR_FINISHED;
}
} // namespace blender::ed::object::bake_simulation
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot)
@ -725,3 +877,62 @@ void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "selected", false, "Selected", "Delete cache on all selected objects");
}
void OBJECT_OT_simulation_nodes_cache_bake_single(wmOperatorType *ot)
{
using namespace blender::ed::object::bake_simulation;
ot->name = "Bake Single Simulation Zone";
ot->description = "Bake a single simulation zone";
ot->idname = "OBJECT_OT_simulation_nodes_cache_bake_single";
ot->exec = bake_single_simulation_exec;
ot->modal = bake_single_simulation_modal;
WM_operator_properties_id_lookup(ot, false);
RNA_def_string(ot->srna,
"modifier_name",
nullptr,
0,
"Modifier Name",
"Name of the modifier that contains the node to bake");
RNA_def_int(ot->srna,
"bake_id",
0,
0,
INT32_MAX,
"Bake ID",
"Nested node id of the node to bake",
0,
INT32_MAX);
}
void OBJECT_OT_simulation_nodes_cache_delete_single(wmOperatorType *ot)
{
using namespace blender::ed::object::bake_simulation;
ot->name = "Delete Single Cached Simulation";
ot->description = "Delete simulation data of a single simulation zone";
ot->idname = "OBJECT_OT_simulation_nodes_cache_delete_single";
ot->exec = delete_baked_simulation_single_exec;
WM_operator_properties_id_lookup(ot, false);
RNA_def_string(ot->srna,
"modifier_name",
nullptr,
0,
"Modifier Name",
"Name of the modifier that contains the node");
RNA_def_int(ot->srna,
"bake_id",
0,
0,
INT32_MAX,
"Bake ID",
"Nested node id of the bake to delete",
0,
INT32_MAX);
}

View File

@ -347,6 +347,8 @@ void OBJECT_OT_bake(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_bake_single(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_delete_single(wmOperatorType *ot);
/* `object_random.cc` */

View File

@ -259,6 +259,8 @@ void ED_operatortypes_object()
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_calculate_to_frame);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_bake);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_delete);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_bake_single);
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_delete_single);
WM_operatortype_append(OBJECT_OT_drop_named_material);
WM_operatortype_append(OBJECT_OT_drop_geometry_nodes);
WM_operatortype_append(OBJECT_OT_unlink_data);

View File

@ -2820,6 +2820,14 @@ void ED_region_clear(const bContext *C, const ARegion *region, const int /*Theme
}
}
static void region_clear_fully_transparent(const bContext *C)
{
/* view should be in pixelspace */
UI_view2d_view_restore(C);
GPU_clear_color(0, 0, 0, 0);
}
BLI_INLINE bool streq_array_any(const char *s, const char *arr[])
{
for (uint i = 0; arr[i]; i++) {
@ -3574,11 +3582,8 @@ void ED_region_header_layout(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
}
void ED_region_header_draw(const bContext *C, ARegion *region)
static void region_draw_blocks_in_view2d(const bContext *C, const ARegion *region)
{
/* clear */
ED_region_clear(C, region, region_background_color_id(C, region));
UI_view2d_view_ortho(&region->v2d);
/* View2D matrix might have changed due to dynamic sized regions. */
@ -3591,6 +3596,31 @@ void ED_region_header_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
}
void ED_region_header_draw(const bContext *C, ARegion *region)
{
/* clear */
ED_region_clear(C, region, region_background_color_id(C, region));
region_draw_blocks_in_view2d(C, region);
}
void ED_region_header_draw_with_button_sections(const bContext *C,
const ARegion *region,
const uiButtonSectionsAlign align)
{
const ThemeColorID bgcolorid = region_background_color_id(C, region);
/* Clear and draw button sections background when using region overlap. Otherwise clear using the
* background color like normal. */
if (region->overlap) {
region_clear_fully_transparent(C);
UI_region_button_sections_draw(region, bgcolorid, align);
}
else {
ED_region_clear(C, region, bgcolorid);
}
region_draw_blocks_in_view2d(C, region);
}
void ED_region_header(const bContext *C, ARegion *region)
{
/* TODO: remove? */
@ -3598,6 +3628,14 @@ void ED_region_header(const bContext *C, ARegion *region)
ED_region_header_draw(C, region);
}
void ED_region_header_with_button_sections(const bContext *C,
ARegion *region,
const uiButtonSectionsAlign align)
{
ED_region_header_layout(C, region);
ED_region_header_draw_with_button_sections(C, region, align);
}
void ED_region_header_init(ARegion *region)
{
UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy);

View File

@ -856,6 +856,20 @@ static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const b
break;
}
if (az->type == AZONE_REGION) {
const ARegion *region = az->region;
const int local_xy[2] = {xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin};
/* Respect button sections: If the mouse is horizontally hovering empty space defined by a
* separator-spacer between buttons, don't allow scaling the region from there. Used for
* regions that have a transparent background between such button sections, users don't
* expect to be able to resize from there. */
if (region->visible && (region->flag & RGN_FLAG_RESIZE_RESPECT_BUTTON_SECTIONS) &&
!UI_region_button_sections_is_inside_x(az->region, local_xy[0]))
{
az = nullptr;
break;
}
break;
}
if (az->type == AZONE_FULLSCREEN) {

View File

@ -188,8 +188,6 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u
uchar col1b[4], col2b[4];
uchar col_summary[4];
const bool show_group_colors = U.animation_flag & USER_ANIM_SHOW_CHANNEL_GROUP_COLORS;
/* get theme colors */
UI_GetThemeColor4ubv(TH_SHADE2, col2);
UI_GetThemeColor4ubv(TH_HILITE, col1);
@ -245,45 +243,9 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u
immUniformColor3ubvAlpha(col2b, sel ? col1[3] : col2b[3]);
break;
}
case ANIMTYPE_GROUP: {
bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
if (show_group_colors && agrp->customCol) {
if (sel) {
immUniformColor3ubvAlpha((uchar *)agrp->cs.select, col1a[3]);
}
else {
immUniformColor3ubvAlpha((uchar *)agrp->cs.solid, col2a[3]);
}
}
else {
immUniformColor4ubv(sel ? col1a : col2a);
}
case ANIMTYPE_GROUP:
immUniformColor4ubv(sel ? col1a : col2a);
break;
}
case ANIMTYPE_FCURVE: {
FCurve *fcu = static_cast<FCurve *>(ale->data);
if (show_group_colors && fcu->grp && fcu->grp->customCol) {
immUniformColor3ubvAlpha((uchar *)fcu->grp->cs.active, sel ? col1[3] : col2[3]);
}
else {
immUniformColor4ubv(sel ? col1 : col2);
}
break;
}
case ANIMTYPE_GPLAYER: {
if (show_group_colors) {
uchar gpl_col[4];
bGPDlayer *gpl = (bGPDlayer *)ale->data;
rgb_float_to_uchar(gpl_col, gpl->color);
gpl_col[3] = col1[3];
immUniformColor4ubv(sel ? col1 : gpl_col);
}
else {
immUniformColor4ubv(sel ? col1 : col2);
}
break;
}
default: {
immUniformColor4ubv(sel ? col1 : col2);
}
@ -294,26 +256,11 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u
}
else if (ac->datatype == ANIMCONT_GPENCIL) {
uchar *color;
uchar gpl_col[4];
switch (ale->type) {
case ANIMTYPE_SUMMARY:
color = col_summary;
break;
case ANIMTYPE_GPLAYER: {
if (show_group_colors) {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
rgb_float_to_uchar(gpl_col, gpl->color);
gpl_col[3] = col1[3];
color = sel ? col1 : gpl_col;
}
else {
color = sel ? col1 : col2;
}
break;
}
case ANIMTYPE_GREASE_PENCIL_LAYER_GROUP:
color = sel ? col1a : col2a;
break;
@ -356,6 +303,16 @@ static void draw_backdrops(bAnimContext *ac, ListBase &anim_data, View2D *v2d, u
immRectf(pos, v2d->cur.xmin, ymin, ac->scene->r.sfra, ymax);
immRectf(pos, ac->scene->r.efra, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
}
/* Alpha-over the channel color, if it's there. */
{
const bool show_group_colors = U.animation_flag & USER_ANIM_SHOW_CHANNEL_GROUP_COLORS;
uint8_t color[3];
if (show_group_colors && acf->get_channel_color && acf->get_channel_color(ale, color)) {
immUniformColor3ubvAlpha(color, 32);
immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax);
}
}
}
}
@ -748,56 +705,112 @@ static void timeline_cache_draw_single(PTCacheID *pid, float y_offset, float hei
GPU_matrix_pop();
}
static void timeline_cache_draw_simulation_nodes(const blender::bke::bake::ModifierCache &cache,
const float y_offset,
const float height,
const uint pos_id)
struct SimulationRange {
blender::IndexRange frames;
blender::bke::bake::CacheStatus status;
};
static void timeline_cache_draw_simulation_nodes(
const blender::Span<SimulationRange> simulation_ranges,
const bool all_simulations_baked,
float *y_offset,
const float line_height,
const uint pos_id)
{
std::lock_guard lock{cache.mutex};
if (cache.cache_by_id.is_empty()) {
if (simulation_ranges.is_empty()) {
return;
}
/* Draw the state if one of the simulation zones. This is fine for now, because there is no ui
* that allows caching zones independently. */
const blender::bke::bake::NodeCache &node_cache = **cache.cache_by_id.values().begin();
if (node_cache.frame_caches.is_empty()) {
return;
bool has_bake = false;
for (const SimulationRange &sim_range : simulation_ranges) {
switch (sim_range.status) {
case blender::bke::bake::CacheStatus::Invalid:
case blender::bke::bake::CacheStatus::Valid:
break;
case blender::bke::bake::CacheStatus::Baked:
has_bake = true;
break;
}
}
blender::Set<int> status_change_frames_set;
for (const SimulationRange &sim_range : simulation_ranges) {
status_change_frames_set.add(sim_range.frames.first());
status_change_frames_set.add(sim_range.frames.one_after_last());
}
blender::Vector<int> status_change_frames;
status_change_frames.extend(status_change_frames_set.begin(), status_change_frames_set.end());
std::sort(status_change_frames.begin(), status_change_frames.end());
const blender::OffsetIndices<int> frame_ranges = status_change_frames.as_span();
GPU_matrix_push();
GPU_matrix_translate_2f(0.0, float(V2D_SCROLL_HANDLE_HEIGHT) + y_offset);
GPU_matrix_scale_2f(1.0, height);
GPU_matrix_translate_2f(0.0, float(V2D_SCROLL_HANDLE_HEIGHT) + *y_offset);
GPU_matrix_scale_2f(1.0, line_height);
float color[4];
UI_GetThemeColor4fv(TH_SIMULATED_FRAMES, color);
switch (node_cache.cache_status) {
case blender::bke::bake::CacheStatus::Invalid: {
color[3] = 0.4f;
break;
blender::float4 base_color;
UI_GetThemeColor4fv(TH_SIMULATED_FRAMES, base_color);
blender::float4 invalid_color = base_color;
invalid_color.w *= 0.4f;
blender::float4 valid_color = base_color;
valid_color.w *= 0.7f;
blender::float4 baked_color = base_color;
float max_used_height = 1.0f;
for (const int range_i : frame_ranges.index_range()) {
const blender::IndexRange frame_range = frame_ranges[range_i];
const int start_frame = frame_range.first();
const int end_frame = frame_range.last();
bool has_bake_at_frame = false;
bool has_valid_at_frame = false;
bool has_invalid_at_frame = false;
for (const SimulationRange &sim_range : simulation_ranges) {
if (sim_range.frames.contains(start_frame)) {
switch (sim_range.status) {
case blender::bke::bake::CacheStatus::Invalid:
has_invalid_at_frame = true;
break;
case blender::bke::bake::CacheStatus::Valid:
has_valid_at_frame = true;
break;
case blender::bke::bake::CacheStatus::Baked:
has_bake_at_frame = true;
break;
}
}
}
case blender::bke::bake::CacheStatus::Valid: {
color[3] = 0.7f;
break;
if (!(has_bake_at_frame || has_valid_at_frame || has_invalid_at_frame)) {
continue;
}
case blender::bke::bake::CacheStatus::Baked: {
color[3] = 1.0f;
break;
if (all_simulations_baked) {
immUniformColor4fv(baked_color);
immBeginAtMost(GPU_PRIM_TRIS, 6);
immRectf_fast(pos_id, start_frame, 0, end_frame + 1.0f, 1.0f);
immEnd();
}
else {
if (has_valid_at_frame || has_invalid_at_frame) {
immUniformColor4fv(has_invalid_at_frame ? invalid_color : valid_color);
immBeginAtMost(GPU_PRIM_TRIS, 6);
const float top = has_bake ? 2.0f : 1.0f;
immRectf_fast(pos_id, start_frame, 0.0f, end_frame + 1.0f, top);
immEnd();
max_used_height = top;
}
if (has_bake_at_frame) {
immUniformColor4fv(baked_color);
immBeginAtMost(GPU_PRIM_TRIS, 6);
immRectf_fast(pos_id, start_frame, 0, end_frame + 1.0f, 1.0f);
immEnd();
}
}
}
immUniformColor4fv(color);
immBeginAtMost(GPU_PRIM_TRIS, node_cache.frame_caches.size() * 6);
for (const std::unique_ptr<blender::bke::bake::FrameCache> &frame_cache :
node_cache.frame_caches.as_span())
{
const int frame = frame_cache->frame.frame();
immRectf_fast(pos_id, frame - 0.5f, 0, frame + 0.5f, 1.0f);
}
immEnd();
GPU_matrix_pop();
*y_offset += max_used_height * 2;
}
void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Scene *scene)
@ -832,6 +845,8 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
y_offset += cache_draw_height;
}
if (saction->cache_display & TIME_CACHE_SIMULATION_NODES) {
blender::Vector<SimulationRange> simulation_ranges;
bool all_simulations_baked = true;
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
@ -846,10 +861,29 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
if ((nmd->node_group->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_SIMULATION_ZONE) == 0) {
continue;
}
timeline_cache_draw_simulation_nodes(
*nmd->runtime->cache, y_offset, cache_draw_height, pos_id);
y_offset += cache_draw_height;
const blender::bke::bake::ModifierCache &modifier_cache = *nmd->runtime->cache;
{
std::lock_guard lock{modifier_cache.mutex};
for (const std::unique_ptr<blender::bke::bake::NodeCache> &node_cache_ptr :
modifier_cache.cache_by_id.values())
{
const blender::bke::bake::NodeCache &node_cache = *node_cache_ptr;
if (node_cache.frame_caches.is_empty()) {
all_simulations_baked = false;
continue;
}
if (node_cache.cache_status != blender::bke::bake::CacheStatus::Baked) {
all_simulations_baked = false;
}
const int start_frame = node_cache.frame_caches.first()->frame.frame();
const int end_frame = node_cache.frame_caches.last()->frame.frame();
const blender::IndexRange frame_range{start_frame, end_frame - start_frame + 1};
simulation_ranges.append({frame_range, node_cache.cache_status});
}
}
}
timeline_cache_draw_simulation_nodes(
simulation_ranges, all_simulations_baked, &y_offset, cache_draw_height, pos_id);
}
GPU_blend(GPU_BLEND_NONE);

View File

@ -110,7 +110,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
asset_params->asset_library_ref.type = ASSET_LIBRARY_ALL;
asset_params->asset_library_ref.custom_library_index = -1;
asset_params->import_type = FILE_ASSET_IMPORT_FOLLOW_PREFS;
asset_params->import_method = FILE_ASSET_IMPORT_FOLLOW_PREFS;
}
FileSelectParams *base_params = &asset_params->base_params;
@ -519,12 +519,12 @@ int ED_fileselect_asset_import_method_get(const SpaceFile *sfile, const FileDirE
const FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
if (params->import_type == FILE_ASSET_IMPORT_FOLLOW_PREFS) {
if (params->import_method == FILE_ASSET_IMPORT_FOLLOW_PREFS) {
std::optional import_method = file->asset->get_import_method();
return import_method ? *import_method : -1;
}
switch (eFileAssetImportType(params->import_type)) {
switch (eFileAssetImportMethod(params->import_method)) {
case FILE_ASSET_IMPORT_LINK:
return ASSET_IMPORT_LINK;
case FILE_ASSET_IMPORT_APPEND:

View File

@ -889,6 +889,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
float r_start;
float r_end;
BKE_action_frame_range_get(static_cast<bAction *>(ale->data), &r_start, &r_end);
BKE_nla_clip_length_ensure_nonzero(&r_start, &r_end);
immRectf(pos, r_end, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
break;

View File

@ -245,6 +245,52 @@ float2 space_node_group_offset(const SpaceNode &snode)
return float2(0);
}
static const bNode *group_node_by_name(const bNodeTree &ntree, StringRef name)
{
for (const bNode *node : ntree.group_nodes()) {
if (node->name == name) {
return node;
}
}
return nullptr;
}
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node)
{
BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast<bNode *>(&query_node)));
std::optional<int32_t> id_in_node;
const char *group_node_name = nullptr;
const bNode *node = &query_node;
LISTBASE_FOREACH_BACKWARD (const bNodeTreePath *, path, &snode.treepath) {
const bNodeTree *ntree = path->nodetree;
if (group_node_name) {
node = group_node_by_name(*ntree, group_node_name);
}
bool found = false;
for (const bNestedNodeRef &ref : ntree->nested_node_refs_span()) {
if (node->is_group()) {
if (ref.path.node_id == node->identifier && ref.path.id_in_node == id_in_node) {
group_node_name = path->node_name;
id_in_node = ref.id;
found = true;
break;
}
}
else if (ref.path.node_id == node->identifier) {
group_node_name = path->node_name;
id_in_node = ref.id;
found = true;
break;
}
}
if (!found) {
return std::nullopt;
}
}
return id_in_node;
}
std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode)
{
if (snode.id == nullptr) {

View File

@ -40,9 +40,8 @@ void TreeElementIDObject::expand(SpaceOutliner & /*space_outliner*/) const
object_.id.newid = (ID *)(&legacy_te_);
expand_animation_data(object_.adt);
expand_data();
expand_pose();
expand_data();
expand_materials();
expand_constraints();
expand_modifiers();

View File

@ -197,6 +197,7 @@ static ImBuf *sequencer_make_scope(Scene *scene, ImBuf *ibuf, ImBuf *(*make_scop
display_ibuf, &scene->view_settings, &scene->display_settings);
scope = make_scope_fn(display_ibuf);
IMB_rectfill_alpha(scope, 1.0f);
IMB_freeImBuf(display_ibuf);

View File

@ -1626,6 +1626,11 @@ static void view3d_header_region_listener(const wmRegionListenerParams *params)
blender::ed::geometry::clear_operator_asset_trees();
ED_region_tag_redraw(region);
break;
default:
if (ELEM(wmn->action, NA_ADDED, NA_REMOVED)) {
blender::ed::geometry::clear_operator_asset_trees();
ED_region_tag_redraw(region);
}
}
break;
case NC_NODE:

View File

@ -869,6 +869,7 @@ void ED_region_image_metadata_draw(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
immUniformThemeColor(TH_METADATA_BG);
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
immUnbindProgram();
@ -880,6 +881,7 @@ void ED_region_image_metadata_draw(
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
BLF_disable(blf_mono_font, BLF_CLIPPING);
GPU_blend(GPU_BLEND_NONE);
}
/* *** lower box*** */
@ -894,6 +896,7 @@ void ED_region_image_metadata_draw(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
immUniformThemeColor(TH_METADATA_BG);
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
immUnbindProgram();
@ -905,6 +908,7 @@ void ED_region_image_metadata_draw(
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
BLF_disable(blf_mono_font, BLF_CLIPPING);
GPU_blend(GPU_BLEND_NONE);
}
GPU_matrix_pop();

View File

@ -7,6 +7,8 @@
*/
#ifndef USE_GPU_SHADER_CREATE_INFO
# pragma once
# include "GPU_shader_shared_utils.h"
typedef struct TestOutputRawData TestOutputRawData;

View File

@ -13,6 +13,7 @@
#include "vk_immediate.hh"
#include "vk_memory.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
#include "vk_texture.hh"
@ -201,6 +202,12 @@ void VKContext::bind_graphics_pipeline(const GPUPrimType prim_type,
{
VKShader *shader = unwrap(this->shader);
BLI_assert(shader);
BLI_assert_msg(
prim_type != GPU_PRIM_POINTS || shader->interface_get().is_point_shader(),
"GPU_PRIM_POINTS is used with a shader that doesn't set point size before "
"drawing fragments. Calling code should be adapted to use a shader that sets the "
"gl_PointSize before entering the fragment stage. For example `GPU_SHADER_3D_POINT_*`.");
shader->update_graphics_pipeline(*this, prim_type, vertex_attribute_object);
VKPipeline &pipeline = shader->pipeline_get();

View File

@ -18,6 +18,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
static size_t PUSH_CONSTANTS_FALLBACK_NAME_LEN = strlen(PUSH_CONSTANTS_FALLBACK_NAME);
using namespace blender::gpu::shader;
shader_builtins_ = info.builtins_;
attr_len_ = info.vertex_inputs_.size();
uniform_len_ = info.push_constants_.size();

View File

@ -32,6 +32,8 @@ class VKShaderInterface : public ShaderInterface {
VKPushConstants::Layout push_constants_layout_;
shader::BuiltinBits shader_builtins_;
public:
VKShaderInterface() = default;
@ -53,6 +55,11 @@ class VKShaderInterface : public ShaderInterface {
return static_cast<shader::Type>(attr_types_[location]);
}
bool is_point_shader() const
{
return (shader_builtins_ & shader::BuiltinBits::POINT_SIZE) == shader::BuiltinBits::POINT_SIZE;
}
private:
/**
* Retrieve the shader input for the given resource.

View File

@ -209,6 +209,11 @@ if(WITH_OPENVDB)
)
endif()
if(WITH_MATERIALX)
add_definitions(-DWITH_MATERIALX)
list(APPEND LIB MaterialXCore)
endif()
blender_add_lib(bf_io_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h

View File

@ -52,7 +52,9 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
const View3D *view3d = nullptr;
Main *bmain = nullptr;
Scene *scene = nullptr;
ShadingSettings shading_settings;
bool use_materialx = true;
private:
ObjectDataMap objects_;

View File

@ -12,6 +12,11 @@
#include <pxr/imaging/hd/tokens.h>
#include <pxr/usdImaging/usdImaging/materialParamUtils.h>
#ifdef WITH_MATERIALX
# include <pxr/usd/usdMtlx/reader.h>
# include <pxr/usd/usdMtlx/utils.h>
#endif
#include "MEM_guardedalloc.h"
#include "BKE_lib_id.h"
@ -30,7 +35,10 @@
#include "intern/usd_exporter_context.h"
#include "intern/usd_writer_material.h"
#ifdef WITH_MATERIALX
# include "shader/materialx/node_parser.h"
# include "shader/materialx/material.h"
#endif
namespace blender::io::hydra {
MaterialData::MaterialData(HydraSceneDelegate *scene_delegate,
@ -67,10 +75,35 @@ void MaterialData::init()
get_time_code,
export_params,
image_cache_file_path()};
/* Create USD material. */
pxr::UsdShadeMaterial usd_material = usd::create_usd_material(
export_context, material_path, (Material *)id, "st");
pxr::UsdShadeMaterial usd_material;
#ifdef WITH_MATERIALX
if (scene_delegate_->use_materialx) {
MaterialX::DocumentPtr doc = blender::nodes::materialx::export_to_materialx(
scene_delegate_->depsgraph, (Material *)id, cache_or_get_image_file);
pxr::UsdMtlxRead(doc, stage);
/* Logging stage: creating lambda stage_str() to not call stage->ExportToString()
* if log won't be printed. */
auto stage_str = [&stage]() {
std::string str;
stage->ExportToString(&str);
return str;
};
ID_LOGN(2, "Stage:\n%s", stage_str().c_str());
if (pxr::UsdPrim materials = stage->GetPrimAtPath(pxr::SdfPath("/MaterialX/Materials"))) {
pxr::UsdPrimSiblingRange children = materials.GetChildren();
if (!children.empty()) {
usd_material = pxr::UsdShadeMaterial(*children.begin());
}
}
}
else
#endif
{
usd_material = usd::create_usd_material(export_context, material_path, (Material *)id, "st");
}
/* Convert USD material to Hydra material network map, adapted for render delegate. */
const pxr::HdRenderDelegate *render_delegate =

View File

@ -400,8 +400,8 @@ typedef enum eBone_BBoneHandleType {
/* bone->bbone_mapping_mode */
typedef enum eBone_BBoneMappingMode {
BBONE_MAPPING_STRAIGHT = 0, /* Default mode that ignores the rest pose curvature. */
BBONE_MAPPING_CURVED = 1, /* Mode that takes the rest pose curvature into account. */
BBONE_MAPPING_STRAIGHT = 0, /* Default mode that ignores the rest pose curvature. */
BBONE_MAPPING_CURVED = 1, /* Mode that takes the rest pose curvature into account. */
} eBone_BBoneMappingMode;
/* bone->bbone_flag */

View File

@ -2325,6 +2325,29 @@ typedef struct NodesModifierSettings {
struct IDProperty *properties;
} NodesModifierSettings;
typedef struct NodesModifierBake {
/** An id that references a nested node in the node tree. Also see #bNestedNodeRef. */
int id;
/** #NodesModifierBakeFlag. */
uint32_t flag;
/**
* Directory where the baked data should be stored. This is only used when
* `NODES_MODIFIER_BAKE_CUSTOM_PATH` is set.
*/
char *directory;
/**
* Frame range for the simulation and baking that is used if
* `NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE` is set.
*/
int frame_start;
int frame_end;
} NodesModifierBake;
typedef enum NodesModifierBakeFlag {
NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE = 1 << 0,
NODES_MODIFIER_BAKE_CUSTOM_PATH = 1 << 1,
} NodesModifierBakeFlag;
typedef struct NodesModifierData {
ModifierData modifier;
struct bNodeTree *node_group;
@ -2333,13 +2356,20 @@ typedef struct NodesModifierData {
* Directory where baked simulation states are stored. This may be relative to the .blend file.
*/
char *simulation_bake_directory;
/** NodesModifierFlag. */
int8_t flag;
char _pad[7];
char _pad[3];
int bakes_num;
NodesModifierBake *bakes;
void *_pad2;
NodesModifierRuntimeHandle *runtime;
#ifdef __cplusplus
NodesModifierBake *find_bake(int id);
const NodesModifierBake *find_bake(int id) const;
#endif
} NodesModifierData;
typedef enum NodesModifierFlag {

View File

@ -716,6 +716,7 @@ typedef struct bNodeTree {
const bNestedNodeRef *nested_node_ref_from_node_id_path(blender::Span<int> node_ids) const;
[[nodiscard]] bool node_id_path_from_nested_node_ref(const int32_t nested_node_id,
blender::Vector<int32_t> &r_node_ids) const;
const bNode *find_nested_node(int32_t nested_node_id) const;
/**
* Update a run-time cache for the node tree based on it's current state. This makes many methods

View File

@ -797,7 +797,9 @@ enum {
OB_HIDE_VOLUME_SCATTER = 1 << 7,
OB_HIDE_SHADOW = 1 << 8,
OB_HOLDOUT = 1 << 9,
OB_SHADOW_CATCHER = 1 << 10
OB_SHADOW_CATCHER = 1 << 10,
OB_HIDE_PROBE_VOLUME = 1 << 11,
OB_HIDE_PROBE_CUBEMAP = 1 << 12,
};
/** #Object.shapeflag */

View File

@ -266,6 +266,8 @@
.eevee = _DNA_DEFAULT_SceneEEVEE, \
\
.hydra = _DNA_DEFAULT_SceneHydra, \
.simulation_frame_start = 1, \
.simulation_frame_end = 250, \
}
/** \} */

View File

@ -2052,6 +2052,13 @@ typedef struct Scene {
/** Settings to be override by work-spaces. */
IDProperty *layer_properties;
/**
* Frame range used for simulations in geometry nodes by default, if SCE_CUSTOM_SIMULATION_RANGE
* is set. Individual simulations can overwrite this though.
*/
int simulation_frame_start;
int simulation_frame_end;
struct SceneDisplay display;
struct SceneEEVEE eevee;
struct SceneGpencil grease_pencil_settings;
@ -2491,6 +2498,7 @@ enum {
SCE_FRAME_DROP = 1 << 3,
SCE_KEYS_NO_SELONLY = 1 << 4,
SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK = 1 << 5,
SCE_CUSTOM_SIMULATION_RANGE = 1 << 6,
};
/* Return flag BKE_scene_base_iter_next functions. */

Some files were not shown because too many files have changed in this diff Show More