# Flipbook keyframe animation with individual mesh per frame

**URL:** <https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661>\
**Category:** USD\
**Tags:** beginner, usd\
**Created:** [August 28, 2025, 7:38am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661 "2025-08-28T07:38:42Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![georginikolov](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/georginikolov/32/974_2.png) [@georginikolov](https://forum.aousd.org/u/georginikolov)\
**Post date:** [August 28, 2025, 7:38am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/1 "2025-08-28T07:38:42Z")

</div>

Hello all,

I have a LiDAR point cloud animation which I want to generate a USD version of. I have different particles count possible in each frame, so I want to generate per-frame points mesh that I flip through on each new frame.

I managed to generate this with an AI agent and it does seem like what I am after:

```auto
#usda 1.0
(
    defaultPrim = "World"
    endTimeCode = 2
    framesPerSecond = 1
    startTimeCode = 0
)

def Xform "World"
{
    def Points "Frame_0"
    {
        point3f[] points = [
            (-3.0, 0.0, 0.0), (-2.5, 0.2, 0.1), (-2.0, 0.0, 0.0), (-1.5, -0.1, 0.2), (-1.0, 0.0, 0.0),
            (-0.5, 0.3, -0.1), (0.0, 0.0, 0.0), (0.5, -0.2, 0.3), (1.0, 0.0, 0.0), (1.5, 0.1, -0.2),
            (2.0, 0.0, 0.0), (-2.2, 0.5, 0.4), (-0.8, -0.3, 0.1), (0.3, 0.4, -0.3), (1.8, -0.1, 0.2)
        ]
        color3f[] primvars:displayColor = [
            (1.0, 0.0, 0.0), (0.9, 0.1, 0.0), (0.8, 0.2, 0.0), (0.7, 0.3, 0.0), (0.6, 0.4, 0.0),
            (0.5, 0.5, 0.0), (1.0, 0.0, 0.2), (0.9, 0.0, 0.3), (0.8, 0.0, 0.4), (0.7, 0.0, 0.5),
            (0.6, 0.0, 0.6), (1.0, 0.2, 0.0), (0.8, 0.4, 0.0), (0.6, 0.6, 0.0), (0.9, 0.1, 0.1)
        ] (
            interpolation = "vertex"
        )
        float[] widths = [
            0.8, 1.0, 0.9, 1.2, 0.7, 1.1, 0.6, 1.3, 0.8, 0.9, 1.0, 0.7, 1.2, 0.8, 1.1
        ]
        
        token visibility.timeSamples = {
            0: "inherited",
            1: "invisible", 
            2: "invisible"
        }

        uniform token subdivisionScheme = "none"
        double3 xformOp:rotateXYZ = (0, 0, 0)
        double3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (0, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
    }

    def Points "Frame_1" 
    {
        point3f[] points = [
            (-2.8, 1.2, 0.5), (-2.0, 1.8, 0.3), (-1.3, 0.9, -0.2), (-0.7, 1.5, 0.4), (0.2, 0.7, 0.1),
            (0.8, 1.3, -0.3), (1.5, 0.4, 0.6), (2.2, 1.1, -0.1), (2.7, 0.8, 0.2), (-2.5, -0.6, 0.7),
            (-1.1, 1.7, -0.4), (0.4, -0.8, 0.5), (1.7, 1.6, 0.0), (2.3, -0.3, 0.8), (0.0, 2.1, -0.2)
        ]
        color3f[] primvars:displayColor = [
            (0.0, 1.0, 0.0), (0.1, 0.9, 0.2), (0.2, 0.8, 0.4), (0.0, 0.7, 0.6), (0.3, 0.6, 0.1),
            (0.1, 0.9, 0.3), (0.0, 0.8, 0.5), (0.2, 0.7, 0.2), (0.0, 1.0, 0.1), (0.4, 0.6, 0.0),
            (0.1, 0.8, 0.4), (0.0, 0.9, 0.2), (0.3, 0.7, 0.1), (0.0, 0.6, 0.7), (0.2, 1.0, 0.0)
        ] (
            interpolation = "vertex"
        )
        float[] widths = [
            1.2, 0.8, 1.4, 0.6, 1.6, 0.9, 1.1, 1.3, 0.7, 1.5, 0.8, 1.0, 1.2, 0.9, 1.4
        ]
        
        token visibility.timeSamples = {
            0: "invisible",
            1: "inherited",
            2: "invisible"
        }

        uniform token subdivisionScheme = "none"
        double3 xformOp:rotateXYZ = (0, 0, 0)
        double3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (0, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
    }

    def Points "Frame_2"
    {
        point3f[] points = [
            (-1.5, -1.8, 1.0), (-0.9, -2.3, 0.8), (-0.2, -1.4, 1.2), (0.6, -2.0, 0.6), (1.3, -1.1, 0.9),
            (1.9, -1.7, 1.1), (2.5, -0.8, 0.7), (-2.1, 0.9, -0.5), (-1.4, -0.4, 1.3), (-0.6, 0.6, -0.8),
            (0.1, -1.9, 1.4), (0.9, 0.3, -0.6), (1.6, -1.2, 1.0), (2.2, 0.7, -0.4), (-0.3, 1.8, 0.9)
        ]
        color3f[] primvars:displayColor = [
            (0.0, 0.0, 1.0), (0.2, 0.0, 0.9), (0.4, 0.0, 0.8), (0.6, 0.0, 0.7), (0.1, 0.2, 0.9),
            (0.3, 0.1, 0.8), (0.0, 0.3, 1.0), (0.2, 0.2, 0.8), (0.4, 0.0, 0.9), (0.0, 0.4, 0.7),
            (0.5, 0.1, 0.8), (0.1, 0.0, 1.0), (0.3, 0.2, 0.6), (0.0, 0.1, 0.9), (0.4, 0.3, 0.7)
        ] (
            interpolation = "vertex"
        )
        float[] widths = [
            0.9, 1.3, 0.7, 1.5, 0.8, 1.1, 1.4, 0.6, 1.2, 1.0, 0.9, 1.3, 0.8, 1.4, 1.1
        ]
        
        token visibility.timeSamples = {
            0: "invisible",
            1: "invisible",
            2: "inherited"
        }

        uniform token subdivisionScheme = "none"
        double3 xformOp:rotateXYZ = (0, 0, 0)
        double3 xformOp:scale = (1, 1, 1)
        double3 xformOp:translate = (0, 0, 0)
        uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
    }
}

```

So far I can play this file correctly in macOS preview. It also aligns with [this sample](https://github.com/ft-lab/sample_usd/blob/main/samples/PointClouds/PointClouds.usda.) I found online.

When I try to convert it to USDZ to send it as an iMessage for example, the file no longer plays back. I am using `usdzip` to create my `.usdz` file. My questions are:

1. The `token visibility.timeSamples = { “visible”, “hidden” }` playback approach seems correct to me? Core glTF does not have anything like `visibility.timeSamples` so I am forced to move inactive frames to position (1000, 0, 0) instead in my exporter.
2. Why would the `.usdz` file produced by `usdzip` fail opening and playing back? Can such a point cloud animation be achieved? Ideally I want to be able to play the point clouds in iMessage / macOS as I am aiming towards a more casual crowd.

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![dhruvgovil](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/dhruvgovil/32/3_2.png) [@dhruvgovil](https://forum.aousd.org/u/dhruvgovil)\
**Post date:** [August 29, 2025, 12:13am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/2 "2025-08-29T00:13:11Z")

</div>

Hi!

so the issue you’re hitting is that RealityKit doesn’t support vertex animation.

see this link for the differences in renderers [Validating feature support for USD files | Apple Developer Documentation](https://developer.apple.com/documentation/usd/validating-usd-files)

---

<div class="post-metadata">

**Author:** ![georginikolov](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/georginikolov/32/974_2.png) [@georginikolov](https://forum.aousd.org/u/georginikolov)\
**Post date:** [August 29, 2025, 10:34am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/3 "2025-08-29T10:34:55Z")

</div>

Thanks for the answer @dhruvgovil! I read through the linked docs and all makes sense.

Another approach I had in mind was to instance a shape many times to achieve my point clouds. I can see however that `PointInstancer` is not supported either.

 ![Screenshot 2025-08-29 at 12.32.55](https://us1.discourse-cdn.com/flex016/uploads/aousd/original/1X/14b6aea0ee23e2829b8fdd7f697d334a242af62c.png)

I suppose I can’t do anything to achieve animated point clouds? I guess for now I will export each point cloud frame as a single merged mesh.

---

<div class="post-metadata">

**Author:** ![dhruvgovil](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/dhruvgovil/32/3_2.png) [@dhruvgovil](https://forum.aousd.org/u/dhruvgovil)\
**Post date:** [August 29, 2025, 7:14pm UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/4 "2025-08-29T19:14:27Z")

</div>

Correct, unfortunately you’d currently have to do it via meshes.

You could do some tricks in a shader or code. In code you could hide and unhide stuff. For shaders, you could drive properties of the mesh by time.

A common technique to get around the lack of vertex animation in many realtime engines is a vertex animation texture. Basically baking the position deltas down per frame and then using a shader to apply the deltas.

But very much depends on the specific projects as there’s no silver bullet

---

<div class="post-metadata">

**Author:** ![georginikolov](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/georginikolov/32/974_2.png) [@georginikolov](https://forum.aousd.org/u/georginikolov)\
**Post date:** [August 30, 2025, 6:24pm UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/5 "2025-08-30T18:24:10Z")

</div>

Interesting, as I was reading on the docs I did not see mention of programmability. I want to support QuickLook / macOS preview primarily. Could runtime logic be inserted somehow?

---

<div class="post-metadata">

**Author:** ![dhruvgovil](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/dhruvgovil/32/3_2.png) [@dhruvgovil](https://forum.aousd.org/u/dhruvgovil)\
**Post date:** [August 30, 2025, 9:50pm UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/6 "2025-08-30T21:50:39Z")

</div>

Apologies, I didn’t mean that you could program it directly within the USD. But you could if you were making an app

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [August 31, 2025, 12:10am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/7 "2025-08-31T00:10:33Z")

</div>

Also just wanted to note, even though it doesn’t help with the iOS/USDZ target, that the example you found isn’t really using the schemas in their intended, most efficient way. Firstly, the `Points` schemas has no `subdivisionScheme` attribute (that belongs only to `Mesh`), but more importantly, all of the attributes in the schema are directly animatable with timeSamples, so rather than needing to make a separate `Points` prim for each frame and coordinating each’s visibility, you can just animate the `points`, `primvars:displayColor`, and `widths` attributes over time. That should work equally well in MacOS preview (I believe since it’s using Storm), and most DCC integrations.

---

<div class="post-metadata">

**Author:** ![georginikolov](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/georginikolov/32/974_2.png) [@georginikolov](https://forum.aousd.org/u/georginikolov)\
**Post date:** [August 31, 2025, 4:15am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/8 "2025-08-31T04:15:10Z")

</div>

Thanks for the input all. I have different amount of points per frame so morphing would not work.

my questions have been answered - thanks!

---

<div class="post-metadata">

**Author:** ![thomaskumlehnvisales](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thomaskumlehnvisales](https://forum.aousd.org/u/thomaskumlehnvisales)\
**Post date:** [September 4, 2025, 7:02am UTC](https://forum.aousd.org/t/flipbook-keyframe-animation-with-individual-mesh-per-frame/2661/9 "2025-09-04T07:02:44Z")

</div>

Last time, I tried a similar approach to work in AR Quick Look, I used scale = (0,0,0) instead of visibility or moving erratically. Problem with this: It can cause motion blur.

To counter/reduce that, try to either increase the fps from 60 to 600, or use timeSamples frames like 0.95, 1.0, 1.05
