Part 3. The Sandy Primitives
The Plane3D
The Plane3D primitive may not seem very fancy, but let me tell you it is very useful in all its simplcity. You use it to build the floor and walls or fences, or just simple image containers. I have used it for my coordinate planes, that I will soon introduce.
Here is the Plane3D constructor signature:
public function Plane3D( p_sName:String = null, p_nHeight:Number = 100, p_nWidth:Number = 100, p_nQualityH:uint = 1, p_nQualityV:uint=1, p_sType:String = Plane3D.XY_ALIGNED, p_sMode:String = PrimitiveMode.TRI )
As you can see, all parameters have default values, as for the Box. Normally we want to pass in our own values, and remember the basic rule here: We must pass all values up to and including the last non default value.
The two directions in which the plane stretches are arbitrarily called width and height, so the two first arguments are the length or the sides. The quality arguments are for the number of polygons in each direction. The interesting parameter p_sType determines the direction of the plane. XY_ALIGNED means that the plane will be parallel to the global xy plane. The last argument is the creation mode, the default being triangular polygons.
We are using the same setup as before, just changing the createScene() method to create Plane3D objects.
Tips: When I am creating similar experiments, I just
save the fla and as files under new names. I change the class name and the
constructor name, and make the new class the document class. I then compile
to check that it compiles and I didn't forget anything.
Then I start modifying the code
With the camera set at z = -200, to make the planes fully visible, here are some Plane3D variants.
private function createScene():Group { var root:Group = new Group(); shape = new Plane3D('Plane 1'); // Default values var materialAttr:MaterialAttributes = new MaterialAttributes( new LineAttributes( 0.5, 0x2111BB, 0.4 ), new LightAttributes( true, 0.1 )); var material:Material = new ColorMaterial( 0xFFCC33, 1, materialAttr ); material.lightingEnable = true; var app:Appearance = new Appearance( material ); shape.appearance = app; root.addChild( shape ); return root; }
Note that I changed the name of the object to 'shape' instead of 'plane'.
That's because I'm lazy, and I don't want to change it again, when I move on
to the next primitive.
After all, they are all Shape3D instances.
Here is the plane we get, using default values:
Note, that when you rotate this plane, it seems to disappear, not because we
are too close to the camera, but because one side of the plane is regarded
"back side".
This phenomenon is called back face culling, which means that the back side
of a polygon is "culled" and not drawn. This was fine for the cube, as long
as we kept the camera outside the cube, and the computer didn't have to
bother about its inside - the back faces. Here we may to see the plane form
all directions, and so we have to inhibit back face culling. We do that by
setting the enableBackFaceCulling property to false.
shape.enableBackFaceCulling = false; // Plane 2
Plane 2 | Plane 3 |
Rotating Plane 2, we notice that the "back side" has more shadow from the
light source than the "front side". If we want them to be equally lit, we
can shut off the light, by setting the enableLighting to false ( or comment
it out ). Look at Plane 3 above to see the result.
You should also test what happens if you leave the light on, but add a back face material to the plane appearance.
Directions and quality
It is possible to create planes in three orientations, XY_ALIGNED, YZ_ALIGNED and ZX_ALIGNED. The plane will lie in one of the principal coordinate planes of the world, and it will have its central point in the origin.
It can then be translated and rotated to the position and orientation we want. Here are three planes, one in the zx coordinate plane and two parallel to the yz plane. We want to be able to rotate all three planes together, so we have to add them to a transform group, and then manipulate the group. This is our new scene creating method.
private function createScene():Group { // Create groups var root:Group = new Group(); planeGroup = new TransformGroup(); // Create materials an appearances // The same as before // Create shapes var shape1:Shape3D = new Plane3D('Plane 1', 200, 200, 10, 10, Plane3D.ZX_ALIGNED); var shape2:Shape3D = new Plane3D('Plane 1', 50, 100, 5, 10, Plane3D.YZ_ALIGNED); shape2.x = -20; var shape3:Shape3D = new Plane3D('Plane 1', 50, 100, 5, 10, Plane3D.YZ_ALIGNED); shape3.x = 20; shape1.appearance = app; shape1.enableBackFaceCulling = false; shape2.appearance = app; shape2.enableBackFaceCulling = false; shape3.appearance = app; shape3.enableBackFaceCulling = false; // Group the shapes together and add them to the root group planeGroup.addChild( shape1 ); planeGroup.addChild( shape2 ); planeGroup.addChild( shape3 ); root.addChild(planeGroup); return root; }
The version to the left behaves really strange. The two vertical planes flips to the front and back of the horizontal plane, as we rotate the group. In an effort to save computing resources, each object is given a single container or canvas to draw on. The z sorting algorithm decides which object is nearest to the camera and so should be drawn above the other. This of course is a very rough method, that works well when objects in the scene are far apart, but is a disaster when objects cuts through each other, as our planes do.
We can make things much better by giving each polygon its own canvas to draw on, by setting the useSingleContainer property to false.
shape1.useSingleContainer = false; shape2.useSingleContainer = false; shape3.useSingleContainer = false;
The result is seen to the right above. We can also see that the problem, however less, has not disappeared. This is an artifact of the z sorting algorithm Sandy uses. To be exact it would require lots of calculations to decide which pixels are hidden and which are visible at each frame.
If we are not using texture materials, we are not depending on triangular polygons, for better distortion. In that case we can improve the appearance a bit, by setting the creation mode to PrimitiveMode.QUAD.