This sprint I made a VAT exporter in Blender so that I wouldn't have to se the frame sequence method that I used last sprint. I have done this before, so I figured it would only take a few hours to do. However, there was a big difference with my requirements that I hadn't realized.
![]() |
| The result of my VAT exporter. Notice how the data is laid out in distinct ribbons. |
My 3D model that I am using to do the tests is very hi-poly, 236000 vertices. I am using a hi-poly mesh because it has to have enough topology to do the extreme deformations needed by the effect. The problem is that, using a standard VAT workflow, it would result in a texture that is too wide for Unreal engine to use - because usually, VAT textures have one column of pixels per vertex, and one row of pixels per frame of animation. I didn't want to even try to deal with a texture that was 236000 pixels wide. So, instead, I made my own packed VAT that works a different way.
If the 236000-pixel wide texture is like a ribbon, then, like a ribbon, it can be cut into strips and rearranged into the shape of a square. That is what my work was, this week - building the program to create the UV Map for reading the VAT, and to pull the data from the scene and make the VAT itself.
The second challenge came when I decided to use NumPy, because using plain Python would be annoyingly slow. I wanted to do this to gain some experience in NumPy, because it is an essential tool for high-performance programming. But programming with NumPy is challenging, because, instead of writing code that loops through individual vertices and places the vertex coordinate in individual color channels of individual pixels in an image, NumPy code operates on whole arrays of data all at once. So instead of dealing with things one at a time, my code used an array of vertex indices to generate an array of indices to pick pixels in a flat array of pixel byte data. I had to use slices to separate the X, Y, and Z vertex coordinates into the R, G, and B components of the pixels.
The most difficult part of writing this program was that it was hard to debug. You can't do meaningful print debugging with 236,000 values. printing only a few values makes it hard to notice trends. The breakthrough occurred when I realized I could just put the information I was looking for into the texture itself, in place of the VAT information. Sure enough, I was running into the classic programming problem - an off-by-one error.
Working with NumPy, however, means that I can build new instances of the VFX and iterate on them very easily. I think I will also be able to read the texture with Niagara particles.
![]() |
| Off-By-One and worse, flipped on one axis. |
You might ask me why I chose to do this in Blender instead of using a mesh cache to load it into Houdini and use Houdini's VAT export. There are a few reasons... 1) because I know what my code is doing and I can reverse-engineer it to unpack it in a shader more easily; 2) because it is important for me to practice with NumPy; 3) because I didn't realize it would be so difficult.
Ultimately, the real problem wasn't that this was a difficult problem - it's that I have been overwhelmed with other work, sleep deprived, afflicted with seasonal allergies, and preoccupied with events like EA day. I just wasn't able to think clearly until today. Everything was simple today when I sat down to work!
Anyhow, I exported the texture to Unreal Engine and set up the material. It's working now!
![]() |
| Working and driven by the Sequencer in Unreal Engine. |
I also spent some time working on skinning the updated character mesh, and helping Kaleigha figure out a problem with the existing character mesh. I am going to finish this work during the next sprint.
I'm also (finally) going to be able to focus entirely on the visual aspect of the VFX. Hooray!
Here are my Perforce Submissions:
![]() |
| In Unreal Engine |
![]() |
| Source Assets. I'm sure it will finish eventually, right? |





No comments:
Post a Comment