I have two Vec3s, Camera Forward and Turret Forward. Both of these vectors are on different planes where Camera Forward is based on a free-look camera and Turret Forward is determined by the tank it sits on, the terrain the tank is on, etc. Turret Up and Camera Up are rarely ever going to match.
My issue is as follows: I want the turret to be able to rotate using a fixed velocity (44 degrees per second) so that it always converges with the direction that the camera is pointed. If the tank is at a weird angle where it simply cannot converge with the camera, it should find the closest place and sit there instead of jitter around indefinitely.
I cannot for the life of me seem to solve this problem. I've tried several methods I found online that always produce weird results.
local forward = player.direction:rotate(player.turret, player.up)
local side = forward:cross(player.up)
local projection = self.camera.direction:dot(forward) * forward + self.camera.direction:dot(side) * side
local angle = math.atan2(forward.y, forward.x) - math.atan2(projection.y, projection.x)
if angle ~= 0 then
local dt = love.timer.getDelta()
if angle <= turret_speed * dt then
player.turret_velocity = turret_speed
elseif angle >= -turret_speed * dt then
player.turret_velocity = -turret_speed
else
player.turret_velocity = 0
player.turret = player.turret + angle
end
end
I would do it differently
obtain camera direction vector c in GCS (global coordinate system)
I use Z axis as viewing axis so just extract z axis from transform matrix
for more info look here understanding transform matrices
obtain turret direction vector t in GCS
the same as bullet 1.
compute rotated turret direction vectors in booth directions
t0=rotation(-44.0deg/s)*t
t1=rotation(+44.0deg/s)*t
now compute the dot products
a =dot(c,t)
a0=dot(c,t0)
a1=dot(c,t1)
determine turret rotation
if max(a0,a,a1)==a0 rotate(-44.0deg/s)`
if max(a0,a,a1)==a1 rotate(+44.0deg/s)`
[Notes]
this should converge to desired direction
the angle step should be resized to match the time interval used for update this
you can use any common coordinate system for bullets 1,2 not just GCS
in this case the dot product is cos(angle between vectors) because both c,t are unit vectors (if taken from standard transform matrix)
so if cos(angle)==1 then the directions are the same
but your camera can be rotated in different axis so just find the maximum of cos(angle)
After some more research and testing, I ended up with the following solution. It works swimmingly!
function Gameplay:moved_axisright(joystick, x, y)
if not self.manager.id then return end
local turret_speed = math.rad(44)
local stick = cpml.vec2(-x, -y)
local player = self.players[self.manager.id]
-- Mouse and axis control camera view
self.camera:rotateXY(stick.x * 18, stick.y * 9)
-- Get angle between Camera Forward and Turret Forward
local fwd = cpml.vec2(0, 1):rotate(player.orientation.z + player.turret)
local cam = cpml.vec2(1, 0):rotate(math.atan2(self.camera.direction.y, self.camera.direction.x))
local angle = fwd:angle_to(cam)
-- If the turret is not true, adjust it
if math.abs(angle) > 0 then
local function new_angle(direction)
local dt = love.timer.getDelta()
local velocity = direction * turret_speed * dt
return cpml.vec2(0, 1):rotate(player.orientation.z + player.turret + velocity):angle_to(cam)
end
-- Rotate turret into the correct direction
if new_angle(1) < 0 then
player.turret_velocity = turret_speed
elseif new_angle(-1) > 0 then
player.turret_velocity = -turret_speed
else
-- If rotating the turret a full frame will overshoot, set turret to camera position
-- atan2 starts from the left and we need to also add half a rotation. subtract player orientation to convert to local space.
player.turret = math.atan2(self.camera.direction.y, self.camera.direction.x) + (math.pi * 1.5) - player.orientation.z
player.turret_velocity = 0
end
end
local direction = cpml.mat4():rotate(player.turret, { 0, 0, 1 }) * cpml.mat4():rotate(player.orientation.z, { 0, 0, 1 })
player.turret_direction = cpml.vec3(direction * { 0, 1, 0, 1 })
end
I have exported some hair particules from Blender (a hairstyle). These are composed of several lines (GL_LINES). My openGL program displays these particules without any problem. Now I just want to apply light properties on these particules. Blender does not export the normals vectors so I need to compute them by myself. I know the following rule :
If we define a line segment as [AB] in two dimensions,
we have dx = xB - xA and dy = yB - yA, then the normals are N1(-dy, dx) and N2(dy, -dx).
I hope I did not make any mistake.
But I don't know the rule for a 3D space line segment definition if I add the z dimention in my line segment coordinates (for instance A(5, 2, 3) and B(0, 0, -5)).
Does anyone can help me?
Since Aki forgot that comments aren't answers:
Lines in 3D space don't have a normal. Technically, lines in 2D space don't have a normal either; they have two normals.
There are an infinite number of directions that are perpendicular to a line in 3D space. All of these normals are in the same plane, but with different directions. Without some more advanced algorithm (likely based on adjacent lines), there is no way to pick one of these normals over another.
If you assume that you can get two vectors to begin with, and it looks like that's what you are saying, call them v, w, to get a normal vector take the cross product. It's not a bad idea to normalize v, w to begin with, depending on the situation. The cross product can be given by:
v x w =(v_2w_3 - v_3w_2, v_3x_1 - v_1w_3, v_1w_2 - v_3w_1),
Here v_i is the ith component of v and so on. The numbers next to each other represent multiplication. You, of course, have plus or minus this vector giving two possibilities.
I had a similar question, and even used the indefinite article "a". Some have suggested there is no norm to a 3D line segment by saying there is an infinite number of them. Yet, miss the indefinite article "a" --- which I assume could mean any 1 of infinite.
What happens when someone does not have two vectors to start with?
vector is the unit vector of the line segment or vector.
create a rotation matrix around vector to obtain 1 of infinite norms
It took some time, but using Eigen template library and 10000 random test samples. Here is the code:
#include <Eigen/Core>
#include <Eigen/Geometry>
Eigen::MatrixXd samples = Eigen::MatrixXd::Random(10000, 3); // 3x3 Matrix filled with random numbers between (-1,1)
for (int i = 0; i < 10000; ++i)
{
Eigen::Vector3d vector(samples(i, 0), samples(i, 1), samples(i, 2));
vector.normalize();
Eigen::Vector3d zaxis(0, 0, 1);
Eigen::Vector3d xaxis = zaxis.cross(vector);
xaxis.normalize();
Eigen::Vector3d yaxis = vector.cross(xaxis);
yaxis.normalize();
Eigen::Matrix3d m;
m(0, 0) = xaxis(0);
m(0, 1) = yaxis(0);
m(0, 2) = vector(0);
m(1, 0) = xaxis(1);
m(1, 1) = yaxis(1);
m(1, 2) = vector(1);
m(2, 0) = xaxis(2);
m(2, 1) = yaxis(2);
m(2, 2) = vector(2);
// one of two easy points to use to get 1 of infinite norms --- the other being (1, 0, 0)
Eigen::Vector3d point(0, 1, 0);
point = m * point;
point.normalize();
auto norm = point.cross(vector);
norm.normalize(); // 1 of an infinite number of norms
auto check = norm.dot(vector); // verify with dot product
if (std::abs(check) >= 1e-12)
{
//complain
}
}
I'm trying to come up with a proper algorithm to create a matrix which gets the quad to face the camer straight up but I'm having difficulty.
The quad I'm drawing is facing downwards, here's the code I have so far:
D3DXVECTOR3 pos;
pos = D3DXVECTOR3(-2.0f, 6.0f, 0.1f);
D3DXMATRIX transform;
D3DXMatrixTranslation(&transform, pos.x, pos.y, pos.z);
D3DXVECTOR3 axis = D3DXVECTOR3(0, -1, 0);
D3DXVECTOR3 quadtocam = pos - EmCameraManager::Get().GetPosition();
D3DXVec3Normalize(&quadtocam,&quadtocam);
D3DXVECTOR3 ortho;
D3DXVec3Cross(&ortho, &axis, &quadtocam);
float rotAngle = acos(D3DXVec3Dot(&axis,&quadtocam));
D3DXMATRIX rot;
D3DXMatrixRotationAxis(&rot,&ortho,rotAngle);
transform = rot*transform;
This works when it comes to making the quad face the camera however it doesn't stay up right when facing it from all angles.
In this screen shot: http://imgur.com/hFmzc.png On the left the quad is being viewed straight on (vector is 0,0,1), in the other its being viewed from an arbitrary angle.
Both times it's facing the camera, however when from an arbitrary angle its tilting along its local z axis. I'm not sure how to fix it and was wondering what the next step here would be?
Rotating around an arbitrary axis will always make that. You should rotate your model around y axis first to make it point(up vector) to your camera and after rotate it around your ortho axis which would be aligned with the model's right vector.
Assuming that z axis is coming out of the screen here's some code:
D3DXVECTOR3 Zaxis = D3DXVECTOR3(0, 1, 0);
D3DXVECTOR3 flattenedQuadtocam = quadtocam;
flattenedQuadtocam.y = 0;
float firstRotAngle = acos(D3DXVec3Dot(&Zaxis,&flattenedQuadtocam));
D3DXMATRIX firstRot;
D3DXMatrixRotationAxis(&firstRot,&Zaxis,firstRotAngle);
transform = firstRot*transform;
This should be put right after this line:
D3DXVec3Normalize(&quadtocam,&quadtocam);
Then the rest of your code should work.
I have 3d mesh and I would like to draw each face a 2d shape.
What I have in mind is this:
for each face
1. access the face normal
2. get a rotation matrix from the normal vector
3. multiply each vertex to the rotation matrix to get the vertices in a '2d like ' plane
4. get 2 coordinates from the transformed vertices
I don't know if this is the best way to do this, so any suggestion is welcome.
At the moment I'm trying to get a rotation matrix from the normal vector,
how would I do this ?
UPDATE:
Here is a visual explanation of what I need:
At the moment I have quads, but there's no problem
converting them into triangles.
I want to rotate the vertices of a face, so that
one of the dimensions gets flattened.
I also need to store the original 3d rotation of the face.
I imagine that would be inverse rotation of the face
normal.
I think I'm a bit lost in space :)
Here's a basic prototype I did using Processing:
void setup(){
size(400,400,P3D);
background(255);
stroke(0,0,120);
smooth();
fill(0,120,0);
PVector x = new PVector(1,0,0);
PVector y = new PVector(0,1,0);
PVector z = new PVector(0,0,1);
PVector n = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);
PVector[] face = {p0,p1,p2,p3};
PVector[] face2d = new PVector[4];
PVector nr = PVector.add(n,new PVector());//clone normal
float rx = degrees(acos(n.dot(x)));//angle between normal and x axis
float ry = degrees(acos(n.dot(y)));//angle between normal and y axis
float rz = degrees(acos(n.dot(z)));//angle between normal and z axis
PMatrix3D r = new PMatrix3D();
//is this ok, or should I drop the builtin function, and add
//the rotations manually
r.rotateX(rx);
r.rotateY(ry);
r.rotateZ(rz);
print("original: ");println(face);
for(int i = 0 ; i < 4; i++){
PVector rv = new PVector();
PVector rn = new PVector();
r.mult(face[i],rv);
r.mult(nr,rn);
face2d[i] = PVector.add(face[i],rv);
}
print("rotated: ");println(face2d);
//draw
float scale = 100.0;
translate(width * .5,height * .5);//move to centre, Processing has 0,0 = Top,Lef
beginShape(QUADS);
for(int i = 0 ; i < 4; i++){
vertex(face2d[i].x * scale,face2d[i].y * scale,face2d[i].z * scale);
}
endShape();
line(0,0,0,nr.x*scale,nr.y*scale,nr.z*scale);
//what do I do with this ?
float c = cos(0), s = sin(0);
float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z;
PMatrix3D m = new PMatrix3D(x2+(1-x2)*c, n.x*n.y*(1-c)-n.z*s, n.x*n.z*(1-c)+n.y*s, 0,
n.x*n.y*(1-c)+n.z*s,y2+(1-y2)*c,n.y*n.z*(1-c)-n.x*s,0,
n.x*n.y*(1-c)-n.y*s,n.x*n.z*(1-c)+n.x*s,z2-(1-z2)*c,0,
0,0,0,1);
}
Update
Sorry if I'm getting annoying, but I don't seem to get it.
Here's a bit of python using Blender's API:
import Blender
from Blender import *
import math
from math import sin,cos,radians,degrees
def getRotMatrix(n):
c = cos(0)
s = sin(0)
x2 = n.x*n.x
y2 = n.y*n.y
z2 = n.z*n.z
l1 = x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s
l2 = n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s
l3 = n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c
m = Mathutils.Matrix(l1,l2,l3)
return m
scn = Scene.GetCurrent()
ob = scn.objects.active.getData(mesh=True)#access mesh
out = ob.name+'\n'
#face0
f = ob.faces[0]
n = f.v[0].no
out += 'face: ' + str(f)+'\n'
out += 'normal: ' + str(n)+'\n'
m = getRotMatrix(n)
m.invert()
rvs = []
for v in range(0,len(f.v)):
out += 'original vertex'+str(v)+': ' + str(f.v[v].co) + '\n'
rvs.append(m*f.v[v].co)
out += '\n'
for v in range(0,len(rvs)):
out += 'original vertex'+str(v)+': ' + str(rvs[v]) + '\n'
f = open('out.txt','w')
f.write(out)
f.close
All I do is get the current object, access the first face, get the normal, get the vertices, calculate the rotation matrix, invert it, then multiply it by each vertex.
Finally I write a simple output.
Here's the output for a default plane for which I rotated all the vertices manually by 30 degrees:
Plane.008
face: [MFace (0 3 2 1) 0]
normal: [0.000000, -0.499985, 0.866024](vector)
original vertex0: [1.000000, 0.866025, 0.500000](vector)
original vertex1: [-1.000000, 0.866026, 0.500000](vector)
original vertex2: [-1.000000, -0.866025, -0.500000](vector)
original vertex3: [1.000000, -0.866025, -0.500000](vector)
rotated vertex0: [1.000000, 0.866025, 1.000011](vector)
rotated vertex1: [-1.000000, 0.866026, 1.000012](vector)
rotated vertex2: [-1.000000, -0.866025, -1.000012](vector)
rotated vertex3: [1.000000, -0.866025, -1.000012](vector)
Here's the first face of the famous Suzanne mesh:
Suzanne.001
face: [MFace (46 0 2 44) 0]
normal: [0.987976, -0.010102, 0.154088](vector)
original vertex0: [0.468750, 0.242188, 0.757813](vector)
original vertex1: [0.437500, 0.164063, 0.765625](vector)
original vertex2: [0.500000, 0.093750, 0.687500](vector)
original vertex3: [0.562500, 0.242188, 0.671875](vector)
rotated vertex0: [0.468750, 0.242188, -0.795592](vector)
rotated vertex1: [0.437500, 0.164063, -0.803794](vector)
rotated vertex2: [0.500000, 0.093750, -0.721774](vector)
rotated vertex3: [0.562500, 0.242188, -0.705370](vector)
The vertices from the Plane.008 mesh are altered, the ones from Suzanne.001's mesh
aren't. Shouldn't they ? Should I expect to get zeroes on one axis ?
Once I got the rotation matrix from the normal vector, what is the rotation on x,y,z ?
Note: 1. Blender's Matrix supports the * operator 2.In Blender's coordinate system Z point's up. It looks like a right handed system, rotated 90 degrees on X.
Thanks
That looks reasonable to me. Here's how to get a rotation matrix from normal vector. The normal is the vector. The angle is 0. You probably want the inverse rotation.
Is your mesh triangulated? I'm assuming it is. If so, you can do this, without rotation matrices. Let the points of the face be A,B,C. Take any two vertices of the face, say A and B. Define the x axis along vector AB. A is at 0,0. B is at 0,|AB|. C can be determined from trigonometry using the angle between AC and AB (which you get by using the dot product) and the length |AC|.
You created the m matrix correctly. This is the rotation that corresponds to your normal vector. You can use the inverse of this matrix to "unrotate" your points. The normal of face2d will be x, i.e. point along the x-axis. So extract your 2d coordinates accordingly. (This assumes your quad is approximately planar.)
I don't know the library you are using (Processing), so I'm just assuming there are methods for m.invert() and an operator for applying a rotation matrix to a point. They may of course be called something else. Luckily the inverse of a pure rotation matrix is its transpose, and multiplying a matrix and a vector are straightforward to do manually if you need to.
void setup(){
size(400,400,P3D);
background(255);
stroke(0,0,120);
smooth();
fill(0,120,0);
PVector x = new PVector(1,0,0);
PVector y = new PVector(0,1,0);
PVector z = new PVector(0,0,1);
PVector n = new PVector(0.378521084785,0.925412774086,0.0180059205741);//normal
PVector p0 = new PVector(0.372828125954,-0.178844243288,1.35241031647);
PVector p1 = new PVector(-1.25476706028,0.505195975304,0.412718296051);
PVector p2 = new PVector(-0.372828245163,0.178844287992,-1.35241031647);
PVector p3 = new PVector(1.2547672987,-0.505196034908,-0.412717700005);
PVector[] face = {p0,p1,p2,p3};
PVector[] face2d = new PVector[4];
//what do I do with this ?
float c = cos(0), s = sin(0);
float x2 = n.x*n.x,y2 = n.y*n.y,z2 = n.z*n.z;
PMatrix3D m_inverse =
new PMatrix3D(x2+(1-x2)*c, n.x*n.y*(1-c)+n.z*s, n.x*n.y*(1-c)-n.y*s, 0,
n.x*n.y*(1-c)-n.z*s,y2+(1-y2)*c,n.x*n.z*(1-c)+n.x*s, 0,
n.x*n.z*(1-c)+n.y*s,n.y*n.z*(1-c)-n.x*s,z2-(1-z2)*c, 0,
0,0,0,1);
face2d[0] = m_inverse * p0; // Assuming there's an appropriate operator*().
face2d[1] = m_inverse * p1;
face2d[2] = m_inverse * p2;
face2d[3] = m_inverse * p3;
// print & draw as you did before...
}
For face v0-v1-v3-v2 vectors v3-v0, v3-v2 and a face normal already form rotation matrix that would transform 2d face into 3d face.
Matrix represents coordinate system. Each row (or column, depending on notation) corresponds to axis coordinate system within new coordinate system. 3d rotation/translation matrix can be represented as:
vx.x vx.y vx.z 0
vy.x vy.y vy.z 0
vz.x vz.y vz.z 0
vp.x vp.y vp.z 1
where vx is an x axis of a coordinate system, vy - y axis, vz - z axis, and vp - origin of new system.
Assume that v3-v0 is an y axis (2nd row), v3-v2 - x axis (1st row), and normal - z axis (3rd row). Build a matrix from them. Then invert matrix. You'll get a matrix that will rotate a 3d face into 2d face.
I have 3d mesh and I would like to draw each face a 2d shape.
I suspect that UV unwrapping algorithms are closer to what you want to achieve than trying to get rotation matrix from 3d face.
That's very easy to achieve: (Note: By "face" I mean "triangle")
Create a view matrix that represents a camera looking at a face.
Determine the center of the face with bi-linear interpolation.
Determine the normal of the face.
Position the camera some units in opposite normal direction.
Let the camera look at the center of the face.
Set the cameras up vector point in the direction of the middle of any vertex of the face.
Set the aspect ratio to 1.
Compute the view matrix using this data.
Create a orthogonal projection matrix.
Set the width and height of the view frustum large enough to contain the whole face (e.g. the length of the longest site of a face).
Compute the projection matrix.
For every vertex v of the face, multiply it by both matrices: v * view * projection.
The result is a projection of Your 3d faces into 2d space as if You were looking at them exactly orthogonal without any perspective disturbances. The final coordinates will be in normalized screen coordinates where (-1, -1) is the bottom left corner, (0, 0) is the center and (1, 1) is the top right corner.