Related
I'm building my own small game engine for learning. So basically pure Java. I have Lines which are defined by a start and endpoint (x and y coordinates)
Now I have a ball with a velocity vector. I want to "bounce" off the wall, which could positioned in any possible angle. How do I find out the new velocity vector after the collision happend? I know the point S, P1 and P2 (see image)
I thought about calculating the angle, and change the x and y components. But I can't find a way how to do this for all possible angles.
I could find many solutions for walls which are parallel to the canvas borders, but no general solution. How do "big" game engines handle this common problem?
edit:
My updated Vector class methods:
public static Vector bounce(Vector normal, Vector velocity) {
Vector tmp = Vector.multiplication(2*Vector.dot(normal,velocity), normal);
return Vector.addition(tmp, velocity);
}
public static Vector multiplication(double multi, Vector n) {
Vector new_vector = new Vector(n.x * multi, n.y * multi);
return new_vector;
}
public static double dot(Vector a, Vector b) {
return a.x*b.x + a.y*b.y; // + a.z*b.z if you're in 3D
}
My test function:
#Test
public void testBounce() {
Vector normal_vector_corrected = new Vector(0, 1);
Vector start_velocity = new Vector(3, -3);
Vector bounced_vector = Vector.bounce(normal_vector_corrected, start_velocity);
System.out.println("normal vector: "+normal_vector_corrected);
System.out.println("start_velocity: "+start_velocity);
System.out.println("bounced_vector "+bounced_vector);
}
The output is this:
normal vector: <Vector x=0,00, y=1,00>
start_velocity: <Vector x=3,00, y=-3,00>
bounced_vector <Vector x=3,00, y=-9,00>
According to my calculations, bounced_vector should be x=3,y=3 instead. Where is my mistake? (My example as picture:)
edit2:
I found that it has to be return Vec.add(tmp, v);. Furthermore, I had to inverse the velocity vector.
The "bounced velocity vector" v' is obtained from the original velocity v and the surface normal unit vector n with 2(n . v)n + v where . stands for the vector dot product. This is usually called a reflection; the velocity vector is reflected across the surface normal.
In case you're not familiar with the terminology, the surface normal is a vector that is perpendicular (at 90-degree angle) to the surface. A unit vector is a vector with length 1.
I assume you already have a class to represent vectors, called Vec, with methods to multiply a vector with a scalar and to add two vectors. You could write the bounce operation as:
static Vec bounce(Vec n, Vec v) {
Vec tmp = Vec.scalarMultiply(-2*Vec.dot(n,v), n);
return Vec.add(tmp, v);
}
static double dot(Vec a, Vec b) {
return a.x*b.x + a.y*b.y; // + a.z*b.z if you're in 3D
}
As for how to get the surface normal, that will depend on if you're in 2D or 3D. Assuming 2D, it's simple: if (x,y) is the vector from P1 to P2, then (-y,x) is perpendicular to it, and one unit normal would be:
n = (-y/sqrt(x*x+y*y), x/sqrt(x*x+y*y))
The other possible unit normal is -n. You would use one or the other depending on which side of the surface you are.
You should store the normal vector with the geometry of the scene so you don't have to calculate it every time.
I need help with calculating the lookAt method
Here is my method
public void lookAt(Vector3f position, Vector3f direction, Vector3f up) {
Vector3f f = new Vector3f();
Vector3f u = new Vector3f();
Vector3f s = new Vector3f();
Vector3f.sub(direction, position, f);
f.normalise(f);
up.normalise(u);
Vector3f.cross(f, u, s);
s.normalise(s);
Vector3f.cross(s, f, u);
this.setIdentity();
this.m00 = s.x;
this.m10 = s.y;
this.m20 = s.z;
this.m01 = u.x;
this.m11 = u.y;
this.m21 = u.z;
this.m02 = -f.x;
this.m12 = -f.y;
this.m22 = -f.z;
this.m30 = -Vector3f.dot(s, position);
this.m31 = -Vector3f.dot(u, position);
this.m32 = Vector3f.dot(f, position);
}
but when I test it like this camera.lookAt(position, new Vector3f(1, 0 ,0), new Vector3f(0, -1, 0)); my camera is looking down, end if only i do this camera.lookAt(position, new Vector3f(10000, 0 ,0), new Vector3f(0, -1, 0));, camera is looking forward. Can you help please ?
P.S. sorry for my english
The second parameter of a lookAt function is usually not the direction in which you want to look but the point which you want to look at. As far as I can see, the calculation of your method also expects a second point and not a direction (which is calculated from the two point and stored in f).
In conclusion, the results you get look correct to me, except that you passed the wrong parameters to the function.
How to create a function that creates a matrix from a point and a direction
The rotation required is one that maps the minus z-direction onto the view vector. Additionally, we want the x-vector to be mapped such that it is perpendicular to the plane spanned by view and up vector. This can relatively easy be achieved by writing -view in the third row of the 3x3 matrix. The other two vectors can then be computed as the cross products of view and up vector which results in a right-vector onto which the x-axis should be mapped. The last vector (the target mapping for the y-axis) is then computed by the cross product of view and right vector. The cross products for the last vector is used, since we know that all rotation matrices have base vectors that are perpendicular:
viewv = normalize(-view)
rightv = normalize(cross(view, up))
upv = normalize(cross(view, up))
-- rightv --
rotation_matrix = [ -- upv -- ]
-- viewv --
When the camera is located in the origin, then we are done now. But since this is in general not the case, we have to add a translation part that transforms the scene such that the camera is the origin. Thus t = -camera.
The final matrix is now composed by first translating the space and then rotating it according to our calculated matrix:
lookat_matrix = rotation_matrix * translate(-camera)
Since it is fairly late here and depending on the notation you use it might be that the rotation matrix has to be transposed and that some signs have to be adjusted.
I need to rotate a triangle so that it lies on a plane given by a normal n and a constant d.
I have the normal n1 of the plane that the two triangles lie in. Now i need to rotate the right red triangle so that it results in the orange one.
The points of the triangles and the normals are stored as 3-dimensional vectors.
Until now, I did the following:
Get the normalized rotation quaternion (rotQuat) between n1 and n2.
Multiply every point of the triangle by the quaternion. Therefore I convert the point to a quaternion(point.x, point.y, point.z, 0) and do the multiplcation as follows: resultQuat = rotQuat * point * conjugate(rotQuat). I then apply x, y and z of the result to the point.
This is how i get the rotation between two vectors:
public static Quaternion getRotationBetweenTwoVector3f(Vector3f vec1, Vector3f vec2)
{
Vector3f cross = Vector3f.cross(vec1, vec2);
float w = (float) (java.lang.Math.sqrt(java.lang.Math.pow(vec1.getLength(), 2) * java.lang.Math.pow(vec2.getLength(), 2)) + Vector3f.dot(vec1, vec2));
Quaternion returnQuat = new Quaternion(cross.x, cross.y, cross.z, w);
returnQuat.normalize();
return returnQuat;
}
The problem is that the triangle has the correct orientation after the rotation, but the triangle also moves it's position. I need a rotation that rotates the triangle so that it's still connected to the two points of the left red triangle (like the orange one).
How is this possible?
Your problem is that rotation matrix/quaternions rotate points around an axis that passes through the origin. To rotate around different point than the origin, you need to translate the triangle points to the origin (just Substract the rotation point value from the triangle points), then multiply by the quaternion and then translate back.
So the algorithm becomes:
translatedPoints[i] = triPoints[i] - rotationPoint;
translatedPoints rotate using quaternion
translate translatedPoints back by adding the rotation point value.
I have 4 points, three of the points make two lines like a V or a < or a >, you get the idea, now I got a point that's in that cone (the V) and I can get the dist from the top to the bottom left, and bottom right, but not where the bottom pos is.
Maybe this will help.
And I have code to go along with this problem:
public float GetDist(Vector3f one, Vector3f two, Vector3f three, Vector3f p){
Vector3f one_to_point = new Vector3f(0,0,0);
Vector3f.sub(p,one,one_to_point); //Storing vector A->P
Vector3f one_to_two = new Vector3f(0,0,0);
Vector3f.sub(two, one, one_to_two); //Storing vector A->B
Vector3f one_to_three = new Vector3f(0,0,0);
Vector3f.sub(three, one, one_to_three); //Storing vector A->C
float q1 = Vector3f.dot(one_to_point, one_to_two) / one_to_two.lengthSquared(); // The normalized "distance" from a to b
float q2 = Vector3f.dot(one_to_point, one_to_three) / one_to_three.lengthSquared(); // The normalized "distance" from a to c
Now I already know that the pos vector is in the cone, so what do I need to do to get the pos as shown by the green circle's pos in the image?
You have 4 points in the plane : A,B,C and D (where D is what you label as pos in your diagram). A unique straight line can be drawn between any 2 distinct points so find the straight line that links A and D and get its equation in the form
y = m_1 * x + c_1
Do the same for line the points B and C to get
y = m_2 * x + c_2
Now you know the 2 lines, you can solve this pair of simultaneous equations to get the point (x,y) that lies at the green circle on your diagram - I shall call this E. Given E, calculate the length of vector BE and divide by the length of vector BC. This value is the value of X that you are looking for in your question.
If you do not know how to find the equation for a line that goes through 2 points look at this link for details http://www.ugrad.math.ubc.ca/coursedoc/math100/notes/zoo/eqline.html
I do not doubt that there is a simpler more elegant way to do this but if you get no other answer here, this approach will serve your purpose.
It's been a while since I've done vector algebra, but let me see if I got this right. You're looking for the green point, which is where a line from A to Pos intersects a line from B to C.
I believe if you knew the ratio of the angle that B-A-Pos forms to the angle that B-A-C forms, that ratio would be the same as the ratio of the distance from B to Green to the distance from B to C. From B, the direction of Green is the same as the direction of C, so the vector that represents Green's location is
VectorGreen = VectorB + (x1 / x2)(VectorC - VectorB) //The vector to B plus a fraction of the vector from B to C
x1 = arccos ( Normalize(VectorP - VectorA ) * Normalize(VectorB - VectorA) )//Angle between A to B and A to Pos
x2 = arccos ( Normalize(VectorB - VectorA ) * Normalize(VectorC - VectorA) ) //Angle between A to B and A to C
I wish to determine the 2D screen coordinates (x,y) of points in 3D space (x,y,z).
The points I wish to project are real-world points represented by GPS coordinates and elevation above sea level.
For example:
Point (Lat:49.291882, Long:-123.131676, Height: 14m)
The camera position and height can also be determined as a x,y,z point. I also have the heading of the camera (compass degrees), its degree of tilt (above/below horizon) and the roll (around the z axis).
I have no experience of 3D programming, therefore, I have read around the subject of perspective projection and learnt that it requires knowledge of matrices, transformations etc - all of which completely confuse me at present.
I have been told that OpenGL may be of use to construct a 3D model of the real-world points, set up the camera orientation and retrieve the 2D coordinates of the 3D points.
However, I am not sure if using OpenGL is the best solution to this problem and even if it is I have no idea how to create models, set up cameras etc
Could someone suggest the best method to solve my problem? If OpenGL is a feasible solution i'd have to use OpenGL ES if that makes any difference. Oh and whatever solution I choose it must execute quickly.
Here's a very general answer. Say the camera's at (Xc, Yc, Zc) and the point you want to project is P = (X, Y, Z). The distance from the camera to the 2D plane onto which you are projecting is F (so the equation of the plane is Z-Zc=F). The 2D coordinates of P projected onto the plane are (X', Y').
Then, very simply:
X' = ((X - Xc) * (F/Z)) + Xc
Y' = ((Y - Yc) * (F/Z)) + Yc
If your camera is the origin, then this simplifies to:
X' = X * (F/Z)
Y' = Y * (F/Z)
You do indeed need a perspective projection and matrix operations greatly simplify doing so. I assume you are already aware that your spherical coordinates must be transformed to Cartesian coordinates for these calculations.
Using OpenGL would likely save you a lot of work over rolling your own software rasterizer. So, I would advise trying it first. You can prototype your system on a PC since OpenGL ES is not too different as long as you keep it simple.
If just need to compute coordinates of some points, you should only need some algebra skills, not 3D programming with openGL.
Moreover openGL does not deal with Geographic coordinates
First get some doc about WGS84 and geodesic coordinates, you have first to convert your GPS data into a cartesian frame ( for instance the earth centric cartesian frame in which is defined the WGS84 ellipsoid ).
Then the computations with matrix can take place.
The chain of transformations is roughly :
WGS84
earth centric coordinates
some local frame
camera frame
2D projection
For the first conversion see this
The last involves a projection matrix
The others are only coordinates rotations and translation.
The "some local frame" is the local cartesian frame with origin as your camera location
tangent to the ellipsoid.
I'd recommend "Mathematics for 3D Game Programming and Computer Graphics" by Eric Lengyel. It covers matrices, transformations, the view frustum, perspective projection and more.
There is also a good chapter in The OpenGL Programming Guide (red book) on viewing transformations and setting up a camera (including how to use gluLookAt).
If you aren't interested in displaying the 3D scene and are limited to using OpenGL ES then it may be better to just write your own code to do the mapping from 3D to 2D window coords. As a starting point you could download Mesa 3D, an open source implementation of OpenGL, to see how they implement gluPerspective (to set a projection matrix), gluLookAt (to set a camera transformation) and gluProject (to project a 3D point to 2D window coords).
return [((fol/v[2])*v[0]+x),((fol/v[2])*v[1]+y)];
Point at [0,0,1] will be x=0 and y=0, unless you add center screen xy - it's not camera xy. fol is focal length, derived from fov angle and screen width - how high is the triangle (tangent). This method will not match three.js perspective matrix, which is why am I looking for that.
I should not be looking for it. I matched xy on openGL, perfectly like super glue! But I cannot get it to work right in java. THAT Perfect match follows.
var pmat = [0,0,0,0,0,0,0,0,0,0,
(farclip + nearclip) / (nearclip - farclip),-1,0,0,
2*farclip*nearclip / (nearclip - farclip),0 ];
void setpmat() {
double fl; // = tan(dtor(90-fovx/aspect/2)); /// UNIT focal length
fl = 1/tan(dtor(fov/Aspect/2)); /// same number
pmat[0] = fl/Aspect;
pmat[5] = fl;
}
void fovmat(double v[],double p[]) {
int cx = (int)(_Width/2),cy = (int)(_Height/2);
double pnt2[4], pnt[4] = { 0,0,0,1 } ;
COPYVECTOR(pnt,p);NORMALIZE(pnt);
popmatrix4(pnt2,pmat,pnt);
COPYVECTOR(v,pnt2);
v[0] *= -cx; v[1] *= -cy;
v[0] += cx; v[1] += cy;
} // world to screen matrix
void w2sm(int xy[],double p[]) {
double v[3]; fovmat(v,p);
xy[0] = (int)v[0];
xy[1] = (int)v[1];
}
I have one more way to match three.js xy, til I get the matrix working, just one condition. must run at Aspect of 2
function w2s(fol,v,x,y) {
var a = width / height;
var b = height/width ;
/// b = .5 // a = 2
var f = 1/Math.tan(dtor(_fov/a)) * x * b;
return [intr((f/v[2])*v[0]+x),intr((f/v[2])*v[1]+y)];
}
Use it with the inverted camera matrix, you will need invert_matrix().
v = orbital(i);
v = subv(v,campos);
v3 = popmatrix(wmatrix,v); //inverted mat
if (v3[2] > 0) {
xy = w2s(flen,v3,cx,cy);
Finally here it is, (everyone ought to know by now), the no-matrix match, any aspect.
function angle2fol(deg,centerx) {
var b = width / height;
var a = dtor(90 - (clamp(deg,0.0001,174.0) / 2));
return asa_sin(PI_5,centerx,a) / b;
}
function asa_sin(a,s,b) {
return Math.sin(b) * (s / Math.sin(PI-(a+b)));
} // ASA solve opposing side of angle2 (b)
function w2s(fol,v,x,y) {
return [intr((fol/v[2])*v[0]+x),intr((fol/v[2])*v[1]+y)];
}
Updated the image for the proof. Input _fov gets you 1.5 that, "approximately." To see the FOV readout correctly, redo the triangle with the new focal length.
function afov(deg,centerx) {
var f = angle2fol(deg,centerx);
return rtod(2 * sss_cos(f,centerx,sas_cos(f,PI_5,centerx)));
}
function sas_cos(s,a,ss) {
return Math.sqrt((Math.pow(s,2)+Math.pow(ss,2))-(2*s*ss*Math.cos(a)));
} // Side Angle Side - solve length of missing side
function sss_cos(a,b,c) {
with (Math) {
return acos((pow(a,2)+pow(c,2)-pow(b,2))/(2*a*c));
}
} // SSS solve angle opposite side2 (b)
Star library confirmed the perspective, then possible to measure the VIEW! http://innerbeing.epizy.com/cwebgl/perspective.jpg
I can explain the 90 deg correction to moon's north pole in one word precession. So what is the current up vector. pnt? radec?
function ininorths() {
if (0) {
var c = ctime;
var v = LunarPos(jdm(c));
c += secday();
var vv = LunarPos(jdm(c));
vv = crossprod(v,vv);
v = eyeradec(vv);
echo(v,vv);
v = [266.86-90,65.64]; //old
}
var v = [282.6425,65.8873]; /// new.
// ...
}
I have yet to explain the TWO sets of vectors: Three.milkyway.matrix and the 3D to 2D drawing. They ARE:
function drawmilkyway() {
var v2 = radec2pos(dtor(192.8595), dtor(27.1283),75000000);
// gcenter 266.4168 -29.0078
var v3 = radec2pos(dtor(266.4168), dtor(-29.0078),75000000);
// ...
}
function initmwmat() {
var r,u,e;
e = radec2pos(dtor(156.35), dtor(12.7),1);
u = radec2pos(dtor(60.1533), dtor(25.5935),1);
r = normaliz(crossprod(u,e));
u = normaliz(crossprod(e,r));
e = normaliz(crossprod(r,u));
var m = MilkyWayMatrix;
m[0]=r[0];m[1]=r[1];m[2]=r[2];m[3]=0.0;
m[4]=u[0];m[5]=u[1];m[6]=u[2];m[7]=0.0;
m[8]=e[0];m[9]=e[1];m[10]=e[2];m[11]=0.0;
m[12]=0.0;m[13]=0.0;m[14]=0.0;m[15]=1.0;
}
/// draw vectors and matrix were the same in C !
void initmwmat(double m[16]) {
double r[3], u[3], e[3];
radec2pos(e,dtor(192.8595), dtor(27.1283),1); //up
radec2pos(u,dtor(266.4051), dtor(-28.9362),-1); //eye
}