Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
48 changed files with 1317 additions and 287 deletions
Showing only changes of commit baf768ecfb - Show all commits

View File

@ -7726,22 +7726,6 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccc" />
</g>
<g
id="g3791"
inkscape:label="CA-24">
<path
sodipodi:nodetypes="csssscccccc"
inkscape:connector-curvature="0"
id="path28911-6"
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ssccccsssccccc"
inkscape:connector-curvature="0"
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
id="path28913-4" />
</g>
<g
style="display:inline;enable-background:new"
id="g28909-7"
@ -7762,6 +7746,22 @@
transform="matrix(1,0,0,-1,520,1334)"
id="path28907-4" />
</g>
<g
id="g3791"
inkscape:label="CA-24">
<path
sodipodi:nodetypes="csssscccccc"
inkscape:connector-curvature="0"
id="path28911-6"
d="m 489,604 v 8 c 0,0.54532 0.45468,1 1,1 h 12 c 0.54532,0 1,-0.45468 1,-1 v -8 h -1 v 8 h -12 v -8 z"
style="opacity:0.6;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke" />
<path
sodipodi:nodetypes="ssccccsssccccc"
inkscape:connector-curvature="0"
style="vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
d="m 490,599 c -0.54532,0 -1,0.45468 -1,1 v 3 h 1 12 1 v -3 c 0,-0.54532 -0.45468,-1 -1,-1 z m 0,1 h 2 v 2 h -2 z"
id="path28913-4" />
</g>
<g
id="g9239"
inkscape:label="CA-23"
@ -13113,6 +13113,40 @@
d="m 221.03217,109.33958 c 0.67621,0.01 0.67621,-1.00961 0,-1 h -2.79493 c 1.0479,-1.11729 1.7641,-1.66802 2.82812,-2.73204 0.62065,-0.56444 -0.28321,-1.46832 -0.84765,-0.84766 -1.06063,1.10128 -1.59202,1.7772 -2.68554,2.87072 v -2.79102 c 0.01,-0.67616 -1.00956,-0.67616 -1,0 v 4 c 3e-5,0.27613 0.22387,0.49998 0.5,0.5 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
id="g24638"
inkscape:label="O-18"
style="display:inline;enable-background:new">
<path
style="color:#000000;opacity:1;fill:none;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none"
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
id="path6454-6" />
<path
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
id="path15424"
sodipodi:nodetypes="cc" />
</g>
<g
id="g24643"
inkscape:label="O-17"
style="display:inline;enable-background:new">
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 343.45275,317.52104 4.51631,-3.99333"
id="path4694"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
id="path6454"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 350.84964,311.18794 4.21454,-3.72502"
id="path19640"
sodipodi:nodetypes="cc" />
</g>
<g
style="display:inline;enable-background:new"
id="g28812"
@ -18997,36 +19031,6 @@
id="path4817-4"
inkscape:connector-curvature="0" />
</g>
<g
id="g24638">
<path
style="color:#000000;fill:none;stroke-linecap:round;-inkscape-stroke:none;opacity:1;stroke:#ffffff;stroke-opacity:1;stroke-linejoin:round"
d="m 374.12695,305.31445 c -1.10721,0 -1.93616,0.24199 -2.97851,0.63086 -1.04236,0.38888 -2.18224,0.95849 -3.30078,1.77735 -2.2371,1.63771 -4.46485,4.54533 -4.46485,8.40625 a 3.0581999,3.0581999 0 0 0 3.0586,3.05859 3.0581999,3.0581999 0 0 0 3.05859,-3.05859 c 0,-1.63187 0.77567,-2.603 1.96094,-3.47071 0.59263,-0.43385 1.26841,-0.77311 1.82422,-0.98047 0.5558,-0.20735 1.10962,-0.24804 0.84179,-0.24804 a 3.0581999,3.0581999 0 0 0 3.05664,-3.0586 3.0581999,3.0581999 0 0 0 -3.05664,-3.05664 z"
id="path6454-6" />
<path
style="display:inline;opacity:0.5;fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
d="m 366.4419,316.12916 c 0,-5.49278 6.00534,-7.75723 7.68412,-7.75723"
id="path15424"
sodipodi:nodetypes="cc" />
</g>
<g
id="g24643">
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 343.45275,317.52104 4.51631,-3.99333"
id="path4694"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 342.44508,318.83525 c 14.125,0.14581 -0.42233,-12.9179 13.5429,-12.85486"
id="path6454"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.5;fill:#ffffff;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
d="m 350.84964,311.18794 4.21454,-3.72502"
id="path19640"
sodipodi:nodetypes="cc" />
</g>
</g>
<g
inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -101,7 +101,11 @@ const UserDef U_default = {
.gp_euclideandist = 2,
.gp_eraser = 25,
.gp_settings = 0,
#ifdef __APPLE__
.gpu_backend = GPU_BACKEND_METAL,
#else
.gpu_backend = GPU_BACKEND_OPENGL,
#endif
/** Initialized by: #BKE_studiolight_default. */
.light_param = {{0}},

View File

@ -397,7 +397,7 @@ class ConstraintButtonsPanel:
sub.prop(con, "invert_z", text="Z", toggle=True)
row.label(icon='BLANK1')
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.space_template(layout, con)
@ -488,7 +488,7 @@ class ConstraintButtonsPanel:
self.target_template(layout, con)
layout.prop(con, "remove_target_shear")
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.space_template(layout, con)
@ -513,7 +513,7 @@ class ConstraintButtonsPanel:
subsub.prop(con, "eval_time", text="")
row.prop_decorator(con, "eval_time")
layout.prop(con, "mix_mode", text="Mix")
layout.prop(con, "mix_mode", text="Mix", text_ctxt=i18n_contexts.constraint)
self.draw_influence(layout, con)
@ -1024,7 +1024,7 @@ class ConstraintButtonsSubPanel:
col.prop(con, "to_min_z" + ext, text="Min")
col.prop(con, "to_max_z" + ext, text="Max")
layout.prop(con, "mix_mode" + ext, text="Mix")
layout.prop(con, "mix_mode" + ext, text="Mix", text_ctxt=i18n_contexts.constraint)
def draw_armature_bones(self, context):
layout = self.layout

View File

@ -98,6 +98,7 @@ class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclaration;
class NodeDeclarationBuilder;
class GatherAddNodeSearchParams;
class GatherLinkSearchOpParams;
} // namespace nodes
namespace realtime_compositor {
@ -122,6 +123,10 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket
using NodeGatherSocketLinkOperationsFunction =
void (*)(blender::nodes::GatherLinkSearchOpParams &params);
/* Adds node add menu operations that are specific to this node type. */
using NodeGatherAddOperationsFunction =
void (*)(blender::nodes::GatherAddNodeSearchParams &params);
using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation
*(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node);
using NodeGetCompositorShaderNodeFunction =
@ -135,6 +140,7 @@ typedef void *NodeGeometryExecFunction;
typedef void *NodeDeclareFunction;
typedef void *NodeDeclareDynamicFunction;
typedef void *NodeGatherSocketLinkOperationsFunction;
typedef void *NodeGatherAddOperationsFunction;
typedef void *SocketGetCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPTypeFunction;
typedef void *SocketGetGeometryNodesCPPValueFunction;
@ -353,6 +359,13 @@ typedef struct bNodeType {
*/
NodeGatherSocketLinkOperationsFunction gather_link_search_ops;
/**
* Add to the list of search items gathered by the add-node search. The default behavior of
* adding a single item with the node name is usually enough, but node types can have any number
* of custom search items.
*/
NodeGatherAddOperationsFunction gather_add_node_search_ops;
/** True when the node cannot be muted. */
bool no_muting;

View File

@ -25,6 +25,8 @@
#include "BKE_particle.h"
#include "BLI_kdopbvh.h"
#include "BLT_translation.h"
#include "BKE_modifier.h"
#include "RNA_enum_types.h"
@ -1607,7 +1609,7 @@ BoidRule *boid_new_rule(int type)
rule->type = type;
rule->flag |= BOIDRULE_IN_AIR | BOIDRULE_ON_LAND;
BLI_strncpy(rule->name, rna_enum_boidrule_type_items[type - 1].name, sizeof(rule->name));
BLI_strncpy(rule->name, DATA_(rna_enum_boidrule_type_items[type - 1].name), sizeof(rule->name));
return rule;
}

View File

@ -508,6 +508,7 @@ static void prepare_inferencing_interfaces(
bool update_field_inferencing(const bNodeTree &tree)
{
BLI_assert(tree.type == NTREE_GEOMETRY);
tree.ensure_topology_cache();
const Span<const bNode *> nodes = tree.all_nodes();

View File

@ -142,6 +142,18 @@ double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(
*/
char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
eFileAttributes BLI_file_attributes(const char *path);
/**
* Changes the current working directory to the provided path.
*
* Usage of this function is strongly discouraged as it is not thread safe. It will likely cause
* issues if there is an operation on another thread that does not expect the current working
* directory to change. This has been added to support USDZ export, which has a problematic
* "feature" described in this issue https://projects.blender.org/blender/blender/issues/99807. It
* will be removed if it is possible to resolve that issue upstream in the USD library.
*
* \return true on success, false otherwise.
*/
bool BLI_change_working_dir(const char *dir);
/** \} */

View File

@ -504,8 +504,9 @@ void _va_mul_m3_series_4(float r[3][3],
const float m2[3][3],
const float m3[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
float s[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(r, s, m3);
}
void _va_mul_m3_series_5(float r[3][3],
const float m1[3][3],
@ -513,9 +514,11 @@ void _va_mul_m3_series_5(float r[3][3],
const float m3[3][3],
const float m4[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
mul_m3_m3m3(r, r, m4);
float s[3][3];
float t[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(t, s, m3);
mul_m3_m3m3(r, t, m4);
}
void _va_mul_m3_series_6(float r[3][3],
const float m1[3][3],
@ -524,10 +527,12 @@ void _va_mul_m3_series_6(float r[3][3],
const float m4[3][3],
const float m5[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
mul_m3_m3m3(r, r, m4);
mul_m3_m3m3(r, r, m5);
float s[3][3];
float t[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(t, s, m3);
mul_m3_m3m3(s, t, m4);
mul_m3_m3m3(r, s, m5);
}
void _va_mul_m3_series_7(float r[3][3],
const float m1[3][3],
@ -537,11 +542,13 @@ void _va_mul_m3_series_7(float r[3][3],
const float m5[3][3],
const float m6[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
mul_m3_m3m3(r, r, m4);
mul_m3_m3m3(r, r, m5);
mul_m3_m3m3(r, r, m6);
float s[3][3];
float t[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(t, s, m3);
mul_m3_m3m3(s, t, m4);
mul_m3_m3m3(t, s, m5);
mul_m3_m3m3(r, t, m6);
}
void _va_mul_m3_series_8(float r[3][3],
const float m1[3][3],
@ -552,12 +559,14 @@ void _va_mul_m3_series_8(float r[3][3],
const float m6[3][3],
const float m7[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
mul_m3_m3m3(r, r, m4);
mul_m3_m3m3(r, r, m5);
mul_m3_m3m3(r, r, m6);
mul_m3_m3m3(r, r, m7);
float s[3][3];
float t[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(t, s, m3);
mul_m3_m3m3(s, t, m4);
mul_m3_m3m3(t, s, m5);
mul_m3_m3m3(s, t, m6);
mul_m3_m3m3(r, s, m7);
}
void _va_mul_m3_series_9(float r[3][3],
const float m1[3][3],
@ -569,13 +578,15 @@ void _va_mul_m3_series_9(float r[3][3],
const float m7[3][3],
const float m8[3][3])
{
mul_m3_m3m3(r, m1, m2);
mul_m3_m3m3(r, r, m3);
mul_m3_m3m3(r, r, m4);
mul_m3_m3m3(r, r, m5);
mul_m3_m3m3(r, r, m6);
mul_m3_m3m3(r, r, m7);
mul_m3_m3m3(r, r, m8);
float s[3][3];
float t[3][3];
mul_m3_m3m3(s, m1, m2);
mul_m3_m3m3(t, s, m3);
mul_m3_m3m3(s, t, m4);
mul_m3_m3m3(t, s, m5);
mul_m3_m3m3(s, t, m6);
mul_m3_m3m3(t, s, m7);
mul_m3_m3m3(r, t, m8);
}
/** \} */
@ -593,8 +604,9 @@ void _va_mul_m4_series_4(float r[4][4],
const float m2[4][4],
const float m3[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
float s[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(r, s, m3);
}
void _va_mul_m4_series_5(float r[4][4],
const float m1[4][4],
@ -602,9 +614,11 @@ void _va_mul_m4_series_5(float r[4][4],
const float m3[4][4],
const float m4[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
mul_m4_m4m4(r, r, m4);
float s[4][4];
float t[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(t, s, m3);
mul_m4_m4m4(r, t, m4);
}
void _va_mul_m4_series_6(float r[4][4],
const float m1[4][4],
@ -613,10 +627,12 @@ void _va_mul_m4_series_6(float r[4][4],
const float m4[4][4],
const float m5[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
mul_m4_m4m4(r, r, m4);
mul_m4_m4m4(r, r, m5);
float s[4][4];
float t[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(t, s, m3);
mul_m4_m4m4(s, t, m4);
mul_m4_m4m4(r, s, m5);
}
void _va_mul_m4_series_7(float r[4][4],
const float m1[4][4],
@ -626,11 +642,13 @@ void _va_mul_m4_series_7(float r[4][4],
const float m5[4][4],
const float m6[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
mul_m4_m4m4(r, r, m4);
mul_m4_m4m4(r, r, m5);
mul_m4_m4m4(r, r, m6);
float s[4][4];
float t[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(t, s, m3);
mul_m4_m4m4(s, t, m4);
mul_m4_m4m4(t, s, m5);
mul_m4_m4m4(r, t, m6);
}
void _va_mul_m4_series_8(float r[4][4],
const float m1[4][4],
@ -641,12 +659,14 @@ void _va_mul_m4_series_8(float r[4][4],
const float m6[4][4],
const float m7[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
mul_m4_m4m4(r, r, m4);
mul_m4_m4m4(r, r, m5);
mul_m4_m4m4(r, r, m6);
mul_m4_m4m4(r, r, m7);
float s[4][4];
float t[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(t, s, m3);
mul_m4_m4m4(s, t, m4);
mul_m4_m4m4(t, s, m5);
mul_m4_m4m4(s, t, m6);
mul_m4_m4m4(r, s, m7);
}
void _va_mul_m4_series_9(float r[4][4],
const float m1[4][4],
@ -658,13 +678,15 @@ void _va_mul_m4_series_9(float r[4][4],
const float m7[4][4],
const float m8[4][4])
{
mul_m4_m4m4(r, m1, m2);
mul_m4_m4m4(r, r, m3);
mul_m4_m4m4(r, r, m4);
mul_m4_m4m4(r, r, m5);
mul_m4_m4m4(r, r, m6);
mul_m4_m4m4(r, r, m7);
mul_m4_m4m4(r, r, m8);
float s[4][4];
float t[4][4];
mul_m4_m4m4(s, m1, m2);
mul_m4_m4m4(t, s, m3);
mul_m4_m4m4(s, t, m4);
mul_m4_m4m4(t, s, m5);
mul_m4_m4m4(s, t, m6);
mul_m4_m4m4(t, s, m7);
mul_m4_m4m4(r, t, m8);
}
/** \} */

View File

@ -54,11 +54,36 @@
#include "BLI_linklist.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#if !defined(__APPLE__)
/* The implementation for Apple lives in storage_apple.mm.*/
bool BLI_change_working_dir(const char *dir)
{
BLI_assert(BLI_thread_is_main());
if (!BLI_is_dir(dir)) {
return false;
}
# if defined(WIN32)
wchar_t wdir[FILE_MAX];
if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) {
return false;
}
return _wchdir(wdir) == 0;
# else
int result = chdir(dir);
if (result == 0) {
BLI_setenv("PWD", dir);
}
return result == 0;
# endif
}
char *BLI_current_working_dir(char *dir, const size_t maxncpy)
{
#if defined(WIN32)
# if defined(WIN32)
wchar_t path[MAX_PATH];
if (_wgetcwd(path, MAX_PATH)) {
if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
@ -66,7 +91,7 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
}
}
return NULL;
#else
# else
const char *pwd = BLI_getenv("PWD");
if (pwd) {
size_t srclen = BLI_strnlen(pwd, maxncpy);
@ -77,8 +102,9 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
return NULL;
}
return getcwd(dir, maxncpy);
#endif
# endif
}
#endif /* !defined (__APPLE__) */
double BLI_dir_free_space(const char *dir)
{

View File

@ -13,6 +13,7 @@
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
/* Extended file attribute used by OneDrive to mark placeholder files. */
static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen";
@ -185,3 +186,29 @@ const char *BLI_expand_tilde(const char *path_with_tilde)
}
return path_expanded;
}
char *BLI_current_working_dir(char *dir, const size_t maxncpy)
{
/* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/
static char path_expanded[PATH_MAX];
@autoreleasepool {
NSString *path = [[NSFileManager defaultManager] currentDirectoryPath];
const size_t length = maxncpy > PATH_MAX ? PATH_MAX : maxncpy;
[path getCString:path_expanded maxLength:length encoding:NSUTF8StringEncoding];
BLI_strncpy(dir, path_expanded, maxncpy);
return path_expanded;
}
}
bool BLI_change_working_dir(const char *dir)
{
@autoreleasepool {
NSString *path = [[NSString alloc] initWithUTF8String:dir];
if ([[NSFileManager defaultManager] changeCurrentDirectoryPath:path] == YES) {
return false;
}
else {
return true;
}
}
}

View File

@ -1,11 +1,27 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "BLI_fileops.hh"
#include "testing/testing.h"
#include "BLI_fileops.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_threads.h"
namespace blender::tests {
class ChangeWorkingDirectoryTest : public testing::Test {
public:
std::string test_temp_dir;
void TearDown() override
{
if (!test_temp_dir.empty()) {
BLI_delete(test_temp_dir.c_str(), true, false);
}
BLI_threadapi_exit();
}
};
TEST(fileops, fstream_open_string_filename)
{
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
@ -37,4 +53,43 @@ TEST(fileops, fstream_open_charptr_filename)
/* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
}
TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
{
/* Must use because BLI_change_working_dir() checks that we are on the main thread. */
BLI_threadapi_init();
char original_wd[FILE_MAX];
if (!BLI_current_working_dir(original_wd, FILE_MAX)) {
FAIL() << "unable to get the current working directory";
}
const std::string temp_file_name(std::tmpnam(nullptr));
test_temp_dir = temp_file_name + "овый";
if (BLI_exists(test_temp_dir.c_str())) {
BLI_delete(test_temp_dir.c_str(), true, false);
}
ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
<< "changing directory to a non-existent directory is expected to fail.";
ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
<< "temporary directory should have been created successfully.";
ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
<< "temporary directory should succeed changing directory.";
char cwd[FILE_MAX];
if (!BLI_current_working_dir(cwd, FILE_MAX)) {
FAIL() << "unable to get the current working directory";
}
ASSERT_EQ(BLI_path_cmp_normalized(cwd, test_temp_dir.c_str()), 0)
<< "the path of the current working directory should equal the path of the temporary "
"directory that was created.";
ASSERT_TRUE(BLI_change_working_dir(original_wd))
<< "changing directory back to the original working directory should succeed.";
}
} // namespace blender::tests

View File

@ -100,6 +100,40 @@ TEST(math_matrix, interp_m3_m3m3_singularity)
EXPECT_M3_NEAR(result, expect, 1e-5);
}
TEST(math_matrix, mul_m3_series)
{
float matrix[3][3] = {
{2.0f, 0.0f, 0.0f},
{0.0f, 3.0f, 0.0f},
{0.0f, 0.0f, 5.0f},
};
mul_m3_series(matrix, matrix, matrix, matrix);
float expect[3][3] = {
{8.0f, 0.0f, 0.0f},
{0.0f, 27.0f, 0.0f},
{0.0f, 0.0f, 125.0f},
};
EXPECT_M3_NEAR(matrix, expect, 1e-5);
}
TEST(math_matrix, mul_m4_series)
{
float matrix[4][4] = {
{2.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 3.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 5.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 7.0f},
};
mul_m4_series(matrix, matrix, matrix, matrix);
float expect[4][4] = {
{8.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 27.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 125.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 343.0f},
};
EXPECT_M4_NEAR(matrix, expect, 1e-5);
}
namespace blender::tests {
using namespace blender::math;

View File

@ -770,7 +770,11 @@ void blo_do_versions_userdef(UserDef *userdef)
/* Set GPU backend to OpenGL. */
if (!USER_VERSION_ATLEAST(305, 5)) {
#ifdef __APPLE__
userdef->gpu_backend = GPU_BACKEND_METAL;
#else
userdef->gpu_backend = GPU_BACKEND_OPENGL;
#endif
}
if (!USER_VERSION_ATLEAST(305, 10)) {

View File

@ -126,6 +126,10 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D"
#define BLT_I18NCONTEXT_EDITOR_FILEBROWSER "File browser"
/* Generic contexts. */
#define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality"
#define BLT_I18NCONTEXT_CONSTRAINT "Constraint"
/* Helper for bpy.app.i18n object... */
typedef struct {
const char *c_id;
@ -190,6 +194,8 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_FILEBROWSER, "editor_filebrowser"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \
{ \
NULL, NULL, NULL \
} \

View File

@ -1191,7 +1191,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
need_edgesel |= BM_ELEM_CD_GET_BOOL(l, edgesel_offset);
}
}
if (pin_layer_index) {
if (pin_layer_index >= 0) {
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
need_pin |= BM_ELEM_CD_GET_BOOL(l, pin_offset);
}

View File

@ -338,16 +338,12 @@ class ShadowPunctual : public NonCopyable, NonMovable {
float size_x_, size_y_;
/** Shape type. */
eLightType light_type_;
/** Random position on the light. In world space. */
float3 random_offset_;
/** Light position. */
float3 position_;
/** Near and far clip distances. */
float far_, near_;
/** Number of tile-maps needed to cover the light angular extents. */
int tilemaps_needed_;
/** Visibility cone angle from the light source. */
int cone_aperture_;
public:
ShadowPunctual(ShadowModule &module) : shadows_(module){};

View File

@ -164,7 +164,7 @@ DEF_ICON(NLA)
DEF_ICON(PREFERENCES)
DEF_ICON(TIME)
DEF_ICON(NODETREE)
DEF_ICON(GEOMETRY_NODES)
DEF_ICON_MODIFIER(GEOMETRY_NODES)
DEF_ICON(CONSOLE)
DEF_ICON_BLANK(183)
DEF_ICON(TRACKER)

View File

@ -3446,55 +3446,63 @@ static char *vertex_group_lock_description(bContext * /*C*/,
int action = RNA_enum_get(params, "action");
int mask = RNA_enum_get(params, "mask");
const char *action_str, *target_str;
/* NOTE: constructing the following string literals can be done in a less verbose way,
* however the resulting strings can't be usefully translated, (via `TIP_`). */
switch (action) {
case VGROUP_LOCK:
action_str = TIP_("Lock");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Lock all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Lock selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Lock unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(
TIP_("Lock selected and unlock unselected vertex groups of the active object"));
}
break;
case VGROUP_UNLOCK:
action_str = TIP_("Unlock");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Unlock all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Unlock selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Unlock unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(
TIP_("Unlock selected and lock unselected vertex groups of the active object"));
}
break;
case VGROUP_TOGGLE:
action_str = TIP_("Toggle locks of");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Toggle locks of all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
return BLI_strdup(TIP_("Toggle locks of selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Toggle locks of unselected vertex groups of the active object"));
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(TIP_(
"Toggle locks of all and invert unselected vertex groups of the active object"));
}
break;
case VGROUP_INVERT:
action_str = TIP_("Invert locks of");
break;
default:
return nullptr;
}
switch (mask) {
case VGROUP_MASK_ALL:
target_str = TIP_("all");
break;
case VGROUP_MASK_SELECTED:
target_str = TIP_("selected");
break;
case VGROUP_MASK_UNSELECTED:
target_str = TIP_("unselected");
break;
case VGROUP_MASK_INVERT_UNSELECTED:
switch (action) {
case VGROUP_INVERT:
target_str = TIP_("selected");
break;
case VGROUP_LOCK:
target_str = TIP_("selected and unlock unselected");
break;
case VGROUP_UNLOCK:
target_str = TIP_("selected and lock unselected");
break;
default:
target_str = TIP_("all and invert unselected");
switch (mask) {
case VGROUP_MASK_ALL:
return BLI_strdup(TIP_("Invert locks of all vertex groups of the active object"));
case VGROUP_MASK_SELECTED:
case VGROUP_MASK_INVERT_UNSELECTED:
return BLI_strdup(TIP_("Invert locks of selected vertex groups of the active object"));
case VGROUP_MASK_UNSELECTED:
return BLI_strdup(TIP_("Invert locks of unselected vertex groups of the active object"));
}
break;
default:
return nullptr;
}
return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str);
return nullptr;
}
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot)

View File

@ -195,7 +195,7 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY &&
if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && brush &&
sculpt_automasking_is_constrained_by_radius(brush)) {
return true;
}

View File

@ -796,6 +796,40 @@ static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/)
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
static void sculpt_mesh_filter_cancel(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
PBVHNode **nodes;
int nodes_num;
if (!ss || !ss->pbvh) {
return;
}
/* Gather all PBVH leaf nodes. */
BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &nodes_num);
for (int i : IndexRange(nodes_num)) {
PBVHNode *node = nodes[i];
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, ob, nodes[i], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
copy_v3_v3(vd.co, orig_data.co);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update(node);
}
BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB);
}
static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);

View File

@ -26,6 +26,8 @@
#include "WM_api.h"
#include "NOD_add_node_search.hh"
#include "ED_asset.h"
#include "ED_node.h"
@ -36,12 +38,9 @@ struct bContext;
namespace blender::ed::space_node {
struct AddNodeItem {
std::string ui_name;
nodes::AddNodeInfo info;
std::string identifier;
std::string description;
std::optional<AssetHandle> asset;
std::function<void(const bContext &, bNodeTree &, bNode &)> after_add_fn;
int weight = 0;
};
struct AddNodeSearchStorage {
@ -77,11 +76,11 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree,
}
AddNodeItem item{};
item.ui_name = ED_asset_handle_get_name(&asset);
item.info.ui_name = ED_asset_handle_get_name(&asset);
item.identifier = node_tree.typeinfo->group_idname;
item.description = asset_data.description == nullptr ? "" : asset_data.description;
item.info.description = asset_data.description == nullptr ? "" : asset_data.description;
item.asset = asset;
item.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) {
item.info.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.flag &= ~NODE_OPTIONS;
node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, asset);
@ -139,9 +138,9 @@ static void gather_search_items_for_node_groups(const bContext &C,
continue;
}
AddNodeItem item{};
item.ui_name = node_group->id.name + 2;
item.info.ui_name = node_group->id.name + 2;
item.identifier = node_tree.typeinfo->group_idname;
item.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) {
item.info.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) {
Main &bmain = *CTX_data_main(&C);
node.id = &node_group->id;
id_us_plus(node.id);
@ -161,19 +160,18 @@ static void gather_add_node_operations(const bContext &C,
if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
continue;
}
if (StringRefNull(node_tree.typeinfo->group_idname) == node_type->idname) {
/* Skip the empty group type. */
if (!node_type->gather_add_node_search_ops) {
continue;
}
if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) {
continue;
Vector<nodes::AddNodeInfo> info_items;
nodes::GatherAddNodeSearchParams params(*node_type, node_tree, info_items);
node_type->gather_add_node_search_ops(params);
for (nodes::AddNodeInfo &info : info_items) {
AddNodeItem item{};
item.info = std::move(info);
item.identifier = node_type->idname;
r_search_items.append(item);
}
AddNodeItem item{};
item.ui_name = IFACE_(node_type->ui_name);
item.identifier = node_type->idname;
item.description = TIP_(node_type->ui_description);
r_search_items.append(std::move(item));
}
NODE_TYPES_END;
@ -199,7 +197,7 @@ static void add_node_search_update_fn(
StringSearch *search = BLI_string_search_new();
for (AddNodeItem &item : storage.search_add_items) {
BLI_string_search_add(search, item.ui_name.c_str(), &item, item.weight);
BLI_string_search_add(search, item.info.ui_name.c_str(), &item, item.info.weight);
}
/* Don't filter when the menu is first opened, but still run the search
@ -210,7 +208,7 @@ static void add_node_search_update_fn(
for (const int i : IndexRange(filtered_amount)) {
AddNodeItem &item = *filtered_items[i];
if (!UI_search_item_add(items, item.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
if (!UI_search_item_add(items, item.info.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
break;
}
}
@ -234,8 +232,8 @@ static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2)
bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str());
BLI_assert(new_node != nullptr);
if (item->after_add_fn) {
item->after_add_fn(*C, node_tree, *new_node);
if (item->info.after_add_fn) {
item->info.after_add_fn(*C, node_tree, *new_node);
}
new_node->locx = storage.cursor.x / UI_DPI_FAC;
@ -266,7 +264,7 @@ static ARegion *add_node_search_tooltip_fn(
uiSearchItemTooltipData tooltip_data{};
BLI_strncpy(tooltip_data.description,
item->asset ? item->description.c_str() : TIP_(item->description.c_str()),
item->asset ? item->info.description.c_str() : TIP_(item->info.description.c_str()),
sizeof(tooltip_data.description));
return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);

View File

@ -993,7 +993,9 @@ static void node_group_make_insert_selected(const bContext &C,
}
}
bke::node_field_inferencing::update_field_inferencing(group);
if (group.type == NTREE_GEOMETRY) {
bke::node_field_inferencing::update_field_inferencing(group);
}
nodes::update_node_declaration_and_sockets(ntree, *gnode);
/* Add new links to inputs outside of the group. */

View File

@ -1363,8 +1363,84 @@ static void rotation_set_fn(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, co
ggd->rotation = *(const float *)value;
}
static void gizmo_3d_setup_default_matrix(wmGizmo *axis, const int axis_idx)
{
float matrix[3][3];
switch (axis_idx) {
/* Arrow. */
case MAN_AXIS_TRANS_X:
case MAN_AXIS_SCALE_X:
case MAN_AXIS_ROT_X:
copy_v3_fl3(matrix[0], 0.0f, -1.0f, 0.0f);
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
copy_v3_fl3(matrix[2], 1.0f, 0.0f, 0.0f);
break;
case MAN_AXIS_TRANS_Y:
case MAN_AXIS_SCALE_Y:
case MAN_AXIS_ROT_Y:
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
copy_v3_fl3(matrix[2], 0.0f, 1.0f, 0.0f);
break;
case MAN_AXIS_TRANS_Z:
case MAN_AXIS_SCALE_Z:
case MAN_AXIS_ROT_Z:
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
copy_v3_fl3(matrix[1], 0.0f, 1.0f, 0.0f);
copy_v3_fl3(matrix[2], 0.0f, 0.0f, 1.0f);
break;
case MAN_AXIS_TRANS_XY:
case MAN_AXIS_SCALE_XY:
copy_v3_fl3(matrix[0], MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
copy_v3_fl3(matrix[1], 0.0f, MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
copy_v3_fl3(matrix[2], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
break;
case MAN_AXIS_TRANS_YZ:
case MAN_AXIS_SCALE_YZ:
copy_v3_fl3(matrix[0], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
copy_v3_fl3(matrix[1], 0.0f, MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
copy_v3_fl3(matrix[2], -MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
break;
case MAN_AXIS_TRANS_ZX:
case MAN_AXIS_SCALE_ZX:
copy_v3_fl3(matrix[0], MAN_AXIS_SCALE_PLANE_SCALE, 0.0f, 0.0f);
copy_v3_fl3(matrix[1], 0.0f, 0.0f, MAN_AXIS_SCALE_PLANE_SCALE);
copy_v3_fl3(matrix[2], 0.0f, -MAN_AXIS_SCALE_PLANE_SCALE, 0.0f);
break;
case MAN_AXIS_TRANS_C:
case MAN_AXIS_SCALE_C:
case MAN_AXIS_ROT_C:
case MAN_AXIS_ROT_T:
default:
return;
}
copy_m4_m3(axis->matrix_offset, matrix);
switch (axis_idx) {
case MAN_AXIS_TRANS_XY:
case MAN_AXIS_TRANS_YZ:
case MAN_AXIS_TRANS_ZX:
case MAN_AXIS_SCALE_XY:
case MAN_AXIS_SCALE_YZ:
case MAN_AXIS_SCALE_ZX: {
float offs[3];
add_v3_v3v3(offs, axis->matrix_offset[0], axis->matrix_offset[1]);
mul_v3_fl(offs, MAN_AXIS_SCALE_PLANE_OFFSET);
WM_gizmo_set_matrix_offset_location(axis, offs);
} break;
default:
return;
}
}
static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx)
{
gizmo_3d_setup_default_matrix(axis, axis_idx);
switch (axis_idx) {
/* Arrow. */
case MAN_AXIS_TRANS_X:
@ -1385,15 +1461,10 @@ static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx)
case MAN_AXIS_TRANS_ZX:
case MAN_AXIS_SCALE_XY:
case MAN_AXIS_SCALE_YZ:
case MAN_AXIS_SCALE_ZX: {
case MAN_AXIS_SCALE_ZX:
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_PLANE);
const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE);
WM_gizmo_set_matrix_offset_location(axis, ofs);
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
break;
}
/* Dial. */
case MAN_AXIS_TRANS_C:
@ -1489,12 +1560,12 @@ static void gizmo_3d_setup_draw_from_twtype(wmGizmo *axis, const int axis_idx, c
case MAN_AXIS_SCALE_X:
case MAN_AXIS_SCALE_Y:
case MAN_AXIS_SCALE_Z: {
float start_co[3] = {0.0f, 0.0f, 0.0f};
float start;
float end;
gizmo_line_range(twtype, axis_type, &start_co[2], &end);
gizmo_line_range(twtype, axis_type, &start, &end);
madd_v3_v3fl(axis->matrix_offset[3], axis->matrix_offset[2], start);
WM_gizmo_set_matrix_offset_location(axis, start_co);
RNA_float_set(axis->ptr, "length", end - start_co[2]);
RNA_float_set(axis->ptr, "length", end - start);
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
break;
}
@ -1803,61 +1874,49 @@ static void gizmo_refresh_from_matrix(wmGizmo *axis,
const short axis_type = gizmo_get_axis_type(axis_idx);
const int aidx_norm = gizmo_orientation_axis(axis_idx, nullptr);
WM_gizmo_set_matrix_location(axis, twmat[3]);
switch (axis_idx) {
case MAN_AXIS_TRANS_X:
case MAN_AXIS_TRANS_Y:
case MAN_AXIS_TRANS_Z:
case MAN_AXIS_SCALE_X:
case MAN_AXIS_SCALE_Y:
case MAN_AXIS_SCALE_Z: {
const float *z_axis = twmat[aidx_norm];
if (axis_type == MAN_AXES_SCALE) {
/* Scale handles are cubes that don't look right when not aligned with other axes.
* This is noticeable when the axis is rotated to something besides the global-axis. */
const int aidx_norm_y = (aidx_norm + 2) % 3;
const float *y_axis = twmat[aidx_norm_y];
WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
if (scale) {
float start, end;
gizmo_line_range(twtype, axis_type, &start, &end);
RNA_float_set(axis->ptr, "length", (end * scale[aidx_norm]) - start);
}
}
else {
WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis);
}
break;
}
case MAN_AXIS_ROT_X:
case MAN_AXIS_ROT_Y:
case MAN_AXIS_ROT_Z:
WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]);
break;
case MAN_AXIS_TRANS_XY:
case MAN_AXIS_TRANS_YZ:
case MAN_AXIS_TRANS_ZX:
case MAN_AXIS_SCALE_XY:
case MAN_AXIS_SCALE_YZ:
case MAN_AXIS_SCALE_ZX: {
const int aidx_norm_x = (aidx_norm + 1) % 3;
const int aidx_norm_y = (aidx_norm + 2) % 3;
const float *y_axis = twmat[aidx_norm_y];
const float *z_axis = twmat[aidx_norm];
WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
if (axis_type == MAN_AXES_SCALE) {
float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f};
if (scale) {
ofs[0] *= scale[aidx_norm_x];
ofs[1] *= scale[aidx_norm_y];
}
WM_gizmo_set_matrix_offset_location(axis, ofs);
case MAN_AXIS_SCALE_ZX:
copy_m4_m4(axis->matrix_basis, twmat);
if (scale) {
float offs[3];
add_v3_v3v3(offs, axis->matrix_offset[0], axis->matrix_offset[1]);
mul_v3_fl(offs, MAN_AXIS_SCALE_PLANE_OFFSET);
mul_v3_v3(offs, scale);
WM_gizmo_set_matrix_offset_location(axis, offs);
}
break;
}
case MAN_AXIS_SCALE_X:
case MAN_AXIS_SCALE_Y:
case MAN_AXIS_SCALE_Z:
copy_m4_m4(axis->matrix_basis, twmat);
if (scale) {
float start, end;
gizmo_line_range(twtype, axis_type, &start, &end);
RNA_float_set(axis->ptr, "length", (end - start) * scale[aidx_norm]);
}
break;
case MAN_AXIS_TRANS_X:
case MAN_AXIS_TRANS_Y:
case MAN_AXIS_TRANS_Z:
copy_m4_m4(axis->matrix_basis, twmat);
break;
case MAN_AXIS_ROT_X:
case MAN_AXIS_ROT_Y:
case MAN_AXIS_ROT_Z:
copy_m4_m4(axis->matrix_basis, twmat);
orthogonalize_m4(axis->matrix_basis, aidx_norm);
break;
case MAN_AXIS_SCALE_C:
case MAN_AXIS_ROT_C:
case MAN_AXIS_ROT_T:
default:
WM_gizmo_set_matrix_location(axis, twmat[3]);
break;
}
}
@ -2076,12 +2135,16 @@ static void gizmo_3d_draw_invoke(wmGizmoGroup *gzgroup,
}
if (axis == axis_active) {
if (axis_active_type == MAN_AXES_ROTATE) {
gizmo_3d_dial_matrixbasis_calc(region,
axis_active->matrix_basis[2],
axis_active->matrix_basis[3],
mval,
axis_active->matrix_basis);
if (axis_active_type == MAN_AXES_ROTATE && axis_idx_active != MAN_AXIS_ROT_T) {
float mat[3][3];
mul_m3_m4m4(mat, axis_active->matrix_basis, axis_active->matrix_offset);
gizmo_3d_dial_matrixbasis_calc(
region, mat[2], axis_active->matrix_basis[3], mval, axis_active->matrix_offset);
copy_m3_m4(mat, axis_active->matrix_basis);
invert_m3(mat);
mul_m4_m3m4(axis_active->matrix_offset, mat, axis_active->matrix_offset);
zero_v3(axis_active->matrix_offset[3]);
}
gizmo_3d_setup_draw_modal(axis_active, axis_idx);

View File

@ -1754,9 +1754,8 @@ void MTLFrameBuffer::blit(uint read_slot,
uint height,
eGPUFrameBufferBits blit_buffers)
{
BLI_assert(this);
BLI_assert(metal_fb_write);
if (!(this && metal_fb_write)) {
if (!metal_fb_write) {
return;
}
MTLContext *mtl_context = reinterpret_cast<MTLContext *>(GPU_context_active_get());
@ -1899,4 +1898,4 @@ int MTLFrameBuffer::get_height()
return height_;
}
} // blender::gpu
} // namespace blender::gpu

View File

@ -1190,11 +1190,11 @@ static void rna_ParticleTarget_name_get(PointerRNA *ptr, char *str)
}
}
else {
strcpy(str, "Invalid target!");
strcpy(str, TIP_("Invalid target!"));
}
}
else {
strcpy(str, "Invalid target!");
strcpy(str, TIP_("Invalid target!"));
}
}

View File

@ -39,6 +39,7 @@ set(INC
set(SRC
intern/add_node_search.cc
intern/derived_node_tree.cc
intern/geometry_nodes_lazy_function.cc
intern/geometry_nodes_log.cc
@ -54,6 +55,7 @@ set(SRC
intern/node_util.cc
intern/socket_search_link.cc
NOD_add_node_search.hh
NOD_common.h
NOD_composite.h
NOD_derived_node_tree.hh

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <functional>
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_node_types.h" /* Necessary for eNodeSocketInOut. */
#include "NOD_node_declaration.hh"
struct bContext;
namespace blender::nodes {
struct AddNodeInfo {
using AfterAddFn = std::function<void(const bContext &C, bNodeTree &node_tree, bNode &node)>;
std::string ui_name;
std::string description;
AfterAddFn after_add_fn;
int weight = 0;
};
class GatherAddNodeSearchParams {
const bNodeType &node_type_;
const bNodeTree &node_tree_;
Vector<AddNodeInfo> &r_items;
public:
GatherAddNodeSearchParams(const bNodeType &node_type,
const bNodeTree &node_tree,
Vector<AddNodeInfo> &r_items)
: node_type_(node_type), node_tree_(node_tree), r_items(r_items)
{
}
const bNodeTree &node_tree() const
{
return node_tree_;
}
const bNodeType &node_type() const
{
return node_type_;
}
/**
* \param weight: Used to customize the order when multiple search items match.
*/
void add_item(std::string ui_name,
std::string description,
AddNodeInfo::AfterAddFn fn = {},
int weight = 0);
};
void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams &params);
} // namespace blender::nodes

View File

@ -7,6 +7,7 @@
#include "BKE_node_runtime.hh"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "node_composite_util.hh"
@ -35,4 +36,5 @@ void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short ncla
ntype->updatefunc = cmp_node_update_default;
ntype->insert_link = node_insert_link_default;
ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
}

View File

@ -428,6 +428,7 @@ void register_node_type_cmp_cryptomatte_legacy()
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
ntype.realtime_compositor_unsupported_message = N_(
"Node not supported in the Viewport compositor");

View File

@ -58,6 +58,7 @@ void register_node_type_cmp_sephsva()
&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
@ -112,6 +113,7 @@ void register_node_type_cmp_combhsva()
&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);

View File

@ -58,6 +58,7 @@ void register_node_type_cmp_seprgba()
&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
@ -112,6 +113,7 @@ void register_node_type_cmp_combrgba()
&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);

View File

@ -86,6 +86,7 @@ void register_node_type_cmp_sepycca()
ntype.declare = file_ns::cmp_node_sepycca_declare;
ntype.initfunc = file_ns::node_composit_init_mode_sepycca;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
@ -174,6 +175,7 @@ void register_node_type_cmp_combycca()
ntype.declare = file_ns::cmp_node_combycca_declare;
ntype.initfunc = file_ns::node_composit_init_mode_combycca;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);

View File

@ -58,6 +58,7 @@ void register_node_type_cmp_sepyuva()
&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
@ -112,6 +113,7 @@ void register_node_type_cmp_combyuva()
&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA (Legacy)", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);

View File

@ -3,6 +3,7 @@
#include "node_function_util.hh"
#include "node_util.h"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
static bool fn_node_poll_default(const bNodeType * /*ntype*/,
@ -23,4 +24,5 @@ void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclas
ntype->poll = fn_node_poll_default;
ntype->insert_link = node_insert_link_default;
ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
}

View File

@ -10,6 +10,7 @@
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
namespace blender::nodes {
@ -58,4 +59,5 @@ void geo_node_type_base(bNodeType *ntype, int type, const char *name, short ncla
ntype->poll = geo_node_poll_default;
ntype->insert_link = node_insert_link_default;
ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
}

View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_node.h"
#include "BLT_translation.h"
#include "NOD_add_node_search.hh"
#include "NOD_node_declaration.hh"
namespace blender::nodes {
void GatherAddNodeSearchParams::add_item(std::string ui_name,
std::string description,
AddNodeInfo::AfterAddFn fn,
int weight)
{
r_items.append(AddNodeInfo{std::move(ui_name), std::move(description), std::move(fn), weight});
}
void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams &params)
{
params.add_item(IFACE_(params.node_type().ui_name), TIP_(params.node_type().ui_description));
}
} // namespace blender::nodes

View File

@ -11,6 +11,7 @@
#include "node_shader_util.hh"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "node_exec.h"
@ -44,6 +45,7 @@ void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, shor
ntype->poll = sh_node_poll_default;
ntype->insert_link = node_insert_link_default;
ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
}
void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
@ -51,6 +53,7 @@ void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nc
sh_node_type_base(ntype, type, name, nclass);
ntype->poll = sh_fn_poll_default;
ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
}
/* ****** */

View File

@ -12,7 +12,9 @@
#include "node_shader_util.hh"
#include "NOD_add_node_search.hh"
#include "NOD_socket_search_link.hh"
#include "RNA_enum_types.h"
namespace blender::nodes::node_sh_mix_cc {
@ -223,6 +225,16 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
static void gather_add_node_searches(GatherAddNodeSearchParams &params)
{
params.add_item(IFACE_("Mix"), params.node_type().ui_description);
params.add_item(IFACE_("Mix Color"),
params.node_type().ui_description,
[](const bContext & /*C*/, bNodeTree & /*node_tree*/, bNode &node) {
node_storage(node).data_type = SOCK_RGBA;
});
}
static void node_mix_init(bNodeTree * /*tree*/, bNode *node)
{
NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__);
@ -497,5 +509,6 @@ void register_node_type_sh_mix()
ntype.draw_buttons = file_ns::sh_node_mix_layout;
ntype.labelfunc = file_ns::sh_node_mix_label;
ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
ntype.gather_add_node_search_ops = file_ns::gather_add_node_searches;
nodeRegisterType(&ntype);
}

View File

@ -156,5 +156,6 @@ void register_node_type_sh_mix_rgb()
ntype.gpu_fn = file_ns::gpu_shader_mix_rgb;
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
nodeRegisterType(&ntype);
}

View File

@ -40,6 +40,7 @@ void register_node_type_sh_sephsv()
ntype.declare = file_ns::node_declare_sephsv;
ntype.gpu_fn = file_ns::gpu_shader_sephsv;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@ -77,6 +78,7 @@ void register_node_type_sh_combhsv()
ntype.declare = file_ns::node_declare_combhsv;
ntype.gpu_fn = file_ns::gpu_shader_combhsv;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
nodeRegisterType(&ntype);
}

View File

@ -80,6 +80,7 @@ void register_node_type_sh_seprgb()
ntype.gpu_fn = file_ns::gpu_shader_seprgb;
ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@ -125,6 +126,7 @@ void register_node_type_sh_combrgb()
ntype.gpu_fn = file_ns::gpu_shader_combrgb;
ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function;
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
nodeRegisterType(&ntype);
}

View File

@ -36,6 +36,7 @@ void register_node_type_sh_squeeze()
sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value (Legacy)", NODE_CLASS_CONVERTER);
ntype.gather_link_search_ops = nullptr;
ntype.gather_add_node_search_ops = nullptr;
ntype.declare = file_ns::node_declare;
ntype.gpu_fn = file_ns::gpu_shader_squeeze;

View File

@ -933,6 +933,49 @@ if(WITH_CODEC_FFMPEG)
)
endif()
if(NOT OPENIMAGEIO_IDIFF)
message(STATUS "Disabling ImBuf image format tests because OIIO idiff does not exist")
else()
SET(OPTIONAL_FORMATS "")
if(WITH_IMAGE_CINEON)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} CINEON")
endif()
if(WITH_IMAGE_HDR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} HDR")
endif()
if(WITH_IMAGE_OPENEXR)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENEXR")
endif()
if(WITH_IMAGE_OPENJPEG)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENJPEG")
endif()
if(WITH_IMAGE_TIFF)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} TIFF")
endif()
if(WITH_IMAGE_WEBP)
set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} WEBP")
endif()
add_blender_test(
bf_imbuf_save
--python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_save.py
--
-test_dir "${TEST_SRC_DIR}/imbuf_io"
-output_dir "${TEST_OUT_DIR}/imbuf_io/save"
-idiff "${OPENIMAGEIO_IDIFF}"
-optional_formats "${OPTIONAL_FORMATS}"
)
add_blender_test(
bf_imbuf_load
--python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_load.py
--
-test_dir "${TEST_SRC_DIR}/imbuf_io"
-output_dir "${TEST_OUT_DIR}/imbuf_io/load"
-idiff "${OPENIMAGEIO_IDIFF}"
-optional_formats "${OPTIONAL_FORMATS}"
)
endif()
# ------------------------------------------------------------------------------
# SEQUENCER RENDER TESTS

View File

@ -0,0 +1,179 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import sys
import unittest
import bpy
sys.path.append(str(pathlib.Path(__file__).parent.absolute()))
from modules.colored_print import print_message
from modules.imbuf_test import AbstractImBufTest
args = None
class ImBufTest(AbstractImBufTest):
@classmethod
def setUpClass(cls):
AbstractImBufTest.init(args)
if cls.update:
os.makedirs(cls.reference_load_dir, exist_ok=True)
def _get_image_files(self, file_pattern):
return [f for f in pathlib.Path(self.reference_dir).glob(file_pattern)]
def _validate_metadata(self, img, ref_metadata_path, out_metadata_path):
channels = img.channels
is_float = img.is_float
colorspace = img.colorspace_settings.name
alpha_mode = img.alpha_mode
actual_metadata = f"{channels=} {is_float=} {colorspace=} {alpha_mode=}"
# Save actual metadata
out_metadata_path.write_text(actual_metadata, encoding="utf-8")
if ref_metadata_path.exists():
# Compare with expected
try:
expected_metadata = ref_metadata_path.read_text(encoding="utf-8")
failed = not (actual_metadata == expected_metadata)
except BaseException as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
failed = True
else:
if not self.update:
return False
failed = True
if failed and self.update:
# Update reference if requested.
ref_metadata_path.write_text(actual_metadata, encoding="utf-8")
failed = False
return not failed
def _save_exr(self, img, out_exr_path):
scene = bpy.data.scenes[0]
image_settings = scene.render.image_settings
image_settings.file_format = "OPEN_EXR"
image_settings.color_mode = "RGBA"
image_settings.color_depth = "32"
image_settings.exr_codec = "ZIP"
img.save_render(str(out_exr_path), scene=scene)
def _validate_pixels(self, img, ref_exr_path, out_exr_path):
self._save_exr(img, out_exr_path)
return self.call_idiff(ref_exr_path, out_exr_path)
def check(self, file_pattern):
image_files = self._get_image_files(file_pattern)
if len(image_files) == 0:
self.fail(f"No images found for pattern {file_pattern}")
for image_path in image_files:
print_message(image_path.name, 'SUCCESS', 'RUN')
# Load the image under test
bpy.ops.image.open(filepath=str(image_path))
img = bpy.data.images[image_path.name]
# Compare the image with our exr/metadata references
exr_filename = image_path.with_suffix(".exr").name
metadata_filename = image_path.with_suffix(".txt").name
ref_exr_path = self.reference_load_dir.joinpath(exr_filename)
ref_metadata_path = self.reference_load_dir.joinpath(metadata_filename)
out_exr_path = self.output_dir.joinpath(exr_filename)
out_metadata_path = self.output_dir.joinpath(metadata_filename)
res1 = self._validate_metadata(img, ref_metadata_path, out_metadata_path)
res2 = self._validate_pixels(img, ref_exr_path, out_exr_path)
if not res1 or not res2:
self.errors += 1
print_message("Results are different from reference data")
print_message(image_path.name, 'FAILURE', 'FAILED')
else:
print_message(image_path.name, 'SUCCESS', 'OK')
class ImBufLoadTest(ImBufTest):
def test_load_bmp(self):
self.check("*.bmp")
def test_load_png(self):
self.check("*.png")
def test_load_exr(self):
self.skip_if_format_missing("OPENEXR")
self.check("*.exr")
def test_load_hdr(self):
self.skip_if_format_missing("HDR")
self.check("*.hdr")
def test_load_targa(self):
self.check("*.tga")
def test_load_tiff(self):
self.skip_if_format_missing("TIFF")
self.check("*.tif")
def test_load_jpeg(self):
self.check("*.jpg")
def test_load_jpeg2000(self):
self.skip_if_format_missing("OPENJPEG")
self.check("*.jp2")
self.check("*.j2c")
def test_load_dpx(self):
self.skip_if_format_missing("CINEON")
self.check("*.dpx")
def test_load_cineon(self):
self.skip_if_format_missing("CINEON")
self.check("*.cin")
def test_load_webp(self):
self.skip_if_format_missing("WEBP")
self.check("*.webp")
def main():
global args
import argparse
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
else:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('-test_dir', required=True, type=pathlib.Path)
parser.add_argument('-output_dir', required=True, type=pathlib.Path)
parser.add_argument('-idiff', required=True, type=pathlib.Path)
parser.add_argument('-optional_formats', required=True)
args, remaining = parser.parse_known_args(argv)
unittest.main(argv=remaining)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,273 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import sys
import unittest
import bpy
sys.path.append(str(pathlib.Path(__file__).parent.absolute()))
from modules.colored_print import print_message
from modules.imbuf_test import AbstractImBufTest
args = None
TEMPLATE_RGBA08 = "template-rgba08.png"
TEMPLATE_RGBA32 = "template-rgba32.exr"
class ImBufTest(AbstractImBufTest):
@classmethod
def setUpClass(cls):
AbstractImBufTest.init(args)
if cls.update:
os.makedirs(cls.reference_dir, exist_ok=True)
def _load_template_image(self, name, template_name):
image_path = str(self.test_dir.joinpath(template_name))
bpy.ops.image.open(filepath=image_path)
img = bpy.data.images[template_name]
img.name = name
return img
def _setup_image(self, src, ext, settings):
scene = bpy.data.scenes[0]
image_settings = scene.render.image_settings
# Make an appropriate filename which embeds all relevant settings and
# set the file output parameters for the exact configuration we want
name = ""
for s in settings:
if s == "color_depth":
name += str(settings[s]).rjust(2, '0') + "-"
else:
name += str(settings[s]) + "-"
setattr(image_settings, s, settings[s])
image_name = name[:-1].lower() + "__from__" + src + "." + ext
return image_name
def _save_image(self, src, image_name):
loaders = {
"rgba08": lambda name: self._load_template_image(name, TEMPLATE_RGBA08),
"rgba32": lambda name: self._load_template_image(name, TEMPLATE_RGBA32),
}
# Load the template image and assign it the image name
img = loaders[src](image_name)
# Save the image in the desired format with the desired settings
scene = bpy.data.scenes[0]
ref_image_path = self.reference_dir.joinpath(img.name)
out_image_path = self.output_dir.joinpath(img.name)
img.save_render(str(out_image_path), scene=scene)
# Completely remove image in case it was modified during save
img.user_clear()
bpy.data.images.remove(img)
return ref_image_path, out_image_path
def _validate(self, ref_image_path, out_image_path):
return self.call_idiff(ref_image_path, out_image_path)
def check(self, src, ext, settings):
image_name = self._setup_image(src, ext, settings)
print_message(image_name, 'SUCCESS', 'RUN')
ref_image_path, out_image_path = self._save_image(src, image_name)
if not self._validate(ref_image_path, out_image_path):
self.errors += 1
print_message("Save result is different from reference image")
print_message(ref_image_path.name, 'FAILURE', 'FAILED')
else:
print_message(ref_image_path.name, 'SUCCESS', 'OK')
# autopep8: off
class ImBufSaveTest(ImBufTest):
def test_save_bmp(self):
self.check(src="rgba08", ext="bmp", settings={"file_format": "BMP", "color_mode": "BW"})
self.check(src="rgba08", ext="bmp", settings={"file_format": "BMP", "color_mode": "RGB"})
self.check(src="rgba32", ext="bmp", settings={"file_format": "BMP", "color_mode": "BW"})
self.check(src="rgba32", ext="bmp", settings={"file_format": "BMP", "color_mode": "RGB"})
def test_save_png(self):
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "8", "compression": 15})
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "8", "compression": 15})
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "8", "compression": 15})
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "16", "compression": 25})
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "16", "compression": 25})
self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "16", "compression": 25})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "8", "compression": 15})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "8", "compression": 15})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "8", "compression": 15})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "16", "compression": 25})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "16", "compression": 25})
self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "16", "compression": 25})
def test_save_exr(self):
self.skip_if_format_missing("OPENEXR")
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"})
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"})
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"})
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"})
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"})
self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"})
self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"})
def test_save_hdr(self):
self.skip_if_format_missing("HDR")
self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "BW"})
self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "RGB"})
self.check(src="rgba32", ext="hdr", settings={"file_format": "HDR", "color_mode": "BW"})
self.check(src="rgba32", ext="hdr", settings={"file_format": "HDR", "color_mode": "RGB"})
def test_save_targa(self):
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "BW"})
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGB"})
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGBA"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "BW"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGB"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGBA"})
def test_save_targa_raw(self):
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "BW"})
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGB"})
self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGBA"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "BW"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGB"})
self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGBA"})
def test_save_tiff(self):
self.skip_if_format_missing("TIFF")
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "8", "tiff_codec": "DEFLATE"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "8", "tiff_codec": "LZW"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "8", "tiff_codec": "PACKBITS"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "16", "tiff_codec": "PACKBITS"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "16", "tiff_codec": "LZW"})
self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "16", "tiff_codec": "DEFLATE"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "8", "tiff_codec": "DEFLATE"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "8", "tiff_codec": "LZW"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "8", "tiff_codec": "PACKBITS"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "16", "tiff_codec": "PACKBITS"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "16", "tiff_codec": "LZW"})
self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "16", "tiff_codec": "DEFLATE"})
def test_save_jpeg(self):
self.check(src="rgba08", ext="jpg", settings={"file_format": "JPEG", "color_mode": "BW", "quality": 90})
self.check(src="rgba08", ext="jpg", settings={"file_format": "JPEG", "color_mode": "RGB", "quality": 90})
self.check(src="rgba32", ext="jpg", settings={"file_format": "JPEG", "color_mode": "BW", "quality": 70})
self.check(src="rgba32", ext="jpg", settings={"file_format": "JPEG", "color_mode": "RGB", "quality": 70})
def test_save_jpeg2000(self):
self.skip_if_format_missing("OPENJPEG")
# Is there a better combination of settings we can use so there's not so many?
# Is this a good mix?
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90})
self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70})
self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70})
# Note: The 'use_jpeg2k_cinema_preset' option mandates very large images
# self.check(src="rgba08", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70})
# self.check(src="rgba32", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": True, "use_jpeg2k_ycc": False, "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": True, "use_jpeg2k_ycc": False, "quality": 70})
self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": False, "use_jpeg2k_ycc": True, "quality": 70})
self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": False, "use_jpeg2k_ycc": True, "quality": 70})
def test_save_dpx(self):
self.skip_if_format_missing("CINEON")
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "8", "use_cineon_log": False})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "12", "use_cineon_log": False})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "16", "use_cineon_log": False})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "10", "use_cineon_log": True})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "12", "use_cineon_log": True})
self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "16", "use_cineon_log": True})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "8", "use_cineon_log": False})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "12", "use_cineon_log": False})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "16", "use_cineon_log": False})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "10", "use_cineon_log": True})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "12", "use_cineon_log": True})
self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "16", "use_cineon_log": True})
def test_save_cineon(self):
self.skip_if_format_missing("CINEON")
self.check(src="rgba08", ext="cin", settings={"file_format": "CINEON", "color_mode": "RGB"})
self.check(src="rgba32", ext="cin", settings={"file_format": "CINEON", "color_mode": "RGB"})
def test_save_webp(self):
self.skip_if_format_missing("WEBP")
self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 90})
self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 90})
# Note: These 2 variations are problematic on MacOS ARM64 (#105006)
# self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 70})
# self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 70})
# autopep8: on
def main():
global args
import argparse
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
else:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('-test_dir', required=True, type=pathlib.Path)
parser.add_argument('-output_dir', required=True, type=pathlib.Path)
parser.add_argument('-idiff', required=True, type=pathlib.Path)
parser.add_argument('-optional_formats', required=True)
args, remaining = parser.parse_known_args(argv)
unittest.main(argv=remaining)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import sys
class COLORS_ANSI:
RED = '\033[00;31m'
GREEN = '\033[00;32m'
ENDC = '\033[0m'
class COLORS_NONE:
RED = ''
GREEN = ''
ENDC = ''
COLORS = COLORS_NONE
def use_message_colors():
global COLORS, COLORS_ANSI
COLORS = COLORS_ANSI
def print_message(message, type=None, status=''):
if type == 'SUCCESS':
print(COLORS.GREEN, end="")
elif type == 'FAILURE':
print(COLORS.RED, end="")
status_text = ...
if status == 'RUN':
status_text = " RUN "
elif status == 'OK':
status_text = " OK "
elif status == 'PASSED':
status_text = " PASSED "
elif status == 'FAILED':
status_text = " FAILED "
else:
status_text = status
if status_text:
print("[{}]" . format(status_text), end="")
print(COLORS.ENDC, end="")
print(" {}" . format(message))
sys.stdout.flush()

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import pathlib
import shutil
import subprocess
import unittest
from .colored_print import (print_message, use_message_colors)
class AbstractImBufTest(unittest.TestCase):
@classmethod
def init(cls, args):
cls.test_dir = pathlib.Path(args.test_dir)
cls.reference_dir = pathlib.Path(args.test_dir).joinpath("reference")
cls.reference_load_dir = pathlib.Path(args.test_dir).joinpath("reference_load")
cls.output_dir = pathlib.Path(args.output_dir)
cls.diff_dir = pathlib.Path(args.output_dir).joinpath("diff")
cls.idiff = pathlib.Path(args.idiff)
cls.optional_formats = args.optional_formats
os.makedirs(cls.diff_dir, exist_ok=True)
cls.errors = 0
cls.fail_threshold = 0.016
cls.fail_percent = 1
cls.verbose = os.environ.get("BLENDER_VERBOSE") is not None
cls.update = os.getenv('BLENDER_TEST_UPDATE') is not None
if os.environ.get("BLENDER_TEST_COLOR") is not None:
use_message_colors()
def setUp(self):
self.errors = 0
print_message("")
def tearDown(self):
if self.errors > 0:
self.fail("{} errors encountered" . format(self.errors))
def skip_if_format_missing(self, format):
if self.optional_formats.find(format) < 0:
self.skipTest("format not available")
def call_idiff(self, ref_path, out_path):
ref_filepath = str(ref_path)
out_filepath = str(out_path)
out_name = out_path.name
if os.path.exists(ref_filepath):
# Diff images test with threshold.
command = (
str(self.idiff),
"-fail", str(self.fail_threshold),
"-failpercent", str(self.fail_percent),
ref_filepath,
out_filepath,
)
try:
subprocess.check_output(command)
failed = False
except subprocess.CalledProcessError as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
failed = e.returncode != 1
else:
if not self.update:
return False
failed = True
if failed and self.update:
# Update reference image if requested.
shutil.copy(out_filepath, ref_filepath)
failed = False
# Generate diff image.
diff_img = str(self.diff_dir.joinpath(out_name + ".diff.png"))
command = (
str(self.idiff),
"-o", diff_img,
"-abs", "-scale", "16",
ref_filepath,
out_filepath
)
try:
subprocess.check_output(command)
except subprocess.CalledProcessError as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
return not failed

View File

@ -14,44 +14,7 @@ import sys
import time
from . import global_report
class COLORS_ANSI:
RED = '\033[00;31m'
GREEN = '\033[00;32m'
ENDC = '\033[0m'
class COLORS_DUMMY:
RED = ''
GREEN = ''
ENDC = ''
COLORS = COLORS_DUMMY
def print_message(message, type=None, status=''):
if type == 'SUCCESS':
print(COLORS.GREEN, end="")
elif type == 'FAILURE':
print(COLORS.RED, end="")
status_text = ...
if status == 'RUN':
status_text = " RUN "
elif status == 'OK':
status_text = " OK "
elif status == 'PASSED':
status_text = " PASSED "
elif status == 'FAILED':
status_text = " FAILED "
else:
status_text = status
if status_text:
print("[{}]" . format(status_text), end="")
print(COLORS.ENDC, end="")
print(" {}" . format(message))
sys.stdout.flush()
from .colored_print import (print_message, use_message_colors)
def blend_list(dirpath, device, blacklist):
@ -151,8 +114,7 @@ class Report:
self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
if os.environ.get("BLENDER_TEST_COLOR") is not None:
global COLORS, COLORS_ANSI
COLORS = COLORS_ANSI
use_message_colors()
self.failed_tests = ""
self.passed_tests = ""