How to pick specific parts of a Shape3D shape (processing)

I am attempting to make a voxel-ish world builder, and I need to be able to stack the different blocks. How can I pick the separate sides of the block with the shapes3d library. Whenever I pick the shape, it renders it as the entire shape, and I am unsure of how to select only the part that I would like, so that I can build upon it.

import peasy.*;
import shapes3d.*;
import shapes3d.utils.*;

Shape3D picked = null;
boolean clicked = true;

PeasyCam cam;

float[] xArr,yArr,zArr;
float x,y,z;
Box[] boxArr;
color[] colArr;
CameraState defaultState;

void setup(){

    size(1200,800,P3D);
    cam = new PeasyCam(this,width/2,height/2,0,500);
    cam.setMinimumDistance(200);
    cam.setMaximumDistance(2000);
    cam.setWheelScale(.05);
    cam.setDistance(1000);
    cam.rotateZ(PI/4);
    cam.rotateX(-PI/3);
    cam.setResetOnDoubleClick(false);
    defaultState = cam.getState();
    x=width/2;
    y=height/2;
    z=0;

    xArr = new float[200];
    yArr = new float[200];
    zArr = new float[200];
    colArr = new color[200];
    boxArr= new Box[200];

    for(int i=0;i<boxArr.length;i++){
        boxArr[i]=null;
    }

    xArr[0]=x;
    yArr[0]=y;
    zArr[0]=z;
    boxArr[0]=new Box(this);
    colArr[0]=color(255,255,255);
}

final float xrot=-PI/8,yrot=PI/4,zrot=0;
color col = color(255,255,255);
void mouseClicked(){
    clicked=true;
}

void draw(){
    pushMatrix();

    if (keyPressed&&key=='r'){
        cam.setState(defaultState);
        cam.setDistance(1000);
    }

    background(213, 243, 245);
    ambientLight(200,200,200);
    fill(col);
    lights();
    directionalLight(100,100,100,50,-50,0);

    int actuallength=0;
    for (int i=0;i<boxArr.length;i++){
        if(boxArr[i]!=null){
            actuallength++;
        }
    }

    for(int i = 0; i<actuallength; i++){  
        boxArr[i].fill(colArr[i]);
        boxArr[i].strokeWeight(1.75);
        boxArr[i].stroke(155);
        boxArr[i].moveTo(xArr[i],yArr[i],zArr[i]);
        boxArr[i].drawMode(Shape3D.SOLID|Shape3D.WIRE);
        boxArr[i].draw();
    }

    if (clicked){
        clicked=false;
        picked = Shape3D.pickShape(this, mouseX, mouseY);

        int pos=0;
        for(int i=0;i<boxArr.length;i++){
            if(boxArr[i]==picked){
                pos=i;
            }
        }
        if(picked!=null){
            println();
            boxArr[actuallength]=new Box(this);
            xArr[actuallength]=xArr[pos]+(100);
            yArr[actuallength]=yArr[pos];
            zArr[actuallength]=zArr[pos];
            colArr[actuallength]=color(255,255,255);
        }
    }

    popMatrix();
}
1 Like

i not know that library, but
-a- some things you do in draw should be done in setup
( or better at new box creation )
-b- you use array for remember position and color
( good idea if you not use that library at all )
but here the box knows where it is
( no need to set it again in every draw loop )
and i show you how to read its position back
-c- your logic on click select a box
( but you deleted the selection color thing from the example )
and directly makes a new one
( even there is one already…)
you not used good print to understand whats going on in your logic

i hope following mod will help little bit:

import peasy.*;
import shapes3d.*;
import shapes3d.utils.*;

Shape3D picked = null;
boolean clicked = true;

PeasyCam cam;

float[] xArr, yArr, zArr;
float x, y, z;
Box[] boxArr;
color[] colArr;
CameraState defaultState;

void setup() {

  size(1200, 800, P3D);
  cam = new PeasyCam(this, width/2, height/2, 0, 500);
  cam.setMinimumDistance(200);
  cam.setMaximumDistance(2000);
  cam.setWheelScale(.05);
  cam.setDistance(1000);
  cam.rotateZ(PI/4);
  cam.rotateX(-PI/3);
  cam.setResetOnDoubleClick(false);
  defaultState = cam.getState();
  x=width/2;
  y=height/2;
  z=0;
  boxArr= new Box[200];
  for (int i=0; i<boxArr.length; i++)  boxArr[i]=null;
  make_new_box(0);
}

final float xrot=-PI/8, yrot=PI/4, zrot=0;
color col = color(255, 255, 255);

void mouseClicked() {
  clicked=true;
}

int pos=-1;
PVector pp = new PVector(0, 0, 0);
void draw() {
  pushMatrix();

  if (keyPressed&&key=='r') {
    cam.setState(defaultState);
    cam.setDistance(1000);
  }

  background(213, 243, 245);
  ambientLight(200, 200, 200);
  fill(col);
  lights();
  directionalLight(100, 100, 100, 50, -50, 0);

  int actuallength=0;
  for (int i=0; i<boxArr.length; i++) {
    if (boxArr[i]!=null) {
      actuallength++;
    }
  }

  for (int i = 0; i < actuallength; i++) {  
    if ( i == pos ) { 
      boxArr[i].stroke(color(200, 0, 0));
      pp = boxArr[i].getPosVec();  // READBACK POS http://lagers.org.uk/s3d4p/ref/classshapes3d_1_1_shape3_d.html
    } else             boxArr[i].stroke(155);
    boxArr[i].draw();
  }

  if (clicked) {
    clicked=false;
    picked = Shape3D.pickShape(this, mouseX, mouseY);

    if (picked!=null) {
      pos = -1;
      for (int i=0; i<boxArr.length; i++)
        if ( boxArr[i] == picked )
          pos=i;
      make_new_box(actuallength); //_with position after pos?even there is one already ?
    }
  }

  popMatrix();
}

void make_new_box(int j) {
  println("picked pos "+pos+" at "+pp+" make new box "+j);
  boxArr[j]=new Box(this);
  //boxArr[j].moveTo(pp.x+100, 0,0);
  boxArr[j].moveTo(pp.x+100, pp.y+100, pp.z+100);
  boxArr[j].fill(color(random(100, 255), random(100, 255), random(100, 255)) );
  boxArr[j].strokeWeight(1.75);
  boxArr[j].drawMode(Shape3D.SOLID|Shape3D.WIRE);
}

1 Like

If I am understanding right, you want to “pick” one of the six faces of a box, and then highlight that side only to indicate that it has been picked. Is that right?

The Shapes3D.Box class is designed for exactly this.

http://www.lagers.org.uk/s3d4p/ref/classshapes3d_1_1_shape3_d.html

Each shape may consist of 1-8 parts, for instance the

  • Box has 6 parts, the 6 sides […]

So when shape picking it is not only possible to detect whic shape is picked but also the actual part.

So, building on the example from @kll – when you call pick and get back a Picked, it contains an integer that indicates which part (which side of the box) was picked. You can then you yourshape.fill(color, partnum) with a picked part number to only fill that side of the box, for instance. (untested)

Crosspost: https://stackoverflow.com/questions/58697488/how-to-pick-specific-parts-of-a-shape3d-shape-processing