scaffold.libs_as.starling.rendering.MeshStyle.as Maven / Gradle / Ivy
// =================================================================================================
//
// Starling Framework
// Copyright 2011-2015 Gamua. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.rendering
{
import flash.geom.Matrix;
import flash.geom.Point;
import starling.core.starling_internal;
import starling.display.Mesh;
import starling.events.Event;
import starling.events.EventDispatcher;
import starling.textures.Texture;
import starling.textures.TextureSmoothing;
/** Dispatched every frame on styles assigned to display objects connected to the stage. */
[Event(name="enterFrame", type="starling.events.EnterFrameEvent")]
/** MeshStyles provide a means to completely modify the way a mesh is rendered.
* The base class provides Starling's standard mesh rendering functionality: colored and
* (optionally) textured meshes. Subclasses may add support for additional features like
* color transformations, normal mapping, etc.
*
* Using styles
*
* First, create an instance of the desired style. Configure the style by updating its
* properties, then assign it to the mesh. Here is an example that uses a fictitious
* ColorStyle
:
*
*
* var image:Image = new Image(heroTexture);
* var colorStyle:ColorStyle = new ColorStyle();
* colorStyle.redOffset = 0.5;
* colorStyle.redMultiplier = 2.0;
* image.style = colorStyle;
*
* Beware:
*
*
* - A style instance may only be used on one object at a time.
* - A style might require the use of a specific vertex format;
* when the style is assigned, the mesh is converted to that format.
*
*
* Creating your own styles
*
* To create custom rendering code in Starling, you need to extend two classes:
* MeshStyle
and MeshEffect
. While the effect class contains
* the actual AGAL rendering code, the style provides the API that other developers will
* interact with.
*
* Subclasses of MeshStyle
will add specific properties that configure the
* style's outcome, like the redOffset
and redMultiplier
properties
* in the sample above. Here's how to properly create such a class:
*
*
* - Always provide a constructor that can be called without any arguments.
* - Override
copyFrom
— that's necessary for batching.
* - Override
createEffect
— this method must return the
* MeshEffect
that will do the actual Stage3D rendering.
* - Override
updateEffect
— this configures the effect created above
* right before rendering.
* - Override
canBatchWith
if necessary — this method figures out if one
* instance of the style can be batched with another. If they all can, you can leave
* this out.
*
*
* If the style requires a custom vertex format, you must also:
*
*
* - add a static constant called
VERTEX_FORMAT
to the class and
* - override
get vertexFormat
and let it return exactly that format.
*
*
* When that's done, you can turn to the implementation of your MeshEffect
;
* the createEffect
-override will return an instance of this class.
* Directly before rendering begins, Starling will then call updateEffect
* to set it up.
*
* @see MeshEffect
* @see VertexDataFormat
* @see starling.display.Mesh
*/
public class MeshStyle extends EventDispatcher
{
/** The vertex format expected by this style (the same as found in the MeshEffect-class). */
public static const VERTEX_FORMAT:VertexDataFormat = MeshEffect.VERTEX_FORMAT;
private var _type:Class;
private var _target:Mesh;
private var _texture:Texture;
private var _textureSmoothing:String;
private var _vertexData:VertexData; // just a reference to the target's vertex data
private var _indexData:IndexData; // just a reference to the target's index data
// helper objects
private static var sPoint:Point = new Point();
/** Creates a new MeshStyle instance.
* Subclasses must provide a constructor that can be called without any arguments. */
public function MeshStyle()
{
_textureSmoothing = TextureSmoothing.BILINEAR;
_type = Object(this).constructor as Class;
}
/** Copies all properties of the given style to the current instance (or a subset, if the
* classes don't match). Must be overridden by all subclasses!
*/
public function copyFrom(meshStyle:MeshStyle):void
{
_texture = meshStyle._texture;
_textureSmoothing = meshStyle._textureSmoothing;
}
/** Creates a clone of this instance. The method will work for subclasses automatically,
* no need to override it. */
public function clone():MeshStyle
{
var clone:MeshStyle = new _type();
clone.copyFrom(this);
return clone;
}
/** Creates the effect that does the actual, low-level rendering.
* Must be overridden by all subclasses!
*/
public function createEffect():MeshEffect
{
return new MeshEffect();
}
/** Updates the settings of the given effect to match the current style.
* The given effect
will always match the class returned by
* createEffect
.
*
* Must be overridden by all subclasses!
*/
public function updateEffect(effect:MeshEffect, state:RenderState):void
{
effect.texture = _texture;
effect.textureSmoothing = _textureSmoothing;
effect.mvpMatrix3D = state.mvpMatrix3D;
effect.alpha = state.alpha;
}
/** Indicates if the current instance can be batched with the given style.
* To be overridden by subclasses if default behavior is not sufficient.
* The base implementation just checks if the styles are of the same type
* and if the textures are compatible.
*/
public function canBatchWith(meshStyle:MeshStyle):Boolean
{
if (_type == meshStyle._type)
{
var newTexture:Texture = meshStyle._texture;
if (_texture == null && newTexture == null) return true;
else if (_texture && newTexture)
return _texture.base == newTexture.base &&
_textureSmoothing == meshStyle._textureSmoothing;
else return false;
}
else return false;
}
/** Copies the raw vertex data of the target mesh to the given VertexData instance.
* If you pass a matrix, all vertices will be transformed during the process.
*
* This method is called on batching. Subclasses may override it if they need to modify
* the vertex data in that process. Per default, just the "position" attribute is
* transformed.
*/
public function copyVertexDataTo(target:VertexData, targetVertexID:int=0, matrix:Matrix=null,
vertexID:int=0, numVertices:int=-1):void
{
_vertexData.copyTo(target, targetVertexID, matrix, vertexID, numVertices);
}
/** Copies the raw index data to the given IndexData instance.
* The given offset value will be added to all indices during the process.
*
* This method is called on batching. Subclasses may override it if they need to modify
* the index data in that process.
*/
public function copyIndexDataTo(target:IndexData, targetIndexID:int=0, offset:int=0,
indexID:int=0, numIndices:int=-1):void
{
_indexData.copyTo(target, targetIndexID, offset, indexID, numIndices);
}
/** Call this method if the target needs to be redrawn.
* The call is simply forwarded to the mesh. */
protected function setRequiresRedraw():void
{
if (_target) _target.setRequiresRedraw();
}
/** Called when assigning a target mesh. Override to plug in class-specific logic. */
protected function onTargetAssigned(target:Mesh):void
{ }
// enter frame event
override public function addEventListener(type:String, listener:Function):void
{
if (type == Event.ENTER_FRAME && _target)
_target.addEventListener(Event.ENTER_FRAME, onEnterFrame);
super.addEventListener(type, listener);
}
override public function removeEventListener(type:String, listener:Function):void
{
if (type == Event.ENTER_FRAME && _target)
_target.removeEventListener(type, onEnterFrame);
super.removeEventListener(type, listener);
}
private function onEnterFrame(event:Event):void
{
dispatchEvent(event);
}
// internal methods
/** @private */
starling_internal function setTarget(target:Mesh=null, vertexData:VertexData=null,
indexData:IndexData=null):void
{
if (_target != target)
{
if (_target) _target.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
if (vertexData) vertexData.format = vertexFormat;
_target = target;
_vertexData = vertexData;
_indexData = indexData;
if (target)
{
if (hasEventListener(Event.ENTER_FRAME))
target.addEventListener(Event.ENTER_FRAME, onEnterFrame);
onTargetAssigned(target);
}
}
}
// vertex manipulation
/** Returns the alpha value of the vertex at the specified index. */
public function getVertexAlpha(vertexID:int):Number
{
return _vertexData.getAlpha(vertexID);
}
/** Sets the alpha value of the vertex at the specified index to a certain value. */
public function setVertexAlpha(vertexID:int, alpha:Number):void
{
_vertexData.setAlpha(vertexID, "color", alpha);
setRequiresRedraw();
}
/** Returns the RGB color of the vertex at the specified index. */
public function getVertexColor(vertexID:int):uint
{
return _vertexData.getColor(vertexID);
}
/** Sets the RGB color of the vertex at the specified index to a certain value. */
public function setVertexColor(vertexID:int, color:uint):void
{
_vertexData.setColor(vertexID, "color", color);
setRequiresRedraw();
}
/** Returns the texture coordinates of the vertex at the specified index. */
public function getTexCoords(vertexID:int, out:Point = null):Point
{
if (_texture) return _texture.getTexCoords(_vertexData, vertexID, "texCoords", out);
else return _vertexData.getPoint(vertexID, "texCoords", out);
}
/** Sets the texture coordinates of the vertex at the specified index to the given values. */
public function setTexCoords(vertexID:int, u:Number, v:Number):void
{
if (_texture) _texture.setTexCoords(_vertexData, vertexID, "texCoords", u, v);
else _vertexData.setPoint(vertexID, "texCoords", u, v);
setRequiresRedraw();
}
// properties
/** Returns a reference to the vertex data of the assigned target (or null
* if there is no target). Beware: the style itself does not own any vertices;
* it is limited to manipulating those of the target mesh. */
protected function get vertexData():VertexData { return _vertexData; }
/** Returns a reference to the index data of the assigned target (or null
* if there is no target). Beware: the style itself does not own any indices;
* it is limited to manipulating those of the target mesh. */
protected function get indexData():IndexData { return _indexData; }
/** The actual class of this style. */
public function get type():Class { return _type; }
/** Changes the color of all vertices to the same value.
* The getter simply returns the color of the first vertex. */
public function get color():uint
{
if (_vertexData.numVertices > 0) return _vertexData.getColor(0);
else return 0x0;
}
public function set color(value:uint):void
{
var i:int;
var numVertices:int = _vertexData.numVertices;
for (i=0; inull
, if there is none). */
public function get texture():Texture { return _texture; }
public function set texture(value:Texture):void
{
if (value != _texture)
{
if (value)
{
var i:int;
var numVertices:int = _vertexData ? _vertexData.numVertices : 0;
for (i = 0; i < numVertices; ++i)
{
getTexCoords(i, sPoint);
value.setTexCoords(_vertexData, i, "texCoords", sPoint.x, sPoint.y);
}
}
_texture = value;
setRequiresRedraw();
}
}
/** The smoothing filter that is used for the texture. @default bilinear */
public function get textureSmoothing():String { return _textureSmoothing; }
public function set textureSmoothing(value:String):void
{
if (value != _textureSmoothing)
{
_textureSmoothing = value;
setRequiresRedraw();
}
}
/** The target the style is currently assigned to. */
public function get target():Mesh { return _target; }
}
}