Assigning time codes to individual points of UsdGeomPoints

Hello,

I am currently using USD Python API and experimenting with UsdGeomPoints. I’d like to assign UsdTimeCodes to individual points on the points attribute of the UsdGeomPoints structure.

Let me explain it in a little bit more detail:

Let’s say I have 5 3D points in an array PTS = [P_0, P_1, P_2, P_3, P_4] (assuming P_i == (x_i, y_i, z_i) where i = [0, 1, 2, 3, 4]). Using UsdGeom.Points.GetPointsAttr returns Usd.Attribute, and using Usd.Attribute.Set method, I can set PTS array to UsdGeomPoints’s points attribute.

Usd.Attribute.Set takes a time code as the second argument on the function signature and by default it is Usd.TimeCode.Default(). However, the second argument is not an array of Usd.TimeCode elements.

I am wondering if it is possible to assign the time code array, which is the same size of my PTS array, to the points, e.g. TC = [T_0, T_1, T_2, T_3, T_4] where T_i = Usd.TimeCode(i)

I prefer not to create individual UsdGeom.Points element for each point I have as there is a possibility that PTS array might have more than 10 million elements and the points in the PTS array has no difference other than their time codes.

Do you have any suggestions on adding individual time codes to a point cloud in USD?

Thanks in advance.

First let’s be clear about what the UsdTimeCode parameter on Set() is for: it’s about animating the attribute value. So for the UsdGeomPoints.points attribute, it allows you to add timeSamples of the point positions at various points in time, so that the points will animate over time. Sample-to-Sample coherence of elements within the array is not mandatory, and you can use the ids member to track identity of points over time.

With that understanding (if it makes sense) can you please describe your problem again?

Thanks @spiff for the quick response. What you described is exactly what I would like to achieve: animating points over time

In my case, the animation of the points would look like points appearing as each frame passes. For instance, at frame = 0, no points are displayed and at frame = 5, five points are displayed, assuming that each point becomes visible on each frame from 0 to 5. I’d like to mention this, because I don’t have velocity or similar physical attributes for the points, only visibility.

I am guessing, in this case, one way of achieving the animation would be

  • Step 1, assign no points to UsdGeomPoints.points attribute, set timecode = 0
  • Step 2, assign 1 point to the attribute [P_0] and timecode = 1
  • Step 3, assign 2 points to the attirbute [P_0, P_1] and timecode = 2

and so on.

I hope this makes more sense.

Yes, you have it! Array-valued attributes can have different numbers of elements for each timesample (in geometry schemas we call this a “varying topology primitive “, and you must supply velocities if you want motion blur, in such cases.

Just make sure you pass an empty array for the sample at TimeCode 0. Your Points use is basically the same as you’d expect to see a particle-emitter encoded in USD.

1 Like

Thank you very much for your reply @spiff! Your answer also describes some of the details I frequently see on the OpenUSD documentation. I also didn’t know what I was asking is called a particle-emitter :smile:

I had a chance to experiment a little bit, and I can see that there are many other ways to animate, e.g. time varying display colors and more. I am fairly new to programming animations, and I am so happy to see that I can literally record the outcome of my work to a USD file in an animated way. That would reduce the time to post-process the results a lot.

I also would like to ask a follow-up question, if you don’t mind:

Sample-to-Sample coherence of elements within the array is not mandatory, and you can use the ids member to track identity of points over time.

You mentioned about tracking points with ids member. I see that UsdGeomPoints has ids member, but I am a bit confused on how to use it.

If we go back to the previous example I mentioned, we have the array of points PTS = [P_0, P_1, P_2, P_3, P_4] and my USD object has it like this UsdGeomPoints.points.default = PTS. If I understand correctly; I can create an array of IDs, IDS = [0 ,1, 2, 3, 4] and make this array time varying like the method above;

  • { timecode = 0, ids_arr = [ ] }
  • { timecode = 1, ids_arr = [0, 1]}
  • { timecode = 2, ids_arr = [0, 1, 2] }

Would that also achieve the same particle-emitter result?

You may find this documentation interesting, if not exactly germaine, since it sounds like possibly your points are not moving.

The way that UsdGeom deals with varying topology, point-based primitives, ids are not required to get renders with proper motion-blur for moving geometry. But they are necessary if any of your software needs to be able to track the identity of points from sample to sample.

In the “particle emitter” scenario I mentioned, you often set a max number of points in your simulator; as in your scenario, the output may contain from zero up to the maximum number of points in the first several/dozens/hundreds of timeSamples. But once the maximum number of points has been emitted, the oldest points will “die” to make way for new points, and so on and so on, for each subsequent timeSample. The simulator knows which points are which, and can be instructed to author ids at each timeSample as well… Each “identified point” will typically start out as a low-numbered element in the array and gradually wise in index until it disappears, with its id tracking along with it.

Finally, in terms of expectations, USD can easily handle arrays and geometry of 10M points. However, 10M timeSamples for any given property is not a scenario we commonly encounter, and not one that has been particularly optimized for. I’m not sure that’s your scenario, but wanted to just put that out there.

Thank you very much for sharing the documentation @spiff ! OpenUSD documentation is massive, and it is easy to miss important information. The documentation you shared is very helpful, I appreciate you.

As you may have guessed, I could also compute velocities and/or accelerations alongside the point cloud. I don’t think velocity or acceleration information would be always available for a point cloud as the documentation states (however, could always be computable in some way), but I also understand the documentation’s perspective. For my example case, I wasn’t looking for a motion blur effect or any kind of effect, but the documentation clearly explains why velocity/acceleration values are necessary and this is completely fine.

The size of the point cloud I use may go above 10M points, but I am also generating bins (containers) from the point cloud with respect to some (physical) properties. The number of bins I generate is usually less than 10000, but each bin may contain different number of points, e.g. len(bin_1) != len(bin_2). Animating those bins is perfectly acceptable for me, i.e. ~10000 time samples, and I interpret that this is also your suggestion instead of animating ~10M time samples.

One important thing I figured out regarding animations in USD is setting up a couple of variables, such as start timecode and end timecode. It is a small thing, and everyone would know, but it might become a blocker when someone is a complete beginner like me. For reference purposes, I’d like to share below how to set these variables:

  • UsdStage::SetStartTimeCode(float)
  • UsdStage::SetEndTimeCode(float)
  • UsdStage::SetTimeCodePerSecond(float)

Setting timecode per second has some additional details, so it is always good to take a look at the UsdStage docs for clarification.

Thanks again for the valuable information you shared with me @spiff !