/
VertexDataFormat.as
275 lines (230 loc) · 10.1 KB
/
VertexDataFormat.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
// =================================================================================================
//
// Starling Framework
// Copyright Gamua GmbH. 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.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
import starling.core.Starling;
import starling.utils.StringUtil;
/** Describes the memory layout of VertexData instances, as used for every single vertex.
*
* <p>The format is set up via a simple String. Here is an example:</p>
*
* <listing>
* format = VertexDataFormat.fromString("position:float2, color:bytes4");</listing>
*
* <p>This String describes two attributes: "position" and "color". The keywords after
* the colons depict the format and size of the data that each attribute uses; in this
* case, we store two floats for the position (taking up the x- and y-coordinates) and four
* bytes for the color. (The available formats are the same as those defined in the
* <code>Context3DVertexBufferFormat</code> class:
* <code>float1, float2, float3, float4, bytes4</code>.)</p>
*
* <p>You cannot create a VertexData instance with its constructor; instead, you must use the
* static <code>fromString</code>-method. The reason for this behavior: the class maintains
* a cache, and a call to <code>fromString</code> will return an existing instance if an
* equivalent format has already been created in the past. That saves processing time and
* memory.</p>
*
* <p>VertexDataFormat instances are immutable, i.e. they are solely defined by their format
* string and cannot be changed later.</p>
*
* @see VertexData
*/
public class VertexDataFormat
{
private var _format:String;
private var _vertexSize:int;
private var _attributes:Vector.<VertexDataAttribute>;
// format cache
private static var sFormats:Dictionary = new Dictionary();
/** Don't use the constructor, but call <code>VertexDataFormat.fromString</code> instead.
* This allows for efficient format caching. */
public function VertexDataFormat()
{
_attributes = new Vector.<VertexDataAttribute>();
}
/** Creates a new VertexDataFormat instance from the given String, or returns one from
* the cache (if an equivalent String has already been used before).
*
* @param format
*
* Describes the attributes of each vertex, consisting of a comma-separated
* list of attribute names and their format, e.g.:
*
* <pre>"position:float2, texCoords:float2, color:bytes4"</pre>
*
* <p>This set of attributes will be allocated for each vertex, and they will be
* stored in exactly the given order.</p>
*
* <ul>
* <li>Names are used to access the specific attributes of a vertex. They are
* completely arbitrary.</li>
* <li>The available formats can be found in the <code>Context3DVertexBufferFormat</code>
* class in the <code>flash.display3D</code> package.</li>
* <li>Both names and format strings are case-sensitive.</li>
* <li>Always use <code>bytes4</code> for color data that you want to access with the
* respective methods.</li>
* <li>Furthermore, the attribute names of colors should include the string "color"
* (or the uppercase variant). If that's the case, the "alpha" channel of the color
* will automatically be initialized with "1.0" when the VertexData object is
* created or resized.</li>
* </ul>
*/
public static function fromString(format:String):VertexDataFormat
{
if (format in sFormats) return sFormats[format];
else
{
var instance:VertexDataFormat = new VertexDataFormat();
instance.parseFormat(format);
var normalizedFormat:String = instance._format;
if (normalizedFormat in sFormats)
instance = sFormats[normalizedFormat];
sFormats[format] = instance;
sFormats[normalizedFormat] = instance;
return instance;
}
}
/** Creates a new VertexDataFormat instance by appending the given format string
* to the current instance's format. */
public function extend(format:String):VertexDataFormat
{
return fromString(_format + ", " + format);
}
// query methods
/** Returns the size of a certain vertex attribute in bytes. */
public function getSize(attrName:String):int
{
return getAttribute(attrName).size;
}
/** Returns the size of a certain vertex attribute in 32 bit units. */
public function getSizeIn32Bits(attrName:String):int
{
return getAttribute(attrName).size / 4;
}
/** Returns the offset (in bytes) of an attribute within a vertex. */
public function getOffset(attrName:String):int
{
return getAttribute(attrName).offset;
}
/** Returns the offset (in 32 bit units) of an attribute within a vertex. */
public function getOffsetIn32Bits(attrName:String):int
{
return getAttribute(attrName).offset / 4;
}
/** Returns the format of a certain vertex attribute, identified by its name.
* Typical values: <code>float1, float2, float3, float4, bytes4</code>. */
public function getFormat(attrName:String):String
{
return getAttribute(attrName).format;
}
/** Returns the name of the attribute at the given position within the vertex format. */
public function getName(attrIndex:int):String
{
return _attributes[attrIndex].name;
}
/** Indicates if the format contains an attribute with the given name. */
public function hasAttribute(attrName:String):Boolean
{
var numAttributes:int = _attributes.length;
for (var i:int=0; i<numAttributes; ++i)
if (_attributes[i].name == attrName) return true;
return false;
}
// context methods
/** Specifies which vertex data attribute corresponds to a single vertex shader
* program input. This wraps the <code>Context3D</code>-method with the same name,
* automatically replacing <code>attrName</code> with the corresponding values for
* <code>bufferOffset</code> and <code>format</code>. */
public function setVertexBufferAt(index:int, buffer:VertexBuffer3D, attrName:String):void
{
var attribute:VertexDataAttribute = getAttribute(attrName);
Starling.context.setVertexBufferAt(index, buffer, attribute.offset / 4, attribute.format);
}
// parsing
private function parseFormat(format:String):void
{
if (format != null && format != "")
{
_attributes.length = 0;
_format = "";
var parts:Array = format.split(",");
var numParts:int = parts.length;
var offset:int = 0;
for (var i:int=0; i<numParts; ++i)
{
var attrDesc:String = parts[i];
var attrParts:Array = attrDesc.split(":");
if (attrParts.length != 2)
throw new ArgumentError("Missing colon: " + attrDesc);
var attrName:String = StringUtil.trim(attrParts[0]);
var attrFormat:String = StringUtil.trim(attrParts[1]);
if (attrName.length == 0 || attrFormat.length == 0)
throw new ArgumentError(("Invalid format string: " + attrDesc));
var attribute:VertexDataAttribute =
new VertexDataAttribute(attrName, attrFormat, offset);
offset += attribute.size;
_format += (i == 0 ? "" : ", ") + attribute.name + ":" + attribute.format;
_attributes[_attributes.length] = attribute; // avoid 'push'
}
_vertexSize = offset;
}
else
{
_format = "";
}
}
/** Returns the normalized format string. */
public function toString():String
{
return _format;
}
// internal methods
/** @private */
internal function getAttribute(attrName:String):VertexDataAttribute
{
var i:int, attribute:VertexDataAttribute;
var numAttributes:int = _attributes.length;
for (i=0; i<numAttributes; ++i)
{
attribute = _attributes[i];
if (attribute.name == attrName) return attribute;
}
return null;
}
/** @private */
internal function get attributes():Vector.<VertexDataAttribute>
{
return _attributes;
}
// properties
/** Returns the normalized format string. */
public function get formatString():String
{
return _format;
}
/** The size (in bytes) of each vertex. */
public function get vertexSize():int
{
return _vertexSize;
}
/** The size (in 32 bit units) of each vertex. */
public function get vertexSizeIn32Bits():int
{
return _vertexSize / 4;
}
/** The number of attributes per vertex. */
public function get numAttributes():int
{
return _attributes.length;
}
}
}