Skip to main content
added 441 characters in body
Source Link
Adam B
  • 800
  • 3
  • 15

Based on @DMGregory 's helpful comments, here is my solution:


To Cache (called in OnEnable()):

        void CacheMeshAndSkeleton() {
            if (originalBonePositions == null) {
                originalBonePositions = new Vector3[skinnedMeshRenderer.bones.Length];
                for (int boneIndex = 0; boneIndex < skinnedMeshRenderer.bones.Length; boneIndex++) {
                    originalBonePositions[boneIndex] = skinnedMeshRenderer.bones[boneIndex].position;
                }
            }
            if (originalMesh == null) originalMesh = skinnedMeshRenderer.sharedMesh;
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);          
        }

Note that the null-check isn't really needed. For me it was, since I'm running the script in edit mode using [ExecuteInEditMode], and edit mode seems to call OnEnable more than once, so I needed to prevent it from overwriting the original mesh with a deformed one.


To Reset:

        void ResetMeshAndSkeleton() {
            for (int boneIndex = 0; boneIndex < originalBonePositions.Length; boneIndex++) {
                skinnedMeshRenderer.bones[boneIndex].position = originalBonePositions[boneIndex];
            }
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);
            // Note that it seems to me that order might be important here. 
            // If you reset the the Mesh first, when you move the bones back, 
            // it will apply linearblendskinning to the mesh and deform it to the bone positions. 
            // Not sure why this matters, but seemed to in my testing
        }

This seems to work great!

UPDATE:

I found an increase in performance by instead caching the mesh's arrays (vertices, etc) and swapping them back to restore.

I used the CopyTo array function. sourceArray.CopyTo(destinationArray, 0); where 0 is the index to start copying from.

In my case I was only manipulating the vertices, the tris, normals, etc were never altered, so just copying this one array saved a huge amount of performance vs. instantiation.

Based on @DMGregory 's helpful comments, here is my solution:


To Cache (called in OnEnable()):

        void CacheMeshAndSkeleton() {
            if (originalBonePositions == null) {
                originalBonePositions = new Vector3[skinnedMeshRenderer.bones.Length];
                for (int boneIndex = 0; boneIndex < skinnedMeshRenderer.bones.Length; boneIndex++) {
                    originalBonePositions[boneIndex] = skinnedMeshRenderer.bones[boneIndex].position;
                }
            }
            if (originalMesh == null) originalMesh = skinnedMeshRenderer.sharedMesh;
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);          
        }

Note that the null-check isn't really needed. For me it was, since I'm running the script in edit mode using [ExecuteInEditMode], and edit mode seems to call OnEnable more than once, so I needed to prevent it from overwriting the original mesh with a deformed one.


To Reset:

        void ResetMeshAndSkeleton() {
            for (int boneIndex = 0; boneIndex < originalBonePositions.Length; boneIndex++) {
                skinnedMeshRenderer.bones[boneIndex].position = originalBonePositions[boneIndex];
            }
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);
            // Note that it seems to me that order might be important here. 
            // If you reset the the Mesh first, when you move the bones back, 
            // it will apply linearblendskinning to the mesh and deform it to the bone positions. 
            // Not sure why this matters, but seemed to in my testing
        }

This seems to work great!

Based on @DMGregory 's helpful comments, here is my solution:


To Cache (called in OnEnable()):

        void CacheMeshAndSkeleton() {
            if (originalBonePositions == null) {
                originalBonePositions = new Vector3[skinnedMeshRenderer.bones.Length];
                for (int boneIndex = 0; boneIndex < skinnedMeshRenderer.bones.Length; boneIndex++) {
                    originalBonePositions[boneIndex] = skinnedMeshRenderer.bones[boneIndex].position;
                }
            }
            if (originalMesh == null) originalMesh = skinnedMeshRenderer.sharedMesh;
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);          
        }

Note that the null-check isn't really needed. For me it was, since I'm running the script in edit mode using [ExecuteInEditMode], and edit mode seems to call OnEnable more than once, so I needed to prevent it from overwriting the original mesh with a deformed one.


To Reset:

        void ResetMeshAndSkeleton() {
            for (int boneIndex = 0; boneIndex < originalBonePositions.Length; boneIndex++) {
                skinnedMeshRenderer.bones[boneIndex].position = originalBonePositions[boneIndex];
            }
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);
            // Note that it seems to me that order might be important here. 
            // If you reset the the Mesh first, when you move the bones back, 
            // it will apply linearblendskinning to the mesh and deform it to the bone positions. 
            // Not sure why this matters, but seemed to in my testing
        }

This seems to work great!

UPDATE:

I found an increase in performance by instead caching the mesh's arrays (vertices, etc) and swapping them back to restore.

I used the CopyTo array function. sourceArray.CopyTo(destinationArray, 0); where 0 is the index to start copying from.

In my case I was only manipulating the vertices, the tris, normals, etc were never altered, so just copying this one array saved a huge amount of performance vs. instantiation.

Source Link
Adam B
  • 800
  • 3
  • 15

Based on @DMGregory 's helpful comments, here is my solution:


To Cache (called in OnEnable()):

        void CacheMeshAndSkeleton() {
            if (originalBonePositions == null) {
                originalBonePositions = new Vector3[skinnedMeshRenderer.bones.Length];
                for (int boneIndex = 0; boneIndex < skinnedMeshRenderer.bones.Length; boneIndex++) {
                    originalBonePositions[boneIndex] = skinnedMeshRenderer.bones[boneIndex].position;
                }
            }
            if (originalMesh == null) originalMesh = skinnedMeshRenderer.sharedMesh;
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);          
        }

Note that the null-check isn't really needed. For me it was, since I'm running the script in edit mode using [ExecuteInEditMode], and edit mode seems to call OnEnable more than once, so I needed to prevent it from overwriting the original mesh with a deformed one.


To Reset:

        void ResetMeshAndSkeleton() {
            for (int boneIndex = 0; boneIndex < originalBonePositions.Length; boneIndex++) {
                skinnedMeshRenderer.bones[boneIndex].position = originalBonePositions[boneIndex];
            }
            skinnedMeshRenderer.sharedMesh = Instantiate(originalMesh);
            // Note that it seems to me that order might be important here. 
            // If you reset the the Mesh first, when you move the bones back, 
            // it will apply linearblendskinning to the mesh and deform it to the bone positions. 
            // Not sure why this matters, but seemed to in my testing
        }

This seems to work great!