PageRenderTime 34ms CodeModel.GetById 17ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/de/nulldesign/nd2d/materials/Sprite2DBlurMaterial.as

https://bitbucket.org/HopeSky/mars_nd2d
ActionScript | 352 lines | 229 code | 74 blank | 49 comment | 26 complexity | 167b67a2248f652d7252f79995383dac MD5 | raw file
  1/*
  2 * ND2D - A Flash Molehill GPU accelerated 2D engine
  3 *
  4 * Author: Lars Gerckens
  5 * Copyright (c) nulldesign 2011
  6 * Repository URL: http://github.com/nulldesign/nd2d
  7 * Getting started: https://github.com/nulldesign/nd2d/wiki
  8 *
  9 *
 10 * Licence Agreement
 11 *
 12 * Permission is hereby granted, free of charge, to any person obtaining a copy
 13 * of this software and associated documentation files (the "Software"), to deal
 14 * in the Software without restriction, including without limitation the rights
 15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 16 * copies of the Software, and to permit persons to whom the Software is
 17 * furnished to do so, subject to the following conditions:
 18 *
 19 * The above copyright notice and this permission notice shall be included in
 20 * all copies or substantial portions of the Software.
 21 *
 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 28 * THE SOFTWARE.
 29 */
 30
 31package de.nulldesign.nd2d.materials {
 32
 33	import com.adobe.utils.AGALMiniAssembler;
 34
 35	import de.nulldesign.nd2d.display.Camera2D;
 36
 37	import de.nulldesign.nd2d.geom.Face;
 38	import de.nulldesign.nd2d.geom.UV;
 39	import de.nulldesign.nd2d.geom.Vertex;
 40	import de.nulldesign.nd2d.materials.shader.Shader2D;
 41	import de.nulldesign.nd2d.materials.shader.ShaderCache;
 42	import de.nulldesign.nd2d.materials.texture.ASpriteSheetBase;
 43	import de.nulldesign.nd2d.materials.texture.SpriteSheet;
 44
 45	import flash.display.Shader;
 46	import flash.display.Stage;
 47
 48	import flash.display3D.Context3D;
 49	import flash.display3D.Context3DProgramType;
 50	import flash.display3D.Context3DTextureFormat;
 51	import flash.display3D.Program3D;
 52	import flash.display3D.textures.Texture;
 53	import flash.geom.Matrix;
 54	import flash.geom.Matrix3D;
 55	import flash.geom.Point;
 56	import flash.geom.Rectangle;
 57
 58	/**
 59	 * http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/
 60	 */
 61	public class Sprite2DBlurMaterial extends Sprite2DMaterial {
 62
 63		protected const HORIZONTAL_FRAGMENT_SHADER:String =
 64			// -4
 65				"mov ft0, v0								\n" +
 66						"sub ft0.x, ft0.x, fc3.y					\n" +
 67						"tex ft1, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 68						"mul ft1, ft1, fc2.x						\n" +
 69
 70					// -3
 71						"add ft0.x, ft0.x, fc3.z					\n" +
 72						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 73						"mul ft2, ft2, fc2.y						\n" +
 74						"add ft1, ft1, ft2							\n" +
 75
 76					// -2
 77						"add ft0.x, ft0.x, fc3.z					\n" +
 78						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 79						"mul ft2, ft2, fc2.z						\n" +
 80						"add ft1, ft1, ft2							\n" +
 81
 82					// -1
 83						"add ft0.x, ft0.x, fc3.z					\n" +
 84						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 85						"mul ft2, ft2, fc2.w						\n" +
 86						"add ft1, ft1, ft2							\n" +
 87
 88					// 0
 89						"add ft0.x, ft0.x, fc3.z					\n" +
 90						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 91						"mul ft2, ft2, fc3.x						\n" +
 92						"add ft1, ft1, ft2							\n" +
 93
 94					// 1
 95						"add ft0.x, ft0.x, fc3.z					\n" +
 96						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
 97						"mul ft2, ft2, fc2.w						\n" +
 98						"add ft1, ft1, ft2							\n" +
 99
100					// 2
101						"add ft0.x, ft0.x, fc3.z					\n" +
102						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
103						"mul ft2, ft2, fc2.z						\n" +
104						"add ft1, ft1, ft2							\n" +
105
106					// 3
107						"add ft0.x, ft0.x, fc3.z					\n" +
108						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
109						"mul ft2, ft2, fc2.y						\n" +
110						"add ft1, ft1, ft2							\n" +
111
112					// 4
113						"add ft0.x, ft0.x, fc3.z					\n" +
114						"tex ft2, ft0, fs0 <TEXTURE_SAMPLING_OPTIONS>		\n" +
115						"mul ft2, ft2, fc2.x						\n" +
116						"add ft1, ft1, ft2							\n" +
117
118						"mul ft1, ft1, fc0							\n" +
119						"add oc, ft1, fc1							\n";
120
121		protected var VERTICAL_FRAGMENT_SHADER:String;
122
123		protected var horizontalShader:Shader2D;
124		protected var verticalShader:Shader2D;
125
126		protected const MAX_BLUR:uint = 4;
127
128		protected var blurredTexture:Texture;
129		protected var blurredTexture2:Texture;
130		protected var blurredTextureCam:Camera2D = new Camera2D(1, 1);
131		protected var activeRenderToTexture:Texture;
132
133		protected var blurX:uint;
134		protected var blurY:uint;
135
136		protected const BLUR_DIRECTION_HORIZONTAL:uint = 0;
137		protected const BLUR_DIRECTION_VERTICAL:uint = 1;
138
139		protected var fragmentData:Vector.<Number>;
140
141		public function Sprite2DBlurMaterial(blurX:uint = 4, blurY:uint = 4) {
142
143			super();
144
145			VERTICAL_FRAGMENT_SHADER = HORIZONTAL_FRAGMENT_SHADER.replace("sub ft0.x, ft0.x, fc3.y", "sub ft0.y, ft0.y, fc3.y");
146			var reg:RegExp = /add ft0.x, ft0.x, fc3.z/g;
147			VERTICAL_FRAGMENT_SHADER = VERTICAL_FRAGMENT_SHADER.replace(reg, "add ft0.y, ft0.y, fc3.z");
148
149			fragmentData = new Vector.<Number>(8, true);
150			setBlur(blurX, blurY);
151		}
152
153		public function setBlur(blurX:uint = 4, blurY:uint = 4):void {
154			this.blurX = blurX;
155			this.blurY = blurY;
156
157			drawCalls = Math.max(1, Math.ceil(blurX / MAX_BLUR) + Math.ceil(blurY / MAX_BLUR));
158		}
159
160		protected function updateBlurKernel(radius:uint, direction:uint):void {
161
162			fragmentData[0] = 0.0; //0.05; // fc2.x
163			fragmentData[1] = 0.0; //0.09; // fc2.y
164			fragmentData[2] = 0.0; //0.12; // fc2.z
165			fragmentData[3] = 0.0; //0.15; // fc2.w
166			fragmentData[4] = 1.0; //0.16; // fc3.x
167			// movement: minus 4 and plus 1 several times...
168			fragmentData[5] = 4.0 * (1.0 / (direction == BLUR_DIRECTION_HORIZONTAL ? texture.textureWidth : texture.textureHeight)); // fc3.y
169			fragmentData[6] = 1.0 * (1.0 / (direction == BLUR_DIRECTION_HORIZONTAL ? texture.textureWidth : texture.textureHeight)); // fc3.z
170			fragmentData[7] = 0.0;  // fc3.w
171
172			// http://stackoverflow.com/questions/1696113/how-do-i-gaussian-blur-an-image-without-using-any-in-built-gaussian-functions
173			if(radius == 0) return;
174
175			var kernelLen:uint = radius * 2 + 1;
176			var r:Number = -radius;
177			var kernel:Array = [];
178			var twoRadiusSquaredRecip:Number = 1.0 / (2.0 * radius * radius);
179			var sqrtTwoPiTimesRadiusRecip:Number = 1.0 / (Math.sqrt(2.0 * Math.PI) * radius);
180			var kernelSum:Number = 0.0;
181			var i:int = 0;
182
183			for(i = 0; i < kernelLen; i++) {
184				var x:Number = r * r;
185				kernel[i] = sqrtTwoPiTimesRadiusRecip * Math.exp(-x * twoRadiusSquaredRecip);
186				r++;
187				kernelSum += kernel[i];
188			}
189
190			for(i = 0; i < kernelLen; i++) {
191				kernel[i] /= kernelSum;
192			}
193
194			var idx:uint = 4;
195			for(i = kernelLen / 2; i >= 0; i--) {
196				fragmentData[idx--] = kernel[i];
197			}
198		}
199
200		override protected function prepareForRender(context:Context3D):void {
201
202			// there is no ipad1 fragment shader fix for this material yet. so assume, the node is always tinted and use the color transform shader
203			nodeTinted = true;
204
205			super.prepareForRender(context);
206
207			context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
208
209			if(!blurredTexture) {
210				blurredTexture = context.createTexture(texture.textureWidth, texture.textureHeight, Context3DTextureFormat.BGRA, true);
211			}
212
213			if(!blurredTexture2) {
214				blurredTexture2 = context.createTexture(texture.textureWidth, texture.textureHeight, Context3DTextureFormat.BGRA, true);
215			}
216		}
217
218		protected function renderBlur(context:Context3D, startTri:uint, numTris:uint):void {
219			activeRenderToTexture = (activeRenderToTexture == blurredTexture ? blurredTexture2 : blurredTexture);
220			context.setRenderToTexture(activeRenderToTexture, false, 2, 0);
221			context.clear(0.0, 0.0, 0.0, 0.0);
222			context.drawTriangles(indexBuffer, startTri * 3, numTris);
223			context.setTextureAt(0, activeRenderToTexture);
224		}
225
226		override public function render(context:Context3D, faceList:Vector.<Face>, startTri:uint, numTris:uint):void {
227			generateBufferData(context, faceList);
228
229			// set up camera for blurry texture
230			blurredTextureCam.resizeCameraStage(texture.textureWidth, texture.textureHeight);
231			blurredTextureCam.x = -texture.textureWidth * 0.5;
232			blurredTextureCam.y = -texture.textureHeight * 0.5;
233
234			// save camera matrix
235			var savedCamMatrix:Matrix3D = viewProjectionMatrix;
236			var savedSpriteSheet:ASpriteSheetBase = spriteSheet;
237			var savedModelMatrix:Matrix3D = modelMatrix;
238			viewProjectionMatrix = blurredTextureCam.getViewProjectionMatrix();
239			spriteSheet = null;
240			modelMatrix = new Matrix3D();
241
242			updateBlurKernel(MAX_BLUR, BLUR_DIRECTION_HORIZONTAL);
243			prepareForRender(context);
244
245			activeRenderToTexture = null;
246			var totalSteps:int;
247			var i:uint;
248
249			// BLUR X
250			totalSteps = Math.floor(blurX / MAX_BLUR);
251
252			for(i = 0; i < totalSteps; i++) {
253				renderBlur(context, startTri, numTris);
254			}
255
256			if(blurX % MAX_BLUR != 0) {
257				updateBlurKernel(blurX % MAX_BLUR, BLUR_DIRECTION_HORIZONTAL);
258				context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
259
260				renderBlur(context, startTri, numTris);
261			}
262
263			// BLUR Y
264			context.setProgram(verticalShader.shader);
265			updateBlurKernel(MAX_BLUR, BLUR_DIRECTION_VERTICAL);
266			context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
267
268			totalSteps = Math.floor(blurY / MAX_BLUR);
269
270			for(i = 0; i < totalSteps; i++) {
271				renderBlur(context, startTri, numTris);
272			}
273
274			if(blurY % MAX_BLUR != 0) {
275				updateBlurKernel(blurY % MAX_BLUR, BLUR_DIRECTION_VERTICAL);
276				context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, fragmentData, 2);
277
278				renderBlur(context, startTri, numTris);
279			}
280
281			context.setRenderToBackBuffer();
282
283			// FINAL PASS
284			viewProjectionMatrix = savedCamMatrix;
285			spriteSheet = savedSpriteSheet;
286			modelMatrix = savedModelMatrix;
287
288			updateBlurKernel(0, BLUR_DIRECTION_HORIZONTAL);
289			prepareForRender(context);
290
291			if(blurX == 0 && blurY == 0) {
292				activeRenderToTexture = texture.getTexture(context);
293			}
294
295			context.setTextureAt(0, activeRenderToTexture);
296
297			context.drawTriangles(indexBuffer, startTri * 3, numTris);
298
299			clearAfterRender(context);
300		}
301
302		override public function handleDeviceLoss():void {
303			super.handleDeviceLoss();
304			blurredTexture = null;
305			blurredTexture2 = null;
306		}
307
308		override protected function initProgram(context:Context3D):void {
309			if(!shaderData) {
310				horizontalShader = ShaderCache.getInstance().getShader(context, this, VERTEX_SHADER, HORIZONTAL_FRAGMENT_SHADER, 4, texture.textureOptions, 0);
311				verticalShader = ShaderCache.getInstance().getShader(context, this, VERTEX_SHADER, VERTICAL_FRAGMENT_SHADER, 4, texture.textureOptions, 1000);
312
313				shaderData = horizontalShader;
314			}
315		}
316		
317		override public function dispose():void
318		{
319			if(horizontalShader) 
320			{
321				horizontalShader.dispose();
322				horizontalShader = null;
323			}
324			
325			if(verticalShader) 
326			{
327				verticalShader.dispose();
328				verticalShader = null;
329			}
330			
331			if(blurredTexture) 
332			{
333				blurredTexture.dispose();
334				blurredTexture = null;
335			}
336			
337			if(blurredTexture2) 
338			{
339				blurredTexture2.dispose();
340				blurredTexture2 = null;
341			}
342
343			if(activeRenderToTexture) 
344			{
345				activeRenderToTexture.dispose();
346				activeRenderToTexture = null;
347			}
348
349			super.dispose();
350		}
351	}
352}