Render to texture not functioning - actionscript-3

I'm trying to test Stage3D's setRenderToTexture function, but my test code is not providing any output. I was hoping someone could tell me why - I've tried practically everything to get this working. The code I've created is below:
import flash.events.Event;
import flash.display.*;
import flash.display3D.*;
import flash.display3D.textures.Texture;
import AGALMiniAssembler;
//using stage size in several locations for simplicity - needs to be square, 2^n
[SWF(width='512', height='512', backgroundColor='0x000000', frameRate='60')]
//vars
var context0:Context3D;
var vBuff:VertexBuffer3D;
var iBuff:IndexBuffer3D;
var tex0:Texture;
var tex1:Texture;
var vShader:AGALMiniAssembler = new AGALMiniAssembler();
var fShader:AGALMiniAssembler = new AGALMiniAssembler();
var program:Program3D;
//create a square
var square:Shape = new Shape();
square.graphics.beginFill(0xaa00ff,1);
square.graphics.drawRect(10, 10, stage.stageWidth - 20, stage.stageHeight - 20);
//draw square to bitmapdata and create one blank bitmapdata
var tex0data:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0);
tex0data.draw(square);
var tex1data:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0);
//initialize stage3d
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, initStage3D);
stage.stage3Ds[0].requestContext3D();
function initStage3D(e:Event):void {
context0 = stage.stage3Ds[0].context3D;
context0.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0);
//create buffers - simple quad
var vBuff_vec:Vector.<Number> = new <Number>[-1, -1, 0, 0, 1,
-1, 1, 0, 0, 0,
1, 1, 0, 1, 0,
1, -1, 0, 1, 1];
var iBuff_vec:Vector.<uint> = new <uint>[0, 1, 2, 0, 2, 3]
vBuff = context0.createVertexBuffer(4, 5);
vBuff.uploadFromVector(vBuff_vec, 0, 4);
iBuff = context0.createIndexBuffer(6);
iBuff.uploadFromVector(iBuff_vec, 0, 6);
//load bitmapdata into textures - square to tex0 and blank to tex1
tex0 = context0.createTexture(tex0data.width,tex0data.height,'bgra',false);
tex0.uploadFromBitmapData(tex0data);
tex1 = context0.createTexture(tex1data.width, tex1data.height,'bgra',true);
tex1.uploadFromBitmapData(tex1data);
//create and upload simple shader program
vShader.assemble('vertex', 'mov v0, va1 \n'+ //uv coords to v0
'mov op, va0'); //output xyz coords
fShader.assemble('fragment', 'tex oc, v0, fs0 <2d, linear, nomip>'); //output texture at fs0
program = context0.createProgram();
program.upload(vShader.agalcode, fShader.agalcode);
context0.setProgram(program);
//set buffers
context0.setVertexBufferAt(0, vBuff, 0, 'float3');
context0.setVertexBufferAt(1, vBuff, 3, 'float2');
//prep rendering
addEventListener(Event.ENTER_FRAME, render);
}
function render(e:Event):void {
context0.clear();
//render tex0 onto tex1
context0.setRenderToTexture(tex1);
context0.setTextureAt(0, tex0);
context0.drawTriangles(iBuff);
//render tex1 to back buffer and present
context0.setTextureAt(0, tex1);
context0.setRenderToBackBuffer();
context0.drawTriangles(iBuff);
context0.present();
}
EDIT: I have noticed that changing the color of the blank bitmap does change the color of the final output - it's being displayed, but the square isn't being rendered to it.

FINALLY figured this out! There is a subtle, but important, step that must be taken before a texture is viable as a render target - it must be cleared after the render target is switched. Revising the render function as follows causes the program to function as expected:
function render(e:Event):void {
//render tex0 onto tex1
context0.setRenderToTexture(tex1);
context0.clear();
context0.setTextureAt(0, tex0);
context0.drawTriangles(iBuff);
//render tex1 to back buffer and present
context0.setTextureAt(0, tex1);
context0.setRenderToBackBuffer();
context0.clear();
context0.drawTriangles(iBuff);
context0.present();
}

Related

Dynamically generated display object as a gradient mask

I just want to create a gradient mask –which is generated dynamically through AS3 code– for an object (a pentagon in my following example,) and I simply, cannot! [The SWF file of what I've tried.]
The code works just fine unless it ignores the alpha gradient of the dynamically created Sprite for being used as a gradient mask (it's being treated as a solid mask), while the exact code acknowledges the "on-stage" created object (through the user interface) for being a gradient mask!
I think the runtime just cannot cache the object as bitmap and hence the ignorance! However, I'm stuck at making that happen! So please, shed some lights on this, any help is greatly appreciated in advance :)
var X:Number = 100, Y:Number = 35, W:Number = 350, H:Number = 150;
var mat:Matrix = new Matrix();
mat.createGradientBox(W, H, 0, X, Y);
var gradientMask:Sprite = new Sprite();
gradientMask.graphics.beginGradientFill(GradientType.LINEAR, [0, 0], [1, 0], [0, 255], mat);
gradientMask.graphics.drawRect(X, Y, W, H);
gradientMask.graphics.endFill();
pentagon1.cacheAsBitmap = true;
pentagon2.cacheAsBitmap = true;
onStageGradient.cacheAsBitmap = true;
gradientMask.cacheAsBitmap = true;
pentagon1.mask = gradientMask;
pentagon2.mask = onStageGradient;
stage.addEventListener(Event.ENTER_FRAME, _onEnterFrame);
function _onEnterFrame(e:Event):void {
pentagon1.x += 7;
pentagon2.x += 7;
if (pentagon1.x > 500) {
pentagon1.x = 0;
pentagon2.x = 0;
}
}
You have to add the gradientMask to the display list to have it in effect.
pentagon1.parent.addChild(gradientMask);

Stage3D function

I've been learning about stage3D, and am trying to render something for the first time. No run-time errors appear during the course of this program, but I am not seeing any output... my code is as follows (with comments where necessary)
import flash.events.Event;
import flash.display.*;
import flash.display3D.*;
import flash.display3D.textures.Texture;
import stillicidium.rendering.MeshMaker;
import stillicidium.rendering.AGALMiniAssembler;
stage.scaleMode = 'noBorder';
var context0:Context3D;
var vBuff:VertexBuffer3D;
var iBuff:IndexBuffer3D;
var tex:Texture;
//MeshMaker is designed to make a stage-fitted n-by-n mesh with
//corresponding uv coords - form is like: (x, y, 0 ,u, v)
var mm:MeshMaker = new MeshMaker(this.stage, 20);
var vBuff_vec:Vector.<Number> = mm.GetVertexBuffer();
var iBuff_vec:Vector.<uint> = mm.GetIndexBuffer();
mm = null;
var vShader:AGALMiniAssembler = new AGALMiniAssembler();
var pShader:AGALMiniAssembler = new AGALMiniAssembler();
var program:Program3D;
//Create a shape for drawing
var square:Shape = new Shape();
square.graphics.lineStyle(3,0xffffff,1,true);
square.graphics.beginFill(0xaa00ff,1);
square.graphics.moveTo(100,100);
square.graphics.lineTo(stage.stageWidth-100,100);
square.graphics.lineTo(stage.stageWidth-100,stage.stageHeight-100);
square.graphics.lineTo(100,stage.stageHeight-100);
square.graphics.lineTo(100,100);
//Draw shape as BitmapData to use as texture
var bmpdata:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,true,0);
bmpdata.draw(square);
//Initialize stage3D
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, initStage3D);
stage.stage3Ds[0].requestContext3D();
function initStage3D(e:Event):void {
context0 = stage.stage3Ds[0].context3D;
context0.configureBackBuffer(stage.stageWidth, stage.stageHeight, 4, true);
vBuff = context0.createVertexBuffer(0.2 * vBuff_vec.length, 5);
vBuff.uploadFromVector(vBuff_vec, 0, 0.2 * vBuff_vec.length);
vBuff_vec = null;
iBuff = context0.createIndexBuffer(iBuff_vec.length);
iBuff.uploadFromVector(iBuff_vec, 0, iBuff_vec.length);
iBuff_vec = null;
tex = context0.createTexture(bmpdata.width,bmpdata.height,'bgra',false);
tex.uploadFromBitmapData(bmpdata,0);
//Passes vertex buffer 1 to fragment shader
//Outputs vertex buffer 0
vShader.assemble('vertex', 'mov v0 va1 \n'+'mov op va0');
//Textures using data from vertex shader and sends to temporary register
//Outputs temporary register
pShader.assemble('fragment', 'tex ft0 v0 fs0 <2d,linear,nomip> \n'+'mov oc ft0');
program = context0.createProgram();
program.upload(vShader.agalcode, pShader.agalcode);
addEventListener(Event.ENTER_FRAME, render);
}
function render(e:Event):void {
if (!context0) { return; }
context0.clear();
//(x,y,0) to vertex buffer 0, (u,v) to vertex buffer 1
context0.setVertexBufferAt(0, vBuff, 0, 'float3');
context0.setVertexBufferAt(1, vBuff, 3, 'float2');
context0.setTextureAt(0, tex);
context0.setProgram(program);
context0.drawTriangles(iBuff);
context0.present();
}
I've compared this to several online examples, and have found no discrepancies. I'm thinking that there might be some setting I'm overlooking, or requirement I'm missing... I'm lost though. Thoughts?
The mesh size wasn't correct - the tutorials I read failed to mention that x, y, and z values used as vertex shader output are between -1 and 1. Hazarding a guess: my mesh fell outside the zone being rendered, and all the polygons were lost during the stage of rendering where the viewport is clipped.

Converting a Shape to a Bitmap

I have created a subclass of Shape that contains paired GraphicsPath and GraphicsStroke objects. The class has a public method for retrieving the shape as a bitmap for passage to a Pixelbender kernel - the method is as follows:
public function GetBitmap():Bitmap{
var bmpData:BitmapData = new BitmapData(this.width, this.height, true, 0x00FFFFFF);
bmpData.draw(this);
return new Bitmap(bmpData);
}
To test these, I have the following code:
var v:Vector.<Number> = new <Number>[10,10,50,10,50,50]; //defines GraphicsPath.data to be used
var wave:CustomWave = new CustomWave(v,0xff0000); //constructor for the subclass
wave.Refresh(); //clears the Shape and redraws the GraphicsPath
//adds as shape
addChild(wave);
//adds as bitmap
var bmp:Bitmap = wave.GetBitmap()
addChild(bmp);
The visual output from both:
Any idea what could be causing the difference?
It's a long story, how to work with width and height, if you don't want any problems, draw your path from (0,0) or you can modify your function:
public function GetBitmap():Bitmap {
var bounds: Rectangle = getBounds(this);
var bmpData:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
bmpData.draw(this, new Matrix(1, 0, 0, 1, -bounds.x, -bounds.y));
return new Bitmap(bmpData);
}
It may have something to do with the registration point of the Shape you're drawing into the Bitmap. Any chance you draw stuff above or to the left of the 0,0 coordinates? Then you'd have to add some params (perhaps a transformation Matrix) to the draw call, to get the right offset and such.

Reduce lag in Random Color Gradient Tweening (Programatically Generated)

Hello there,
I'm making some tests with random color gradient tweenings (to use them as backgrounds). After trying different things, I've found a code here and slightly changed it into the one below, which unfortunately produces lots of lag (interfering with timers and so in the same class). I'm using the GreenSock TweenLite/TweenMax engine:
package{
//...
public class Main extends MovieClip{
var color1:uint = Math.floor(Math.random()*0xFFFFFF + 1);
var color2:uint = Math.floor(Math.random()*0xFFFFFF + 1);
var colors:Object = {left:color1, right:color2};
var newColor1:uint = Math.floor(Math.random()*0xFFFFFF + 1);
var newColor2:uint = Math.floor(Math.random()*0xFFFFFF + 1);
var newColors:Object = {left:newColor1, right:newColor2};
var mySprite:Sprite = new Sprite();
public function Main() {
mySprite.x = 0;
mySprite.y = 0;
stage.addChildAt(mySprite, 1);
drawGradient();
animateBackground();
//...
}
//...
function drawGradient(){
var m:Matrix = new Matrix();
m.createGradientBox(805, 485, 0, 0, 0);
mySprite.graphics.beginGradientFill(GradientType.LINEAR, [colors.left, colors.right], [1, 1], [0x00, 0xFF], m, SpreadMethod.REFLECT);
mySprite.graphics.drawRoundRect(0,0,805,485, 0);
stage.setChildIndex(mySprite, 1);
}
function animateBackground(){
TweenMax.to(colors, 3, {hexColors:{left:newColor1, right:newColor2}, onUpdate:drawGradient, onComplete:reRandomize});
}
function reRandomize(){
color1 = newColor1;
color2 = newColor2;
newColor1 = Math.floor(Math.random()*0xFFFFFF + 1);
newColor2 = Math.floor(Math.random()*0xFFFFFF + 1);
animateBackground();
}
Everything works fine, but the lag keeps increasing and increasing. To make yourself an idea, the swf, which was previously 36 kB large, rose up to 74 kB just with this color implementation.
I was wondering if there is any more efficient way to deal with this effect, or something particularly laggy in the code. Does anybody know a way? Any help would be awesome and greatly appreciated.
Thank you in advance!
NOTE: I know this is the cause of lag after several test with different things disabled, so it is very unlikely that the excessive lag is from another source.
You forgot clearing the old graphics from the mySprite, thus any extra commands produce layers upon layers of graphics on that sprite. Add mySprite.graphics.clear() call to drawGradient function prior to drawing another gradient, should do.
function drawGradient(){
var m:Matrix = new Matrix();
m.createGradientBox(805, 485, 0, 0, 0);
mySprite.graphics.clear(); // here
mySprite.graphics.beginGradientFill(GradientType.LINEAR, [colors.left, colors.right], [1, 1], [0x00, 0xFF], m, SpreadMethod.REFLECT);
mySprite.graphics.drawRoundRect(0,0,805,485, 0);
stage.setChildIndex(mySprite, 1);
}

ActionScript 3 - adding gradient to object

I have an instance of a MovieClip called 'BlueBox', which is just a 'Drawing Object' which is blue. It is inside a container called 'option1MC' which is inside 'option1Container' which is on the stage.. It's color can be changed like so:
option1Container.addEventListener(MouseEvent.MOUSE_OVER, option1ContainerOver);
var optionOver:ColorTransform = new ColorTransform();
optionOver.color = 0xC56516;
function option1ContainerOver(evt:Event):void {
option1Container.option1MC.BlueBox.transform.colorTransform = optionOver;
}
I want to change the color to a gradient. This is what I tried:
var mtr:Matrix = new Matrix();
mtr.createGradientBox(option1Container.option1MC.BlueBox.width, option1Container.option1MC.BlueBox.height, 0, 0, 0);
mtr.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x0000FF], [1, 1], [0x00, 0xFF], mtr);
mtr.graphics.drawRect(0,0,option1Container.option1MC.BlueBox.width, option1Container.option1MC.BlueBox.height);
However, right now when I run the .fla file, it i giving me an error saying:
Access of possible undefined property graphics through a reference with static type flasg.geom:Matrix:
and is referring to the lines
mtr.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x0000FF], [1, 1], [0x00, 0xFF], mtr);
mtr.graphics.drawRect(0,0,option1Container.option1MC.BlueBox.width, option1Container.option1MC.BlueBox.height);
Any idea why?
A Matrix does not have a graphics property. You would need to apply this to a DisplayObject with a graphics property. That would look something like this with your setup:
//create the sprite to do the gradient fill on or use your MovieClip
//var spriteObject:Sprite = new Sprite() or just use option1Container;
//enter your matrix
var mtr:Matrix = new Matrix();
mtr.createGradientBox(option1Container.BlueBox.width, option1Container.BlueBox.height, 0, 0, 0);
//apply it to the sprite
sprite.graphics.beginGradientFill("linear", [0xFF0000, 0x0000FF], [1, 1], [0x00, 0xFF], mtr);
sprite.graphics.drawRect(0, 0, 50, 40);
sprite.graphics.endFill();
addChild(sprite);
I created a sprite variable here, which is not necessary as you can just swap sprite with option1Container
You can reference adobe's help for this as well: http://help.adobe.com/en_US/as3/dev/WS5b3ccc516d4fbf351e63e3d118a9b90204-7dd7.html
Now if you drew the MovieClip graphics in the IDE you won't currently be able to change it. In which case you can create a new MovieClip and add it to your container like so:
//note I made a target variable just to save on typing:
var target:MovieClip = option1Container.option1MC.BlueBox;
var gradientLayer:MovieClip = new MovieClip();
var gradientBox:Matrix = new Matrix();
gradientBox.createGradientBox(target.width, target.height, 0, 0, 0);
gradientLayer.graphics.beginGradientFill("linear", [0xFF0000, 0x0000FF], [1, 1], [0x00, 0xFF], gradientBox);
gradientLayer.graphics.drawRect(0, 0, target.width, target.height);
gradientLayer.graphics.endFill();
//giving the asset a name will allow you to easily remove it later
gradientLayer.name = "gradientLayer";
target.addChild(gradientLayer);
Notice I gave the gradient asset a name of "gradientLayer". If you want to remove this child later on you can do that with:
option1Container.option1MC.BlueBox.removeChild( option1Container.option1MC.BlueBox.getChildByName( "gradientLayer" ) );