/
RenderUtil.as
283 lines (250 loc) · 11.6 KB
/
RenderUtil.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
276
277
278
279
280
281
282
283
// =================================================================================================
//
// 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.utils
{
import flash.display.Stage3D;
import flash.display3D.Context3D;
import flash.display3D.Context3DMipFilter;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTextureFilter;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DWrapMode;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.utils.setTimeout;
import starling.core.Starling;
import starling.errors.AbstractClassError;
import starling.textures.Texture;
import starling.textures.TextureSmoothing;
/** A utility class containing methods related to Stage3D and rendering in general. */
public class RenderUtil
{
/** @private */
public function RenderUtil()
{
throw new AbstractClassError();
}
/** Clears the render context with a certain color and alpha value. */
public static function clear(rgb:uint=0, alpha:Number=0.0,
depth:Number=1.0, stencil:uint=0):void
{
Starling.context.clear(
Color.getRed(rgb) / 255.0,
Color.getGreen(rgb) / 255.0,
Color.getBlue(rgb) / 255.0,
alpha, depth, stencil
);
}
/** Returns the flags that are required for AGAL texture lookup,
* including the '<' and '>' delimiters. */
public static function getTextureLookupFlags(format:String, mipMapping:Boolean,
repeat:Boolean=false,
smoothing:String="bilinear"):String
{
// TODO this method can probably be removed
var options:Array = ["2d", repeat ? "repeat" : "clamp"];
if (format == Context3DTextureFormat.COMPRESSED)
options.push("dxt1");
else if (format == "compressedAlpha")
options.push("dxt5");
if (smoothing == TextureSmoothing.NONE)
options.push("nearest", mipMapping ? "mipnearest" : "mipnone");
else if (smoothing == TextureSmoothing.BILINEAR)
options.push("linear", mipMapping ? "mipnearest" : "mipnone");
else
options.push("linear", mipMapping ? "miplinear" : "mipnone");
return "<" + options.join() + ">";
}
/** Returns a bit field uniquely describing texture format and premultiplied alpha,
* so that each required AGAL variant will get its unique ID. This method is most
* useful when overriding the <code>programVariantName</code> method of custom
* effects.
*
* @return a bit field using the 3 least significant bits.
*/
public static function getTextureVariantBits(texture:Texture):uint
{
if (texture == null) return 0;
var bitField:uint = 0;
var formatBits:uint = 0;
switch (texture.format)
{
case Context3DTextureFormat.COMPRESSED_ALPHA:
formatBits = 3; break;
case Context3DTextureFormat.COMPRESSED:
formatBits = 2; break;
default:
formatBits = 1;
}
bitField |= formatBits;
if (!texture.premultipliedAlpha)
bitField |= 1 << 2;
return bitField;
}
/** Calls <code>setSamplerStateAt</code> at the current context,
* converting the given parameters to their low level counterparts. */
public static function setSamplerStateAt(sampler:int, mipMapping:Boolean,
smoothing:String="bilinear",
repeat:Boolean=false):void
{
var wrap:String = repeat ? Context3DWrapMode.REPEAT : Context3DWrapMode.CLAMP;
var filter:String;
var mipFilter:String;
if (smoothing == TextureSmoothing.NONE)
{
filter = Context3DTextureFilter.NEAREST;
mipFilter = mipMapping ? Context3DMipFilter.MIPNEAREST : Context3DMipFilter.MIPNONE;
}
else if (smoothing == TextureSmoothing.BILINEAR)
{
filter = Context3DTextureFilter.LINEAR;
mipFilter = mipMapping ? Context3DMipFilter.MIPNEAREST : Context3DMipFilter.MIPNONE;
}
else
{
filter = Context3DTextureFilter.LINEAR;
mipFilter = mipMapping ? Context3DMipFilter.MIPLINEAR : Context3DMipFilter.MIPNONE;
}
Starling.context.setSamplerStateAt(sampler, wrap, filter, mipFilter);
}
/** Creates an AGAL source string with a <code>tex</code> operation, including an options
* list with the appropriate format flag.
*
* <p>Note that values for <code>repeat/clamp</code>, <code>filter</code> and
* <code>mip-filter</code> are not included in the options list, since it's preferred
* to set those values at runtime via <code>setSamplerStateAt</code>.</p>
*
* <p>Starling expects every color to have its alpha value premultiplied into
* the RGB channels. Thus, if this method encounters a non-PMA texture, it will
* (per default) convert the color in the result register to PMA mode, resulting
* in an additional <code>mul</code>-operation.</p>
*
* @param resultReg the register to write the result into.
* @param uvReg the register containing the texture coordinates.
* @param sampler the texture sampler to use.
* @param texture the texture that's active in the given texture sampler.
* @param convertToPmaIfRequired indicates if a non-PMA color should be converted to PMA.
* @param tempReg if 'resultReg' is the output register and PMA conversion is done,
* a temporary register is needed.
*
* @return the AGAL source code, line break(s) included.
*/
public static function createAGALTexOperation(
resultReg:String, uvReg:String, sampler:int, texture:Texture,
convertToPmaIfRequired:Boolean=true, tempReg:String="ft0"):String
{
var format:String = texture.format;
var formatFlag:String;
switch (format)
{
case Context3DTextureFormat.COMPRESSED:
formatFlag = "dxt1"; break;
case Context3DTextureFormat.COMPRESSED_ALPHA:
formatFlag = "dxt5"; break;
default:
formatFlag = "rgba";
}
var needsConversion:Boolean = convertToPmaIfRequired && !texture.premultipliedAlpha;
var texReg:String = needsConversion && resultReg == "oc" ? tempReg : resultReg;
var operation:String = "tex " + texReg + ", " + uvReg + ", fs" + sampler +
" <2d, " + formatFlag + ">\n";
if (needsConversion)
{
if (resultReg == "oc") // the output color register cannot use a write mask ...
{
operation += "mul " + texReg + ".xyz, " + texReg + ".xyz, " + texReg + ".www\n";
operation += "mov " + resultReg + ", " + texReg;
}
else
{
operation += "mul " + resultReg + ".xyz, " + texReg + ".xyz, " + texReg + ".www\n";
}
}
return operation;
}
/** Requests a context3D object from the given Stage3D object.
*
* @param stage3D The stage3D object the context needs to be requested from.
* @param renderMode The 'Context3DRenderMode' to use when requesting the context.
* @param profile If you know exactly which 'Context3DProfile' you want to use, simply
* pass a String with that profile.
*
* <p>If you are unsure which profiles are supported on the current
* device, you can also pass an Array of profiles; they will be
* tried one after the other (starting at index 0), until a working
* profile is found. If none of the given profiles is supported,
* the Stage3D object will dispatch an ERROR event.</p>
*
* <p>You can also pass the String 'auto' to use the best available
* profile automatically. This will try all known Stage3D profiles,
* beginning with the most powerful.</p>
*/
public static function requestContext3D(stage3D:Stage3D, renderMode:String, profile:*):void
{
var profiles:Array;
var currentProfile:String;
var executeFunc:Function = SystemUtil.isDesktop ?
execute : SystemUtil.executeWhenApplicationIsActive;
if (profile == "auto")
profiles = ["enhanced",
"standardExtended", "standard", "standardConstrained",
"baselineExtended", "baseline", "baselineConstrained"];
else if (profile is String)
profiles = [profile as String];
else if (profile is Array)
profiles = profile as Array;
else
throw new ArgumentError("Profile must be of type 'String' or 'Array'");
stage3D.addEventListener(Event.CONTEXT3D_CREATE, onCreated, false, 100);
stage3D.addEventListener(ErrorEvent.ERROR, onError, false, 100);
requestNextProfile();
function requestNextProfile():void
{
currentProfile = profiles.shift();
try
{
executeFunc(stage3D.requestContext3D, renderMode, currentProfile);
}
catch (error:Error)
{
if (profiles.length != 0) setTimeout(requestNextProfile, 1);
else throw error;
}
}
function onCreated(event:Event):void
{
var context:Context3D = stage3D.context3D;
if (renderMode == Context3DRenderMode.AUTO && profiles.length != 0 &&
context.driverInfo.indexOf("Software") != -1)
{
onError(event);
}
else
{
onFinished();
}
}
function onError(event:Event):void
{
if (profiles.length != 0)
{
event.stopImmediatePropagation();
setTimeout(requestNextProfile, 1);
}
else onFinished();
}
function onFinished():void
{
stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onCreated);
stage3D.removeEventListener(ErrorEvent.ERROR, onError);
}
}
}
}