I'm using the blender game engine and python I made a script that makes an empty follow my cursor in 3D space. (I use the keyboard for height for now).
Now I wanted to implement a LookAt function for a general object rather than a camera, using python. I want the object to look exactly at the point I'm hovering (the empty position) at the screen. For now I'm using a cube so basically one face of the cube should always face the empty.
So, I thought of using matrices or quaternions but the problem is that All I have is a direction vector and I chose the x axis for the local look direction. So either way I need to calculate the euler angles and convert them to axis-rotation angles. (theta*[axis^]).
The resources I have in the Blender Game Engine is: mathutils (provide quarternions, euler based rotations (via axis-angles), matrices) - though it doesn't have any updated documentation which is just annyoingly horrible! I have to print help to get some sort of info!
Now I've been able to make the object look at the empty when I rotate only the Z axis. I used a little trick that handles the angle sign for me using simple trigonometry, so sign is handled and I don't need any matrix trickery or quarternions. The problem begins when I try to rotate once again - I want to rotate the Y axis for the up-down look (as known in 3D we need two sorts of rotations to face someone, the third is just for rotating the view upside-down - "rolling the camrea") since this rotation axis is the look direction vector.
Here's my script:
import bge
from mathutils import Vector, Matrix
import math
# Basic stuff
cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
c = scene.objects["Cube"]
e = scene.objects["Empty"]
# axises (we're using localOrientation)
x = Vector((1.0,0.0,0.0))
y = Vector((0.0,1.0,0.0))
z = Vector((0.0,0.0,1.0))
vec = Vector(e.worldPosition - c.worldPosition) # direction vector
# Converting direction vector into euler angles
# Using trigonometry we get: tan(psi) = cos(phi2)/cos(phi1)
# Where phi1 is the angle between x axises (euler angle)
# and phi2 is the euler of the y axises.
# psi is the z rotation angle.
# get cos(euler_angle)
phi1 = vec.dot(x)/vec.length # = cos p1
phi2 = vec.dot(y)/vec.length # = cos p2
phi3 = vec.dot(z)/vec.length # = cos p3
# get the rotation/steer angles
zAngle = math.atan(phi2/phi1)
yAngle = math.atan2(phi3,phi1)
xAngle = math.atan(phi2/phi3)
# use only 2 as the third must adapt (also: view concept - x is the looking direction, rotating it would make rolling)
r = c.localOrientation.to_euler()
r.z = zAngle
r.y = -yAngle
#r.x = xAngle
c.localOrientation = r
Seperately each axis works perfectly, but when combined, there are little jump glitches when I get through the global Y axis.
Also, it seems that the "local" orientation in blender is just the same as the "worldOrientation" which is also annoying cause I'm not sure anymore in what frame of reference I'm working anymore. If anyone knows, please help !
Edit 1:
Appearantely there's a built in logic block that handles this for me and when I press "3D" it tracks AND succeeds on rotating BOTH axises. Though, I still want to know what's the problem with my script! What did the 3D button do that I didn't?
Edit 2:
I tried stop making trigo tricks and found out that when I use local orientation I ALWAYS get a gimbal lock in one axis. That's probably what happened behind the scenes. Thanks for anyone interested, if you have any good trick I'd still be glad to hear =]!
I have a youtube tutorial on how to make the camera look at specific objects. It may help.
https://www.youtube.com/watch?v=hwbObDkiJrE
But the concept, when using the gui, is to open the object->relations panel and for the object you want to be doing the LookAt, you make it the child of the object you want it to follow (the parent). You then select 'Vertex' as the relationship. This will then affect the rotation angles of the child object only.
Try this,
bpy.data.objects['child'].parent = bpy.data.objects['parent']
bpy.data.objects['child'].parent_type = 'VERTEX'
and actually there is more info here
https://blender.stackexchange.com/questions/26108/how-do-i-parent-objects
Related
I am trying to build a rotation controller for my threejs objects. My rotation method is the following:
function rotate(axis, angle) {
rotMat = new THREE.Matrix4().makeRotationAxis(axis, angle);
rotMat.multiply(mesh.matrix);
rotQuat = new THREE.Quaternion().setFromRotationMatrix(rotMat);
mesh.quaternion.copy(rotQuat);
mesh.updateMatrix();
}
I need to do it this way in order to have a rotation around the world axes and not the local axes (related to this post -> I also cannot use the Euler rotation member here because of a problem i describe here)
Getting to my problem:
I made this JSFiddle which shows the issue pretty good.
How to recreate:
1) Open the fiddle link.
2) Press X, Y or Z on your keyboard to enter the rotation mode for the desired axis.
3) Hold 'Arrow Up' key and rotate as long as the 'strange' scaling occurs. Should happen at an angle of 90-100 degrees. Note that the scaling continues if you keep rotating
Also note that i decrease the rotation step size (rotation speed) when getting to the specific angle area. The scaling only occurs when the rotation step size is quite small.
My question is:
Does somebody know why a rotation is causing a scale?
The reason why this is happening is because you need to feed a 'pure' rotation matrix to the Quaternion.setFromRotationMatrix method. So changing the rotate function to the following will work:
function rotate (axis, angle) {
rotMat = new THREE.Matrix4().makeRotationAxis(axis, angle);
rotMat.multiply(mesh.matrix);
var rotMat2 = new THREE.Matrix4().extractRotation(rotMat);
rotQuat = new THREE.Quaternion().setFromRotationMatrix(rotMat2);
mesh.quaternion.copy(rotQuat);
mesh.updateMatrix();
}
I'm attempting to add a small value to a World Matrix in order to replicate the accuracy of a fired weapon [pistol, assault rifle]
Currently, my World Matrix resides at a Parent Objects' position, with the ability to rotate about the Y axis exclusively.
I've done this in Unity3D, running whenever the object needs to be created [once per]:
var coneRotation = Quaternion.Euler(Random.Range(-spread, spread), Random.Range(-spread, spread), 0);
var go = Instantiate(obj, parent.transform.position, transform.rotation * coneRotation) as GameObject;
and am attempting to replicate the results using Direct3D11.
This lambda returns a random value between [-1.5, 1.5] currently:
auto randF = [&](float lower_bound, float uppder_bound) -> float
{
return lower_bound + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (uppder_bound - lower_bound)));
};
My first thought was to simply multiply a random x && y into the forward vector of an object upon initialization, and move it in this fashion: position = position + forward * speed * dt; [speed being 1800], though the rotation is incorrect (not to mention bullets fire up).
I've also attempted to make a Quaternion [as in Unity3D]: XMVECTOR quaternion = XMVectorSet(random_x, random_y, 0) and creating a Rotation Matrix using XMMatrixRotationQuaternion.
Afterwards I call XMStoreFloat4x4(&world_matrix, XMLoadFloat4x4(&world_matrix) * rotation);, and restore the position portion of the matrix [accessing world_matrix._41/._42/._43] (world_matrix being the matrix of the "bullet" itself, not the parent).
[I've also tried to reverse the order of the multiplication]
I've read that the XMMatrixRotationQuaternion doesn't return as an Euler Quaternion, and XMQuaternionToAxisAngle does, though I'm not entirely certain how to use it.
What would be the proper way to accomplish something like this?
Many thanks!
Your code XMVECTOR quaternion = XMVectorSet(random_x, random_y, 0); is not creating a valid quaternion. First, if you did not set the w component to 1, then the 4-vector quaternion doesn't actually represent a 3D rotation. Second, a quaternion's vector components are not Euler angles.
You want to use XMQuaternionRotationRollPitchYaw which constructs a quaternion rotation from Euler angle input, or XMQuaternionRotationRollPitchYawFromVector which takes the three Euler angles as a vector. These functions are doing what Unity's Quaternion.Euler method is doing.
Of course, if you want a rotation matrix and not a quaternion, then you can XMMatrixRotationRollPitchYaw or XMMatrixRotationRollPitchYawFromVector to directly construct a 4x4 rotation matrix from Euler angles--which actually uses quaternions internally anyhow. Based on your code snippet, it looks like you already have a base rotation as a quaternion you want to concatenate with your spread quaternion, so you probably don't want to use this option for this case.
Note: You should look at using the C++11 standard <random> rather than your home-rolled lambda wrapper around the terrible C rand function.
Something like:
std::random_device rd;
std::mt19937 gen(rd());
// spread should be in radians here (not degrees which is what Unity uses)
std::uniform_real_distribution<float> dis(-spread, spread);
XMVECTOR coneRotation = XMQuaternionRotationRollPitchYaw( dis(gen), dis(gen), 0 );
XMVECTOR rot = XMQuaternionMultiply( parentRot, coneRotation );
XMMATRIX transform = XMMatrixAffineTransformation( g_XMOne, g_XMZero, rot, parentPos );
BTW, if you are used to Unity or XNA Game Studio C# math libraries, you might want to check out the SimpleMath wrapper for DirectXMath in DirectX Tool Kit.
By now I am so confused that I'm not sure of my vector math anymore.. I have a Matrix4: MatrixA representing an objects (sensor cube) world transform. I want to place this object so that it's forward direction is pointing in the same direction as a given normalized Vector3: VecA . I also want to translate the objects (i.e. 4 units) in VecA's direction from a given point: VecB (the translation part works, using the same direction vector, VecA)
I have tried all the ways I can think of including rotate()+translate(), setToWorld(), setToLookAt(), setToRotation(), manually editing the values (column 3) of the Matrix4 (this gave the best results in terms of rotation but I get a skewed cube)
I know my direction vector (VecA) is OK. (by printing it's value and also visually confirming it by looking at the working translation using the same vector)
Can someone please tell me how I should do to achieve my desired results, thanks!
Assuming you're unrotated "forward direction" is (0,0,1), your unrotated "up direction" is (0,1,0) and you don't want to rotate the up direction (if possible), then something like this (untested code) should be what you need:
Vector3 vx = new Vector3(), vy = new Vector3(), vz = new Vector3();
Matrix4 m = new Matrix4();
...
vecB.set(vecA).scl(4.f); // if understand correctly, this is what you want
vz.set(vecA).nor();
vx.set(vz).crs(0, 1, 0).nor();
vy.set(vz).crs(vx).nor();
m.idt();
m.val[Matrix4.M00] = vx.x; m.val[Matrix4.M01] = vx.y; m.val[Matrix4.M02] = vx.z;
m.val[Matrix4.M10] = vy.x; m.val[Matrix4.M11] = vy.y; m.val[Matrix4.M12] = vy.z;
m.val[Matrix4.M20] = vz.x; m.val[Matrix4.M21] = vz.y; m.val[Matrix4.M22] = vz.z;
m.trn(vecB);
It is possible that you need to switch the crs arguments though (e.g. vy.set(vx).crs(vz).nor(), in case the rotation is upside-down). Alternatively you could use a Quaternion to specify the rotation and use m.set(vecB, rotationQuaternion);.
I'm trying to set the ProjectionMatrix of a Three.js Perspective Camera to match a projection Matrix I calculated with a different program.
So I set the camera's position and rotation like this:
self.camera.position.x = 0;
self.camera.position.y = 0;
self.camera.position.z = 142 ;
self.camera.rotation.x = 0.0;// -0.032
self.camera.rotation.y = 0.0;
self.camera.rotation.z = 0;
Next I created a 4x4 Matrix (called Matrix4 in Three.js) like this:
var projectionMatrix = new THREE.Matrix4(-1426.149, -145.7176, -523.0170, 225.07519, -42.40711, -1463.2367, -23.6839, 524.3322, -0.0174, -0.11928, -0.99270, 0.43826, 0, 0, 0, 1);
and changed the camera's projection Matrix entries like this:
for ( var i = 0; i < 16; i++) {
self.camera.projectionMatrix.elements[i] = projectionMatrix.elements[i];
}
when I now render the scene I just get a black screen and can't see any of the objects I inserted. Turning the angle of the Camera doesn't help either. I still can't see any objects.
If I insert a
self.camera.updateProjectionMatrix();
after setting the camera's projection Matrix to the values of my projectionMatrix the camera is set back to the original Position (x=0,y=0,z=142 and looking at the origin where I created some objects) and the values I set in the camera's matrix seem to have been overwritten. I checked that by printing the cameras projection Matrix to the console. If I do not call the updateProjectionMatrix() function the values stay as I set them.
Does somebody have an idea how to solve this problem?
If I do not call the updateProjectionMatrix() function the values stay as I set them.
Correct, updateProjectionMatrix() calculates those 16 numbers you pasted in your projection matrix based on a bunch of parameters. Those parameters are, the position and rotation you set above, plus the parameters you passed (or default) for the camera. (these actually make the matrixWorld and its inverse.
In case of a perspective camera, you don't have much - near, far, fov and aspect. Left,right,top,bottom are derived from these, with an orthographic camera you set them directly. These are then used to compose the projection matrix.
Scratch a pixel has a REALLY good tutorial on this subject. The next lesson on the openGL projection matrix is actually more relevant to WebGL. left right top and bottom are made from your FOV and your aspect ratio. Add near and far and you've got yourself a projection matrix.
Now, in order for this thing to work, you either have to know what you're doing, or get really lucky. Pasting these numbers from somewhere else and getting it to work is short of winning the lottery. Best case scenario, you can have your scale all wrong and clipping your scene. Worst case, you've mixed a completely different matrix, different XYZ convention, and there's no way you'll get it to work, or at least make sense.
Out of curiosity, what are you trying to do? Are you trying to match your camera to a camera from somewhere else?
I am starting with Three.js so I might have misunderstood some basics of the concept. I have a usual 3d scene with a hierarchy like this:
.
+-container #(0,0,0) (Object3d, no own geometry)
+-child 1 #(1,1,1)
+-child 2 #(1, -2, 5)
+-child 3 #(-4, -2, -3)
.
.
. more should come
all »children« of the »container« are imported models from Blender. What I would like to do is to rotate the whole container around a pivot axis based on the current selection, which should be one of the children.
Image three cubes in Blender, all selected with the 3d cursor at center of first in location and center of transformation. A rotation transforms all cubes, but the rotation is relative to the first in selection.
In terms of three.js, what would like to do is to rotate the container, so that the rotation is applied to all children.
To do that I think that the following steps should do the trick:
create a matrix,
translate that matrix by the negative of the selected objects position
rotate that matrix
translate the matrix back to the selected objects position
apply the transform to the container
I have tried the following code but the result is just wrong:
var sp = selection.position.clone(),
m = new THREE.Matrix4();
selection.localToWorld(sp);
m.setPosition(sp.clone().negate());
//I've used makeRotationX for testing purposes, should be replaced with quaternion rotation later on…
m = m.multiply(new THREE.Matrix4().makeRotationX(2*180/Math.PI));
m = m.multiply(new THREE.Matrix4().makeTranslation(sp.x,sp.y,sp.z));
this._container.applyMatrix(m);
Thanks for help!
UPDATE
sign error—this works:
var sp = selection.position.clone(),
m = new THREE.Matrix4();
m.makeTranslation(sp.x,sp.y,sp.z);
m.multiply(new THREE.Matrix4().makeRotationX(0.1));
m.multiply(new THREE.Matrix4().makeTranslation(-sp.x,-sp.y,-sp.z));
this._container.applyMatrix(m);
BUT that code does not really look that good, creating three matrices for that single operating seems to bit of overhead, what is the usual »three.js-way«?
UPDATE #2
Due to the comment here is an image describing what I would like to do:
The »arrows« at the origin stand for the parent container and the cube, the sphere and the cone are its »children«. The red line shows the line I would like rotate the parent around, this way the rotation is applied to all children.
rotateOnAxis() takes a Vector as axis, so the line the objects rotates around crosses its origin.