1
1

Compare commits

...

22 Commits

Author SHA1 Message Date
8118685a83 - normalize qt before doing math on them
- ensure strip alignment panel script loaded
2020-12-10 00:09:52 -05:00
188dc880c6 - clean up formatting and obselete comments 2020-12-09 23:50:17 -05:00
0b2c79e88b - minor update for recent commits on master by others 2020-12-09 23:35:10 -05:00
5e12a5622c - bugfix: fixed the scaling problem. Was due to not normalizing quaternion after reading from fcurves 2020-12-09 23:19:05 -05:00
a30c8168a0 - due to last commit, no longer need C-side python UI for alignment properties 2020-12-09 23:19:04 -05:00
5d4a514a88 - general bugfix that should be committed separately: context.selected_nla_strips would have the wrong pointerRNA associated with it. This leads to unresponsive UI when strip values changed through python-made UI. 2020-12-09 23:19:04 -05:00
7c80dddb8c - typo fix: rna_nla: remove parm
- blend xform allocation separated from adding to list to make duplication simpler
2020-12-09 23:19:03 -05:00
d52a4fa0c6 - add proper support for duplicating nla strips with preblend transforms 2020-12-09 23:18:33 -05:00
30efd99a84 - renamed NlaStripPreBlendTransform to NlaBlendTransform, preblend to blend, etc 2020-12-09 23:18:32 -05:00
962e481f26 - fix preblend transform deletion. By this point, we can actually test the alignment feature 2020-12-09 23:17:00 -05:00
d9280548ff - used quaternion slerp through shortest angle for non full replace blends to fix issue of rotation through longer angle. Unsure if animator ever wants the opposite. If so, that makes things a bit more complicated for specifying which nla channels should have it applied. 2020-12-09 23:16:59 -05:00
d40ea0b3f4 support for file read/writing new preblend xform data 2020-12-09 23:16:59 -05:00
28fefc3205 - actionclip raw eval temporarily always creates new channels until I update function call 2020-12-09 23:16:58 -05:00
a87a4de7b1 - rna for preblend bones (rna path not working properly) 2020-12-09 23:16:58 -05:00
465eeddc04 - fixed preblend xform struct byte alignment
- fixed rna collection to support add/removal of preblend xforms
- removed C-based nla alignment UI
2020-12-09 23:16:57 -05:00
a586fd4e3d - WIP add nla alignment UI in python 2020-12-09 23:14:13 -05:00
27388587e6 - WIP rough core implementation of strip preblend xform support for animation alignment 2020-12-09 23:14:13 -05:00
cfa7e581da -some early WIP for strip alignment:
Overall Need:
UI:
-ability to add NlaStrip preblend transform elements using UI
-add, remove, clear
-bone_name from selection, from active
-ability to copy bone targets between strips

UI:
-visualization of traversal bone for general alignment,
similar to motion paths but without the need for an accurate world
transform. It should update as preblend xform changes. Ideally this
path is grabbale and transformable like any other object.
Would require making own transform_convert_stripPreBlendTransform
code... (opportunity to make Python API for it?)

Core algorithm implementation:
-instead of blending strips as their value obtained, we need to
get all raw values. Then apply preblend xform, then we can blend the
whole snapshot. For efficiency, we can allocate the relevant NlaEvalChannels
for bones right after creating NlaEvalData.
2020-12-09 23:14:12 -05:00
7728869f24 NLA: Action Strip Defaults Sync Length On
Differential Revision: https://developer.blender.org/D9661
2020-12-09 23:13:34 -05:00
Wayde Moss
a052813127 Feature: NLA Merge Strips
Todo: add media and example files

This patch is relative to {D8296} (which is relative to {D9247}. Apply {D9247} first then {D8296} then this one.

For user-facing design discussion: {T80235}
____

**Problem/Solution:**
Before, the only way to combine multiple Nla strips into a single strip is to execute a Bake operator. However, this will merge **all** strips into a replace strip with full influence. This patch creates a new operator "Resample Strips To New" which effectively allows merging a set of selected NlaStrips into a single strip of any blendmode and any nonzero influence. The old selected strips will be muted while preserving the whole Nla result. (I used the word "resample" because it's more general. But most animators will recognize it as a "merge"). This patch offers a more flexible solution to {T69105}, where the solution implemented was to force baking to a single replace strip with full influence.

**Feature:** Nla add support for resampling selected strips into a new track. The more specific use is to support merging selected Nla strips into a single strip. The core resampling function is (anim_sys.c) //BKE_animsys_resample_selected_strips()//

(direct copy+paste of function comment)
```
/** Mute selected NLA strips and resample into a new track. The final Nla stack result will be
 * preserved when possible. New resampled strip will be selected. Previously selected strips will
 * be muted and deselected afterward.
 *
 * \param resample_blendmode: Resulting resampled strip's blend mode.
 * \param resample_influence: Resulting resampled strip's influence. above.
 * \param resample_insertion_nlt_index: NlaTrack to insert the resample track above or below.
 * \param insert_track_lower: Side of resample_insertion_nlt_index to place resample track.
 * \returns: The new resample track. Returns NULL and does nothing if in tweak mode, resample
 * influence zero, or no fcurves are involved in the resample.
 */
```

**Intended Uses:**

Merge Strips: User selects a block of NlaStrips and Resamples. Effectively, all selected strips will become muted and a new "merged" track will be created. Since the overall NLA stack result is preserved, the result is equivalent to a merge.

Convert Strip: User selects a single NlaStrip and Resamples with a different blendmode and/or influence.

**Potential improvements/changes:**

For frames where user had a keyframe, make them non-selected. Select non-user keys. This allows a follow-up op to do an Fcurve simplify or decimate of only the baked keyframes. Effectively it allows a follow-up Smart Bake that preserves user keys. Perhaps this can be done by the caller.

Allow user to somehow select channels to be resampled. Currently all channels found in all selected strips are resampled. Though a simple work around is to delete the undesired  channels after the resample.

**Limitations and potential problems:**

Design: When resample strip value not valid, what should we do? Currently we write a default value. Nothing we write will preserve the animation. This leaves the problem as a "Known Issue".

This operator will not properly resample outside of the resample bounds. Generally, it's not possible since multiple strips with non-None extend modes can not be represented by a single strip of any extend mode.. Maybe it's possible by properly setting the pre and post extrapolation for individual fcurves?

_____

**Some existing functions in (anim_sys.c) that had to be modified slightly for //BKE_animsys_resample_selected_strips()//:**

- changes (anim_sys.c) //nlastrip_evaluate()// and its nested calls to pass //allow_alloc_channels //to control whether NlaEvalChannels are allocated or to only blend existing channels. Flushing and keyframing require allocation. The new merging operator only needs to blend existing channels and requires no allocation.

There are potential areas for refactoring but haven't been implemented to keep the review simpler and more linear. It's an attempt to prevent introducing bugs that occur as a result of refactoring instead of the patch itself.

Differential Revision: https://developer.blender.org/D8867
2020-12-09 23:11:36 -05:00
Wayde Moss
8a74c60c4a Feature: NLA: Evaluate Whole NLA Stack in Tweak Mode
Feature: NLA: show and evaluate whole NLA stack while in tweak mode:

**Note for reviewers**
This patch is relative to {D9247}. Apply that patch first, then apply this one afterward.

For reviewers, the two core functions changed are in anim_sys.c (BKE_animsys_nla_remap_keyframe_values and animsys_calculate_nla). I've separated the old animsys_evaluate_nla() into for_flush and for_keyframing variations to make the intent clear.

I should add that the nlastrip_evaluate() and recursive strip evaluate calls have been duplicated for inverting. They can be refactored but I've decided against it. I didn't want to refactor and add a new feature in a single patch. Keeping those calls nearly the same should make them easier to understand relative to the old implementation. I suppose I could've refactored first then made the patch, but without the duplication it could've been difficult to see the motivation for refactoring choices.

**Question**: I also noticed a UI-based confusion introduced by patch. If the animator has a nonpushed action then it has influence even while in tweak mode which is expected. However, the bounds of that influence is currently not drawn in any way while in tweak mode. It can be a surprise and source of confusion when the non-pushed action begins to influence the animation result. I should talk to the UI team about how it should be drawn. Or maybe it should be a separate patch so the UI can be developed separately given this patch is relatively big already? Should it just be part of {D7600} since that patch is close to the problem area and this one depends on it?

**Problem/Solution**
This feature solves the problem of not being able to see and consider the final animation result while tweaking a strip. Before, as a user keyframes a strip, the upper strips would be disabled. Now, the user can optionally view the upper strip affects while keyframing. The strips above it are accounted for such that the final NLA result matches what the user visually keyframed. The feature pros and cons itself sounds self explanatory but let me know if I should explain more in depth.

**Implementation**
The core implementation is that each upper strip needs to be inverted separately, solving for the blend output of the lower stack. That goes on until we have solved for the blend output of the tweaked strip and lower stack. Then we use the old existing invert math to get the fcurve value of the tweak strip. The lower stack can be inverted as a group instead of separately because we're solving for the tweak strip's value, not the lower blended value, and it doesn't include the tweaked strip.

**Changes to old behavior (Evaluating without upper stack)**
Evaluating upper stack is optional and, for now, set as default (Tab). The RClick context menu and header menu exposes the option to evaluate without the upper stack (Ctrl+Tab).

Tweaked strip no longer uses animdata->act_influence since that's used by the non-pushed action.

**Bugfixes Included Relative to Public Version **
This includes bugfixes such as evaluating meta strips correctly when next to a transition {D8287}.

**Limitations Introduced by Patch**
Keyframing through a quaternion transition of (Combine<->Replace/Add/Sub/Mul) is not handled properly and will fail to insert a keyframe. I'm still working out the math, at least for the case of (Combine<->Replace). Other quaternion transition cases should be handled properly (Combine<->Combine) and (Add/Sub/Mul/Replace <-> Add/Sub/Mul/Replace). All non-quaternion transition cases should be handled properly. This limitation only applies when such transitions appear above the tweaked strip and the upper NLA stack is enabled. Transitions that appear below the tweaked strip does not break keyframing.

**Example Files**
Location Focused:
 {F8802904}
Quaternion Focused:
 {F8802907}
Scale Focused:
 {F8802906}

Each file has multiple objects in it that have simple NLA setups. It's just meant to save a little bit of time. Autokeying is on and keying sets match the file focus. Per check, just unhide one object at a time, enter tweak mode on the lowest strip, then try to keyframe. In most cases, your overall NLA results should be preserved when refreshing the keyframe(changing to different frame then back to the keyframed one). General failure cases include not being able to keyframe through full (influence = 1) Replace strips nor Multiply Zero strips. These are expected, mathematically there is no solution. For quaternion, an additional failure case can occur even when keyframing through a partial Replace strip. Quaternions only interpolate within 180 degrees so if the solution requires that it rotates further than 180, then the keyframe will be wrong. **(Note to self: Todo)** Current patch implementation does not properly fail on this case.

For location, it's easy to verify by using auto keying and snapping Suzanne to cursor. Refresh the frame and her origin should match the cursor. Generally, I just eyeball things and use the grid.

No specific Euler file since NLA evaluation is the same as location values. The Scale file only includes Combine strip examples since other blends modes evaluate the same as any other property, like location.

**Existing Limitations of NLA Not Solved By Patch**
If the tweaked strip's action evaluates multiple times in the same frame, then it's not solved for correctly. To support this is nontrivial. We have to allow keyframing at multiple frames and account for the different flags of each strip that uses the same action. The lower stack would have to be inverted separately just like the upper stack. Each strip may also sample the action using a different action range too.. Transitions between the linked action.. It's complicated. I expect it to be rare for the user to linked duplicate an action, overlay them stack-wise and want to keyframe.. That feature is out of scope. The attached file is a quick setup to verify the limitation. Autokeying is already on. Move the cube on the X-Axis then refresh the frame (change back to the same frame). The cube will be in a different location than keyed.
{F8883083}

Differential Revision: https://developer.blender.org/D8296
2020-12-09 23:11:35 -05:00
Wayde Moss
24442431d4 Refactor: NLA Prep for D8296
No intended functional changes.

This is the refactor of NLA evaluation to prepare for {D8296}. I was using TortoiseGitMerge to look at the diff to try to make the diff more readable.. but it looks like arcanist(?)/this-site uses a different method.

Refactor notes

- generally, functions not shuffled around so diffs are clearer
- in //(anim_sys.c)  nlaeval_blend_value()//, the bitmap writes to// NlaEvalChannel->valid// are unused and redundant so they're removed.
- renames //NlaEvalChannel->valid// to //NlaEvalChannel->domain// for term consistency with usage
- for blending functions, more descriptive names used and float equality checks changed to use //IS_EQF()// instead of ==.
- //(anim_sys.c) animsys_evaluate_nla()// separated into //for_flush //and //for_keyframing //variants to reduce complexity by making the use clear.
  - the dummy strip creation has been refactored to two separate functions for the tweak strip and nonpushed action strip. Both are evaluated differently from other strips and eachother. There's no need to interweave them. A future patch {D8296} generally requires both strips.
- XXX //(anim_sys.c) nlatrack_find_tweaked()// is a work around and temporary. If anyone has any insight into this problem, help is appreciated.
- //(anim_sys.c) nonstrip_action_fill_strip_data()// doesn't call// nlastrips_ctime_get_strip()// due to a future patch {D8867} needs to call the latter N times but the former only needs to be called once.
- //(anim_sys.c) BKE_is_nlatrack_evaluatable() //is an API function so a future patch {D8867} can access it.
- //(anim_sys.c) BKE_animsys_nla_remap_keyframe_values()// removed full replace strip early out. Future patch {D8296} can't use it.
- add const, limited to refactored areas
- remove redundant switch-case fall throughs to default
- //nlaeval_blend_flush()// small refactor, replace branching with assertion
- return value of //nla_combine_quaternion_invert_get_fcurve_values()// implemented to handle case where influence == 0. This failure case added to other blend inversions too. The Nla remap function  // (anim_sys.c) BKE_animsys_nla_remap_keyframe_values()// does this check but future patch {D8867} will not use the remap function directly.

Differential Revision: https://developer.blender.org/D9247
2020-12-09 23:10:31 -05:00
18 changed files with 3657 additions and 379 deletions

View File

@@ -2167,6 +2167,9 @@ def km_nla_generic(_params):
{"properties": [("isolate_action", True)]}),
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("isolate_action", True)]}),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("use_upper_stack_evaluation", False)]}),
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS', "ctrl": True},None),
("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
])
@@ -2250,6 +2253,7 @@ def km_nla_editor(params):
("nla.delete", {"type": 'X', "value": 'PRESS'}, None),
("nla.delete", {"type": 'DEL', "value": 'PRESS'}, None),
("nla.split", {"type": 'Y', "value": 'PRESS'}, None),
("NLA_OT_resample_strips", {"type": 'M', "value": 'PRESS',"shift":True},None),
("nla.mute_toggle", {"type": 'H', "value": 'PRESS'}, None),
("nla.swap", {"type": 'F', "value": 'PRESS', "alt": True}, None),
("nla.move_up", {"type": 'PAGE_UP', "value": 'PRESS', "repeat": True}, None),

View File

@@ -49,6 +49,7 @@ _modules = [
"properties_mask_common",
"properties_material",
"properties_material_gpencil",
"properties_nla",
"properties_object",
"properties_paint_common",
"properties_grease_pencil_common",

View File

@@ -0,0 +1,173 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# 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.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.types import Panel
from bpy.props import (StringProperty,
BoolProperty,
IntProperty,
FloatProperty,
EnumProperty,
PointerProperty,
CollectionProperty,
FloatVectorProperty,
IntVectorProperty,
)
def get_active_strip(context):
items = [strip for strip in context.selected_nla_strips if strip.active]
return items[0] if items else None
class OBJECT_OT_nla_add_blend(bpy.types.Operator):
bl_idname = "object.nla_add_blend"
bl_label = "Add Preblend Transform"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls,context):
return context.selected_nla_strips
def execute(self, context):
active_strip = get_active_strip(context)
print(dict(active_strip.blend_transforms))
k = active_strip.blend_transforms.add()
# print(k.path_from_id("location"))
# print(active_strip.path_resolve("blend_transforms"))
# print(k.id_data)
return {'FINISHED'}
class OBJECT_OT_nla_remove_blend(bpy.types.Operator):
bl_idname = "object.nla_remove_blend"
bl_label = "Remove Preblend Transform"
bl_options = {'REGISTER', 'UNDO'}
blend_index : IntProperty()
@classmethod
def poll(cls,context):
return context.selected_nla_strips
def execute(self, context):
active_strip = get_active_strip(context)
active_strip.blend_transforms.remove_at(blend_index=self.blend_index)
return {'FINISHED'}
class OBJECT_OT_nla_blend_add_bone(bpy.types.Operator):
bl_idname = "object.blend_add_bone"
bl_label = "Preblend Add Bone"
bl_options = {'REGISTER', 'UNDO'}
blend_index : IntProperty()
@classmethod
def poll(cls,context):
return context.selected_nla_strips
def execute(self, context):
active_strip = get_active_strip(context)
blend = active_strip.blend_transforms[self.blend_index]
bone = blend.bones.add()
bone.name ="Hips"
return {'FINISHED'}
class OBJECT_OT_nla_blend_remove_bone(bpy.types.Operator):
bl_idname = "object.blend_remove_bone"
bl_label = "Preblend Remove Bone"
bl_options = {'REGISTER', 'UNDO'}
blend_index : IntProperty()
bone_index : IntProperty()
@classmethod
def poll(cls,context):
return context.selected_nla_strips
def execute(self, context):
active_strip = get_active_strip(context)
blend = active_strip.blend_transforms[self.blend_index]
blend.bones.remove_at(bone_index=self.bone_index)
return {'FINISHED'}
class OBJECT_PT_nla_alignment(Panel):
bl_space_type = 'NLA_EDITOR'
bl_region_type = 'UI'
bl_label = "Alignment"
bl_category = "Strip"
bl_context = "object"
def draw(self, context):
#Q: not in a poll() so it doesnt disappear on nla strip deselection (annoying)
if not context.selected_nla_strips:
return
layout = self.layout
active_strip = get_active_strip(context)
layout.prop(active_strip,"name")
# layout.prop(active_strip,"action")
# layout.prop(active_strip,"blend_transforms")
# layout.prop(active_strip,"frame_start")
# layout.prop(context.active_object,"location")
# layout.prop(context.active_object.pose.bones["Hips"],"location")
# if(context.active_pose_bone):
# c = context.active_pose_bone.constraints
# if(c):
# layout.prop(c[0],'type')
layout.operator(OBJECT_OT_nla_add_blend.bl_idname,text='New Transform',icon='ADD')
for i,blend in enumerate(active_strip.blend_transforms):
box = layout.box()
row = box.row(align=True)
row.label(text="World Transform")
row.operator(OBJECT_OT_nla_remove_blend.bl_idname,text='',icon='REMOVE').blend_index = i
box.prop(blend,"location")
box.prop(blend,"euler")
box.prop(blend,"scale")
col = box.column(align=True)
row = col.row(align=True)
#todo: support for objects?
row.label(text="Bones")
row.operator(OBJECT_OT_nla_blend_add_bone.bl_idname,text='',icon='ADD').blend_index = i
for j,bone in enumerate(blend.bones):
row = col.row(align=True)
# print([b for b in context.active_object.data.bones])
row.prop_search(bone,"name",context.active_object.data,"bones",text='')
#row.prop(bone,"name")
op = row.operator(OBJECT_OT_nla_blend_remove_bone.bl_idname,text='',icon='REMOVE')
op.blend_index = i
op.bone_index = j
classes = (
# Object Panels
OBJECT_PT_nla_alignment,
OBJECT_OT_nla_add_blend,
OBJECT_OT_nla_remove_blend,
OBJECT_OT_nla_blend_remove_bone,
OBJECT_OT_nla_blend_add_bone,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@@ -165,6 +165,7 @@ class NLA_MT_edit(Menu):
layout.separator()
layout.operator("nla.duplicate", text="Duplicate").linked = False
layout.operator("nla.duplicate", text="Linked Duplicate").linked = True
layout.operator("nla.resample_strips")
layout.operator("nla.split")
layout.operator("nla.delete")
layout.operator("nla.tracks_delete")
@@ -198,6 +199,7 @@ class NLA_MT_edit(Menu):
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Exclude Upper Stack)").use_upper_stack_evaluation = False
class NLA_MT_add(Menu):
@@ -260,6 +262,7 @@ class NLA_MT_context_menu(Menu):
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Exclude Upper Stack)").use_upper_stack_evaluation = False
layout.separator()
@@ -268,6 +271,10 @@ class NLA_MT_context_menu(Menu):
layout.separator()
col = layout.column()
col.operator_context="INVOKE_DEFAULT"
col.operator("nla.resample_strips")
layout.operator("nla.split")
layout.operator("nla.delete")

View File

@@ -217,13 +217,24 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
const struct AnimationEvalContext *anim_eval_context,
const bool flush_to_original);
bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
const AnimationEvalContext *anim_eval_context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
char rna_path[],
float *values,
int count,
int index,
bool *r_force_all);
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache);
struct NlaTrack *BKE_animsys_resample_selected_strips(struct Main *main,
struct Depsgraph *depsgraph,
struct AnimData *adt,
struct PointerRNA *id_ptr,
char resample_name[],
short resample_blendmode,
float resample_influence,
int resample_insertion_nlt_index,
bool insert_track_lower);
/* ************************************* */
/* Evaluation API */

View File

@@ -59,6 +59,7 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
const bool use_same_actions,
const int flag);
void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
struct NlaBlendTransform *BKE_nlastrip_blend_transform_copy(struct NlaBlendTransform *strip);
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
@@ -67,6 +68,15 @@ struct NlaStrip *BKE_nlastrip_new(struct bAction *act);
struct NlaStrip *BKE_nlastack_add_strip(struct AnimData *adt,
struct bAction *act,
const bool is_liboverride);
struct NlaBlendTransform *BKE_nlastrip_new_blend_transform();
void BKE_nlastrip_free_blend_transform(struct NlaStrip *strip, struct NlaBlendTransform *blend);
void BKE_nlastrip_free_blend_transform_at(struct NlaStrip *strip, int blend_index);
struct NlaBlendTransform_BoneTarget *BKE_blend_transform_new_bone();
void BKE_blend_transform_free_bone(struct NlaBlendTransform *blend,
struct NlaBlendTransform_BoneTarget *bone_name);
void BKE_blend_transform_free_bone_at(struct NlaBlendTransform *blend, int bone_name_index);
struct NlaStrip *BKE_nla_add_soundstrip(struct Main *bmain,
struct Scene *scene,
struct Speaker *speaker);

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,8 @@
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
@@ -211,10 +213,32 @@ NlaStrip *BKE_nlastrip_copy(Main *bmain,
BLI_addtail(&strip_d->strips, cs_d);
}
BLI_listbase_clear(&strip_d->blend_transforms);
LISTBASE_FOREACH (NlaBlendTransform *, blend_xform, &strip->blend_transforms) {
NlaBlendTransform *duplicate_blend_xform = BKE_nlastrip_blend_transform_copy(blend_xform);
BLI_addtail(&strip_d->blend_transforms, duplicate_blend_xform);
}
/* return the strip */
return strip_d;
}
struct NlaBlendTransform *BKE_nlastrip_blend_transform_copy(NlaBlendTransform *blend_xform)
{
NlaBlendTransform *duplicate_blend_xform = BKE_nlastrip_new_blend_transform();
copy_v3_v3(duplicate_blend_xform->location, blend_xform->location);
copy_v3_v3(duplicate_blend_xform->euler, blend_xform->euler);
copy_v3_v3(duplicate_blend_xform->scale, blend_xform->scale);
LISTBASE_FOREACH (NlaBlendTransform_BoneTarget *, bone_target, &blend_xform->bones) {
NlaBlendTransform_BoneTarget *duplicate_target = BKE_blend_transform_new_bone();
BLI_addtail(&duplicate_blend_xform->bones, duplicate_target);
strcpy(duplicate_target->name, bone_target->name);
}
return duplicate_blend_xform;
}
/**
* Copy a single NLA Track.
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
@@ -357,6 +381,9 @@ NlaStrip *BKE_nlastrip_new(bAction *act)
strip->scale = 1.0f;
strip->repeat = 1.0f;
/* For convenience, (see T82309#1055954). */
strip->flag |= NLASTRIP_FLAG_SYNC_LENGTH;
/* return the new strip */
return strip;
}
@@ -395,6 +422,61 @@ NlaStrip *BKE_nlastack_add_strip(AnimData *adt, bAction *act, const bool is_libo
return strip;
}
NlaBlendTransform *BKE_nlastrip_new_blend_transform()
{
NlaBlendTransform *blend = MEM_callocN(sizeof(NlaBlendTransform), __func__);
blend->location[0] = 0;
blend->location[1] = 0;
blend->location[2] = 0;
blend->euler[0] = 0;
blend->euler[1] = 0;
blend->euler[2] = 0;
blend->scale[0] = 1;
blend->scale[1] = 1;
blend->scale[2] = 1;
return blend;
}
NlaBlendTransform *BKE_nlastrip_copy_blend_transform(NlaStrip *strip)
{
}
void BKE_nlastrip_free_blend_transform(NlaStrip *strip, NlaBlendTransform *blend)
{
// todo: ensure pattern of add/removal matches others (assumptions, that remove also frees, etc)
BLI_remlink(&strip->blend_transforms, blend);
MEM_freeN(blend);
}
void BKE_nlastrip_free_blend_transform_at(NlaStrip *strip, int blend_index)
{
NlaBlendTransform *blend = BLI_findlink(&strip->blend_transforms, blend_index);
if (blend) {
BKE_nlastrip_free_blend_transform(strip, blend);
}
}
NlaBlendTransform_BoneTarget *BKE_blend_transform_new_bone()
{
NlaBlendTransform_BoneTarget *bone_target = MEM_callocN(sizeof(NlaBlendTransform_BoneTarget),
__func__);
return bone_target;
}
void BKE_blend_transform_free_bone(NlaBlendTransform *blend,
NlaBlendTransform_BoneTarget *bone_name)
{
// todo: ensure pattern of add/removal matches others (assumptions, that remove also frees, etc)
BLI_remlink(&blend->bones, bone_name);
MEM_freeN(bone_name);
}
void BKE_blend_transform_free_bone_at(NlaBlendTransform *blend, int bone_name_index)
{
NlaBlendTransform_BoneTarget *bone_name = BLI_findlink(&blend->bones, bone_name_index);
if (bone_name) {
BKE_blend_transform_free_bone(blend, bone_name);
}
}
/* Add a NLA Strip referencing the given speaker's sound */
NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
{
@@ -2115,8 +2197,11 @@ bool BKE_nla_tweakmode_enter(AnimData *adt)
/* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled
* - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
*/
for (nlt = activeTrack; nlt; nlt = nlt->next) {
nlt->flag |= NLATRACK_DISABLED;
activeTrack->flag |= NLATRACK_DISABLED;
if ((adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
for (nlt = activeTrack->next; nlt; nlt = nlt->next) {
nlt->flag |= NLATRACK_DISABLED;
}
}
/* handle AnimData level changes:
@@ -2210,6 +2295,11 @@ static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips)
BKE_fcurve_blend_write(writer, &strip->fcurves);
BKE_fmodifiers_blend_write(writer, &strip->modifiers);
BLO_write_struct_list(writer, NlaBlendTransform, &strip->blend_transforms);
LISTBASE_FOREACH (NlaBlendTransform *, blend, &strip->blend_transforms) {
BLO_write_struct_list(writer, NlaBlendTransform_BoneTarget, &blend->bones);
}
/* write the strip's children */
blend_write_nla_strips(writer, &strip->strips);
}
@@ -2229,6 +2319,12 @@ static void blend_data_read_nla_strips(BlendDataReader *reader, ListBase *strips
/* strip's F-Modifiers */
BLO_read_list(reader, &strip->modifiers);
BKE_fmodifiers_blend_read_data(reader, &strip->modifiers, NULL);
BLO_read_list(reader, &strip->blend_transforms);
LISTBASE_FOREACH (NlaBlendTransform *, blend, &strip->blend_transforms) {
BLO_read_list(reader, &blend->bones);
}
}
}

View File

@@ -79,6 +79,15 @@ typedef struct NlaValidMask {
typedef struct NlaEvalChannelSnapshot {
struct NlaEvalChannel *channel;
/** Only used for keyframe remapping for inverting through a transition. Bit set if raw value was
* written so we know where the channel value comes from (whether it's the property's default or
* from the strip). */
NlaValidMask raw_value_sampled;
/** Only used for keyframe remapping to know whether channel value can be further inverted to
* solve for tweak strip value. */
NlaValidMask invertible;
int length; /* Number of values in the property. */
bool is_base; /* Base snapshot of the channel. */
@@ -113,7 +122,7 @@ typedef struct NlaEvalChannel {
NlaEvalChannelSnapshot *blend_snapshot;
/* Mask of array items controlled by NLA. */
NlaValidMask valid;
NlaValidMask domain;
/* Base set of values. */
NlaEvalChannelSnapshot base_snapshot;
@@ -155,9 +164,13 @@ typedef struct NlaKeyframingContext {
/* Data of the currently edited strip (copy, or fake strip for the main action). */
NlaStrip strip;
NlaEvalStrip *eval_strip;
/** Storage for the non-stashed action as a strip. Only used for keyframing. */
NlaStrip nonuser_act_strip;
/* Evaluated NLA stack below the current strip. */
NlaEvalData nla_channels;
/* Strips above tweaked strip. */
ListBase upper_estrips;
/* Evaluated NLA stack below the tweak strip. */
NlaEvalData lower_nla_channels;
} NlaKeyframingContext;
/* --------------- NLA Functions (not to be used as a proper API) ----------------------- */
@@ -180,7 +193,23 @@ void nlastrip_evaluate(PointerRNA *ptr,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
const struct AnimationEvalContext *anim_eval_context,
const bool flush_to_original);
const bool flush_to_original,
bool allow_alloc_channels);
void nlastrip_evaluate_invert_get_lower_values(
PointerRNA *ptr,
NlaEvalData *upper_eval_data,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
const struct AnimationEvalContext *anim_eval_context);
void nlastrip_evaluate_raw_value(PointerRNA *ptr,
NlaEvalData *upper_eval_data,
ListBase *modifiers,
NlaEvalStrip *nes,
NlaEvalSnapshot *snapshot,
const struct AnimationEvalContext *anim_eval_context,
short *r_blendmode,
float *r_influence);
void nladata_flush_channels(PointerRNA *ptr,
NlaEvalData *channels,
NlaEvalSnapshot *snapshot,

View File

@@ -1116,8 +1116,10 @@ static float *visualkey_get_values(
static float *get_keyframe_values(ReportList *reports,
PointerRNA ptr,
PropertyRNA *prop,
char rna_path[],
int index,
struct NlaKeyframingContext *nla_context,
const AnimationEvalContext *anim_eval_context,
eInsertKeyFlags flag,
float *buffer,
int buffer_size,
@@ -1139,8 +1141,15 @@ static float *get_keyframe_values(ReportList *reports,
}
/* adjust the value for NLA factors */
if (!BKE_animsys_nla_remap_keyframe_values(
nla_context, &ptr, prop, values, *r_count, index, r_force_all)) {
if (!BKE_animsys_nla_remap_keyframe_values(nla_context,
anim_eval_context,
&ptr,
prop,
rna_path,
values,
*r_count,
index,
r_force_all)) {
BKE_report(
reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value");
@@ -1300,8 +1309,10 @@ bool insert_keyframe_direct(ReportList *reports,
float *values = get_keyframe_values(reports,
ptr,
prop,
fcu->rna_path,
index,
nla_context,
anim_eval_context,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,
@@ -1474,8 +1485,10 @@ int insert_keyframe(Main *bmain,
float *values = get_keyframe_values(reports,
ptr,
prop,
rna_path,
array_index,
nla_context,
anim_eval_context,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,

View File

@@ -663,7 +663,8 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext
NlaTrack *nlt = (NlaTrack *)ale->data;
LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip);
CTX_data_list_add(result, ale->id, &RNA_NlaStrip, strip);
}
}
}

View File

@@ -38,6 +38,7 @@
#include "BLT_translation.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_lib_id.h"
@@ -110,6 +111,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
int filter;
const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action");
const bool use_upper_stack_evaluation = RNA_boolean_get(op->ptr, "use_upper_stack_evaluation");
bool ok = false;
/* get editor data */
@@ -131,6 +133,12 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ale->data;
if (use_upper_stack_evaluation) {
adt->flag |= ADT_NLA_EVAL_UPPER_TRACKS;
}
else {
adt->flag &= ~ADT_NLA_EVAL_UPPER_TRACKS;
}
/* try entering tweakmode if valid */
ok |= BKE_nla_tweakmode_enter(adt);
@@ -194,6 +202,14 @@ void NLA_OT_tweakmode_enter(wmOperatorType *ot)
"Enable 'solo' on the NLA Track containing the active strip, "
"to edit it without seeing the effects of the NLA stack");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(
ot->srna,
"use_upper_stack_evaluation",
true,
"Evaluate Upper Stack",
"In tweak mode, display the effects of the tracks above the tweak strip. ");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ------------- */
@@ -1436,6 +1452,278 @@ void NLA_OT_split(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ******************** Resample Strips Operator ***************************** */
/** Resample the selected NLA-Strips into a single strip, preserving the overall NLA stack
* animation.
*/
/** Enum used for resample operator. */
typedef enum eNlaTrackInsertion_Location {
NLT_INSERT_ABOVE_SELECTED_STRIPS = 0,
NLT_INSERT_BELOW_SELECTED_STRIPS,
NLT_INSERT_ABOVE_ACTIVE_STRIP,
NLT_INSERT_BELOW_ACTIVE_STRIP,
NLT_INSERT_ABOVE_SELECTED_TRACKS,
NLT_INSERT_BELOW_SELECTED_TRACKS,
NLT_INSERT_ABOVE_ACTIVE_TRACK,
NLT_INSERT_BELOW_ACTIVE_TRACK,
} eNlaTrackInsertion_Location;
const EnumPropertyItem rna_enum_nla_insertion_location[] = {
{NLT_INSERT_ABOVE_ACTIVE_TRACK,
"ABOVE_ACTIVE_TRACK",
0,
"Above Active Track",
"Insert track above active track"},
{NLT_INSERT_ABOVE_SELECTED_TRACKS,
"ABOVE_SELECTED_TRACKS",
0,
"Above Selected Tracks",
"Insert track above all selected tracks"},
{NLT_INSERT_ABOVE_ACTIVE_STRIP,
"ABOVE_ACTIVE_STRIP",
0,
"Above Active Strip",
"Insert track above active strip"},
{NLT_INSERT_ABOVE_SELECTED_STRIPS,
"ABOVE_SELECTED_STRIPS",
0,
"Above Selected Strips",
"Insert track above all selected strips"},
{NLT_INSERT_BELOW_ACTIVE_TRACK,
"BELOW_ACTIVE_TRACK",
0,
"Below Active Track",
"Insert track below active track"},
{NLT_INSERT_BELOW_SELECTED_TRACKS,
"BELOW_SELECTED_TRACKS",
0,
"Below Selected Tracks",
"Insert track below all selected tracks"},
{NLT_INSERT_BELOW_ACTIVE_STRIP,
"BELOW_ACTIVE_STRIP",
0,
"Below Active Strip",
"Insert track below active strip"},
{NLT_INSERT_BELOW_SELECTED_STRIPS,
"BELOW_SELECTED_STRIPS",
0,
"Below Selected Strips",
"Insert track below all selected strips"},
{0, NULL, 0, NULL, NULL},
};
static void resample_strips_get_insertion_index(ListBase *nla_tracks,
int insertion_location,
int *r_insertion_index,
bool *r_insert_below)
{
int active_strip_nlt_index = -1;
int lowest_selected_strip_nlt_index = -1;
int highest_selected_strip_nlt_index = -1;
int active_nlt_index = -1;
int lowest_selected_nlt_index = -1;
int highest_selected_nlt_index = -1;
/** Obtain indices for each variable above.
*
* \note We don't care if insertion location based on track or strip that is not evaluated or
* muted. We only care about selection. This allows user to choose resample location without
* having to first create dummy tracks or strips.
*/
int nlt_index;
LISTBASE_FOREACH_INDEX (NlaTrack *, nlt, nla_tracks, nlt_index) {
if ((nlt->flag & NLATRACK_SELECTED) != 0) {
highest_selected_nlt_index = nlt_index;
if (lowest_selected_nlt_index == -1) {
lowest_selected_nlt_index = nlt_index;
}
}
if (nlt->flag & NLATRACK_ACTIVE) {
active_nlt_index = nlt_index;
}
for (NlaStrip *strip = nlt->strips.first; strip; strip = strip->next) {
if ((strip->flag & NLASTRIP_FLAG_SELECT) != 0) {
highest_selected_strip_nlt_index = nlt_index;
if (lowest_selected_strip_nlt_index == -1) {
lowest_selected_strip_nlt_index = nlt_index;
}
}
if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
active_strip_nlt_index = nlt_index;
}
}
}
/** Write outputs. */
switch (insertion_location) {
case NLT_INSERT_ABOVE_ACTIVE_STRIP:
*r_insertion_index = active_strip_nlt_index;
*r_insert_below = false;
break;
case NLT_INSERT_BELOW_ACTIVE_STRIP:
*r_insertion_index = active_strip_nlt_index;
*r_insert_below = true;
break;
case NLT_INSERT_ABOVE_SELECTED_STRIPS:
*r_insertion_index = highest_selected_strip_nlt_index;
*r_insert_below = false;
break;
case NLT_INSERT_BELOW_SELECTED_STRIPS:
*r_insertion_index = lowest_selected_strip_nlt_index;
*r_insert_below = true;
break;
case NLT_INSERT_ABOVE_ACTIVE_TRACK:
*r_insertion_index = active_nlt_index;
*r_insert_below = false;
break;
case NLT_INSERT_BELOW_ACTIVE_TRACK:
*r_insertion_index = active_nlt_index;
*r_insert_below = true;
break;
case NLT_INSERT_ABOVE_SELECTED_TRACKS:
*r_insertion_index = highest_selected_nlt_index;
*r_insert_below = false;
break;
case NLT_INSERT_BELOW_SELECTED_TRACKS:
*r_insertion_index = lowest_selected_nlt_index;
*r_insert_below = true;
break;
default:
*r_insertion_index = -1;
break;
}
}
static int nlaedit_resample_strips_exec(bContext *C, wmOperator *op)
{
char resample_name[MAX_NAME];
RNA_string_get(op->ptr, "name", resample_name);
int insertion_location = RNA_enum_get(op->ptr, "insertion_location");
short resample_blendmode = RNA_enum_get(op->ptr, "blendmode");
float resample_influence = RNA_float_get(op->ptr, "influence");
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) == 0) {
return OPERATOR_CANCELLED;
}
/* Get a list of AnimDatas being shown in the NLA. */
ListBase anim_data = {NULL, NULL};
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
PointerRNA id_ptr;
bool any_resample_succeeded = false;
for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) {
int resample_insertion_index;
bool insert_below;
resample_strips_get_insertion_index(
&ale->adt->nla_tracks, insertion_location, &resample_insertion_index, &insert_below);
if (resample_insertion_index == -1) {
/** No strip selected. */
BKE_report(op->reports, RPT_ERROR, "Insertion Location not found!");
continue;
}
RNA_id_pointer_create(ale->id, &id_ptr);
NlaTrack *resampled_nlt = BKE_animsys_resample_selected_strips(ac.bmain,
ac.depsgraph,
ale->adt,
&id_ptr,
resample_name,
resample_blendmode,
resample_influence,
resample_insertion_index,
insert_below);
if (resampled_nlt) {
any_resample_succeeded = true;
}
}
/* Free temp data. */
ANIM_animdata_freelist(&anim_data);
if (!any_resample_succeeded) {
/** Avoid pushing undo. */
return OPERATOR_CANCELLED;
}
/* Refresh auto strip properties. */
ED_nla_postop_refresh(&ac);
/* New f-curves were added, meaning it's possible that it affects dependency graph components
* which weren't previously animated. */
DEG_relations_tag_update(ac.bmain);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void NLA_OT_resample_strips(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Resample Strips To New";
ot->idname = "NLA_OT_resample_strips";
ot->description = "Resample selected strips into new strip";
/* api callbacks */
ot->invoke = WM_operator_props_popup_confirm;
ot->exec = nlaedit_resample_strips_exec;
ot->poll = nlaop_poll_tweakmode_off;
/* own properties */
PropertyRNA *prop;
prop = RNA_def_string(
ot->srna, "name", "Resample", sizeof(((NlaTrack *)NULL)->name), "Name", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna,
"blendmode",
rna_enum_nla_mode_blend_items,
NLASTRIP_MODE_REPLACE,
"Blend Mode",
"");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_float_factor(ot->srna, "influence", 1, 0, 1, "Influence", "", 0, 1);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna,
"insertion_location",
rna_enum_nla_insertion_location,
NLT_INSERT_BELOW_SELECTED_STRIPS,
"Insertion Location",
"");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *********************************************** */
/* NLA Editing Operations (Modifying) */

View File

@@ -98,6 +98,7 @@ void NLA_OT_meta_remove(wmOperatorType *ot);
void NLA_OT_duplicate(wmOperatorType *ot);
void NLA_OT_delete(wmOperatorType *ot);
void NLA_OT_split(wmOperatorType *ot);
void NLA_OT_resample_strips(wmOperatorType *ot);
void NLA_OT_mute_toggle(wmOperatorType *ot);

View File

@@ -143,6 +143,7 @@ void nla_operatortypes(void)
WM_operatortype_append(NLA_OT_duplicate);
WM_operatortype_append(NLA_OT_delete);
WM_operatortype_append(NLA_OT_split);
WM_operatortype_append(NLA_OT_resample_strips);
WM_operatortype_append(NLA_OT_mute_toggle);

View File

@@ -696,6 +696,32 @@ typedef enum eFCurve_Smoothing {
/* NLA Strips ------------------------------------- */
typedef struct NlaBlendTransform {
struct NlaBlendTransform *prev, *next;
/** Used for Strip Alignment of bones: This applies a transform to strip bone channels before
* blending. This allows the animator to align traversal bones (like root bone) so that
blending
* will occur in the proper blend space. Otherwise, blending a Run midway through a Walk
cycle,
* where the root travels, results in the root blending to Run frame0, where the root is at
zero.
*/
float location[3];
float euler[3];
float scale[3];
float _pad;
// char transform_target;
// char _pad0[3];
/*Element: NlaBlendTransform_BoneTarget*/
ListBase bones;
} NlaBlendTransform;
typedef struct NlaBlendTransform_BoneTarget {
struct NlaBlendTransform_BoneTarget *prev, *next;
char name[64];
} NlaBlendTransform_BoneTarget;
/**
* NLA Strip (strip)
*
@@ -755,6 +781,9 @@ typedef struct NlaStrip {
/* Pointer to an original NLA strip. */
struct NlaStrip *orig_strip;
/*Element: NlaBlendTransform */
ListBase blend_transforms;
void *_pad3;
} NlaStrip;
@@ -797,6 +826,7 @@ typedef enum eNlaStrip_Flag {
NLASTRIP_FLAG_USR_INFLUENCE = (1 << 5),
NLASTRIP_FLAG_USR_TIME = (1 << 6),
NLASTRIP_FLAG_USR_TIME_CYCLIC = (1 << 7),
NLASTRIP_FLAG_ALIGNED = (1 << 8),
/** NLA strip length is synced to the length of the referenced action */
NLASTRIP_FLAG_SYNC_LENGTH = (1 << 9),
@@ -1124,6 +1154,8 @@ typedef enum eAnimData_Flag {
ADT_NLA_EDIT_NOMAP = (1 << 3),
/** NLA-Strip F-Curves are expanded in UI. */
ADT_NLA_SKEYS_COLLAPSED = (1 << 4),
/** Evaluate Tracks above tweaked strip. */
ADT_NLA_EVAL_UPPER_TRACKS = (1 << 5),
/** Drivers expanded in UI. */
ADT_DRIVERS_COLLAPSED = (1 << 10),

View File

@@ -429,6 +429,8 @@ extern StructRNA RNA_MulticamSequence;
extern StructRNA RNA_MultiplyGpencilModifier;
extern StructRNA RNA_MultiresModifier;
extern StructRNA RNA_MusgraveTexture;
extern StructRNA RNA_NlaBlendTransform_BoneTarget;
extern StructRNA RNA_NlaBlendTransform;
extern StructRNA RNA_NlaStrip;
extern StructRNA RNA_NlaTrack;
extern StructRNA RNA_Node;

View File

@@ -134,6 +134,23 @@ static void rna_AnimData_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *p
ANIM_id_update(bmain, id);
}
static void rna_AnimData_upper_stack_evaluation_set(PointerRNA *ptr, const bool value)
{
AnimData *adt = (AnimData *)ptr->data;
if ((adt->flag & ADT_NLA_EDIT_ON) && adt->act_track) {
if (value) {
for (NlaTrack *nlt = adt->act_track; nlt; nlt = nlt->next) {
nlt->flag &= ~NLATRACK_DISABLED;
}
}
else {
for (NlaTrack *nlt = adt->act_track; nlt; nlt = nlt->next) {
nlt->flag |= NLATRACK_DISABLED;
}
}
}
}
static void rna_AnimData_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
DEG_relations_tag_update(bmain);
@@ -1384,8 +1401,14 @@ static void rna_def_animdata(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Pin in Graph Editor", "");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
RNA_define_lib_overridable(false);
prop = RNA_def_property(srna, "use_upper_stack_evaluation", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ADT_NLA_EVAL_UPPER_TRACKS);
RNA_def_property_boolean_funcs(prop, NULL, "rna_AnimData_upper_stack_evaluation_set");
RNA_def_property_ui_text(prop, "Evaluate tracks above tweaked strip. ", "");
RNA_def_property_update(
prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED | ND_NLA, "rna_AnimData_update");
RNA_define_lib_overridable(false);
/* Animation Data API */
RNA_api_animdata(srna);
}

View File

@@ -37,6 +37,49 @@
#include "WM_api.h"
#include "WM_types.h"
/* enum defines exported for rna_animation.c */
const EnumPropertyItem rna_enum_nla_mode_blend_items[] = {
{NLASTRIP_MODE_REPLACE,
"REPLACE",
0,
"Replace",
"The strip values replace the accumulated results by amount specified by influence"},
{NLASTRIP_MODE_COMBINE,
"COMBINE",
0,
"Combine",
"The strip values are combined with accumulated results by appropriately using addition, "
"multiplication, or quaternion math, based on channel type"},
{0, "", 0, NULL, NULL},
{NLASTRIP_MODE_ADD,
"ADD",
0,
"Add",
"Weighted result of strip is added to the accumulated results"},
{NLASTRIP_MODE_SUBTRACT,
"SUBTRACT",
0,
"Subtract",
"Weighted result of strip is removed from the accumulated results"},
{NLASTRIP_MODE_MULTIPLY,
"MULTIPLY",
0,
"Multiply",
"Weighted result of strip is multiplied with the accumulated results"},
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_nla_mode_extend_items[] = {
{NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents"},
{NLASTRIP_EXTEND_HOLD,
"HOLD",
0,
"Hold",
"Hold the first frame if no previous strips in track, and always hold last frame"},
{NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame"},
{0, NULL, 0, NULL, NULL},
};
#ifdef RNA_RUNTIME
# include <math.h>
@@ -101,6 +144,93 @@ static char *rna_NlaStrip_path(PointerRNA *ptr)
return BLI_strdup("");
}
static char *rna_NlaBlendTransform_path(PointerRNA *ptr)
{
NlaBlendTransform *blend_xform = (NlaBlendTransform *)ptr->data;
AnimData *adt = BKE_animdata_from_id(ptr->owner_id);
/* if we're attached to AnimData, try to resolve path back to AnimData */
if (adt) {
NlaTrack *nlt;
NlaStrip *nls;
NlaBlendTransform *pbxform;
int pbxform_index;
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
for (nls = nlt->strips.first; nls; nls = nls->next) {
pbxform_index = 0;
for (pbxform = nls->blend_transforms.first; pbxform;
pbxform = pbxform->next, pbxform_index++) {
if (pbxform == blend_xform) {
/* XXX but if we animate like this, the control will never work... */
char name_esc_nlt[sizeof(nlt->name) * 2];
char name_esc_nls[sizeof(nls->name) * 2];
BLI_strescape(name_esc_nlt, nlt->name, sizeof(name_esc_nlt));
BLI_strescape(name_esc_nls, nls->name, sizeof(name_esc_nls));
return BLI_sprintfN(
"animation_data.nla_tracks[\"%s\"].strips[\"%s\"].blend_transforms[%i]",
name_esc_nlt,
name_esc_nls,
pbxform_index);
}
}
}
}
}
/* no path */
return BLI_strdup("");
}
static char *rna_NlaBlendTransform_BoneTarget_path(PointerRNA *ptr)
{
NlaBlendTransform_BoneTarget *search_bone_name = (NlaBlendTransform_BoneTarget *)ptr->data;
AnimData *adt = BKE_animdata_from_id(ptr->owner_id);
/* if we're attached to AnimData, try to resolve path back to AnimData */
if (adt) {
NlaTrack *nlt;
NlaStrip *nls;
NlaBlendTransform *pbxform;
NlaBlendTransform_BoneTarget *bone_name;
int pbxform_index;
int bone_index;
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
for (nls = nlt->strips.first; nls; nls = nls->next) {
pbxform_index = 0;
for (pbxform = nls->blend_transforms.first; pbxform;
pbxform = pbxform->next, pbxform_index++) {
bone_index = 0;
for (bone_name = pbxform->bones.first; bone_name;
bone_name = bone_name->next, bone_index++) {
if (bone_name == search_bone_name) {
/* XXX but if we animate like this, the control will never work... */
char name_esc_nlt[sizeof(nlt->name) * 2];
char name_esc_nls[sizeof(nls->name) * 2];
BLI_strescape(name_esc_nlt, nlt->name, sizeof(name_esc_nlt));
BLI_strescape(name_esc_nls, nls->name, sizeof(name_esc_nls));
return BLI_sprintfN(
"animation_data.nla_tracks[\"%s\"].strips[\"%s\"].blend_transforms[%i].bones["
"%i]",
name_esc_nlt,
name_esc_nls,
pbxform_index,
bone_index);
}
}
}
}
}
}
/* no path */
return BLI_strdup("");
}
static void rna_NlaStrip_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ID *id = ptr->owner_id;
@@ -506,51 +636,52 @@ static void rna_NlaTrack_solo_set(PointerRNA *ptr, bool value)
}
}
static void rna_nlastrip_blendXform_bone_name_set(PointerRNA *ptr, const char *value)
{
NlaBlendTransform_BoneTarget *pbxform_bone_name = (NlaBlendTransform_BoneTarget *)ptr->data;
BLI_strncpy_utf8(pbxform_bone_name->name, value, sizeof(pbxform_bone_name->name));
}
static NlaBlendTransform *rna_NlaStrip_blend_new(NlaStrip *strip, ReportList *reports)
{
NlaBlendTransform *blend_xform = BKE_nlastrip_new_blend_transform();
BLI_addtail(&strip->blend_transforms, blend_xform);
return blend_xform;
}
static void rna_NlaStrip_blend_remove(NlaStrip *strip, ReportList *reports, PointerRNA *blend)
{
BKE_nlastrip_free_blend_transform(strip, (NlaBlendTransform *)blend->data);
}
static void rna_NlaStrip_blend_remove_at(NlaStrip *strip, ReportList *reports, int blend_index)
{
BKE_nlastrip_free_blend_transform_at(strip, blend_index);
}
static NlaBlendTransform_BoneTarget *rna_blend_bone_name_new(NlaBlendTransform *blend,
ReportList *reports)
{
NlaBlendTransform_BoneTarget *bone_target = BKE_blend_transform_new_bone();
BLI_addtail(&blend->bones, bone_target);
return bone_target;
}
static void rna_blend_bone_name_remove(NlaBlendTransform *blend,
ReportList *reports,
PointerRNA *bone_name)
{
BKE_blend_transform_free_bone(blend, (NlaBlendTransform_BoneTarget *)bone_name->data);
}
static void rna_blend_bone_name_remove_at(NlaBlendTransform *blend,
ReportList *reports,
int bone_name_index)
{
BKE_blend_transform_free_bone_at(blend, bone_name_index);
}
#else
/* enum defines exported for rna_animation.c */
const EnumPropertyItem rna_enum_nla_mode_blend_items[] = {
{NLASTRIP_MODE_REPLACE,
"REPLACE",
0,
"Replace",
"The strip values replace the accumulated results by amount specified by influence"},
{NLASTRIP_MODE_COMBINE,
"COMBINE",
0,
"Combine",
"The strip values are combined with accumulated results by appropriately using addition, "
"multiplication, or quaternion math, based on channel type"},
{0, "", 0, NULL, NULL},
{NLASTRIP_MODE_ADD,
"ADD",
0,
"Add",
"Weighted result of strip is added to the accumulated results"},
{NLASTRIP_MODE_SUBTRACT,
"SUBTRACT",
0,
"Subtract",
"Weighted result of strip is removed from the accumulated results"},
{NLASTRIP_MODE_MULTIPLY,
"MULTIPLY",
0,
"Multiply",
"Weighted result of strip is multiplied with the accumulated results"},
{0, NULL, 0, NULL, NULL},
};
const EnumPropertyItem rna_enum_nla_mode_extend_items[] = {
{NLASTRIP_EXTEND_NOTHING, "NOTHING", 0, "Nothing", "Strip has no influence past its extents"},
{NLASTRIP_EXTEND_HOLD,
"HOLD",
0,
"Hold",
"Hold the first frame if no previous strips in track, and always hold last frame"},
{NLASTRIP_EXTEND_HOLD_FORWARD, "HOLD_FORWARD", 0, "Hold Forward", "Only hold last frame"},
{0, NULL, 0, NULL, NULL},
};
static void rna_def_strip_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -579,6 +710,46 @@ static void rna_def_strip_fcurves(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
}
static void rna_def_nlastrip_blendXforms(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "NlaBlendTransforms");
srna = RNA_def_struct(brna, "NlaBlendTransforms", NULL);
RNA_def_struct_sdna(srna, "NlaStrip");
RNA_def_struct_ui_text(
srna, "Nla Strip Preblend Transforms", "Collection of Preblend Transforms");
/* add target */
func = RNA_def_function(srna, "add", "rna_NlaStrip_blend_new");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Add a new bone");
/* return type */
parm = RNA_def_pointer(func, "blend", "NlaBlendTransform", "", "");
RNA_def_function_return(func, parm);
/* remove target */
func = RNA_def_function(srna, "remove", "rna_NlaStrip_blend_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Remove an existing bone from the armature");
/* target to remove*/
parm = RNA_def_pointer(func, "blend", "NlaBlendTransform", "", "NlaBlendTransform to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
/* remove target */
func = RNA_def_function(srna, "remove_at", "rna_NlaStrip_blend_remove_at");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Remove an existing bone from the armature");
/* target to remove*/
parm = RNA_def_int(func, "blend_index", 0, 0, INT_MAX, "blend_index", "", 0, INT_MAX);
}
static void rna_def_nlastrip(BlenderRNA *brna)
{
StructRNA *srna;
@@ -823,6 +994,18 @@ static void rna_def_nlastrip(BlenderRNA *brna)
"after tweaking strip and its keyframes");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update");
// prop = RNA_def_property(srna, "use_alignment", PROP_BOOLEAN, PROP_NONE);
// RNA_def_property_boolean_sdna(prop, NULL, "flag", NLASTRIP_FLAG_ALIGNED);
// RNA_def_property_ui_text(
// prop, "Use Alignment", "Mark strip as aligned with other marked overlapping strips");
prop = RNA_def_property(srna, "blend_transforms", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "blend_transforms", NULL);
RNA_def_property_struct_type(prop, "NlaBlendTransform");
RNA_def_property_ui_text(prop, "PreBlendTransforms", "");
rna_def_nlastrip_blendXforms(
brna, prop); // GG: todo: test to ensure add/removal works before copying to bones
RNA_define_lib_overridable(false);
}
@@ -931,13 +1114,105 @@ static void rna_def_nlatrack(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_blend_bones(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
PropertyRNA *prop;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "NlaBlendTransform_BoneTargets");
srna = RNA_def_struct(brna, "NlaBlendTransform_BoneTargets", NULL);
RNA_def_struct_sdna(srna, "NlaBlendTransform");
RNA_def_struct_ui_text(srna,
"Nla Strip Preblend Transform Bone Names",
"Collection of Preblend Transform Bone Names");
/* add target */
func = RNA_def_function(srna, "add", "rna_blend_bone_name_new");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Add a new bone");
/* return type */
parm = RNA_def_pointer(func, "bone_name", "NlaBlendTransform_BoneTarget", "", "");
RNA_def_function_return(func, parm);
/* remove target */
func = RNA_def_function(srna, "remove", "rna_blend_bone_name_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Remove an existing bone from the armature");
/* target to remove*/
parm = RNA_def_pointer(
func, "bone_name", "NlaBlendTransform_BoneTarget", "", "NlaBlendTransform to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
/* remove target */
func = RNA_def_function(srna, "remove_at", "rna_blend_bone_name_remove_at");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Remove an existing bone from the armature");
/* target to remove*/
parm = RNA_def_int(func, "bone_index", 0, 0, INT_MAX, "bone_index", "", 0, INT_MAX);
}
static void rna_def_nlastrip_blendXform(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
/* struct definition */
srna = RNA_def_struct(brna, "NlaBlendTransform", NULL);
RNA_def_struct_ui_text(srna, "NLA Strip Pre-Blend Transform", "");
RNA_def_struct_path_func(srna, "rna_NlaBlendTransform_path");
/* name property */
/** Note: should not be animated */
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_ui_text(prop, "Location", "");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update");
prop = RNA_def_property(srna, "euler", PROP_FLOAT, PROP_EULER);
RNA_def_property_ui_text(prop, "Rotation", "");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3);
RNA_def_property_ui_text(prop, "Scale", "");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update");
prop = RNA_def_property(srna, "bones", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "NlaBlendTransform_BoneTarget");
RNA_def_property_ui_text(prop, "Bones", "Bones to apply pre-blend transform to before blending");
rna_def_blend_bones(brna, prop);
}
static void rna_def_nlastrip_blendXform_bone_name(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
/* struct definition */
srna = RNA_def_struct(brna, "NlaBlendTransform_BoneTarget", NULL);
RNA_def_struct_ui_text(srna, "Bone Name", "");
RNA_def_struct_path_func(srna, "rna_NlaBlendTransform_BoneTarget_path");
RNA_def_struct_ui_icon(srna, ICON_GIZMO); /* XXX */
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_ui_text(prop, "Name", "Bone Name");
RNA_def_struct_name_property(srna, prop);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_nlastrip_blendXform_bone_name_set");
}
/* --------- */
void RNA_def_nla(BlenderRNA *brna)
{
rna_def_nlatrack(brna);
rna_def_nlastrip(brna);
rna_def_nlastrip_blendXform(brna);
rna_def_nlastrip_blendXform_bone_name(brna);
}
#endif