Papervision3D – fixes to VirtualMouse, Interactivity and using UIComponents

First off, I’ll just say that this kind of work is enough to cause you to go barking mad. Just set yer hair on fire and start running the halls naked now.

I’ll start off by archiving what I found, then go into “best practices” on how to create a plane using a movieclip that has UIComponents in it.

Findings:

  • VirtualMouse has a weird issue when stage quality is set to anything other than LOW and you use coordinate conversion on the values passed in. When set to LOW, it works as expected, but when set to any other setting, the UIComponents only lit up when you moused over the label of a component, not the drawn area and the basic movieclip I had in my test with rollover events didn’t even fire. Only when I moved the movieclip to 0,0, then did getObjectsUnderPoint did it work perfectly under all quality settings. Oye – I don’t know why I’m afraid. But in testing in a pure flash IDE test, quality settings didn’t cause any issues with getObjectsUnderPoint() – so, it’s obviously a VirtualMouse issue.
  • When you have a scrollRect defined on a movieclip, getObjectsUnderPoint() does not return any values IF you move the movieclip THEN call getObjectsUnderPoint. Only AFTER the displayList is updated does it work with the movieclip moved. That kinda sucked for what I had to accomplish with PV3D unfortunately. But fortunately, a new feature JUST implemented by Carlos makes this a non issue altogether. I know, you’re asking, why in the WORLD would you be doing such a thing, but it’ll make sense later on as I explain how to use UIComponents with movies used for materials.
  • You might think that if a movieclip/sprite is invisible (visible = false), that it’s not being rendered and therefore, you’re saving on the CPU – this maybe true to a certain extent of course, but if you right click and show redraw regions in the flash player, you can clearly see that if there ARE any visual changes like an animation or rollover state in your movieclip/sprite, you’ll see a redraw region show up right where the animation/visual change is taking place. Now, I’m not at all sure what this means in terms of how much processing is taken up because of it, but it IS causing the flash player to consider that are for SOME type of work.

Now, on to why I’m actually writing this post: How to use UIComponents in materials used on 3D objects with Papervision3D.

Initially, we have to realize what’s happening with UIComponents and what the initial problems are. The UIComponents are, when first added to a movieclip have a certain size, but it’s not the expected/anticipated size you might see when you add them to the Flash IDE stage at design time. What you see is a nice rendered component at design time, but at runtime, if you have *not* added the components to the displayList, you won’t see anything but the component labels, and they’ll be a size you didn’t anticipate.

This is simply because they don’t initialize until ADDED_TO_STAGE event is fired and the Stage property is set. So, for Papervision3D, this poses some problems right off the bat, since we don’t really want the movie clip and components sitting on stage AND wrapped around our 3D object. This means we have to use MovieMaterial and add the movieclip to the stage. Oh, and by the way, there’s another issue – timing. You *can* create movieclip, put it on stage, then create the MovieMaterial and just go on down the list, but there will be the possibility that the user would see this until the components are initialized AND another render has taken place AND the material is told to update:

One thing you’ll notice in this screenshot is that there is a black are at the bottom and the original movieclip is squished. That’s because the original size of the components is much different than after their initialized. In the screen grab below, I’ve put all of the Flash IDE UIComponents on stage and drawn a green bounding box before they intialize and a white bounding box after they initialize. You can see that their sizes change quite drastically and because my checkbox and radiobutton are at the bottom of the movieclip material, the dimensions of the movieclip are thrown off:

You can see in the pic above that checkbox and radiobutton are much taller than their final render and that textArea really never reflects what you visually see. I’m not sure what’s causing it’s size to remain that way, but it is. And the progressbar, slider and color picker don’t have any visual assets until their initialized as you can see.

Ok so what’s the solution to all these issues?!?! I’ll just step down the list in order, post the code and you can play😉

  • Create your movie clip and then move it off stage where it can’t be seen. In the code below, I move it to -10000. I did some speed tests and it turns out that just simply setting x/y values is faster than using coordinate conversion methods. So, virtualMouse will move your movie to 0,0, then get the objects it needs, then move it back to it’s original location. Since this executes between frame renders, it doesn’t impact the displayList and you never see it happen.
  • Create a method like parseComponents() below. The idea is that you go through all of your components, addEventListeners to when they’re added to stage, THEN when they’re all added, create the MovieMaterial and Plane objects.

[as]

// import UIComponent:
import fl.core.UIComponent;

// later…

var cls:Class = getDefinitionByName(“Mc”) as Class;
movie = new cls() as Sprite;
movie.x = movie.y = -10000;

parseComponents();
addChild(movie);

protected function parseComponents():void
{
uiCounter = 0;
for( var i:int = 0; i<movie.numChildren; i++)
{
var mc:DisplayObject = DisplayObject(movie.getChildAt(i));
if( mc is UIComponent )
{
mc.addEventListener(Event.ADDED_TO_STAGE, handleComponentReady, false, 0, true);
uiCounter++;
}
}
trace(uiCounter);
}

protected function handleComponentReady(e:Event):void
{
var uiComponent:UIComponent = UIComponent(e.currentTarget);
uiComponent.removeEventListener(Event.ADDED_TO_STAGE, handleComponentReady);
uiCounter–;
if( uiCounter == 0 ) createPlane();
}
[/as]

NOTE: if you’re working in FlexBuilder or Eclipse, you’ll need to add the fl classes so that you can add the UIComponent import

  • Now, after we’re sure all components are loaded, create the MovieMaterial and apply it to your 3D object and pass one more NEW argument: Rectangle:

[as]
protected function createPlane():void
{
movieMat = new MovieMaterial( movie, false, true, false, new Rectangle(0,0,256,256) );
movieMat.oneSide = false;
movieMat.setQuality(StageQuality.HIGH, stage, true);
movieMat.interactive = true;
movieMat.smooth = true;

plane = new Plane(movieMat,256,256,6,6);
plane.yaw(-20);
view.scene.addChild(plane);
}[/as]

Now, one extra cool thing to note here is, that the checkbox and radiobutton are STILL to low and actually cause a hairline black area to be drawn on the material. So, to be even MORE dogmatic about making sure our material stays the same dimensions, you can now pass in a rectangle object to the MovieMaterial. The great thing about this is, it’s used that the time the bitmapData object is created from the source movieclip. Soooooooooooo the scrollRect issue I mentioned earlier is no longer an issue, and you don’t have to incure that cost on your movieclip just for the sake of making sure the UIComponents don’t have inappropriate relations with your dimensions🙂

[kml_flashembed movie=”http://www.rockonflash.com/demos/pv3d/ComponentMaterialTest.swf&#8221; width=”550″ height=”400″/]

Get the source

Final code:
[as]
package
{
import fl.core.UIComponent;

import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.utils.getDefinitionByName;

import org.papervision3d.cameras.CameraType;
import org.papervision3d.materials.MovieAssetMaterial;
import org.papervision3d.materials.MovieMaterial;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.stats.StatsView;

public class ComponentMaterialTest extends Sprite
{
public var mc :MovieClip;

public var view :BasicView = new BasicView(0,0, true, true, CameraType.FREE);
public var plane :Plane;
public var mat :MovieAssetMaterial;
public var movieMat :MovieMaterial;
public var movie :Sprite;
public var uiCounter :Number = 0;

public function ComponentMaterialTest()
{
super();
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}

protected function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.quality = StageQuality.MEDIUM;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

var cls:Class = getDefinitionByName(“Mc”) as Class;
movie = new cls() as Sprite;
movie.x = movie.y = -10000;

parseComponents();
addChild(movie);

view.camera.zoom = 1;
view.camera.focus = 1100;

addChild(view);
addChild(new StatsView(view.renderer));
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}

protected function createPlane():void
{
movieMat = new MovieMaterial( movie, false, true, false, new Rectangle(0,0,256,256) );
movieMat.oneSide = false;
movieMat.setQuality(StageQuality.HIGH, stage, true);
movieMat.interactive = true;
movieMat.smooth = true;

plane = new Plane(movieMat,256,256,6,6);
plane.yaw(30);
view.scene.addChild(plane);
}

protected function loop(e:Event):void
{
view.singleRender();
}

protected function handleComponentReady(e:Event):void
{
var uiComponent:UIComponent = UIComponent(e.currentTarget);
uiComponent.removeEventListener(Event.ADDED_TO_STAGE, handleComponentReady);
uiCounter–;
if( uiCounter == 0 ) createPlane();
}

protected function parseComponents():void
{
uiCounter = 0;
for( var i:int = 0; i<movie.numChildren; i++)
{
var mc:DisplayObject = DisplayObject(movie.getChildAt(i));
if( mc is UIComponent )
{
mc.addEventListener(Event.ADDED_TO_STAGE, handleComponentReady, false, 0, true);
uiCounter++;
}
}
trace(uiCounter);
}
}
}
[/as]

  1. interesting as usual john! \m/ rock on!

  2. “That’s because the original size of the components is much different than after their initialized. In the screen grab below, I’ve put all of the Flash IDE UIComponents on stage and drawn a green bounding box before they intialize and a white bounding box after they initialize. You can see that their sizes change quite drastically and because my checkbox and radiobutton are at the bottom of the movieclip material, the dimensions of the movieclip are thrown off:”

    I think most of them will be down to TextField. It has a default height of 100, 100.

  3. Ideally the components would have some kind of ‘CREATION_COMPLETE’ event. Is ‘ADDED_TO_STAGE’ guaranteed to work if you were creating a single component?

    • Jack
    • July 25th, 2008

    Looks great. But can you add containers to a plane in Flex?

  4. @Tink: there isn’t a CREATION_COMPLETE in the AS3 api, that’s Flex only. So yeah, that’ll work. At least, in my tests its worked fine. The point of this excersize was that you want to wait until all of your components have initialized, and that’s just after they’ve had their stage setter set/called.

  5. @Jack: you can add anything you like to a DisplayObject that’s used for the MovieMaterial. The source of a MovieMaterial is base object of DisplayObject and so you can do whatever you want. I haven’t tested flex components to be honest however.

  6. Yeah I’m aware there aint one, which is why I said ideally they would have one.

    If you create a single component, no container for it, and use ADDED_TO_STAGE it still works fine. I’m sure we had issues with a component taking a frame to lay itself out after it has been added to the stage. Could very well be wrong.

    If it don’t work, you could monkey patch UIComponent to dispatch a creation complete event and include it in the PV3D source. Not great, but effective.

    • CrazyFlasher
    • July 25th, 2008

    thats not work .
    can you added papervision class?

    • Jack
    • July 27th, 2008

    @John
    I set a canvas with a couple of components as the MovieMaterial for the plane and it works just fine(with interactivity) but I can see the original canvas on the background.

    • Wayne Anderson
    • July 27th, 2008

    John, thanks for ‘clearing the trees’ of this part of the frontier… I was just getting ready to start playing with components and you have no doubt saved me (and thousands of others) many hours.

  7. @Tink: yeah I’d actually thought about reworking the components to be PV3D friendly and releasing those, but dood, who has the time😉 And I’m not sure that these components are necessesarily the best for 3D work anyway. I would think a lighterweight version of these could be created especially for this sort of thing, wouldn’t you think?

  8. @crazyFlasher: No, but you can sync with the repository at : http://code.google.com/p/papervision3d/source/checkout

    it’d be much better for you to stay in sync with the repo to have the latest version of PV3D. This code is based on the GreatWhite branch btw.

    • ERuiz
    • August 20th, 2008

    Jack can you post an example getting flex components to work on a plane

  9. Aloha,

    With beta 2 and latest GW – Focus should be 1000 instead of 1100.

    • LT
    • September 30th, 2008

    John,

    Would you mind posting up example that we could use with current GW and Flex? Really confused right now on how to implement this.

    Thanks!

  10. Hi John,

    I have been struggling with getting papervision Flex UIComponents to work together for a while. Do you have a solid example for using flex UIComponents from Flex in Papervision projects? This would be super helpful, and your the best man to explain how all this would work!

    Thanks a ton,
    Lance

  11. Hi John,

    I just got done watching your pv3d screencast on WinterWonderland and it’s fantastic! Thanks! I then tried following your guidelines on creating a Flex UIComponent in Papervision and made something that works, but it uses a ton of CPU (way more than it should), not sure why… I thought about creating a generic UIComponentMovieClip class for wrapping Flex UIComponents in MovieClips for Papervision, like your AbstractAnimationObject in WinterWonderland, but I’m not quite there yet.

    Any further ideas on how to make this work well?

    Here’s the source of what I put together:
    http://www.systemsofseven.com/papervision/PV3D_UIComponent/FlexPV3D.html

    Best,
    Lance

  1. November 1st, 2008

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s