[edited] how to align an ofNode orientation with a vector?

hi, I’m creating a simple butterfly movement using ofNode.

ofNode butterfly;
ofPlanePrimitive lWing, rWing;

both of these wings are offset from the center and are parented to the butterfly object

	lWing.setParent(butterfly);
	rWing.setParent(butterfly);
	lWing.move(-110, 0,0);
	rWing.move(110, 0, 0);

before trying to make the wings flap, I’d like to make sure that each butterfly and its wings is aligned with its velocity

For debugging purpose I draw the head (using position+velocity) and tail (using position) of the butterfly in red and white respectively to get an idea of the orientation that they are supposed to have

I was trying to avoid going through all the maths so I tried the SetOrientation function

void Boid::update() {
	butterfly.move(vel);
	butterfly.setOrientation(vel);
}

But could see no change in alignment.

I’ve put the math on paper here :

But isn’t there a simpler solution using some built-in ofNode functions ?
I’m just trying to align my ofNode object with a velocity vector.

To make sure I got everything right I applied the maths very basically :

	// given base orientation (u) and goal orientation (v)
	glm::vec3 u = butterfly.getOrientationEulerDeg();
	glm::vec3 v = vel;

	// find the rotation axis (w) = the vector orthogonal to (u) and (v)
	glm::vec3 w;
	w.z = 1; // fix this else infinite solutions
	w.x = w.z * (u.z * v.y - u.y * v.z) / u.y * v.x - u.x * v.y; // used the property that u.w = 0 and v.w = 0
	w.y = w.z * (u.z * v.x - u.x * v.z) / u.x * v.y - u.y * v.x; // when w is orthogonal to u and v

	// now I calculate the angle (a) between (u) and (v)
	float a = acos(dot(u, v) / length(u)*length(v));

	// now use the rotation axis and the angle calculate to rotate my butterfly from u to v
	butterfly.rotateDeg(a, w);

but all my ofNode children disapeared. So I’m guessing there’s a bigger problem somewhere in my code. Because if I understand rotateDeg() right, whatever the angle and whatever the axis is, my objects shouldn’t fly away from their origin

edit : I’ve tried to reproduce this in a minimal environment and hit the same issues exactly so there must be something wrong with the approach mentioned here

I’ve managed to find a very simple solution using

butterfly.lookAt(pos+vel);

Which makes complete sense, understood some of my errors, that calling Rotate inside the update() function will of course cause chaos. I still have a few troubles understanding RotateAroundDeg() though and would love seeing any kind of tutorials, I’ve searched the function everywhere I could think of but didnt find any applications in 3D space

Hi, notice that ofNode::rotateAroundDeg makes the node to rotate around a certain point, changing its position but it will not change the orientation of the node. So you will note get the result you are looking for.
On top of that it is a cumulative function, so it rotates the node over the current rotation instead of setting it to a specific orientation.

But actually there is an quite simple way for solving the problem

Hey, I think that lookAt is not really what you need as it will change the orientation of the wings following some point, which can be a bit counter intuitive.
Check the following.


class ofApp : public ofBaseApp{
	
public:
	void setup();
	void update();
	void draw();
	
	ofNode body;
	ofPlanePrimitive wing;
	
	ofEasyCam cam;
	
};

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

	wing.setParent(body);
	
	glm::vec3 wingSize(100,100, 0);
	wing.set(wingSize.x, wingSize.y, 2, 2);
	
	//move all the vertices so these start at 0,0
	for(auto&v: wing.getMesh().getVertices())
	{
		v += wingSize/2.0;
	}
	
	
}

//--------------------------------------------------------------
void ofApp::update(){
	wing.setOrientation(glm::angleAxis(ofMap(sin(ofGetElapsedTimef()), -1, 1, -PI*0.25, PI*0.25), glm::vec3(0,1,0)));
}

//--------------------------------------------------------------
void ofApp::draw(){
	cam.begin();
	wing.draw();
	body.draw();
	cam.end();
}

ofNode will set its orientation, or rotate around its origin, so the trick is to move the vertices of the wing so these start at 0,0. By default, most of OF’s 3D primitives are centered in the origin.

1 Like

is moving the vertices what helps change the rotation center of the wings ?
I understand how that is more intuitive ! But waw that solution was totally out of my scope.
I made a small drawing to illustrate what I mean by rotation center
wingrot|500x500

I’ll decode your setOrientation function asap, there’s a few things I haven’t seen before so that oneliner is definitely food for thought. thanks a lot !

sort of. The center of rotation is the same, but instead we are drawing the mesh in a different position.

So this one liner

	wing.setOrientation(glm::angleAxis(ofMap(sin(ofGetElapsedTimef()), -1, 1, -PI*0.25, PI*0.25), glm::vec3(0,1,0)));

can be unscrambled as follows

// ofMap allows you to map a value from one range to another range.
// the parameters are (inputValue, inputMinValue, inputMaxValue, outputMinValue, outputMaxValue)
float angle = ofMap(sin(ofGetElapsedTimef()), -1, 1, -PI*0.25, PI*0.25);
//angleAxis is a glm function that creates a quaternion based on an angle (in radians) and an axis of rotation (which should be normalized).
glm::quat orientation = glm::angleAxis(angle, glm::vec3(0,1,0));
	wing.setOrientation(orientation);

Thanks a bunch, I’m having a last tiny bit of an issue, which is, to make sure that the butterfly is facing the direction it is going in I do have to make sure to put velocity in the mix and the best way I’ve found to do that is still

butterfly.lookAt(pos + vel);

this ensures that the butterfly ofNode stays in the direction of the velocity even when it changes. But when calling this it de-axes the wings on the YZ plane, when they were originally on the XY plane. I’ve spent the last hour trying to figure a way to move the plane the same way you did through each vertices and moving it to a XZ plane so that the lookAt() function de-axes it on the right plane, but I’m struggling to find a way.

is there a way to “redraw” the plane the same way you did using the for loop but on another plane ?

Hi, I am not following your question but do instead

butterfly.lookAt(butterfly.getGlobalPosition() + vel);

Which should keep the buterfly pointings towards the velocity.

I dont see that wing deaxis. if it is happening it might be that the wings parent is not the buterfly

I’ve put up a tiny example here to illustrate this

but in any case thanks a lot, I’m sure I’ll eventually find a turnaround

Hey,
I just looked at your example and I had to do some minor modifications to make it run.
The problem you have is with the axis of rotation which is set in the update.
I would recommend you to make a small gui and use these values as “offsets” for the rotation of the wings so you can properly align these. Or go deeper into the maths and find out which are the correct coordinates and axis. As far as I recall the ofNode’s lookAt function will make the node’s local Z axis to point towards the lookat point.

Also I would recommend that you dont use the pos variable and you rely on the butterfly’s node position.

1 Like

thanks a lot !