Search Unity

Mesh Animator - Highly efficient animated crowds. For support, please use email.

Discussion in 'Assets and Asset Store' started by jschieck, Dec 7, 2014.

  1. LaireonGames

    LaireonGames

    Joined:
    Nov 16, 2013
    Posts:
    705
    Yup done that, il2cpp doesn't inline either :)

    BTW not saying its a bottle neck, not even run it yet! But its an easy area to see improvement
     
  2. LaireonGames

    LaireonGames

    Joined:
    Nov 16, 2013
    Posts:
    705
    Also I just tried to make a material using the standard shader provided and its throwing this error:

    Shader error in 'Mesh Animator/Standard': variable 'vertexInput' used without having been completely initialized at Assets/MeshAnimator/Shaders/MeshAnimatorStandardBase.cginc(37) (on d3d11)

    Using the built in renderer on Unity 2020.1
     
  3. LaireonGames

    LaireonGames

    Joined:
    Nov 16, 2013
    Posts:
    705
    Wow.... um ok is there any reason why you are keeping the alpha channel for the textures? So far as I can see you always set it to 1 which means if its not being used 25% of the data in the textures is doing nothing!

    Screenshot_2.png

    Screenshot_3.png
     
  4. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    There are some more questionable things going on, e.g. the _shaderTime property. That might as well be
    Time.timeSinceLevelLoad.

    Custom shaders are also missing documentation on how to apply normals correctly, best to just copy over whats done in their shaders (like mobile/diffuse)
     
    Last edited: Jan 22, 2021
  5. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    This plugin has a lot of issues. Just fyi if you have different meshes that use the same material it breaks, you have to manually make different materials for each mesh.
     
  6. jschieck

    jschieck

    Joined:
    Nov 10, 2010
    Posts:
    429
    Shader time and unity time are not the same thing. As a game is paused and unpaused or minimized and maximized the two can become out of sync.

    This is a requirement for material property blocks and instancing...
    This is controlled in the bake window by the texture quality setting. Believe me I am well aware there are more efficient ways to do things as I've been using Unity for a very long time, but when you make an asset that's thousands of people use, you have to make it accessible as well, for both new users and pros.
     
    Last edited: Jan 25, 2021
  7. LaireonGames

    LaireonGames

    Joined:
    Nov 16, 2013
    Posts:
    705
    This has nothing to do with what I'm talking about. You can change the texture format in this window. But those formats are still wrongly utilised.

    Congratulations, but you still could have easily made it accessible and not waste 25% of the data... The workflow is exactly the same, literally nothing would have been different to end users if this was accounted for in the first place. Since it wasn't done properly in the first place though, people would need to rebake animations and that is indeed a mess.
     
  8. jschieck

    jschieck

    Joined:
    Nov 10, 2010
    Posts:
    429
    Thank you for your feedback, enjoy your refund, lets reserve this thread for paying customers.
     
    HenryChinaski and LaireonGames like this.
  9. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    You and i know thats the case. But clearly if your plugin requires this it should be documented that this is the case. Newer users will not know this, and run into the same issue i had. Why isnt there a warning built in?

    I plan to make a workaround for myself too, simply instantiate one new material per sharedmesh. That completely solves the issue.
     
  10. SnaiperoG

    SnaiperoG

    Joined:
    Apr 6, 2015
    Posts:
    66
    Hi, im still using 1.x version on MA. Im looking now how working 2.0.18 with my models. There is big differece between these versions? There is no bugs now with my models. Ive tried a new shader animated mode, and there is a huge animations size. For example, snapshot: 2.8 mb, shader animated: 16mb, etc. up to 64mb. Its too large. I guess there is no point to use it and i should better stay on snapshot mode? I working on mobile game and size is importal to me.
     
  11. SnaiperoG

    SnaiperoG

    Joined:
    Apr 6, 2015
    Posts:
    66
    Also, there is a thing, present even in last version. When animated mesh offscreen, events not working, when using regular animator, they works. Example, i have my enemies footsteps working by animation events, and because of it, my footstep sounds not working when my enemies not in the camera right now
     
  12. jschieck

    jschieck

    Joined:
    Nov 10, 2010
    Posts:
    429
    There are lots of changes, but no major performance improvements for snapshot mode from 1x to 2x. Shader animated can produce larger memory footprints, but it's very dependent on the model and animation length. If snapshot mode is working great then no reason to switch.
    Do you have the Update When Offsceen option enabled on the Mesh Animator component?
     
  13. SnaiperoG

    SnaiperoG

    Joined:
    Apr 6, 2015
    Posts:
    66
    Hi, thanks for reply. No i didnt enable it. There is big performance difference if i will enable it? Because only what i want to work offscreen are events and i have a lot of enemies on screen
     
  14. niflying

    niflying

    Joined:
    Jul 11, 2012
    Posts:
    108
    Any good news for HDRP shader animated mode?
     
  15. alejojamcity

    alejojamcity

    Joined:
    Aug 30, 2018
    Posts:
    7
    Hello @jschieck, We are creating a new game where we plan to have hundreds of animated units on screen. In the past we have struggled to have many concurrent skinned mesh rendered units, so we recently purchased Mesh Animator to give it a go.

    We are testing on our baseline device a moto e6 play, which has very weak hardware (CPU Quad-core 1.5 GHz Cortex-A53, GPU PowerVR GE8100). We set up a benchmark scene where we try to instance 150 animated characters. We tried a simple SMR with an animator and a single looped animation, vs Mesh Animator both in Shader and Snapshot modes. We tried this on both the old rendering pipeline and in URP 10.2.2 running on Unity 2020.2.2.

    Here are the results for the Legacy Render Pipeline:
    Legacy-Animator.png

    Legacy-MeshAnimatorShader.png

    Legacy-MeshAnimatorSnapshot.png

    Although Mesh Animator using Shader mode was very underwhelming (which makes sense due to the very poor GPU on the device), Snapshot mode worked really well maxing out at 60fps.

    When we switched to URP, Shader was still underwhelming, although a bit better than in Legacy, but Snapshot mode worked really poorly, maxing out at 40 fps compared to 60 on legacy, and worse than using skinned meshes and an animator:

    URP-MeshAnimatorShader.png

    URP-MeshAnimatorSnapshot.png

    We were wondering if you may have an idea on why this could happen, as we were really encouraged about the potential of using Mesh Animator in the project but unfortunately, it will be built on top of the Universal Render Pipeline.
     
    Last edited: Feb 4, 2021
  16. alejojamcity

    alejojamcity

    Joined:
    Aug 30, 2018
    Posts:
    7
    It seems that the URP shaders have some compile errors on URP 10.2.2. Could this be related? Currently, shaders reside under the Failed to compile folder in the shaders drop down and show multiple errors when selected:

    Screenshot 2021-02-04 155953.png
     
  17. niflying

    niflying

    Joined:
    Jul 11, 2012
    Posts:
    108
    Hi, I've got this iusse on MA 2.0.18。 Did you find a good way ?
     
  18. Klausology

    Klausology

    Joined:
    Aug 29, 2017
    Posts:
    131
    Unfortunately I wasn't able to solve this issue, the developer did not respond either. What I did was to change all my animation to looping animation, and then manually call pause using a coroutine by setting the yield return waitforseconds to a value of my choosing. :(
     
  19. niflying

    niflying

    Joined:
    Jul 11, 2012
    Posts:
    108
    Great idea, thank you. ^_^
     
    Klausology likes this.
  20. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    Is anyone getting good results with crossfade?
    It jitters a lot in shader mode.
     
  21. Klausology

    Klausology

    Joined:
    Aug 29, 2017
    Posts:
    131
    Hey @laurentlavigne! I used crossfade in shader mode without jitters. Though I was using the built in render pipeline, not sure if that made any difference.
     
  22. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    URP works as well in the demo scene but not in my game. The wrapper code and state machine I use works fine in the old animation component so there must be something about meshanimator that's different.
    Perhaps it's because crossfade is being hit hard nearly every frame and with speed change to match the chararcter speed. How do you call .Crossfade? Can you share some code? Also do you change the speed of the animator?
    I'm wondering if the stutter comes from the speed being too low, I believe shader mode doesn't do blend between the vertex frames so it's a hard frame...
     
  23. Klausology

    Klausology

    Joined:
    Aug 29, 2017
    Posts:
    131
    Hmmm it is definitely strange that it is working in the demo but not in your game. What baking FPS did you use? I noticed that baking FPS at 30 wasn't particularly smooth for my game. Baking at 60fps was satisfactory for my use case. Changing the speed too low does make things not smooth in my experience as well. I do not change the speed of the animator unfortunately.

    My code is relatively simple:

    Code (CSharp):
    1.  
    2. //Chase playah if not too far away
    3. if (playahToEnemyDistance <= 15f)
    4. {
    5.      IsChildrenChasingPlayah[i] = true;
    6.  
    7.      ChildrenMA[i].Crossfade(ChildrenType[i] == 1 ? "Run_Enemy1" : "Walk_Enemy1", 0.4f);
    8. }
    9. else
    10. {
    11.       //Idle if too far away
    12.       IsChildrenIdling[i] = true;
    13.                
    14.       ChildrenMA[i].Crossfade("Idle_Enemy1", 0.4f);
    15. }
    This is a small snippet in an update loop for the enemies. You will notice that I have a run_enemy animation and a walk_enemy animation. I do this instead of adjusting the speed of the animator as I have found that this yields a better result. Mayhaps you could try doing something similar? Also, the boolean (IsChildrenChasingPlayah and IsChildrenIdling) are there to ensure I do not call crossfade again unnecessarily.

    Also, if you wanted some visual, you can see in the first 10 seconds of my video, the enemies are getting pushed constantly and resuming their chase animation frequently without jitters too:

     
    Last edited: Feb 9, 2021
    chadfranklin47 and hopeful like this.
  24. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    I'll bake at 240Hz and see. Good thing my models are low poly :)
    I "think" (can't be too sure with old code that I wrote back when I was a sperm) that the AI spams Play on every frame. This worked perfectly in the legacy animation component, somehow... oh I remember now yeah: if you spam the same clip to legacy it'll assume that you want to continue with the same clip so if you want to restart you needed to Rewind. That's a bold assumption, which we seem to have worked around somehow and worked like fabulous.
    Alright now I hate reading someone else's code but with the dev AWOL...
    Down the rabbit hole I go...
    Gotcha!
    upload_2021-2-9_9-55-46.png
    Each time CrossFade is called, the tick is reset. So yeah durrr it'll jitter when spammed.
    Time to be more gentle with MA.

    This is pissing me off. I'll revise my asstore review down 1 star for each week of silence.
     
    Klausology likes this.
  25. Klausology

    Klausology

    Joined:
    Aug 29, 2017
    Posts:
    131
    Ouh! So that was the issue! Glad it has been solved :)

    Yea, I know how you feel. It is also partly why I am holding back on reviewing the asset as I would very much like to give it a full 5-star review. This asset is truly without equal on the asset store. The asset isn't abandoned, gets updated periodically. It even gets a new and improved shader mode! In fact, the developer does respond, just not every single time. When I first bought this asset and discovered an issue with exposed transform crossfading, the developer responded and provided a fix rather quickly! On top of that, the developer has to deal with complaints that are not even a problem with the asset itself at times. That, and the current state of the world now might explain the occasional missed queries from customers. That being said, it can be a little discouraging when some issues are not acknowledged at all :( ¯\_(ツ)_/¯
     
  26. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    No it's not solved, it requires major change in how the AI pokes at the animation system.
    Same! That's also why I held back rating for 6 month bumping head on all these glitches. 6 month is good patience and ratings are the only way we know what we're getting into and this is potentially a big waste of time so it's important that they reflect the experience. That said, you're right, some ppl are suffering a great deal these days so I'll go easy and just describe the problems for those who read the reviews past the star rating..
     
  27. Klausology

    Klausology

    Joined:
    Aug 29, 2017
    Posts:
    131
    Owww! :confused: That is a bummer, we have to be gentle with MA as you have said indeed.

    You are certainly right about this as well. Hmmmm =/
     
  28. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    I added an extra coat of vaseline and now it's working.
    Here is what I did for those diving into it: added a priority flag to those clips that need to override others, for example an attack, death and test if a new clip is requested by the AI that its flag is either == or > to that of the currently played clip. Now it's nearly indistinguishable from the base legacy behavior. Which is pretty badass on 100s of creatures, on a switch.
     
    hopeful, Klausology and Jmonroe like this.
  29. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Yeah, same here, URP shaders throwing errors on URP 10.3.1. Have you been able to do anything about it by any chance?
     
  30. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    I'm also running into an error when the Bake FPS is set to 120 and the "Frame Skip" values are less than 4:

    DivideByZeroException: Attempted to divide by zero.
    FSG.MeshAnimator.Snapshot.DeltaCompressedFrameData.op_Implicit (FSG.MeshAnimator.Snapshot.DeltaCompressedFrameData s) (at Assets/MeshAnimator/Scripts/Snapshot/SnapshotMeshFrameData.cs:82)
    FSG.MeshAnimator.Snapshot.SnapshotMeshAnimation.get_frameData () (at Assets/MeshAnimator/Scripts/Snapshot/SnapshotMeshAnimation.cs:35)
    FSG.MeshAnimator.Snapshot.SnapshotMeshAnimation.OnEnable () (at Assets/MeshAnimator/Scripts/Snapshot/SnapshotMeshAnimation.cs:55)
    UnityEditor.AssetDatabase:CreateAsset(Object, String)
    FSG.MeshAnimator.MeshAnimationCreator:CreateSnapshots() (at Assets/MeshAnimator/Editor/MeshAnimationCreator.cs:1798)
    FSG.MeshAnimator.MeshAnimationCreator:CreateSnapshots() (at Assets/MeshAnimator/Editor/MeshAnimationCreator.cs:1478)
    FSG.MeshAnimator.MeshAnimationCreator:OnGUI() (at Assets/MeshAnimator/Editor/MeshAnimationCreator.cs:912)
    UnityEngine.GUIUtility:processEvent(Int32, IntPtr, Boolean&)

    Has anyone run into this one before or know what it means?
     
  31. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    bug - this happens after bake:
    upload_2021-2-19_13-30-10.png
     
  32. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    bug -- when recompiling
    upload_2021-2-19_13-31-53.png

    upload_2021-2-19_13-32-6.png
     
  33. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    and in URP, with the included lit shader or custom shader, objects disappear
    upload_2021-2-19_13-35-57.png
     
  34. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    the MA component still counting frames so I don't know what's happening but this bug has been creeping in since version 2.0.18
    upload_2021-2-20_20-59-4.png
    note: disappears also in a build!

    edit: this problem only appears on models that are composed either of multiple meshes merged together or multiple materials.
    solution = switch to snapshot :/
     
    Last edited: Feb 22, 2021
  35. andthenwhat

    andthenwhat

    Joined:
    May 11, 2015
    Posts:
    19
    [Nevermind, SOLVED]
    is there a fix (or ETA for a fix) for the shader errors in URP? I read through the thread since the first mention of the issue, but didn't see one (apologies if I missed it):

    Shader error in 'Mesh Animator/Universal Render Pipeline/Lit': 'InitializeStandardLitSurfaceData': output parameter 'outSurfaceData' not completely initialized at Assets/MeshAnimator/Shaders/URP/MeshAnimator-URP-LitInput.hlsl(89) (on d3d11)

    Shader error in 'Mesh Animator/Universal Render Pipeline/Simple Lit': 'InitializeInputData': output parameter 'inputData' not completely initialized at Assets/MeshAnimator/Shaders/URP/MeshAnimator-URP-SimpleLitForwardPass.hlsl(44) (on d3d11)

    in the shader, if you just start the shader with e.g. outSurfaceData = (SurfaceData)0; it removes the errors and everything seems to work fine.
     
    Last edited: Feb 22, 2021
    Jmonroe likes this.
  36. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Could you be a bit more specific? Where should we put that line in which shader file?
     
  37. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    note: for URP users. if somehow shader animation works for you, know that the shader graph vertex subgraph is about 2x slower than the hand written MA lit!
     
    Last edited: Feb 25, 2021
    Klausology likes this.
  38. SnaiperoG

    SnaiperoG

    Joined:
    Apr 6, 2015
    Posts:
    66
    Also there is a bug with events. IDK was it fixed in recent updates, but. When you see mesh animator, and it play some events, for example play footstep sound. If you turn camera to other way from Manim (no offscreen update), wait some time, and focus camera on Manim again, it will trigger all not played events at ones. As if it stores all events, but not playing them, and when you have Manim on camera again it plays all events, which makes no sence
     
  39. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    Yeah this thing is a mess. In the same vein, I tried using LOD and what happens is the animations get out of sync by more than the deltaFPS.
     
    Klausology likes this.
  40. niflying

    niflying

    Joined:
    Jul 11, 2012
    Posts:
    108
    Hi
    Anyone got this issue?
    the gameobject can't follow the expose transform well on shader mode.
    Please check this out:
    issue.gif
    the weapon can't follow the hand ><
    @jschieck Could you please help?
     
    chadfranklin47 and HenryChinaski like this.
  41. tcz8

    tcz8

    Joined:
    Aug 20, 2015
    Posts:
    504
    Hi everyone!

    I found an interesting video for a similar solution in Unreal that uses the particle system to render the vertex animated meshes. Maybe a good feature for a future version of Mesh Animator? Hard to tell since he also uses other tricks, but he definitely displays an impressive amount of units. It also contains good ideas on how to build and optimize an AI for so many characters.

     
    Last edited: Mar 10, 2021
    Jmonroe likes this.
  42. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    363
    Hello.

    I am very new to this "batching" animation and I have a few questions to see if this fits for my game so if some of these questions are "dumb" i apologize in advance.

    i have an RTS game that needs to support 150 - 200+ units fighting each other these units are controlled by a group controller making them fight each other (with my own AI implementation).

    This means that some of the units may be fighting while other units do stuff like climbing walls and stand idle waiting for orders.

    Can these things be batched? Like can I have agents actually doing their "job" in terms of the AI and still get the benefits of the GPU instancing or is it only stationary units that get "activated" when in range?
     
  43. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    They can, units that play different animations can still be batched in one draw call.

    It works by baking the animation to a texture, and each unit only needs to know their specific animation-offset. There is a limit to the texture size, so you cant have an infinite amount of animations.
     
    Last edited: Mar 16, 2021
  44. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    363
    So if i have a Character Controller like Opsive i can still control my characters movement / actions when barching ?
     
  45. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    and another bug: when the game quits
    upload_2021-3-19_20-7-3.png
     
  46. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    I had the same issue with another asset (GPUInstancer - Crowd Animations) and they said it was because to get the exposed transform position, they have to query the GPU asynchronously. Though i'm not 100% sure that's the same issue here, but it would make sense. I don't understand why they can't just bake a list of vector3's beforehand and just use that. Maybe it's possible with a custom solution. Maybe bake a list of 60 vector3's (for a 1 second 60fps animation) and set the bone position ourselves.

    Edit: My curiosity is getting the better of me. I found that if you maximize the game view, the transforms sync much better. Digging a little deeper, it seems RefreshTimeData() in ShaderMeshAnimator.cs needs to be called (it's called by OnApplicationFocus()). It's still not as good as snapshot mode though...

    Edit 2: Check my post 3 posts below this one for a solution.
     
    Last edited: Apr 3, 2021
  47. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Also, if anyone else is having issues with the URP shaders, thanks to @andthenwhat , put "outSurfaceData = (SurfaceData)0;" at line 91 of Assets\MeshAnimator\Shaders\URP\MeshAnimator-URP-LitInput.hlsl and "inputData = (InputData)0;" at line 46 of Assets\MeshAnimator\Shaders\URP\MeshAnimator-URP-SimpleLitForwardPass.hlsl.
    This at least gets the models animating, but the bounds aren't working properly so the mesh disappears when it should be on screen. I'll have to duplicate the mesh and edit the bounds. Rather than doing this for each instance, only do it for one mesh and set the MeshFilter.sharedMesh property of all instances.
     
    Last edited: Apr 1, 2021
  48. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Ok, just got done with a long debugging session. If you encounter a bug with the Shader workflow when baking animations and the textures come out as 0x0 dimensions "degenerate textures", open MeshAnimationCreator.cs, and under line 1798 "AssetDatabase.CreateAsset(meshAnim, meshAnimationPath);", add:
    AssetDatabase.SaveAssets();

    Not sure why without this, the error happens, but I believe this bug can only happen when using "Bake Texture Size" - "Smallest Common" or "Most Common". This happened for me when baking to a location, deleting the assets (for whatever reason), then baking again to the same location.
     
    Last edited: Apr 1, 2021
  49. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    Edit: In my opinion, spare yourself the trouble and avoid Shader Mode, for now at least. With some modifications not showcased here (such as using a single IJobParallelForTransform to update the exposedTransforms), I was able to get Snapshot mode to work fine and more performant, but with many units, crossfading simply becomes impossible. I'll leave this here though.

    I apologize for the spam (and the long post), but I've noticed quite a few more things you guys might find useful. Trying to write these down before I forget all the changes I made.

    As the code is currently... when using Shader mode, after entering a crossfade, exposedTransformCrossfade.isFading is never reset to false as it should be, and like it is with Snapshot mode. I added:

    Code (CSharp):
    1. if (exposedTransformCrossfade.isFading)
    2. {
    3.     if (exposedTransformCrossfade.currentFrame >= exposedTransformCrossfade.framesNeeded)
    4.     {
    5.         ReturnCrossfadeToPool(false);
    6.     }
    7. }
    in the DisplayFrame() method of ShaderMeshAnimator.cs right before the final return statement and that seems to work fine.

    Now the more complex stuff:

    I started off by playing the same animation side by side, one using snapshot mode and one using shader mode. My eyes picked up the slightest jitter on the SHADERMeshAnimator. With both animations running side by side, I paused the editor and began clicking the "Next Frame" button. While the SNAPSHOTMeshAnimator would switch to a new frame every click, the SHADERMeshAnimator would sometimes stick on the same animation frame for 2 game frames.

    To diagnose the problem, I needed more control, so I created the attached scripts. Here is a preview:

    Code (CSharp):
    1. public ShaderMeshAnimator shaderMeshAnimator;
    2.  
    3.     [Range(0, 100)]
    4.     public int testCurrentFrame;
    5.     private int lastTestCurrentFrame;
    6.  
    7.     private void OnEnable()
    8.     {
    9.         shaderMeshAnimator.Pause();
    10.     }
    11.  
    12.     void Update()
    13.     {
    14.         if (testCurrentFrame != lastTestCurrentFrame)
    15.         {
    16.             shaderMeshAnimator.TestSetAnimationCurrentFrame(testCurrentFrame);
    17.             lastTestCurrentFrame = testCurrentFrame;
    18.         }
    19.     }
    And added this public function to ShaderMeshAnimator.cs:

    Code (CSharp):
    1. public void TestSetAnimationCurrentFrame(int currentFrame)
    2.         {
    3.             float shaderTime = _shaderTime.y;
    4.             timeBlockData.x = currentFrame;
    5.             timeBlockData.y = currentFrame;
    6.             timeBlockData.z = shaderTime;
    7.             timeBlockData.w = shaderTime;
    8.  
    9.             meshRenderer.GetPropertyBlock(materialPropertyBlock);
    10.             materialPropertyBlock.SetVector(_animTimeProp, timeBlockData);
    11.             // set property block
    12.             meshRenderer.SetPropertyBlock(materialPropertyBlock);
    13.         }
    To use it, add the script to a scene object with the ShaderMeshAnimator component, but have it disabled before you hit play. Then during playmode, enable the script and drag the slider to change the exact frame of the animation that will be shown. Also make sure that "Play Automatically" is enabled.

    Now in my case (it should differ depending on how many vertices your mesh has, and the size of your animation textures), I found that without fail, going from frame 27 to frame 28 (and a few other frame pairs) would yield no change to the visuals of the ShaderMeshAnimator. I would love to know if other people notice this and if my fix works for you as well.

    I will say this was not the solution I was expecting and also that I have almost zero experience with shaders. Anyway, there were three shader files I had to edit:
    Assets\MeshAnimator\Shaders\MeshAnimator.cginc
    Assets\MeshAnimator\ShaderGraph\MeshAnimator.hlsl
    Assets\MeshAnimator\Shaders\MeshAnimator.hlsl

    Each of these has a "GetPixelOffset()" method. They all have these lines:
    Code (CSharp):
    1. float1 localOffset = floor(currentFrame / framesPerTexture);
    2. float1 frameOffset = floor(currentFrame % framesPerTexture);
    Change those to:
    Code (CSharp):
    1. float1 localOffset = floor(currentFrame / framesPerTexture + 0.0001);
    2. float1 frameOffset = floor((currentFrame % framesPerTexture) + 0.0001);
    Note: In MeshAnimator.cginc, use "float" rather than "float1".

    My best guess is that there is some precision issue and adding a small value prevents the floor() method from going one int too low.

    Ok, so that fixed the issue with the frame doubling. But there's more. This one I might consider optional, especially if you don't have transforms exposed, but my SHADERMeshAnimator didn't sync up perfectly with the SNAPSHOTMeshAnimator until doing this as well. At the top of the same "GetPixelOffset()" method in those three shader files, there seems to be a problem with the calculation of normalizedTime and I simply couldn't get it to work (please let me know if you manage to figure it out). This is the culprit behind the exposeTransforms not syncing perfectly. It's not a problem with the exposeTransforms, but with the ShaderMeshAnimator. But my workaround is not to calculate normalizedTime at all, and instead, just pass in the current frame (though this does have a small performance overhead). Back in ShaderMeshAnimator.cs, directly above the if statement I added at the beginning of this post, add:
    Code (CSharp):
    1. timeBlockData.x = currentFrame;
    2. //meshRenderer.GetPropertyBlock(materialPropertyBlock);
    3. materialPropertyBlock.SetVector(_animTimeProp, timeBlockData);
    4. meshRenderer.SetPropertyBlock(materialPropertyBlock);
    I don't think the GetPropertyBlock() call is necessary so I commented it out. I also haven't had any problems after commenting out this block of code from the method (I believe only since I'm directly passing in the currentFrame):
    Code (CSharp):
    1.             float currentStartTime = timeBlockData.z;
    2.             float currentEndTime = timeBlockData.w;
    3.             float newEndTime = currentStartTime + (currentAnimation.length / (speed * currentAnimation.playbackSpeed));
    4.             if (currentEndTime != newEndTime)
    5.             {
    6.                 timeBlockData.w = newEndTime;
    7.                 meshRenderer.GetPropertyBlock(materialPropertyBlock);
    8.                 materialPropertyBlock.SetVector(_animTimeProp, timeBlockData);
    9.                 meshRenderer.SetPropertyBlock(materialPropertyBlock);
    10.             }
    Then back at the top of the three "GetPixelOffset()" methods in the shader files, comment out the top three lines and paste
    float currentFrame = animTimeInfo.x;
    in MeshAnimator.cginc and
    float1 currentFrame = animTimeInfo.x;
    in the other two files directly below the newly commented out lines.

    This works because _AnimTimeInfo.x (timeBlockData.x in this case) is currently unused (I didn't find anywhere that is using it) so we can assign to it and read from it in the shader files. This inadvertently solved all but one (which I will address in a moment) of my problems with the syncing exposeTransforms because I am now directly controlling the current frame of the GPU Instanced animator. Yesterday, 3 posts ago, I noted that RefreshTimeData() needed to get called for the animations to sync to the transforms properly and that reopening the Game window will get that called. That is no longer necessary after implementing this. I commented out all calls to RefreshTimeData() and I have no problems so far.

    Now, as mentioned earlier, my final problem with exposeTransforms comes when I try to crossfade. The SHADERMeshAnimator looks a bit different (and better in my case) than the SnapshotMeshAnimator when crossfading. The crossfade is smoother when using SHADERmeshAnimator while crossfading with SNAPSHOTMeshAnimator is more "jumpy" and gave me these weird artifacts on the mesh. The problem is, the current method of crossfading, which occurs in the DisplayFrame() Method of MeshAnimatorBase.cs, has the transforms acting "jumpy" like they would when childed to a SNAPSHOTMeshAnimator. And they sync perfectly when childed to a SNAPSHOTMeshAnimator, but they're not in this case. In this case, the head of my character, which is childed to my "head bone" exposeTransform, temporarily visually disconnects from the body until the crossfade is complete and of course I can't have that. (Edit: I believe this is because the original crossfading method continues crossfading to the initial frame of the animation rather than to the current frame. It's possible it could be fixed for Snapshot mode as well.)

    Since the workflow is going to be different between SHADERMeshAnimator and SNAPSHOTMeshAnimator now, I needed to move the crossfade code for the SHADERMeshAnimator out of MeshAnimatorBase and into ShaderMeshAnimator.cs. This one requires the most changes to the code. First, add
    protected bool isShaderCrossfading;
    to MeshAnimatorBase.cs. Then, in ShaderMeshAnimator.cs, add
    isShaderCrossfading = false;
    to the OnCurrentAnimationChanged() method. This ensures that when starting to play a new animation, we stop the current crossfade if any. Now add:
    Code (CSharp):
    1.  
    2. private int crossfadeFramesNeeded;
    3.         private int currentCrossfadeFrame;
    4.         private MeshFrameDataBase crossfadeFromFrame;
    5.  
    to ShaderMeshAnimator.cs. Now if you made it this far, you'll want to remove the if statement I added at the beginning of this post as exposedTransformCrossfade is no longer going to be used. Comment out the final return statement and add this below:
    Code (CSharp):
    1.  
    2. if (hasExposedTransforms && isShaderCrossfading)
    3.             {
    4.                 if (currentCrossfadeFrame >= crossfadeFramesNeeded)
    5.                 {
    6.                     currentCrossfadeFrame = 0;
    7.                     isShaderCrossfading = false;
    8.                 } else {
    9.                     MeshFrameDataBase toFrame = currentAnimation.GetNearestFrame(currentFrame);
    10.  
    11.                     var exposedTransforms = currentAnimation.ExposedTransforms;
    12.                     float lerpValue = currentCrossfadeFrame / (float)crossfadeFramesNeeded;
    13.  
    14.                     for (int i = 0; i < exposedTransforms.Length; i++)
    15.                     {
    16.                         Transform child = null;
    17.                         if (childMap.TryGetValue(exposedTransforms[i], out child))
    18.                         {
    19.                             Matrix4x4 c = MatrixLerp(crossfadeFromFrame.exposedTransforms[i], toFrame.exposedTransforms[i], lerpValue);
    20.                             MatrixUtils.FromMatrix4x4(child, c);
    21.                         }
    22.                     }
    23.  
    24.                     currentCrossfadeFrame++;
    25.                 }
    26.             }
    27.  
    28.             // generate frames if needed and show the current animation frame
    29.             currentAnimation.GenerateFrame(baseMesh, currentFrame);
    30.             currentAnimation.DisplayFrame(this, currentFrame, previousFrame);
    31.             return true;
    32.  
    You will also need to change the MatrixLerp() method inside MeshAnimatorBase.cs to protected rather than private.

    Note: There is some more code you'll need to move over from MeshAnimatorBase.cs if you want to get it working with Root Motion.

    Now, in the Crossfade() method of ShaderMeshAnimator, comment out
    base.StartCrossfade(index, speed);
    and add below:
    Code (CSharp):
    1.  
    2. if (index < 0 || animations.Length <= index || currentAnimIndex == index)
    3.                 return;
    4.             if (currentAnimation == null)
    5.             {
    6.                 crossfadeFromFrame = defaultAnimation.GetNearestFrame(0);
    7.             }
    8.             else
    9.             {
    10.                 crossfadeFromFrame = currentAnimation.GetNearestFrame(currentFrame);
    11.             }
    12.             Play(index);
    13.             currentCrossfadeFrame = 0;
    14.             crossfadeFramesNeeded = Mathf.Max((int)(speed * FPS), 1);
    15.             isShaderCrossfading = true;
    16.  
    And finally, inside MeshAnimatorBase.cs, in the UpdateTick() method, find
    if (hasExposedTransforms)
    and change it to
    if(hasExposedTransforms && !isShaderCrossfading)
    . This will ensure that when crossfading, it doesn't update the transform positions as it does normally.

    Here is a video showing what it looks like before & after. The ShaderMeshAnimator is on the right while the SnapshotMeshAnimator is on the left. (Edit: after making several changes now in this post, the end result looks even better than in the video):


    I still think the exposeTransform crossfade could look a little better (it is more apparent with longer crossfades). Please let me know if you can find a better way of doing it. (Edit: I have updated the post to my newer and better crossfading method.)

    I'll also upload the two helper scripts that I used to manually control the snapshot and shader MeshAnimators. I believe that is all I have for now, hope I didn't forget anything. If I remember any other changes I made, I will add those along with any notable future changes. Hope this helps someone. I would like to know what you @jschieck think about these findings as you would know how to integrate these suggestions and fixes a lot better.

    Edit:
    When using SetTime() or SetTimeNormalized(), I needed to paste:
    lastFrameTime = Time.time;
    after the line with "currentAnimTime = ...". Without this, float actualDelta inside the UpdateTick() method will be added to currentAnimTime and cause the animation not to start where you want it to.
    I have also made several changes above.
     

    Attached Files:

    Last edited: Apr 20, 2021
  50. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,362
    Consider this thread yours.