Flipcard (based on Topics/Textures/TextureQuad example) Front and Back

Hi there,

Hope this msg finds you well. Greetings from the Dominican Rep. :slight_smile:
I am trying to do a FLIPCARD, meaning, a image on one side and another image on the other side. So that when you rotate it, you see one image on on side and another on the other side.
In the attached code. I have two(2) PImages, and drew two (2) PShape Quads. Each PShape Quad has as a texture it’s own PImage.

I was under the impression, that in P3D if I had a two (2) Quads and if I rotated on of them to -180 degrees, had each with their own push(), translate(), pop(), then displayed them to the drawing context with Shape; when I rotated on the Y axis them, I would see on one side one textured quad, and on the other side the other textured quad.

What I do see in front is the one that I drew with shape last, no matter what angle rotateY has. I feel there is something fundamental that I am not understanding. Any help would be appreciated :slight_smile:

PS: I already did another working example doing the two (2) textured quads each on its own PGraphics layer, and it worked. I post here because I seem to be under a grave misunderstanding and want to learn.
I do not understand how In the main draw context, (no PGraphics layers) how if I have two PShape quads individually drawn and individually textured with a a particular image. Why when I rotate them both simultaneously on the Y axis, I do not see one in the front and on in the back back? I tried changing their Z value on translate, one with -50, and the other 50 to see if it would make a difference, but it did not.

I uploaded the images online so that this posting could work, but I have them in the data folder. I added a small circle to the right of each (one yellow and one blue) to have a reference when I rotated the one that is supposed to be on the backside of the flipcard. Thank you for looking.

Find my example code below:

PImage imgIN;
PImage imgOUT;

PShape quadIN;
PShape quadOUT;

void setup() {
  size(500, 500, P3D);
  //both PImages dims are: Width: 212 x Height: 300
  imgIN = loadImage("https://i.ibb.co/8KwnfvB/Pa-IN.png");
  imgIN.resize(0, 300);
  
  imgOUT = loadImage("https://i.ibb.co/QmjM7fG/Pa-OUT.png");
  imgOUT.resize(0, 300);

  noStroke();
  //START PShape quadIN
  quadIN = createShape();
  quadIN.beginShape();
  quadIN.noFill();
  quadIN.texture(imgIN);
  quadIN.vertex(-106, -150, 0, 0, 0);
  quadIN.vertex(106, -150, 0, imgIN.width, 0);
  quadIN.vertex(106, 150, 0, imgIN.width, imgIN.height);
  quadIN.vertex(-106, 150, 0, 0, imgIN.height);
  quadIN.endShape();
  //END PShape quadIN

  //START PShape quadOUT
  quadOUT = createShape();
  quadOUT.beginShape();
  quadOUT.texture(imgOUT);
  quadOUT.vertex(-106, -150, 0, 0, 0);
  quadOUT.vertex(106, -150, 0, imgOUT.width, 0);
  quadOUT.vertex(106, 150, 0, imgOUT.width, imgOUT.height);
  quadOUT.vertex(-106, 150, 0, 0, imgOUT.height);
  quadOUT.endShape();
  //END PShape quadOUT
}
void draw() {
  background(0);

  //START shape quadIN
  pushMatrix();
  translate(width / 2, height / 2, -5);
  float startAngle = -180;
  float angle = frameCount*-0.5;
  rotateY(radians(startAngle+angle));
  fill(#0768ED);
  circle(150, 100, 50);
  shape(quadIN, 0, 0);
  popMatrix();
  //END shape quadIN

  //START shape quadOUT
  pushMatrix();
  translate(width / 2, height / 2, 5);
  rotateY(radians(angle));
  fill(#FAF30A);
  circle(150, 100, 50);
  shape(quadOUT, 0, 50);
  popMatrix();
  //END shape quadOUT
}
1 Like

Hi,

I got it working but I am not sure I understand why… :frowning:
I had to add a 2nd translation to each PShape quad before displaying them with shape(). What I don’t understand is that that 2nd translation in both cases is:
translate(0, 0, -2); for the one in the back
translate(0, 0, -2); for the one in the front

When I removed the second translations and added that Z value in the last argument of the 1st translation, it does not work. What is the need for the second translation?
This is super odd, I solved it, but I do not understand why, so I am back at square one.

Here is my new reduced / working void draw():

Thank you for looking.

void draw() {
  background(0);

  //START shape quadIN
  push();
  translate(width/2, height/2, 0);  //===> 1st translation
  rotateY(radians(frameCount*0.5));
  translate(0, 0, -10);      //===> 2nd translation
  shape(quadIN, 0, 0);
  pop();
  //END shape quadIN

  //START shape quadOUT
  push();
  translate(width/2, height/2, 0);   //===> 1st translation
  rotateY(radians(frameCount*0.5));
  translate(0, 0, 10);    //===> 2nd translation
  shape(quadOUT, 0, 0);
  pop();
  //END shape quadOUT
}
1 Like

Hello @Gus_Fermin ,

I have also grappled with this before but ended up simplifying my approach.

You can do this for simple rotations about the Y:

void draw() {
  background(0);
  translate(width / 2, height / 2, -5);
  float startAngle = -TAU/4;
  float angle = (frameCount%720)*TAU/720;
  float angle2 = startAngle+angle;
  rotateY(angle2);
  //fill(#0768ED);
  //circle(150, 100, 50);
  if (angle2 > -TAU/4 && angle2 < TAU/4)
    shape(quadIN, 0, 0);
  else
    shape(quadOUT, 0, 0);
  }

I did this for this example:
OpenTrack UDP Data Tracking

More complex rotations may be problematic.

UPDATE:
I see that you sorted this out already!

:)

1 Like

You are rotating about (0, 0, 0) and then offsetting the Z slightly so they are not in the same plane and you can see the face of each quad as they rotate. With out the Z offset you do not see each face as it rotates.

I added some axes along X, Y and Z to your code to help visualize this:

void draw() 
  {
  background(0);
  push();
  translate(width/2, height/2, 0);  //===> 1st translation
  rotateY(radians(frameCount*0.5));
  
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);
 
  translate(0, 0, .01);     // +Z .01
  shape(quadIN, 0, 0);
  translate(0, 0, -.02);    // -Z .01 This just translates it back and some without the need for another push() and pop()
  shape(quadOUT, 0, 0);
  pop();
  }

I also wanted to see how small I could go with the Z offset.

These may be of interest to you in future:
hint() / Reference / Processing.org
I tired some of these with no Z offset to see if it made a difference and it did not seem to.

:)

1 Like

Hola glv :slight_smile:

I am still studying your reply to me about pixel exploration! :slight_smile:
Thank you very much for taking a look and for your solution. I did, in my own exploration, before the above a sketch taking into account the angles and changing the pimages according to a center angle, in order to give the illusion that the card was flipping.

On this instance however, I set out to draw a card (1side recto+1+side verso) in 3D SPACE.

If you look at my working draw() solution above, it works, I just do not understand why it works. I needed to specify on both quads a 2nd translation, only moving the Z parameter to make it work… do not kno if I am stating my question clearly…

1 Like

Hello again!

We may have been out of sync answering in real time…

Another example that rotates and translates the shape in setup() before you use it:

Click here to see code!
PImage imgIN;
PImage imgOUT;

PShape quadIN;
PShape quadOUT;

void setup() {
  size(500, 500, P3D);
  //both PImages dims are: Width: 212 x Height: 300
  imgIN = loadImage("https://i.ibb.co/8KwnfvB/Pa-IN.png");
  imgIN.resize(0, 300);
  
  imgOUT = loadImage("https://i.ibb.co/QmjM7fG/Pa-OUT.png");
  imgOUT.resize(0, 300);

  noStroke();
  //START PShape quadIN
  quadIN = createShape();
  quadIN.beginShape();
  quadIN.noFill();
  quadIN.texture(imgIN);
  quadIN.vertex(-106, -150, 0, 0, 0);
  quadIN.vertex(106, -150, 0, imgIN.width, 0);
  quadIN.vertex(106, 150, 0, imgIN.width, imgIN.height);
  quadIN.vertex(-106, 150, 0, 0, imgIN.height);
  quadIN.endShape();
  //quadIN.rotateY(TAU/2);
  quadIN.translate(0, 0, 10);
  //END PShape quadIN

  //START PShape quadOUT
  quadOUT = createShape();
  quadOUT.beginShape();
  quadOUT.texture(imgOUT);
  quadOUT.vertex(-106, -150, 0, 0, 0);
  quadOUT.vertex(106, -150, 0, imgOUT.width, 0);
  quadOUT.vertex(106, 150, 0, imgOUT.width, imgOUT.height);
  quadOUT.vertex(-106, 150, 0, 0, imgOUT.height);
  quadOUT.endShape();
  //quadOUT.rotateY(TAU/2);
  quadOUT.translate(0, 0, -10);
  //END PShape quadOUT  
}

void draw() 
  {
  background(0);
  push();
  translate(width/2, height/2, 0);
  int counter = frameCount%360; //0 to 359 and repeats
  //println(counter);
  rotateY(counter*(TAU/360));
  
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);

  shape(quadIN, 0, 0);
  shape(quadOUT, 0, 0);
  pop();
  }

Consider also grouping the shapes once you have them sorted out:

Thank you for the enjoyable distraction!

:)

1 Like

Thank you so much GLV!!! I think I understand it better now. As a side note / curiosity I noticed that in both, my example and your example, one of the PShapes has a black background slightly visible when it passes the 90 degrees angle. I do not know why, and only it is one of them. both of them have a transparent (no background) PNG as a texture… Do you happen to know why is that the case? find image below:

Thank you again :slight_smile:

PS: Your reply / help / didactic impulse that I was referring to in the initial msg of this thread is this one:

using-pixels-array-over-points-trying-to-moving-pixels-xy-off-screen-like-points/24309

It really blew my mind and opened up the pixels array for me. It is from 2020-10-05. :slight_smile:

1 Like

Hello,

Pixel manipulation can be fun!

I don’t have an answer for this… yet.
I am confident that there are members here that can answer this with clarity.

Another exploration of this:

Click here to see code!
PImage imgIN;
PImage imgOUT;

PShape quadIN;
PShape quadOUT;

boolean toggle;

void setup() {
  size(800, 400, P3D);
  //both PImages dims are: Width: 212 x Height: 300
  imgIN = loadImage("https://i.ibb.co/8KwnfvB/Pa-IN.png");
  imgIN.resize(0, 300);
  
  imgOUT = loadImage("https://i.ibb.co/QmjM7fG/Pa-OUT.png");
  imgOUT.resize(0, 300);

  noStroke();
  //START PShape quadIN
  quadIN = createShape();
  quadIN.beginShape();
  //quadIN.normal(0, 0, 1); // Tried -1 also
  quadIN.noFill();
  quadIN.texture(imgIN);
  quadIN.vertex(-106, -150, 0,    0, 0);
  quadIN.vertex(106, -150, 0,     imgIN.width, 0);
  quadIN.vertex(106, 150, 0,      imgIN.width, imgIN.height);
  quadIN.vertex(-106, 150, 0, 0,  imgIN.height);
  quadIN.endShape();
  //quadIN.rotateY(TAU/2);
  quadIN.translate(0, 0, 20);
  //END PShape quadIN

  //START PShape quadOUT
  quadOUT = createShape();
  quadOUT.beginShape();
  //quadOUT.normal(0, 0, 1); //// Tried -1 also
  quadOUT.texture(imgOUT);
  quadOUT.vertex(-106, -150, 0,    0, 0);
  quadOUT.vertex(106, -150, 0,     imgOUT.width, 0);
  quadOUT.vertex(106, 150, 0,      imgOUT.width, imgOUT.height);
  quadOUT.vertex(-106, 150, 0, 0,  imgOUT.height);
  quadOUT.endShape();
  //quadOUT.rotateY(TAU/2);
  quadOUT.translate(0, 0, -20);
  //END PShape quadOUT 
  
  //hint(ENABLE_DEPTH_SORT);
  //hint(ENABLE_DEPTH_TEST);
  
  ortho();
}

void draw() 
  {
  background(128);
  //lights();
  
  if(toggle)
    {
    //hint(ENABLE_DEPTH_MASK);
    hint(ENABLE_DEPTH_SORT);
    //hint(ENABLE_DEPTH_TEST);
    println("ENABLE");
    }
  else
    {
    //hint(ENABLE_DEPTH_MASK);  
    hint(DISABLE_DEPTH_SORT);
    //hint(DISABLE_DEPTH_TEST);
    println("DISABLE");
    }
  
  int counter = frameCount%360; //0 to 359 and repeats
  //println(counter);
  
 push();
  translate(width/4, height/2, 0);
  rotateY(counter*(TAU/360)); 
  //noFill();
  //stroke(0);
  rectMode(CENTER);
  push();
  //translate(0, 0, -100);
  rect(0, -100, 200, 50);
  pop();
  
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);
  
  push();
  //translate(0, 0, 1);
  shape(quadIN, 0, 0);
  
  pop();
  push();
  //translate(0, 0, -1);
  shape(quadOUT, 0, 0);
  pop();
 pop();
 
//************************

 push();
  translate(3*width/4, height/2, 0);
  rotateY(counter*(TAU/360)); 
  //noFill();
  //stroke(0);
  rectMode(CENTER);
  push();
  //translate(0, 0, -100);
  rect(0, -100, 200, 50);
  pop();
   
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);
  
  imageMode(CENTER);
  push();
  translate(0, 0, 20);
  image(imgIN, 0, 0);
  
  pop();
  push();
  translate(0, 0, -20);
  image(imgOUT, 0, 0);
  pop();
 pop(); 
  }
  
void keyPressed()
  {
  toggle = !toggle;
  }

A lot of stuff is commented to show what I was trying out.

Another topic of interest with textured shapes:
Faster Rendering Techniques in P3D?

:)

1 Like

Hola glv :slight_smile:

Your exploration is great. Just ran it… when one toggles the: hint(ENABLE_DEPTH_SORT);
with keyPressed()in your code, the background disappears!! but only on the second copy located at the right!?!?! :sweat_smile:Now it is even more of an enigma lol.
The only discernible difference between the left-shapes and the right-shapes is a imageMode(CENTER); before the first push before displaying the 1st shape, yet still it is only the one on the right that is able to toggle that background… very strange behavior…

Tried yesterday to o everything in draw, thinking that it might be like a mirage/background/leftover stuck to it from setup or something, lol, same result. Tried it with and without the ortho() to the same result…
maybe @neilcsmith might know why this is the case? as he has dived into the workings of everything to be able to make https://www.praxislive.org/

Abrazos

Hello,

One more for the road…

I created the shapes in draw() for this example code.

Click here for code!
PImage imgIN;
PImage imgOUT;

PShape quadIN;
PShape quadOUT;

boolean toggle;

void setup() 
  {
  size(800, 400, P3D);
  //both PImages dims are: Width: 212 x Height: 300
  imgIN = loadImage("https://i.ibb.co/8KwnfvB/Pa-IN.png");
  imgIN.resize(0, 300);
  
  imgOUT = loadImage("https://i.ibb.co/QmjM7fG/Pa-OUT.png");
  imgOUT.resize(0, 300);
  
  ortho();
  }

void draw() 
  {
  background(128);
  //lights();
  
  if(toggle)
    {
    //hint(ENABLE_DEPTH_MASK);
    hint(ENABLE_DEPTH_SORT);
    //hint(ENABLE_DEPTH_TEST);
    println("ENABLE");
    }
  else
    {
    //hint(ENABLE_DEPTH_MASK);  
    hint(DISABLE_DEPTH_SORT);
    //hint(DISABLE_DEPTH_TEST);
    println("DISABLE");
    }
  
  int counter = frameCount%360; //0 to 359 and repeats
  //println(counter);
  
 push();
  translate(width/4, height/2, 0);
  rotateY(counter*(TAU/360)); 
  //noFill();
  //stroke(0);
  rectMode(CENTER);
  push();
  //translate(0, 0, -100);
  rect(0, -100, 200, 50);
  pop();
  
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);
  
  push();
  noStroke();
  beginShape();
  texture(imgIN);
  vertex(-106, -150, 20,    0, 0);
  vertex(106, -150, 20,     imgIN.width, 0);
  vertex(106, 150, 20,      imgIN.width, imgIN.height);
  vertex(-106, 150, 20, 0,  imgIN.height);
  endShape();
  
  beginShape();
  texture(imgOUT);
  vertex(-106, -150, -20,    0, 0);
  vertex(106, -150, -20,     imgOUT.width, 0);
  vertex(106, 150, -20,      imgOUT.width, imgOUT.height);
  vertex(-106, 150, -20,     0,  imgOUT.height);
  endShape();
  pop();
 pop();
 
//************************

 push();
  translate(3*width/4, height/2, 0);
  rotateY(counter*(TAU/360)); 
  //noFill();
  //stroke(0);
  rectMode(CENTER);
  push();
  //translate(0, 0, -100);
  rect(0, -100, 200, 50);
  pop();
   
  stroke(255, 255, 0);
  line(-150, 0, 0, 150, 0, 0);
  line(0, -150, 0, 0, 150, 0);
  line(0, 0, -150, 0, 0, 150);
  
  imageMode(CENTER);
  push();
  translate(0, 0, 20);
  image(imgIN, 0, 0);
  
  pop();
  push();
  translate(0, 0, -20);
  image(imgOUT, 0, 0);
  pop();
 pop(); 
  }
  
void keyPressed()
  {
  toggle = !toggle;
  }

I am off to the wilderness now!

:)

1 Like

Hola glv,

Thank you I see it now, although can’t say I understand it? when you draw the quads in draw() and not in setup(), you can make that RotatingBackgroundglitch dissapear. In the second instance of the shapes to the right, you displayed the original PImages with image() as opposed to with shape() and it works ? I was under the impression that the PImages were being used only as textures for the PShape quads… So don’t know how displaying them with image() is displaying the drawn quads…

If I understood correctly, here in the above examples, you only drew the shapes in draw, but you did not create the quads PShapes… so the PShapes / quads are not created as drawing objects to be instantiated, only direct drawing in draw…

Have a good trip! :slight_smile:

Hello,

Related topic from @raron here:

:)

1 Like

Hello,

I found this on GitHub:

:)

1 Like