miyehn-AnimatedLoop

Finally finally here’s my GIF!

(It’s huge , like 12.8MB. Below are two screenshots)

It was so hard to come up with a concept I’m happy with. In the beginning I had no idea what I wanted to do except I was awed by the idea of turning a circle into a triangle by turning a cone sideways. So I started by thinking about what other interesting 3D shapes could look entirely different when viewed from different angles.

 

I tried implementing some of them to see a rough effect but they didn’t look great so I started over. I tried to make something harmonic but that double helix looked so rigid. Well at least I learned to implement a double helix and that ortho() is a really cool function to use 3D objects in 2D designs …. Maybe these tricks will be useful in some other projects.

I then started playing with materials and lights in Processing and found my new interest. I wish I could animate light sources and play with lights and shadows, but I soon realized Processing can’t simulate cast shadows. So eventually I came up with what I have now, which is a moving creature occasionally covering up the light source (and itself, too) so only part of itself is visible in the light.

I followed Golan’s advice to let the creature run off canvas and intentionally set sphereDetail() to some low value, as if the creature has scales. They’re apparently not perfectly modeled and controlled by me and there’re small glitches here and there… I’m still not sure if they add to the organic feel of the creature, or just look like glitches…. For a one-week project I’m happy with it for sure though. If I really had more time I’ll probably improve something else first anyway, such as replacing its head by some actual head-looking shapes, instead of just having a big ball.

Below is the code I have. When it begins to run the first ~100 frames are buggy. From frame 361 to 720 makes a good loop, though.

 

PVector[] rr;
float[] thickness;
int ind;
int len = 160;
float bigness = 2.0;
 
void setup_() {
  size(600, 600, P3D);
  frameRate(50);
  //ortho();
  noStroke();
  sphereDetail(14);
  rr = new PVector[len];
  thickness = new float[len];
  for (int i=0; i<len; i++) {
    rr[i] = new PVector();
    thickness[i] = 80;
  }
}
 
void draw_() {
  background(30, 29, 40);
 
  pushMatrix();
  translate(width/2, height/2);
  scale(bigness); 
  //set up lightings and materials
  fill(255);
  ambientLight(30, 29, 40);
  lightSpecular(255, 255, 255);
  specular(0, 80, 80);
  shininess(5.0);
  pointLight(220, 189, 101, 200*sin(radians(frameCount)), 200*cos(radians(frameCount)), 0);
 
  updateTail();
  float x = 200*sin(2*radians(frameCount));
  float y = 200*cos(3*radians(frameCount));
  float z = 60*sin(5*radians(frameCount));
  rr[ind] = new PVector(x, y, z);
  int tmp = prev(ind);
  while (tmp!=ind) {
    drawPoint(rr[tmp].x, rr[tmp].y, rr[tmp].z, thickness[tmp]);
    tmp = prev(tmp);
  }
  ind = next(ind);
  popMatrix();
 
}
 
void updateTail() {
  int i=prev(ind);
  int j=0;
  while (i!=ind) {
    float tmp = map(j, 0, len, 0, 1);
    float tmp2 = function_BrycePolynomial(tmp, 3);
    thickness[i] = map(tmp2*len, len, 0, 0, 80*bigness);
    i=prev(i);
    j++;
  }
}
 
int prev(int i) {
  if (i==0) return rr.length-1;
  else return i-1;
}
int next(int i) {
  if (i==rr.length-1) return 0;
  else return i+1;
}
 
void drawPoint(float x, float y, float z, float size) {
  pushMatrix();
  translate(x, y, z);
  sphere(size/2);
  popMatrix();
}
 
float function_BrycePolynomial (float x, int n){
  //functionName = "Bryce's Cubic";
 
  float xnm1 = pow (x, n-1); 
  float xn = xnm1 * x; 
 
  return n*xnm1 - (n-1)*xn; 
}