Geometry Nodes: add Inspection Index to Repeat Zone #112818
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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...).
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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="",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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{};
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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_);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ®ion->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(®ion->v2d.cur) /
|
||||
(BLI_rcti_size_x(®ion->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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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` */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(®ion->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(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*/
|
||||
|
||||
#ifndef USE_GPU_SHADER_CREATE_INFO
|
||||
# pragma once
|
||||
|
||||
# include "GPU_shader_shared_utils.h"
|
||||
|
||||
typedef struct TestOutputRawData TestOutputRawData;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -266,6 +266,8 @@
|
|||
.eevee = _DNA_DEFAULT_SceneEEVEE, \
|
||||
\
|
||||
.hydra = _DNA_DEFAULT_SceneHydra, \
|
||||
.simulation_frame_start = 1, \
|
||||
.simulation_frame_end = 250, \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue