3D Rotation Question

Hey all,

I used the Flocking example as a starting point, and made a flocking sketch that works in 3D, with multiple flocks being influenced by FFT band amplitudes. The problem i’m running into is getting each boid to rotate on the Z axis also as it flies in that direction.

The documentation looks like rotate( ) works for 2D rotation. There’s also a rotateZ( ) method, but I’m not sure how to combine a rotate( ) + rotateZ( ), or how to determine the correct amount for rotateZ( ). PVector.heading( ) only calculates 2D.

Here’s the method controlling the rotation:

  // Draws triangle of boid pointing in direction of velocity
  // Currently only rotates in XY directions. Needs to also rotate 
  // to face Z direction
  void render()
  {
    float theta = velocity.heading() + radians(90);
    
    fill(c1);      // Boid color
    //stroke(c2);    // Boid outline color
    noStroke();    // No outline runs smoother.
    
    // Individual boid position translation matrix
    pushMatrix();
      translate(position.x, position.y, position.z);
      ps.updateOrigin(new PVector(0.0f, r * 2.0f, 0.0f));
      rotate(theta);                                          // **HOW TO DO 3D ROTATION?**
      beginShape(TRIANGLES);
      vertex(0, -r * 2.0f);
      vertex(-r, r * 2.0f);
      vertex(r, r * 2.0f);
      endShape();
      
      // Adds particle out the rear of boid every jetFrames number of frames
      if ( (frameCount % jetFrames) == 0 )
      {
        ps.addParticle();
      }
      
      ps.run();
    popMatrix();
  }
  

Here’s a video of it running in its current state:

Is there a simple/straightforward way to incorporate that third axis rotation? Or break rotate( ) out into rotateX( ), rotateY( ), and rotateZ( )?

(if you need to see more of the code, let me know. It’s a large class, and the sketch has 5 classes. Didn’t want to overload the post.)

Thanks in advance!

Hello,

These are references:

:)

2 Likes

Thanks much!

After reading the 3D Rotation links (and re-reading a few times), i got a better grasp of what’s happening for each rotation axis and rotation( ). (It’s been a LONG time since i took a math class).

I broke out the rotate(theta) to a theta for each axis and i’m definitely getting the boids rotating to point in 3D directions! Some do appear to be flying backwards/sideways sometimes, which is a bit confusing (why only some, not all… or why not backwards on a specific axis?) But, i’ll do some more digging to make sure my calculations and implementations are correct. Making the Boids 3D pyramids might help visualize it, so i’ll work on that.

void render()
  {
    //float theta = velocity.heading() + radians(90);
    
    PVector v1 = new PVector(velocity.x, velocity.y);
    PVector v2 = new PVector(velocity.x, velocity.z);
    PVector v3 = new PVector(velocity.y, velocity.z);
    
    float thetaZ = v1.heading() + radians(90);
    float thetaY = v2.heading() + radians(90);
    float thetaX = v3.heading() + radians(90);
    
    fill(c1);      // Boid color
    //stroke(c2);    // Boid outline color
    noStroke();    // No outline runs smoother.
    
    // Individual boid position translation matrix
    pushMatrix();
      translate(position.x, position.y, position.z);
      ps.updateOrigin(new PVector(0.0f, r * 2.0f, 0.0f));
      //rotate(theta);
      rotateZ(thetaZ);
      rotateY(thetaY);
      rotateX(thetaX);
      
      beginShape(TRIANGLES);
      vertex(0, -r * 2.0f);
      vertex(-r, r * 2.0f);
      vertex(r, r * 2.0f);
      endShape();
      
      // Adds particle out the rear of boid every jetFrames number of frames
      if ( (frameCount % jetFrames) == 0 )
      {
        ps.addParticle();
      }
      
      ps.run();
    popMatrix();
  }

Thanks again @glv ! Cheers

2 Likes

You could also try drawing them based on their current position and velocity each frame instead of trying to transform to the correct orientation and then drawing a standard shape. Use a method that takes the position and velocity as parameters and draws a triangle (or whatever) with the “front” vertex at position + velocity.

So, if i’m understanding you @rbrauer, have the vertexes of the shape calculated each frame based on position + velocity. So the lead point would always be at position and the base points would be at, say,

vertex( (positionX - velocityX) * lengthOfSide, (posY - vY) * length, (posZ - vZ) * length );

I’m half making sure i understand your suggestion and half thinking out loud. :thinking::grinning:

**edited to say the vertex math needs offsets and isn’t exactly right, but it’s the idea i’m going for.

I did 3D flocking once (can’t find the code at the moment unfortunately) and I think I used something like that. I don’t remember exactly how it went, but I guess you would need a vector perpendicular to the velocity as well.

1 Like

This math probably is not entirely correct, but it kind of works…

{
  //https://github.com/processing/processing/issues/5476
  System.setProperty("jogl.disable.openglcore", "false");

  //https://discourse.processing.org/t/x11util-display-shutdown/7292/8
  Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    public void run() {
      println(" ");
      System.err.close();
    }
  }));
}

Thing test;

void setup() {
  size(1920, 1080, P3D);
  noFill();
  stroke(255);
  strokeWeight(2);
  test = new Thing(
    new PVector(width * 0.5, height * 0.5, 0));
}

void draw() {
  beginCamera();
  camera();
  translate(width*0.5, height*0.5, 0);
  rotateY(frameCount * 0.01);
  translate(-test.pos.x, -test.pos.y, -test.pos.z);
  endCamera();

  background(0);
  test.tick();
  test.disp();
}

class Thing {
  ArrayList<PVector> trail;
  PVector pos, vel, acc;
  
  Thing(PVector pos) {
    this.pos = pos;
    vel = new PVector();
    acc = PVector.random3D();
    trail = new ArrayList<PVector>();
  }
  
  void tick() {
    if(random(1000) > 950) {
      acc = PVector.random3D();
    }
    vel.add(acc);
    vel.mult(0.95);
    pos.add(vel);

    trail.add(pos.copy());
    if(trail.size() > 1000) {
      trail.remove(0);
    }
  }
  
  void disp() {
    float scl = 200;
    
    PVector dir = vel.copy();
    dir.normalize();
    dir.mult(scl);
    dir.add(pos);
    
    PVector perp = pos.cross(dir);
    perp.normalize();
    perp.mult(scl);

    PVector left = PVector.mult(perp, -0.25);
    left.add(pos);
    PVector right = PVector.mult(perp, 0.25);
    right.add(pos);

    beginShape(TRIANGLES);
    vertex(dir.x, dir.y, dir.z);
    vertex(right.x, right.y, right.z);
    vertex(left.x, left.y, left.z);
    endShape(CLOSE);
    
    beginShape();
    for(PVector cur : trail) {
      vertex(cur.x, cur.y, cur.z);
    }
    endShape();
  }
}
1 Like

@rbrauer I’m happy with it not being entirely correct if it points them in basically the right direction. I’ll give it a look in the sketch tomorrow!

FWIW, i figured it out. I went to the Processing GitHub to see how PVector.heading() worked. It’s just atan2(y, x).

The 2D rotations work like:

float theta = velocity.heading() + radians(90);
rotate(theta);

Since rotate() is the same as rotateZ(), i just did a theta for the Z and Y axes.

float thetaZ = atan2(dir.y, dir.x) + radians(90);
float thetaY = atan2(dir.x, dir.z) + radians(90);

rotateZ(thetaZ);
rotateY(thetaY);

I’m not 100% certain why adding a thetaX and rotateX() breaks the rotation. It kinda makes sense when i close my eyes and visualize it. But, i’m not too concerned with figuring out the actual reason because it works!

@rbrauer Your code actually worked too, getting my boids to point in the correct direction. However, it broke my particle system shooting out the back of the boids. And before going down that rabbit hole figuring out how to fix that, i checked the Processing GitHub and had my idea. But, thanks again because it was oh-so-close. :metal:

Cheers!

1 Like