Minimum 3D in 2D mode

I’m creating a 2D app that needs to show several individual images lied up in rows and columns . When the user touches on each of these images, I want to do a 3D spinning animation - flipping it in 360 degrees over Y axis. How can I do that without having to convert the entire app to do everything in 3D mode?

Thanks a lot @dave1707 . This is very helpful.

A simple way to do this without taking 3D perspective and the camera into account is to just reduce the sprite x scale to 0, then increase it to 1. I flip a sprite 180 degrees using tween animation in the example below. I also include a value that switches from +1 to -1 on each flip and is multiplied by the x scale value (so the sprite is drawn as flipped when needed), and a Boolean so that taps will be ignored while the object is flipping.

To spin 360 degrees, flip it twice using nested tweets or a tween sequence: reduce the sprite x scale to 0, then increase to 1, then reduce to 0, and finally increase to 1 again.

-- Flip Example

function setup()
    object = {}
    object.sprite = "Cargo Bot:Crate Green 2"
    object.scaleX = 1 -- scale value from 0 to 1
    object.flipX = -1 -- flip value is either +1 or -1 to draw flipped sprite
    object.flipTime = 0.25 -- time for half a flip to occur
    object.flipping = false -- is object in the process of flipping
    
    print("Tap screen to flip object")
end

function draw()
    background(0)

    pushMatrix()
        translate(WIDTH/2,HEIGHT/2)
        scale(2) -- doubles size of object just to see it better
        scale(object.scaleX,1)
        sprite(object.sprite,0,0)
    popMatrix()
end

function touched(touch)
    if touch.state == ENDED then
        if object.flipping == false then -- if object is not flipping
            object.flipping = true -- set object to flipping
            tween(object.flipTime,object,{scaleX = 0},{easing = tween.easing.cubicIn},function() tween(object.flipTime,object,{scaleX = 1 * object.flipX},{easing = tween.easing.cubicOut}, function() object.flipping = false end) object.flipX = object.flipX * -1 end)
        end
    end
end

@mindless Nice use of Tweens. I originally tried something like I show below, but I didn’t like the way the flipping looked. Your use of Tweens makes the flipping look better.

displayMode(FULLSCREEN)

function setup()
    img=readImage("Cargo Bot:Crate Green 2")
    w=img.width
    h=img.height
    size=5
    v=-3
    z=w   
end

function draw()
    background(0)
    z=z+v    
    if z>w or z<-w then
        v=v*-1
    end
    for x=1,3 do
        for y=1,3 do
            if x==2 and y==2 then
                sprite("Cargo Bot:Crate Green 2",x*210,y*210,z*size,h*size)
            else
                sprite("Cargo Bot:Crate Green 2",x*210,y*210,w*size,h*size)
            end
        end
    end
end

Do you have a 3D object of what you want to spin. Is it the same 3D object for each image or a different object for each image.

The only thing that I have for each image is the image object itself. Each image is different from one another. I don’t have any 3D object. My understanding is that an image is internally represented with mesh, which is used for 3D drawing. I was trying to do

rotate(deg, 0, 1, 0)

But it didn’t rotate the images, rather it clips off left and right side of the image. Then when I turn on 3D drawing by

perspective()

Then the rotate command works but now I have to draw everything (all images, not just the spinning one) in 3D coordinates (instead of just plain 2D x and y) which makes everything more complicated. Also, the images are not displayed the same size I intended them to be on screen anymore because there’s camera settings involved.

I think 2d mode is just a specific projection, but it’s still using the 3d under the hood. So potentially you can do it in 2d mode, but rather than using the spriting image stuff you need your image to be a mesh.

If that’s not doable, you can actually do both. So in your main method draw first the 2d stuff, then do your perspective() call, and then draw the ones that need to be 3d. which should work. I did something a long time ago using this approach so it would draw the hud in 2d mode, and some spaceship kind of stuff in 3d. Or you can do it the other way and perspective() and do 3d first, then ortho() and do the 2d stuff…

@tsukit Here’s something I threw together. It’s kind of a mess, but I didn’t want to spend more time on it if it’s not what you’re after. Tap a sprite to select, tap outside the sprites to deselect.

supportedOrientations(LANDSCAPE_ANY)

function setup() 
    xx,yy=0,0
    w,h=42,42       -- sprite sizes are 42x42
    tab={   "Cargo Bot:Crate Blue 1","Cargo Bot:Crate Blue 2","Cargo Bot:Crate Yellow 3",
            "Cargo Bot:Crate Yellow 3","Cargo Bot:Crate Green 2","Cargo Bot:Crate Green 1",
            "Cargo Bot:Crate Red 2","Cargo Bot:Crate Yellow 3","Cargo Bot:Crate Red 1"}
    m=mesh()
    m:addRect(0,0,w*4,h*4)
    ang,ss=0,0
end

function draw()  
    background(0)    
    p=0
    for x=1,3 do
        for y=1,3 do
            p=p+1
            if p~=ss then
                sprite(tab[p],x*w*2+WIDTH/2-w*4,y*h*2+HEIGHT/2-h*4,w*2,h*2)
            end
        end
    end
    
    if ss>0 then
        if xx+yy>0 then
            translate(-168*xx+168*2,168*yy-168*2)
        end
        perspective(90, WIDTH/HEIGHT)
        camera(0,0,-800,0,0,0, 0,1,0)   
        rotate(ang,0,1,0)
        m.texture=tab[ss]
        ang=ang+10
        m:draw()
    end
end

function touched(t)
    if t.state==BEGAN then
        s,ss=0,0
        xx,yy=0,0
        for x=1,3 do
            for y=1,3 do
                s=s+1
                if t.x>x*w*2+WIDTH/2-w*5 and t.x<x*w*2+WIDTH/2-w*3 and
                        t.y>y*h*2+HEIGHT/2-h*4-h and t.y<y*h*2+HEIGHT/2-h*4+h then
                    ss=s 
                    xx=x
                    yy=y
                end
            end
        end
    end
end

Thanks a lot @spacemonkey.

@dave1707 that’s exactly what I need!. Really appreciated. If you could, can you explain a bit more on why we have

perspective(90, WIDTH/HEIGHT)
camera(0,0,-800,0,0,0, 0,1,0)

and

translate(-168xx+1682,168yy-1682)

Thanks so much again.

@tsukit The perspective command just sets the viewing angle for the camera. The camera command (x,y,z,x,y,z,x,y,z) sets where you’re viewing from (x,y,z) and where you’re looking (x,y,z) and which axis is up (x,y,z). See the documentation for more info. The translate command just moves the spinning sprite to the location of the sprite that was touched. A lot of the code was just tweeked to make things fit where they needed. When you modify the code for your use, a lot of the values will probable need changing.

@dave1707 thanks for the info. Sorry that I didn’t make myself clear. In particular, my questions are these:

perspective(90, WIDTH/HEIGHT)

Why do we choose 90 degree viewing, instead of the default 45 degree?

camera(0,0,-800,0,0,0, 0,1,0)

Why are the eye Z is set at -800? Why not -700, -500 or not some other numbers? I tried making this a parameter and I don’t see differences when setting to a different value. Is it -800 a magic number?

translate(-168xx+1682,168yy-1682)

Why do we need a different coordinate when drawing in 3D? Is this because of the camera command above? Also I notice that the higher/lower the Y is, the angle of the flipping image look increasingly distorted (as if you’re looking from under or above it).

Sorry if my questions sound ignorant. I’m not familiar with 3D stuff.

@tsukit A lot of the values I picked was because they worked OK. There wasn’t anything special for them. The -800 was used because I wanted to be far away from the spinning object. The closer you got to the image, the more it looked like you were viewing it from either side or above or below it. For translate, I needed to shift the spinning image far enough to fill the position of the tapped image. 3D and 2D doesn’t work together easily, at least with what I was trying to do. As I said above, if you try to use the code, you’ll have to tweak the values to get it to work with whatever you want to do.

Thanks, @dave1707. Tweens can be a little strange in that sometimes they don’t work as expected, so I’ve learned to nest them and/or use function calls to get them to do what I want. It can get a little ridiculous and hard to read, but it works. I figure why write code to animate when it’s already there?