Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-04-20 17:56:22 +02:00
commit 7107753f73
30 changed files with 566 additions and 134 deletions

View File

@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C.
Blender does have the functionality for poll functions to describe why they fail,
but its currently not used much, if you're interested to help improve the API
feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
where its not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into

View File

@ -350,7 +350,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
bl_label = "Available"
# poll - selected objects or selected object with animation data
def poll(ksi, context):
def poll(self, context):
ob = context.active_object
if ob:
# TODO: this fails if one animation-less object is active, but many others are selected
@ -366,14 +366,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
###############################
# All properties that are likely to get animated in a character rig
class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(useful when blocking out a shot)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
bl_label = "Whole Character"
class WholeCharacterMixin:
# these prefixes should be avoided, as they are not really bones
# that animators should be touching (or need to touch)
badBonePrefixes = (
@ -387,38 +380,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
)
# poll - pose-mode on active object only
def poll(ksi, context):
def poll(self, context):
return ((context.active_object) and (context.active_object.pose) and
(context.active_object.mode == 'POSE'))
# iterator - all bones regardless of selection
def iterator(ksi, context, ks):
def iterator(self, context, ks):
for bone in context.active_object.pose.bones:
if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
ksi.generate(context, ks, bone)
if not bone.name.startswith(self.badBonePrefixes):
self.generate(context, ks, bone)
# generator - all unlocked bone transforms + custom properties
def generate(ksi, context, ks, bone):
def generate(self, context, ks, bone):
# loc, rot, scale - only include unlocked ones
if not bone.bone.use_connect:
ksi.doLoc(ks, bone)
self.doLoc(ks, bone)
if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
ksi.doRot4d(ks, bone)
self.doRot4d(ks, bone)
else:
ksi.doRot3d(ks, bone)
ksi.doScale(ks, bone)
self.doRot3d(ks, bone)
self.doScale(ks, bone)
# bbone properties?
ksi.doBBone(context, ks, bone)
self.doBBone(context, ks, bone)
# custom props?
ksi.doCustomProps(ks, bone)
self.doCustomProps(ks, bone)
# ----------------
# helper to add some bone's property to the Keying Set
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
def addProp(self, ks, bone, prop, index=-1, use_groups=True):
# add the property name to the base path
id_path = bone.path_from_id()
id_block = bone.id_data
@ -439,16 +431,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# ----------------
# location properties
def doLoc(ksi, ks, bone):
def doLoc(self, ks, bone):
if bone.lock_location == (False, False, False):
ksi.addProp(ks, bone, "location")
self.addProp(ks, bone, "location")
else:
for i in range(3):
if not bone.lock_location[i]:
ksi.addProp(ks, bone, "location", i)
self.addProp(ks, bone, "location", i)
# rotation properties
def doRot4d(ksi, ks, bone):
def doRot4d(self, ks, bone):
# rotation mode affects the property used
if bone.rotation_mode == 'QUATERNION':
prop = "rotation_quaternion"
@ -459,40 +451,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
if bone.lock_rotations_4d:
# can check individually
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False):
ksi.addProp(ks, bone, prop)
self.addProp(ks, bone, prop)
else:
if bone.lock_rotation_w is False:
ksi.addProp(ks, bone, prop, 0) # w = 0
self.addProp(ks, bone, prop, 0) # w = 0
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
elif True not in bone.lock_rotation:
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
# other than all open unless we keyframe the whole lot
ksi.addProp(ks, bone, prop)
self.addProp(ks, bone, prop)
def doRot3d(ksi, ks, bone):
def doRot3d(self, ks, bone):
if bone.lock_rotation == (False, False, False):
ksi.addProp(ks, bone, "rotation_euler")
self.addProp(ks, bone, "rotation_euler")
else:
for i in range(3):
if not bone.lock_rotation[i]:
ksi.addProp(ks, bone, "rotation_euler", i)
self.addProp(ks, bone, "rotation_euler", i)
# scale properties
def doScale(ksi, ks, bone):
def doScale(self, ks, bone):
if bone.lock_scale == (0, 0, 0):
ksi.addProp(ks, bone, "scale")
self.addProp(ks, bone, "scale")
else:
for i in range(3):
if not bone.lock_scale[i]:
ksi.addProp(ks, bone, "scale", i)
self.addProp(ks, bone, "scale", i)
# ----------------
# bendy bone properties
def doBBone(ksi, context, ks, pchan):
def doBBone(self, context, ks, pchan):
bone = pchan.bone
# This check is crude, but is the best we can do for now
@ -500,12 +492,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# (and the bone is a control bone). This may lead to some
# false positives...
if bone.bbone_segments > 1:
keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan)
keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan)
# ----------------
# custom properties
def doCustomProps(ksi, ks, bone):
def doCustomProps(self, ks, bone):
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
@ -528,39 +520,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
continue
if rna_property.rna_type in prop_type_compat:
ksi.addProp(ks, bone, prop_path)
self.addProp(ks, bone, prop_path)
elif prop_rna.is_animatable:
ksi.addProp(ks, bone, prop)
# All properties that are likely to get animated in a character rig, only selected bones.
self.addProp(ks, bone, prop)
class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo):
class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(useful when blocking out a shot)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
bl_label = "Whole Character"
class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(only selected bones)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID
bl_label = "Whole Character (Selected Bones Only)"
# iterator - all bones regardless of selection
def iterator(ksi, context, ks):
def iterator(self, context, ks):
# Use either the selected bones, or all of them if none are selected.
bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones
for bone in bones:
if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
if bone.name.startswith(self.badBonePrefixes):
continue
ksi.generate(context, ks, bone)
self.generate(context, ks, bone)
# Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter.
poll = BUILTIN_KSI_WholeCharacter.poll
generate = BUILTIN_KSI_WholeCharacter.generate
addProp = BUILTIN_KSI_WholeCharacter.addProp
doLoc = BUILTIN_KSI_WholeCharacter.doLoc
doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d
doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d
doScale = BUILTIN_KSI_WholeCharacter.doScale
doBBone = BUILTIN_KSI_WholeCharacter.doBBone
doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps
###############################
@ -578,7 +565,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@ -604,7 +591,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@ -638,7 +625,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
def generate(ksi, context, ks, data):
def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)

View File

@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
/**
* Values to create the message that describes the reason poll failed.
*
* \note This must be called in the same context as the poll function that created it.
*/
struct bContextPollMsgDyn_Params {
/** The result is allocated . */
char *(*get_fn)(bContext *C, void *user_data);
/** Optionally free the user-data. */
void (*free_fn)(bContext *C, void *user_data);
void *user_data;
};
const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
const struct bContextPollMsgDyn_Params *params);
void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*

View File

@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
const char *operator_poll_msg; /* reason for poll failing */
/* Operator poll. */
/**
* Store the reason the poll function fails (static string, not allocated).
* For more advanced formatting use `operator_poll_msg_dyn_params`.
*/
const char *operator_poll_msg;
/**
* Store values to dynamically to create the string (called when a tool-tip is shown).
*/
struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
return newC;
}
void CTX_free(bContext *C)
{
/* This may contain a dynamically allocated message, free. */
CTX_wm_operator_poll_msg_clear(C);
MEM_freeN(C);
}
@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
void CTX_wm_operator_poll_msg_clear(bContext *C)
{
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
if (params->free_fn != NULL) {
params->free_fn(C, params->user_data);
}
params->get_fn = NULL;
params->free_fn = NULL;
params->user_data = NULL;
C->wm.operator_poll_msg = NULL;
}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
CTX_wm_operator_poll_msg_clear(C);
C->wm.operator_poll_msg = msg;
}
const char *CTX_wm_operator_poll_msg_get(bContext *C)
void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
const struct bContextPollMsgDyn_Params *params)
{
CTX_wm_operator_poll_msg_clear(C);
C->wm.operator_poll_msg_dyn_params = *params;
}
const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
{
struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
if (params->get_fn != NULL) {
char *msg = params->get_fn(C, params->user_data);
if (msg != NULL) {
*r_free = true;
}
return msg;
}
*r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}

View File

@ -1065,6 +1065,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
remapped_object.runtime.bb = NULL;
remapped_object.data = &remapped_curve;
/* Clear all modifiers for the bevel object.
@ -1077,6 +1078,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object bevel_object = {{NULL}};
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
bevel_object.runtime.bb = NULL;
BLI_listbase_clear(&bevel_object.modifiers);
remapped_curve.bevobj = &bevel_object;
}
@ -1085,6 +1087,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
Object taper_object = {{NULL}};
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
taper_object.runtime.bb = NULL;
BLI_listbase_clear(&taper_object.modifiers);
remapped_curve.taperobj = &taper_object;
}
@ -1107,6 +1110,10 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
MEM_SAFE_FREE(remapped_object.runtime.bb);
MEM_SAFE_FREE(taper_object.runtime.bb);
MEM_SAFE_FREE(bevel_object.runtime.bb);
BKE_object_free_curve_cache(&bevel_object);
BKE_object_free_curve_cache(&taper_object);
}
@ -1535,7 +1542,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
int did_shapekeys = 0;
bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@ -1590,7 +1597,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
did_shapekeys = 1;
did_shapekeys = true;
}
/* copy texture space */
@ -1619,13 +1626,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
/* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
* BKE_mesh_update_customdata_pointers() call below. */
tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
CustomData_add_layer(&tmp.ldata,
CD_MLOOP,
CD_ASSIGN,
(alloctype == CD_ASSIGN) ? mesh_src->mloop :
MEM_dupallocN(mesh_src->mloop),
tmp.totloop);
CustomData_add_layer(&tmp.pdata,
CD_MPOLY,
CD_ASSIGN,
(alloctype == CD_ASSIGN) ? mesh_src->mpoly :
MEM_dupallocN(mesh_src->mpoly),
tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@ -1634,15 +1646,16 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
if (totloop == mesh_dst->totloop) {
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
if (alloctype == CD_ASSIGN) {
/* Assign NULL to prevent double-free. */
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
}
}
}
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
/* since 2.65 caller must do! */
// BKE_mesh_tessface_calc(&tmp);
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);

View File

@ -1336,7 +1336,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
return (mti->modifyGeometrySet != NULL);
}
if (ob->type == OB_VOLUME) {
return (mti->modifyVolume != NULL);
return (mti->modifyVolume != NULL) || (mti->modifyGeometrySet != NULL);
}
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {

View File

@ -39,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@ -1003,13 +1004,12 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
Volume *volume_input)
static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *object,
Volume *volume_input,
GeometrySet &geometry_set)
{
Volume *volume = volume_input;
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@ -1030,25 +1030,22 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
if (mti->modifyVolume) {
/* Ensure we are not modifying the input. */
if (volume == volume_input) {
volume = BKE_volume_copy_for_eval(volume, true);
if (mti->modifyGeometrySet) {
mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
else if (mti->modifyVolume) {
VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
Volume *volume_old = volume_component.get_for_write();
if (volume_old == nullptr) {
volume_old = BKE_volume_new_for_eval(volume_input);
volume_component.replace(volume_old);
}
Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
if (volume_next && volume_next != volume) {
/* If the modifier returned a new volume, release the old one. */
if (volume != volume_input) {
BKE_id_free(nullptr, volume);
}
volume = volume_next;
Volume *volume_new = mti->modifyVolume(md, &mectx, volume_old);
if (volume_new != volume_old) {
volume_component.replace(volume_new);
}
}
}
return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@ -1072,6 +1069,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
{
if (!geometry_set.has<VolumeComponent>()) {
return nullptr;
}
VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
Volume *volume = volume_component.release();
if (volume != nullptr) {
/* Add back, but only as read-only non-owning component. */
volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
}
else {
/* The component was empty, we can remove it. */
geometry_set.remove<VolumeComponent>();
}
return volume;
}
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@ -1079,11 +1094,22 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
GeometrySet geometry_set;
VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
volume_evaluate_modifiers(depsgraph, scene, object, volume, geometry_set);
Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
/* If the geometry set did not contain a volume, we still create an empty one. */
if (volume_eval == nullptr) {
volume_eval = BKE_volume_new_for_eval(volume);
}
/* Assign evaluated object. */
const bool is_owned = (volume != volume_eval);
BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
const bool eval_is_owned = (volume != volume_eval);
BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)

View File

@ -57,6 +57,7 @@ set(SRC
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
intern/versioning_300.c
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c

View File

@ -3868,6 +3868,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
blo_do_versions_290(fd, lib, main);
blo_do_versions_300(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
@ -3891,6 +3892,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports)
do_versions_after_linking_270(main);
do_versions_after_linking_280(main, reports);
do_versions_after_linking_290(main, reports);
do_versions_after_linking_300(main, reports);
do_versions_after_linking_cycles(main);
main->is_locked_for_linking = false;

View File

@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *
void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain);
void do_versions_after_linking_250(struct Main *bmain);
@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain);
void do_versions_after_linking_270(struct Main *bmain);
void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_cycles(struct Main *bmain);
/* This is rather unfortunate to have to expose this here, but better use that nasty hack in

View File

@ -0,0 +1,63 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup blenloader
*/
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
#include "BKE_main.h"
#include "BLO_readfile.h"
#include "readfile.h"
void do_versions_after_linking_300(Main *UNUSED(bmain), ReportList *UNUSED(reports))
{
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - #blo_do_versions_300 in this file.
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
}
}
/* NOLINTNEXTLINE: readability-function-size */
void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *UNUSED(bmain))
{
UNUSED_VARS(fd);
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
}
}

View File

@ -171,7 +171,8 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
});
}
else {
txl->filtered_radiance = NULL;
DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
}
/**

View File

@ -1081,12 +1081,22 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
bool prev_ssr = sldata->common_data.ssr_toggle;
if (prev_ssr) {
/* We need to disable ssr here so output radiance is not directed to the ssr buffer. */
sldata->common_data.ssr_toggle = false;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
material_renderpass_accumulate(fbl,
material_accum_ps,
NULL,
pd,
txl->spec_color_accum,
sldata->renderpass_ubo.spec_color);
if (prev_ssr) {
sldata->common_data.ssr_toggle = prev_ssr;
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
material_renderpass_accumulate(fbl,

View File

@ -61,7 +61,7 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
common_data->ao_dist = scene_eval->eevee.gtao_distance;
common_data->ao_factor = scene_eval->eevee.gtao_factor;
common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor);
common_data->ao_quality = scene_eval->eevee.gtao_quality;
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {

View File

@ -210,14 +210,14 @@ void occlusion_eval(OcclusionData data,
bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) :
(min_v4(abs(data.horizons)) == M_PI);
if (early_out) {
visibility = dot(N, Ng) * 0.5 + 0.5;
visibility = saturate(dot(N, Ng) * 0.5 + 0.5);
visibility = min(visibility, data.custom_occlusion);
if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
bent_normal = N;
}
else {
bent_normal = normalize(N + Ng);
bent_normal = safe_normalize(N + Ng);
}
return;
}
@ -283,7 +283,9 @@ void occlusion_eval(OcclusionData data,
bent_normal = N;
}
else {
bent_normal = normalize(mix(bent_normal, N, sqr(sqr(sqr(visibility)))));
/* Note: using pow(visibility, 6.0) produces NaN (see T87369). */
float tmp = saturate(pow6(visibility));
bent_normal = normalize(mix(bent_normal, N, tmp));
}
}
@ -386,7 +388,8 @@ float specular_occlusion(
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
float specular_occlusion = isect_solid_angle / specular_solid_angle;
/* Mix because it is unstable in unoccluded areas. */
visibility = mix(specular_occlusion, 1.0, pow(visibility, 8.0));
float tmp = saturate(pow8(visibility));
visibility = mix(specular_occlusion, 1.0, tmp);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);

View File

@ -91,6 +91,11 @@ vec2 sqr(vec2 a) { return a * a; }
vec3 sqr(vec3 a) { return a * a; }
vec4 sqr(vec4 a) { return a * a; }
/* Use manual powers for fixed powers. pow() can have unpredicatble results on some implementations.
* (see T87369, T87541) */
float pow6(float x) { return sqr(sqr(x) * x); }
float pow8(float x) { return sqr(sqr(sqr(x))); }
float len_squared(vec3 a) { return dot(a, a); }
float len_squared(vec2 a) { return dot(a, a); }

View File

@ -186,17 +186,17 @@ enum {
UI_RETURN_POPUP_OK = 1 << 5,
};
/* but->flag - general state flags. */
/** #uiBut.flag general state flags. */
enum {
/** Warning, the first 6 flags are internal. */
UI_BUT_ICON_SUBMENU = 1 << 6,
UI_BUT_ICON_PREVIEW = 1 << 7,
/* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */
UI_BUT_ICON_SUBMENU = 1 << 7,
UI_BUT_ICON_PREVIEW = 1 << 8,
UI_BUT_NODE_LINK = 1 << 8,
UI_BUT_NODE_ACTIVE = 1 << 9,
UI_BUT_DRAG_LOCK = 1 << 10,
UI_BUT_NODE_LINK = 1 << 9,
UI_BUT_NODE_ACTIVE = 1 << 10,
UI_BUT_DRAG_LOCK = 1 << 11,
/** Grayed out and un-editable. */
UI_BUT_DISABLED = 1 << 11,
UI_BUT_DISABLED = 1 << 12,
UI_BUT_ANIMATED = 1 << 13,
UI_BUT_ANIMATED_KEY = 1 << 14,

View File

@ -1121,17 +1121,17 @@ static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
data->cancel = true;
return;
}
/* If the value entered is the exact same, do not trigger an update. */
if (data->value == data->startvalue) {
data->cancel = true;
return;
}
}
else {
ui_but_value_set(but, data->value);
}
/* If the value entered is the exact same, do not trigger an update. */
if (data->value == data->startvalue) {
data->cancel = true;
return;
}
ui_but_update_edited(but);
ui_apply_but_func(C, but);

View File

@ -77,17 +77,20 @@ struct wmTimer;
/* popover width (multiplied by 'U.widget_unit') */
#define UI_POPOVER_WIDTH_UNITS 10
/* uiBut->flag */
/** #uiBut.flag */
enum {
UI_SELECT = (1 << 0), /* use when the button is pressed */
UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */
/** Use when the button is pressed. */
UI_SELECT = (1 << 0),
/** Temporarily hidden (scrolled out of the view). */
UI_SCROLLED = (1 << 1),
UI_ACTIVE = (1 << 2),
UI_HAS_ICON = (1 << 3),
UI_HIDDEN = (1 << 4),
UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */
/** Display selected, doesn't impact interaction. */
UI_SELECT_DRAW = (1 << 5),
/** Property search filter is active and the button does not match. */
UI_SEARCH_FILTER_NO_MATCH = (1 << 12),
/* warn: rest of uiBut->flag in UI_interface.h */
UI_SEARCH_FILTER_NO_MATCH = (1 << 6),
/* WARNING: rest of #uiBut.flag in UI_interface.h */
};
/* uiBut->dragflag */

View File

@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
/* button is disabled, we may be able to tell user why */
if (but->flag & UI_BUT_DISABLED) {
const char *disabled_msg = NULL;
bool disabled_msg_free = false;
/* if operator poll check failed, it can give pretty precise info why */
if (but->optype) {
CTX_wm_operator_poll_msg_set(C, NULL);
CTX_wm_operator_poll_msg_clear(C);
WM_operator_poll_context(C, but->optype, but->opcontext);
disabled_msg = CTX_wm_operator_poll_msg_get(C);
disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
}
/* alternatively, buttons can store some reasoning too */
else if (but->disabled_info) {
@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
});
field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg);
}
if (disabled_msg_free) {
MEM_freeN((void *)disabled_msg);
}
}
if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) {

View File

@ -301,6 +301,11 @@ static void action_header_region_init(wmWindowManager *UNUSED(wm), ARegion *regi
static void action_header_region_draw(const bContext *C, ARegion *region)
{
/* The anim context is not actually used, but this makes sure the action being displayed is up to
* date. */
bAnimContext ac;
ANIM_animdata_get_context(C, &ac);
ED_region_header(C, region);
}

View File

@ -256,7 +256,7 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C)
return {};
}
Object *object_orig = (Object *)used_id;
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);

View File

@ -66,6 +66,28 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
return ATTR_DOMAIN_POINT;
}
static bool conversion_can_be_skipped(const GeometryComponent &component,
const StringRef source_name,
const StringRef result_name,
const AttributeDomain result_domain,
const CustomDataType result_type)
{
if (source_name != result_name) {
return false;
}
ReadAttributeLookup read_attribute = component.attribute_try_get_for_read(source_name);
if (!read_attribute) {
return false;
}
if (read_attribute.domain != result_domain) {
return false;
}
if (read_attribute.varray->type() != *bke::custom_data_type_to_cpp_type(result_type)) {
return false;
}
return true;
}
static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
@ -78,6 +100,10 @@ static void attribute_convert_calc(GeometryComponent &component,
component, source_name, result_name) :
domain;
if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
return;
}
GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {

View File

@ -79,6 +79,7 @@ set(SRC
bpy_rna_driver.c
bpy_rna_gizmo.c
bpy_rna_id_collection.c
bpy_rna_operator.c
bpy_rna_types_capi.c
bpy_rna_ui.c
bpy_traceback.c
@ -118,6 +119,7 @@ set(SRC
bpy_rna_driver.h
bpy_rna_gizmo.h
bpy_rna_id_collection.h
bpy_rna_operator.h
bpy_rna_types_capi.h
bpy_rna_ui.h
bpy_traceback.h

View File

@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
}
static void bpy_context_end(bContext *C)
{
if (UNLIKELY(C == NULL)) {
return;
}
CTX_wm_operator_poll_msg_clear(C);
}
/**
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
* In this case we don't want the Python context to override the values as it causes problems
@ -524,6 +532,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
/* Clear Python values in the context so freeing the context after Python exits doesn't crash. */
bpy_context_end(BPY_context_get());
/* Decrement user counts of all callback functions. */
BPY_rna_props_clear_all();

View File

@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
const char *msg = CTX_wm_operator_poll_msg_get(C);
bool msg_free = false;
const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */
CTX_wm_operator_poll_msg_clear(C);
if (msg_free) {
MEM_freeN((void *)msg);
}
error_val = -1;
}
else {

View File

@ -0,0 +1,150 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup pythonintern
*
* This file extends `bpy.types.Operator` with C/Python API methods and attributes.
*/
#include <Python.h>
#include "BLI_string.h"
#include "BKE_context.h"
#include "../generic/python_utildefines.h"
#include "BPY_extern.h"
#include "bpy_capi_utils.h"
/* -------------------------------------------------------------------- */
/** \name Operator `poll_message_set` Method
* \{ */
static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data)
{
PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *py_args = user_data;
PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0);
if (PyUnicode_Check(py_func_or_msg)) {
return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg));
}
PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX);
PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first);
Py_DECREF(py_args_after_first);
char *msg = NULL;
bool error = false;
/* NULL for no string. */
if (py_msg == NULL) {
error = true;
}
else {
if (py_msg == Py_None) {
/* pass */
}
else if (PyUnicode_Check(py_msg)) {
msg = BLI_strdup(PyUnicode_AsUTF8(py_msg));
}
else {
PyErr_Format(PyExc_TypeError,
"poll_message_set(function, ...): expected string or None, got %.200s",
Py_TYPE(py_msg)->tp_name);
error = true;
}
Py_DECREF(py_msg);
}
if (error) {
PyErr_Print();
PyErr_Clear();
}
PyGILState_Release(gilstate);
return msg;
}
static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
{
/* Handles the GIL. */
BPY_DECREF(user_data);
}
PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
".. method:: poll_message_set(message, ...)\n"
"\n"
" Set the message to show in the tool-tip when poll fails.\n"
"\n"
" When message is callable, "
"additional user defined positional arguments are passed to the message function.\n"
"\n"
" :param message: The message or a function that returns the message.\n"
" :type message: string or a callable that returns a string or None.\n");
static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
{
const ssize_t args_len = PyTuple_GET_SIZE(args);
if (args_len == 0) {
PyErr_SetString(PyExc_ValueError,
"poll_message_set(message, ...): requires a message argument");
return NULL;
}
PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0);
if (PyUnicode_Check(py_func_or_msg)) {
if (args_len > 1) {
PyErr_SetString(PyExc_ValueError,
"poll_message_set(message): does not support additional arguments");
return NULL;
}
}
else if (PyCallable_Check(py_func_or_msg)) {
/* pass */
}
else {
PyErr_Format(PyExc_TypeError,
"poll_message_set(message, ...): "
"expected at least 1 string or callable argument, got %.200s",
Py_TYPE(py_func_or_msg)->tp_name);
return NULL;
}
bContext *C = BPY_context_get();
struct bContextPollMsgDyn_Params params = {
.get_fn = pyop_poll_message_get_fn,
.free_fn = pyop_poll_message_free_fn,
.user_data = Py_INCREF_RET(args),
};
CTX_wm_operator_poll_msg_set_dynamic(C, &params);
Py_RETURN_NONE;
}
PyMethodDef BPY_rna_operator_poll_message_set_method_def = {
"poll_message_set",
(PyCFunction)BPY_rna_operator_poll_message_set,
METH_VARARGS | METH_STATIC,
BPY_rna_operator_poll_message_set_doc,
};
/** \} */

View File

@ -0,0 +1,31 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup pythonintern
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern PyMethodDef BPY_rna_operator_poll_message_set_method_def;
#ifdef __cplusplus
}
#endif

View File

@ -41,6 +41,8 @@
#include "bpy_rna_types_capi.h"
#include "bpy_rna_ui.h"
#include "bpy_rna_operator.h"
#include "../generic/py_capi_utils.h"
#include "RNA_access.h"
@ -86,6 +88,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator
* \{ */
static struct PyMethodDef pyrna_operator_methods[] = {
{NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */
{NULL, NULL, 0, NULL},
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Window Manager Clipboard Property
*
@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void)
/* Space */
pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL);
/* wmOperator */
ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def);
BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2);
pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL);
/* WindowManager */
pyrna_struct_type_extend_capi(
&RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset);

View File

@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wmWindowManager *wm = CTX_wm_manager(C);
int retval = OPERATOR_CANCELLED;
CTX_wm_operator_poll_msg_set(C, NULL);
CTX_wm_operator_poll_msg_clear(C);
if (op == NULL || op->type == NULL) {
return retval;
@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C,
{
int retval;
CTX_wm_operator_poll_msg_set(C, NULL);
CTX_wm_operator_poll_msg_clear(C);
/* Dummy test. */
if (ot) {