AnimationPoser

Plugins AnimationPoser

Holy crap, I'm starting to understand something here and making things work 🥳
This looks so promising, time to play 🎲 🤓
 
Some thoughts related to the "state arrival issue" - take with loads of salt, as I'm not a plugin dev & my coding skills/experience are very limited (my skillset is mathematics/physics)

-Solve the "state arrival" issue. As IdlePoser was designed, the transition between states is such that the controllers deaccelerate when arriving at a state, then accelerate again to leave that state. Think about it as an airplane arriving at the airport, stopping by, and then launching in order to travel to the next stop. This makes it so AnimationPoser is yet not the best tool to make animations like walking cycles, since the person is constantly stopping at the states, instead of going directly through it, like in the keyframes of VamTimeline. What looks more like keyframes are the "control points" of IdlePoser: the controllers don't stop by at these control point states, only go directly through it. However, the number of control points is limited to 4 in IdlePoser (and AnimationPoser). A solution could be that, every time you arrive at a state, the next N (say, N=4) states are sorted (which is entirely possible in advance) and then properly interpolated. When arriving at the next state, the list is updated and the last interpolation curve is averaged with the new one. A user-adjustable parameter could control how much the animation should "stick" to that state (in other words, how much that state should behave as a regular state instead of a control state). With that change, the code relating to control states could be removed, as it would not be necessary anymore. With this, VamTimeline animations would probably be able to be seamlessly imported into AnimationPoser.

(!!! Note: A curve in VaM is a six-dimensional parametric object - with three time-dependent coordinates representing position, and three more representing orientation. When ppl talk about Splines (Bezier, Hermitian, Cadmull-Rom etc) or Bezier-curves, they usally mean only the position, ie the first three coordinates. Orientation as a function of time is often calculated as a simple linear approximation (Lerp/Slerp))

Idleposer/ Bernstein "representation"

So, afaics, part of the confusion in comparing idleposer's "states" with keyframes is down to the mathematical representation: Both plugins use interpolation splines to calculate navigation paths - Idleposer, however (afaics) uses the cubic splines in "Bernstein-representation" (https://en.wikipedia.org/wiki/Bézier_curve).

220px-Bezier_curve.svg.png


When you look at the four control points, it's important to understand their physical/geometrical meaning: The first and last (p0 and p4) represent the initial and final position of the curve segment between two consequtive timesteps. The interpretation of the intermediate points p2 and p3 is less obvious: They are related to the tangent (ie the velocity-vector) at inital and final position, but differ in length to those tangents.

The problem with those (internal) control points is that they'll never lie on the curve - unsurprisingly, since they are related to the tangent(-vector), not the position - which makes the Bernstein-representation a bit unintuitve visually and conceptionally.


A better way to represent the spline?

However, it's possible to convert a cubic spline in Bernstein-Representation to the equivalent cubic hermitian spline (Look eg at the table in https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations - That's the conversion formula for cubic splines).

Hermite_spline_2-segments.svg


The resulting curves are completely identical - it's like referring to the same object in two different languages (like, say "milk" is "Milch" in German & "lait" in French, but it's always a fluid & a cow is involved ... ;)) - but the cubic Hermitian splines are more intuitve visually/geometrically: There are no intermediate "control points" that don't lie on the curve (but influence it's shape in a non-intuitve way); instead, their place is taken by the tangents (m0, m1) at the initial and final positions (p0, p1). You know what the tangent to a point on a curve is from everyday experience: If the curve is the path of an object, the tangent is the velocity(-vector) at each point.


TL;DR - It may be worthwhile to re-write, at least partially, the mathematical basis that idleposer/animationposer uses. At least the GUI and the graphical representation. It's totally fine if the plugin uses the Bernstein-representation internally for computation - in fact, it's probably advisable, because the the latter is the natural framework for the computationally efficient De-Casteljau algorithm - but for some higher-level coding purposes and the especially the user-interface/GUI, it might be advantagous to temporarily (!) use a different mathematical representation. Afaik, it's quite common to do it that way: Define a curve in terms of a more intuitve representation (Hermitian splines, Cadmul-Rom, Kochanek-Bartels etc) and then convert to the Bernstein-Representation internally for computation.



P.S.: Another thing to think about is whether it'd save us time in the long run if we implemented our own "library" of spline functions & "helper functions". Or adapted a suitable existing one?

Splines have lots of nice mathematical properties, and there's examples how to implement them, along with some tremendously useful helper functions (even in Unity), but ... Unity 2018 doesn't yet have a native spline library (Unity 2020 does have such a native spline library, and I guess it's reasonable to assume that VaM 2.x will use those, so one might be tempted to simply wait for 2.x .... but afaics, Meshed is some time away from even starting to work on 2.x navigation system).

Iirc, there's so far three existing implementations in VaM:
1) The Spline-functions that Mac implemented for Idleposer
2) VaM's native functions that are part of the animationpattern-/steps & mesh classes
3) The libraries in AcidBubbles' Timeline
(However, I'm not sure how easy it would be to use Acid's stuff (provided he'd be OK with it in the first place), since it was developed for a completely different idea of workflow, especially wrt the "anchoring" concept that makes idleposer so interesting in the first place (iirc, Acid uses blending to achieve similar functionality, but ...)

Afaics, all three have their own strengths - Idleposer comes with the real-time anchoring that is the basis for the workflow-idea presented in Animationposer, VaM's native AnimationPattern/Steps implement Cadmull-Rom (?) splines + animationcurves and some GUI-functionality to manipulate the curve with grabable handles (missing in both idleposer & Timeline) and Timeline comes with ... too many powerful high-level options to list.

But each is kind of focussed on it's own workflow-concept - and all of them lack some nifty helper functions that are well-documented online, or even implemented in Unity (https://github.com/yasirkula/UnityBezierSolution#other-components).
  1. What about offsets to the curve?
  2. What if you want to have several different atoms move on one curve, while keeping a fixed distance from each other, like a train? (eg in order to animate all three penis controllers of a dick following a strongly curved trajectory without clipping through the body?)
  3. What if you want not only the position of a point on the curve, but also the corresponding tangent/normal/binormal vector(s)?
  4. Or what if you want, say, a function that returns, for a given spatial point, the nearest point on the curve in terms of the associated curve parameter (either as "animation-time" or as "arclength")?

Those aren't ar-fetched examples - there's at least one freeware Unity project that implements such a spline-library with the helper functions listed above in the Unity-version VaM 1.x uses, but ... we'd have to check how feasible it is to adapt it to VaM's "atom" framework (iirc, you can attach splines to objects and have it calculate an animation curve from the spline). And we'd also have to check whether the implementation it's computationally effective enough to make sense to use alongside such a hardware-hungry piece of code as VaM 1.x. (afaics, the abovementioned "library" is more a straightforward implementation of a coding-tutorial - not necessarily optimized)
 
Last edited:
@Case I have advanced a lot in refactoring the plugin. I am transforming the state graph into an actual Markov Chain, with transition probabilities per transition (instead of per state). I am going to tackle the "state arrival issue" in the next few hours. This will leave only the mathematical kernel to be improved latter. Your post then is extremely well timed. The mathematical kernel is quite self contained (there are functions with the specific mathematical names: bezier quadratic, bezier cubic, etc), so if you want to do some experiments, keep an eye on github as I'm updating the code as I go. I am actually amazed that someone read the description about the "state arrival" issue and came back with actual mathematical research. This is amazing. It feels like being part of a research community around open source software. This is what I want for the future of this plugin.

The plugin is deviating profoundly from IdlePoser in this next version and will be it's own thing (the thing I actually envisioned originally). By the way, the mathematical solution you presented is extremely relevant. I would have to do some calculations, because the list of next 4 states is updated upon arrival at each state, so a little bit of dynamic adaptation is necessary for the curves.

Do you happen to know what interpolation method is used in VamTimeline? It would be interesting to have cross-compatibility.
 
Do you happen to know what interpolation method is used in VamTimeline? It would be interesting to have cross-compatibility.

Not sure what exactly AcidBubbles uses in Timeline - we had a discussion a while ago, but I never gotten around to taking a closer look. IIRC, he did his own thing to a certain degree - ie not relying on Unity or Meshed's stuff. And Timeline knows non-smooth curve-forms - it's not just all splines. And I think it also uses some global smoothing function ... IDK if that one is strictly speaking a "splinecurve" anymore? And lastly, Acid updates & re-writes his stuff so frequently that ... whelp. ;) If he told us "It's all NURBS these days, dudes ...", I wouldn't be surprised ...

VaM itself uses Cadmull-Rom splines, I think.
 
I refactored the plugin and it is working pretty great right now. I still didn't do the "sort ahead" thing to use an interpolation curve for the next N states, but what I noticed is that linear interpolation already works GREAT. So I might leave curve interpolation (splines, etc) for a next version. It really looks like a cherry on top of the cake at this point. I'll release the next version pretty soon and I'd ask anyone with interest in the mathematical side to take a look. Things in the core are already significantly clearer after the refactoring.
 
haremlife updated AnimationPoser with a new update entry:

A lot of changes to the plugin! The core has been significantly rewritten and a lot of bugs fixed

Version 3.1 is sort of an emergency update. A lot of people have been facing issues with version 2. If you were unable to use the plugin before, please give it a try now.

Version 3 in general is a huge step forward. It will implement my original vision for the plugin. Version 1 was a sort of compromise: I wanted to repurpose IdlePoser for animation, but changing it as little as possible. After the repercussions, I have decided to truly separate AnimationPoser from IdlePoser and turn...

Read the rest of this update entry...
 
BTW, currently, the slowing between transition is the Smooth function and it should not slow down if you put both values to 0s. The current implementation of smoothstep is merging 3 curve segments, and the center curve is just linear.

Here's a quick example in Python of the smoothstep function, using percentage instead of seconds.

Python:
    @staticmethod
    def smooth_step(a, b, d, t):
        d = max(d, 0.01)
        t = clamp(t, 0, d)  # for float
        t = np.clip(t, 0, d)  # for array
        if a + b > 1:  # Safety control
            scale = 1 / (a + b)
            a *= scale
            b *= scale
        n = d - 0.5 * (a + b)
        s = d - t
        if t < a:
            return (a - 0.5 * t) * (t / a) ** 3 / n
        elif s >= b:
            return (t - 0.5 * a) / n
        else:
            return (0.5 * s - b) * (s / b) ** 3 / n + 1.

and here's another example for speed, by using arrays instead of a loop and generating an array of 1000 t samples that can stored and just refered to in the Update loop. In this example, t is normalized from 0 to 1 in all cases, and the distance d is not required.
Python:
    @staticmethod
    def smooth_array(a, b, samples=1000):
        # Sweet spot 1000 to 10000, 50 to 200 us

        t = np.linspace(0, 1, samples)
        n = 1 - 0.5 * (a + b)
        s = t[::-1]

        aa = int(a * samples)
        bb = int(b * samples)
        t[:aa] = (a - 0.5 * t[:aa]) * (t[:aa] / a) ** 3 / n
        t[aa:bb] = (t[aa:bb] - 0.5 * a) / n
        t[bb:] = (0.5 * s[bb:] - b) * (s[bb:] / b) ** 3 / n + 1.
        return t

In my Python example, using numpy arrays is about 300 times faster in Python. Of course, Python is much slower in loops, but if for some reason performance becomes a problem, using an arrays to store 1s ahead in a seperate thread could help you scale the maths.

Edit: I just saw that my array example is incomlete and I simplified it. I was aiming for a single use of that function by feeding a and b as arrays to get the t from alltransitions at the same time.
 
Last edited:
I forgot to say: percentage makes more sense because you always get the same smoothing regardless of your duration value. You can then tweak duration alone without having to always change the 2 other values.
 
Regarding the point about the curve that @Case made: he's right.

One solution which would be really cool, would be to set the state nodes as a vector instead of a point (optionally if a curve is required). This would mean that the Bezier curve is still extremely useful, simply that we are using it for what it is.

Think about it (my opinion at least), the actual path that the control node takes at the start and end of a transition seems more relevant than the center point. With other maths, the start angle will be hard to tweak, by trial and fail but by adding optional vectors, you can fully control the direction that the node takes at t=0 and t=1.

If we can get a visual of the start and end vector from each state node, or if we can interact and move the vector tip, then that would be a huge step forward.

You could also set the vector to be relative to other node controls and not set in the world. That way, you can ensure that the starting direction is always the same relative to a muscle or a joint orientation. That may seem complicated but it could all be optional.

Quick example: think about picking a rectangular object, like a book. There is only a specific angle from where the hand can grab it. It should be possible to ensure that the person will always pick the book right, even if it is vertical or horizontal.
 
Last edited:
Sorry for the many posts, but I also had this idea that would really be useful:

Normalize the animations to the default Person physiology.

By using localPosition instead of position, then by measuring the distance between each node/joint, we should be able to create animations that can be reused by any shape or size of person and always get it aligned right. We just need a function that will convert the localPositions from the default shape to another.

We do that by using anchors from the moment that a location is important. For example: dancing is easy, you just need to keep the form right, and the hand position matters less. We could save the angle from the elbow (basically, we save the relative position and rotation between each joint to keep the form), for example, and compute a different location for the hand if the arm is shorter or if the arm ratio is different. Another example is a punch animation. Since the arm's final form is completely straight, the final animation would look like a fully extended punch regardless of the shape of the person, but still anchored to its target.

Then, from the moment that you touch a body part, even yours, save the hand position relative to that body part instead. The goal is to have the animation solve the form for you. That means that for a masturbation animation, a person with a different size or smaller arms will still get it perfectly right, but the elbow will be bent differently and the person may even arc her back more. VAM already has comply and off options to solve that naturally.

So, any movement involving a collision should be anchored/relative to that area.
 
I don't want to scare anyone, but the stuff I planned for V3 is probably going to be my last contribution, at least for a while. It will already allow a fairly gigantic amount of possibilities. I agree with the functionality mentioned, like making it easier to reuse animations for characters with different proportions. However I'm gonna need community effort. V3 will allow people to showcase fairly complex and gigantic animations, so that the best way for regular users to move wishlists forward will be to share their creations and bring the attention of the community to the plugin. We already had pretty nice spontaneous contributions in the coding and mathematical sides.
 
Great update, thx for the continious work!
Still have a top wish: expose the transition duration (or any value control) to triggers/plugins!

I know we have wait min/max, but sometimes you just want very easy animations (i.e. 2 or 3 states of rubbing something) play at random or scripted speed. It's maybe just me, but this is plugin breaking for me.
If we could control the values with i.e. Float Randomizer or some MacGruber bricks, possibilities would become endless. This also applies to the other values like wait time etc...

Also I find it impossible to reuse layers/animations in different scenes or on another person, they just work in the original scene you have created them. If I save them and apply them in another scene or on another person, it just breaks the pose or let the atom explode in worst case. Seems like the node positions are saved with their World UI positions. This also happens when I use the exact pose preset with stored root node.

This absolutely breaks the idea to share animations/layers, but maybe I do something wrong.

My approach which fails in this way:
- do a simple or complex animation with different layers
- save one of the layers (i.e. hand states)
- open a new scene, add a person, attach your plugin
- add an animation and load the former saved layer onto it

-> pose absolutely breaks (like hands want to reach their original created position)

What do I do wrong, or is it just a major bug?

Cheers,
Saint
 
BTW, currently, the slowing between transition is the Smooth function and it should not slow down if you put both values to 0s. The current implementation of smoothstep is merging 3 curve segments, and the center curve is just linear.

HEADSUP: Guys, watch out a little with naming - this particular smoothstep is a custom creation by MacGruber (I know bcs I recall the patreon post anouncing it & was tangentially involved in a minor debugging step).

Afaic, this is NOT what is conventionally known as smoothstep in coding-circles! (either a sigmoid-like function like erf(x), or a Hermite polynomial). MacGruber's smoothstep is piecewise defined on the interval [0,1], the conventional implementations are not.

This doesn't mean that Mac's implementation is bad - in fact, it's likely better, being more performant while sacrificing little accuracy - but if this is to be a collaborative effort, we should make an effort to document clearly & unambigously.

Maybe call it "smoothstep_fast" or "smoothstep_MC" or smth?


Sorry for the many posts, but I also had this idea that would really be useful:

Suggestion, if I may? I'd suggest spoilering stuff you consider footnotes or sidetracks, to help ppl stay with your main line of argument - give a little summary in the spoiler-header so ppl can more easily organize mentally.

Trust me - I know the fear that "If I spoiler, ppl will just never read it" - but otoh, if you just plonk everything into one wall of text, you risk them TL;DR-ing past ALL of your stuff ...
 
Last edited:
"Also I find it impossible to reuse layers/animations in different scenes or on another person, they just work in the original scene you have created them. If I save them and apply them in another scene or on another person, it just breaks the pose or let the atom explode in worst case. Seems like the node positions are saved with their World UI positions. This also happens when I use the exact pose preset with stored root node."

This is pretty weird. I will take a look and come back.

"If we could control the values with i.e. Float Randomizer or some MacGruber bricks, possibilities would become endless. This also applies to the other values like wait time etc... "

Do you have some basic knowledge about programming? In the AnimationPoserCore.cs file, there is a comment that reads "// trigger values". This indicates the beginning of the triggers section. For example, I created the "SwitchAnimation" and "SwitchLayer" triggers that you can see there. If you look at SwitchStateAction and SwitchLayerAction, it should give a fairly clear idea about how to approach the trigger actions that you want:

1- Get correct animation, then get correct layer under that animation, then get correct state under that layer, then get correct transition under that state.
2- Set the values that you want on the transition. For example, if you want to change the transition easeOutDuration, do transition.myEaseOutDuration = newValue

That's it.

The difficult question is: how do you tell the virt-a-mate trigger system what is the transition that you want to tweak? It is a lot of information: you gotta tell the animation, the layer, the source state, and the target state. If you have an answer to this question, you can try it.

Now if you just want to tweak the current ongoing transition, then step 1 could be replaced by just writing: transition = myCurrentLayer.myTransition.

However, this would not work since the ongoing transition is actually not "myCurrentLayer.myTransition". The layer is like the brain in this case. It gives an order to the controllers, and then the controllers follow it. The order is the transition, but it has already been flushed, and the controllers are following it. In other words, it would be fairly hard to implement this, and it would demand further refactoring of the core.
 
@Saint66 I know you want to script the transition durations, but wouldn't a good compromise be to have "minTransitionDuration" and "maxTransitionDuration" and the actual transition duration be randomly chosen between those values? This would be much easier to implement and it would be self contained in the plugin itself.
 
@Saint66 I know you want to script the transition durations, but wouldn't a good compromise be to have "minTransitionDuration" and "maxTransitionDuration" and the actual transition duration be randomly chosen between those values? This would be much easier to implement and it would be self contained in the plugin itself.
Yeah that would be another way to at least randomise it 👍🏻
 
@Saint66 I am almost done with the cross-animation transitions, which is a HUGE and extremely powerful addition. Randomizing the transition duration is quite simple and I'll do it in some release of version 3.
 
Omg, it's done. I can't believe I managed to make inter-animation transitions. This is so huge.
 
"Also I find it impossible to reuse layers/animations in different scenes or on another person, they just work in the original scene you have created them. If I save them and apply them in another scene or on another person, it just breaks the pose or let the atom explode in worst case. Seems like the node positions are saved with their World UI positions. This also happens when I use the exact pose preset with stored root node. "

God damn it. V2 was way too rushed. I was so happy with the other user's contribution that I just merged the changes without thoroughly testing. This is quite a simple bug, because the anchor atom is not being renamed to the current person atom.
 
haremlife updated AnimationPoser with a new update entry:

AMAZING! Implemented cross animation transitions! Say goodbye to triggers! Save and load in a whim!

First of all: there was a huge bug left in version 3.1, which made animations which were saved in a person and loaded in another break. This was fixed.

Cross-animation was a huge conceptual challenge. How to implement it? When a state in a layer in an animation transitions into another state in another layer in another animation that doesn't share the same controllers, what happens? What about the other layers, what are going to be the arrival states?

Worse than all, how to create an UI...

Read the rest of this update entry...
 
@Saint66 I just released 3.2! It fixes the bug you mentioned. Now you can load saved animations into other characters.

@Case and @Saint66 This release is huge. Not only you can transition between states in different animations, but also sync the remaining layers. A lot of UI issues were also solved. Things are becoming pretty solid right now. It is the time to start truly building something.

I didn't sleep and have work in a few hours, so tomorrow I will check if there are some obvious bugs. But, from what I could test, the plugin is quite usable with all the new features.

@atani If you liked version 2, give this a check.
 
Create a folder Saves/PluginData/IdlePoser. Move Examples.idlepose into that folder. Now click the "load" button in the plugin, and there you go.

This is a VERY simple kissing animation, that took like 5 minutes to set up. You can continuously build on top of it.
I don't seem to be able to see anything when I move 'example' into the folder. When I hit 'load' it shows empty?
 
@Saint66 I just released 3.2! It fixes the bug you mentioned. Now you can load saved animations into other characters.

@Case and @Saint66 This release is huge. Not only you can transition between states in different animations, but also sync the remaining layers. A lot of UI issues were also solved. Things are becoming pretty solid right now. It is the time to start truly building something.

I didn't sleep and have work in a few hours, so tomorrow I will check if there are some obvious bugs. But, from what I could test, the plugin is quite usable with all the new features.

@atani If you liked version 2, give this a check.

Hi again,

sorry to say but the bug is still not fixed in 3.2. If I save a layer (just 2 states of left hand) and want it to use in another scene/on another atom it only works if I store the pose with position of root node and re-apply it before loading the layer onto another person.
Without doing that, the person stretches its hand in the direction of the original root node position, which leads to broken pose.
Or do I have to save the whole animation? This would break the idea having some standard layers for reuse stored.
 
Back
Top Bottom