First Avalon 3D Demo...
Having my roots in the Amiga platform it should be no surprise that my first Avalon 3D demo would be inspired by the famous demo by RJ Mical.
(As a historical note, the first draft of this demo was written while waiting on a code review for the checkin which finally brought together the pieces to enable 3D. I wrote it to have a pretty screenshot to email to my team to announce that 3D support was now in Avalon. So, when I say "first Avalon3D demo" I really do mean the first... :)
Over the extended weekend I found some time to scrub the code a bit and post the binary (runs on Longhorn build 4074) and source. There are a few things I really like about this demo (aside from the excuse to link the Amiga History Guide).
- It is a great illustration of the usefulness of multiple parenting.
- It demonstrates some of the 2D/3D integration GregSc blogged about recently.
- It shows Xaml being used as a general persistence format.
This demo actually uses two Viewport3Ds as shown in Figure 1-1 below. The top viewport displays the scene normally while the bottom viewport renders the scene inverted on the Y-axis. Rather than replicate the entire Model3D hierarchy between the viewports instead the same subgraph is added to the Viewport3D.Models collection of both viewports. The Visual/Model3D hierarchy is shown in Figure 1-2.
Because of the shared portion of the scene graph we conveniently only have to only worry about animating one spinning ball (which just happens to be rendered from two different views). In general, reusing portions of the scene graph them when possible is a best practice. There are some limitations to multiparenting, however. First, cycles are not allowed in the scene graph for obvious reasons. Second, in order to be shared across UIContexts the Model3Ds will have to be unchangeable. It is also worth mentioning that multiparenting is applicable to Model3Ds only. The Visual/UIElement/Control portions of the scene graph must be trees.
Basic 2D/3D integration is illustrated by the fact that the viewports do not take over the rectangles they occupy. You can still see the blue gradient background of the Window through the Viewport3Ds. If you were to place a button behind the viewport input would still be routed through the unpainted portions to the underlying button just like any other UIElement. The bottom viewport is particularly interesting because we have set it's Opacity causing it be rendered semi-translucently as it is composed into the scene. (To be clear, it is not the ball which is translucent, it's the Viewport3D we are using to display this projection of the 3D scene). This is a minor example of the rich 2D/3D intergration story.
Xaml as a general persistence format
One challenge with working with Avalon 3D is that there is no way to import models... or is there? Because Xaml is a general persistence format loading a Model3D from .Xaml is as simple as:
Model3D myModel = (Model3D) Parser.LoadXml();
That is exactly what this demo does in Window1's Loaded handler to load the embedded Ball.xaml. The challenge right now is not importing, but creating content as Xaml is not yet a widely supported 3D model format. In my case I wrote a small console app to generate the geometry and then used Parser.SaveAsXml. (BTW - If anyone would be interested in working on a .xaml exporter for their favorite 3D tool, ping me and I'll help where I can.)