The forums have permanently moved to forum.kirupa.com. This forum will be kept around in read-only mode for archival purposes. To learn how to continue using your existing account on the new forums, check out this thread.


Page 1 of 2 12 LastLast
Results 1 to 15 of 29

Thread: Two Sided 3D Clip

  1. #1

    Two Sided 3D Clip

    For some reason I expected that with the new z property, rotationX, rotationY, rotationZ, Matrix3D etc... that they would have supported some sort of two-sided movieClip... something like:
    MovieClip.front = 1;
    MovieClip.back = 2;
    // where front and back are the frames from the movieClip to use
    Anyway... this code will give you a two sided MovieClip where frame 1 of your clip is the front and frame 2 is the back:
    VIEW THE SWF
    DOWNLOAD FLA

    Code:
      
    addEventListener(Event.ENTER_FRAME, onLoop);
    
    function onLoop(evt:Event):void {
    
        box.rotationX =   200 - mouseY;
        box.rotationY  =  mouseX;
        makeTwoSided(box);
    }
    
    // assumes that frame 1 of the movieClip is the front and 2 the is back
    function makeTwoSided(mc:MovieClip):void {
            
            box.gotoAndStop(1);
            
            var rx:Number=Math.abs(mc.rotationX%360);
            var ry:Number=Math.abs(mc.rotationY%360);
            
            var turnX:Boolean = (rx > 90 && rx < 270);
            var turnY:Boolean = (ry > 90 && ry < 270);
    
           if (turnX != turnY) {
                box.gotoAndStop(2);
                //  make sure the "b" isn't backwards most of the time
                box.scaleX = -1;
            }
    }
    Last edited by Chimay; December 17th, 2008 at 11:22 AM.

  2. #2
    That's not quite going to cut it.

    Making a two-sided object with the new 3D properties is not exactly easy. In fact I haven't found a way to do it using the new 3D APIs - if it's even possible. The problem mostly relies in the perspective projection and how there is no way to tell what side of an object in 3D space is going to visible until the projection is applied to bring it into the 2D realm.

    One thing you can do is to use 3 globaled points inside the object and check the winding of those points for the side. That make-2-sided function would look something like this:
    Code:
    function makeTwoSided(mc:MovieClip):void {
    	var p1:Point = mc.localToGlobal(new Point(0,0));
    	var p2:Point = mc.localToGlobal(new Point(100,0));
    	var p3:Point = mc.localToGlobal(new Point(0,100));
    	mc.gotoAndStop(((p2.x-p1.x)*(p3.y-p1.y) - (p2.y-p1.y)*(p3.x-p1.x)) < 0 ? 1 : 2);
    }

  3. #3
    biznuge's Avatar
    1,136
    posts
    Use the Fork Luke...
    what would happen in this case if the clip in question, or indeed the camera, were off centre? Not sure this would work for anything but the most simple of tests...
    before you judge someone, you should walk a mile in their shoes. That way, when you judge them, you're a mile away, and you have their shoes...
    "A lack of planning on your part does not constitute an emergency on mine" - Danonthemoon
    She asked for a double entendre, so I gave her one...
    "screw ie. it can lick my balls" - A.J. Cates

  4. #4
    Quote Originally Posted by biznuge View Post
    what would happen in this case if the clip in question, or indeed the camera, were off centre? Not sure this would work for anything but the most simple of tests...
    That's exactly why the original version doesn't hold up. The solution I provided accounts for this. Ex:
    http://www.senocular.com/pub/kirupa/2sided3DMC.html

    You can see with the original version, and the mc on the middle right, the frame (square, circle) changes before it fully rotates at that perspective. The bottom couple change individually when expected.

  5. #5

    Thanks for the info senocular - not sure I fully grasp the winding method without playing with it for a few mins, but looks very cool.

    Too bad its such a tricky thing to implement using movieClip properties, it seems to be one of the most common uses for this type of 3D.


    cheers...
    Last edited by Chimay; December 17th, 2008 at 07:09 PM.

  6. #6
    I'm about to leave work in a little bit, but I'll try to provide a useful description of why that works when I get home

  7. #7
    (Sorry I didn't get this up last night; took longer than I expected )

    Winding and Sides

    A way to determine what side of a 3D object is currently being viewed is through path winding. Winding represents the direction in which a closed polygon shape is drawn. For winding to be determined, you need at minimum 3 points that, when drawn create a triangle. The winding for such a shape (L, M, N) is the direction in which that shape is drawn as you progress from point L to point M to point N (and effectively back to point L again).

    A positively wound shape is a shape that is drawn in a clockwise direction. A negatively wound shape is a shape that is drawn in a counterclockwise direction.



    If we place a positively wound triangular shape on a 3D plane and rotate that plane in any way that shows it's reverse side, the winding of that shape from the perspective of the viewer, or as that shape is viewed in 2D space, will be reversed and become negative. This is natural since anything flipped over is the reverse of what it was originally.



    The key here, is that this flipping occurs from the perspective of the viewer - as the 3D shape is being viewed in 2D space.

    Now, if there was only a way to determine the winding direction of such a path in a 3D plane (as seen in 2D), you would be able to determine what side of that plane is currently visible. Luckily for us, there is a way to do exactly that. In fact, there are a number of ways (one of which, not the best, I describe in the 3D tutorial on kirupa.com). The standard way is to use the cross-product of vectors that make up 2 of a triangular path's sides and check the resulting z value. This is the very same technique used internally in Flash Player for the culling property in the new enhanced Drawing API in Flash Player 10 (Graphics.drawTriangles and GraphicsTrianglePath).


    Determining Winding with Cross-product

    A cross-product is an operation between two 3D vectors to produce another vector. In this context, vector represents a directional line projected into space - not to be confused with the Vector data type which is a typed Array. The vector resulting from a cross-product is perpendicular to the two original vectors.



    The length, or magnitude of the cross-product is proportional to the magnitudes of the original vectors and the angle between them. As the angle gets smaller, so does the magnitude of the cross-product. If/when the vectors cross, the magnitude will be negative.



    The angle here is especially important as we apply this towards determining winding. Consider that winding triangle path in a 3D plane. That path is representative of 2 vectors in that plane. At any point where the plane flips to reveal its back side, those vectors, as they are perceived in 2D space, cross creating a negative magnitude.

    What this means is that winding direction is inline with the magnitude of the cross-product from the vectors that define the path; when the cross-product magnitude is negative, so is the winding.

    With that in mind, we can use three arbitrary points within a plane that define a triangular path, define vectors based on those points as they exist in 2D space from the perspective of the user's view, get their cross-product and check its magnitude to determine winding and in turn what side of the plane we can see.

    Specifically, in terms of Flash, this means specifying points in a movie clip that will represent a winding path/two vectors, getting their global 2D positions with localToGlobal, and then performing the cross-product math to determine a resulting magnitude. For the cross-product, the basic applied formula for a cross-product C from vectors A and B (AB) is:

    Code:
    Cx = AyBz - AzBy
    Cy = AzBx - AxBz
    Cz = AxBy - AyBx
    Remember that a cross-product is between 3D vectors. The vectors we are obtaining from the movie clip are actually 2D based on global locations of the screen. This gives them x and y values, but not a z. For the cross-product then, z for these vectors is 0.

    The fact that we're working from source vectors with a 0 z value is important for two reasons. For one, it greatly reduces our operations since in the formulas listed above, we are left with:

    Code:
    Cx = Ay*0 - 0*By = 0
    Cy = 0*Bx - Ax*0 = 0
    Cz = AxBy - AyBx
    Which leaves only z to be calculated. As a result, and this is the second reason, magnitude is equal to the value of z. No extra calculations are required to obtain the magnitude since it lies directly on the axis. This gives us the following formula:

    Code:
    magnitude = Cz = AxBy - AyBx
    And since winding is negative when magnitude is, we can determine our winding, and hence our side, by checking magnitude > 0.

    So where do vectors A and B come from? We start with points in the 2D coordinate space of the movie clip being transformed in 3D. Those are brought out to global coordinates using localToGlobal which accounts for all transformations 3D and otherwise so we get those points as the user would see them on the screen. Its from these points that the vectors are derived. Vectors, however, are projections from the origin of a 3D coordinate space. For this to work with the points we've obtained from the movie clip, one of the points needs to represent the origin, and the other two the vectors.



    So given points L, M, N, assuming L to be our origin, we get:

    Code:
    Ax = Mx - Lx
    Ay = My - Ly
    Bx = Nx - Lx
    By = Ny - Ly
    And of course for z, we have 0. But z isn't important in our calculations so it is being omitted. Plugging this into our magnitude formula we get

    Code:
    magnitude = (Mx - Lx)*(Ny - Ly) - (My - Ly)*(Nx - Lx)
    And that is exactly what was used to determine the frame required to show in the updated makeTwoSided function, just with a little ternary trickery to do it all inline

    Code:
    var p1:Point = mc.localToGlobal(new Point(0,0));
    var p2:Point = mc.localToGlobal(new Point(100,0));
    var p3:Point = mc.localToGlobal(new Point(0,100));
    var frontFacing:Boolean = ((p2.x-p1.x)*(p3.y-p1.y) - (p2.y-p1.y)*(p3.x-p1.x) > 0);
    By checking whether or not that calculation returns a positive or negative number will let you know whether or not your movie clip is showing its front face or its back face. From that you can change the movie clip to show whatever you want, such as by changing its frame, or even behave in a different manner if you'd like.

  8. #8
    While I was writing the below you posted the description

    Played with it a bit this morning and read up on winding. I think I've got my head wrapped around it. But a description would be great. This is what I didn't grasp at first....

    Code:
    (p2.x-p1.x)*(p3.y-p1.y) - (p2.y-p1.y)*(p3.x-p1.x)
    While googling around I learned that if the result of that calculation is greater than 0 the winding is counter clockwise, if it's less than zero its clockwise....

    I also learned that this could be used to determine if point p3 is to the left of or to the right of an infinite line that passes through p1 and p2.... which actually helped to clarify my understanding of the winding...

  9. #9
    I created a quick demo that uses senocular's winding technique along with z-sorting:

    View Demo

    Download Source

    I learned the z-sorting technique here.

  10. #10
    I'm currently getting an error:
    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at nav3d_fla::MainTimeline/zSort()
    at nav3d_fla::MainTimeline/onRunNav()

  11. #11
    That's odd. Not getting that error, maybe the source I zipped up was unsaved.... re-uploaded:

    download source

    Thanks again for the in depth post about winding... it's easier to understand than the stuff I found via google.

  12. #12
    still getting the error. Are you testing with the debugger player? Thats how I can tell the errors are occuring.

  13. #13
    I wasn't using the debug player...

    (now that I am) There was a small bug... I was using this:

    Code:
    pInfo[i].plane.transform.getRelativeMatrix3D(this).position.z;
    before altering the z property of any of the planes. I just set the planes z to -1 by default and that solved this issue. I've updated the source files.


    View Demo

    Download Source

  14. #14
    much better

    And well done

  15. #15
    Nice Chimay... although I have to read seno's description again :eek:


    it was a bit over my head

    But I shold understand it after reading it a few.... lot more times.

    Twitter / The Human Conditions

    biznuge: "that doesn't grammatical sense..."

Page 1 of 2 12 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

Home About kirupa.com Meet the Moderators Advertise

 Link to Us

 Credits

Copyright 1999 - 2012