chaine-Body

Idle:

Blinking:

Mouth open:

My first idea (scratched):

Idea that I pursued:

Starting off this project, I was debating between two ideas. One where I would play around with rotations, and the other where I would manipulate the eyes depending on the facial expression. (especially the mouth) I decided that I would like to express emotions creatively, so I decided to pursue that. At first, I wanted the face to express sadness by crying whenever the mouth opened and the eyebrows were furrowed, but I had some issues with creating the tears to be more fluid-like, so it kind of just looked like a scary person shooting out spherical beams. I got some new ideas with that mistake; and I thought it would be cool for the person to transform into an evil medusa with red pupils when you made a scary face. Also, I ended up removing the mouth entirely because the focus was in the eyes. Aesthetically speaking, I wish I played around with the eyes' outlines more because I think it's quite distracting especially since none of the other shapes have an outline. I also wish I could diversify the eyes' shapes to make it more evil-looking and scary. (possibly?) Functionally, I wish the eyes would have shot out beams rather than circles or better conveyed the user being frozen by her stone-turning eye beams. In terms of the relationship between the facial motions and the treatment of it, I would say that the motions came first and I only adapted to the possibilities of those motions.

//
// a template for receiving face tracking osc messages from
// Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker
//
// 2012 Dan Wilcox danomatika.com
// for the IACD Spring 2012 class at the CMU School of Art
//
// adapted from from Greg Borenstein's 2011 example
// http://www.gregborenstein.com/
// https://gist.github.com/1603230
//
import oscP5.*;
OscP5 oscP5;
 
 
//I used Dan Shiffman's box2d adaptation: https://github.com/shiffman/Box2D-for-Processing
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.joints.*;
 
Box2DProcessing box2d;
 
ArrayList<Boundary> boundaries;
ArrayList<ParticleSystem> systems;
ArrayList<Box> boxes;
 
// num faces found
int found;
 
// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();
 
// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;
 
PImage outerEye;
 
float aVelocity = .05;
boolean mouseVelocity = false;
float angle = 0;
float amplitudeX = 200;
float amplitudeY = 200;
float theta = 0;
PVector location;
float centerX;
float centerY;
 
void setup() {
  size(640, 480);
  frameRate(30);
  smooth();
 
  box2d = new Box2DProcessing(this);
  box2d.createWorld();
 
  box2d.setGravity(0, -20);
 
  systems = new ArrayList<ParticleSystem>();
  boundaries = new ArrayList<Boundary>();
  boxes = new ArrayList<Box>();
 
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
 
 
  outerEye = loadImage("circlebig.png");
 
  //boundaries.add(new Boundary(0,490,1280,10,0));
 
}
 
void draw() {  
  semiTransparent();
 
  box2d.step();
 
  for (ParticleSystem system: systems) {
    system.run();
 
    int n = (int) random(0, 2);
    system.addParticles(n);
  }
 
  for (Boundary wall: boundaries) {
    wall.display();
  }
 
  float varVelocity = calcVelocity(aVelocity);
  PVector angularVelocity = new PVector (angle, varVelocity);
  PVector amplitude = new PVector (amplitudeX, amplitudeY);
  PVector location = calculateCircle(angularVelocity, amplitude);
  //PVector centerCircle = calculateCenter(centerX, centerY);
 
  pushMatrix();
  if(found > 0) {
    drawOscillatingX(location);
  }
  popMatrix();
 
  for (Box b: boxes) {
    b.display();
  }
}
 
void semiTransparent() {
  rectMode(CORNER);
  noStroke();
  float backColor = map (mouthHeight, 1, 5, 255, 0);
  fill(backColor, backColor, backColor, 40);
  rect(0,0, width, height);
  stroke(0);
  noFill();
}
 
//basics of eye blink, iris movement from: https://raw.githubusercontent.com/jayjaycody/ComputationalCameras/master/wk3_Face/jai_face_keyPressComplexity/jai_face_keyPressComplexity.pde
 
float calcVelocity(float aVelocity) {
  float velocity = aVelocity;
  if (mouseVelocity == false) {
  }
  if (mouseVelocity == true) {
    velocity = map(mouseX, 0, width, -1, 1);
  }
  return velocity;
}
 
PVector calculateCircle (PVector angularVelocity, PVector amplitude) {
  float x = amplitude.x * cos (theta);
  float y = amplitude.y * sin (theta);
  location = new PVector (x, y);
  theta += angularVelocity.y;
  return location;
}
 
PVector calculateCenter (float centerX, float centerY) {
  PVector centerCircle = new PVector (centerX, centerY);
  return centerCircle;
}
 
void drawOscillatingX (PVector location) {
 
    float mouthScalar = map(mouthWidth, 10, 18, 0, 1.5); // make a scalar for location.x as a function of mouth
    location.mult(mouthScalar);
 
    float newPosX = map (posePosition.x, 0, 640, 0, width);
    float newPosY = map(posePosition.y, 0, 480, 0, height);  
    translate(width - newPosX, newPosY-100);
    scale(poseScale*.3);
    float irisColR = map (mouthHeight, 1, 5, 102, 204);
    float irisColG = map (mouthHeight, 1, 5, 204, 51);
    float irisColB = map (mouthHeight, 1, 5, 255, 0);
 
    float leftEyeMove = map(location.x, - amplitudeX, amplitudeX, -25, 33);
    pushMatrix();
    translate (leftEyeMove, 0);
    //Left iris
    fill(irisColR, irisColG, irisColB);
    noStroke();
 
    float eyeMult = map (mouthHeight, 1, 5, 1, 2);
 
    float irisSizeL = map (eyeLeft, 2, 3.5, 0, 50);
    ellipse(-100, 0, irisSizeL * eyeMult, irisSizeL * eyeMult);
 
    ////LeftPupil
    float eyeOutlineCol = map (mouthHeight, 1, 5, 0, 255);
 
    popMatrix();
 
    float rightEyeMove = map(location.x, - amplitudeX, amplitudeX, -33, 25);
    pushMatrix();   
    translate(rightEyeMove, 0);
    //right EYE
    //Right Iris
    fill(irisColR, irisColG, irisColB);
    noStroke();
 
    float irisSizeR = map (eyeRight, 2, 3.5, 0, 50);
    ellipse(100, 0, irisSizeR * eyeMult, irisSizeR * eyeMult);
 
    //Right Pupil
    stroke(eyeOutlineCol); 
    popMatrix();
    noFill();
 
 
    //get eye informatio and set scalar
    float blinkAmountRight = map (eyeRight, 2.5, 3.8, 0, 125);
    float blinkAmountLeft = map (eyeLeft, 2.5, 3.8, 0, 125);
 
 
    float eyeMultiplier = map (mouthHeight, 1, 5, 1, 3);
    // right eye size, blink and movement
    ellipse (100, 0, amplitudeX *.6, blinkAmountRight * eyeMultiplier); //scalar added to eyeHeight
    if (eyeRight < 2.7) {
      fill(255, 230, 204);
      ellipse (100, 0, amplitudeX *.6, blinkAmountRight*1.6 * (4 * eyeMultiplier/5)); //scalar added to eyeHeight
      noFill();
    }
 
    //left eye size, blink, and movement
    ellipse (-100, 0, amplitudeX *.6, blinkAmountLeft * eyeMultiplier); 
    if (eyeLeft < 2.7) {
      fill(255, 230, 204);
      ellipse (-100, 0, amplitudeX *.6, blinkAmountLeft*1.6 * (4 * eyeMultiplier/5)); //scalar added to eyeHeight
      noFill();
    }
 
    if (mouthHeight > 3.3) {
      //float mapScale = map (poseScale, 0, 4, 0, 1);
      pushMatrix();
      //translate(posePosition.x, posePosition.y);
      //scale(poseScale);
      Box p = new Box((width - posePosition.x - 100), (posePosition.y - 50));
      Box q = new Box((width - posePosition.x + 100), (posePosition.y - 50));
      boxes.add(p);
      boxes.add(q);
      popMatrix();
    }
 
}
 
public void found(int i) {
  println("found: " + i);
  found = i;
}
 
public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}
 
public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}
 
public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}
 
public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}
 
public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}
 
public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}
 
public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}
 
public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}
 
public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}
 
public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}
 
public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}
 
// all other OSC messages end up here
void oscEvent(OscMessage m) {
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}