Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
7107753f73
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ¶ms,
|
||||
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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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, ¶ms);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
/** \} */
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue