Turning a card emulating 3D?

Hello everyone

In my game there are cards (also with holes) rotating simulating 3D.

To do this use a transaction as:

local card = display.newImageRect( "card.png", 200, 200 ) card.x = display.contentCenterX card.y = display.contentCenterY transition.to(card.path, {time = 1000, x1 = 200, x2 = 200, x3 = -200, x4 = -200 })

or to turn them use the Touch User.

I wanted to know if there a way to emulate this effect better.

I enclose two photos:

  1. what I get

  2. what I would get (red borders)

The most critical phases as you can notice are 4 and 5 because the card completely disappears.

Thanks in advance for support

Hi.

I threw together something like this a few months back for somebody, but can’t find the snippet, so I’ve tried to reproduce it here:

Turning effect

You can change x to see it as time progresses, or just replace CoronaVertexUserData.x with CoronaTotalTime to see something that changes with time.

If that fits your needs, you can follow this and this to use it in your own code.

I haven’t seen divide-by-0s cause issues, but if you find those, namely in your #4 and #5 cases, that can be addressed by clamping or adding a small value, though it’s a bit messy with the numbers being able to change sign.

Hello thanks for response :slight_smile:

I’m trying again and again but I can not run the kernel. Is the first time, I try to use it and I’m definitely doing something wrong … :huh:

I also downloaded other projects, for example, but always gives me problems when I step in the fill.

Would not you have something ready so I can see where I am wrong and if all goes well.

I also tried it like you said to replace CoronaVertexUserData.x  with  CoronaTotalTime.

I also decreased the time, but it seems like mine transaction lua …

Thanks again

Just threw it into something of mine. Give this a go, replacing  image3 by your own image:

local kernel = { category = "filter", name = "rotate" } kernel.vertexData = { { index = 0, name = "t", default = 0 } } kernel.fragment = [[P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { texCoord.x = .5 + (texCoord.x - .5) / cos(CoronaVertexUserData.x \* 2. \* 3.14159); // As mentioned, you can also use CoronaTotalTime here ^^^^^^^ return CoronaColorScale(texture2D(CoronaSampler0, texCoord)); }]] graphics.defineEffect(kernel) image3.fill.effect = "filter.custom.rotate" transition.to(image3.fill.effect, { t = 15, time = 7500 })

thank you!

I tried the code and it works. :slight_smile:

But not still solve my problama in the points 4 and 5.

Also the holes during rotation have no “depth” … :frowning:

A trivial solution that I thought for the holes was to put a picture alike in slightly more to the left and maybe just more dark color.

While for steps 4 and 5 I do not know. I wish I was more 3D and emulates a thickness to the paper.

I hope I do not ask too much. And the only part that I can not fix

Ah, I completely misread what you wrote, then.  :stuck_out_tongue:

I puttered around a bit more and now have this: updated This tries to look for some nearby opaque neighbors (when the pixel itself is somewhat transparent) and if it finds them, use red, with some more stuff to try to handle the 4 / 5 case. These tricks are pretty image-dependent, as you can sort of see by spinning the plane around, but in your case you seem to have a pretty simple shape.

wow!

It works and it’s what I needed. I apologize for my English that led you astray. But I think there is a problem but I can not figure out what kind.

During rotation in the simulator, I see a background between the paper and the edge … what can it be? I am attaching a photo(name:first).

Lastly the problem of the previous 4 and 5 seems solved, but you can cover the empty space (name:second)? or maybe now I ask too much?

thank you so much for your time, I therein am truly grateful :smiley:

for your “xScale=0” problem on the middle frame, I handled it in a decidedly low-tech way.  given a known fixed number of animation frames, i just predefined the values i wanted. for example, say you need 10 frames, then:

local xScales = { 0.9, 0.8, 0.6, 0.4, 0.1, -0.1, -0.4, -0.6, -0.8, -0.9 } card.xScale = xScales[card.frame]

those values are just approximations here as examples but note that zero is avoided.  (actual values should be the cosine of a fixed angular step, say from 0-180 degrees by 20’s, to avoid 90) do this in an enterFrame -type loop so that you can make it “discrete”, not via a transition which is too “continuous” (and will often have that troublesome ‘exactly half-way’ t=0.5 frame)

thank you davebollinger

This way I could effectively avoid the problem but I can not give the 3D effect. Missing the edge …

I am continuing to make several attempts of code. But I can not fix.

I also wanted to he could move the whole user with the touch but with this new code I find difficulty …

This is the only part of the game that requires a 3D emulation for the rest I have no problems.

So I’m looking for the best solution

does your flip animation really last a full second?  cards are really thin (relative to their width/height) and if you did it at a quicker speed then perhaps no-one would notice if your very fancy effect were absent? but if they’re thicker, like tiles rather than cards, then it might be easier to pre-render – the trick then would be to exactly match your “stencil” image of the card face on top as it’s flipping relative to the 3D underneath.  (as long as you knew the exact rotation and perspective projection used to render it, then ought to be possible – tricky, but possible)

In efects they could be considered tiles. The photo you’ve attached is what I want. Only with two additions: 1. It can be rotated with the touch of the user. 2. Give the same effect to the holes in the tiles. “that’s all”

@maximo97

No worries about your English. I was just careless looking over it the first time.

I think the issue is that the edge pixels get clamped (notice that the error doesn’t happen when the corner starts to get round) and so you’re never finding transparencies. Could you try it with this? I tried to break it up and explain some of the steps, too, so it isn’t quite so magical.  :stuck_out_tongue: (I usually refer to this doc, myself.)

Alternatively, just pad the sides of the image with transparent pixels.

Very good

Sorry I’m late, but I was doing various tests on the effects. The comments were very helpful.

I rewrote a piece of code out of context so I can show you. Then I think he goes. Only thing I do not understand is in touch method that give value “t” because executes a perfect turning.  Also how I decide which way I want to go the edge (photo)?

I think I will have solved this all perfect. I thank you again

code:

 --snap to use as a card or tile (Slightly larger to have the desidered border) local card = display.newSnapshot( 230, 230 ) card.x = display.contentCenterX card.y = display.contentCenterY --snap group local cardG = card.group --background (used as a card board) local bg = display.newRect( cardG, 0, 0, 200, 200 ) bg:setFillColor( 1, 1, 1, 0 ) bg.strokeWidth = 20 bg:setStrokeColor(.7, .7, .7, 1) --hearts suit local hearts = display.newImageRect( cardG, "hearts.png", 60, 60 ) hearts.x = bg.x - (bg.width\*0.5) + (hearts.width\*0.5) + bg.strokeWidth --diamonds suit local diamonds = display.newImageRect( cardG, "diamonds.png", 60, 60 ) diamonds.x = bg.x + (bg.width\*0.5) - (diamonds.width\*0.5) - bg.strokeWidth --clubs suit local clubs = display.newImageRect( cardG, "clubs.png", 60, 60 ) clubs.y = bg.y - (bg.height\*0.5) + (clubs.height\*0.5) + bg.strokeWidth --spades suit local spades = display.newImageRect( cardG, "spades.png", 60, 60 ) spades.y = bg.y + (bg.height\*0.5) - (spades.height\*0.5) - bg.strokeWidth --centre of card local centre = display.newRect( cardG, 0, 0, 60, 60 ) --button that serves the user to turn the card local rotateButton = display.newRect( 0, 0, 30, 50 ) rotateButton:setFillColor( 0, 1, 0 ) rotateButton.x = rotateButton.width rotateButton.y = display.contentCenterY --rotateButton touch event function rotateButton:touch( event ) if ( event.phase == "began" ) then display.getCurrentStage():setFocus(self, event.id) self.isFocus = true elseif(self.isFocus) then if event.phase == "moved" then --distance path with his finger by the user local distanceTraveled = (event.x-event.xStart) --distance in proportion to the width of the paper local distanceInProportion = (distanceTraveled/card.width)\*2 print(distanceInProportion) if(distanceInProportion \> 0) and (distanceInProportion \< 2) then --tilting the paper by changing the scale (old method) --card.xScale = 1-distanceInProportion --tilting the paper with the new effect --I multiplied by seven only to see the operation but do not understand how to calculate the right number card.fill.effect.t = distanceInProportion\*7 end elseif event.phase == "ended" or event.phase == "cancelled" then display.getCurrentStage():setFocus( self, nil ) self.isFocus = false end end return true end rotateButton:addEventListener( "touch" ) local kernel = { category = "filter", name = "rotate" } kernel.vertexData = { { index = 0, name = "t", default = 0 } } kernel.fragment = [[const P\_UV float PI = 3.14159; const P\_UV float WIDTH = 13.; #define NPIXELS CoronaTexelSize.z \* WIDTH P\_UV float LessThan (P\_UV float threshold, P\_UV float x) { return step(abs(x), threshold); // 1.0 if true, 0.0 otherwise } P\_UV float MoreThan (P\_UV float threshold, P\_UV float x) { return step(-abs(x), threshold); // 1.0 if true, 0.0 otherwise (the negative just accounts for exactly equal values) } P\_UV float ProjectX (P\_UV float x, P\_UV float denom) { return .5 + (x - .5) / denom; } P\_UV float InBounds (P\_UV vec2 uv) { P\_UV vec2 from\_center = step(vec2(-.5), abs(uv - .5)); // 1.0 if inside [0, 1] along axis, 0.0 otherwise (done for x and y) return min(from\_center.x, from\_center.y); // If either is 0.0, we're out-of-bounds; otherwise we're inside } P\_COLOR vec4 FragmentKernel( P\_UV vec2 texCoord ) { P\_UV float angle = CoronaVertexUserData.x \* .09 \* PI; P\_UV float cosa = cos(angle), sina = sin(angle), xcur = texCoord.x; P\_UV float denom = sign(cosa) \* max(abs(cosa), 1e-4); // avoid divides by 0 to avoid driver issues, etc. P\_UV float off\_edge = InBounds(texCoord); texCoord.x = ProjectX(texCoord.x, denom); P\_COLOR vec4 pixel = texture2D(CoronaSampler0, texCoord) \* off\_edge; P\_UV float neighbor\_x = ProjectX(xcur - NPIXELS \* sina, denom); // look a few pixels to the left or right (depending on angle and distance from sideways) P\_UV float at\_center\_and\_sideways = MoreThan(.9975, sina) \* LessThan(NPIXELS / 2., xcur - .5); // the multiply is basically an "and" // if both predicates return 1.0, it's 1.0 // otherwise it's 0.0 texCoord.x = mix(neighbor\_x, .5, at\_center\_and\_sideways); // "interpolate" (using our 0.0 or 1.0 result) to pick a value P\_COLOR vec4 neighbor = texture2D(CoronaSampler0, texCoord) \* InBounds(texCoord); return CoronaColorScale(mix(mix(vec4(0.), vec4(1., 0., 0., 1.), neighbor.a), pixel, pixel.a)); } ]] graphics.defineEffect(kernel) --Apply the new effect card.fill.effect = "filter.custom.rotate"

Hi.

2. * PI , in the shader, is one full turn. With the constant now being .09 you’d have to scale up a bit, or change that back. Also, t  corresponds to CoronaVertexUserData.x , if that wasn’t clear. (The “index = 0” in the kernel.vertexData means “put this name into x”.) You can give it a better name if you want.

Basically, t = 1 or t = -1 should mean a full turn, .5 or -.5 a halfway one. So I figured you’d have something like:

if card.fill.effect.t \< .5 then -- original transition.to(card.fill.effect, { t = .5, time = 1000 }) else -- flipped transition.to(card.fill.effect, { t = 0, time = 1000 }) end

If it’s easier to think about, you can use degrees here (so replace the .5 values with 180). Then just change

P\_UV float angle = CoronaVertexUserData.x \* .09 \* PI;

to

P\_UV float angle = rad(CoronaVertexUserData.x);

well, now it is more clear !!

But I have to keep doing tests because I find a problem for every solution looks a bit what happens …  :huh:

But I will not bother you too much hope to take care of myself

thanks again for the help :slight_smile:

Oh, heh. The black holes must be because it’s looking too far into the texture (i.e.,  -NPIXELS * sina ). You could try making WIDTH smaller, but of course it shrinks your red bar too. Or just use a different scale factor than NPIXELS.

Not sure about the other case. I’ll see if I can sit down with your example later, maybe tomorrow. (I’d like to use this effect later too, so it’s not really a bother.)

Great!

Well then they are waiting anxiously. Meanwhile I will do tests alone

Hello StarCrunch

You have some news about my code?

I have done so many tests also during the holidays but still nothing perfect …

thank you

Hi. I ended up needing to hurry and finish a few other things, and since then have been doing various travel and visiting. So I haven’t written any code this past week (at all), nor been online much. I was abruptly reminded of this thread by the blog, though! :P I’ll try to take a look again soon.

Sorry I’m late I was sure that I have answered the same day I saw the notification … :huh:

Thanks for the support I look forward to seeing a solution :slight_smile: