USD: Skeleton and blend shape import #110912
@ -19,6 +19,11 @@ float safe_modulo(float a, float b)
|
||||
return (b != 0.0) ? fmod(a, b) : 0.0;
|
||||
}
|
||||
|
||||
float safe_floored_modulo(float a, float b)
|
||||
{
|
||||
return (b != 0.0) ? a - floor(a / b) * b : 0.0;
|
||||
}
|
||||
|
||||
float fract(float a)
|
||||
{
|
||||
return a - floor(a);
|
||||
|
@ -52,6 +52,8 @@ shader node_math(string math_type = "add",
|
||||
Value = Value1 - floor(Value1);
|
||||
else if (math_type == "modulo")
|
||||
Value = safe_modulo(Value1, Value2);
|
||||
else if (math_type == "floored_modulo")
|
||||
Value = safe_floored_modulo(Value1, Value2);
|
||||
else if (math_type == "trunc")
|
||||
Value = trunc(Value1);
|
||||
else if (math_type == "snap")
|
||||
|
@ -145,6 +145,8 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c)
|
||||
return a - floorf(a);
|
||||
case NODE_MATH_MODULO:
|
||||
return safe_modulo(a, b);
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
return safe_floored_modulo(a, b);
|
||||
case NODE_MATH_TRUNC:
|
||||
return a >= 0.0f ? floorf(a) : ceilf(a);
|
||||
case NODE_MATH_SNAP:
|
||||
|
@ -182,6 +182,7 @@ typedef enum NodeMathType {
|
||||
NODE_MATH_PINGPONG,
|
||||
NODE_MATH_SMOOTH_MIN,
|
||||
NODE_MATH_SMOOTH_MAX,
|
||||
NODE_MATH_FLOORED_MODULO,
|
||||
} NodeMathType;
|
||||
|
||||
typedef enum NodeVectorMathType {
|
||||
|
@ -6292,6 +6292,7 @@ NODE_DEFINE(MathNode)
|
||||
type_enum.insert("less_than", NODE_MATH_LESS_THAN);
|
||||
type_enum.insert("greater_than", NODE_MATH_GREATER_THAN);
|
||||
type_enum.insert("modulo", NODE_MATH_MODULO);
|
||||
type_enum.insert("floored_modulo", NODE_MATH_FLOORED_MODULO);
|
||||
type_enum.insert("absolute", NODE_MATH_ABSOLUTE);
|
||||
type_enum.insert("arctan2", NODE_MATH_ARCTAN2);
|
||||
type_enum.insert("floor", NODE_MATH_FLOOR);
|
||||
|
@ -732,6 +732,11 @@ ccl_device float safe_modulo(float a, float b)
|
||||
return (b != 0.0f) ? fmodf(a, b) : 0.0f;
|
||||
}
|
||||
|
||||
ccl_device float safe_floored_modulo(float a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? a - floorf(a / b) * b : 0.0f;
|
||||
}
|
||||
|
||||
ccl_device_inline float sqr(float a)
|
||||
{
|
||||
return a * a;
|
||||
|
@ -219,6 +219,7 @@ class GHOST_DeviceVK {
|
||||
device_features.dualSrcBlend = VK_TRUE;
|
||||
device_features.logicOp = VK_TRUE;
|
||||
device_features.imageCubeArray = VK_TRUE;
|
||||
device_features.multiViewport = VK_TRUE;
|
||||
#endif
|
||||
|
||||
VkDeviceCreateInfo device_create_info = {};
|
||||
|
@ -18,7 +18,7 @@ luma: [0.2126, 0.7152, 0.0722]
|
||||
description: RRT version ut33
|
||||
|
||||
roles:
|
||||
reference: Linear
|
||||
reference: Linear CIE-XYZ E
|
||||
|
||||
# Internal scene linear space
|
||||
scene_linear: Linear
|
||||
@ -41,6 +41,7 @@ roles:
|
||||
|
||||
# For interop between configs, and to determine XYZ for rendering
|
||||
aces_interchange: Linear ACES
|
||||
cie_xyz_d65_interchange: Linear CIE-XYZ D65
|
||||
|
||||
# Specified by OCIO, not used in Blender
|
||||
color_timing: Filmic Log
|
||||
@ -61,6 +62,27 @@ active_displays: [sRGB]
|
||||
active_views: [Standard, Filmic, Filmic Log, False Color, Raw]
|
||||
|
||||
colorspaces:
|
||||
- !<ColorSpace>
|
||||
name: Linear CIE-XYZ E
|
||||
aliases: ["FilmLight: Linear - XYZ", Linear CIE-XYZ I-E]
|
||||
family: Chromaticity
|
||||
equalitygroup:
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
1931 CIE XYZ standard with assumed illuminant E white point
|
||||
isdata: false
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Linear CIE-XYZ D65
|
||||
aliases: [cie_xyz_d65, CIE-XYZ-D65, XYZ, Linear CIE-XYZ D65]
|
||||
family: Chromaticity
|
||||
equalitygroup:
|
||||
bitdepth: 32f
|
||||
description: |
|
||||
1931 CIE XYZ with adapted illuminant D65 white point
|
||||
isdata: false
|
||||
from_scene_reference: !<FileTransform> {src: xyz_E_to_D65.spimtx, interpolation: linear}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Linear
|
||||
family: linear
|
||||
@ -69,6 +91,10 @@ colorspaces:
|
||||
description: |
|
||||
Rec. 709 (Full Range), Blender native linear space
|
||||
isdata: false
|
||||
from_scene_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear CIE-XYZ D65}
|
||||
- !<MatrixTransform> {matrix: [ 3.2410032329763587, -1.5373989694887855, -0.4986158819963629, 0, -0.9692242522025164, 1.8759299836951759, 0.0415542263400847, 0, 0.0556394198519755, -0.2040112061239099, 1.0571489771875333, 0, 0, 0, 0, 1]}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Linear ACES
|
||||
@ -80,7 +106,7 @@ colorspaces:
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: srgb_to_xyz.spimtx, interpolation: linear}
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear CIE-XYZ D65}
|
||||
- !<BuiltinTransform> {style: "UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD", direction: inverse}
|
||||
|
||||
- !<ColorSpace>
|
||||
@ -93,19 +119,9 @@ colorspaces:
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: srgb_to_xyz.spimtx, interpolation: linear}
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear CIE-XYZ D65}
|
||||
- !<BuiltinTransform> {style: "UTILITY - ACES-AP1_to_CIE-XYZ-D65_BFD", direction: inverse}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: XYZ
|
||||
family: linear
|
||||
equalitygroup:
|
||||
bitdepth: 32f
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: srgb_to_xyz.spimtx, interpolation: linear}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: sRGB
|
||||
family:
|
||||
@ -114,7 +130,10 @@ colorspaces:
|
||||
description: |
|
||||
sRGB display space
|
||||
isdata: false
|
||||
from_reference: !<ExponentWithLinearTransform> {gamma: 2.4, offset: 0.055, direction: inverse}
|
||||
from_scene_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear}
|
||||
- !<ExponentWithLinearTransform> {gamma: 2.4, offset: 0.055, direction: inverse}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Non-Color
|
||||
@ -135,11 +154,15 @@ colorspaces:
|
||||
Log based filmic shaper with 16.5 stops of latitude, and 25 stops of dynamic range
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<AllocationTransform> {allocation: lg2, vars: [-12.473931188, 12.526068812]}
|
||||
- !<FileTransform> {src: filmic_desat65cube.spi3d, interpolation: best}
|
||||
- !<AllocationTransform> {allocation: uniform, vars: [0, 0.66]}
|
||||
to_reference: !<AllocationTransform> {allocation: lg2, vars: [-12.473931188, 4.026068812], direction: inverse}
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear}
|
||||
- !<AllocationTransform> {allocation: lg2, vars: [-12.473931188, 12.526068812]}
|
||||
- !<FileTransform> {src: filmic_desat65cube.spi3d, interpolation: best}
|
||||
- !<AllocationTransform> {allocation: uniform, vars: [0, 0.66]}
|
||||
to_scene_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<AllocationTransform> {allocation: lg2, vars: [-12.473931188, 4.026068812], direction: inverse}
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Linear, direction: inverse}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: Filmic sRGB
|
||||
@ -150,9 +173,9 @@ colorspaces:
|
||||
sRGB display space with Filmic view transform
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear, dst: Filmic Log}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear}
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Filmic Log}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear}
|
||||
|
||||
- !<ColorSpace>
|
||||
name: False Color
|
||||
@ -163,61 +186,61 @@ colorspaces:
|
||||
Filmic false color view transform
|
||||
isdata: false
|
||||
from_reference: !<GroupTransform>
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear, dst: Filmic Log}
|
||||
- !<MatrixTransform> {matrix: [0.2126729, 0.7151521, 0.0721750, 0, 0.2126729, 0.7151521, 0.0721750, 0, 0.2126729, 0.7151521, 0.0721750, 0, 0, 0, 0, 1]}
|
||||
- !<FileTransform> {src: filmic_false_color.spi3d, interpolation: best}
|
||||
children:
|
||||
- !<ColorSpaceTransform> {src: Linear CIE-XYZ E, dst: Filmic Log}
|
||||
- !<MatrixTransform> {matrix: [0.2126729, 0.7151521, 0.0721750, 0, 0.2126729, 0.7151521, 0.0721750, 0, 0.2126729, 0.7151521, 0.0721750, 0, 0, 0, 0, 1]}
|
||||
- !<FileTransform> {src: filmic_false_color.spi3d, interpolation: best}
|
||||
looks:
|
||||
- !<Look>
|
||||
name: Very High Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_1.20_1-00.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_1.20_1-00.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
||||
- !<Look>
|
||||
name: High Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0.99_1-0075.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0.99_1-0075.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
||||
- !<Look>
|
||||
name: Medium High Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-85_1-011.spi1d, interpolation: best}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-85_1-011.spi1d, interpolation: best}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
||||
- !<Look>
|
||||
name: Medium Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
children:
|
||||
|
||||
- !<Look>
|
||||
name: Medium Low Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-60_1-04.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-60_1-04.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
||||
- !<Look>
|
||||
name: Low Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-48_1-09.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-48_1-09.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
||||
- !<Look>
|
||||
name: Very Low Contrast
|
||||
process_space: Filmic Log
|
||||
transform: !<GroupTransform>
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-35_1-30.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
children:
|
||||
- !<FileTransform> {src: filmic_to_0-35_1-30.spi1d, interpolation: linear}
|
||||
- !<FileTransform> {src: filmic_to_0-70_1-03.spi1d, interpolation: linear, direction: inverse}
|
||||
|
@ -1,3 +0,0 @@
|
||||
0.4124564 0.3575761 0.1804375 0
|
||||
0.2126729 0.7151522 0.0721750 0
|
||||
0.0193339 0.1191920 0.9503041 0
|
@ -1,3 +0,0 @@
|
||||
1.0521111 0.0000000 0.0000000 0
|
||||
0.0000000 1.0000000 0.0000000 0
|
||||
0.0000000 0.0000000 0.9184170 0
|
@ -0,0 +1,3 @@
|
||||
0.95318743 -0.02659057 0.02387315 0
|
||||
-0.03824666 1.02884062 0.00940604 0
|
||||
0.00260677 -0.00303325 1.08925647 0
|
@ -79,6 +79,7 @@ const UserDef U_default = {
|
||||
|
||||
.scrollback = 256,
|
||||
.node_margin = 80,
|
||||
.node_preview_res = 120,
|
||||
.transopts = USER_TR_TOOLTIPS,
|
||||
.menuthreshold1 = 5,
|
||||
.menuthreshold2 = 2,
|
||||
|
@ -846,7 +846,7 @@ class NODE_PT_overlay(Panel):
|
||||
col.prop(overlay, "show_context_path", text="Context Path")
|
||||
col.prop(snode, "show_annotation", text="Annotations")
|
||||
|
||||
if snode.supports_preview:
|
||||
if snode.supports_previews:
|
||||
col.separator()
|
||||
col.prop(overlay, "show_previews", text="Previews")
|
||||
|
||||
|
@ -501,6 +501,7 @@ class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel):
|
||||
col = layout.column()
|
||||
col.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color")
|
||||
col.prop(edit, "node_margin", text="Node Auto-Offset Margin")
|
||||
col.prop(edit, "node_preview_resolution", text="Node Preview Resolution")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@ -2407,6 +2408,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
||||
({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")),
|
||||
({"property": "use_rotation_socket"}, ("/blender/blender/issues/92967", "#92967")),
|
||||
({"property": "use_node_group_operators"}, ("/blender/blender/issues/101778", "#101778")),
|
||||
({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bDeformGroup;
|
||||
struct CustomData;
|
||||
struct CustomDataLayer;
|
||||
struct ID;
|
||||
@ -117,6 +118,12 @@ 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);
|
||||
|
@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 14
|
||||
#define BLENDER_FILE_SUBVERSION 15
|
||||
|
||||
/* 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
|
||||
|
@ -89,6 +89,7 @@ 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);
|
||||
/**
|
||||
|
@ -30,6 +30,12 @@ typedef struct Global {
|
||||
*/
|
||||
struct Main *main;
|
||||
|
||||
/**
|
||||
* Preview main is stored to avoid loading the preview file in multiple scenarios.
|
||||
* It is actually shared between shader node previews and asset previews.
|
||||
*/
|
||||
struct Main *pr_main;
|
||||
|
||||
/** Last saved location for images. */
|
||||
char ima[1024]; /* 1024 = FILE_MAX */
|
||||
/** Last used location for library link/append. */
|
||||
|
@ -57,6 +57,7 @@ void BKE_object_material_from_eval_data(struct Main *bmain,
|
||||
struct Material *BKE_material_add(struct Main *bmain, const char *name);
|
||||
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
|
||||
void BKE_gpencil_material_attr_init(struct Material *ma);
|
||||
void BKE_material_make_node_previews_dirty(struct Material *ma);
|
||||
|
||||
/* UNUSED */
|
||||
// void automatname(struct Material *);
|
||||
|
@ -94,6 +94,12 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
|
||||
*/
|
||||
uint8_t runtime_flag = 0;
|
||||
|
||||
/**
|
||||
* Contains a number increased for each nodetree update.
|
||||
* Store a state variable in the `NestedTreePreviews` structure to compare if they differ.
|
||||
*/
|
||||
uint32_t previews_refresh_state = 0;
|
||||
|
||||
/**
|
||||
* Storage of nodes based on their identifier. Also used as a contiguous array of nodes to
|
||||
* allow simpler and more cache friendly iteration. Supports lookup by integer or by node.
|
||||
|
@ -235,6 +235,8 @@ bool BKE_scene_uses_blender_eevee(const struct Scene *scene);
|
||||
bool BKE_scene_uses_blender_workbench(const struct Scene *scene);
|
||||
bool BKE_scene_uses_cycles(const struct Scene *scene);
|
||||
|
||||
bool BKE_scene_uses_shader_previews(const struct Scene *scene);
|
||||
|
||||
/**
|
||||
* Return whether the Cycles experimental feature is enabled. It is invalid to call without first
|
||||
* ensuring that Cycles is the active render engine (e.g. with #BKE_scene_uses_cycles).
|
||||
|
@ -31,6 +31,7 @@
|
||||
#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"
|
||||
@ -223,13 +224,14 @@ bool BKE_id_attribute_rename(ID *id,
|
||||
return true;
|
||||
}
|
||||
|
||||
struct AttrUniqueData {
|
||||
ID *id;
|
||||
};
|
||||
|
||||
static bool unique_name_cb(void *arg, const char *name)
|
||||
bool BKE_id_attribute_and_defgroup_unique_name_check(void *arg, const char *name)
|
||||
{
|
||||
AttrUniqueData *data = (AttrUniqueData *)arg;
|
||||
AttributeAndDefgroupUniqueNameData *data = static_cast<AttributeAndDefgroupUniqueNameData *>(
|
||||
arg);
|
||||
|
||||
if (BKE_defgroup_unique_name_check(data, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DomainInfo info[ATTR_DOMAIN_NUM];
|
||||
get_domains(data->id, info);
|
||||
@ -254,15 +256,18 @@ static bool unique_name_cb(void *arg, const char *name)
|
||||
|
||||
bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
|
||||
{
|
||||
AttrUniqueData data{id};
|
||||
AttributeAndDefgroupUniqueNameData data{id, nullptr};
|
||||
|
||||
const int name_maxncpy = CustomData_name_maxncpy_calc(name);
|
||||
|
||||
/* Set default name if none specified.
|
||||
* 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(unique_name_cb, &data, defname, '.', outname, name_maxncpy);
|
||||
return BLI_uniquename_cb(
|
||||
BKE_id_attribute_and_defgroup_unique_name_check, &data, defname, '.', outname, name_maxncpy);
|
||||
}
|
||||
|
||||
CustomDataLayer *BKE_id_attribute_new(ID *id,
|
||||
@ -301,6 +306,10 @@ CustomDataLayer *BKE_id_attribute_new(ID *id,
|
||||
attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefaultValue());
|
||||
|
||||
const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
|
||||
if (index == -1) {
|
||||
BKE_reportf(reports, RPT_WARNING, "Layer '%s' could not be created", uniquename);
|
||||
}
|
||||
|
||||
return (index == -1) ? nullptr : &(customdata->layers[index]);
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,6 @@ void BKE_collection_compat_blend_read_expand(BlendExpander *expander, SceneColle
|
||||
|
||||
void BKE_collection_blend_read_expand(BlendExpander *expander, Collection *collection)
|
||||
{
|
||||
BLI_assert(collection->runtime.gobject_hash == nullptr);
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
||||
BLO_expand(expander, cob->ob);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_data_transfer.h"
|
||||
#include "BKE_deform.h" /* own include */
|
||||
@ -681,9 +682,9 @@ 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, Object *ob)
|
||||
static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, ID *id)
|
||||
{
|
||||
const ListBase *defbase = BKE_object_defgroup_list(ob);
|
||||
const ListBase *defbase = BKE_id_defgroup_list_get(id);
|
||||
|
||||
LISTBASE_FOREACH (bDeformGroup *, curdef, defbase) {
|
||||
if (dg != curdef) {
|
||||
@ -696,21 +697,31 @@ static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, Object *
|
||||
return false;
|
||||
}
|
||||
|
||||
struct DeformGroupUniqueNameData {
|
||||
Object *ob;
|
||||
bDeformGroup *dg;
|
||||
};
|
||||
|
||||
static bool defgroup_unique_check(void *arg, const char *name)
|
||||
bool BKE_defgroup_unique_name_check(void *arg, const char *name)
|
||||
{
|
||||
DeformGroupUniqueNameData *data = static_cast<DeformGroupUniqueNameData *>(arg);
|
||||
return defgroup_find_name_dupe(name, data->dg, data->ob);
|
||||
AttributeAndDefgroupUniqueNameData *data = static_cast<AttributeAndDefgroupUniqueNameData *>(
|
||||
arg);
|
||||
return defgroup_find_name_dupe(name, data->dg, data->id);
|
||||
}
|
||||
|
||||
void BKE_object_defgroup_unique_name(bDeformGroup *dg, Object *ob)
|
||||
{
|
||||
DeformGroupUniqueNameData data{ob, dg};
|
||||
BLI_uniquename_cb(defgroup_unique_check, &data, DATA_("Group"), '.', dg->name, sizeof(dg->name));
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
|
||||
float BKE_defvert_find_weight(const MDeformVert *dvert, const int defgroup)
|
||||
|
@ -1728,6 +1728,14 @@ void GreasePencil::set_active_layer(const blender::bke::greasepencil::Layer *lay
|
||||
reinterpret_cast<const GreasePencilLayer *>(layer));
|
||||
}
|
||||
|
||||
bool GreasePencil::is_layer_active(const blender::bke::greasepencil::Layer *layer) const
|
||||
{
|
||||
if (layer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return this->get_active_layer() == layer;
|
||||
}
|
||||
|
||||
static blender::VectorSet<blender::StringRefNull> get_node_names(GreasePencil &grease_pencil)
|
||||
{
|
||||
using namespace blender;
|
||||
|
@ -304,6 +304,27 @@ void BKE_gpencil_material_attr_init(Material *ma)
|
||||
}
|
||||
}
|
||||
|
||||
static void nodetree_mark_previews_dirty_reccursive(bNodeTree *tree)
|
||||
{
|
||||
if (tree == nullptr) {
|
||||
return;
|
||||
}
|
||||
tree->runtime->previews_refresh_state++;
|
||||
for (bNode *node : tree->all_nodes()) {
|
||||
if (node->type == NODE_GROUP) {
|
||||
bNodeTree *nested_tree = reinterpret_cast<bNodeTree *>(node->id);
|
||||
nodetree_mark_previews_dirty_reccursive(nested_tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_material_make_node_previews_dirty(Material *ma)
|
||||
{
|
||||
if (ma && ma->nodetree) {
|
||||
nodetree_mark_previews_dirty_reccursive(ma->nodetree);
|
||||
}
|
||||
}
|
||||
|
||||
Material *BKE_material_add(Main *bmain, const char *name)
|
||||
{
|
||||
Material *ma;
|
||||
|
@ -478,6 +478,7 @@ class NodeTreeMainUpdater {
|
||||
this->update_internal_links(ntree);
|
||||
this->update_generic_callback(ntree);
|
||||
this->remove_unused_previews_when_necessary(ntree);
|
||||
this->make_node_previews_dirty(ntree);
|
||||
|
||||
this->propagate_runtime_flags(ntree);
|
||||
if (ntree.type == NTREE_GEOMETRY) {
|
||||
@ -723,6 +724,19 @@ class NodeTreeMainUpdater {
|
||||
blender::bke::node_preview_remove_unused(&ntree);
|
||||
}
|
||||
|
||||
void make_node_previews_dirty(bNodeTree &ntree)
|
||||
{
|
||||
ntree.runtime->previews_refresh_state++;
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (node->type != NODE_GROUP) {
|
||||
continue;
|
||||
}
|
||||
if (bNodeTree *nested_tree = reinterpret_cast<bNodeTree *>(node->id)) {
|
||||
this->make_node_previews_dirty(*nested_tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void propagate_runtime_flags(const bNodeTree &ntree)
|
||||
{
|
||||
ntree.ensure_topology_cache();
|
||||
|
@ -870,6 +870,13 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
|
||||
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER);
|
||||
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
|
||||
data,
|
||||
IDP_foreach_property(view_layer->id_properties,
|
||||
IDP_TYPE_FILTER_ID,
|
||||
BKE_lib_query_idpropertiesForeachIDLink_callback,
|
||||
data));
|
||||
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
|
||||
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
|
||||
@ -3017,6 +3024,11 @@ bool BKE_scene_uses_cycles(const Scene *scene)
|
||||
return STREQ(scene->r.engine, RE_engine_id_CYCLES);
|
||||
}
|
||||
|
||||
bool BKE_scene_uses_shader_previews(const Scene *scene)
|
||||
{
|
||||
return BKE_scene_uses_blender_eevee(scene) || BKE_scene_uses_cycles(scene);
|
||||
}
|
||||
|
||||
/* This enumeration has to match the one defined in the Cycles addon. */
|
||||
enum eCyclesFeatureSet {
|
||||
CYCLES_FEATURES_SUPPORTED = 0,
|
||||
|
@ -22,6 +22,11 @@ MINLINE float safe_modf(float a, float b)
|
||||
return (b != 0.0f) ? fmodf(a, b) : 0.0f;
|
||||
}
|
||||
|
||||
MINLINE float safe_floored_modf(float a, float b)
|
||||
{
|
||||
return (b != 0.0f) ? a - floorf(a / b) * b : 0.0f;
|
||||
}
|
||||
|
||||
MINLINE float safe_logf(float a, float base)
|
||||
{
|
||||
if (UNLIKELY(a <= 0.0f || base <= 0.0f)) {
|
||||
|
@ -847,6 +847,10 @@ void blo_do_versions_userdef(UserDef *userdef)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(400, 15)) {
|
||||
userdef->node_preview_res = 120;
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
@ -76,6 +76,9 @@ void MathNode::convert_to_operations(NodeConverter &converter,
|
||||
case NODE_MATH_MODULO:
|
||||
operation = new MathModuloOperation();
|
||||
break;
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
operation = new MathFlooredModuloOperation();
|
||||
break;
|
||||
case NODE_MATH_ABSOLUTE:
|
||||
operation = new MathAbsoluteOperation();
|
||||
break;
|
||||
|
@ -589,6 +589,36 @@ void MathModuloOperation::update_memory_buffer_partial(BuffersIterator<float> &i
|
||||
}
|
||||
}
|
||||
|
||||
void MathFlooredModuloOperation::execute_pixel_sampled(float output[4],
|
||||
float x,
|
||||
float y,
|
||||
PixelSampler sampler)
|
||||
{
|
||||
float input_value1[4];
|
||||
float input_value2[4];
|
||||
|
||||
input_value1_operation_->read_sampled(input_value1, x, y, sampler);
|
||||
input_value2_operation_->read_sampled(input_value2, x, y, sampler);
|
||||
|
||||
if (input_value2[0] == 0) {
|
||||
output[0] = 0.0;
|
||||
}
|
||||
else {
|
||||
output[0] = input_value1[0] - floorf(input_value1[0] / input_value2[0]) * input_value2[0];
|
||||
}
|
||||
|
||||
clamp_if_needed(output);
|
||||
}
|
||||
|
||||
void MathFlooredModuloOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
|
||||
{
|
||||
for (; !it.is_end(); ++it) {
|
||||
const float value2 = *it.in(1);
|
||||
*it.out = (value2 == 0) ? 0 : *it.in(0) - floorf(*it.in(0) / value2) * value2;
|
||||
clamp_when_enabled(it.out);
|
||||
}
|
||||
}
|
||||
|
||||
void MathAbsoluteOperation::execute_pixel_sampled(float output[4],
|
||||
float x,
|
||||
float y,
|
||||
|
@ -224,6 +224,14 @@ class MathModuloOperation : public MathBaseOperation {
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class MathFlooredModuloOperation : public MathBaseOperation {
|
||||
public:
|
||||
void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
||||
protected:
|
||||
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
|
||||
};
|
||||
|
||||
class MathAbsoluteOperation : public MathBaseOperation {
|
||||
public:
|
||||
void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
|
||||
|
@ -49,7 +49,6 @@ class World {
|
||||
Instance &inst_;
|
||||
|
||||
DefaultWorldNodeTree default_tree;
|
||||
bool has_volume_ = false;
|
||||
|
||||
/* Used to detect if world change. */
|
||||
::World *prev_original_world = nullptr;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_node.h"
|
||||
|
||||
@ -291,9 +292,10 @@ void ANIM_sync_animchannels_to_data(const bContext *C)
|
||||
animchan_sync_gplayer(ale);
|
||||
break;
|
||||
case ANIMTYPE_GREASE_PENCIL_LAYER:
|
||||
using namespace blender::bke::greasepencil;
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
|
||||
GreasePencilLayer *layer = static_cast<GreasePencilLayer *>(ale->data);
|
||||
if (grease_pencil->active_layer == layer) {
|
||||
Layer *layer = static_cast<Layer *>(ale->data);
|
||||
if (grease_pencil->is_layer_active(layer)) {
|
||||
layer->base.flag |= GP_LAYER_TREE_NODE_SELECT;
|
||||
}
|
||||
else {
|
||||
|
@ -1767,7 +1767,7 @@ static size_t animdata_filter_grease_pencil_layers_data(ListBase *anim_data,
|
||||
}
|
||||
|
||||
/* Only if the layer is active. */
|
||||
if ((filter_mode & ANIMFILTER_ACTIVE) && (grease_pencil->active_layer == layer)) {
|
||||
if ((filter_mode & ANIMFILTER_ACTIVE) && grease_pencil->is_layer_active(layer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
47
source/blender/editors/include/ED_node_preview.hh
Normal file
47
source/blender/editors/include/ED_node_preview.hh
Normal file
@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
struct bContext;
|
||||
struct bNodeTree;
|
||||
struct ImBuf;
|
||||
struct Render;
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
struct NestedTreePreviews {
|
||||
Render *previews_render = nullptr;
|
||||
/* Use this map to keep track of the latest ImBuf used (after freeing the renderresult). */
|
||||
blender::Map<int32_t, ImBuf *> previews_map;
|
||||
int preview_size;
|
||||
bool rendering = false;
|
||||
bool restart_needed = false;
|
||||
uint32_t cached_previews_refresh_state = -1;
|
||||
uint32_t rendering_previews_refresh_state = -1;
|
||||
NestedTreePreviews(const int size) : preview_size(size) {}
|
||||
~NestedTreePreviews()
|
||||
{
|
||||
if (this->previews_render) {
|
||||
RE_FreeRender(this->previews_render);
|
||||
}
|
||||
for (ImBuf *ibuf : this->previews_map.values()) {
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void free_previews(wmWindowManager &wm, SpaceNode &snode);
|
||||
ImBuf *node_preview_acquire_ibuf(bNodeTree &ntree,
|
||||
NestedTreePreviews &tree_previews,
|
||||
const bNode &node);
|
||||
void node_release_preview_ibuf(NestedTreePreviews &tree_previews);
|
||||
NestedTreePreviews *get_nested_previews(const bContext &C, SpaceNode &snode);
|
||||
void stop_preview_job(wmWindowManager &wm);
|
||||
|
||||
} // namespace blender::ed::space_node
|
@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
struct DEGEditorUpdateContext;
|
||||
@ -22,6 +23,8 @@ struct ScrArea;
|
||||
struct bContext;
|
||||
struct bScreen;
|
||||
struct PreviewImage;
|
||||
struct ViewLayer;
|
||||
struct World;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
|
||||
@ -64,7 +67,10 @@ enum ePreviewRenderMethod {
|
||||
PR_ICON_DEFERRED = 2,
|
||||
};
|
||||
|
||||
void ED_preview_ensure_dbase();
|
||||
bool ED_check_engine_supports_preview(const Scene *scene);
|
||||
const char *ED_preview_collection_name(ePreviewType pr_type);
|
||||
|
||||
void ED_preview_ensure_dbase(bool with_gpencil);
|
||||
void ED_preview_free_dbase();
|
||||
|
||||
/**
|
||||
@ -72,6 +78,17 @@ void ED_preview_free_dbase();
|
||||
*/
|
||||
bool ED_preview_id_is_supported(const ID *id);
|
||||
|
||||
void ED_preview_set_visibility(Main *pr_main,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
ePreviewType pr_type,
|
||||
ePreviewRenderMethod pr_method);
|
||||
struct World *ED_preview_prepare_world(Main *pr_main,
|
||||
const Scene *scene,
|
||||
const World *world,
|
||||
ID_Type id_type,
|
||||
ePreviewRenderMethod pr_method);
|
||||
|
||||
void ED_preview_shader_job(const bContext *C,
|
||||
void *owner,
|
||||
ID *id,
|
||||
|
@ -157,7 +157,6 @@ struct IconPreview {
|
||||
/** \name Preview for Buttons
|
||||
* \{ */
|
||||
|
||||
static Main *G_pr_main = nullptr;
|
||||
static Main *G_pr_main_grease_pencil = nullptr;
|
||||
|
||||
#ifndef WITH_HEADLESS
|
||||
@ -180,21 +179,25 @@ static Main *load_main_from_memory(const void *blend, int blend_size)
|
||||
}
|
||||
#endif
|
||||
|
||||
void ED_preview_ensure_dbase()
|
||||
void ED_preview_ensure_dbase(const bool with_gpencil)
|
||||
{
|
||||
#ifndef WITH_HEADLESS
|
||||
static bool base_initialized = false;
|
||||
static bool base_initialized_gpencil = false;
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
if (!base_initialized) {
|
||||
G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size);
|
||||
G.pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size);
|
||||
base_initialized = true;
|
||||
}
|
||||
if (!base_initialized_gpencil && with_gpencil) {
|
||||
G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend,
|
||||
datatoc_preview_grease_pencil_blend_size);
|
||||
base_initialized = true;
|
||||
base_initialized_gpencil = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool check_engine_supports_preview(Scene *scene)
|
||||
bool ED_check_engine_supports_preview(const Scene *scene)
|
||||
{
|
||||
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
||||
return (type->flag & RE_USE_PREVIEW) != 0;
|
||||
@ -207,8 +210,8 @@ static bool preview_method_is_render(const ePreviewRenderMethod pr_method)
|
||||
|
||||
void ED_preview_free_dbase()
|
||||
{
|
||||
if (G_pr_main) {
|
||||
BKE_main_free(G_pr_main);
|
||||
if (G.pr_main) {
|
||||
BKE_main_free(G.pr_main);
|
||||
}
|
||||
|
||||
if (G_pr_main_grease_pencil) {
|
||||
@ -225,7 +228,7 @@ static Scene *preview_get_scene(Main *pr_main)
|
||||
return static_cast<Scene *>(pr_main->scenes.first);
|
||||
}
|
||||
|
||||
static const char *preview_collection_name(const ePreviewType pr_type)
|
||||
const char *ED_preview_collection_name(const ePreviewType pr_type)
|
||||
{
|
||||
switch (pr_type) {
|
||||
case MA_FLAT:
|
||||
@ -265,7 +268,7 @@ static void switch_preview_collection_visibility(ViewLayer *view_layer, const eP
|
||||
{
|
||||
/* Set appropriate layer as visible. */
|
||||
LayerCollection *lc = static_cast<LayerCollection *>(view_layer->layer_collections.first);
|
||||
const char *collection_name = preview_collection_name(pr_type);
|
||||
const char *collection_name = ED_preview_collection_name(pr_type);
|
||||
|
||||
for (lc = static_cast<LayerCollection *>(lc->layer_collections.first); lc; lc = lc->next) {
|
||||
if (STREQ(lc->collection->id.name + 2, collection_name)) {
|
||||
@ -326,11 +329,11 @@ static void switch_preview_floor_visibility(Main *pr_main,
|
||||
}
|
||||
}
|
||||
|
||||
static void set_preview_visibility(Main *pr_main,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
const ePreviewType pr_type,
|
||||
const ePreviewRenderMethod pr_method)
|
||||
void ED_preview_set_visibility(Main *pr_main,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
const ePreviewType pr_type,
|
||||
const ePreviewRenderMethod pr_method)
|
||||
{
|
||||
switch_preview_collection_visibility(view_layer, pr_type);
|
||||
switch_preview_floor_visibility(pr_main, scene, view_layer, pr_method);
|
||||
@ -443,13 +446,13 @@ static void preview_sync_exposure(World *dst, const World *src)
|
||||
dst->range = src->range;
|
||||
}
|
||||
|
||||
static World *preview_prepare_world(Main *pr_main,
|
||||
const Scene *sce,
|
||||
const World *world,
|
||||
const ID_Type id_type,
|
||||
const ePreviewRenderMethod pr_method)
|
||||
World *ED_preview_prepare_world(Main *pr_main,
|
||||
const Scene *scene,
|
||||
const World *world,
|
||||
const ID_Type id_type,
|
||||
const ePreviewRenderMethod pr_method)
|
||||
{
|
||||
World *result = preview_get_world(pr_main, sce, id_type, pr_method);
|
||||
World *result = preview_get_world(pr_main, scene, id_type, pr_method);
|
||||
if (world) {
|
||||
preview_sync_exposure(result, world);
|
||||
}
|
||||
@ -494,7 +497,7 @@ static Scene *preview_prepare_scene(
|
||||
sce->r.cfra = scene->r.cfra;
|
||||
|
||||
/* Setup the world. */
|
||||
sce->world = preview_prepare_world(
|
||||
sce->world = ED_preview_prepare_world(
|
||||
pr_main, sce, scene->world, static_cast<ID_Type>(id_type), sp->pr_method);
|
||||
|
||||
if (id_type == ID_TE) {
|
||||
@ -531,7 +534,7 @@ static Scene *preview_prepare_scene(
|
||||
(sp->pr_method == PR_ICON_RENDER && sp->pr_main == G_pr_main_grease_pencil) ?
|
||||
MA_SPHERE_A :
|
||||
(ePreviewType)mat->pr_type);
|
||||
set_preview_visibility(pr_main, sce, view_layer, preview_type, sp->pr_method);
|
||||
ED_preview_set_visibility(pr_main, sce, view_layer, preview_type, sp->pr_method);
|
||||
}
|
||||
else {
|
||||
sce->display.render_aa = SCE_DISPLAY_AA_OFF;
|
||||
@ -579,7 +582,7 @@ static Scene *preview_prepare_scene(
|
||||
BLI_addtail(&pr_main->lights, la);
|
||||
}
|
||||
|
||||
set_preview_visibility(pr_main, sce, view_layer, MA_LAMP, sp->pr_method);
|
||||
ED_preview_set_visibility(pr_main, sce, view_layer, MA_LAMP, sp->pr_method);
|
||||
|
||||
if (sce->world) {
|
||||
/* Only use lighting from the light. */
|
||||
@ -608,7 +611,7 @@ static Scene *preview_prepare_scene(
|
||||
BLI_addtail(&pr_main->worlds, wrld);
|
||||
}
|
||||
|
||||
set_preview_visibility(pr_main, sce, view_layer, MA_SKY, sp->pr_method);
|
||||
ED_preview_set_visibility(pr_main, sce, view_layer, MA_SKY, sp->pr_method);
|
||||
sce->world = wrld;
|
||||
}
|
||||
|
||||
@ -1522,7 +1525,7 @@ static void other_id_types_preview_render(IconPreview *ip,
|
||||
}
|
||||
|
||||
if ((ma == nullptr) || (ma->gp_style == nullptr)) {
|
||||
sp->pr_main = G_pr_main;
|
||||
sp->pr_main = G.pr_main;
|
||||
}
|
||||
else {
|
||||
sp->pr_main = G_pr_main_grease_pencil;
|
||||
@ -1581,7 +1584,7 @@ static void icon_preview_startjob_all_sizes(void *customdata,
|
||||
* necessary to know here what happens inside lower-level functions. */
|
||||
const bool use_solid_render_mode = (ip->id != nullptr) && ELEM(GS(ip->id->name), ID_OB, ID_AC);
|
||||
if (!use_solid_render_mode && preview_method_is_render(pr_method) &&
|
||||
!check_engine_supports_preview(ip->scene))
|
||||
!ED_check_engine_supports_preview(ip->scene))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -1929,7 +1932,7 @@ void ED_preview_icon_render(
|
||||
bool stop = false, update = false;
|
||||
float progress = 0.0f;
|
||||
|
||||
ED_preview_ensure_dbase();
|
||||
ED_preview_ensure_dbase(true);
|
||||
|
||||
ip.bmain = CTX_data_main(C);
|
||||
ip.scene = scene;
|
||||
@ -1973,7 +1976,7 @@ void ED_preview_icon_job(
|
||||
|
||||
IconPreview *ip, *old_ip;
|
||||
|
||||
ED_preview_ensure_dbase();
|
||||
ED_preview_ensure_dbase(true);
|
||||
|
||||
/* suspended start means it starts after 1 timer step, see WM_jobs_timer below */
|
||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
||||
@ -2038,11 +2041,11 @@ void ED_preview_shader_job(const bContext *C,
|
||||
/* Use workspace render only for buttons Window,
|
||||
* since the other previews are related to the datablock. */
|
||||
|
||||
if (preview_method_is_render(method) && !check_engine_supports_preview(scene)) {
|
||||
if (preview_method_is_render(method) && !ED_check_engine_supports_preview(scene)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ED_preview_ensure_dbase();
|
||||
ED_preview_ensure_dbase(true);
|
||||
|
||||
wm_job = WM_jobs_get(CTX_wm_manager(C),
|
||||
CTX_wm_window(C),
|
||||
@ -2075,7 +2078,7 @@ void ED_preview_shader_job(const bContext *C,
|
||||
}
|
||||
|
||||
if ((ma == nullptr) || (ma->gp_style == nullptr)) {
|
||||
sp->pr_main = G_pr_main;
|
||||
sp->pr_main = G.pr_main;
|
||||
}
|
||||
else {
|
||||
sp->pr_main = G_pr_main_grease_pencil;
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "ED_node.hh"
|
||||
#include "ED_node_preview.hh"
|
||||
#include "ED_paint.hh"
|
||||
#include "ED_render.hh"
|
||||
#include "ED_view3d.hh"
|
||||
@ -177,6 +178,11 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
|
||||
ED_render_engine_area_exit(bmain, area);
|
||||
}
|
||||
}
|
||||
/* Invalidate all shader previews. */
|
||||
blender::ed::space_node::stop_preview_job(*static_cast<wmWindowManager *>(bmain->wm.first));
|
||||
LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
|
||||
BKE_material_make_node_previews_dirty(ma);
|
||||
}
|
||||
RE_FreePersistentData(nullptr);
|
||||
/* Inform all render engines and draw managers. */
|
||||
DEGEditorUpdateContext update_ctx = {nullptr};
|
||||
|
@ -43,6 +43,7 @@ set(SRC
|
||||
node_ops.cc
|
||||
node_relationships.cc
|
||||
node_select.cc
|
||||
node_shader_preview.cc
|
||||
node_templates.cc
|
||||
node_view.cc
|
||||
space_node.cc
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "BKE_node_tree_update.h"
|
||||
#include "BKE_node_tree_zones.hh"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
@ -68,6 +69,7 @@
|
||||
|
||||
#include "ED_gpencil_legacy.hh"
|
||||
#include "ED_node.hh"
|
||||
#include "ED_node_preview.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_space_api.hh"
|
||||
#include "ED_viewer_path.hh"
|
||||
@ -98,6 +100,7 @@
|
||||
namespace geo_log = blender::nodes::geo_eval_log;
|
||||
using blender::bke::bNodeTreeZone;
|
||||
using blender::bke::bNodeTreeZones;
|
||||
using blender::ed::space_node::NestedTreePreviews;
|
||||
|
||||
/**
|
||||
* This is passed to many functions which draw the node editor.
|
||||
@ -116,6 +119,8 @@ struct TreeDrawContext {
|
||||
* currently drawn node tree can be retrieved from the log below.
|
||||
*/
|
||||
blender::Map<const bNodeTreeZone *, geo_log::GeoTreeLog *> geo_log_by_zone;
|
||||
|
||||
NestedTreePreviews *nested_group_infos = nullptr;
|
||||
/**
|
||||
* True if there is an active realtime compositor using the node tree, false otherwise.
|
||||
*/
|
||||
@ -351,8 +356,7 @@ static void node_update_basis(const bContext &C,
|
||||
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
|
||||
|
||||
const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS);
|
||||
const bool inputs_first = node.inputs.first &&
|
||||
!(node.outputs.first || (node.flag & NODE_PREVIEW) || node_options);
|
||||
const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options);
|
||||
|
||||
/* Get "global" coordinates. */
|
||||
float2 loc = node_to_view(node, float2(0));
|
||||
@ -529,7 +533,7 @@ static void node_update_basis(const bContext &C,
|
||||
}
|
||||
|
||||
/* Little bit of space in end. */
|
||||
if (node.inputs.first || (node.flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0) {
|
||||
if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) {
|
||||
dy -= NODE_DYS / 2;
|
||||
}
|
||||
|
||||
@ -2107,6 +2111,10 @@ static void node_draw_extra_info_panel(const Scene *scene,
|
||||
if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) {
|
||||
return;
|
||||
}
|
||||
if (preview && !(preview->x > 0 && preview->y > 0)) {
|
||||
/* If the preview has an non-drawable size, just dont draw it. */
|
||||
preview = nullptr;
|
||||
}
|
||||
Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(tree_draw_ctx, snode, node);
|
||||
if (extra_info_rows.size() == 0 && !preview) {
|
||||
return;
|
||||
@ -2205,12 +2213,14 @@ static void node_draw_basis(const bContext &C,
|
||||
bNodeInstanceKey key)
|
||||
{
|
||||
const float iconbutw = NODE_HEADER_ICON_SIZE;
|
||||
bNodeInstanceHash *previews = static_cast<bNodeInstanceHash *>(
|
||||
CTX_data_pointer_get(&C, "node_previews").data);
|
||||
const bool show_preview = (snode.overlay.flag & SN_OVERLAY_SHOW_PREVIEWS) &&
|
||||
(node.flag & NODE_PREVIEW) &&
|
||||
(U.experimental.use_shader_node_previews ||
|
||||
ntree.type != NTREE_SHADER);
|
||||
|
||||
/* Skip if out of view. */
|
||||
rctf rect_with_preview = node.runtime->totr;
|
||||
if (node.flag & NODE_PREVIEW && previews && snode.overlay.flag & SN_OVERLAY_SHOW_PREVIEWS) {
|
||||
if (show_preview) {
|
||||
rect_with_preview.ymax += NODE_WIDTH(node);
|
||||
}
|
||||
if (BLI_rctf_isect(&rect_with_preview, &v2d.cur, nullptr) == false) {
|
||||
@ -2234,19 +2244,36 @@ static void node_draw_basis(const bContext &C,
|
||||
|
||||
GPU_line_width(1.0f);
|
||||
|
||||
ImBuf *preview = nullptr;
|
||||
if (node.flag & NODE_PREVIEW && previews && snode.overlay.flag & SN_OVERLAY_SHOW_PREVIEWS) {
|
||||
bNodePreview *preview_compositor = static_cast<bNodePreview *>(
|
||||
BKE_node_instance_hash_lookup(previews, key));
|
||||
if (preview_compositor) {
|
||||
preview = preview_compositor->ibuf;
|
||||
/* Overlay atop the node. */
|
||||
{
|
||||
bool drawn_with_previews = false;
|
||||
|
||||
if (show_preview) {
|
||||
bNodeInstanceHash *previews_compo = static_cast<bNodeInstanceHash *>(
|
||||
CTX_data_pointer_get(&C, "node_previews").data);
|
||||
NestedTreePreviews *previews_shader = tree_draw_ctx.nested_group_infos;
|
||||
|
||||
if (previews_shader) {
|
||||
ImBuf *preview = node_preview_acquire_ibuf(ntree, *previews_shader, node);
|
||||
node_draw_extra_info_panel(CTX_data_scene(&C), tree_draw_ctx, snode, node, preview, block);
|
||||
node_release_preview_ibuf(*previews_shader);
|
||||
drawn_with_previews = true;
|
||||
}
|
||||
else if (previews_compo) {
|
||||
bNodePreview *preview_compositor = static_cast<bNodePreview *>(
|
||||
BKE_node_instance_hash_lookup(previews_compo, key));
|
||||
if (preview_compositor) {
|
||||
node_draw_extra_info_panel(
|
||||
CTX_data_scene(&C), tree_draw_ctx, snode, node, preview_compositor->ibuf, block);
|
||||
drawn_with_previews = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drawn_with_previews == false) {
|
||||
node_draw_extra_info_panel(CTX_data_scene(&C), tree_draw_ctx, snode, node, nullptr, block);
|
||||
}
|
||||
}
|
||||
if (!preview || !(preview->x && preview->y)) {
|
||||
preview = nullptr;
|
||||
}
|
||||
|
||||
node_draw_extra_info_panel(CTX_data_scene(&C), tree_draw_ctx, snode, node, preview, block);
|
||||
|
||||
/* Header. */
|
||||
{
|
||||
@ -2275,7 +2302,7 @@ static void node_draw_basis(const bContext &C,
|
||||
float iconofs = rct.xmax - 0.35f * U.widget_unit;
|
||||
|
||||
/* Preview. */
|
||||
if (node.typeinfo->flag & NODE_PREVIEW) {
|
||||
if (node_is_previewable(ntree, node)) {
|
||||
iconofs -= iconbutw;
|
||||
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
|
||||
uiBut *but = uiDefIconBut(&block,
|
||||
@ -3482,6 +3509,12 @@ static void draw_nodetree(const bContext &C,
|
||||
else if (ntree.type == NTREE_COMPOSIT) {
|
||||
tree_draw_ctx.used_by_realtime_compositor = realtime_compositor_is_in_use(C);
|
||||
}
|
||||
else if (ntree.type == NTREE_SHADER && U.experimental.use_shader_node_previews &&
|
||||
BKE_scene_uses_shader_previews(CTX_data_scene(&C)) &&
|
||||
U.experimental.use_shader_node_previews)
|
||||
{
|
||||
tree_draw_ctx.nested_group_infos = get_nested_previews(C, *snode);
|
||||
}
|
||||
|
||||
node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
|
||||
node_draw_zones(tree_draw_ctx, region, *snode, ntree);
|
||||
|
@ -504,7 +504,8 @@ bool ED_node_is_geometry(SpaceNode *snode)
|
||||
|
||||
bool ED_node_supports_preview(SpaceNode *snode)
|
||||
{
|
||||
return ED_node_is_compositor(snode);
|
||||
return ED_node_is_compositor(snode) ||
|
||||
(U.experimental.use_shader_node_previews && ED_node_is_shader(snode));
|
||||
}
|
||||
|
||||
void ED_node_shader_default(const bContext *C, ID *id)
|
||||
@ -1123,6 +1124,16 @@ void node_set_hidden_sockets(bNode *node, int set)
|
||||
}
|
||||
}
|
||||
|
||||
bool node_is_previewable(const bNodeTree &ntree, const bNode &node)
|
||||
{
|
||||
if (ntree.type == NTREE_SHADER) {
|
||||
return U.experimental.use_shader_node_previews &&
|
||||
!(node.is_frame() || node.is_group_input() || node.is_group_output() ||
|
||||
node.type == SH_NODE_OUTPUT_MATERIAL);
|
||||
}
|
||||
return node.typeinfo->flag & NODE_PREVIEW;
|
||||
}
|
||||
|
||||
static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket)
|
||||
{
|
||||
const float node_socket_height = node_socket_calculate_height(socket);
|
||||
@ -1567,7 +1578,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
|
||||
for (bNode *node : snode->edittree->all_nodes()) {
|
||||
if (node->flag & SELECT) {
|
||||
|
||||
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
|
||||
if (toggle_flag == NODE_PREVIEW && !node_is_previewable(*snode->edittree, *node)) {
|
||||
continue;
|
||||
}
|
||||
if (toggle_flag == NODE_OPTIONS &&
|
||||
@ -1587,7 +1598,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
|
||||
for (bNode *node : snode->edittree->all_nodes()) {
|
||||
if (node->flag & SELECT) {
|
||||
|
||||
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
|
||||
if (toggle_flag == NODE_PREVIEW && !node_is_previewable(*snode->edittree, *node)) {
|
||||
continue;
|
||||
}
|
||||
if (toggle_flag == NODE_OPTIONS &&
|
||||
@ -1646,8 +1657,6 @@ static int node_preview_toggle_exec(bContext *C, wmOperator * /*op*/)
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
node_flag_toggle_exec(snode, NODE_PREVIEW);
|
||||
|
||||
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
|
||||
@ -1655,6 +1664,17 @@ static int node_preview_toggle_exec(bContext *C, wmOperator * /*op*/)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool node_previewable(bContext *C)
|
||||
{
|
||||
if (ED_operator_node_active(C)) {
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
if (ED_node_supports_preview(snode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NODE_OT_preview_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
@ -1664,7 +1684,7 @@ void NODE_OT_preview_toggle(wmOperatorType *ot)
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = node_preview_toggle_exec;
|
||||
ot->poll = composite_node_active;
|
||||
ot->poll = node_previewable;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compute_context.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_vector.hh"
|
||||
@ -39,6 +40,7 @@ struct AssetItemTree;
|
||||
}
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
struct NestedTreePreviews;
|
||||
|
||||
/** Temporary data used in node link drag modal operator. */
|
||||
struct bNodeLinkDrag {
|
||||
@ -106,6 +108,13 @@ struct SpaceNode_Runtime {
|
||||
/** Temporary data for node insert offset (in UI called Auto-offset). */
|
||||
NodeInsertOfsData *iofsd;
|
||||
|
||||
/**
|
||||
* Use this to store data for the displayed node tree. It has an entry for every distinct
|
||||
* nested nodegroup.
|
||||
*/
|
||||
Map<ComputeContextHash, std::unique_ptr<space_node::NestedTreePreviews>>
|
||||
tree_previews_per_context;
|
||||
|
||||
/**
|
||||
* Temporary data for node add menu in order to provide longer-term storage for context pointers.
|
||||
* Recreated every time the root menu is opened. In the future this will be replaced with an "all
|
||||
@ -330,6 +339,7 @@ bool composite_node_editable(bContext *C);
|
||||
|
||||
bool node_has_hidden_sockets(bNode *node);
|
||||
void node_set_hidden_sockets(bNode *node, int set);
|
||||
bool node_is_previewable(const bNodeTree &ntree, const bNode &node);
|
||||
int node_render_changed_exec(bContext *, wmOperator *);
|
||||
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
|
||||
const float2 &cursor,
|
||||
|
769
source/blender/editors/space_node/node_shader_preview.cc
Normal file
769
source/blender/editors/space_node/node_shader_preview.cc
Normal file
@ -0,0 +1,769 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edrend
|
||||
*
|
||||
* This file implements shader node previews which rely on a structure owned by each SpaceNode.
|
||||
* We take advantage of the RenderResult available as ImBuf images to store a Render for every
|
||||
* viewed nested node tree present in a SpaceNode. The computation is initiated at the moment of
|
||||
* drawing nodes overlays. One render is started for the current nodetree, having a ViewLayer
|
||||
* associated with each previewed node.
|
||||
*
|
||||
* We separate the previewed nodes in two categories: the shader ones and the non-shader ones.
|
||||
* - for non-shader nodes, we use AOVs(Arbitrary Output Variable) which highly speed up the
|
||||
* rendering process by rendering every non-shader node at the same time. They are rendered in the
|
||||
* first ViewLayer.
|
||||
* - for shader nodes, we render them each in a different ViewLayer, by routing the node to the
|
||||
* output of the material in the preview scene.
|
||||
*
|
||||
* At the moment of drawing, we take the Render of the viewed node tree and extract the ImBuf of
|
||||
* the wanted viewlayer/pass for each previewed node.
|
||||
*/
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "BKE_colortools.h"
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
#include "ED_node_preview.hh"
|
||||
#include "ED_render.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "node_intern.hh"
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Structs
|
||||
* \{ */
|
||||
using NodeSocketPair = std::pair<bNode *, bNodeSocket *>;
|
||||
|
||||
struct ShaderNodesPreviewJob {
|
||||
NestedTreePreviews *tree_previews;
|
||||
Scene *scene;
|
||||
/* Pointer to the job's stop variable which is used to know when the job is asked for finishing.
|
||||
* The idea is that the renderer will read this value frequently and abort the render if it is
|
||||
* true. */
|
||||
bool *stop;
|
||||
/* Pointer to the job's update variable which is set to true to refresh the UI when the renderer
|
||||
* is delivering a fresh result. It allows the job to give some UI refresh tags to the WM. */
|
||||
bool *do_update;
|
||||
|
||||
Material *mat_copy;
|
||||
bNode *mat_output_copy;
|
||||
NodeSocketPair mat_displacement_copy;
|
||||
/* TreePath used to locate the nodetree.
|
||||
* bNodeTreePath elements have some listbase pointers which should not be used. */
|
||||
Vector<bNodeTreePath *> treepath_copy;
|
||||
Vector<bNode *> AOV_nodes;
|
||||
Vector<bNode *> shader_nodes;
|
||||
|
||||
bNode *rendering_node;
|
||||
bool rendering_AOVs;
|
||||
|
||||
Main *bmain;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compute Context functions
|
||||
* \{ */
|
||||
|
||||
static void ensure_nodetree_previews(const bContext &C,
|
||||
NestedTreePreviews &tree_previews,
|
||||
Material &material,
|
||||
ListBase &treepath);
|
||||
|
||||
static std::optional<ComputeContextHash> get_compute_context_hash_for_node_editor(
|
||||
const SpaceNode &snode)
|
||||
{
|
||||
Vector<const bNodeTreePath *> treepath = snode.treepath;
|
||||
if (treepath.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (treepath.size() == 1) {
|
||||
/* Top group. */
|
||||
ComputeContextHash hash;
|
||||
hash.v1 = hash.v2 = 0;
|
||||
return hash;
|
||||
}
|
||||
ComputeContextBuilder compute_context_builder;
|
||||
for (const int i : treepath.index_range().drop_back(1)) {
|
||||
/* The tree path contains the name of the node but not its ID. */
|
||||
const bNode *node = nodeFindNodebyName(treepath[i]->nodetree, treepath[i + 1]->node_name);
|
||||
if (node == nullptr) {
|
||||
/* The current tree path is invalid, probably because some parent group node has been
|
||||
* deleted. */
|
||||
return std::nullopt;
|
||||
}
|
||||
compute_context_builder.push<bke::NodeGroupComputeContext>(*node);
|
||||
}
|
||||
return compute_context_builder.hash();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the `NestedTreePreviews *` for the nodetree shown in the SpaceNode.
|
||||
* This is the first function in charge of the previews by calling `ensure_nodetree_previews`.
|
||||
*/
|
||||
NestedTreePreviews *get_nested_previews(const bContext &C, SpaceNode &snode)
|
||||
{
|
||||
if (snode.id == nullptr || GS(snode.id->name) != ID_MA) {
|
||||
return nullptr;
|
||||
}
|
||||
NestedTreePreviews *tree_previews = nullptr;
|
||||
if (auto hash = get_compute_context_hash_for_node_editor(snode)) {
|
||||
tree_previews = snode.runtime->tree_previews_per_context
|
||||
.lookup_or_add_cb(*hash,
|
||||
[&]() {
|
||||
return std::make_unique<NestedTreePreviews>(
|
||||
U.node_preview_res);
|
||||
})
|
||||
.get();
|
||||
Material *ma = reinterpret_cast<Material *>(snode.id);
|
||||
ensure_nodetree_previews(C, *tree_previews, *ma, snode.treepath);
|
||||
}
|
||||
return tree_previews;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Preview scene
|
||||
* \{ */
|
||||
|
||||
static Material *duplicate_material(const Material &mat)
|
||||
{
|
||||
Material *ma_copy = reinterpret_cast<Material *>(
|
||||
BKE_id_copy_ex(nullptr,
|
||||
&mat.id,
|
||||
nullptr,
|
||||
LIB_ID_CREATE_LOCAL | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA));
|
||||
return ma_copy;
|
||||
}
|
||||
|
||||
static Scene *preview_prepare_scene(const Main *bmain,
|
||||
const Scene *scene_orig,
|
||||
Main *pr_main,
|
||||
Material *mat_copy)
|
||||
{
|
||||
Scene *scene_preview;
|
||||
|
||||
memcpy(pr_main->filepath, BKE_main_blendfile_path(bmain), sizeof(pr_main->filepath));
|
||||
|
||||
if (pr_main == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
scene_preview = static_cast<Scene *>(pr_main->scenes.first);
|
||||
if (scene_preview == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ViewLayer *view_layer = static_cast<ViewLayer *>(scene_preview->view_layers.first);
|
||||
|
||||
/* Only enable the combined render-pass. */
|
||||
view_layer->passflag = SCE_PASS_COMBINED;
|
||||
view_layer->eevee.render_passes = 0;
|
||||
|
||||
/* This flag tells render to not execute depsgraph or F-Curves etc. */
|
||||
scene_preview->r.scemode |= R_BUTS_PREVIEW;
|
||||
scene_preview->r.mode |= R_PERSISTENT_DATA;
|
||||
STRNCPY(scene_preview->r.engine, scene_orig->r.engine);
|
||||
|
||||
scene_preview->r.color_mgt_flag = scene_orig->r.color_mgt_flag;
|
||||
BKE_color_managed_display_settings_copy(&scene_preview->display_settings,
|
||||
&scene_orig->display_settings);
|
||||
|
||||
BKE_color_managed_view_settings_free(&scene_preview->view_settings);
|
||||
BKE_color_managed_view_settings_copy(&scene_preview->view_settings, &scene_orig->view_settings);
|
||||
|
||||
scene_preview->r.alphamode = R_ADDSKY;
|
||||
|
||||
scene_preview->r.cfra = scene_orig->r.cfra;
|
||||
|
||||
/* Setup the world. */
|
||||
scene_preview->world = ED_preview_prepare_world(
|
||||
pr_main, scene_preview, scene_orig->world, ID_MA, PR_BUTS_RENDER);
|
||||
|
||||
BLI_addtail(&pr_main->materials, mat_copy);
|
||||
scene_preview->world->use_nodes = false;
|
||||
scene_preview->world->horr = 0.05f;
|
||||
scene_preview->world->horg = 0.05f;
|
||||
scene_preview->world->horb = 0.05f;
|
||||
|
||||
ED_preview_set_visibility(
|
||||
pr_main, scene_preview, view_layer, ePreviewType(mat_copy->pr_type), PR_BUTS_RENDER);
|
||||
|
||||
BKE_view_layer_synced_ensure(scene_preview, view_layer);
|
||||
LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
|
||||
if (base->object->id.name[2] == 'p') {
|
||||
if (OB_TYPE_SUPPORT_MATERIAL(base->object->type)) {
|
||||
/* Don't use BKE_object_material_assign, it changed mat->id.us, which shows in the UI. */
|
||||
Material ***matar = BKE_object_material_array_p(base->object);
|
||||
int actcol = max_ii(base->object->actcol - 1, 0);
|
||||
|
||||
if (matar && actcol < base->object->totcol) {
|
||||
(*matar)[actcol] = mat_copy;
|
||||
}
|
||||
}
|
||||
else if (base->object->type == OB_LAMP) {
|
||||
base->flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scene_preview;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Preview rendering
|
||||
* \{ */
|
||||
|
||||
/* Return the socket used for previewing the node (should probably follow more precise rules). */
|
||||
static const bNodeSocket *node_find_preview_socket(const bNode &node)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
|
||||
if (socket->is_visible()) {
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool node_use_aov(const bNode &node)
|
||||
{
|
||||
const bNodeSocket *socket = node_find_preview_socket(node);
|
||||
return socket != nullptr && socket->type != SOCK_SHADER;
|
||||
}
|
||||
|
||||
static ImBuf *get_image_from_viewlayer_and_pass(RenderResult &rr,
|
||||
const char *layer_name,
|
||||
const char *pass_name)
|
||||
{
|
||||
RenderLayer *rl;
|
||||
if (layer_name) {
|
||||
rl = RE_GetRenderLayer(&rr, layer_name);
|
||||
}
|
||||
else {
|
||||
rl = static_cast<RenderLayer *>(rr.layers.first);
|
||||
}
|
||||
if (rl == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
RenderPass *rp;
|
||||
if (pass_name) {
|
||||
rp = RE_pass_find_by_name(rl, pass_name, nullptr);
|
||||
}
|
||||
else {
|
||||
rp = static_cast<RenderPass *>(rl->passes.first);
|
||||
}
|
||||
ImBuf *ibuf = rp ? rp->ibuf : nullptr;
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
/* `node_release_preview_ibuf` should be called after this. */
|
||||
ImBuf *node_preview_acquire_ibuf(bNodeTree &ntree,
|
||||
NestedTreePreviews &tree_previews,
|
||||
const bNode &node)
|
||||
{
|
||||
if (tree_previews.previews_render == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RenderResult *rr = RE_AcquireResultRead(tree_previews.previews_render);
|
||||
ImBuf *&image_cached = tree_previews.previews_map.lookup_or_add(node.identifier, nullptr);
|
||||
if (rr == nullptr) {
|
||||
return image_cached;
|
||||
}
|
||||
if (image_cached == nullptr) {
|
||||
if (tree_previews.rendering == false) {
|
||||
ntree.runtime->previews_refresh_state++;
|
||||
}
|
||||
else {
|
||||
/* When the render process is started, the user must see that the preview area is open. */
|
||||
ImBuf *image_latest = nullptr;
|
||||
if (node_use_aov(node)) {
|
||||
image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, node.name);
|
||||
}
|
||||
else {
|
||||
image_latest = get_image_from_viewlayer_and_pass(*rr, node.name, nullptr);
|
||||
}
|
||||
if (image_latest) {
|
||||
IMB_refImBuf(image_latest);
|
||||
image_cached = image_latest;
|
||||
}
|
||||
}
|
||||
}
|
||||
return image_cached;
|
||||
}
|
||||
|
||||
void node_release_preview_ibuf(NestedTreePreviews &tree_previews)
|
||||
{
|
||||
if (tree_previews.previews_render == nullptr) {
|
||||
return;
|
||||
}
|
||||
RE_ReleaseResult(tree_previews.previews_render);
|
||||
}
|
||||
|
||||
/* Get a link to the node outside the nested nodegroups by creating a new output socket for each
|
||||
* nested nodegroup. To do so we cover all nested nodetrees starting from the farthest, and
|
||||
* update the `nested_node_iter` pointer to the current nodegroup instance used for linking. We
|
||||
* stop before getting to the main nodetree because the output type is different. */
|
||||
static void connect_nested_node_to_node(const Span<bNodeTreePath *> treepath,
|
||||
bNode &nested_node,
|
||||
bNodeSocket &nested_socket,
|
||||
bNode &final_node,
|
||||
bNodeSocket &final_socket)
|
||||
{
|
||||
bNode *nested_node_iter = &nested_node;
|
||||
bNodeSocket *nested_socket_iter = &nested_socket;
|
||||
for (int i = treepath.size() - 1; i > 0; --i) {
|
||||
bNodeTreePath *path = treepath[i];
|
||||
bNodeTreePath *path_prev = treepath[i - 1];
|
||||
bNodeTree *nested_nt = path->nodetree;
|
||||
bNode *output_node = nullptr;
|
||||
for (bNode *iter_node : nested_nt->all_nodes()) {
|
||||
if (iter_node->is_group_output() && iter_node->flag & NODE_DO_OUTPUT) {
|
||||
output_node = iter_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output_node == nullptr) {
|
||||
output_node = nodeAddStaticNode(nullptr, nested_nt, NODE_GROUP_OUTPUT);
|
||||
output_node->flag |= NODE_DO_OUTPUT;
|
||||
}
|
||||
|
||||
ntreeAddSocketInterface(nested_nt, SOCK_OUT, nested_socket_iter->idname, nested_node.name);
|
||||
BKE_ntree_update_main_tree(G.pr_main, nested_nt, nullptr);
|
||||
bNodeSocket *out_socket = bke::node_find_enabled_input_socket(*output_node, nested_node.name);
|
||||
|
||||
nodeAddLink(nested_nt, nested_node_iter, nested_socket_iter, output_node, out_socket);
|
||||
BKE_ntree_update_main_tree(G.pr_main, nested_nt, nullptr);
|
||||
|
||||
/* Change the `nested_node` pointer to the nested nodegroup instance node. The tree path
|
||||
* contains the name of the instance node but not its ID. */
|
||||
nested_node_iter = nodeFindNodebyName(path_prev->nodetree, path->node_name);
|
||||
|
||||
/* Update the sockets of the node because we added a new interface. */
|
||||
BKE_ntree_update_tag_node_property(path_prev->nodetree, nested_node_iter);
|
||||
BKE_ntree_update_main_tree(G.pr_main, path_prev->nodetree, nullptr);
|
||||
|
||||
/* Now use the newly created socket of the nodegroup as previewing socket of the nodegroup
|
||||
* instance node. */
|
||||
nested_socket_iter = bke::node_find_enabled_output_socket(*nested_node_iter, nested_node.name);
|
||||
}
|
||||
|
||||
nodeAddLink(treepath.first()->nodetree,
|
||||
nested_node_iter,
|
||||
nested_socket_iter,
|
||||
&final_node,
|
||||
&final_socket);
|
||||
}
|
||||
|
||||
/* Connect the node to the output of the first nodetree from `treepath`. Last element of `treepath`
|
||||
* should be the path to the node's nodetree */
|
||||
static void connect_node_to_surface_output(const Span<bNodeTreePath *> treepath,
|
||||
bNode &node,
|
||||
bNode &output_node)
|
||||
{
|
||||
bNodeSocket *out_surface_socket = nullptr;
|
||||
bNodeTree *main_nt = treepath.first()->nodetree;
|
||||
bNodeSocket *node_preview_socket = const_cast<bNodeSocket *>(node_find_preview_socket(node));
|
||||
if (node_preview_socket == nullptr) {
|
||||
return;
|
||||
}
|
||||
/* Ensure output is usable. */
|
||||
out_surface_socket = nodeFindSocket(&output_node, SOCK_IN, "Surface");
|
||||
if (out_surface_socket->link) {
|
||||
/* Make sure no node is already wired to the output before wiring. */
|
||||
nodeRemLink(main_nt, out_surface_socket->link);
|
||||
}
|
||||
|
||||
connect_nested_node_to_node(
|
||||
treepath, node, *node_preview_socket, output_node, *out_surface_socket);
|
||||
BKE_ntree_update_main_tree(G.pr_main, main_nt, nullptr);
|
||||
}
|
||||
|
||||
/* Connect the nodes to some aov nodes located in the first nodetree from `treepath`. Last element
|
||||
* of `treepath` should be the path to the nodes nodetree. */
|
||||
static void connect_nodes_to_aovs(const Span<bNodeTreePath *> treepath, const Span<bNode *> &nodes)
|
||||
{
|
||||
if (nodes.size() == 0) {
|
||||
return;
|
||||
}
|
||||
bNodeTree *main_nt = treepath.first()->nodetree;
|
||||
for (bNode *node : nodes) {
|
||||
bNodeSocket *node_preview_socket = const_cast<bNodeSocket *>(node_find_preview_socket(*node));
|
||||
|
||||
bNode *aov_node = nodeAddStaticNode(nullptr, main_nt, SH_NODE_OUTPUT_AOV);
|
||||
strcpy(reinterpret_cast<NodeShaderOutputAOV *>(aov_node->storage)->name, node->name);
|
||||
if (node_preview_socket == nullptr) {
|
||||
continue;
|
||||
}
|
||||
bNodeSocket *aov_socket = nodeFindSocket(aov_node, SOCK_IN, "Color");
|
||||
|
||||
connect_nested_node_to_node(treepath, *node, *node_preview_socket, *aov_node, *aov_socket);
|
||||
}
|
||||
BKE_ntree_update_main_tree(G.pr_main, main_nt, nullptr);
|
||||
}
|
||||
|
||||
/* Called by renderer, checks job stops. */
|
||||
static bool nodetree_previews_break(void *spv)
|
||||
{
|
||||
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(spv);
|
||||
|
||||
return *(job_data->stop);
|
||||
}
|
||||
|
||||
static bool prepare_viewlayer_update(void *pvl_data, ViewLayer *vl, Depsgraph *depsgraph)
|
||||
{
|
||||
bNode *node = nullptr;
|
||||
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(pvl_data);
|
||||
for (bNode *node_iter : job_data->shader_nodes) {
|
||||
if (STREQ(vl->name, node_iter->name)) {
|
||||
node = node_iter;
|
||||
job_data->rendering_node = node_iter;
|
||||
job_data->rendering_AOVs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node == nullptr) {
|
||||
job_data->rendering_node = nullptr;
|
||||
job_data->rendering_AOVs = true;
|
||||
/* The AOV layer is the default `ViewLayer` of the scene(which should be the first one). */
|
||||
return job_data->AOV_nodes.size() > 0 && !vl->prev;
|
||||
}
|
||||
|
||||
bNodeSocket *displacement_socket = nodeFindSocket(
|
||||
job_data->mat_output_copy, SOCK_IN, "Displacement");
|
||||
if (job_data->mat_displacement_copy.first != nullptr && displacement_socket->link == nullptr) {
|
||||
nodeAddLink(job_data->treepath_copy.first()->nodetree,
|
||||
job_data->mat_displacement_copy.first,
|
||||
job_data->mat_displacement_copy.second,
|
||||
job_data->mat_output_copy,
|
||||
displacement_socket);
|
||||
}
|
||||
connect_node_to_surface_output(job_data->treepath_copy, *node, *job_data->mat_output_copy);
|
||||
|
||||
if (depsgraph != nullptr) {
|
||||
/* Used to refresh the dependency graph so that the material can be updated. */
|
||||
for (bNodeTreePath *path_iter : job_data->treepath_copy) {
|
||||
DEG_graph_id_tag_update(
|
||||
G.pr_main, depsgraph, &path_iter->nodetree->id, ID_RECALC_NTREE_OUTPUT);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Called by renderer, refresh the UI. */
|
||||
static void all_nodes_preview_update(void *npv, RenderResult *rr, struct rcti * /*rect*/)
|
||||
{
|
||||
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(npv);
|
||||
*job_data->do_update = true;
|
||||
if (bNode *node = job_data->rendering_node) {
|
||||
ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(node->identifier,
|
||||
nullptr);
|
||||
ImBuf *image_latest = get_image_from_viewlayer_and_pass(*rr, node->name, nullptr);
|
||||
if (image_latest == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (image_cached != image_latest) {
|
||||
if (image_cached != nullptr) {
|
||||
IMB_freeImBuf(image_cached);
|
||||
}
|
||||
IMB_refImBuf(image_latest);
|
||||
image_cached = image_latest;
|
||||
}
|
||||
}
|
||||
if (job_data->rendering_AOVs) {
|
||||
for (bNode *node : job_data->AOV_nodes) {
|
||||
ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(node->identifier,
|
||||
nullptr);
|
||||
ImBuf *image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, node->name);
|
||||
if (image_latest == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (image_cached != image_latest) {
|
||||
if (image_cached != nullptr) {
|
||||
IMB_freeImBuf(image_cached);
|
||||
}
|
||||
IMB_refImBuf(image_latest);
|
||||
image_cached = image_latest;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void preview_render(ShaderNodesPreviewJob &job_data)
|
||||
{
|
||||
/* Get the stuff from the builtin preview dbase. */
|
||||
Scene *scene = preview_prepare_scene(
|
||||
job_data.bmain, job_data.scene, G.pr_main, job_data.mat_copy);
|
||||
if (scene == nullptr) {
|
||||
return;
|
||||
}
|
||||
Span<bNodeTreePath *> treepath = job_data.treepath_copy;
|
||||
|
||||
/* Disconnect all input sockets of the material output node, but keep track of the displacment
|
||||
* node. */
|
||||
bNodeSocket *disp_socket = nodeFindSocket(job_data.mat_output_copy, SOCK_IN, "Displacement");
|
||||
if (disp_socket->link != nullptr) {
|
||||
job_data.mat_displacement_copy = std::make_pair(disp_socket->link->fromnode,
|
||||
disp_socket->link->fromsock);
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket_iter, &job_data.mat_output_copy->inputs) {
|
||||
if (socket_iter->link != nullptr) {
|
||||
nodeRemLink(treepath.first()->nodetree, socket_iter->link);
|
||||
}
|
||||
}
|
||||
|
||||
/* AOV nodes are rendered in the first RenderLayer so we route them now. */
|
||||
connect_nodes_to_aovs(treepath, job_data.AOV_nodes);
|
||||
|
||||
/* Create the AOV passes for the viewlayer. */
|
||||
ViewLayer *AOV_layer = static_cast<ViewLayer *>(scene->view_layers.first);
|
||||
for (bNode *node : job_data.shader_nodes) {
|
||||
ViewLayer *vl = BKE_view_layer_add(scene, node->name, AOV_layer, VIEWLAYER_ADD_COPY);
|
||||
strcpy(vl->name, node->name);
|
||||
}
|
||||
for (bNode *node : job_data.AOV_nodes) {
|
||||
ViewLayerAOV *aov = BKE_view_layer_add_aov(AOV_layer);
|
||||
strcpy(aov->name, node->name);
|
||||
}
|
||||
scene->r.xsch = job_data.tree_previews->preview_size;
|
||||
scene->r.ysch = job_data.tree_previews->preview_size;
|
||||
scene->r.size = 100;
|
||||
|
||||
if (job_data.tree_previews->previews_render == nullptr) {
|
||||
char name[32];
|
||||
SNPRINTF(name, "Preview %p", &job_data.tree_previews);
|
||||
job_data.tree_previews->previews_render = RE_NewRender(name);
|
||||
}
|
||||
Render *re = job_data.tree_previews->previews_render;
|
||||
|
||||
/* `sce->r` gets copied in RE_InitState. */
|
||||
scene->r.scemode &= ~(R_MATNODE_PREVIEW | R_TEXNODE_PREVIEW);
|
||||
scene->r.scemode &= ~R_NO_IMAGE_LOAD;
|
||||
|
||||
scene->display.render_aa = SCE_DISPLAY_AA_SAMPLES_8;
|
||||
|
||||
RE_display_update_cb(re, &job_data, all_nodes_preview_update);
|
||||
RE_test_break_cb(re, &job_data, nodetree_previews_break);
|
||||
RE_prepare_viewlayer_cb(re, &job_data, prepare_viewlayer_update);
|
||||
|
||||
/* Lens adjust. */
|
||||
float oldlens = reinterpret_cast<Camera *>(scene->camera->data)->lens;
|
||||
|
||||
RE_ClearResult(re);
|
||||
RE_PreviewRender(re, G.pr_main, scene);
|
||||
|
||||
reinterpret_cast<Camera *>(scene->camera->data)->lens = oldlens;
|
||||
|
||||
/* Free the aov layers and the layers generated for each node. */
|
||||
BLI_freelistN(&AOV_layer->aovs);
|
||||
ViewLayer *vl = AOV_layer->next;
|
||||
while (vl) {
|
||||
ViewLayer *vl_rem = vl;
|
||||
vl = vl->next;
|
||||
BLI_remlink(&scene->view_layers, vl_rem);
|
||||
BKE_view_layer_free(vl_rem);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Preview job management
|
||||
* \{ */
|
||||
|
||||
static void update_needed_flag(const bNodeTree &nt, NestedTreePreviews &tree_previews)
|
||||
{
|
||||
if (tree_previews.rendering) {
|
||||
if (nt.runtime->previews_refresh_state != tree_previews.rendering_previews_refresh_state) {
|
||||
tree_previews.restart_needed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (nt.runtime->previews_refresh_state != tree_previews.cached_previews_refresh_state) {
|
||||
tree_previews.restart_needed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (tree_previews.preview_size != U.node_preview_res) {
|
||||
tree_previews.restart_needed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void shader_preview_startjob(void *customdata,
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float * /*progress*/)
|
||||
{
|
||||
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(customdata);
|
||||
|
||||
job_data->stop = stop;
|
||||
job_data->do_update = do_update;
|
||||
*do_update = true;
|
||||
bool size_changed = job_data->tree_previews->preview_size != U.node_preview_res;
|
||||
if (size_changed) {
|
||||
job_data->tree_previews->preview_size = U.node_preview_res;
|
||||
}
|
||||
|
||||
/* Find the shader output node. */
|
||||
for (bNode *node_iter : job_data->mat_copy->nodetree->all_nodes()) {
|
||||
if (node_iter->type == SH_NODE_OUTPUT_MATERIAL && node_iter->flag & NODE_DO_OUTPUT) {
|
||||
job_data->mat_output_copy = node_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (job_data->mat_output_copy == nullptr) {
|
||||
job_data->mat_output_copy = nodeAddStaticNode(
|
||||
nullptr, job_data->mat_copy->nodetree, SH_NODE_OUTPUT_MATERIAL);
|
||||
}
|
||||
|
||||
bNodeTree *active_nodetree = job_data->treepath_copy.last()->nodetree;
|
||||
for (bNode *node : active_nodetree->all_nodes()) {
|
||||
if (!(node->flag & NODE_PREVIEW)) {
|
||||
/* Clear the cached preview for this node to be sure that the preview is rerendered if
|
||||
* needed. */
|
||||
if (ImBuf **ibuf = job_data->tree_previews->previews_map.lookup_ptr(node->identifier)) {
|
||||
IMB_freeImBuf(*ibuf);
|
||||
*ibuf = nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (node_use_aov(*node)) {
|
||||
job_data->AOV_nodes.append(node);
|
||||
}
|
||||
else {
|
||||
job_data->shader_nodes.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (job_data->tree_previews->preview_size > 0) {
|
||||
preview_render(*job_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void shader_preview_free(void *customdata)
|
||||
{
|
||||
ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(customdata);
|
||||
for (bNodeTreePath *path : job_data->treepath_copy) {
|
||||
MEM_freeN(path);
|
||||
}
|
||||
job_data->treepath_copy.clear();
|
||||
job_data->tree_previews->rendering = false;
|
||||
job_data->tree_previews->cached_previews_refresh_state =
|
||||
job_data->tree_previews->rendering_previews_refresh_state;
|
||||
if (job_data->mat_copy != nullptr) {
|
||||
BLI_remlink(&G.pr_main->materials, job_data->mat_copy);
|
||||
BKE_id_free(G.pr_main, &job_data->mat_copy->id);
|
||||
job_data->mat_copy = nullptr;
|
||||
}
|
||||
MEM_delete(job_data);
|
||||
}
|
||||
|
||||
static void ensure_nodetree_previews(const bContext &C,
|
||||
NestedTreePreviews &tree_previews,
|
||||
Material &material,
|
||||
ListBase &treepath)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(&C);
|
||||
if (!ED_check_engine_supports_preview(scene)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bNodeTree *displayed_nodetree = static_cast<bNodeTreePath *>(treepath.last)->nodetree;
|
||||
update_needed_flag(*displayed_nodetree, tree_previews);
|
||||
if (!(tree_previews.restart_needed)) {
|
||||
return;
|
||||
}
|
||||
if (tree_previews.rendering) {
|
||||
WM_jobs_stop(CTX_wm_manager(&C),
|
||||
CTX_wm_space_node(&C),
|
||||
reinterpret_cast<void *>(shader_preview_startjob));
|
||||
return;
|
||||
}
|
||||
tree_previews.rendering = true;
|
||||
tree_previews.restart_needed = false;
|
||||
tree_previews.rendering_previews_refresh_state =
|
||||
displayed_nodetree->runtime->previews_refresh_state;
|
||||
|
||||
ED_preview_ensure_dbase(false);
|
||||
|
||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(&C),
|
||||
CTX_wm_window(&C),
|
||||
CTX_wm_space_node(&C),
|
||||
"Shader Previews",
|
||||
WM_JOB_EXCL_RENDER,
|
||||
WM_JOB_TYPE_RENDER_PREVIEW);
|
||||
ShaderNodesPreviewJob *job_data = MEM_new<ShaderNodesPreviewJob>(__func__);
|
||||
|
||||
job_data->scene = scene;
|
||||
job_data->tree_previews = &tree_previews;
|
||||
job_data->bmain = CTX_data_main(&C);
|
||||
job_data->mat_copy = duplicate_material(material);
|
||||
job_data->rendering_node = nullptr;
|
||||
job_data->rendering_AOVs = false;
|
||||
|
||||
/* Update the treepath copied to fit the structure of the nodetree copied. */
|
||||
bNodeTreePath *root_path = MEM_cnew<bNodeTreePath>(__func__);
|
||||
root_path->nodetree = job_data->mat_copy->nodetree;
|
||||
job_data->treepath_copy.append(root_path);
|
||||
for (bNodeTreePath *original_path = static_cast<bNodeTreePath *>(treepath.first)->next;
|
||||
original_path;
|
||||
original_path = original_path->next)
|
||||
{
|
||||
bNodeTreePath *new_path = MEM_cnew<bNodeTreePath>(__func__);
|
||||
memcpy(new_path, original_path, sizeof(bNodeTreePath));
|
||||
bNode *parent = nodeFindNodebyName(job_data->treepath_copy.last()->nodetree,
|
||||
original_path->node_name);
|
||||
new_path->nodetree = reinterpret_cast<bNodeTree *>(parent->id);
|
||||
job_data->treepath_copy.append(new_path);
|
||||
}
|
||||
|
||||
WM_jobs_customdata_set(wm_job, job_data, shader_preview_free);
|
||||
WM_jobs_timer(wm_job, 0.2, NC_NODE, NC_NODE);
|
||||
WM_jobs_callbacks(wm_job, shader_preview_startjob, nullptr, nullptr, nullptr);
|
||||
|
||||
WM_jobs_start(CTX_wm_manager(&C), wm_job);
|
||||
}
|
||||
|
||||
void stop_preview_job(wmWindowManager &wm)
|
||||
{
|
||||
WM_jobs_stop(&wm, nullptr, reinterpret_cast<void *>(shader_preview_startjob));
|
||||
}
|
||||
|
||||
void free_previews(wmWindowManager &wm, SpaceNode &snode)
|
||||
{
|
||||
/* This should not be called from the drawing pass, because it will result in a deadlock. */
|
||||
WM_jobs_kill(&wm, &snode, shader_preview_startjob);
|
||||
snode.runtime->tree_previews_per_context.clear_and_shrink();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ed::space_node
|
@ -24,6 +24,7 @@
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "ED_node.hh"
|
||||
#include "ED_node_preview.hh"
|
||||
#include "ED_render.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_space_api.hh"
|
||||
@ -330,6 +331,15 @@ static void node_init(wmWindowManager * /*wm*/, ScrArea *area)
|
||||
}
|
||||
}
|
||||
|
||||
static void node_exit(wmWindowManager *wm, ScrArea *area)
|
||||
{
|
||||
SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
|
||||
|
||||
if (snode->runtime) {
|
||||
free_previews(*wm, *snode);
|
||||
}
|
||||
}
|
||||
|
||||
static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
|
||||
{
|
||||
if (ELEM(nullptr, ntree, id)) {
|
||||
@ -1131,6 +1141,7 @@ void ED_spacetype_node()
|
||||
st->create = node_create;
|
||||
st->free = node_free;
|
||||
st->init = node_init;
|
||||
st->exit = node_exit;
|
||||
st->duplicate = node_duplicate;
|
||||
st->operatortypes = node_operatortypes;
|
||||
st->keymap = node_keymap;
|
||||
|
@ -66,6 +66,7 @@ set(SRC
|
||||
../include/ED_mesh.hh
|
||||
../include/ED_node_c.hh
|
||||
../include/ED_node.hh
|
||||
../include/ED_node_preview.hh
|
||||
../include/ED_numinput.hh
|
||||
../include/ED_object.hh
|
||||
../include/ED_outliner.hh
|
||||
|
@ -571,6 +571,7 @@ set(GLSL_SRC_TEST
|
||||
tests/shaders/gpu_compute_ssbo_test.glsl
|
||||
tests/shaders/gpu_compute_vbo_test.glsl
|
||||
tests/shaders/gpu_compute_dummy_test.glsl
|
||||
tests/shaders/gpu_framebuffer_layer_viewport_test.glsl
|
||||
tests/shaders/gpu_push_constants_test.glsl
|
||||
)
|
||||
|
||||
|
@ -35,6 +35,9 @@ typedef enum eGPUFrameBufferBits {
|
||||
|
||||
ENUM_OPERATORS(eGPUFrameBufferBits, GPU_STENCIL_BIT)
|
||||
|
||||
/* Guaranteed by the spec and is never greater than 16 on any hardware or implementation. */
|
||||
#define GPU_MAX_VIEWPORTS 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -340,10 +343,23 @@ void GPU_framebuffer_default_size(GPUFrameBuffer *framebuffer, int width, int he
|
||||
* or when binding the frame-buffer after modifying its attachments.
|
||||
*
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
* \note Setting a singular viewport will only change the state of the first viewport.
|
||||
* \note Must be called after first bind.
|
||||
*/
|
||||
void GPU_framebuffer_viewport_set(
|
||||
GPUFrameBuffer *framebuffer, int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Similar to `GPU_framebuffer_viewport_set()` but specify the bounds of all 16 viewports.
|
||||
* By default geometry renders only to the first viewport. That can be changed by setting
|
||||
* `gpu_ViewportIndex` in the vertex.
|
||||
*
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
* \note Must be called after first bind.
|
||||
*/
|
||||
void GPU_framebuffer_multi_viewports_set(GPUFrameBuffer *gpu_fb,
|
||||
const int viewport_rects[GPU_MAX_VIEWPORTS][4]);
|
||||
|
||||
/**
|
||||
* Return the viewport offset and size in a int quadruple: (x, y, width, height).
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
|
@ -391,6 +391,12 @@ void GPU_framebuffer_viewport_set(GPUFrameBuffer *gpu_fb, int x, int y, int widt
|
||||
unwrap(gpu_fb)->viewport_set(viewport_rect);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_multi_viewports_set(GPUFrameBuffer *gpu_fb,
|
||||
const int viewport_rects[GPU_MAX_VIEWPORTS][4])
|
||||
{
|
||||
unwrap(gpu_fb)->viewport_multi_set(viewport_rects);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_viewport_get(GPUFrameBuffer *gpu_fb, int r_viewport[4])
|
||||
{
|
||||
unwrap(gpu_fb)->viewport_get(r_viewport);
|
||||
|
@ -81,8 +81,9 @@ class FrameBuffer {
|
||||
/** Debug name. */
|
||||
char name_[DEBUG_NAME_LEN];
|
||||
/** Frame-buffer state. */
|
||||
int viewport_[4] = {0};
|
||||
int viewport_[GPU_MAX_VIEWPORTS][4] = {{0}};
|
||||
int scissor_[4] = {0};
|
||||
bool multi_viewport_ = false;
|
||||
bool scissor_test_ = false;
|
||||
bool dirty_state_ = true;
|
||||
|
||||
@ -157,10 +158,22 @@ class FrameBuffer {
|
||||
|
||||
inline void viewport_set(const int viewport[4])
|
||||
{
|
||||
if (!equals_v4v4_int(viewport_, viewport)) {
|
||||
copy_v4_v4_int(viewport_, viewport);
|
||||
if (!equals_v4v4_int(viewport_[0], viewport)) {
|
||||
copy_v4_v4_int(viewport_[0], viewport);
|
||||
dirty_state_ = true;
|
||||
}
|
||||
multi_viewport_ = false;
|
||||
}
|
||||
|
||||
inline void viewport_multi_set(const int viewports[GPU_MAX_VIEWPORTS][4])
|
||||
{
|
||||
for (size_t i = 0; i < GPU_MAX_VIEWPORTS; i++) {
|
||||
if (!equals_v4v4_int(viewport_[i], viewports[i])) {
|
||||
copy_v4_v4_int(viewport_[i], viewports[i]);
|
||||
dirty_state_ = true;
|
||||
}
|
||||
}
|
||||
multi_viewport_ = true;
|
||||
}
|
||||
|
||||
inline void scissor_set(const int scissor[4])
|
||||
@ -178,7 +191,7 @@ class FrameBuffer {
|
||||
|
||||
inline void viewport_get(int r_viewport[4]) const
|
||||
{
|
||||
copy_v4_v4_int(r_viewport, viewport_);
|
||||
copy_v4_v4_int(r_viewport, viewport_[0]);
|
||||
}
|
||||
|
||||
inline void scissor_get(int r_scissor[4]) const
|
||||
|
@ -178,6 +178,11 @@ enum class BuiltinBits {
|
||||
VERTEX_ID = (1 << 14),
|
||||
WORK_GROUP_ID = (1 << 15),
|
||||
WORK_GROUP_SIZE = (1 << 16),
|
||||
/**
|
||||
* Allow setting the target viewport when using multi viewport feature.
|
||||
* \note Emulated through geometry shader on older hardware.
|
||||
*/
|
||||
VIEWPORT_INDEX = (1 << 17),
|
||||
|
||||
/* Not a builtin but a flag we use to tag shaders that use the debug features. */
|
||||
USE_DEBUG_DRAW = (1 << 29),
|
||||
|
@ -780,8 +780,8 @@ void MTLFrameBuffer::apply_state()
|
||||
|
||||
/* Ensure viewport has been set. NOTE: This should no longer happen, but kept for safety to
|
||||
* track bugs. If viewport size is zero, use framebuffer size. */
|
||||
int viewport_w = viewport_[2];
|
||||
int viewport_h = viewport_[3];
|
||||
int viewport_w = viewport_[0][2];
|
||||
int viewport_h = viewport_[0][3];
|
||||
if (viewport_w == 0 || viewport_h == 0) {
|
||||
MTL_LOG_WARNING("Viewport had width and height of (0,0) -- Updating -- DEBUG Safety check");
|
||||
viewport_w = default_width_;
|
||||
@ -789,7 +789,7 @@ void MTLFrameBuffer::apply_state()
|
||||
}
|
||||
|
||||
/* Update Context State. */
|
||||
mtl_ctx->set_viewport(viewport_[0], viewport_[1], viewport_w, viewport_h);
|
||||
mtl_ctx->set_viewport(viewport_[0][0], viewport_[0][1], viewport_w, viewport_h);
|
||||
mtl_ctx->set_scissor(scissor_[0], scissor_[1], scissor_[2], scissor_[3]);
|
||||
mtl_ctx->set_scissor_enabled(scissor_test_);
|
||||
|
||||
|
@ -592,7 +592,8 @@ void GLBackend::capabilities_init()
|
||||
GLContext::explicit_location_support = epoxy_gl_version() >= 43;
|
||||
GLContext::geometry_shader_invocations = epoxy_has_gl_extension("GL_ARB_gpu_shader5");
|
||||
GLContext::fixed_restart_index_support = epoxy_has_gl_extension("GL_ARB_ES3_compatibility");
|
||||
GLContext::layered_rendering_support = epoxy_has_gl_extension("GL_AMD_vertex_shader_layer");
|
||||
GLContext::layered_rendering_support = epoxy_has_gl_extension(
|
||||
"GL_ARB_shader_viewport_layer_array");
|
||||
GLContext::native_barycentric_support = epoxy_has_gl_extension(
|
||||
"GL_AMD_shader_explicit_vertex_parameter");
|
||||
GLContext::multi_bind_support = GLContext::multi_bind_image_support = epoxy_has_gl_extension(
|
||||
|
@ -43,10 +43,10 @@ GLFrameBuffer::GLFrameBuffer(
|
||||
height_ = h;
|
||||
srgb_ = false;
|
||||
|
||||
viewport_[0] = scissor_[0] = 0;
|
||||
viewport_[1] = scissor_[1] = 0;
|
||||
viewport_[2] = scissor_[2] = w;
|
||||
viewport_[3] = scissor_[3] = h;
|
||||
viewport_[0][0] = scissor_[0] = 0;
|
||||
viewport_[0][1] = scissor_[1] = 0;
|
||||
viewport_[0][2] = scissor_[2] = w;
|
||||
viewport_[0][3] = scissor_[3] = h;
|
||||
|
||||
if (fbo_id_) {
|
||||
debug::object_label(GL_FRAMEBUFFER, fbo_id_, name_);
|
||||
@ -230,7 +230,20 @@ void GLFrameBuffer::apply_state()
|
||||
return;
|
||||
}
|
||||
|
||||
glViewport(UNPACK4(viewport_));
|
||||
if (multi_viewport_ == false) {
|
||||
glViewport(UNPACK4(viewport_[0]));
|
||||
}
|
||||
else {
|
||||
/* Great API you have there! You have to convert to float values for setting int viewport
|
||||
* values. **Audible Facepalm** */
|
||||
float viewports_f[GPU_MAX_VIEWPORTS][4];
|
||||
for (int i = 0; i < GPU_MAX_VIEWPORTS; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
viewports_f[i][j] = viewport_[i][j];
|
||||
}
|
||||
}
|
||||
glViewportArrayv(0, GPU_MAX_VIEWPORTS, viewports_f[0]);
|
||||
}
|
||||
glScissor(UNPACK4(scissor_));
|
||||
|
||||
if (scissor_test_) {
|
||||
|
@ -551,6 +551,10 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con
|
||||
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) {
|
||||
ss << "out int gpu_Layer;\n";
|
||||
}
|
||||
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX))
|
||||
{
|
||||
ss << "out int gpu_ViewportIndex;\n";
|
||||
}
|
||||
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
|
||||
if (!GLContext::native_barycentric_support) {
|
||||
/* Disabled or unsupported. */
|
||||
@ -584,6 +588,13 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
|
||||
for (const StageInterfaceInfo *iface : in_interfaces) {
|
||||
print_interface(ss, "in", *iface);
|
||||
}
|
||||
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) {
|
||||
ss << "#define gpu_Layer gl_Layer\n";
|
||||
}
|
||||
if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX))
|
||||
{
|
||||
ss << "#define gpu_ViewportIndex gl_ViewportIndex\n";
|
||||
}
|
||||
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
|
||||
if (!GLContext::native_barycentric_support) {
|
||||
ss << "flat in vec4 gpu_pos[3];\n";
|
||||
@ -736,6 +747,8 @@ std::string GLShader::workaround_geometry_shader_source_create(
|
||||
|
||||
const bool do_layer_workaround = !GLContext::layered_rendering_support &&
|
||||
bool(info.builtins_ & BuiltinBits::LAYER);
|
||||
const bool do_viewport_workaround = !GLContext::layered_rendering_support &&
|
||||
bool(info.builtins_ & BuiltinBits::VIEWPORT_INDEX);
|
||||
const bool do_barycentric_workaround = !GLContext::native_barycentric_support &&
|
||||
bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD);
|
||||
|
||||
@ -752,6 +765,9 @@ std::string GLShader::workaround_geometry_shader_source_create(
|
||||
if (do_layer_workaround) {
|
||||
ss << "in int gpu_Layer[];\n";
|
||||
}
|
||||
if (do_viewport_workaround) {
|
||||
ss << "in int gpu_ViewportIndex[];\n";
|
||||
}
|
||||
if (do_barycentric_workaround) {
|
||||
ss << "flat out vec4 gpu_pos[3];\n";
|
||||
ss << "smooth out vec3 gpu_BaryCoord;\n";
|
||||
@ -764,6 +780,9 @@ std::string GLShader::workaround_geometry_shader_source_create(
|
||||
if (do_layer_workaround) {
|
||||
ss << " gl_Layer = gpu_Layer[0];\n";
|
||||
}
|
||||
if (do_viewport_workaround) {
|
||||
ss << " gl_ViewportIndex = gpu_ViewportIndex[0];\n";
|
||||
}
|
||||
if (do_barycentric_workaround) {
|
||||
ss << " gpu_pos[0] = gl_in[0].gl_Position;\n";
|
||||
ss << " gpu_pos[1] = gl_in[1].gl_Position;\n";
|
||||
@ -796,6 +815,9 @@ bool GLShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info
|
||||
if (!GLContext::layered_rendering_support && bool(builtins & BuiltinBits::LAYER)) {
|
||||
return true;
|
||||
}
|
||||
if (!GLContext::layered_rendering_support && bool(builtins & BuiltinBits::VIEWPORT_INDEX)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -853,8 +875,9 @@ static char *glsl_patch_default_get()
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_shading_language_420pack: enable\n");
|
||||
}
|
||||
if (GLContext::layered_rendering_support) {
|
||||
STR_CONCAT(patch, slen, "#extension GL_AMD_vertex_shader_layer: enable\n");
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_shader_viewport_layer_array: enable\n");
|
||||
STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n");
|
||||
STR_CONCAT(patch, slen, "#define gpu_ViewportIndex gl_ViewportIndex\n");
|
||||
}
|
||||
if (GLContext::native_barycentric_support) {
|
||||
STR_CONCAT(patch, slen, "#extension GL_AMD_shader_explicit_vertex_parameter: enable\n");
|
||||
|
@ -111,6 +111,11 @@ void math_modulo(float a, float b, float c, out float result)
|
||||
result = compatible_fmod(a, b);
|
||||
}
|
||||
|
||||
void math_floored_modulo(float a, float b, float c, out float result)
|
||||
{
|
||||
result = (b != 0.0) ? a - floor(a / b) * b : 0.0;
|
||||
}
|
||||
|
||||
void math_trunc(float a, float b, float c, out float result)
|
||||
{
|
||||
result = trunc(a);
|
||||
|
@ -6,10 +6,13 @@
|
||||
|
||||
#include "GPU_context.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
static void test_framebuffer_clear_color_single_attachment()
|
||||
@ -248,4 +251,77 @@ static void test_framebuffer_cube()
|
||||
}
|
||||
GPU_TEST(framebuffer_cube)
|
||||
|
||||
/* Effectively tests the same way EEVEE-Next shadows are rendered. */
|
||||
static void test_framebuffer_multi_viewport()
|
||||
{
|
||||
using namespace gpu::shader;
|
||||
|
||||
GPU_render_begin();
|
||||
|
||||
const int2 size(4, 4);
|
||||
const int layers = 256;
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture = GPU_texture_create_2d_array(
|
||||
__func__, UNPACK2(size), layers, 1, GPU_RG32I, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(&framebuffer,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
int viewport_rects[16][4];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
viewport_rects[i][0] = i % 4;
|
||||
viewport_rects[i][1] = i / 4;
|
||||
viewport_rects[i][2] = 1;
|
||||
viewport_rects[i][3] = 1;
|
||||
}
|
||||
GPU_framebuffer_multi_viewports_set(framebuffer, viewport_rects);
|
||||
|
||||
const float4 clear_color(0.0f);
|
||||
GPU_framebuffer_clear_color(framebuffer, clear_color);
|
||||
|
||||
ShaderCreateInfo create_info("");
|
||||
create_info.vertex_source("gpu_framebuffer_layer_viewport_test.glsl");
|
||||
create_info.fragment_source("gpu_framebuffer_layer_viewport_test.glsl");
|
||||
create_info.builtins(BuiltinBits::VIEWPORT_INDEX | BuiltinBits::LAYER);
|
||||
create_info.fragment_out(0, Type::IVEC2, "out_value");
|
||||
|
||||
GPUShader *shader = GPU_shader_create_from_info(
|
||||
reinterpret_cast<GPUShaderCreateInfo *>(&create_info));
|
||||
|
||||
/* TODO(fclem): remove this boilerplate. */
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
||||
GPUVertBuf *verts = GPU_vertbuf_create_with_format(&format);
|
||||
GPU_vertbuf_data_alloc(verts, 3);
|
||||
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, verts, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
|
||||
GPU_batch_set_shader(batch, shader);
|
||||
|
||||
int tri_count = size.x * size.y * layers;
|
||||
GPU_batch_draw_advanced(batch, 0, tri_count * 3, 0, 1);
|
||||
|
||||
GPU_batch_discard(batch);
|
||||
|
||||
GPU_finish();
|
||||
|
||||
int2 *read_data = static_cast<int2 *>(GPU_texture_read(texture, GPU_DATA_INT, 0));
|
||||
for (auto layer : IndexRange(layers)) {
|
||||
for (auto viewport : IndexRange(16)) {
|
||||
int2 expected_color(layer, viewport);
|
||||
int2 pixel_color = read_data[viewport + layer * 16];
|
||||
EXPECT_EQ(pixel_color, expected_color);
|
||||
}
|
||||
}
|
||||
MEM_freeN(read_data);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture);
|
||||
GPU_shader_free(shader);
|
||||
|
||||
GPU_render_end();
|
||||
}
|
||||
GPU_TEST(framebuffer_multi_viewport)
|
||||
|
||||
} // namespace blender::gpu::tests
|
||||
|
@ -0,0 +1,23 @@
|
||||
|
||||
#ifdef GPU_VERTEX_SHADER
|
||||
void main()
|
||||
{
|
||||
/* Fullscreen triangle. */
|
||||
int v = gl_VertexID % 3;
|
||||
float x = -1.0 + float((v & 1) << 2);
|
||||
float y = -1.0 + float((v & 2) << 1);
|
||||
/* NOTE: Make it cover more than one viewport to test default scissors. */
|
||||
gl_Position = vec4(x * 2.0, y * 2.0, 1.0, 1.0);
|
||||
|
||||
int index = gl_VertexID / 3;
|
||||
gpu_ViewportIndex = index % 16;
|
||||
gpu_Layer = index / 16;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GPU_FRAGMENT_SHADER
|
||||
void main()
|
||||
{
|
||||
out_value = ivec2(gpu_Layer, gpu_ViewportIndex);
|
||||
}
|
||||
#endif
|
@ -376,7 +376,7 @@ void VKCommandBuffer::ensure_active_framebuffer()
|
||||
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
render_pass_begin_info.renderPass = state.framebuffer_->vk_render_pass_get();
|
||||
render_pass_begin_info.framebuffer = state.framebuffer_->vk_framebuffer_get();
|
||||
render_pass_begin_info.renderArea = state.framebuffer_->vk_render_area_get();
|
||||
render_pass_begin_info.renderArea = state.framebuffer_->vk_render_areas_get()[0];
|
||||
/* We don't use clear ops, but vulkan wants to have at least one. */
|
||||
VkClearValue clear_value = {};
|
||||
render_pass_begin_info.clearValueCount = 1;
|
||||
|
@ -71,52 +71,53 @@ void VKFrameBuffer::bind(bool /*enabled_srgb*/)
|
||||
context.activate_framebuffer(*this);
|
||||
}
|
||||
|
||||
VkViewport VKFrameBuffer::vk_viewport_get() const
|
||||
Array<VkViewport, 16> VKFrameBuffer::vk_viewports_get() const
|
||||
{
|
||||
VkViewport viewport;
|
||||
int viewport_rect[4];
|
||||
viewport_get(viewport_rect);
|
||||
Array<VkViewport, 16> viewports(this->multi_viewport_ ? GPU_MAX_VIEWPORTS : 1);
|
||||
|
||||
viewport.x = viewport_rect[0];
|
||||
viewport.y = viewport_rect[1];
|
||||
viewport.width = viewport_rect[2];
|
||||
viewport.height = viewport_rect[3];
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
/*
|
||||
* Vulkan has origin to the top left, Blender bottom left. We counteract this by using a negative
|
||||
* viewport when flip_viewport_ is set. This flips the viewport making any draw/blit use the
|
||||
* correct orientation.
|
||||
*/
|
||||
if (flip_viewport_) {
|
||||
viewport.y = height_ - viewport_rect[1];
|
||||
viewport.height = -viewport_rect[3];
|
||||
int index = 0;
|
||||
for (VkViewport &viewport : viewports) {
|
||||
viewport.x = viewport_[index][0];
|
||||
viewport.y = viewport_[index][1];
|
||||
viewport.width = viewport_[index][2];
|
||||
viewport.height = viewport_[index][3];
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
/*
|
||||
* Vulkan has origin to the top left, Blender bottom left. We counteract this by using a
|
||||
* negative viewport when flip_viewport_ is set. This flips the viewport making any draw/blit
|
||||
* use the correct orientation.
|
||||
*/
|
||||
if (flip_viewport_) {
|
||||
viewport.y = height_ - viewport_[index][1];
|
||||
viewport.height = -viewport_[index][3];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return viewport;
|
||||
return viewports;
|
||||
}
|
||||
|
||||
VkRect2D VKFrameBuffer::vk_render_area_get() const
|
||||
Array<VkRect2D, 16> VKFrameBuffer::vk_render_areas_get() const
|
||||
{
|
||||
VkRect2D render_area = {};
|
||||
Array<VkRect2D, 16> render_areas(this->multi_viewport_ ? GPU_MAX_VIEWPORTS : 1);
|
||||
|
||||
if (scissor_test_get()) {
|
||||
int scissor_rect[4];
|
||||
scissor_get(scissor_rect);
|
||||
render_area.offset.x = scissor_rect[0];
|
||||
render_area.offset.y = scissor_rect[1];
|
||||
render_area.extent.width = scissor_rect[2];
|
||||
render_area.extent.height = scissor_rect[3];
|
||||
for (VkRect2D &render_area : render_areas) {
|
||||
if (scissor_test_get()) {
|
||||
int scissor_rect[4];
|
||||
scissor_get(scissor_rect);
|
||||
render_area.offset.x = scissor_rect[0];
|
||||
render_area.offset.y = scissor_rect[1];
|
||||
render_area.extent.width = scissor_rect[2];
|
||||
render_area.extent.height = scissor_rect[3];
|
||||
}
|
||||
else {
|
||||
render_area.offset.x = 0;
|
||||
render_area.offset.y = 0;
|
||||
render_area.extent.width = width_;
|
||||
render_area.extent.height = height_;
|
||||
}
|
||||
}
|
||||
else {
|
||||
render_area.offset.x = 0;
|
||||
render_area.offset.y = 0;
|
||||
render_area.extent.width = width_;
|
||||
render_area.extent.height = height_;
|
||||
}
|
||||
|
||||
return render_area;
|
||||
return render_areas;
|
||||
}
|
||||
|
||||
bool VKFrameBuffer::check(char /*err_out*/[256])
|
||||
@ -170,7 +171,7 @@ void VKFrameBuffer::clear(const Vector<VkClearAttachment> &attachments) const
|
||||
return;
|
||||
}
|
||||
VkClearRect clear_rect = {};
|
||||
clear_rect.rect = vk_render_area_get();
|
||||
clear_rect.rect = vk_render_areas_get()[0];
|
||||
clear_rect.baseArrayLayer = 0;
|
||||
clear_rect.layerCount = 1;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
@ -107,8 +108,8 @@ class VKFrameBuffer : public FrameBuffer {
|
||||
BLI_assert(vk_render_pass_ != VK_NULL_HANDLE);
|
||||
return vk_render_pass_;
|
||||
}
|
||||
VkViewport vk_viewport_get() const;
|
||||
VkRect2D vk_render_area_get() const;
|
||||
Array<VkViewport, 16> vk_viewports_get() const;
|
||||
Array<VkRect2D, 16> vk_render_areas_get() const;
|
||||
VkImage vk_image_get() const
|
||||
{
|
||||
BLI_assert(vk_image_ != VK_NULL_HANDLE);
|
||||
|
@ -164,12 +164,12 @@ void VKPipeline::finalize(VKContext &context,
|
||||
/* Viewport state. */
|
||||
VkPipelineViewportStateCreateInfo viewport_state = {};
|
||||
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
VkViewport viewport = framebuffer.vk_viewport_get();
|
||||
viewport_state.pViewports = &viewport;
|
||||
viewport_state.viewportCount = 1;
|
||||
VkRect2D scissor = framebuffer.vk_render_area_get();
|
||||
viewport_state.pScissors = &scissor;
|
||||
viewport_state.scissorCount = 1;
|
||||
Array<VkViewport, 16> viewports = framebuffer.vk_viewports_get();
|
||||
viewport_state.pViewports = &viewports[0];
|
||||
viewport_state.viewportCount = viewports.size();
|
||||
Array<VkRect2D, 16> scissors = framebuffer.vk_render_areas_get();
|
||||
viewport_state.pScissors = &scissors[0];
|
||||
viewport_state.scissorCount = scissors.size();
|
||||
pipeline_create_info.pViewportState = &viewport_state;
|
||||
|
||||
/* Multi-sample state. */
|
||||
|
@ -500,6 +500,11 @@ static char *glsl_patch_get()
|
||||
|
||||
STR_CONCAT(patch, slen, "#define gl_InstanceID gpu_InstanceIndex\n");
|
||||
|
||||
/* TODO(fclem): This creates a validation error and should be already part of Vulkan 1.2. */
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_shader_viewport_layer_array: enable\n");
|
||||
STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n");
|
||||
STR_CONCAT(patch, slen, "#define gpu_ViewportIndex gl_ViewportIndex\n");
|
||||
|
||||
STR_CONCAT(patch, slen, "#define DFDX_SIGN 1.0\n");
|
||||
STR_CONCAT(patch, slen, "#define DFDY_SIGN 1.0\n");
|
||||
|
||||
@ -526,6 +531,7 @@ Vector<uint32_t> VKShader::compile_glsl_to_spirv(Span<const char *> sources,
|
||||
shaderc::Compiler &compiler = backend.get_shaderc_compiler();
|
||||
shaderc::CompileOptions options;
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||
if (G.debug & G_DEBUG_GPU_RENDERDOC) {
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_zero);
|
||||
options.SetGenerateDebugInfo();
|
||||
|
@ -83,7 +83,6 @@ bool ArmatureExporter::add_instance_controller(Object *ob)
|
||||
}
|
||||
|
||||
/* write root bone URLs */
|
||||
Bone *bone;
|
||||
LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
|
||||
write_bone_URLs(ins, ob_arm, bone);
|
||||
}
|
||||
|
@ -154,7 +154,6 @@ BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool fo
|
||||
#endif
|
||||
|
||||
if (ob->type == OB_ARMATURE) {
|
||||
bPoseChannel *pchan;
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
|
||||
Bone *bone = pchan->bone;
|
||||
Matrix bmat;
|
||||
@ -222,7 +221,6 @@ bool BCAnimationSampler::is_animated_by_constraint(Object *ob,
|
||||
ListBase *conlist,
|
||||
std::set<Object *> &animated_objects)
|
||||
{
|
||||
bConstraint *con;
|
||||
LISTBASE_FOREACH (bConstraint *, con, conlist) {
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
|
||||
@ -231,7 +229,6 @@ bool BCAnimationSampler::is_animated_by_constraint(Object *ob,
|
||||
}
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
bConstraintTarget *ct;
|
||||
Object *obtar;
|
||||
bool found = false;
|
||||
|
||||
|
@ -70,7 +70,6 @@ bool ControllerExporter::add_instance_controller(Object *ob)
|
||||
}
|
||||
|
||||
/* write root bone URLs */
|
||||
Bone *bone;
|
||||
LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
|
||||
write_bone_URLs(ins, ob_arm, bone);
|
||||
}
|
||||
@ -424,7 +423,6 @@ std::string ControllerExporter::add_joints_source(Object *ob_arm,
|
||||
std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX;
|
||||
|
||||
int totjoint = 0;
|
||||
bDeformGroup *def;
|
||||
LISTBASE_FOREACH (bDeformGroup *, def, defbase) {
|
||||
if (is_bone_defgroup(ob_arm, def)) {
|
||||
totjoint++;
|
||||
|
@ -198,7 +198,6 @@ void SceneExporter::writeNode(Object *ob)
|
||||
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
bConstraintTarget *ct;
|
||||
Object *obtar;
|
||||
|
||||
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
|
||||
|
@ -253,7 +253,6 @@ Object *bc_get_assigned_armature(Object *ob)
|
||||
ob_arm = ob->parent;
|
||||
}
|
||||
else {
|
||||
ModifierData *mod;
|
||||
LISTBASE_FOREACH (ModifierData *, mod, &ob->modifiers) {
|
||||
if (mod->type == eModifierType_Armature) {
|
||||
ob_arm = ((ArmatureModifierData *)mod)->object;
|
||||
@ -435,8 +434,6 @@ bool bc_is_leaf_bone(Bone *bone)
|
||||
|
||||
EditBone *bc_get_edit_bone(bArmature *armature, char *name)
|
||||
{
|
||||
EditBone *eBone;
|
||||
|
||||
LISTBASE_FOREACH (EditBone *, eBone, armature->edbo) {
|
||||
if (STREQ(name, eBone->name)) {
|
||||
return eBone;
|
||||
@ -765,7 +762,6 @@ static bool has_custom_props(Bone *bone, bool enabled, std::string key)
|
||||
|
||||
void bc_enable_fcurves(bAction *act, char *bone_name)
|
||||
{
|
||||
FCurve *fcu;
|
||||
char prefix[200];
|
||||
|
||||
if (bone_name) {
|
||||
|
@ -49,8 +49,8 @@ void USD_unregister_hook(struct USDHook *hook)
|
||||
USDHook *USD_find_hook_name(const char name[])
|
||||
{
|
||||
/* sanity checks */
|
||||
if (g_usd_hooks.empty() || (name == NULL) || (name[0] == 0)) {
|
||||
return NULL;
|
||||
if (g_usd_hooks.empty() || (name == nullptr) || (name[0] == 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USDHookList::iterator hook_iter = std::find_if(
|
||||
@ -58,7 +58,7 @@ USDHook *USD_find_hook_name(const char name[])
|
||||
return strcmp(hook->idname, name) == 0;
|
||||
});
|
||||
|
||||
return (hook_iter == g_usd_hooks.end()) ? NULL : *hook_iter;
|
||||
return (hook_iter == g_usd_hooks.end()) ? nullptr : *hook_iter;
|
||||
}
|
||||
|
||||
namespace blender::io::usd {
|
||||
@ -81,7 +81,7 @@ struct USDSceneExportContext {
|
||||
|
||||
USDSceneExportContext(pxr::UsdStageRefPtr in_stage, Depsgraph *depsgraph) : stage(in_stage)
|
||||
{
|
||||
RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr);
|
||||
RNA_pointer_create(nullptr, &RNA_Depsgraph, depsgraph, &depsgraph_ptr);
|
||||
}
|
||||
|
||||
pxr::UsdStageRefPtr get_stage()
|
||||
@ -259,7 +259,7 @@ class OnMaterialExportInvoker : public USDHookInvoker {
|
||||
pxr::UsdShadeMaterial &usd_material)
|
||||
: hook_context_(stage), usd_material_(usd_material)
|
||||
{
|
||||
RNA_pointer_create(NULL, &RNA_Material, material, &material_ptr_);
|
||||
RNA_pointer_create(nullptr, &RNA_Material, material, &material_ptr_);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -267,9 +267,9 @@ void set_normal_texture_range(pxr::UsdShadeShader &usd_shader, const InputSpec &
|
||||
}
|
||||
|
||||
/* Create USD Shade Material network from Blender viewport display settings. */
|
||||
void create_usd_viewport_material(const USDExporterContext &usd_export_context,
|
||||
Material *material,
|
||||
pxr::UsdShadeMaterial &usd_material)
|
||||
static void create_usd_viewport_material(const USDExporterContext &usd_export_context,
|
||||
Material *material,
|
||||
pxr::UsdShadeMaterial &usd_material)
|
||||
{
|
||||
/* Construct the shader. */
|
||||
pxr::SdfPath shader_path = usd_material.GetPath().AppendChild(usdtokens::preview_shader);
|
||||
|
@ -451,6 +451,7 @@ typedef struct GreasePencil {
|
||||
const blender::bke::greasepencil::Layer *get_active_layer() const;
|
||||
blender::bke::greasepencil::Layer *get_active_layer_for_write();
|
||||
void set_active_layer(const blender::bke::greasepencil::Layer *layer);
|
||||
bool is_layer_active(const blender::bke::greasepencil::Layer *layer) const;
|
||||
|
||||
blender::bke::greasepencil::Layer &add_layer(blender::bke::greasepencil::LayerGroup &group,
|
||||
blender::StringRefNull name);
|
||||
|
@ -2124,6 +2124,7 @@ typedef enum NodeMathOperation {
|
||||
NODE_MATH_PINGPONG = 37,
|
||||
NODE_MATH_SMOOTH_MIN = 38,
|
||||
NODE_MATH_SMOOTH_MAX = 39,
|
||||
NODE_MATH_FLOORED_MODULO = 40,
|
||||
} NodeMathOperation;
|
||||
|
||||
typedef enum NodeVectorMathOperation {
|
||||
|
@ -692,7 +692,9 @@ typedef struct UserDef_Experimental {
|
||||
char use_new_volume_nodes;
|
||||
char use_rotation_socket;
|
||||
char use_node_group_operators;
|
||||
char use_shader_node_previews;
|
||||
char use_asset_shelf;
|
||||
char _pad[7];
|
||||
/** `makesdna` does not allow empty structs. */
|
||||
} UserDef_Experimental;
|
||||
|
||||
@ -794,7 +796,7 @@ typedef struct UserDef {
|
||||
int scrollback;
|
||||
/** Node insert offset (aka auto-offset) margin, but might be useful for later stuff as well. */
|
||||
char node_margin;
|
||||
char _pad2[1];
|
||||
char node_preview_res;
|
||||
/** #eUserpref_Translation_Flags. */
|
||||
short transopts;
|
||||
short menuthreshold1, menuthreshold2;
|
||||
|
@ -4751,7 +4751,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
|
||||
{"rna_sound.cc", "rna_sound_api.cc", RNA_def_sound},
|
||||
{"rna_ui.cc", "rna_ui_api.cc", RNA_def_ui},
|
||||
#ifdef WITH_USD
|
||||
{"rna_usd.cc", NULL, RNA_def_usd},
|
||||
{"rna_usd.cc", nullptr, RNA_def_usd},
|
||||
#endif
|
||||
{"rna_userdef.cc", nullptr, RNA_def_userdef},
|
||||
{"rna_vfont.cc", "rna_vfont_api.cc", RNA_def_vfont},
|
||||
|
@ -95,6 +95,7 @@ static void rna_Material_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA
|
||||
static void rna_Material_update_previews(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
|
||||
{
|
||||
Material *ma = (Material *)ptr->owner_id;
|
||||
BKE_material_make_node_previews_dirty(ma);
|
||||
|
||||
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_PREVIEW, ma);
|
||||
}
|
||||
|
@ -223,7 +223,16 @@ const EnumPropertyItem rna_enum_node_math_items[] = {
|
||||
{NODE_MATH_TRUNC, "TRUNC", 0, "Truncate", "The integer part of A, removing fractional digits"},
|
||||
RNA_ENUM_ITEM_SEPR,
|
||||
{NODE_MATH_FRACTION, "FRACT", 0, "Fraction", "The fraction part of A"},
|
||||
{NODE_MATH_MODULO, "MODULO", 0, "Modulo", "Modulo using fmod(A,B)"},
|
||||
{NODE_MATH_MODULO,
|
||||
"MODULO",
|
||||
0,
|
||||
"Truncated Modulo",
|
||||
"The remainder of truncated division using fmod(A,B)"},
|
||||
{NODE_MATH_FLOORED_MODULO,
|
||||
"FLOORED_MODULO",
|
||||
0,
|
||||
"Floored Modulo",
|
||||
"The remainder of floored division"},
|
||||
{NODE_MATH_WRAP, "WRAP", 0, "Wrap", "Wrap value to range, wrap(A,B)"},
|
||||
{NODE_MATH_SNAP, "SNAP", 0, "Snap", "Snap to increment, snap(A,B)"},
|
||||
{NODE_MATH_PINGPONG,
|
||||
|
@ -7659,9 +7659,12 @@ static void rna_def_space_node(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Overlay Settings", "Settings for display of overlays in the Node Editor");
|
||||
|
||||
prop = RNA_def_property(srna, "supports_preview", PROP_BOOLEAN, PROP_NONE);
|
||||
prop = RNA_def_property(srna, "supports_previews", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_SpaceNode_supports_previews", nullptr);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Supports Previews",
|
||||
"Whether the node editor's type supports displaying node previews");
|
||||
|
||||
rna_def_space_node_overlay(brna);
|
||||
RNA_api_space_node(srna);
|
||||
|
@ -27,11 +27,11 @@ static StructRNA *rna_USDHook_refine(PointerRNA *ptr)
|
||||
return (hook->rna_ext.srna) ? hook->rna_ext.srna : &RNA_USDHook;
|
||||
}
|
||||
|
||||
static bool rna_USDHook_unregister(Main *bmain, StructRNA *type)
|
||||
static bool rna_USDHook_unregister(Main * /* bmain */, StructRNA *type)
|
||||
{
|
||||
USDHook *hook = static_cast<USDHook *>(RNA_struct_blender_type_get(type));
|
||||
|
||||
if (hook == NULL) {
|
||||
if (hook == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ static bool rna_USDHook_unregister(Main *bmain, StructRNA *type)
|
||||
RNA_struct_free_extension(type, &hook->rna_ext);
|
||||
RNA_struct_free(&BLENDER_RNA, type);
|
||||
|
||||
WM_main_add_notifier(NC_WINDOW, NULL);
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
/* unlink Blender-side data */
|
||||
USD_unregister_hook(hook);
|
||||
@ -58,16 +58,16 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
||||
StructFreeFunc free)
|
||||
{
|
||||
const char *error_prefix = "Registering USD hook class:";
|
||||
USDHook dummy_hook = {NULL};
|
||||
USDHook dummy_hook = {{0}};
|
||||
USDHook *hook;
|
||||
PointerRNA dummy_hook_ptr = {NULL};
|
||||
PointerRNA dummy_hook_ptr = {nullptr};
|
||||
|
||||
/* setup dummy type info to store static properties in */
|
||||
RNA_pointer_create(NULL, &RNA_USDHook, &dummy_hook, &dummy_hook_ptr);
|
||||
RNA_pointer_create(nullptr, &RNA_USDHook, &dummy_hook, &dummy_hook_ptr);
|
||||
|
||||
/* validate the python class */
|
||||
if (validate(&dummy_hook_ptr, data, NULL) != 0) {
|
||||
return NULL;
|
||||
if (validate(&dummy_hook_ptr, data, nullptr) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (strlen(identifier) >= sizeof(dummy_hook.idname)) {
|
||||
@ -77,7 +77,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
||||
error_prefix,
|
||||
identifier,
|
||||
(int)sizeof(dummy_hook.idname));
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* check if we have registered this hook before, and remove it */
|
||||
@ -92,7 +92,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
||||
identifier,
|
||||
dummy_hook.idname,
|
||||
"could not be unregistered");
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ static StructRNA *rna_USDHook_register(Main *bmain,
|
||||
/* add and register with other info as needed */
|
||||
USD_register_hook(hook);
|
||||
|
||||
WM_main_add_notifier(NC_WINDOW, NULL);
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
/* return the struct-rna added */
|
||||
return hook->rna_ext.srna;
|
||||
@ -122,32 +122,30 @@ static void rna_def_usd_hook(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
srna = RNA_def_struct(brna, "USDHook", NULL);
|
||||
srna = RNA_def_struct(brna, "USDHook", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "USD Hook", "Defines callback functions to extend USD IO");
|
||||
RNA_def_struct_sdna(srna, "USDHook");
|
||||
RNA_def_struct_refine_func(srna, "rna_USDHook_refine");
|
||||
RNA_def_struct_register_funcs(srna, "rna_USDHook_register", "rna_USDHook_unregister", NULL);
|
||||
RNA_def_struct_register_funcs(srna, "rna_USDHook_register", "rna_USDHook_unregister", nullptr);
|
||||
|
||||
///* Properties --------------------- */
|
||||
|
||||
RNA_define_verify_sdna(0); /* not in sdna */
|
||||
|
||||
prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "idname");
|
||||
RNA_def_property_string_sdna(prop, nullptr, "idname");
|
||||
RNA_def_property_flag(prop, PROP_REGISTER);
|
||||
RNA_def_property_ui_text(prop, "ID Name", "");
|
||||
|
||||
prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "name");
|
||||
RNA_def_property_string_sdna(prop, nullptr, "name");
|
||||
RNA_def_property_ui_text(prop, "UI Name", "");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_flag(prop, PROP_REGISTER);
|
||||
|
||||
prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "description");
|
||||
RNA_def_property_string_sdna(prop, nullptr, "description");
|
||||
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
|
||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
||||
RNA_def_property_ui_text(prop, "Description", "A short description of the USD hook");
|
||||
|
@ -3303,7 +3303,7 @@ static void rna_def_userdef_theme_space_seq(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
|
||||
prop = RNA_def_property(srna, "transition_strip", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "transition");
|
||||
RNA_def_property_float_sdna(prop, nullptr, "transition");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_ui_text(prop, "Transition Strip", "");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
@ -5456,6 +5456,15 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
|
||||
prop, "Auto-offset Margin", "Minimum distance between nodes for Auto-offsetting nodes");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
prop = RNA_def_property(srna, "node_preview_resolution", PROP_INT, PROP_PIXEL);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "node_preview_res");
|
||||
RNA_def_property_range(prop, 50, 250);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Node Preview Resolution",
|
||||
"Resolution used for Shader node previews (should be changed for "
|
||||
"performance convenience)");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
/* cursor */
|
||||
prop = RNA_def_property(srna, "use_cursor_lock_adjust", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "uiflag", USER_LOCK_CURSOR_ADJUST);
|
||||
@ -6799,6 +6808,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
||||
"Enables the asset shelf regions in the 3D view. Used by the Pose "
|
||||
"Library add-on in Pose Mode only");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_shader_node_previews", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Shader Node Previews", "Enables previews in the shader node editor");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
@ -152,6 +152,8 @@ inline bool try_dispatch_float_math_fl_fl_to_fl(const int operation, Callback &&
|
||||
return dispatch(exec_preset_fast, [](float a, float b) { return (float)(a > b); });
|
||||
case NODE_MATH_MODULO:
|
||||
return dispatch(exec_preset_fast, [](float a, float b) { return safe_modf(a, b); });
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
return dispatch(exec_preset_fast, [](float a, float b) { return safe_floored_modf(a, b); });
|
||||
case NODE_MATH_SNAP:
|
||||
return dispatch(exec_preset_fast,
|
||||
[](float a, float b) { return floorf(safe_divide(a, b)) * b; });
|
||||
|
@ -53,6 +53,8 @@ const FloatMathOperationInfo *get_float_math_operation_info(const int operation)
|
||||
RETURN_OPERATION_INFO("Greater Than", "math_greater_than");
|
||||
case NODE_MATH_MODULO:
|
||||
RETURN_OPERATION_INFO("Modulo", "math_modulo");
|
||||
case NODE_MATH_FLOORED_MODULO:
|
||||
RETURN_OPERATION_INFO("Floored Modulo", "math_floored_modulo");
|
||||
case NODE_MATH_ABSOLUTE:
|
||||
RETURN_OPERATION_INFO("Absolute", "math_absolute");
|
||||
case NODE_MATH_ARCTAN2:
|
||||
|
@ -175,6 +175,16 @@ static void valuefn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_MATH_FLOORED_MODULO: {
|
||||
if (in1 == 0.0f) {
|
||||
*out = 0.0f;
|
||||
}
|
||||
else {
|
||||
*out = in0 - floorf(in0 / in1) * in1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_MATH_ABSOLUTE: {
|
||||
*out = fabsf(in0);
|
||||
break;
|
||||
|
@ -406,6 +406,9 @@ void RE_stats_draw_cb(struct Render *re, void *handle, void (*f)(void *handle, R
|
||||
void RE_progress_cb(struct Render *re, void *handle, void (*f)(void *handle, float));
|
||||
void RE_draw_lock_cb(struct Render *re, void *handle, void (*f)(void *handle, bool lock));
|
||||
void RE_test_break_cb(struct Render *re, void *handle, bool (*f)(void *handle));
|
||||
void RE_prepare_viewlayer_cb(struct Render *re,
|
||||
void *handle,
|
||||
bool (*f)(void *handle, ViewLayer *vl, struct Depsgraph *depsgraph));
|
||||
void RE_current_scene_update_cb(struct Render *re,
|
||||
void *handle,
|
||||
void (*f)(void *handle, struct Scene *scene));
|
||||
|
@ -849,6 +849,14 @@ static void engine_render_view_layer(Render *re,
|
||||
/* Create depsgraph with scene evaluated at render resolution. */
|
||||
ViewLayer *view_layer = static_cast<ViewLayer *>(
|
||||
BLI_findstring(&re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)));
|
||||
if (re->prepare_viewlayer) {
|
||||
if (!re->prepare_viewlayer(re->prepare_vl_handle, view_layer, engine->depsgraph)) {
|
||||
if (re->draw_lock) {
|
||||
re->draw_lock(re->dlh, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
engine_depsgraph_init(engine, view_layer);
|
||||
|
||||
/* Sync data to engine, within draw lock so scene data can be accessed safely. */
|
||||
|
@ -170,6 +170,12 @@ static bool do_write_image_or_movie(Render *re,
|
||||
static void result_nothing(void * /*arg*/, RenderResult * /*rr*/) {}
|
||||
static void result_rcti_nothing(void * /*arg*/, RenderResult * /*rr*/, rcti * /*rect*/) {}
|
||||
static void current_scene_nothing(void * /*arg*/, Scene * /*scene*/) {}
|
||||
static bool prepare_viewlayer_nothing(void * /*arg*/,
|
||||
ViewLayer * /*vl*/,
|
||||
Depsgraph * /*depsgraph*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static void stats_nothing(void * /*arg*/, RenderStats * /*rs*/) {}
|
||||
static void float_nothing(void * /*arg*/, float /*val*/) {}
|
||||
static bool default_break(void * /*arg*/)
|
||||
@ -548,6 +554,7 @@ void RE_InitRenderCB(Render *re)
|
||||
re->display_clear = result_nothing;
|
||||
re->display_update = result_rcti_nothing;
|
||||
re->current_scene_update = current_scene_nothing;
|
||||
re->prepare_viewlayer = prepare_viewlayer_nothing;
|
||||
re->progress = float_nothing;
|
||||
re->test_break = default_break;
|
||||
if (G.background) {
|
||||
@ -916,6 +923,14 @@ void RE_test_break_cb(Render *re, void *handle, bool (*f)(void *handle))
|
||||
re->tbh = handle;
|
||||
}
|
||||
|
||||
void RE_prepare_viewlayer_cb(Render *re,
|
||||
void *handle,
|
||||
bool (*f)(void *handle, ViewLayer *vl, Depsgraph *depsgraph))
|
||||
{
|
||||
re->prepare_viewlayer = f;
|
||||
re->prepare_vl_handle = handle;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -170,6 +170,13 @@ struct Render : public BaseRender {
|
||||
bool (*test_break)(void *handle) = nullptr;
|
||||
void *tbh = nullptr;
|
||||
|
||||
/**
|
||||
* Executed right before the initialisation of the depsgraph, in order to modify some stuff in
|
||||
* the viewlayer. The modified ids must be tagged in the depsgraph.
|
||||
*/
|
||||
bool (*prepare_viewlayer)(void *handle, struct ViewLayer *vl, struct Depsgraph *depsgraph);
|
||||
void *prepare_vl_handle;
|
||||
|
||||
RenderStats i = {};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user