In this article we'll learn about some internal non-documented methods and properties of Alternativa3D engine. This will help developers to implement their own materials.
Some information about rendering system architecture
During BSP-tree building process, the faces of the objects in the scene are put in the tree as polygonal primitives. Each of these primitives represents a whole face or a fragment of a face. During rendering process every primitive is drawn into its own container in the view connected to the camera. Containers are instances of alternativa.engine3d.display.Skin class, which extends flash.display.Sprite. Primitives are drawn by materials – descendants of alternativa.engine3d.materials.SurfaceMaterial class.
The SurfaceMaterial class contains methods from alternativa3d namespace, that are used by rendering system. Every material have to implement these methods in order to correctly draw the faces of corresponding surface. Let's overview these methods.
- alternativa3d function canDraw(primitive:PolyPrimitive):Boolean – by calling this method the rendering system determines if the given primitive have to be rendered;
- alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void – this method renders a primitive. It is called when the canDraw() method returns true.
- camera:Camera3D – camera which renders the scene
- skin:Skin – container to which the primitive is rendered
- length:uint – number of the primitives' vertices in the points array
- points:Array – an array of instances of alternativa.engine3d.materials.DrawPoint class, that contain geometric coordinates of the primitive's vertices in the camera's coordinates system and texture coordinates of the vertices.
- alternativa3d function clear(skin:Skin):void – this method must clear all graphics and other content of the given primitive container;
When properties of a material instance change, the scene must be informed. The SurfaceMaterial class contains a reference to its surface, declared as:
- alternativa3d var _surface:Surface
The alternativa.engine3d.code.Surface class contains a method that passes to the scene a signal about material change:
- alternativa3d function addMaterialChangedOperationToScene():void
Thus, when properties of a material are changed, the method _surface.addMaterialChangedOperationToScene() must be called.
The following properties can also be useful for materials creation:
- Camera3D
- alternativa3d var focalLength:Number – focal distance of the camera
- PolyPrimitive
- alternativa3d var face:Face – the face, which fragment the primitive represents
- Skin
- alternativa3d var primitive:PolyPrimitive – the primitive, linked with the container
- alternativa3d var material:SurfaceMaterial – the material, linked with the container
| Warning Do not assign any values to the properties from alternativa3d namespace, as it will lead to incorrect function of the library. |
An example of extending of an existing material
Using the knowledge we have got, let's create a new material based on the TextureMaterial class. We'll add to this material an ability to set color transformation.
Create a descendant of TextureMaterial and name it TextureMaterialExt for example.
Declare a variable and access methods for color transformation:
// Variable for color transformation storage private var _colorTransfrom:ColorTransform = new ColorTransform(); // The method returns a clone of color transformation. public function get colorTransfrom():ColorTransform { return new ColorTransform(_colorTransfrom.redMultiplier, _colorTransfrom.greenMultiplier, _colorTransfrom.blueMultiplier, _colorTransfrom.alphaMultiplier, _colorTransfrom.redOffset, _colorTransfrom.greenOffset, _colorTransfrom.blueOffset, _colorTransfrom.alphaOffset); } // The method sets color transformation. public function set colorTransform(value:ColorTransform):void { if (value == null) { throw ArgumentError("Color transform cannot be null"); } _colorTransfrom.redMultiplier = value.redMultiplier; _colorTransfrom.greenMultiplier = value.greenMultiplier; _colorTransfrom.blueMultiplier = value.blueMultiplier; _colorTransfrom.alphaMultiplier = value.alphaMultiplier; _colorTransfrom.redOffset = value.redOffset; _colorTransfrom.greenOffset = value.greenOffset; _colorTransfrom.blueOffset = value.blueOffset; _colorTransfrom.alphaOffset = value.alphaOffset; // Sending the signal to the scene if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } }
Extend methods clear() and draw():
// Default color transformation private static const defaultColorTransfrom:ColorTransform = new ColorTransform(); // When the container is cleared it's necessary to clear color transformation override alternativa3d function clear(skin:Skin):void { super.clear(skin); // Clear color transformation skin.transform.colorTransform = defaultColorTransfrom; } // Drawing primitive override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { super.draw(camera, skin, length, points); // Setting current color transformation skin.transform.colorTransform = _colorTransfrom; }
Modify the access methods for the alpha property, to synchronize it with the color transformation:
override public function get alpha():Number { return _colorTransfrom.alphaMultiplier; } override public function set alpha(value:Number):void { super.alpha = value; _colorTransfrom.alphaMultiplier = value; // Sending the signal to the scene if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } }
Assembling all together, we'll get the complete code of the new material class:
package { import alternativa.engine3d.alternativa3d; import alternativa.engine3d.core.Camera3D; import alternativa.engine3d.display.Skin; import alternativa.engine3d.materials.Material; import alternativa.engine3d.materials.TextureMaterial; import alternativa.engine3d.materials.TextureMaterialPrecision; import alternativa.types.Texture; import flash.display.BlendMode; import flash.geom.ColorTransform; use namespace alternativa3d; public class TextureMaterialExt extends TextureMaterial { // Default color transformation private static const defaultColorTransfrom:ColorTransform = new ColorTransform(); // Color transformation private var _colorTransfrom:ColorTransform = new ColorTransform(); // Constructor public function TextureMaterialExt(texture:Texture, alpha:Number=1, repeat:Boolean=true, smooth:Boolean=false, blendMode:String=BlendMode.NORMAL, wireThickness:Number = -1, wireColor:uint=0, precision:Number=TextureMaterialPrecision.MEDIUM) { super(texture, alpha, repeat, smooth, blendMode, wireThickness, wireColor, precision); } // The method returns a clone of color transformation. public function get colorTransfrom():ColorTransform { return new ColorTransform(_colorTransfrom.redMultiplier, _colorTransfrom.greenMultiplier, _colorTransfrom.blueMultiplier, _colorTransfrom.alphaMultiplier, _colorTransfrom.redOffset, _colorTransfrom.greenOffset, _colorTransfrom.blueOffset, _colorTransfrom.alphaOffset); } // The method sets color transformation. public function set colorTransform(value:ColorTransform):void { if (value == null) { throw ArgumentError("Color transform cannot be null"); } _colorTransfrom.redMultiplier = value.redMultiplier; _colorTransfrom.greenMultiplier = value.greenMultiplier; _colorTransfrom.blueMultiplier = value.blueMultiplier; _colorTransfrom.alphaMultiplier = value.alphaMultiplier; _colorTransfrom.redOffset = value.redOffset; _colorTransfrom.greenOffset = value.greenOffset; _colorTransfrom.blueOffset = value.blueOffset; _colorTransfrom.alphaOffset = value.alphaOffset; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } // The method returns alpha transparency override public function get alpha():Number { return _colorTransfrom.alphaMultiplier; } // The method sets alpha transparency override public function set alpha(value:Number):void { super.alpha = value; _colorTransfrom.alphaMultiplier = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } // When the container is cleared it's necessary to clear color transformation override alternativa3d function clear(skin:Skin):void { super.clear(skin); skin.transform.colorTransform = defaultColorTransfrom; } // Drawing primitive override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { super.draw(camera, skin, length, points); skin.transform.colorTransform = _colorTransfrom; } // Creating clone of the material override public function clone():Material { var res:TextureMaterialExt = new TextureMaterialExt(_texture, _alpha, _repeat, _smooth, _blendMode, _wireThickness, _wireColor, _precision); return res; } } }
Below is a simple example with the new material (mouse rotates the cube). Those who interested can download source code of the example.
