0

I need to draw a cube in processing without the use of built-in functions. All I could find was using the box() function then rotating the image to see it in 3D.

How can I achieve this?

c_de
  • 1
  • 2

1 Answers1

1

An alternative is using createShape(BOX) instead:

PShape cube;

void setup(){
  size(600, 600, P3D);
  strokeWeight(9);
  
  cube = createShape(BOX, 150);
}

void draw(){
  background(255);
  lights();
  
  translate(width * 0.5, height * 0.5, 0);
  rotateY(map(mouseX, 0, width, -PI, PI));
  rotateX(map(mouseY, 0, height, PI, -PI));
  
  shape(cube);
}

If this is for a homework/assignment it might not be enough (and you should state that in the question, and you should also post a code snippet of your attempt with a description of what didn't work). You'll need to draw the box from scratch. One easy way to think about is to draw the 8 cube vertices on pen and paper with 0,0,0 at the centre, then keeping in mind where -x/+x, -y/+y, -z/+z axes point, mark each vertex, for example:

( x, y, z)
(-1,-1,-1)
(+1,-1,-1)
(+1,+1,-1)
(-1,+1,-1)

should be the back face. Repeat the process for the rest of the faces.

In Processing you'd use beginShape(QUADS) since you're drawing a cube and it's faces are quads, vertex(x, y, z) to specify each point/vertex and finally endShape() to complete the shape (and "join the dots"):

void setup(){
  size(600, 600, P3D);
  strokeWeight(9);
}

void draw(){
  background(255);
  lights();
  
  translate(width * 0.5, height * 0.5, 0);
  rotateY(map(mouseX, 0, width, -PI, PI));
  rotateX(map(mouseY, 0, height, PI, -PI));
  
  cubeFaces(150);
}

void cubeFaces(float size){
  // half size: keep the pivot at the center of the mesh 
  float s = size * 0.5;
  beginShape(QUADS);
  // back (-z)
  vertex(-s, -s, -s);
  vertex(+s, -s, -s);
  vertex(+s, +s, -s);
  vertex(-s, +s, -s);
  // front (+z)
  vertex(-s, -s, +s);
  vertex(+s, -s, +s);
  vertex(+s, +s, +s);
  vertex(-s, +s, +s);
  // top (-y)
  vertex(-s, -s, +s);
  vertex(-s, -s, -s);
  vertex(+s, -s, -s);
  vertex(+s, -s, +s);
  // bottom (+y)
  vertex(+s, +s, +s);
  vertex(+s, +s, -s);
  vertex(-s, +s, -s);
  vertex(-s, +s, +s);
  // left (-x)
  vertex(-s, -s, +s);
  vertex(-s, -s, -s);
  vertex(-s, +s, -s);
  vertex(-s, +s, +s);
  // right (+x)
  vertex(+s, -s, +s);
  vertex(+s, -s, -s);
  vertex(+s, +s, -s);
  vertex(+s, +s, +s);
  
  endShape();
}

PShape is useful because you can cache the geometry using a very similar beginShape()/endShape(), but rather than recreating the shape every frame you can simply render the predefined geometry:

PShape cube;

void setup(){
  size(600, 600, P3D);
  strokeWeight(9);
  
  cube = getCube(150);
}

void draw(){
  background(255);
  lights();
  
  translate(width * 0.5, height * 0.5, 0);
  rotateY(map(mouseX, 0, width, -PI, PI));
  rotateX(map(mouseY, 0, height, PI, -PI));
  
  shape(cube);
}

void cubeFaces(float size){
  // half size: keep the pivot at the center of the mesh 
  float s = size * 0.5;
  beginShape(QUADS);
  // back (-z)
  vertex(-s, -s, -s);
  vertex(+s, -s, -s);
  vertex(+s, +s, -s);
  vertex(-s, +s, -s);
  // front (+z)
  vertex(-s, -s, +s);
  vertex(+s, -s, +s);
  vertex(+s, +s, +s);
  vertex(-s, +s, +s);
  // top (-y)
  vertex(-s, -s, +s);
  vertex(-s, -s, -s);
  vertex(+s, -s, -s);
  vertex(+s, -s, +s);
  // bottom (+y)
  vertex(+s, +s, +s);
  vertex(+s, +s, -s);
  vertex(-s, +s, -s);
  vertex(-s, +s, +s);
  // left (-x)
  vertex(-s, -s, +s);
  vertex(-s, -s, -s);
  vertex(-s, +s, -s);
  vertex(-s, +s, +s);
  // right (+x)
  vertex(+s, -s, +s);
  vertex(+s, -s, -s);
  vertex(+s, +s, -s);
  vertex(+s, +s, +s);
  
  endShape();
}

PShape getCube(float size){
  PShape cube = createShape();
  // half size: keep the pivot at the center of the mesh 
  float s = size * 0.5;
  cube.beginShape(QUADS);
  // back (-z)
  cube.vertex(-s, -s, -s);
  cube.vertex(+s, -s, -s);
  cube.vertex(+s, +s, -s);
  cube.vertex(-s, +s, -s);
  // front (+z)
  cube.vertex(-s, -s, +s);
  cube.vertex(+s, -s, +s);
  cube.vertex(+s, +s, +s);
  cube.vertex(-s, +s, +s);
  // top (-y)
  cube.vertex(-s, -s, +s);
  cube.vertex(-s, -s, -s);
  cube.vertex(+s, -s, -s);
  cube.vertex(+s, -s, +s);
  // bottom (+y)
  cube.vertex(+s, +s, +s);
  cube.vertex(+s, +s, -s);
  cube.vertex(-s, +s, -s);
  cube.vertex(-s, +s, +s);
  // left (-x)
  cube.vertex(-s, -s, +s);
  cube.vertex(-s, -s, -s);
  cube.vertex(-s, +s, -s);
  cube.vertex(-s, +s, +s);
  // right (+x)
  cube.vertex(+s, -s, +s);
  cube.vertex(+s, -s, -s);
  cube.vertex(+s, +s, -s);
  cube.vertex(+s, +s, +s);
  
  cube.endShape();
  
  return cube;
}

Bonus points, there's yet another way you can draw a cube: as a special case of a cylinder with 4 subdivions where the radius is half the height. Processing ships with an example you can tweak in Processing > Examples > Topics > Geometry > Vertices.

Here's a modified version of the example rendering a cube:

/**
 * Vertices 
 * by Simon Greenwold.
 * 
 * Draw a cylinder centered on the y-axis, going down 
 * from y=0 to y=height. The radius at the top can be 
 * different from the radius at the bottom, and the 
 * number of sides drawn is variable.
 */

void setup() {
  size(640, 360, P3D);
}

void draw() {
  background(0);
  lights();
  translate(width / 2, height / 2);
  rotateY(map(mouseX, 0, width, 0, PI));
  rotateZ(map(mouseY, 0, height, 0, -PI));
  noStroke();
  fill(255, 255, 255);
  translate(0, -40, 0);
  
  //drawCylinder(10, 180, 200, 16); // Draw a mix between a cylinder and a cone
  //drawCylinder(70, 70, 120, 64); // Draw a cylinder
  //drawCylinder(0, 180, 200, 4); // Draw a pyramid
  
  // Draw a cube
  drawCylinder(50, 50, 75, 4);
}

void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) {
  float angle = 0;
  float angleIncrement = TWO_PI / sides;
  beginShape(QUAD_STRIP);
  for (int i = 0; i < sides + 1; ++i) {
    vertex(topRadius*cos(angle), 0, topRadius*sin(angle));
    vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle));
    angle += angleIncrement;
  }
  endShape();
  
  // If it is not a cone, draw the circular top cap
  if (topRadius != 0) {
    angle = 0;
    beginShape(TRIANGLE_FAN);
    
    // Center point
    vertex(0, 0, 0);
    for (int i = 0; i < sides + 1; i++) {
      vertex(topRadius * cos(angle), 0, topRadius * sin(angle));
      angle += angleIncrement;
    }
    endShape();
  }

  // If it is not a cone, draw the circular bottom cap
  if (bottomRadius != 0) {
    angle = 0;
    beginShape(TRIANGLE_FAN);

    // Center point
    vertex(0, tall, 0);
    for (int i = 0; i < sides + 1; i++) {
      vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle));
      angle += angleIncrement;
    }
    endShape();
  }
}

(You can find my explanation of the polar to cartesian formula used above in this answer)

The above is very similar to the old school OpenGL immediate mode drawing. If this is for a more recent CS oriented course for OpenGL one, you might need to use PGL and a vertex array buffer, slightly different calls. The Cube OpenGL Tutorial cover that nicely. Even though it's c++, you'll recognize functions such as genBuffers(), glBindBuffer(), glBufferData() in Processing's PGL class ( Processing > Examples > Demos > Graphics > LowLevelGL* examples are great starting points and align nicely with the triangle OpenGL tutorial)

Update:

There's yet another example that can easily be tweaked to draw a cube: Processing > Examples > Demos > Graphics > Wiggling.

Here's a tweaked version that draws the cube without the wiggling nor the holes in the faces:

PShape cube;
float cubeSize = 320;

void setup() {
  size(1024, 768, P3D);    

  createCube();
}

void draw() {
  background(0);

  translate(width/2, height/2);
  rotateX(frameCount * 0.01f);
  rotateY(frameCount * 0.01f);

  shape(cube);

  if (frameCount % 60 == 0) println(frameRate);
}

public void keyPressed() {
  if (key == '1') {
    cube.setStrokeWeight(1);
  } else if (key == '2') {
    cube.setStrokeWeight(5);
  } else if (key == '3') {
    cube.setStrokeWeight(10);
  }
}

void createCube() {
  cube = createShape(GROUP);  

  PShape face;

  // Create all faces at front position
  for (int i = 0; i < 6; i++) {
    face = createShape();
    createFace(face);
    cube.addChild(face);
  }

  // Rotate all the faces to their positions

  // Front face - already correct
  face = cube.getChild(0);

  // Back face
  face = cube.getChild(1);
  face.rotateY(radians(180));

  // Right face
  face = cube.getChild(2);
  face.rotateY(radians(90));

  // Left face
  face = cube.getChild(3);
  face.rotateY(radians(-90));

  // Top face
  face = cube.getChild(4);
  face.rotateX(radians(90));

  // Bottom face
  face = cube.getChild(5);
  face.rotateX(radians(-90));
}

void createFace(PShape face) {
  face.beginShape(POLYGON);
  face.stroke(255, 0, 0);
  face.fill(255);

  // Draw main shape Clockwise
  face.vertex(-cubeSize/2, -cubeSize/2, +cubeSize/2);
  face.vertex(+cubeSize/2, -cubeSize/2, +cubeSize/2);
  face.vertex(+cubeSize/2, +cubeSize/2, +cubeSize/2);
  face.vertex(-cubeSize / 2, +cubeSize / 2, +cubeSize / 2);

  face.endShape(CLOSE);
}
George Profenza
  • 50,687
  • 19
  • 144
  • 218