This site uses cookies to improve user expirence. Do you wish to allow that? Allow

Basics of Stage3D part IV: working with shaders [expert]

Nowadays shaders have become an inharent part of the 3D graphics, it is basically impossible to display a picture without creating a single shading program. Which is why, this time around we will take a quick look on how to work with shaders by adding a simple lighting to our virtual world. In Flash shaders are created using a series of instructions called AGAL, which is a very different language from ActionSctipt that requires a complete walkthrough on its own for full understanding - those however are easy to find on the Internet so for now we will only focus on the most basic stuff needed for lighting to work.
Returning to the code from previous article first thing we should do is add new Fragment Shader:
private const FRAGMENT_SHADER_LIGHT:String = "dp3 ft1, fc2, v1 n"+
"neg ft1, ft1 n"+
"max ft1, ft1, fc0 n"+
"mul ft2, fc4, ft1 n"+
"mul ft2, ft2, fc3 n"+
"add oc, ft2, fc1";
and Vertex Shader:
private const VERTEX_SHADER_LIGHT:String = "mov vt0, va0n"+
"m44 op, vt0, vc0n"+
"nrm vt1.xyz, va0.xyzn"+
"mov vt1.w, va0.wn"+	
"mov v1, vt1n" +
"mov v2, va1";
(The code has been borrowed from http://blog.norbz.net/2012/04/stage3d-agal-from-scratch-part-vii-let-there-be-light)
Above shaders will render triangles by taking each pixel and applying a light or dark color depending on their relative position to the camera. Before we do anything with that code we still need to compile it into a special "program" using the AGALMiniAssembler:
vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER_LIGHT, false);
fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER_LIGHT, false);

programPairLight = renderContext.createProgram();
programPairLight.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
(Don't forget to prepare the programPairLight variable)
So, right now we have two shaders: the standardprogramPair and light generating programPairLight. Only thing left to do is to decide which one we will be using by calling the setProgram function in Context3D. To better showcase the difference lets create another Box3D (lets call it "box3DLight") and then add the following code before present() but after the "box3D" rendering:
boxMat = box3DLight.getMatrix3D();
boxMat.identity();
boxMat.append(rotMat);
boxMat.appendTranslation(posVec.x + 1, posVec.y + 1, posVec.z+1);

finalTransform.identity();
finalTransform.append(boxMat);
finalTransform.append(world);
finalTransform.append(camera);
finalTransform.append(projection);
	
renderContext.setProgram(programPairLight);
renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true);

var p:Vector3D = camera.position.clone();
p.normalize();
var m3d:Matrix3D = finalTransform.clone();
m3d.invert();
p = m3d.transformVector(p);
p.normalize();
p.negate();

renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>([0,0,0,0]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, Vector.<Number>([p.x,p.y,p.z,1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, Vector.<Number>([1, 1, 1, 1]));
renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, Vector.<Number>([0.6, 0.6, 0.6, 1]) );

box3DLight.render(renderContext);
That is a lot to take in, but what really matters to use happens after setProgram ("boxMat" and "finalTransform" operations are previously discussed transformations from 3D to 2D). The term "program" is not used here without a reason as just like any other program on our computer shaders do a mathematical operations on variables and constants, only by using a very low-leveled language which is also quite limited as it only support up to 8 variables at a time and they only in program's memory. That is not the case for constants, although they are still limited to a maximum of 8, they can be supplied from the outside using either setProgramConstantsFromVector or setProgramConstantsFromMatrix functions - precisely what the code above is doing, it supplies the camera position ("p" variable) and light's color.
When done, we should see something like this:
This application only uses two shaders but there is nothing stopping us from create much, much and then just switch them around using the setProgram function just as we are doing with the lineStyle function when working with Graphics.
Whole code is available here: Tutorial3D_light.zip.

Basics of Stage3D part III: working with rotation [expert]

Continuing from where we left off this time around I want to write a bit about rotations. Anyone who started working with Flash's Stage3D without previous experience in 3D will sooner or later run into unforeseen results with appendRotation() function. To make things even less inviting for newcomers, looking for support on this topic on the Internet most routes lead to one answer "you will need complex numbers for that (quaternions to be exact). Hmm? Okay, quaternions might be really useful, but if you don't need 3D calculations to solve 2D rotations, it would be logical to assume that you also don't need 4D numbers to solve 3D problems, especially when being a newcomer to this topic.
Well, it doesn't matter right now so lets just move along, starting with the code from the previous post: Tutorial3D-2.zip, lets remove all those cubes and only leave one box, as that is all we need. By the way you could also create two additional variables: one for a Vector3D and one for Matrix3D - it will come in handy.
Generally rotation can be added in two ways: locally and globally, or in other words by either taking into the account the existing transformation or not. Which ever way we choose it is worth noting that existing transformation also includes any translation that might be applied to the matrix, which is why it is a good idea to make our lives easier and the rotation in separate Matrix3D object, only appending it to the existing object when needed. So lets start with the global transformation and creating a separate matrix (if you haven't done so already):
private var rotMat:Matrix3D = new Matrix3D();
Next, we will modify the handling of keyboard arrow keys by including the rotation for rotMat:
switch(key&0x0000ff) {
	case 0x00000f: // forward
		rotMat.appendRotation(SPEED, Vector3D.X_AXIS);
		break;
	case 0x0000f0: // backward
		rotMat.appendRotation(-SPEED, Vector3D.X_AXIS);
		break;
}
switch(key&0x00ff00) {
	case 0x000f00: // left
		rotMat.appendRotation(SPEED, Vector3D.Y_AXIS);
		break;
	case 0x00f000: // right
		rotMat.appendRotation(-SPEED, Vector3D.Y_AXIS);
		break;
}
Now only thing left to do is add that rotation to the box3D object, before it has its final transformation created in finalTransform:
var boxMat:Matrix3D = box3D.getMatrix3D();
boxMat.identity();
boxMat.append(rotMat);
Which should result in this:
To make this example a bit more interesting and also check if the rotation is correctly applied, that is without being affected by the translation, lets use the Vector3D I've mentioned earlier. Like this:
private var posVec:Vector3D = new Vector3D();
Working with vectors should not bring any troubles at this stage, so I will only mention we will have to append it in proper place - right before the boxMat.append(rotMat); insert this line:
boxMat.appendTranslation(posVec.x, posVec.y, posVec.z);
In turn getting this:
So the global transformation isn't really that complicated, but there is still the matter of local rotation. Fortunately it is quite trivial and comes down to only finding the right axis:
switch(key&0x0000ff) {
	case 0x00000f: // forward
		rotMat.appendRotation(SPEED, rotMat.transformVector(Vector3D.X_AXIS));
		posVec.y += SPEED/50;
		break;
	case 0x0000f0: // backward
		rotMat.appendRotation(-SPEED, rotMat.transformVector(Vector3D.X_AXIS));
		posVec.y -= SPEED/50;
		break;
}
switch(key&0x00ff00) {
	case 0x000f00: // left
		rotMat.appendRotation(SPEED, rotMat.transformVector(Vector3D.Y_AXIS));
		posVec.x -= SPEED/50;
		break;
	case 0x00f000: // right
		rotMat.appendRotation(-SPEED, rotMat.transformVector(Vector3D.Y_AXIS));
		posVec.x += SPEED/50;
		break;
}
The X_AXIS vector will be transformed the object's local system, which will give us the exact location of the X axis we will use for the rotation. If done right at least one edge (depending on the direction of the rotation) will stay in same place (minus the translation):
Source code: Tutorial3D_rot.zip

Understanding the "Graphics" symbol

Anyone who already spent some quality time in ActionScript is no doubt fully aware what MovieClips do or how they work, however Flash also allows for creation of something very similar, the symbols called "Graphics". At first they might seem like a simplified versions of MovieClips, which is of course true but their existence relies on much simpler rule... during the exporting process all "Graphics" symbols are split into pieces, similar to how the "break apart" command (CLTR+B) does it, with only difference being that all properties and tweens are applied to objects within. From the ActionScript's perspective this gives us an interesting ways of optimization by removing excessively nested MovieClips.
For example lets take a scenario where we want two objects to be circling around each other and also at the same time both of them smoothly relocated to a new position:

So the Anim symbol contains the animation of two circling objects, while on the stage both of them are being moved to a new location. Nothing interesting here yet, but if we decide we want access to both of those objects from ActionScript instinctively the first step will be to switch Anim from "Graphics" into "MovieClip" and than followed by giving it an instance name so we can navigate through it to access the two objects we want. Which is exactly the nesting we can avoid by using the Graphics - of course if we know how they work.
I suggest experimenting with this by yourself, starting by creating two MovieClips on the stage with two different key-frames within them, then name them "mc1" and "mc2" (instance names, so you can access them from ActionScript) and finally enclose the whole thing inside a Graphics symbol. What will happen if we call the following script from the Stage's timeline?
mc1.gotoAndStop(1);
mc2.gotoAndStop(2);
Because the Graphics symbol will be broken apart with MovieClips "mc1" and "mc2" being thrown outside, the script will execute correctly.
A ready example for download: GraphExample.zip

Fireworks generator script

Today something different/fun - fireworks! It seems that is quite a popular script to have around.
My generator can be downloaded from here: foras.zip. The proper class is at "foras.tools.ForFireworksGenerator". The package also contains some other scripts that I often use and I didn't want to split them apart.

Site locking with ActionScript

There is no sugarcoating over it, whatever is put on the Internet can be copied and redistributed without author's permission and Flash is not an exception. Luckily, as opposed to music or pictures, with a bit of ActionScript we can restrict access to our Flash file to only specific sites.
The task is simple, we will take the address our Flash application is started at and compare it to the one we want it to be:
function checkDomain(...domains):Boolean {
	if(domains == null || domains.length==0) return false;
	
	var sList:String = ".*"+domains.join("|.*");
	var sURLswf:String = stage.loaderInfo.url;
	var sRegPattern:String = "^http(|s)://("+sList+")/";
	var regPattern:RegExp = new RegExp(sRegPattern,"i");
	
	return regPattern.test(sURLswf);
}
The checkDomain function takes a list of comma-separated domains and checks them against the address from which the SWF file is being loaded - if any of the supplied domains matches the URL we get true in return. Comparison is done with regular expressions which are not part of this article, so I will just quickly explain that "sURLswf" variable is matched against the pattern in "sRegPattern" variable created from the list of supplied domains.
This actually could do, but there is still a chance our SWF will be loaded using on external Loader, which in turn means "loaderInfo.url" will still return a proper address despite the Loader being on different site. Solution? Simple, we add this line to the checkDomain function:
var sURLLoader:String = stage.loaderInfo.loaderURL;
...
return regPattern.test(sURLswf) && regPattern.test(sURLLoader);
And done, now it will be impossible to run our application from unauthorized servers.
However one thing needs to be pointed out, this function won't prevent the so called hot-linking, or in other words the loading of files from our server onto a completely unrelated website. That being said, we can't really do anything about it from the Flash side as it should be prevented from the server itself - I recommend reading about hot-linking and "htaccess" at this point.
But what if our host simply doesn't not support such countermeasures?
There is one trick that could possibly help, but unfortunately it is not always effective - quite frankly I am even not sure if it works at all, since I never had a chance to test it. Before I say anything more take a moment to check the code you use for embedding the SWF file and make sure that the allowScriptAccess line is set to "sameDomain". This simple change will allow Flash to execute a JavaScript code, which in turn will give us access to the contents of the browser's address bar. To do this we will have to call this code in ActionScript:
var  sURLTop:String;
if(ExternalInterface.available) {
	try {
		sURLTop = ExternalInterface.call("function(){return window.location.href}") as String;
	}catch(e:Error) {
		 sURLTop = "";
	}
} else {
	sURLTop = "";
}
If everything goes as planned, no outsider will be able to do anything at this point - allowing JavaScript execution will provide us with current URL that we will instantly check and block, but on the other hand restricting the code will result in an Error which we will treat as "no access" as well (since our own site does allow JavaScript). This solution would be perfect, but there is nothing stopping the users themselves from disabling the JavaScript in their browsers...

Derpy's Story

For a while now I've been working on a little flash game and time has finally come to release it. After 8 months of work I present you: Derpy's Story.

Strict comparison to a class type without the use of 'is' or 'instanceof'

In ActionScript2 and 3 you can always check if an object belongs to a certain class - in AS2 the keyword for this is instanceof while in AS3 its is and both of them do a pretty fine job, however they also return 'true' when checking a base class against a subclasses. In other words MovieClip check against Sprite will get us 'true':
var mc:MovieClip = new MovieClip();
trace(mc is Sprite); //true
This raises a question: what if we want to check against a specific class and only that one class? Solutions are two:
1. The getQualifiedClassName function.
Which is the most straightforward answer - we extract the class name as a String and compare it against the name we are searching for.
var mc:MovieClip = new MovieClip();
trace(getQualifiedClassName(mc) == "MovieClip"); //true
trace(getQualifiedClassName(mc) == "Sprite"); //false
2. Comparing the constructors.
This is a bit more advanced topic that only works in AS3. It is not always clearly stated but each object in ActionScript3 has a 'constructor' field which always holds the reference to the constructor used in creation process of an object and because in ActionScript there can be only one constructor, it just a matter of comparing them:
var mc:MovieClip = new MovieClip();
var con:Object = MovieClip.prototype.constructor;
trace(mc.constructor == con); //true
However there are few things worth pointing out here:
- because 'prototype' and 'constructor' are created dynamically, their access time is slower in comparison to the getQualifiedClassName function.
- however with a reasonably long loop and local variable holding the constructor reference, execution time can be even as twice as fast.
- mc.constructor in FlashDevelop (but not Flash Professional) will most likely throw an error, which needs to worked around by casting mc to the Object class:
var con:Object = MovieClip.prototype.constructor;
trace((mc as Object).constructor == con); //true
Unfortunately this will decrease the execution time by around 10%.

Creating a subclass with predefined graphics [basic]

Some time ago I have described how to use Interfaces to create a system of popups - here to be exact. I have mentioned then that similar system could be crafted with the use of subclasses, but in case of Flash this might bring unexpected hurdles to overcome. To understand what those hurdles are exactly, it is best to start from the very end by explaining how to deal with them - there are two solutions:

1. One base class to control all subclasses.
Lets say we have two popups, one with a text and other with an image, but also two shared elements: background and "OK" button. In this case it would be best to create three classes: MyPopup which will serve as a base class containing the logic for the background (named e.g. "mcBackground") and for the button ("mcButton"), followed by MyPopupImage responsible for image popup and MyPopupText responsible for the text popup. Unfortunately Flash will refuse to compile this setup, stating that there is a conflict with background in MyPopup and backgrounds in both MyPopupImage and MyPopupText. Of course you could just use different names, but this kind of defeats the purpose - the correct answer is to … delete the subclasses (their files to be exact), allowing Flash to create them on export.

Quite frankly I have no idea how it works, but one thing is for sure, we now can create as many popups as we want using the variables from the base class for background, button and whatever else we want. The one downfall of the solution is of course the need to create all the logic (for every popup) in one place.

2. Using the GET functions.
Here the situation is exactly of opposite of the one described in the previous point, we will be creating separate classes for each popup even it will require zero lines of actual execution code.
Back to the example of 3 classes: MyPopup, MyPopupImage and MyPopupText - we need to build them in such way that no variable conflicts with ones defined in the base class:

//MyPopup class
public class Popup extends MovieClip {
	public function Popup() {
	}
}
//MyPopupImage
public class MyPopupImage extends MyPopup {
	public var mcBackground:MovieClip;
	public var mcButton:MovieClip;
		
	public function MyPopupImage() {	
	}
}
//MyPopupText
public class MyPopupText extends MyPopup {
	public var mcBackground:MovieClip;
	public var mcButton:MovieClip;
		
	public function MyPopupText() {	
	}
}
Okay, but doesn't it make all pointless? From looks of it now the base class doesn't have the access to both "mcBackground" and "mcButton", meaning we wont be able to write single, shared logic for them in MyPopup. This is where the GET functions come into action! Lets added them to MyPopup in following way:
public class MyPopup extends MovieClip {
	public function MyPopup() {
	}
	protected function get popupBackground():MovieClip {
		return null;
	}
	protected function get popupButton():MovieClip {
		return null;
	}
}
And then rebuild MyPopupImage and MyPopupText as so:
//MyPopupImage
public class MyPopupImage extends MyPopup {
	public var mcBackground:MovieClip;
	public var mcButton:MyButton;
	
	public function MyPopupImage() {	
	}
	
	override protected function get popupBackground():MovieClip {
		return mcBackground;
	}
	override protected function get popupButton():MovieClip {
		return mcButton;
	}
}
//MyPopupText
public class MyPopupText extends MyPopup {
	public var mcBackground:MovieClip;
	public var mcButton:MyButton;
	
	public function MyPopupText() {	
	}
	
	override protected function get popupBackground():MovieClip {
		return mcBackground;
	}
	override protected function get popupButton():MovieClip {
		return mcButton;
	}
}
Finished, now base class MyPopup can access backgrounds and buttons from subclasses:
public function MyPopup() {
	super();
	trace(popupBackground!=null); //true
}

Check it out for yourself: subclass.zip

5 reasons why ActionScript2 is still useful

ActionScript3 is without a doubt more advanced language than ActionScript2 and each day more and more programmers move to work with it, however this doesn't mean ActionScript2 should stop existing - I mean, there must be a reason why Adobe haven't kill it yet. Here are 5 reasons why ActionScript2 can still be useful:

5. Easier to learn.
For people only just started their journey with Flash and have not decided yet about their language of choice, AS2 is a great introduction into programming. Not only does it avoid complex programming concepts (for example, type casting), it also does everything in its power to keep the application running.

4. Focused purpose.
With ActionScript3 you can achieve much more than with ActionScript2, but this in exchange requires more involvement from the programmer, sometimes even requiring knowledge outside of the standard documentation (Socket communication, hardware acceleration, 3D graphics). ActionScript does less, but usually it is enough.

3. In basic usage the speed is not a problem.
This has to be the most popular argument in the "AS2 vs AS3" debate, however the truth is, the fastest way to encumber the application is to use complex graphics, rather than the older code. If you aren't sure that you need the speed, chances are you don't.

2. The basic stuff requires less time to create.
Lets say we want to create a simple button that will open a desired website.
In ActionScript2 this will be enough:
on(press) {
	getURL("http://4as.pl");
}
ActionScript3 requires a bit more:
import flash.events.MouseEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
buttonMode = true;
addEventListener(MouseEvent.CLICK,onMouseClick);
function onMouseClick(e:MouseEvent):void {
	navigateToURL(new URLRequest("http://4as.pl"));
}
And not to mention stuff like MovieClip duplication

1. Better compatibility
Often it is easy to forget that Flash isn't exclusive to the PCs - With ActionScript2 there is a bigger potential for wider audience, especially with mobile devices where working with newer versions of Flash Player might be a bit... troublesome.

But all in all ActionScript3 is without a doubt a superior language and is ‘a must’ if we are serious about programming in Flash, especially in more advanced topics like game design.

Sending data from preloader to loaded SWF [basic]

Continuing the topic on interfaces from the previous post, I’ve mentioned that their usefulness is sometimes irreplaceable and one good example of this is when trying to establish a communication channel with loaded SWF file, most frequently seen in preloaders.
Lets start with a basic preloader:
var loadLoader:Loader = new Loader();
loadLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
loadLoader.load(new URLRequest("MyAnimation.swf"));
		
function onLoaded(e:Event):void {
	addChild(loadLoader.content);
}
In this case the MyAnimation.swf file and its content will be put on the stage when preloader finishes loading it. This is without a doubt the simplest solution, giving us pretty much no control over who and how can open this animation, which is why lets, for example, make it only viewable when preloader passes a correct keyword. To do this, we need to go back the the animation file and make it a document class:

In such way that external no external user will be able to start it neither automatically, nor manually:
public class MyAnimation extends MovieClip {
	public function MyAnimation() {
		super.gotoAndStop(1);
	}
public override function gotoAndStop(frame:Object,scene:String=null):void {
		return;
	}
	public override function gotoAndPlay(frame:Object,scene:String=null):void {
		return;
	}
	public override function play():void {
		return;
	}

}
Now even we won’t be able to start it, which is why we will make a backdoor entrance available only to us, by using an Interface of course. Lets make it look like this:
public interface IMyInterface {
	function playUsingPassword(pass:String):void;
}
And then implemented in our MyAnimation class:
public class MyAnimation extends MovieClip implements IMyInterface {
	public function MyAnimation() {
		super.gotoAndStop(1);
	}
	public function playUsingPassword(pass:String):void {
		if(pass == "MyPassword") super.play();
	}
public override function gotoAndStop(frame:Object,scene:String=null):void {
		return;
	}
	public override function gotoAndPlay(frame:Object,scene:String=null):void {
		return;
	}
	public override function play():void {
		return;
	}

}
(Don’t forget to add the implements!)
By writing super.play() we bypass our overwrites of play() from the MovieClip class. One last thing to do is to test it out:
var loadLoader:Loader = new Loader();
loadLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoaded);
loadLoader.load(new URLRequest("MyAnimation.swf"));
		
function onLoaded(e:Event):void {
	addChild(loadLoader.content);

	var main:IMyInterface = loadLoader.content as IMyInterface;
	if(main != null) main.playUsingPassword("MyPassword");
}
Resulting in something like this:
(Animation file is located here: MyAnimation.swf - anyone who wants a challenge can try loading it themselves)
Source: PreloaderInterface.zip


Older posts>>>