# 3D Rotation Question

**URL:** https://discourse.processing.org/t/3d-rotation-question/26438
**Category:** Coding Questions
**Created:** [December 22, 2020, 10:47am UTC](https://discourse.processing.org/t/3d-rotation-question/26438 "2020-12-22T10:47:42Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![RFullum](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/rfullum/32/10454_2.png) [@RFullum](https://discourse.processing.org/u/RFullum)
#### Post date: [December 22, 2020, 10:47am UTC](https://discourse.processing.org/t/3d-rotation-question/26438/1 "2020-12-22T10:47:42Z")

</div>

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:

```auto
  // 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:

> <https://twitter.com/i/status/1337708248410025984>

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!

---

<div class="post-metadata">

### Author: ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)
#### Post date: [December 22, 2020, 11:22am UTC](https://discourse.processing.org/t/3d-rotation-question/26438/2 "2020-12-22T11:22:32Z")

</div>

Hello,

These are references:

> **[Tutorials \\ Processing.org](https://processing.org/tutorials/)**

> **[3D Transformations in Processing](https://behreajj.medium.com/3d-transformations-in-processing-de11acdd1fbc)**
>
> This tutorial explores how translation, rotation and scale — taken together under the umbrella term affine transformations — work in…

> **[3D Rotations in Processing (Vectors, Matrices, Quaternions)](https://behreajj.medium.com/3d-rotations-in-processing-vectors-matrices-quaternions-10e2fed5f0a3)**
>
> This tutorial introduces how to rotate objects in 3D beyond Euler angles; to do this, it looks at the basics of matrices and quaternions…

> **[Jeremy Behreandt – Medium](https://behreajj.medium.com/)**
>
> Read writing from Jeremy Behreandt on Medium. Creative coder from Wisconsin, USA. Every day, Jeremy Behreandt and thousands of other voices read, write, and share important stories on Medium.

`:)`

---

<div class="post-metadata">

### Author: ![RFullum](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/rfullum/32/10454_2.png) [@RFullum](https://discourse.processing.org/u/RFullum)
#### Post date: [December 22, 2020, 3:29pm UTC](https://discourse.processing.org/t/3d-rotation-question/26438/3 "2020-12-22T15:29:00Z")

</div>

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.

```auto
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

---

<div class="post-metadata">

### Author: ![rbrauer](https://avatars.discourse-cdn.com/v4/letter/r/838e76/32.png) [@rbrauer](https://discourse.processing.org/u/rbrauer)
#### Post date: [December 22, 2020, 5:42pm UTC](https://discourse.processing.org/t/3d-rotation-question/26438/4 "2020-12-22T17:42:16Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![RFullum](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/rfullum/32/10454_2.png) [@RFullum](https://discourse.processing.org/u/RFullum)
#### Post date: [December 22, 2020, 6:00pm UTC](https://discourse.processing.org/t/3d-rotation-question/26438/5 "2020-12-22T18:00:02Z")

</div>

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. 🤔😀

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

---

<div class="post-metadata">

### Author: ![rbrauer](https://avatars.discourse-cdn.com/v4/letter/r/838e76/32.png) [@rbrauer](https://discourse.processing.org/u/rbrauer)
#### Post date: [December 22, 2020, 8:30pm UTC](https://discourse.processing.org/t/3d-rotation-question/26438/6 "2020-12-22T20:30:21Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![rbrauer](https://avatars.discourse-cdn.com/v4/letter/r/838e76/32.png) [@rbrauer](https://discourse.processing.org/u/rbrauer)
#### Post date: [December 22, 2020, 10:33pm UTC](https://discourse.processing.org/t/3d-rotation-question/26438/7 "2020-12-22T22:33:11Z")

</div>

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

```auto
{
  //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();
  }
}

```

---

<div class="post-metadata">

### Author: ![RFullum](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/rfullum/32/10454_2.png) [@RFullum](https://discourse.processing.org/u/RFullum)
#### Post date: [December 23, 2020, 12:13am UTC](https://discourse.processing.org/t/3d-rotation-question/26438/8 "2020-12-23T00:13:42Z")

</div>

@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!

---

<div class="post-metadata">

### Author: ![RFullum](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/rfullum/32/10454_2.png) [@RFullum](https://discourse.processing.org/u/RFullum)
#### Post date: [December 24, 2020, 12:05am UTC](https://discourse.processing.org/t/3d-rotation-question/26438/9 "2020-12-24T00:05:15Z")

</div>

FWIW, i figured it out. I went to the [Processing GitHub](https://github.com/processing/processing/blob/94144be998354ecf7a06d5301b7b4fab72df724c/core/src/processing/core/PVector.java) to see how `PVector.heading()` worked. It’s just `atan2(y, x)`.

The 2D rotations work like:

```auto
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.

```auto
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. 🤘

Cheers!
