RSS< Twitter< etc

Baking Dynamic Vertex Lighting Using XMesh

 

The following tutorial covers custom scripting for the XMesh Saver and the storing of animated vertex color data which is not easily achievable in 3ds Max otherwise.

The Problem

A 3ds Max user asked about possible approaches to storing dynamic vertex colors on a mesh.

3ds Max provides the ability to keyframe every map vertex in a Unwrap_UVW modifier to produce animated mapping coordinates or vertex colors, but both the baking process and the playback are rather slow.

An alternative solution exists using the Genome plugin from Thinbox which is discussed elsewhere, but the following workflow using XMesh is the most straight-forward.

Since it requires a MAXScript to be executed on each frame to produce the animated data, we will have to develop a short MAXScript that performs both the vertex color assignment and the baking. Thus, it is a perfect example for using the MAXScript Interface of the XMesh Saver in practice without relying on the shipping UI!

The Solution

We will create a simple test scene with a Teapot and a dynamic Omni light, but the same approach can be applied to any scene.

Example Scene

  • Create a Teapot with 16 segments (or any number of segments for that matter). You can also use any other mesh you want.
  • Set the wireframe color to white or assing a Standard Material.
  • Add a VertexPaint modifier - it will be used to store the Vertex Colors with the lighting.
  • Create an Omni light in the scene. 
  • Enable Auto Key and animate the position of the light in world space to produce some dynamic lighting. Animate the color of the light too, or any other relevant parameters.

   

The Baking Script

The script for baking the vertex colors and saving as XMesh will be relatively simple: 

  • We grab the object to be saved and its VertexPaint modifier into user variables
  • Then we try to get an XMesh Saver license and if we succeeded, we can execute the rest of the code.
  • We set the sequence name to be saved - note that XMesh will automatically set the frame number when called at a specific 3ds Max time, so we just provide a stand-in frame number like 0000.
  • Then we also provide the channels we want to save. If the object contains more mapping channels for example, they can also be included as "Mapping2" to "Mapping99".
  • We set the RenderBegin flag which sets 3ds Max in render state - all modifiers and objects like Particle Flow and TurboSmooth that support separate settings for viewport and render mode will be switched to render mode!
  • Then we loop through the current scene segment's frames and set the time slider on each frame. This is necessary when assigning dynamic vertex colors, simply setting the time context using at time t will not work with the VertexPaint modifier, although it works in other cases.
  • We call the assignVertexColors function of MAXScript to process the lighting on the current frame and bake it into the mesh and pass the VertexPaint modifier for the storage container.
  • Now we can call the  XMeshSaverUtils.SaveMeshToSequence() method which saves one object in local (object) space. The arguments are the object to save, the Ignore Empty Meshes flag, the Ignore Changing Topology flag, the Save In Object Mode flag and the Save Velocity flag. All these flags should be set to true in this case.
  • Once the t loop has finished, we lower the render state flag and switch back to viewport mode, and release the license so others can use XMesh Saver. 

NOTE: We are baking the mesh in object space, but we could bake it in world space using a very small change to the script. In the current case, we could simply replace the original object with the XMesh Loader and assign the same parenting to produce the same motion (if any) without having to load a new vertex list in world space on every frame.

Here is the script:

theObject = $Teapot001
theMod = theObject.vertexpaint
XMeshSaverUtils.AcquireLicense()
if XMeshSaverUtils.HasLicense do
(
XMeshSaverUtils.SetSequenceName @"C:\temp\bakecolor_0000.xmesh"
XMeshSaverUtils.sourceChannels = #("Velocity", "Color", "TextureCoord", "MaterialID", "SmoothingGroup")
XMeshSaverUtils.SetSceneRenderBegin()
for t = animationrange.start to animationrange.end do
(
slidertime = t
AssignVertexColors.ApplyNodes theObject vertexPaint:theMod
XMeshSaverUtils.SaveMeshToSequence theObject true true true true
)--end t loop
XMeshSaverUtils.SetSceneRenderEnd()
)--end if
XMeshSaverUtils.ReleaseLicense()

Running The Script 

  • Running the script will create a sequence of XMesh and XMDAT files in the specified folder - most of the time will be spent assigning vertex colors to the mesh. Since our teapot is not animated otherwise, the vertex list, the face list and the map face list for the color channel will be stored only on the first frame and reused for all following frames. Only the Color channel will be saved on each frame, reducing the data amount significantly.
  • Once the saving is done, we need to create an XMesh Loader object from the Thinkbox category in the Create > Geometry tab of the Command Panel and pick the saved sequence from disk.
  • Make sure the XMesh Loader is aligned pivot-to-pivot with the original teapot, and if you used any animation, parent the XMesh Loader to the original or to its parent to reproduce the same transformations.
  • Hide the original teapot to leave only the XMesh Loader and the light in the scene.
  • Turn off the Omni light - the XMesh Loader will turn black.
  • To see the vertex colors, select the XMesh Loader, right-click and select Object Properties, then check "Display Vertex Colors" and press OK to close the dialog.
  • A current bug in the XMesh Loader prevents it from correctly updating the vertex colors in the viewport when the slider time changes - a simple workaround is to add any modifier like TurnToMesh on top of the stack to force updates! 

You should now see the Teapot showing dynamic Vertex Colors if you move the time slider:

   

The vertex colors represent the ligthing of the Omni light even though the light itself is turned off.