createShape() and lerp()

This is an optimization trick with a PGraphics that relies on a fixed-perspective optical illusion – it has several limitations, the most important being that it only works as long as your 3D camera does not change perspective at all (so, you cannot use this with a PeasyCam in motion).

Here is the sketch output, which looks normal:

…but if we change the perspective, we see that we are periodically drawing a new box into a static backdrop image, rather than re-rendering a full 3D space each frame.

Code revised from your example (press any key to switch perspective):

Summary
ArrayList<Boxx> pastBoxxes;  // non-moving boxxes -- was ArrayList<PShape> shapes
Boxx currentBoxx;  // the moving boxx -- was ArrayList<PVector> olPosition
Boxx futureBoxx;  // the invisible mouse click target boxx -- ArrayList<PVector> newPosition

PGraphics pastBoxxesImage;

float lerpSpeed;
int lerpNear;

boolean setback;

void setup() {
  size(500, 400, P3D);

  pastBoxxesImage = createGraphics(500, 400, P3D);
  pastBoxxesImage.beginDraw();
  pastBoxxesImage.background(255, 0);
  pastBoxxesImage.endDraw();

  noFill();
  hint(ENABLE_DEPTH_SORT);

  pastBoxxes = new ArrayList<Boxx>();
  currentBoxx = new Boxx(width/2, height/2);
  lerpSpeed = 0.05;
  lerpNear = (int)(1/lerpSpeed) + 1;
}

void draw() {
  background(255);

  //for (Boxx box : pastBoxxes) {
  //  stroke(0);
  //  box.display();
  //}
  pushMatrix();
  if(setback){
    translate(0, 0, -40);
    rotateY(PI/8);
  }
  image(pastBoxxesImage, 0, 0);
  rect(0,0,width,height);
  popMatrix();
  
  if (futureBoxx!=null) {
    currentBoxx.move((int) lerp(currentBoxx.getPosX(), futureBoxx.getPosX(), lerpSpeed), (int) lerp(currentBoxx.getPosY(), futureBoxx.getPosY(), lerpSpeed));
    println(1/lerpSpeed, ':', abs(currentBoxx.getPosX() - futureBoxx.getPosX()));
    if (abs(currentBoxx.getPosX() - futureBoxx.getPosX()) < lerpNear) {
      pastBoxxes.add(currentBoxx.copy());
      currentBoxx.display(pastBoxxesImage);
      futureBoxx = null;
      println(pastBoxxes.size());
    }
    stroke(255,0,0);
    currentBoxx.display();
  }
}

void mouseReleased() {
  futureBoxx = new Boxx(mouseX, mouseY);
  for(Boxx boxx : pastBoxxes){
    println(boxx);
  }
}

void keyReleased() {
  setback = !setback;
}

class Boxx {

  int boxWidth, posX, posY;

  Boxx(int posX, int posY) {
    boxWidth = 50;
    this.posX = posX;
    this.posY = posY;
  }
  
  void move(int posX, int posY){
    this.posX = posX;
    this.posY = posY;
  }
  
  Boxx copy() {
    return new Boxx(posX, posY);
  }

  void display() {
    pushMatrix();
    translate(posX, posY);
    box(boxWidth);
    popMatrix();
  }
  
  void display(PGraphics pg) {
    pg.beginDraw();
    pg.pushMatrix();
    pg.translate(posX, posY);
    pg.noFill();
    pg.box(boxWidth);
    pg.popMatrix();
    pg.endDraw();
  }

  int getPosX() {
    return posX;
  }

  int getPosY() {
    return posY;
  }
  
}

For older discussions of these techniques, see: [SOLVED] Draw static rect filling the screen infront of PeasyCam - Processing 2.x and 3.x Forum

Thank you :slight_smile:
And all this code is performing better then a simple iteration just because you just use the same object? Wow!

In my future sketch I think I’ll have to rotate the camera so maybe the PImage version will not work.

Also I noticed that if you try to click when the Boxx is moving, you change the point for that Boxx, this means that you can’t have multiple Boxxes moving, only if you create an entirely new one you can have multiple Boxxes moving?

Thank you

Yes. if you want to have many Boxxes moving at the same time, each towards its own target, then you probably want to make futureBoxx part of the Boxx class – that way each Boxx has its own target.

I’m not sure I understood – which previous code in this thread is the “simple iteration”, and which is the “all this”? If you want to know which of two things performs faster, in general, test them both. However, in your original example you were writing lots of redundant data into your shapes step, which was causing problems – as a rule not writing, storing, or updating or drawing things that are extra is always faster.

Another way of optimizing heavy renders with a rotating camera is to only redraw when the camera moves.

For a very simple system, you can set noLoop() and then call redraw() to activate the draw() only when certain criteria are met – for example, mouseDragged() firing, which give the PeasyCam drag handler its update.

For something slightly more complicated, you can use a flag to track whether you need to redraw the screen or not. Here is an example sketch that draws 20,000 randomly rotated cubes in a 3D environment with PeasyCam. While the mouse is NOT dragging it runs at about 60 fps. When the mouse IS dragging it runs at about 5 fps.

Summary
/**
 * Refresh3D -- only redraw the scene when needed
 * 2018-09-28 Jeremy Douglass - Processing 3.4
 */

import peasy.*;
PeasyCam camera;

int WORKLOAD = 20000;
int lastMillis = 0;
boolean refresh = true;

void setup() {
  size(400, 400, P3D);
  camera = new PeasyCam(this, 0, 0, 0, 450);
  refresh = true;
}

void draw() {
  if (refresh) {
    render();
    refresh = false;
  }
  log();
}

void render() {
  background(0);
  randomSeed(0);
  for (int i=0; i<WORKLOAD; i++) {
    rotateX(random(PI));
    rotateY(random(PI));
    rotateZ(random(PI));
    box(width/2 + random(width/8));
  }
}

void mouseDragged() {
  refresh = true;
}

void log() {
  print((int)(1000/(float)(millis()-lastMillis)), "");
  lastMillis = millis();
}

1 Like