322 lines
13 KiB
ActionScript
322 lines
13 KiB
ActionScript
// =================================================================================================
|
|
//
|
|
// Starling Framework
|
|
// Copyright 2011 Gamua OG. 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.utils
|
|
{
|
|
import flash.geom.Matrix3D;
|
|
import flash.geom.Point;
|
|
import flash.geom.Vector3D;
|
|
|
|
/** The VertexData class manages a raw list of vertex information, allowing direct upload
|
|
* to Stage3D vertex buffers. <em>You only have to work with this class if you create display
|
|
* objects with a custom render function. If you don't plan to do that, you can safely
|
|
* ignore it.</em>
|
|
*
|
|
* <p>To render objects with Stage3D, you have to organize vertex data in so-called
|
|
* vertex buffers. Those buffers reside in graphics memory and can be accessed very
|
|
* efficiently by the GPU. Before you can move data into vertex buffers, you have to
|
|
* set it up in conventional memory - that is, in a Vector object. The vector contains
|
|
* all vertex information (the coordinates, color, and texture coordinates) - one
|
|
* vertex after the other.</p>
|
|
*
|
|
* <p>To simplify creating and working with such a bulky list, the VertexData class was
|
|
* created. It contains methods to specify and modify vertex data. The raw Vector managed
|
|
* by the class can then easily be uploaded to a vertex buffer.</p>
|
|
*
|
|
* <strong>Premultiplied Alpha</strong>
|
|
*
|
|
* <p>The color values of the "BitmapData" object contain premultiplied alpha values, which
|
|
* means that the <code>rgb</code> values were multiplied with the <code>alpha</code> value
|
|
* before saving them. Since textures are created from bitmap data, they contain the values in
|
|
* the same style. On rendering, it makes a difference in which way the alpha value is saved;
|
|
* for that reason, the VertexData class mimics this behavior. You can choose how the alpha
|
|
* values should be handled via the <code>premultipliedAlpha</code> property.</p>
|
|
*
|
|
* <p><em>Note that vertex data with premultiplied alpha values will lose all <code>rgb</code>
|
|
* information of a vertex with a zero <code>alpha</code> value.</em></p>
|
|
*/
|
|
public class VertexData
|
|
{
|
|
/** The total number of elements (Numbers) stored per vertex. */
|
|
public static const ELEMENTS_PER_VERTEX:int = 9;
|
|
|
|
/** The offset of position data (x, y) within a vertex. */
|
|
public static const POSITION_OFFSET:int = 0;
|
|
|
|
/** The offset of color data (r, g, b, a) within a vertex. */
|
|
public static const COLOR_OFFSET:int = 3;
|
|
|
|
/** The offset of texture coordinate (u, v) within a vertex. */
|
|
public static const TEXCOORD_OFFSET:int = 7;
|
|
|
|
private var mRawData:Vector.<Number>;
|
|
private var mPremultipliedAlpha:Boolean;
|
|
private var mNumVertices:int;
|
|
|
|
/** Helper object. */
|
|
private static var sPositions:Vector.<Number> = new Vector.<Number>(12, true);
|
|
|
|
/** Create a new VertexData object with a specified number of vertices. */
|
|
public function VertexData(numVertices:int, premultipliedAlpha:Boolean=false)
|
|
{
|
|
mRawData = new Vector.<Number>(numVertices * ELEMENTS_PER_VERTEX, true);
|
|
mPremultipliedAlpha = premultipliedAlpha;
|
|
mNumVertices = numVertices;
|
|
}
|
|
|
|
/** Creates a duplicate of the vertex data object. */
|
|
public function clone():VertexData
|
|
{
|
|
var clone:VertexData = new VertexData(0, mPremultipliedAlpha);
|
|
clone.mRawData = mRawData.concat();
|
|
clone.mRawData.fixed = true;
|
|
clone.mNumVertices = mNumVertices;
|
|
return clone;
|
|
}
|
|
|
|
/** Copies the vertex data of this instance to another vertex data object,
|
|
* starting at a certain index. */
|
|
public function copyTo(targetData:VertexData, targetVertexID:int=0):void
|
|
{
|
|
// todo: check/convert pma
|
|
|
|
var targetRawData:Vector.<Number> = targetData.mRawData;
|
|
var dataLength:int = mNumVertices * ELEMENTS_PER_VERTEX;
|
|
var targetStartIndex:int = targetVertexID * ELEMENTS_PER_VERTEX;
|
|
|
|
for (var i:int=0; i<dataLength; ++i)
|
|
targetRawData[int(targetStartIndex+i)] = mRawData[i];
|
|
}
|
|
|
|
/** Appends the vertices from another VertexData object. */
|
|
public function append(data:VertexData):void
|
|
{
|
|
mRawData.fixed = false;
|
|
|
|
var rawData:Vector.<Number> = data.mRawData;
|
|
var rawDataLength:int = rawData.length;
|
|
|
|
for (var i:int=0; i<rawDataLength; ++i)
|
|
mRawData.push(rawData[i]);
|
|
|
|
mNumVertices += data.numVertices;
|
|
mRawData.fixed = true;
|
|
}
|
|
|
|
// functions
|
|
|
|
/** Updates the position values of a vertex. */
|
|
public function setPosition(vertexID:int, x:Number, y:Number, z:Number=0.0):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + POSITION_OFFSET;
|
|
mRawData[offset] = x;
|
|
mRawData[int(offset+1)] = y;
|
|
mRawData[int(offset+2)] = z;
|
|
}
|
|
|
|
/** Returns the position of a vertex. */
|
|
public function getPosition(vertexID:int, position:Vector3D):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + POSITION_OFFSET;
|
|
position.x = mRawData[offset];
|
|
position.y = mRawData[int(offset+1)];
|
|
position.z = mRawData[int(offset+2)];
|
|
}
|
|
|
|
/** Updates the color and alpha values of a vertex. */
|
|
public function setColor(vertexID:int, color:uint, alpha:Number=1.0):void
|
|
{
|
|
var multiplier:Number = mPremultipliedAlpha ? alpha : 1.0;
|
|
var offset:int = getOffset(vertexID) + COLOR_OFFSET;
|
|
mRawData[offset] = Color.getRed(color) / 255.0 * multiplier;
|
|
mRawData[int(offset+1)] = Color.getGreen(color) / 255.0 * multiplier;
|
|
mRawData[int(offset+2)] = Color.getBlue(color) / 255.0 * multiplier;
|
|
mRawData[int(offset+3)] = alpha;
|
|
}
|
|
|
|
/** Returns the RGB color of a vertex (no alpha). */
|
|
public function getColor(vertexID:int):uint
|
|
{
|
|
var offset:int = getOffset(vertexID) + COLOR_OFFSET;
|
|
var divisor:Number = mPremultipliedAlpha ? mRawData[offset+3] : 1.0;
|
|
|
|
if (divisor == 0) return 0;
|
|
else
|
|
{
|
|
var red:Number = mRawData[offset] / divisor;
|
|
var green:Number = mRawData[int(offset+1)] / divisor;
|
|
var blue:Number = mRawData[int(offset+2)] / divisor;
|
|
return Color.rgb(red * 255, green * 255, blue * 255);
|
|
}
|
|
}
|
|
|
|
/** Updates the alpha value of a vertex (range 0-1). */
|
|
public function setAlpha(vertexID:int, alpha:Number):void
|
|
{
|
|
if (mPremultipliedAlpha) setColor(vertexID, getColor(vertexID), alpha);
|
|
else
|
|
{
|
|
var offset:int = getOffset(vertexID) + COLOR_OFFSET + 3;
|
|
mRawData[offset] = alpha;
|
|
}
|
|
}
|
|
|
|
/** Returns the alpha value of a vertex in the range 0-1. */
|
|
public function getAlpha(vertexID:int):Number
|
|
{
|
|
var offset:int = getOffset(vertexID) + COLOR_OFFSET + 3;
|
|
return mRawData[offset];
|
|
}
|
|
|
|
/** Updates the texture coordinates of a vertex (range 0-1). */
|
|
public function setTexCoords(vertexID:int, u:Number, v:Number):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + TEXCOORD_OFFSET;
|
|
mRawData[offset] = u;
|
|
mRawData[int(offset+1)] = v;
|
|
}
|
|
|
|
/** Returns the texture coordinates of a vertex in the range 0-1. */
|
|
public function getTexCoords(vertexID:int, texCoords:Point):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + TEXCOORD_OFFSET;
|
|
texCoords.x = mRawData[offset];
|
|
texCoords.y = mRawData[int(offset+1)];
|
|
}
|
|
|
|
// utility functions
|
|
|
|
/** Translate the position of a vertex by a certain offset. */
|
|
public function translateVertex(vertexID:int,
|
|
deltaX:Number, deltaY:Number, deltaZ:Number=0.0):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + POSITION_OFFSET;
|
|
mRawData[offset] += deltaX;
|
|
mRawData[int(offset+1)] += deltaY;
|
|
mRawData[int(offset+2)] += deltaZ;
|
|
}
|
|
|
|
/** Transforms the position of a vertex by multiplication with a transformation matrix. */
|
|
public function transformVertex(vertexID:int, matrix:Matrix3D):void
|
|
{
|
|
var offset:int = getOffset(vertexID) + POSITION_OFFSET;
|
|
sPositions[0] = mRawData[offset];
|
|
sPositions[1] = mRawData[int(offset+1)];
|
|
sPositions[2] = mRawData[int(offset+2)];
|
|
|
|
matrix.transformVectors(sPositions, sPositions);
|
|
mRawData[offset] = sPositions[0];
|
|
mRawData[int(offset+1)] = sPositions[1];
|
|
mRawData[int(offset+2)] = sPositions[2];
|
|
}
|
|
|
|
/** Transforms the position of a vertex by multiplication with a transformation matrix. */
|
|
public function transformQuad(vertexID:int, matrix:Matrix3D):void
|
|
{
|
|
var i:int;
|
|
var offset:int = getOffset(vertexID) + POSITION_OFFSET;
|
|
|
|
for (i=0; i<4; ++i)
|
|
{
|
|
sPositions[int(3*i )] = mRawData[offset];
|
|
sPositions[int(3*i + 1)] = mRawData[int(offset+1)];
|
|
sPositions[int(3*i + 2)] = mRawData[int(offset+2)];
|
|
offset += ELEMENTS_PER_VERTEX;
|
|
}
|
|
|
|
matrix.transformVectors(sPositions, sPositions);
|
|
offset -= ELEMENTS_PER_VERTEX * 4;
|
|
|
|
for (i=0; i<4; ++i)
|
|
{
|
|
mRawData[offset] = sPositions[int(3*i )];
|
|
mRawData[int(offset+1)] = sPositions[int(3*i + 1)];
|
|
mRawData[int(offset+2)] = sPositions[int(3*i + 2)];
|
|
offset += ELEMENTS_PER_VERTEX;
|
|
}
|
|
}
|
|
|
|
/** Sets all vertices of the object to the same color and alpha values. */
|
|
public function setUniformColor(color:uint, alpha:Number=1.0):void
|
|
{
|
|
for (var i:int=0; i<mNumVertices; ++i)
|
|
setColor(i, color, alpha);
|
|
}
|
|
|
|
/** Multiplies the alpha value of a vertex with a certain delta. */
|
|
public function scaleAlpha(vertexID:int, alpha:Number):void
|
|
{
|
|
if (alpha == 1.0) return;
|
|
else if (mPremultipliedAlpha) setAlpha(vertexID, getAlpha(vertexID) * alpha);
|
|
else
|
|
{
|
|
var offset:int = getOffset(vertexID) + COLOR_OFFSET + 3;
|
|
mRawData[offset] *= alpha;
|
|
}
|
|
}
|
|
|
|
private function getOffset(vertexID:int):int
|
|
{
|
|
return vertexID * ELEMENTS_PER_VERTEX;
|
|
}
|
|
|
|
// properties
|
|
|
|
/** Changes the way alpha and color values are stored. Updates all exisiting vertices. */
|
|
public function setPremultipliedAlpha(value:Boolean, updateData:Boolean=true):void
|
|
{
|
|
if (value == mPremultipliedAlpha) return;
|
|
|
|
if (updateData)
|
|
{
|
|
var dataLength:int = mNumVertices * ELEMENTS_PER_VERTEX;
|
|
|
|
for (var i:int=COLOR_OFFSET; i<dataLength; i += ELEMENTS_PER_VERTEX)
|
|
{
|
|
var alpha:Number = mRawData[i+3];
|
|
var divisor:Number = mPremultipliedAlpha ? alpha : 1.0;
|
|
var multiplier:Number = value ? alpha : 1.0;
|
|
|
|
if (divisor != 0)
|
|
{
|
|
mRawData[i] = mRawData[i] / divisor * multiplier;
|
|
mRawData[int(i+1)] = mRawData[int(i+1)] / divisor * multiplier;
|
|
mRawData[int(i+2)] = mRawData[int(i+2)] / divisor * multiplier;
|
|
}
|
|
}
|
|
}
|
|
|
|
mPremultipliedAlpha = value;
|
|
}
|
|
|
|
/** Indicates if the rgb values are stored premultiplied with the alpha value. */
|
|
public function get premultipliedAlpha():Boolean { return mPremultipliedAlpha; }
|
|
|
|
/** The total number of vertices. */
|
|
public function get numVertices():int { return mNumVertices; }
|
|
|
|
public function set numVertices(value:int):void
|
|
{
|
|
mRawData.fixed = false;
|
|
|
|
var delta:int = value * ELEMENTS_PER_VERTEX - mRawData.length;
|
|
var i:int;
|
|
|
|
for (i=0; i<delta; ++i) mRawData.push(0.0);
|
|
for (i=delta; i<0; ++i) mRawData.pop();
|
|
|
|
mNumVertices = value;
|
|
mRawData.fixed = true;
|
|
}
|
|
|
|
/** The raw vertex data; not a copy! */
|
|
public function get rawData():Vector.<Number> { return mRawData; }
|
|
}
|
|
} |