kelvinluck.com

a stroke of luck

Experiment with Papervision 3D particles and effects

A while back I was prototyping something for a client which involved lots of red dots moving around in 3D space, realised using Papervision 3D. I didn’t end up persuing this route with the client in the end but the effect was pretty cool so I thought I might as well share it here.

The idea is that there is a bunch of particles who are bouncing around randomly stuck within an invisible cube. The effect looked OK by itself but then I decided to try adding effects. I used a BlurFilter and a BitmapColorEffect to give the each of the particles trails. Then I changed the clipping point like in the original borg cube effects demo to give the impression of the particles falling. I like this version the best – if you move your mouse from side to side around the bottom of the demo swf then it starts to look like some kind of flocking is going on (like in my perlin noise experiment).

Click on the image below to see the demo. Click inside the demo swf to give it focus and then you can use the following keys:
  • 1 – Sets the render mode to normal clean particles (the default).
  • 2 – Sets the render mode to particles with trails.
  • 3 – Sets the render mode to falling particles with trails.
  • c – Toggles display of a cube showing the area the particles are contained within.

Particles and effects in Papervision 3D

The sourcecode for this example is pretty simple. You can see it below or you can download it from here.

package  
{
   import org.papervision3d.core.effects.BitmapColorEffect;
   import org.papervision3d.core.effects.BitmapLayerEffect;
   import org.papervision3d.core.geom.Particles;
   import org.papervision3d.materials.WireframeMaterial;
   import org.papervision3d.materials.utils.MaterialsList;
   import org.papervision3d.objects.DisplayObject3D;
   import org.papervision3d.objects.primitives.Cube;
   import org.papervision3d.view.AbstractView;
   import org.papervision3d.view.BasicView;
   import org.papervision3d.view.layer.BitmapEffectLayer;
   
   import flash.display.StageQuality;
   import flash.events.Event;
   import flash.events.KeyboardEvent;
   import flash.filters.BlurFilter;
   import flash.geom.Point;      

   /**
    * @author Kelvin Luck
    */

   [SWF(width='450', height='450', backgroundColor='#000000', frameRate='41')]

   public class ParticlesCube extends BasicView
   {
     
      public static const NUM_PARTICLES:int = 300;
      public static const CONTAINING_CUBE_SIZE:int = 500;
     
      public static const RENDER_MODE_CLEAN:int = 0;
      public static const RENDER_MODE_TRAILS:int = 1;
      public static const RENDER_MODE_FALLING:int = 2;
     
      private var particlesContainer:DisplayObject3D;
      private var particlesHolder:Particles;
      private var particles:Array;
      private var boundsCube:Cube;

      private var bfx:BitmapEffectLayer;
     
      private var _renderMode:int;
      public function set renderMode(value:int):void
      {
         if (value == _renderMode) return;
         
         clearBitmapEffects();
         
         var clippingPoint:Point = new Point();
         
         switch (value) {
            case RENDER_MODE_CLEAN:
               // nothing - effects already cleared above...
               break;
            case RENDER_MODE_FALLING:
               clippingPoint.y = -2;
               // fall through...
            case RENDER_MODE_TRAILS:
               bfx = new BitmapEffectLayer(viewport, stage.stageWidth, stage.stageHeight, true, 0xffffff);
               
               bfx.addEffect(new BitmapLayerEffect(new BlurFilter(2, 2, 2)));
               bfx.addEffect(new BitmapColorEffect(1, 1, 1, .9));
               
               bfx.clippingPoint = clippingPoint;
               
               bfx.addDisplayObject3D(particlesHolder);
               
               viewport.containerSprite.addLayer(bfx);
               break;
            default:
               throw new Error(value + ' is an invalid render mode');
         }
         _renderMode = value;
      }
     
      private var _displayCube:Boolean = true;
      public function set displayCube(value:Boolean):void
      {
         if (value != _displayCube) {
            _displayCube = value;
            boundsCube.visible = value;
         }
      }

      public function ParticlesCube()
      {
         super(550, 550);
         
         stage.quality = StageQuality.MEDIUM;
         
         particlesContainer = new DisplayObject3D();
         scene.addChild(particlesContainer);
         
         var cubeMaterial:WireframeMaterial = new WireframeMaterial(0x0000ff, 1, 2);
         var materialsList:MaterialsList = new MaterialsList();
         materialsList.addMaterial(cubeMaterial, 'all');
         
         boundsCube = new Cube(materialsList, CONTAINING_CUBE_SIZE, CONTAINING_CUBE_SIZE, CONTAINING_CUBE_SIZE);
         particlesContainer.addChild(boundsCube);
         displayCube = false;
         
         particlesHolder = new Particles();
         particlesContainer.addChild(particlesHolder);
         
         init(NUM_PARTICLES, CONTAINING_CUBE_SIZE);
         
         stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
         
         startRendering();
      }

      public function init(numParticles:int, containingCubeSize:int):void
      {
         var movingParticle:MovingParticle;
         
         if (particles) {
            particlesHolder.removeAllParticles();
         }
         
         particles = [];
         
         var i:int = numParticles;
         while (i--) {
            movingParticle = new MovingParticle(containingCubeSize);
            particlesHolder.addParticle(movingParticle);
            particles.push(movingParticle);
         }
         
      }

      override protected function onRenderTick(event:Event = null):void
      {
         // move each particle
         var movingParticle:MovingParticle;
         for each (movingParticle in particles) {
            movingParticle.position();
         }
         
         // twist the container based on mouse position
         particlesContainer.rotationY+=((stage.stageWidth/2)-mouseX)/200;
         particlesContainer.rotationX+=((stage.stageHeight/2)-mouseY)/200;
         
         // render
         super.onRenderTick(event);
      }
     
      private function clearBitmapEffects():void
      {
         if (bfx) {
            viewport.containerSprite.removeLayer(bfx);
            bfx = null;
         }
      }
     
      private function onKeyDown(event:KeyboardEvent):void
      {
         switch (String.fromCharCode(event.keyCode)) {
            case '1':
               renderMode = RENDER_MODE_CLEAN;
               break;
            case '2':
               renderMode = RENDER_MODE_TRAILS;
               break;
            case '3':
               renderMode = RENDER_MODE_FALLING;
               break;
            case 'c':
            case 'C':
               displayCube = !_displayCube;
               break;
         }
      }
   }
}

import org.papervision3d.core.geom.renderables.Particle;
import org.papervision3d.materials.special.ParticleMaterial;

internal class MovingParticle extends Particle
{
   
   public static const PARTICLE_SIZE:int = 10;
   public static const MAX_SPEED:int = 5;
   
   private var dX:Number;
   private var dY:Number;
   private var dZ:Number;
   private var halfSize:Number;

   public function MovingParticle(containingCubeSize:int)
   {
      var mat:ParticleMaterial = new ParticleMaterial(0xff0000, 1, ParticleMaterial.SHAPE_CIRCLE);
      super(mat, PARTICLE_SIZE);
     
      var size:int = containingCubeSize;
      halfSize = size / 2;
     
      x = (Math.random() * size) - halfSize;
      y = (Math.random() * size) - halfSize;
      z = (Math.random() * size) - halfSize;
     
      dX = Math.random() * MAX_SPEED;
      dY = Math.random() * MAX_SPEED;
      dZ = Math.random() * MAX_SPEED;
     
   }
   
   public function position():void
   {
      x += dX;
      if (x > halfSize || x < -halfSize) dX *= -1;
      y += dY;
      if (y > halfSize || y < -halfSize) dY *= -1;
      z += dZ;
      if (z > halfSize || z < -halfSize) dZ *= -1;
   }
}
// This line is just to stop the code formatter on my blog getting confused! >


jScrollPane 1.2 released

I’ve just released a new version of jScrollPane – my jQuery plugin for cross-browser customisable scrollbars. It’s been a long time since the plugin has been updated and this release contains a number of significant improvements:
  • a jScrollPane now correctly scrolls when you tab to a focusable element within it [example]
  • a jScrollPane can now automatically re-initialise itself when images inside it load [example]
  • external (and internal) links to an anchor within a jScrollPane now correctly scroll the jScrollPane [example]
  • defaults settings are now user-settable [example]
In addition to these changes, jScrollPane now has a new home on googlecode. This makes it easier for people to keep up to date with the latest version and more importantly it makes it easier for me to track bug reports and issues with jScrollPane (something that was basically impossible on the jQuery plugins site). As you can see, there is already a number of issues raised (one of which has high priority) so there may well be a bug fix release soon. In the meantime, it would be good to get people testing the new release and seeing if I broke anything else while implementing these changes…

Update:

jScrollPane 1.2.1 was released very shortly after 1.2 to fix a fairly serious bug which prevented links within the scroll pane from working correctly. As always, the googlecode site has the latest version.


Now running on wordpress

I’ve finally got around to updating my website and it is now running on the latest, greatest version of WordPress. After many years of fighting against Textpattern and never updating the blog because it was such a hassle I’ve come kicking and screaming into the 21st century. And I have to say I’m pretty pleased so far with WordPress… It was pretty easy to import my database from the the old Textpattern install and then it was just a case of going through and making sure all of my content wasn’t relying on any plugins I had installed in Textpattern.

For a theme, I started from Daydreams 2 by J but I found that there were lots of bits and pieces I wanted to change so I forked it into my own theme that I could add what I liked to it (including the random headers and Shadowbox support for displaying embedded flash) and fix a few bugs to make the generated HTML validate.

I also installed a few plugins straight away:
  • Textile2 helped by formatting all of my old posts imported from Textpattern (I first tried TextileWrapper but had issues with it adding extra P tags).
  • GeshiWrapper did likewise for sourcecode examples.
  • WordPress.com Stats should let me know a little more about my visitors.
  • Last.Fm Records lets me put my most recent popular album covers in the sidebar.
  • Contact Form 7 does my contact page.
  • Delicious for wordpress lets me put my latest links from delicious in the sidebar.
  • All in One SEO Pack might just help improve my search engine rankings.
  • Akismet should hopefully stop the comment spam and maybe I won’t need to moderate all my comments any more.
  • 404-notifier should show me any URLs that I broke when I did the move.
  • Live Comment Preview is a little bit of javascript niceness for when people are leaving comments.
We’ll see as everthing settles into place if they all behave as expected and if I discover I want or need anything else…

Because everything probably didn’t transfer correctly from the old site I have left it available at this URL (2005 was the year I started that blog):

http://2005.kelvinluck.com/

I’m still fiddling around with the theme (I’m not really happy with the headers at the moment) and the feeds to try and get everything working just so but hopefully the site is all functional and I will now be able to easily create new content so hopefully I’ll start doing it a bit more frequently…

And sorry to anyone who was subscribed to my RSS… I fear I may have spammed you all with my old content as part of this move.



New multiplayer papervision game

I’ve just released my entry into the Nonoba Multiplayer API Kick Off competition. It’s a multiplayer take on the old memory cards game where you have to turn over pairs of cards and try to remember what was under each one. The multiplayer aspect makes it much more frantic and fun as other players are looking at the same cards at the same time as you and you don’t want them to steal your pairs!

It will probably make more sense if you try it out yourself so go ahead and click the image below to play multiplayer memory mayhem!
My good friend Leigh Kayley did the designs (including illustrating all of the cool animals) and I built the game using Papervision, PureMVC, GTween and the Nonoba multiplayer API.

While I was building the game I did a little prototype for the score board transitions using papervision which you can see by clicking the image below. Click anywhere in the movie to give a random player some random points (and so maybe rearrange the scores) and press any keyboard key to toggle some mouse following behaviour.

Papervision 3D score panels test

Unfortunately in the game you can’t really see the 3d transitions on the scores so I thought I’d upload this for people to look at. And I’ve also uploaded the source code for anyone who is interested. It’s probably not the best because it was a prototype stuck together quickly but it may be useful to someone…

Update:

I’m pleased to say that Multiplayer Memory Mayhem won third prize in the multiplayer kickoff competion on Nonoba… Yay!


Wave Sequencer experiment

A couple of weeks ago I attended the FitC Amsterdam conference which was great fun. One of the most inspiring sessions I saw was Andre Michelle‘s “Making REAL Music Within Flash” where he talked us through generating sound in flash using the infamous flash sound hack and then showed us how he is redefining what should be possible in flash by building the hobnox audio tool.

I travelled to Amsterdam by train and ferry which meant that I had some spare time on the way back to play and luckily Andre and Joa Ebert have released the complex code behind the flash sound hack as part of their popforge open source library.

So I built my Wave Sequencer. The idea is that it loads in a wav file and splits it into 16 equally sized chunks. You can then re-arrange these chunks to create new variations on the beat. Each 16 chunk section is a pattern and you can create up to 16 different patterns by choosing different patterns from the “pattern bank” at the bottom of the screen.

The loop I’m using is the famous Amen break and I’ve sped it up a bit (and given you a slider so you can control the speed yourself). If you play around with it you’ll see you can slice and dice your own old skool jungle riddims :)

Screenshot of Wave Sequencer Experiment

Note: If you are experiencing choppy audio then you are probably running the latest revision of the flash player (9.0.115.0) which breaks onSoundComplete. So join the petition and ask Adobe to make some noise. I’ve compiled a standalone version of the app against the older 9.0.28.0 player and you can download the PC standalone version which doesn’t have the audio issues.

Obviously this little experiment is very rough around the edges and there is a lot that can be done to improve it but I’ve been too busy with “real work” to look at it in the weeks since I got back from FitC and I thought it was better to publish it as is than to leave it to get lost on my hard drive…

My code here is really simple, all of the complex stuff is done by the popforge library. So big thanks to Andre for the inspiration and the sourcecode :) Hopefully I’ll find some time in the coming months to take this a lot further and to turn it into an AIR app which is actually useful!



Wellcome Trust competition

A few months back I heard about a competition being organised by Ico Design, BD4D and the Wellcome Trust. The idea behind this competition was to “produce an interactive Flash piece that is playful and engaging using at least 2 words from the Word Soup”. The “Word Soup” is a collection of words that form part of the branding for the Wellcome Collection.

I managed to scrape together a little bit of time so I put together an entry. It was a mashup between the provided words and Flickr. I built it using Flashr and skinned it with a nice clean design knocked up by my good friend Leigh Kayley (based on the look and feel of the scramble game we found on the Wellcome collection’s site).

The entry is described quite succinctly in an article in Design Week so I’ll borrow their words:

Kelvin Luck asks us to look at images plucked from Flickr and choose from the wordsoup which one was used to tag the picture. It is harder than you think: what word could describe a masked woman in a spangly bikini who is wielding a chainsaw? Clue: it is not what you might think.

If that doesn’t explain it then you can play it yourself and see if you can get on the high scores. It can be frustrating at times because some people tag their photos a bit randomly but that adds to the adictiveness :)

Last night I was invited to the launch event for the new online exhibits site (where all of the competition entries are showcased) and the announcement of the competition’s winners. I was very surprised and pleased to find out that my entry got the second prize! Yay :)

I’d recommend checking out the site to see the many cool entries and the very nice innovative interface for finding them (also courtesy of Ico Design)



Flash on the Beach and some Perlin Noise

Last week I went to the Flash on the Beach conference and as has been said by many people, it was amazing.

I decided to go for more of the inspirational rather than technical sessions and saw some absolutely amazing speakers including (in order of appearance): Hoss Gifford, Joshua Davis, Brendan Dawes, Craig Swann, Mario Klingemann, Robert Hodgin, Erik Natzke, Chris Allen, Dr Woohoo, Andre Michelle, Marcos Weskamp and Jared Tarbell. Wow!

And as well as all these amazing talks there was the opportunity to meet loads of friends – old and new – and to chat about geeky stuff. And then there was all of the booze and the parties… And a girl in a box!

Anyway, one particular example from Robert got me thinking and wanting to play… He mentioned the PerlinNoise function and talked about how it could be used to simulate flocking. And he showed a slide where a perlin noise image had been used to set the rotation of a set of arrows displayed on top of it. I wanted to play with this and had a little bit of time over the weekend so I managed to come up with this:

My perlin creatures

It’s really simple stuff – you can get the sourcecode and see the process that it evolved through here. The code isn’t beautiful or optimised as I was trying to concentrate on playing rather than doing it right (a concept which seemed to be a recurring theme of the conference).

And I wasn’t the only one who got interested by Robert’s mention of perlin noise. Seb Lee-Delisle posted on Sunday about 3D Perlin Noise in Flash – it looks like a really interesting way to take this further. I can’t wait to see what he comes up with and hopefully to find some more time to play with it myself :)



Flash CS3 trial breaks Juniper Network Connect

It’s been a long time since I’ve posted here because I have been crazily busy working on a massive flex site with a great team over at Tribal DDB London. Hopefully it will go live at the start of next year so I can talk more about it. In the meantime, here is a quick tip for anyone having trouble connecting to a VPN powered by Juniper software’s Network Connect after installing the trial version of Flash CS3.

After installing a trial of Flash CS3 I noticed that I became unable to connect to the VPN. Network Connect would connect briefly and then pop up an alert saying “The Network Connect session terminated. Do you want to reconnect? (nc.windows.app.23711).”

After a bit of googling I found out that there were complaints about the early betas of CS3 and the Bonjour service that they installed affecting networking. And this is what was causing my problem. If you press Ctrl-Alt-Del and look at your processes then you will find a “mDNSResponder.exe” service running after installing Flash CS3. Ending this process allows you to connect through Network Connect.

You will have to do this every time you have restarted your computer as the process is automatically started with the system. Alternatively you can stop the service which starts the mDNSResponder process.

Go to Start Menu > Run and type in “services.msc” to open the services configuration panel. Look for a service with a name something like “##Id_String1.6844F930_1628_4223_B5CC_5BB94B879762##” (I think the bit after the dot will change on each computer). To confirm it’s the correct service double click on it and look at the “Path to executable” – it should be “C:\Program Files\Bonjour\mDNSResponder.exe”. If it is then change the “Startup type” to “disabled” and press the “Stop” button. You should now be able to connect to your VPN.

As far as I can tell, neither of these solutions have had a detrimental effect on my trial install of Flash so I think that they are safe changes to make but no guarantees! Hopefully the information will save someone the time it took me to find the answer.



Slimmer Timer – Flex, Haxe and a web service

I decided it was about time that I learnt how to use Flex. The best way to learn is always by building something so I decided that I’d build a little app to connect to the Slim Timer API and make it easy to keep track of your time. It seemed like something that was perfectly suited to Flex – built from UI components and connecting to an XML webservice.

So here it is: Slimmer Timer. You can download Mac and PC versions as well as seeing a screenshow on the project page at that link.

Although building the app was relatively straightforward, there are some gotchas that I ran into which I thought I would expand on a little here… All of these issues are as I experienced them in the standalone debug player – they may happen differently in the browser plugin.

  • Setting contentType and a custom “Accept” header on a HTTPService only works when the method is POST. If you change the method to GET then these extra headers aren’t sent along with the request.
  • The HTTPService.method documentation is very misleading… GET and POST are the only valid values unless you are using FDS as a proxy. More info on that on this post on FlexCoders

The solution to these problems was to wrap my generated swf in a Screenweaver Hx program. This makes it easy to write a little bit of haXe code which can intercept network calls from the flash and act on them. The haXe wrapper adds the required HTTP headers to my GET calls and also allows Flash to make PUT requests.

To support HTTP PUT requests I appended a “put=true” onto the URL requested by Flash which the haXe wrapper detects and then changes the HTTP request type. haXe’s HTTP class doesn’t support methods other than GET and POST but because it is open source I could easily extend this class to suit my needs. The haXe files I wrote can be seen here if you are interested…

As well allowing me to tweak the network behaviour and therefore make my app work, Screenweaver allowed me to remove the chrome from the app window and make the app slightly transparent. These extras help it to live up to it’s name of “Slimmer Timer” :)

As SlimmerTimer was my first foray into the world of Flex it’s not as well coded as it could be. I’m currently rewriting it using the Cairngorm framework so there may be a better version out some time soon but for now I think it’s quite useful as what it is – a simple little app for keeping track of your time.



Flashconference rocked!

At the end of last week I visited Stuttgart, Germany for FlashConference and I have to say it was wicked! A week later I’ve finally found the time to write a quick overview of the conference…

First up a massive thanks to Mario for getting me on the guest list and motivating me to make the journey – it was definitely worth it. A top notch list of very inspiring speakers – if I had any complaints it would only be that they tried to fit so much into one day so there was no space between the sessions (not even for lunch!).

The day started with a bit of an infomercial from Adobe, nothing particularly new although I’d never seen Scout before and it was a pretty nifty apollo sample app (basically a web browser with some of the features from Firebug and the web developer toolbar built in).

Then Ilias Sounas gave a talk showing how he had made some of his animations in Flash and talking about the importance of telling a good story. He also screened some of his animations – Circle of life was my favourite.

Next up Peter Elst gave an insight onto developing Flash for alternative devices. Everything from PSPs and Nintendo Wiis to Chumbys and other strange devices. Very interesting stuff and an area I definitely want to find some excuses to do some work in. Luckily Peter also had a bunch of ideas for how to convince your clients that building flash stuff for these devices is a good idea so maybe I’ll be able to use those ideas to convince some clients…

Then Claus Wahlers gave a talk about the Flash CS3 components which he helped to build. They definitely look like they are a major step forwards from all the previous ones especially in terms of how easy they are to extend and skin. I’m looking forward to getting my copy of CS3 and playing with them…

I skipped the next session (“Mercedes-Benz E-Class Experience Paris-Beijing”) because I needed to get some lunch! So did quite a few other people and so I got to meet up with Ralph, Asger, Martin, Peter, Claus and Wouter.

After lunch rushed back in for Mario’s session: “The blind Sketchmaker” – Artificial Creativity and Evolutionary Art in Flash. He started by discussing whether computers could create Art. After deciding that this wasn’t the case (as Art is more than just a picture – it’s a complete belief system around it) he went on to prove that he certainly could! He wrote an AS3/ Apollo app which could analyse pictures by many different criteria and learn rules about what makes a “good” picture. The program could then evolve it’s own pictures applying these rules to decide which direction to evolve in. He then got some of these generated pictures painted as full size oil paintings courtesy of a company in China who sent him some spam. Amazing, inspiring stuff – check out the finished artworks on flickr.

Then came Marcus Weskamp who talked about his experience building Wieden and Kennedy’s site. This site needed a way to display the hundreds of jobs this advertising agency has done and Marcus put his data visualisation skills to good use creating a timeline and 3d nodemap showing how all the projects, offices, people and clients were related to each other. Especially interesting was how the idea for the site evolved as a result of meetings between Marcus and his client.

Next Holger Eggert gave an insight into creating animated films in Flash. He gave an insight into how to construct a good story and use Flash/ Photoshop/ After Effects to make it real and he showed some of his work (and work in progress).

Finally, Andre Michelle gave an overview of advanced audio programming in Flash together with a hack which makes it possible to basically write your own samples in Flash and play them back. He demo’d a bunch of little apps he’s built from 303 simulators to little drum machines to .mod file players and tested the conference sound system while he was at it! Again, amazingly inspiring stuff, I need to find some time to play with all of this!

After that we all managed to get into the speakers dinner/ drinks and thus started a big night in Stuttgart… But that’s a different story!

As I said, the conference was really inspiring and it was great to get to meet all of the people involved too. Now that the hangover has died away I’m looking forward to finding some time to play with some of the new ideas I’ve got and looking forward to next years conference!