-
Notifications
You must be signed in to change notification settings - Fork 222
WTF Is Up Skinned Mesh Renderers
One of the most common questions I get asked is, "Does Deform support skinned mesh renderers?" My answer is always "yes, but not how you want."
This is due to how Unity updates skinned meshes. There is no hook that allows us to performantly modify a snapshot of the skinned mesh before it is rendered. The only thing we have access to is the unanimated shared mesh. This means that the Deformable has to modify the mesh before the skinned vertices are moved by their joints.
So what does that look like?
Well, it's not always great. Here's an example:
Notice how the squash and stretch effect looks fine at first, but as soon as I rotate the arms away from their default poses it completely breaks. This is because the Deformable is modifying the mesh before the joints affect it. So in this example the model gets squashed and then the arm joints rotate the arms upwards.
So is it possible to correct the order of operations, and have Deform run on the resulting skinned mesh?
Technically yes, but be warned this is very slow and hacky. (it really is so slow that I hesitate to share this workaround 😅)
While we don't have access to the final skinned mesh that is rendered, Unity allows us to bake out a snapshot to our own mesh using skinnedMeshRenderer.Bake(outputMesh)
This means we could theoretically write a script that bakes out a skinned mesh every frame and sends it off to a "normal" Deformable with a Mesh Filter for deformation and rendering. Reminder: this is incredibly expensive and weird, but sometimes ya gotta do weird shit.
I've gone ahead and written a simple script that should handle interfacing between a plain skinned mesh renderer and a deformable on a Mesh Filter. Point it at a Deformable and a disabled SkinnedMeshRenderer and it will take care of baking out the skinned mesh and sending it to the Deformable.
Here's how my hierarchy is set up with Liam, my test mesh
and here's the component:
using Deform;
using UnityEngine;
[ExecuteAlways]
public class SkinnedMeshBaker : MonoBehaviour
{
public SkinnedMeshRenderer source;
public Deformable target;
private Mesh bakedMesh;
private void OnEnable()
{
if (target == null)
return;
target.UpdateMode = UpdateMode.Custom;
}
private void OnDisable()
{
if (target != null)
target.Complete();
}
private void Update()
{
if (source == null || target == null)
return;
if (bakedMesh == null)
bakedMesh = new Mesh();
// Bake the skinned mesh
source.BakeMesh(bakedMesh);
// Send it to the Deformable
target.ChangeMesh(bakedMesh);
// Schedule the deformable's calculations
target.PreSchedule();
target.Schedule ();
}
private void LateUpdate()
{
if (target == null)
return;
// Complete the deformable calculations
target.Complete();
// And apply the new data
target.ApplyData();
}
}
Once setup, it looks like this. The order of operations is correct now...but at what cost? (⊙_⊙;)
Home
Installation
Getting Started
Creating a Custom Deformer
Masking
Optimization Tips
WTF Is Up Skinned Mesh Renderers?
Deform
DeformableDeformer
DeformableManager
Deformers
BendDeformerBlendDeformer
BulgeDeformer
CubifyDeformer
CurveDisplaceDeformer
CurveScaleDeformer
CylindrifyDeformer
InflateDeformer
LatheDisplaceDeformer
MagnetDeformer
MeltDeformer
RadialCurveDeformer
RadialSkewDeformer
RippleDeformer
ScaleDeformer
SineDeformer
SkewDeformer
SpherifyDeformer
SquashAndStretchDeformer
StarDeformer
TaperDeformer
TextureDisplaceDeformer
TransformDeformer
TransformOffsetDeformer
TwirlDeformer
TwistDeformer
UVOffsetDeformer
UVScaleDeformer
WaveDeformer