Final Draft Progress 2 – Developing Concept

In the time from my previous draft, I actually made two more draft programs and solidified my concept. Essentially, I am taking inspiration from Nam-June Paik’s piece TV Garden. Essentially, using the algorithms from cellular automata and the game of life, the program will develop a garden filled with several different types of flowers. The user will be able to tweak some of the parameters for the game of life to change how the flowers will grow and die on the tile. I also plan to add some user interaction by changing the Hydra visuals, but these will be somewhat limited due to how Hydra visuals are made. The second sketch focused on using the webcam aspect from Hydra and creating visuals that are intended to look like an old RGB TV overlayed on the camera. There is a slider there to modify the glitchiness of the webcam visual. The third draft focuses on developing the concept, creating 3D flowers and a basic grid for the cellular automata aspect. Here are the two sketches:

The flowers are a complex shape created using beginShape() that plots each vertex via a sine and cosine graph mapped to polar coordinates. Each vertex is made four at a time, to create a rectangle. This is repeated throughout the algorithm until the full flower is created. I also had to map these coordinates to a UV map to allow the overlaying of textures onto the flower, which are Hydra visuals. There are also two other types of flowers that are created with essentially the same algorithm, although a little simpler since they do not have faces created, only the vertices. These vertex points then have either a 2D repeating letter or symbol laid onto it or a simple block, creating flowers that are made out of text and blocks. These serve to add more variety to the garden, and also lessen the reliance on the Hydra visual flowers, which are extremely resource-intensive for the program and slows it down significantly. The code for the Hydra flowers can be seen here:

class Flower {
  constructor(x, y, z) {
    this.origin = createVector(x, y, z+15);
    this.v = []; //holds all the vertices
    this.rows = 15; //this is essentially the "resolution" of the flower, its polygon count
    this.cols = 30;
    this.minX = 1000000;
    this.maxX = -1000000;
    this.minY = 1000000;
    this.maxY = -1000000;
    this.choice = random(1) //used to choose one of the hydra canvases randomly
  }
  generate() {
    for (let theta = 0; theta < this.rows; theta++) { //create the coordinates for each vertex
      this.v.push([]);
      for (let phi = 0; phi < this.cols; phi++) { //r essentially maps x and y to polar coordinates
        let r =
          ((60 * pow(abs(sin(((5 / 2) * phi * 360) / this.cols)), 1) + 200) *
            theta) /
          this.rows;
        let x = r * cos((phi * 360) / this.cols);
        let y = r * sin((phi * 360) / this.cols);
        let z =
          this.vShape(180, r / 100, 0.8, 0.2, 1.5) -
          200 +
          this.bumpiness(2.5, r / 100, 12, (phi * 360) / this.cols);

        let pos = createVector(x, y, z);
        this.v[theta].push(pos);

        this.minX = min(this.minX, x); //this will be used to find the min-max range for the sin and cos waves, which will be needed to map the vertices to a UV map
        this.maxX = max(this.maxX, x);
        this.minY = min(this.minY, y);
        this.maxY = max(this.maxY, y);
      }
    }
  }
  show(pg,pg2) {
    push();
    if(this.choice <= 0.5) {
      texture(pg)
    } else {
      texture(pg2)
    }
    translate(this.origin);
    scale(0.1)
    for (let theta = 0; theta < this.v.length; theta++) { //grabs a vertex and three adjacent ones, which will combine to create a rectangle face
      for (let phi = 0; phi < this.v[theta].length; phi++) {
        if (theta < this.v.length - 1 && phi < this.v[theta].length - 1) {
          beginShape();
          vertex(
            this.v[theta][phi].x,
            this.v[theta][phi].y,
            this.v[theta][phi].z,
            map(this.v[theta][phi].x, this.minX, this.maxX, 0, 1), //uv mapping for texture
            map(this.v[theta][phi].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta + 1][phi].x,
            this.v[theta + 1][phi].y,
            this.v[theta + 1][phi].z,
            map(this.v[theta + 1][phi].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta + 1][phi].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta + 1][phi + 1].x,
            this.v[theta + 1][phi + 1].y,
            this.v[theta + 1][phi + 1].z,
            map(this.v[theta + 1][phi + 1].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta + 1][phi + 1].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta][phi + 1].x,
            this.v[theta][phi + 1].y,
            this.v[theta][phi + 1].z,
            map(this.v[theta][phi + 1].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta][phi + 1].y, this.minY, this.maxY, 0, 1)
          );
          endShape(CLOSE);
        } else if (
          theta < this.v.length - 1 &&
          phi == this.v[theta].length - 1
        ) {
          beginShape(); //the first and last values are disconnected, because an array overflow would happen, so we have to manually create a second shape that closes the gap
          vertex(
            this.v[theta][phi].x,
            this.v[theta][phi].y,
            this.v[theta][phi].z,
            map(this.v[theta][phi].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta][phi].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta][0].x,
            this.v[theta][0].y,
            this.v[theta][0].z,
            map(this.v[theta][0].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta][0].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta + 1][0].x,
            this.v[theta + 1][0].y,
            this.v[theta + 1][0].z,
            map(this.v[theta + 1][0].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta + 1][0].y, this.minY, this.maxY, 0, 1)
          );
          vertex(
            this.v[theta + 1][phi].x,
            this.v[theta + 1][phi].y,
            this.v[theta + 1][phi].z,
            map(this.v[theta + 1][phi].x, this.minX, this.maxX, 0, 1),
            map(this.v[theta + 1][phi].y, this.minY, this.maxY, 0, 1)
          );
          endShape(CLOSE);
        }
      }
    }
    pop();
  }

  vShape(A, r, a, b) { //creates the downward curve on the z-axis towards the center
    return A * pow(Math.E, -b * pow(abs(r), 1.5)) * pow(abs(r), a);
  }

  bumpiness(A, r, f, angle) { //creates "bumps" on the flower petals, so they are not completely flat
    return 1 + A * pow(r, 2) * sin(f * angle);
  }
}

It is a very long algorithm that has to be called each draw() frame, so it ends up slowing the program a lot. This is more apparent the more of these flowers there are. I greatly lowered the polygon count of these flowers, but it still has framerate problems, albeit much less than before. Ideally, these would be lowered even more, but too much detail is lost if they are lowered more, and the shape will start not resembling a flower.

The game of life aspect I used is very similar to our class example, just modified to show the flowers on top of the cells that are alive. Because it would overwhelm the program, the next generation of cells has to be done manually, with the press of a button by the user. Because of the resource intensiveness of the flowers, the grid is very small, only 5×5, which creates uninteresting patterns oftentimes.

My next steps will be to try to see if I can optimize the flowers even more, to reclaim more framerate for the program, which will hopefully allow me to create a larger grid to use. I am going to try to experiment with p5.framebuffers instead of p5.graphics, which are supposed to have better performance when working with WEBGL. After that, I will add the user interaction, which will be the modifying of the game of life rules, and finally brush up on visuals. If I have the time, I want to also try and implement more Hydra visuals that the Hydra instances can cycle through, so there is more variety than the current two that are there. I am not quite sure how to implement this yet, so I will have to experiment somewhat. I don’t want to create too many hydra canvases running at once, which would slow the program even more, so I might instead implement some sort of state machine that will cycle through different visuals. I also wanted to add some grass or other greenery so the flat plane is not as visible, but I think I won’t be able to add this due to performance. The camera is also offset right now, and I need to move it so it is properly looking at the garden. I’ve been having trouble doing this as of now, but hopefully, it won’t be too difficult to find a fix. One last thing I am considering is adding TVs of some sort, to further show how this program’s influence from TV Garden. This is a low priority for now though.

Leave a Reply

Your email address will not be published. Required fields are marked *