p5.zip

this is my code file that contains code and 3D model I used.

For this project, I want to explore some 3D stuff with p5 and webGL, so I can’t use the P5 editor since I am importing an obj file.

I wan to import a 3D mesh that use face mesh to change its animation. The mesh itself is in a looping rotation, but the face mesh can change its rotation angle and direction. when I open my mouse, it changes color, scale, and if I open my mouse very big it will explode to particles.

documentation video:

https://youtu.be/xj2SQuazkDc

Code in my file:

// Face-controlled 3D model visualization with mouth detection
let faceMesh;
let video;
let faces = [];
let objModel;
let rotationAngle = 0;
let targetScale = 2.5;
let currentScale = 2.5;
let strokeColor;
let targetColor;
let originalVertices = [];
let lastTargetScale = 2.5; // Track when target changes, not current

function preload() {
  faceMesh = ml5.faceMesh({ maxFaces: 1, refineLandmarks: false, flipHorizontal: false });
  objModel = loadModel('model.obj', true, modelLoaded);
}

function modelLoaded() {
  if (objModel.vertices) {
    originalVertices = objModel.vertices.map(v => createVector(v.x, v.y, v.z));
  }
}

function setup() {
  createCanvas(640, 480, WEBGL);
  video = createCapture(VIDEO);
  video.size(640, 480);
  video.hide();
  faceMesh.detectStart(video, gotFaces);
  strokeColor = color(255);
  targetColor = color(255);
}

function draw() {
  background(0);
  rotationAngle += 0.005;
  
  let isMouthOpen = false;
  let isMouthVeryOpen = false;
  
  if (faces.length > 0) {
    let face = faces[0];
    let mouthHeight = dist(face.keypoints[13].x, face.keypoints[13].y, 
                           face.keypoints[14].x, face.keypoints[14].y);
    isMouthOpen = mouthHeight > 10;
    isMouthVeryOpen = mouthHeight > 20;
    
    // Scale animation
    targetScale = isMouthOpen ? 10.0 : 2.5;
    
    // Color animation - only when target CHANGES (not during lerp)
    if (targetScale !== lastTargetScale) {
      targetColor = color(random(100, 255), random(100, 255), random(100, 255));
      lastTargetScale = targetScale;
    }
    
    currentScale = lerp(currentScale, targetScale, 0.05);
    strokeColor = lerpColor(strokeColor, targetColor, 0.2);
    
    // Draw model
    let nose = face.keypoints[1];
    let rotationY = map(nose.x, 0, width, -PI / 4, PI / 4);
    let rotationX = map(nose.y, 0, height, -PI / 6, PI / 6);
    
    push();
    rotateX(PI);
    rotateY(rotationAngle);
    rotateY(rotationY);
    rotateX(rotationX);
    scale(currentScale);
    
    if (isMouthVeryOpen && originalVertices.length > 0) {
      drawRandomizedModel();
    } else {
      noFill();
      stroke(strokeColor);
      strokeWeight(1);
      model(objModel);
    }
    pop();
  }
}

function drawRandomizedModel() {
  stroke(strokeColor);
  strokeWeight(3);
  beginShape(POINTS);
  for (let v of originalVertices) {
    vertex(v.x + random(-30, 30), v.y + random(-30, 30), v.z + random(-30, 30));
  }
  endShape();
}

function gotFaces(results) {
  faces = results;
}

*code modified by Claude code.