© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
J. MeyerProgramming 101https://doi.org/10.1007/978-1-4842-8194-9_10

10. 3D

Jeanine Meyer1  
(1)
Mt Kisco, NY, USA
 

Abstract

This last chapter is an introduction to the 3D capabilities of Processing. Just as Processing provides ways to draw in the plane, we can build a sketch with objects that are rendered in space, that is, three dimensions. It is important to recall that once things have been drawn in the Processing window, any notion of how the pixels were colored is not maintained. That is why the coding has to keep track of food items and snake segments and rock, slingshot, and chicken, drawing them again as required. Processing does not provide a 3D modeling system, but the facilities provide considerable power. With that in mind, in this chapter, we’ll review programming and Processing concepts and then focus on two 3D sketches.

The first is an animation in which a ball wrapped in the Spanish flag rolls around a wading pool at the Alhambra (Figure 10-1).

A photo of the Alhambra Shape window has a building that has a water pond in its front area. The reflection of a building is viewed in the pond water. It has a water fountain. The image has a text at the bottom of press any key to stop or restart.

Figure 10-1

Ball with Spanish flag rolling around the Alhambra

The second example is a cube, with pairs of photos of Annika’s dance recital on opposing sides (Figure 10-2). The cube can be rotated and builds on a sketch described in the Processing documentation. This can be fully appreciated only by running the program.

A photo of the Annika Frog Flower Makeup window has text at the top of Drag using a mouse anywhere on the screen to rotate the cube. If no action, the cube will rotate by itself. The 3-D cube has 6 faces with 12 edges. Each face of a cube has different photos of the same child.

Figure 10-2

Rotating cube

As with everything in programming, it is essential for your understanding that you experiment: Copy the simple sketches in the documentation and make changes, download or copy the examples in this book and make changes, and create your own sketches. I include extra sketches with the source code. See later in the chapter for screenshots of a dreidel (a top that spins and slows down) and a representation of the solar system, with nine planets rotating round the sun, including Pluto, although that makes for an even greater challenge.

Programming Concepts

Representation of 3D on the flat computer screen requires what is termed rendering. The 3D objects in Processing are collections of flat faces, in certain contexts called facets, made up of edges and vertices (corners) and information on what is the inside vs. the outside of the object. The locations of the vertices are specified using three values for the x, y, and z axes, analogous to x and y coordinates for 2D. In Processing, the standard orientation for the z axis is coming out of the plane of the screen. That is, values for the z dimension increase moving toward us; the default zero z position is at the screen; and values are more negative moving away from us. Although I could, with confidence, make the statement that most programming tools use the origin in the upper left corner, the upside-down arrangement that Processing uses for 2D work, 3D tools differ on the orientation of the three dimensions. Processing uses a left-handed coordinate system, whereas some languages and tools use a right-handed coordinate system.

The task of the renderer is to determine how the object is projected onto the screen to be viewed by the user. This often involves calculating when all or portions of edges and faces are blocked by other faces. This is known as the hidden line or hidden surface removal problem. The calculations use the eye, gaze location (the point in space that the eye is looking at), type of projection (standard perspective or something else), and, sometimes, lighting. Default settings mean that programmers might not need to specify everything to produce results.

Processing and other tools provide a set of primitive 3D shapes along with ways for programmers to construct their own shapes. Spheres are provided as faceted polyhedrons, with the standard setting providing so many facets that they appear round to us. Transformations, such as shown in 2D, are provided.

Processing Programming Features

The first piece of code that signals to Processing that we want to use 3D is the size statement. The line size(500,740,P3D); specifies the dimensions of the Processing window and indicates the use of the 3D renderer. Processing provides two 3D primitives: sphere and box. The sphere takes one parameter, the radius of the sphere. The box takes one or three parameters; using one produces a cube, and using three specifies the three sizes of the three dimensions of the box. The location of the sphere or the box is at the current origin of the coordinate system, which starts in the upper left corner. The result of the following sketch is shown in Figure 10-3.
void setup() {
     size(800,600,P3D);
     sphere(200);
}

A photo of the sketch underscores the 180122b window has the half-sphere which is drawn on the top right corner. It has a large number of vertical and horizontal lines. It looks like a sphere has a large number of triangle structures.

Figure 10-3

Sphere drawn at origin in upper left

I have more to say about the sphere, but first, let’s take the common first step of moving the origin to the center of the window.
void setup() {
     size(800,600,P3D);
     translate(width/2, height/2, 0);
     sphere(200);
}

The code has changed the original origin in the x and y dimensions, but not in z dimensions. This demonstrates the critical aspect of 3D in Processing: drawing is done by setting the origin.

The lines on the sphere mark the facet lines (Figure 10-4). This is a polyhedron with many flat sides, enough to fool our eye into seeing a sphere. The lines are governed by the setting of stroke and the interior of the faces by the setting of fill.

A photo of the sketch underscores the 180122b window has the sphere which is drawn at the center. It has a large number of vertical and horizontal dark lines. It looks like a sphere has a large number of triangle structures. The sphere has a bright color with dark lines on it.

Figure 10-4

Sphere at translated origin

Other transformations, such as rotateX, rotateY, rotateZ, and scale, change what we see in the window. The use of scale can change a sphere into an ellipsoid with different dimensions. You can change the number of facets using sphereDetail, and you can turn off seeing the facets using noStroke. This also removes all edges, which might or might not be what you want.

My next example demonstrates the use of the camera function along with fill and noFill. The first step is to write the setup function and include a call to noFill. The draw function erases the window, as you have seen many times before, and invokes translate to move the drawings to the center of the window. The effects of translate and any other transformation go away after each call of draw and must be repeated.

Using noFill will show you so-called wire frame objects. There is no hidden line removal; all we see are the edges. The following code for setup and draw draws two boxes.
void setup() {
    size(800,600,P3D);
    noFill();
}
void draw() {
    background(200);
    translate(width/2,height/2,0);
    box(30);
    box(100,150,200);
}
This code produces what is shown in Figure 10-5. The two boxes are not obvious. Consider the inner square: if I held up a fully 3D cube right in front of one eye and you closed the other eye, you would see a square, not a cube.

A photo of the box fill test window has a small square box that is placed within the 3-D rectangular box.

Figure 10-5

Box within a box

Now, I show a definition for mouseDragged in which I use the camera function to change the position of the eye to be the current mouse coordinates on the window. You are familiar with providing code for the mouse event functions and making use of mouseX and mouseY. I do not change the other values controlled by the camera function. More accurately, I restate the default values. These include the z location of the eye, the gaze point, and the orientation. My one-eyed camera person is not tilting their head.
void mouseDragged() {
     camera(mouseX, mouseY, (height/2.0) / tan(PI*30.0 / 180.0), width/2.0, height/2.0, 0, 0, 1, 0);
}
Providing this capability for interaction means that the user gets a much greater sense of the 3D nature of the drawing. Figure 10-6 shows the box within box drawing with the only change being the modified eye position.

A photo of the box fill test window has a small square box that is placed within the 3-D rectangular box. Here, the position of the box is changed by dragging the mouse pointer.

Figure 10-6

Changed window after mouse dragging

Finally, I decided I wanted to toggle back and forth between having fill and not having fill, that is, hidden lines removed vs. wire frame. To do this, I declared a global variable noFillB and wrote a keyPressed function.
boolean noFillB = true;
void keyPressed() {
    if (noFillB) {
        fill(255);
        noFillB = false;
    }
    else {
        noFill();
        noFillB = true;
    }
}
Figure 10-7 shows what we would consider a 3D object in space. The fill has been turned back on. The inner box is completely hidden along with three edges of the outer box.

A photo of the box fill test window has a small square box that is placed within the 3D rectangular box. The position of the box is changed by dragging the mouse pointer. Here, the solid rectangular box has a dark outer layer that is visible, and the inner lines are removed.

Figure 10-7

Hidden lines removed

Processing provides texture facilities to put images on 3D shapes. The documentation provides examples of making a 2D map of the world the texture on the sphere. For the rolling ball example, I decided to use the Spanish flag. The first step is to declare a global variable of type PShape.
PShape ball;
Create the shape in the setup function:
PImage design = loadImage("flag.png");
ball = createShape(SPHERE,10);
ball.setTexture(design);
To draw the shape in the window at the current origin, I use
shape(ball);
Recall the hexagons drawn as part of the origami model in Chapter 6. Processing provides a way to create custom shapes in 3D as well as 2D. The vertices are specified using three numbers. It is possible to set the fill for these shapes, as demonstrated by the following code that I have taken from my dreidel example (look ahead to Figure 10-10). The code here, all in setup, draws four triangular sides for the base of the top. Figure 10-8 shows a screenshot. Notice that the vertex mentioned first in the set of vertices starting with beginShape and ending with endShape repeats to close the shape.
void setup() {
       size (600,600,P3D);
       translate(width/2, height/2,0);
       noStroke();
       scale(100);
   fill(100,100,0);
   //bottom triangle from each of the 4 sides to center point
   beginShape();
       vertex(-1,1,1);
       vertex(-1,1,-1);
       vertex(0,2,0);
       vertex(-1,1,1);
   endShape(CLOSE);
   fill(100,0,0);
   beginShape();
       vertex(1,1,1);
       vertex(1,1,-1);
       vertex(0,2,0);
       vertex(1,1,1);
   endShape(CLOSE);
   fill(0,100,0);
   beginShape();
       vertex(-1,1,-1);
       vertex(1,1,-1);
       vertex(0,2,0);
       vertex(-1,1,-1);
   endShape(CLOSE);
   fill(0,0,100);
   beginShape();
       vertex(-1,1,1);
       vertex(1,1,1);
       vertex(0,2,0);
       vertex(-1,1,1);
   endShape(CLOSE);
}

A photo of the dreidel base window has a 3-D diamond shape. A 3-D diamond shape has four sides with one edge and is filled with different colors.

Figure 10-8

3D shape, colored using fill

The code for the dreidel base applied colors using fill in the standard way. An alternative is to put what is called texture on a 3D shape by specifying the position in space of the 3D object and the corresponding position in a 2D image. This requires five numbers. Following the example in the Textured Cube in the Processing documentation, I use the technique of specifying the vertices using unit values and scale the object to be bigger using the scale function. You will find the entire sketch code in the “Program” section for the rotating cube example.

Processing also provides ways of specifying lighting for the scene. It appears that just using the single statement lights(); does improve the look of many sketches involving 3D. The statement must be in the draw function or some other function invoked for each frame, because the settings are reset at each invocation of draw.

I urge you to experiment with the primitives and transformations. I do caution you to proceed at a slow pace, though, as I did in the example with the two boxes. If you change where an object is located and change one or more of the camera parameters and apply rotations, it will not be easy to understand what is going on. If an object such as a sphere is very small, it might appear as a black ball as opposed to a faceted sphere. Making a snowman is a good place to start. You can find a snowman consisting of three spheres and a snowman on a box in the source code. The facets produce a crystal-like effect that fits the idea of a snowman.

Under the Covers

The rendering of 3D scenes into output on the 2D display is called the 3D graphics rendering pipeline. Today, much of the computation is done using special graphics processing units (GPUs) with much parallel computation. The increase in computer speed over the years has been considerable, but the demands from the gaming and movie industries also have increased, so this still is an area of research and development.

Rolling Ball at Alhambra Operation Overview

In the first sketch, the ball rolls down the left side, across the back, up the right side, and then goes back down the right side, across the back from right to left, and up on the left side (Figure 10-1). I do call this a cheap trick: the background is a 2D image from a postcard that I purchased at the Alhambra in Granada, Spain. I term this sketch a cheap trick because of the use of the 2D image from the postcard. Please do keep in mind that the size of the image had to match the size of the Processing window. The ball uses the Processing texture feature to appear to be wrapped in the Spanish flag. The 3D space is superimposed on the background.

The rolling ball at the Alhambra starts in motion. Pressing any key toggles back and forth between stopping and starting. The stop-and-start feature was added later by declaring a global variable, moving, and putting all the coding in the draw function within an if (moving) statement. After I did this, it was much easier for me to produce a screenshot of the sketch with the Spanish flag displayed as I wanted.

Implementing the Rolling Ball at Alhambra

Having been to the Alhambra and possessing the postcard, I decided to produce a sketch that superimposes the movement of a ball against the picture. The program just grew after that, with wrapping the ball in the Spanish flag to show the rotating and providing the stop-and-start feature.

Planning

The first task for the rolling ball was to determine the turning points, and this was done by trial and error. I wrote a sketch that made the postcard picture the background and then drew a sphere at each guessed location. This was an instance of what is termed throw-away code, and you need to be willing to do it. The next task was developing the movement back and forth over the three sides of the reflecting pool. I realized that the coordinates change in z or x. More exactly, the change is decreasing in z, then increasing in x, then increasing in z, then (turning around) decreasing in z, decreasing in x, and increasing in z. I was satisfied with this as an adequate example for my classes. However, when I was writing this book, I decided to do more.

Moving an object in 3D in Processing requires resetting of the origin. If the origin of a sphere is the center, then rotating the sphere on itself is easy once I decide along which axis I want to do the rotation. This creates a new challenge: My code can rotate the sphere, but how to make it visible that the ball is rotating if it is simply a sphere? The answer is to put something on the sphere using the texture facilities. I made use of an image of the Spanish flag. Note that it has file extension png.

I came to the realization that the path was made up of six segments, not three: going down the left side, across from left to right at the back, coming up the right side, going down the right side, across from right to left at the back, and coming up the left side. I used a switch statement in a function I named rotateAndDraw to handle the cases corresponding to the segments.

I put in the stop-and-start facility to give the user something to do, but then I became a happy user because, as I have said, it made it easier to get the screenshot I wanted. My first thought was to use noLoop to stop the action, but I decided that a Boolean would be better. The relationship of the functions is shown in Table 10-1.
Table 10-1

Function Table for Rolling Ball

Function

Invoked by

Invokes

setup

Underlying Java program

 

draw

Underlying Java program

forwardtravel, backwardtravel

forwardtravel

draw

rotateAndDraw

backwardtravel

draw

rotateAndDraw

keyPressed

Underlying Java program

 

Programming the Rolling Ball at Alhambra

The global variables include the values defining the turning points, Boolean (true–false) values, often called flags, the PImage for the background and the PShape for the ball, and the message giving instructions. I note that forwardtravel and backwardtravel could have been part of draw, but this way made sense to me. Table 10-2 shows the code.
Table 10-2

Program for Rolling Ball

PImage bg;

This is the image from the postcard showing the Alhambra

float x,y,z;

Used to indicate the parameters of translate to position the origin to draw the ball

float xstart = 150;

The leftmost x position

float xend = 330;

The rightmost x position

float ylevel = 400;

The constant y level; that is, the height

float zstart = -50;

The farthest away z position

float zend = 450;

The closest z position

boolean forward = true;

Indicating forward motion or not

float a=0;

The a stands for angle; this is used for rotating the ball, initialized to 0

PShape ball;

Will hold the ball, with its texture

boolean moving = true;

Used to indicate movement or not

String msg = "Press any key to stop or restart.";

Instructions

void setup() {

Header for setup

size(500,740,P3D);

Set the dimension of window and 3D

noStroke();

Turn off stroke

sphereDetail(15);

Set the amount of detail (faceting)

bg = loadImage("alhambra.jpg");

Load postcard image

PImage design = loadImage("flag.png");

Load Spanish flag image

ball = createShape(SPHERE,10);

Create a shape

ball.setTexture(design);

Give it texture; that is, wrap the flag around the sphere

background(bg);

Set initial background

x = xstart;

Initialize x

y = ylevel;

Initialize y

z = zend;

Initialize z

textSize(20);

Set text size for instructions

}

Close setup

void draw() {

Header for draw

if (moving) {

Only do something if moving is true

background(bg);

Erase the window and redraw background

fill(0);

Set color for clearing bottom of image

rect(0,700,500,40);

Draw black rectangle

fill(250,0,0);

Set color for instructions message

text(msg,100,720);

Output instructions

lights();

Set lights

if (forward) {

If forward

forwardtravel();

Invoke forwardtravel

}

Close clause

else {

else

backwardtravel();

Invoke backwardtravel

}

Close clause

}

Close the if (moving) clause

}

Close draw

void forwardtravel() {

Header for forwardtravel

if ((z>zstart)&&(x==xstart)) {

Check if at first segment

z--;

Decrement z (move away)

rotateAndDraw(1);

Invoke rotateAndDraw with parameter 1

}

Close clause

else { if (x<xend) {

Check if at segment at back

x++;

Increment x

rotateAndDraw(2);

Invoke rotateAndDraw with parameter 2

}

Close clause

else { z++;

Increment z (move toward viewer)

rotateAndDraw(3);

Invoke rotateAndDraw with parameter 3

if (z>zend) {forward = false;};

If at the end, set forward to false

}

Close clause

}

Close clause

}

Close forwardtravel

void backwardtravel() {

Header for backwardtravel

if ((z>zstart)&&(x>=xend)) {

If at fourth segment

z--;

Decrement z

rotateAndDraw(4);

Invoke rotateAndDraw with parameter 4

}

Close clause

else { if (x>xstart) {

If in back segment

x--;

Decrement x (move to the left)

rotateAndDraw(5);

Invoke rotateAndDraw with parameter 5

}

Close clause

else { z++;

Increment z, now moving toward viewer

rotateAndDraw(6);

Invoke rotateAndDraw with parameter 6

if (z>zend) {forward = true;};

If at end, set forward to true

}

Close clause

}

Close clause

}

Close backwardtravel

void rotateAndDraw(int p) {

Header for rotateAndDraw; parameter will indicate the segment and therefore what gets rotated positively or negatively

a=a+PI/10;

Increment a (the angle)

translate(x,y,z);

Position origin at x,y,z; these have been set previously

switch(p) {

Switch on the parameter

case 1:

First segment, going down the left

rotateX(a);

Rotate around x axis

break;

Leave switch

case 2:

Second segment, going from left to right at the back

rotateZ(a);

Rotate around z axis

break;

Leave switch

case 3:

Third segment, coming up the right

rotateX(-a);

Rotate around x axis, negatively

break;

Leave switch

case 4:

Fourth segment, going back down the right, away from viewer

rotateX(a);

Rotate around x axis

break;

Leave switch

case 5:

Fifth segment, going at the back right to left

rotateZ(-a);

Rotate around z axis, negatively

break;

Leave switch

case 6:

Sixth segment, coming back up the left side

rotateX(-a);

Rotate around x axis, negatively

break;

Leave switch

}

Close switch

shape(ball);

Draw the ball

}

Close rotateAndDraw

void keyPressed() {

Header for keyPressed

moving = !moving;

Toggle the moving Boolean; using !, which is logical not, changes true to false and false to true

}

Close keyPressed

Rotating Cube Operation Overview

The example, shown in Figure 10-2, is based on the Textured Cube described in the Processing documentation. I emphasize again that the sketch needs to be run to be appreciated. The user can rotate the cube using the mouse. Note that dragging the mouse to the right or left causes the cube to be rotated around the y axis. Dragging the mouse up or down the screen causes the cube to be rotated around the x axis. If the mouse is dragged diagonally, it is rotated along both axes.

I made the addition of having the cube rotate by itself after no action by the user after a specified amount of time. This is a nice, although perhaps creepy, effect, and it demonstrates a technique for handling the event of nothing happening.

Implementing the Rotating Cube

I include this as one of the featured examples because of my addition and because I felt it merited extra attention beyond what was provided in the documentation. The main Processing feature demonstrated is applying texture, in the form of images, to faces of a cube.

Planning

I decided to use three images for texture, each for the pair of opposing sides of the cube. Following the Textured Cube example, applying a texture (i.e., an image) to a portion of a 3D shape can be done using unit measurements as opposed to the exact pixel dimensions to relate a set of 3D coordinates (three numbers) to a set of 2D coordinates (two numbers). Each face of the cube is associated with one of the three images.

Two global variables are set up to hold the amount of rotation around the x axis (rotx) and the rotation around the y axis (roty). These values are set in one of two ways. If the user drags the mouse, the mouseDragged function is invoked. The calculation is done to set rotx using the difference between the previous x coordinate of the mouse, held in pmouseY, and the current x coordinate, held in mouseY. The calculation for roty is the difference between mouseX and pmouseY. Here is the code:
void mouseDragged() {
    float rate = 0.01;
    last = millis();
    rotx += (pmouseY-mouseY) * rate;
    roty += (mouseX-pmouseX) * rate;
}

A horizontal (x) movement will set off a rotation around the y axis, and a vertical (y) movement will set off a rotation around the x axis. The expressions involving the previous mouse positions are different because of the upside-down coordinate system. Do not take my word for this. Change mouseDragged and move the mouse and see what seems correct to you.

The rate variable determines how much moves of the mouse affect rotations. You can experiment with the value. Do keep in mind that the mouseDragged function is called at every frame, so you don’t want small moves to lead to big rotations.

The line with millis relates to the second way the rotation variables are set. You can go back to Chapter 5 and review how a pause is implemented in the image test sketch. In the rotating cube example in this chapter, the rotation variables are set if the user does not do anything for a specified amount of time. The specified amount of time is held in the variable interval. The millis function returns the time in milliseconds since the sketch is started. The global variable last is set in setup to the value returned by millis()and in mouseDragged. In the draw function, the following if statement determines if enough time has elapsed since the user did something.
if ((millis()-last) > interval) {
     setRotation();
}
In English, the condition in the if statement asks if the difference between current time and the last time something happened is greater than interval. The setRotation function is the following:
void setRotation() {
    rotx += PI/400;
    roty += PI/400;
}

I could have put these two statements in the if clause, but I generally favor defining functions for distinct tasks.

To produce the cube, I modified the function in the Processing documentation in two ways. I gave the function three parameters for the three sets of opposite sides of the cube. Then I used beginShape(QUADS) and endShape() three times, referencing a different one of the parameters each time. I used the approach of defining the cube as occupying the space from -1 to 1 along the x axis, -1 to 1 along the y axis, and -1 to 1 along the z axis. The vertices of the images are indicated by (0,0), (1,0), (1,1), and (0,1). It is important to note two things. First, this is a very tiny cube. The reason we can see it is that there is a call to scale(200) before the TextureCube function is invoked. Second, there is some distortion of the images I use for this because they are not squares.

The function table for rotating cube is shown in Table 10-3.
Table 10-3

Function Table for Rotating Cube

Function

Invoked by

Invokes

setup

Underlying Java program

 

draw

Underlying Java program

setRotation, TexturedCube

mouseDragged

Underlying Java program

 

setRotation

draw

 

TexturedCube

draw

 

Programming the Rotating Cube

In Table 10-4, you will find the code for this sketch. I did copy the vertex calls in the TexturedCube function from the Processing documentation, but I did do my own fiddling around to convince myself they were correct. I suggest you do your own experimentation. You might end up making the images applied as textures be mirror images some of the time.
Table 10-4

Program of Rotating Cube

PImage frog, flowers, makeup;

For the three images

float rotx = PI/4;

Initial rotation around x axis

float roty = PI/4;

Initial rotation around y axis

int last;

Hold time last thing was done

int interval = 6000;

Amount of wait before rotation “by itself”

void setup() {

Header for setup

size(1000, 1000, P3D);

Set the dimensions of window and set up for 3D

frog = loadImage("AnnikaFrog.JPG");

Load frog image

flowers = loadImage("AnnikaFlowers.JPG");

Load flowers image

makeup = loadImage("AnnikaMakeup.jpg");

Load makeup image

textureMode(NORMAL);

Set normal texture mode

last = millis();

Initial setting of last

}

Close setup

void draw() {

Header for draw

background(0);

Erase window

textSize(20);

Set the text size

text("Drag using mouse anywhere on screen to rotate cube. If no action, cube will rotate by itself.", 17,14);

Give instructions

noStroke();

No stroke

translate(width/2.0, height/2.0, -100);

Move origin to center and back away from viewer

if ((millis()-last) > interval) {

Has there been nothing happening for a long enough time?

setRotation();

Set the rotations

}

Close if clause

rotateX(rotx);

Rotate around x whatever rotx is

rotateY(roty);

Rotate around y whatever roty is

scale(200);

Scale up (because cube defined is tiny)

TexturedCube(frog,flowers,makeup);

Invoke TexturedCube to draw the cube with the images

}

Close draw

void setRotation() {

Header for setRotation

rotx += PI/400;

Increment rotx

roty += PI/400;

Increment roty

}

Close setRotation

void TexturedCube(PImage tex1, PImage tex2, PImage tex3) {

Header for TexturedCube; the parameters are the three images for opposing sides of the cube; the cube is 2 pixels by 2 pixels by 2 pixels

beginShape(QUADS);

Begin a shape, using the QUADS parameter indicating how the images will be applied

texture(tex1);

Use the first image; it will be applied to the faces of the cube that are at z equal to 1 and -1; these are the front face and the back face

vertex(-1, -1,1, 0, 0);

Connect the corners of the face to the corners of the image; the upper left corner of the face is connected to the top left corner of the image

vertex( 1, -1,1, 1, 0);

The upper right corner of the face is connected to the top right corner of the image

vertex( 1,1,1, 1, 1);

The bottom right corner of the face is connected to the bottom right corner of the image

vertex(-1,1,1, 0, 1);

The bottom left corner of the face is connected to the bottom left corner of the image

 

This still uses tex1; now applied to the back face; the x and y specifications will be opposite the previous set of images to display the images correctly

vertex( 1, -1, -1, 0, 0);

Connect the corners of the face to the corners of the image; the upper right corner of the face is connected to the top left corner of the image

vertex(-1, -1, -1, 1, 0);

The upper left corner of the face is connected to the top right corner of the image

vertex(-1,1, -1, 1, 1);

The bottom left corner of the face is connected to the bottom right corner of the image

vertex( 1,1, -1, 0, 1);

The bottom right corner of the face is connected to the bottom left corner of the image

endShape();

End the shape assigning the image as texture in two parts, for the two opposing faces

beginShape(QUADS);

Begin a shape, using the QUADS parameter indicating how the images will be applied

texture(tex2);

Use the second image; it will be applied to the faces of the cube that are at y equal to -1 and 1; these are the top face and the bottom face

vertex(-1, -1, -1, 0, 0);

Now the coordinate that stays the same is the y coordinate for the cube; it is at -1 for the first four (the top face) and then will be at 1 for the next four; the image vertex at 0,0 is at the cube vertex -1,-1,-1

vertex( 1, -1, -1, 1, 0);

The image vertex at 1,0 is at the cube vertex 1,-1,-1

vertex( 1, -1,1, 1, 1);

The image vertex at 1,1 is at the cube vertex 1, -1,1

vertex(-1, -1,1, 0, 1);

The image vertex at 0,1 is at the cube vertex  -1, -1,1

 

Using the same image, tex2, the next four vertices describe the bottom face

vertex(-1,1,1, 0, 0);

The image vertex at 0,0 is at the cube vertex -1,1,1

vertex( 1,1,1, 1, 0);

The image vertex at 1,0 is at the cube vertex 1,1,1

vertex( 1,1, -1, 1, 1);

The image vertex at 1,1 is at the cube vertex 1,1,-1

vertex(-1,1, -1, 0, 1);

The image vertex at 0,1 is at the cube vertex -1,1,-1

endShape();

End the shape

beginShape(QUADS);

Begin a shape, using the QUADS parameter, indicating how the images will be applied

texture(tex3);

Use the third image; it will be applied to the faces of the cube that are at x equal to -1 and 1; these are the left and right faces

vertex(-1, -1, -1, 0, 0);

Now the coordinate that stays the same is the x coordinate for the cube; it is at -1 for the first four (the left face) and then will be at 1 for the next four; the image vertex at 0,0 is at the cube vertex -1,-1,-1

vertex(-1, -1,1, 1, 0);

The image vertex at 1,0 is at the cube vertex -1,-1,1

vertex(-1,1,1, 1, 1);

The image vertex at 1,1 is at the cube vertex -1,1,1

vertex(-1,1, -1, 0, 1);

The image vertex at 0,1 is at the cube vertex -1,1,-1

 

Using the same image, tex3, the next four vertices describe the right face

vertex( 1, -1,1, 0, 0);

The image vertex at 0,0 is at the cube vertex 1,-1,1

vertex( 1, -1, -1, 1, 0);

The image vertex at 1,0 is at the cube vertex  1,-1,-1

vertex( 1,1, -1, 1, 1);

The image vertex at 1,1 is at the cube vertex  1,1,-1

vertex( 1,1,1, 0, 1);

The image vertex at 0,1 is at the cube vertex  1,1,1

endShape();

End the shape

}

 

void mouseDragged() {

Header for mouseDragged

float rate = 0.01;

Used to scale the change in mouse positions

last = millis();

Set last to indicate time when something happened

rotx += (pmouseY-mouseY) * rate;

Increment rotx

roty += (mouseX-pmouseX) * rate;

Increment roty

}

Close mouseDragged

Things to Look Up

The Processing documentation provides a basic tutorial on 3D at https://processing.org/tutorials/p3d/. It includes a description of what it means to be a left-handed coordinate system and the diagram shown in Figure 10-9.

A schematic representation of the cube has a 3-dimensional view. It has a three-coordinate axis of X, y, and Z axes with center points 0, and 0. X and Y are the real values and Z has the imaginary values.

Figure 10-9

Orientation of x, y, and z axes

You will become comfortable with the coordinate system if and when you build on my examples and examples in the Processing documentation and when you design and build your own projects.

Investigate how to make custom shapes and how to apply textures. Review the use of transformations, especially using scale after creating a custom shape using unit dimensions.

Proceed slowly and study the different ways to use the camera function to change the various parameters for calculating the display and how to specify the different ways of lighting. At the risk of repeating myself, proceed step by step. If you change shapes and make transformations and change camera parameters all at once, you probably will get confused.

How to Make This Your Own

You certainly can do your own cheap tricks, making objects move against interesting flat backgrounds. You can use your own pictures for textures on rotating cubes or give the user the option to use images on the local computer or on the Web.

A good next challenge would be bouncing things in a five-sided box. You can decide if there is an invisible sixth side. Another challenge would be a shooter game, perhaps a version of slingshot.

One addition for the rotating cube could provide users a way to upload images from their own local computer. See Chapter 7 for background on how to do this.

I have provided additional examples in the source code section. These include a simple snowman consisting of just three spheres, the simple snowman on a box, the original rolling ball around the Alhambra, a dreidel, and a (crude) solar system.

The dreidel is shown in Figure 10-10. During Hanukkah, people play a gambling game. The player spins the dreidel, a top with four Hebrew letters. When it stops, the letter determines if the spinner takes the whole pot (often Hanukkah gelt, or foil-covered chocolate candy), half the pot, puts in one, or does nothing. In my sketch, the dreidel, made up of textured and colored 3D parts, spins and slows down after a random amount of rotation. There is an adjustment at the end so the final letter is facing directly forward, although I also print the result to the console. Spinning can be restarted using any key, and the mouse can be used to rotate the spinning or stationary dreidel around the x or y axes.

A photo of the dreidel adjusts window has the dreidel with a handle. A spinning top has four sides and each side has a text. It has an edge while spinning it, the sides of the dreidel have been changed. It has a text shin on one side and Gimel on the other side. The remaining two sides are not visible.

Figure 10-10

Dreidel (spinning top)

A student insisted that I include Pluto in a sketch of the solar system. I took this as a fine idea because of the challenge of depicting the Pluto object revolving in a different plane. However, a bigger challenge was that Pluto is very small relative to the other planets. The planets depicted are only very roughly proportional, with Pluto being the most out of proportion. A screenshot is shown in Figure 10-11. You need to look very closely to see the representation of Pluto.

An image of the solar system has a large round shape planet that is Sun and is surrounded by planets in the orbit. It has a dark background.

Figure 10-11

Solar system (planets not in proportion)

What You Learned

This chapter was an introduction to 3D using Processing. You learned about the 3D coordinate system and transformations such as translations and rotations. You learned about the 3D primitives, applying texture, and creating custom 3D shapes. Objects are positioned by transformations of the coordinate system. What you learned to do in 2D can apply to the 3D domain. This applies to the mouseDragged event and the mouseX, mouseY, pmouseX, and pmouseY variables and using millis to insert a wait for something to happen.

What’s Next

This is the last chapter! I hope this book was a satisfactory introduction to programming and the Processing language and it made you want to create your own sketches.

In the Appendix, I provide an introduction to p5js, a companion project by the Processing development community to provide a way to publish (disseminate) Processing sketches on the Web. That is, it is a way to use JavaScript with Processing functions. I offer three examples. The first is my familiar Daddy Logo. The second is a coin-toss type of application, alternating between two photos taken when we were in the Wall Street area in New York City. The third is a 3D helix that can be rotated just as the cube is rotated.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.10.36