Papervision3D: Accessing MovieClips used in materials

I’ve often been asked this question and it’s come up quite a few times on the PV3D list, so I thought I’d post about it just for archive sake if anything else.

Right now, in PV3D, you have the ability to use MovieClips/Sprites for Materials on 3D objects. The 2 materials you would use for this are MovieMaterial and MovieAssetMaterial (Asset means that it’s in the Flash IDE library rather than an instance on stage). When you create these materials, they have reference to a MovieClip/Sprite container.

Often times, you need to communicate with these MovieClips via ActionScript, and I’m going to show you how you can do this with a basic sample that uses a sub class that extends MovieClip, and then later we try to access that MovieClip with some strong typing.

First, let’s create a sub class that extends MovieClip and you would use that to assign to a MovieClip in the Flash IDE library:
[as]
package com.rockonflash.pv3d.materials
{
import flash.display.MovieClip;

public class SpecialSauce extends MovieClip
{
public function SpecialSauce()
{
// constructor
}

public function animate():void
{
gotoAndPlay(“animate”);
}
}
}[/as]

Next, we need to create a MovieAssetMaterial, and then create a reference to it so we can talk to it via ActionScript. First we’re going to call setupScene() and create a plane object that has scope in this class. When we call doAnimation(), we cast the material of the plane object first to make sure it’s a MovieAssetMaterial. This helps avoid any RTE’s obviously, but its necessary since Plane’s material property is set to MaterialObject3D. So, if you tried to access the “movie” property of plane.material, you’d get an compile error since plane.material is thought to be a MaterialObject3D.

In the next part of doAnimation(), we cast the movie property as SpecialSauce. This is for the compiler as well since we want to call a custom method called “animate” that doesn’t exist on MovieClip alone. If the cast is successful, mov.animate() will fire and bingo, you’re in business!
[as]
import com.rockonflash.pv3d.materials.SpecialSauce;

protected var plane:Plane;

public function setupScene():void
{
var mat:MovieAssetMaterial = new MovieAssetMaterial(“com.rockonflash.pv3d.materials.SpecialSauce”, true, true, true);
plane = new Plane(mat, 256, 256, 3, 3);
}

public function doAnimation():void
{
// cast as MovieAssetMaterial, then test for null
var mat:MovieAssetMaterial = plane.material as MovieAssetMaterial;
if( mat )
{
// cast as our sub class, then test for null
var mov:SpecialSauce = mat.movie as SpecialSauce;
if( mov )
{
// now you’re talking to the instance directly
mov.animate();
}
}
}[/as]

Now one last thing to note is the 4th argument on MovieAssetMaterial: unique:Boolean. If set to true, you will get a unique instance of the MovieAssetMaterial. You might think that when you create a MovieAssetMaterial, you’d be creating a unique instance, but in reality, you’re not. By default, Papervision3D re-uses instances of MovieAssetMaterials if they’ve already been created – UNLESS you tell it to create a unique instance. We do that to save on memory and in some cases speed.

hope this helps!

UPDATE - interactivity
So, now to take it a bit further, if you want to get Mouse interactivity going with your MovieClip (SpecialSauce), all you have to do is:

1. set your viewport object’s “interactive” property to true
2. set your material’s “interactive property to true
3. addEventListener to your objects inside of your class or to the class itself:

[as]protected var viewport::Viewport3D;
protected var plane:Plane;
public function setupScene():void
{
// forth argument is ‘interactive’
viewport = new Viewport3D(1200, 600, false, true, true, true);
var mat:MovieAssetMaterial = new MovieAssetMaterial(SpecialSauce, true, true, true);
mat.interactive = true;
mat.movie.addEventListener(MouseEvent.CLICK, handleClick);
plane = new Plane(mat, 256, 256, 3, 3);
}

public function handleClick(e:MouseEvent):void
{
trace(“clicked”);
}
[/as]

At this point, you should see ‘clicked’ in the trace output.

There you go!

    • Sam C
    • February 5th, 2008

    Nice stuff thanks ,

    John is there anyway to draw directly to materials in 2.0 helloMouse3D example was great but couldn’t work out how to port it to 2.0 so much has changed🙂 I guess this might not be ready yet.

    • Sam C
    • February 8th, 2008

    Don’t worry figured it out.

    For anyone else these is how you get the 2.0 current 3d object coordinates
    my3dobject.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, onMouseMove);
    function onMouseMove(event:InteractiveScene3DEvent):void {
    trace(“Coords ” + event.x +” / “+ event.y);
    }

    doh!

    • brian
    • February 13th, 2008

    I’m afraid I’m lost on how this would be coded. Is the second step everything that would be needed in the code?

    I’m getting the errors :The protected attribute can only be used on class property definitions
    and
    1114: The public attribute can only be used inside a package.

  1. @Brian: No, you need to put this code in your own class, that’s why you’re getting those error messages – hth

    • Brian
    • February 14th, 2008

    Ok, I got confused because I didn’t see the package declaration in the second two sections of code. What did you name the class in this case? Thanks for the help, I’m still learning classes so I appreciate your help.

  2. package
    {
    public class Main extends MovieClip
    {
    public function Main()
    {
    // constructor
    }
    }
    }

    this is what a basic class looks like that you can use with a Flash IDE FLA as the main document or with a Flex actionscript project
    hth,

    John

  3. Very nice.

    You can skip the plane if you are working with a cube. My code looks like this:

    private function clickHandler(e:MouseEvent):void
    {
    var mov:SpecialSauce = vm.movie as SpecialSauce;
    if( mov )
    {
    mov.animate();
    }
    }

    …here, vm is my MovieAssetMaterial that I defined at the constructor level.
    I wasn’t using a plane so all I had to do was call this; no need to convert it from a normal Material. I also had a movieclip already in my library which I attached the SpecialSauce class to. Made it real easy!

    Thanks a lot!

    -A

    • Wes
    • March 15th, 2008

    Hi John,

    Can this be used with MovieMaterial? I’m loading an external SWF file as a MovieMaterial, and accessing it through material.movie (which when traced does return [object MovieClip], but it’s just not hearing the gotoAndStop() command that I’m sending it.

    I’m strict typing all the way:

    var sidePlane:Plane = item.getChildByName(nextSide) as Plane;
    trace(“sidePlane: ” + sidePlane);
    var sideMat:MovieMaterial = sidePlane.material as MovieMaterial;
    if (sideMat) {
    trace(“sideMat: ” + sideMat);
    var matMovie:MovieClip = sideMat.movie as MovieClip;
    if (matMovie) {
    trace(“matMovie: ” + matMovie); // returns matMovie: [object MovieClip]
    direction == “forward” ? matMovie.nextFrame() : matMovie.prevFrame();
    }
    }

    I’ve tried matMovie.nextFrame(), matMovie.play(), .gotoandStop, etc.. no results.

    I pulled this in from the Library as a MovieAssetMaterial and gave it a class of its own “SpecialSauce” and everything worked, but I need to be loading an external SWF, not embedding one in the Library.

    Thanks!
    -Wes

    • Daniel
    • March 30th, 2008

    Wes, did you make your material animated?
    sideMat.animated = true;

    Daniel

  4. Hi John,

    Sorry, I am the new starter on PV3D, but i really meet the same problem as your topic. I need to call movie material to play.
    But I totally have no idea about where can i call the “package com.rockonflash.pv3d.materials” and “import com.rockonflash.pv3d.materials.SpecialSauce;”.
    > What is the viewport come from?
    > I can set “true, true, true” in MovieAssetMaterial agruement.
    > There’s no interactive parameter can set too.

    Hope to see your reply soon!
    Million thanks!
    Swan

  5. hey ive been using this technique with good success however at one point in my code i swap out one movieclip material for another and try as i might i can’t get this method to work on the new movieclip material? Im trying to animate a rollover on a child movie clip within the one that is used as the new material.

    I think i have way too much code to post here… but i could send my files if anyone can help take a look?

    ta

    • 😉 no, I’m busy too Marty😉 but post it to the Papervision email list and I’m sure someone out there has time to help you out

      Good luck

  6. found out there is a wierd bug that if your movieclip has a filter on it then the mouseEvent will just be ignored! fixed now but onto other problems…

    i am tweening the x position of a movieclip inside my movieAssetMaterial and it works fine but it also updates the alignment of the material – is there anyway for the cube face itself to act like a mask and just show a certain area of the material?

    thanks

  1. No trackbacks yet.

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