JigLib – Bust a Cap In Papervision3D


glockshot2

I made my first JigLib Demo!  Check it out.

If you don't know what JigLib is yet, you are really missing out.  Its a great project I'm proud to be the on the team of.  It is a 3D physics engine (currently only for Papervision3D, but has been ported to Sandy/Away).  It is a port from the JigLib engine done in C.  For more information on the project, check out the blog.

I don't really want to give a bottoms up rundown on how jiglib works, so I'm just going to point out a few things that will help you pull off effects like you see in this demo - both from the jiglib point of view, but also from a general pv3d and programming angle.

The Environment

This environment is actually very simple.  I have a bitmap for the floor, a lamp model, and a bitmap for the cubes.  I dynamically tint the cube materials to give it a little more depth and variety.  One thing you will notice is that each cube, and each side of each cube, gets its own material.  If you are wondering why, I did this to allow for the bullet holes to be drawn directly onto the material.  I use a movie material so that it is easy to just add a bitmap into with a specific blend mode, and then forget it.

The environment for JigLib is very similar.  Each cube is simply a "skin" for a class that represents a cube in JigLib.  This class is called "JBox".  You can think of a JBox as simply the "form" of a cube - it has position and rotation, which are transfered to the object that it is skinning.  This it the general way that JigLib works.  There are unique JigLib objects which use DisplayObject3D's in Papervision as a skin.  All transforms done to the JigLib physics object is then reflected on the DisplayObject3D.  JigLib has physics objects for Planes, Cubes (Box) and Spheres.  It also includes logic to generate a car with wheels, using a special wheel class and an extension of the JBox class.

So, each cube is controlled by a JBox - and the floor is a JPlane for them to bounce on.  There were some hit detection problems I was having with the plane, so I also added a JCube underneath the plane to give extra "floor" for the objects to run into.  This is a hack fix, but it seems to help.  Something else you can play with to help collision is the timing used by the physics engine.   A lower speed will mean that things will move slower, and detect more collisions more accurately.

The Gun Shot

The gun shot is made up of a couple different parts.  The gun itself actually "shoots" where it is looking - which is always 2000 in front of the barrel.  The only time this is changed is when a hit is detected on a cube.  When the mouse clicks on a cube, the target is changed to the exact coordinates of the hit.

The Hit

The actual hit is determined by the mouse click.  Take a look at the following code:

Actionscript:
  1. var hitPoint : Number3D = new Number3D(event.renderHitData.x, event.renderHitData.y, event.renderHitData.z);
  2.  
  3. var force:Number3D = new Number3D(hitPoint.x-camera.x, hitPoint.y-camera.y, hitPoint.z-camera.z);
  4. force.normalize();
  5. force.multiplyEq(6280);
  6. var rb:RigidBody = skins[event.displayObject3D];
  7. rb.AddWorldForce(new JNumber3D(force.x, force.y, force.z), new JNumber3D(hitPoint.x, hitPoint.y, hitPoint.z));

hitPoint records where the mouse was clicked in world space.  Remember, this is only called when a cube is clicked.  We then get the difference between that point and the camera, to get our "force" vector.  To make it a constant strength, we simply normalize it (make it a length of 1), then multiply it by the force we want.  We get our RigidBody from the dictionary I store the references in.  Then, we use the function "AddWorldForce" - which takes two JNumber3Ds - the force vector, and the world hit point.  This function will add the force to the object from world space - which saves us having to convert things into local coordinates.

The Bullet Trail

The bullet trail is a seperate class called LineOut.  I called it LineOut since it fades itself out every frame.  Once its invisible, it removes itself from the scene.  To get the start and end points of the Line between the gun, I pass in two objects - the target that is positioned at the hit point, and the the muzzle flash that is positioned over the gun barrel.  Before we can use the muzzle flash position, however, we need to update its world coordinates:

startPoint.world.calculateMultiply((startPoint.parent as DisplayObject3D).world, startPoint.transform);

Once the world matrix is updated with the current transform, we can use the world matrix to define the start point of the line.  I use a segmented line because it looks better - but feel free to play with how many vertices are actually used.

The Bullet Hole

Since we used a seperate bitmap for each side of the cubes, we can alter them individually.  This has more memory overhead, but I think it more than makes up for it by having bullet holes.  The bullet hole position is determined, again, by the renderHitData that is passed when a cube is clicked.  The U and V properties of the renderHitData tell us the "x" and "y" coordinates of the click on the material.  Once we have that, we can simply add a bullet hole bitmap to our movie, and position it correctly:

Actionscript:
  1. var mat:MovieMaterial = event.renderHitData.material as MovieMaterial;
  2. var hole : Bitmap = Sprite(mat.movie).addChild(new bulletAsset()) as Bitmap;
  3. hole.blendMode = "overlay";
  4. hole.x = event.renderHitData.u-hole.width*0.5;
  5. hole.y = event.renderHitData.v-hole.height*0.5;
  6.  
  7. mat.drawBitmap();

We take half the width and height to make sure that the bullet hole is properly centered on the hit point.  One other thing to keep in mind is to define the clipping rect for the movie material when you initially make it.  If you don't, your texture will become skewed if bullet holes go off the edge of the material, changing the size of the texture.

Those are the main parts of the demo - hopefully some things are helpful.  I was hoping to show some fun uses of JigLib - and hopefully spawn some cool ideas in the process.

Here is the Source Code

and

A Link to the Demo



Tails, You Win – 3D Trails In Papervision


2009-02-16_2218

I'm not really sure where the inspiration came from, but I decided to take a stab at doing Motion Trails with Pv3D.  Instead, I ended up with something more like a tail - but left the class name MotionTrail... Anyways - there isn't a whole lot to say on the technique I used here - but I will give a general overview just to save you looking at code.  See the Demo.

The tail is defined by Number3D objects which "chase" each other, with the head node being pinned to the target you are chasing.  Each Number3D, which I've extended into a Trail3D class (its internal - just look in the MotionTrail.as file) - springs after the node ahead of it.  You can control this "springyness" by adjusting the minEase property of the MotionTrail.  The minEase refers to the ease amount of the very last node on the tail - which increases each node up to the head, which has an ease of 1.  So, the lower you set minEase, the slinkier the tail will end up looking.  You can control how many nodes are in the tail by adjusting the "iterations" parameter of the MotionTail class.  You need at least 2.

The tail is actually a triangle strip, which progressively gets smaller the closer to the end it gets.  This 1) lets you use any material you want, and 2)easily adjust the look and feel of the tail all you want. Feel free to override it to affect the width with speed, distance, or any other cool variables you want.  It looked good just tapering so I left it that way.   The triangle strips vertices are adjusted to their corresponding TrailNode, so they always face the camera.  This keeps the tail visible from any direction.  Again, you could change this if you wanted it to be a more obvious flat tail.  My initial idea was to use the delta properties of the adjacent nodes to determine the nodes up vector (find the cross product between the dx/dy/dz values of the nodes on either side of the current node) - but again - I liked how this looks so I left it.

The demo itself just makes use of some bezier curves to keep things moving smoothly.  You will notice the nodes are flocking - but really it is just setting the control point and end point of each node within a certain range - which changes positions when the first object arrives.  You can press "F" to toggle it so that each node has its own unique path.

Anyways - here is the code,

and another look at the demo

enjoy.



Cache Me If You Can


At Infrared5, one thing we do to give us a good boost of speed in Flash is using a technique called (at least by me) Bitmap Injection.  The idea behind bitmap injection is to swap out pre-rendered frames of an animated clip into a bitmap object.  This removes the extra draw call of a MovieClip, thus speeding things up.  Since you can simply change the bitmapData property of a Bitmap, it simply reassigns what it is drawing, rather than having to copyPixels or do any of those performance killers.  Since we do it at work, and since Papervision is driven by bitmapData objects - I decided to whip up a quick material which could save your life.

A normal MovieMaterial, when set to animated, redraws the bitmap it is using for every frame of your rendering.  As you can imagine, this costs serious CPU, and if you have too many animated materials, your application will slow to a crawl.

So, I wrote up MovieCacheMaterial.  The name says it all really.  The material draws every frame of the movie into a global array for that particular asset.  Then, if the movie is ever on that same frame again, instead of redrawing the movie, it simply draws the 3D content using the already cached bitmap.  Then, any other DisplayObject3Ds using that same MovieClip will use the same cache as well.  So, your performance on the first loop will be the same as a normal MovieMaterial, but after that, the performance is equivalent to a normal BitmapMaterial - even though there is animation on it!

Check out the Demo

I wrote a hack work-around for the first loop issue, which might be of use to you.  You can call material.ripMovie(stage, frameRate, completeHandler).  You pass in the stage, how fast you want the movie to be ripped into the cache, and what function to call when it is complete.  The material will set your stage FPS to your desired number, then when it has ripped every frame of the MovieClip that was assigned to it, it will call the function you pass in as completeHandler.  This will make it so when you start your application you can have the best performance right off the bat.  Obviously it will take it longer to start up, however, since it has to rip your animation before beginning.

You are amazing Andy - What's the catch?

Unfortunately, there are some limitations to using this material.  The first limitation is that it is a memory hog.  Since you draw every frame of your Movie into memory, that much more memory is going to be used.  To give you an idea of how much that equals:  the animation in the demo above is ~150 frames, and is 512x512.  When it is fully parsed it uses about 140 megs of memory.  Smaller movies will obviously use less.  It is definitely something you need to be aware of however.

Second limitation: it doesn't work with real time dynamic movies or interactions.  Remember, it simply draws every frame of a movieclip.  That content can be dynamic the first loop, but after that, you are going to see exactly what happened the first time over and over again.  This obviously rules out any Virtual Mouse behaviors on a movie as well.  But, if you have a simple animation you want to loop - go for it!

And In the End

One other final thing to point out is that you can control your animation by changing your currentFrame on the MovieClip just like normal.  It won't mess up the caching, so you can pause, play, skip, etc. just like normal.  In the demo, you can press "F" to jump to the middle of the animation - using a gotoAndPlay.

Anyways - thats about all there is to say about it.  You can see the performance difference on the demo.  For me, on 49 planes, i get about 25 FPS with MovieCacheMaterial, and about 5 FPS with regular animated MovieMaterial.

See the Demo Again

and

Get the Source

Feel free to report any bugs.  I probably won't fix any, but it might help others :)   Also, you might have noticed I bolded certain words throughout the post.  If you can guess why I will give you a free copy of my upcoming top secret application (still a little ways off).

Enjoy!



Using QuadTrees In Papervision3D


How to Use It

To get the corrective z-sorting working, you simply need to use a new RenderEngine - QuadrantRenderEngine.  It takes one parameter - type - and depending how you set it you will get different results (and different performance).

type values:

  • QuadrantRenderEngine.CORRECT_Z_FILTER - This filter (QuadrantZFilter) does exactly what it sounds like - it sorts the triangles (when possible) correctly - removing the issues you would normally have with the painters algorithm.  Note that this will not correctly sort triangles that pass through each other.  For that you will need to use the next value.
  • QuadrantRenderEngine.QUAD_SPLIT_FILTER - The QuadrantFilter actually splits faces that intersect each other.  Since you can't correctly z sort triangles that run into each other - if you need the ability to cut faces that intersect, you can use this option.  Often though, after using this, you will still need to use the CORRECT_Z_FILTER to sort the newly cut faces properly.  Which brings us to:
  • QuadrantRenderEngine.ALL_FILTERS - This option uses both the CORRECT_Z_FILTER and QUAD_SPLIT_FILTER - which will give you corrective sorting of triangles no matter if they pass through each other or not.  An alternative to using this value is using CORRECT_Z_FILTER | QUAD_SPLIT_FILTER.

Making it run a little better

One feature we did add to the QuadTree is the ability to turn off testing for certain objects.  While in alot of cases this won't be a huge performance gain - it does offload some math thats not needed if you know it won't be an issue.  An example of this would be having a model floating in a courtyard.  You know the model will never have z-fighting with the courtyard, so you can turn off the courtyard from the tests.  It will still be sorted in the usual pv3d fashion, so you don't have to worry.

To turn off QuadTree testing for a DisplayObject3D - you can set the do3d.testQuad property.  When this boolean is set to false, the faces of the object will not have any QuadTree testing performed on them.  This will save you calculations when you don't need them.

You can also set the maximum depth the QuadTree will subdivided.  This can be set with the QuadTree.maxlevel property.  Mess with this to find a good balance of node size and performance.  Too many nodes can be a waste of infromation and requires testing rediculously small areas - but too large of nodes requires extra processing in many cases, and won't be as accurate.  Play with it per-project to find a good balance.

So When Should I Use This?

Honestly?  This is a feature that everyone wants but isn't the miracle cure they hoped for.  While a very cool feature - it can destroy your CPU.  For things such as model viewers, where you have no control over certain models, it is a great option.  However, for most thing you are MUCH better off using ViewportLayers and correct modeling techniques.  These will add no overhead and will give you the results you want.  In addition, since the QuadTree operates at runtime, if you have a static scene, you can use BSP, which preprocesses the scene.

QuadTreeVisualizer

I've included an extra class (you can see it in action by pressing "V" in the demos).  It is the QuadTreeVisualizer which will show you all the subdivisions you are currently using.  Doesn't help much - but is cool to check out and might help when debugging.

Demos

Demo One - This demo shows Papervision using the QuadrantRenderEngine.ALL_FILTERS property.  Both splitting and corrective z-sorting is performed on the objects.  You can toggle the "testQuad" property for the bottom plane as well.  Check it out HERE

Demo Two - This demo is only using corrective z-sorting.  Toggle it on and off and you can see where triangles are fixed where they were previously messed up.  Check it out HERE

Get The Source - Both demos use the same source - just uncomment where required :)

Enjoy!  And of course let us know of any bugs!



Papervision3D – Now Featuring Frustum Clipping


Yes, its true.  Clipping has now been added to the Papervision3D trunk (2.0).  For those of you unfamilier with the concept of clipping, it solves a very common and very annoying problem in 3D Engines - and does a few other things too.  The problem I'm referring to is when a face is culled when part of it extends past the camera.  You have probably noticed it in racing games or other various demos where the camera gets too close to an object and triangles start disappearing.  The previous solution was trying various things with zoom levels and being more restrictive with camera position to avoid the problem altogether.  Clipping (namely Near plane clipping) resolves this issue by cutting a face into new faces that *don't* pass the camera's fov.  Thus, no more disappearing faces.

Something else clipping helps with is not spending time drawing triangles that are outside the viewing frustum.  When you use full frustum clipping, faces are clipped to the sides of the screen.  Nothing is drawn "outside" the viewable area.  In addition, it removes the need to even project vertices that aren't visible to the camera (don't get too excited, it slows things back down by having to cut faces).

Use

So, using it is way to easy.  There is really only one class you have to know.  FrustumClipping.  This class takes 1 parameter which defines which planes you want to clip against.  You can use multiple planes to clip against by using a bitwise OR (I).  The possible planes you can use are ALL, NEAR, TOP, BOTTOM, LEFT, RIGHT.  I didn't include a far plane since it would just add unwanted computations in most instances.  If you want it let me know its easy to add now.

So, to set the clipping, you simply assign it to the RenderEngine's clipping property:

Actionscript:
  1. renderer.clipping = new FrustumClipping(FrustumClipping.ALL);

Now, all objects will be clipped to the planes you specified.  You can disable on object being clipped by setting DisplayObject3D.useClipping = false;  When that parameter is set, clipping will not be tested and you won't have any performance change.

Features/Information

  • Set camera.useCulling.  When this value is turned on, you get 2 benefits.  1) Objects outside the frustum aren't considered, but 2) ONLY objects that actually cross a frustum plane will be clipped on.  Meaning, objects in the scene that don't touch the edge of the screen are ignored and not even tested.
  • Full Frustum clipping is a costly procedure and often unnecessary.   If you just need general good clipping, and don't want the overhead, only use the near clipping plane: new FrustumClipping(FrustumClipping.NEAR);  I will be working on improving overall performance of the clipping but for now just be aware.
  • Papervision's render pipeline now has the following flow: culling->clipping->projection->draw.  FrustumClipping only projects and draws the vertices that remain after the clipping test.  If you do not use clipping, all vertices will be bulk projected (as before).
  • Use less faces when using clipping!  If you can get away with it, lower the face count as much as possible.  Clipping will add faces where it needs them, but the more faces that have to be tested the slower the performance.
  • Set useClipping = false for all objects that don't leave the screen!  (doesn't matter if you set culling though).

Demo and Source

View the Demo

and

Get the Demo