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.
Related
I am working on a project in LibGDX, and I am using Scene2D actors for some of my sprites. In this regard, I have a sprite, which is spawning somewhere on the screen and needs to move to another position on the screen. To do this I am using the moveTo(xPos, yPos, duration, interpolation) method in the Actions, to make the move animation.
However, when I use this approach, the actor moves like I told it to, but it only moves in a straight line, from point A to B. I have tried several Interpolation options, like Circle interpolation and such, but it seems only to impact the speed of the animation line.
So now my question: How do I make my animation make a smooth curved line (See picture), from A to B?
I am currently using this code to make the Actions animation:
adultCustomerPointActor.addAction(Actions.sequence(
Actions.moveTo(300, 200, 2f, Interpolation.circle)
));
Thanks in advance for your help :)
It's a geometry problem. Using vectors, find the point halfway between the two points:
vec1.set(bx, by).sub(ax, ay).scl(0.5f).add(ax, ay);
Get another vector that is 90 or 270 to from the vector between the points:
vec2.set(bx, by).sub(ax, ay).rotate90().add(vec1);
This vec2 can be scaled to adjust how extreme curvature of the arc is. If you leave it alone, you'll have a quarter circle. You can also scale it negative to reverse the curvature.
Then add the second vector to the first to find the center point of your arc, which we can call point C.
vec1.set(bx, by).sub(vec2); // CB
vec3.set(ax, ay).sub(vec2); // CA
float angle = vec1.angle(vec3);
Now you need a vector that points from point C to point A. You will rotate this vector until it reaches point B. So you need the angle between CA and CB.
So here's a very simplistic class that implements this. It doesn't account yet for deciding if you want the arc to go up or down and if you want to scale how extreme it looks. You could add those as additional parameters with getters/setters. I haven't tested it, so it may need some debugging.
public class ArcToAction extends MoveToAction {
private float angle;
private final Vector2 vec1 = new Vector2(), vec2 = new Vector2(), vec3 = new Vector2();
#Override
protected void begin () {
super.begin();
float ax = target.getX(getAlignment()); // have to recalculate these because private in parent
float ay = target.getY(getAlignment());
vec1.set(getX(), getY()).sub(ax, ay);
vec2.set(vec1).rotate90();
vec1.scl(0.5f).add(ax, ay);
vec2.add(vec1);
vec1.set(bx, by).sub(vec2); // CB
vec3.set(ax, ay).sub(vec2); // CA
angle = vec1.angle(vec3);
}
protected void update (float percent) {
if (percent >= 1){
target.setPosition(getX(), getY(), getAlignment());
return;
}
vec1.set(vec3).rotate(percent * angle);
target.setPosition(vec1.x, vec1.y, getAlignment());
}
}
If you want to support automatic pooling, you can add a method like this:
static public ArcToAction arcTo (float x, float y, float duration, Interpolation interpolation) {
ArcToAction action = Actions.action(ArcToAction .class);
action.setPosition(x, y);
action.setDuration(duration);
action.setInterpolation(interpolation);
return action;
}
So, we have a homework question that asks us to create a hypercube of corners 2^n. And each corner has a set of n-coordinates in a plane of x1, x2, x3... xn. So, a n=3 hypercube has coordinates such as:
000, 001, 011, 010, etc. in a plane x1, x2, x3.
The point of writing this program is so have a recursive method and an iterative method to "walk" through the hypercube and pass every corner exactly once without overlapping it's trail. The professor also demand that our Corner object be a nested class in the hypercube class. So far this is what I've come up with:
ublic class Hypercube
{
private Corner[] walk;
private int size;
private final int ZERO = 0;
private int count;
public Hypercube(int n) throws IllegalHypercubeException {
if (n < 0) {
throw new IllegalHypercubeException("Please enter a positive integer");
} else {
this.size = n;
this.count = 0;
this.walk = new Corner[(int) Math.pow(2, n)];
}
}
public class Corner
{
private int[] coordinates;
public Corner() {
this.coordinates = new int[size];
}this.coordinates = coordinates;
}
I find most difficulty in setting the coordinates before I can even order them recursively in the walk methods. How am I meant to set all the coordinates of each corner of a cube of 2^n corners?
Not going to write a code for it (that's you job), but here's something to orient you:
In a 1D - the corners will be {0} and {1}
In 2D - the corners will be
{
{0,0}, {0,1},
{1,0}, {1,1}
}
In 3D - the corners will be
{
{0,0,0}, {0,0,1}, {0,1,0}, {0,1,1},
{1,0,0}, {1,0,1}, {1,1,0}, {1,1,1}
}
If you still haven't had your Aha! moment, here's the spoiler: make a list of corners in N-dimension by prefixing the all the corners in a N-1 dimension by 0 then by 1.
When you walk through hypercube by it's edges, you change one coordinate at every step. Note there are special binary sequences where exactly one bit is flipped between neighbor elements.
Look at Gray codes (n-bit code for n-dimensional hypercube)- they are well described, numerous methods exist to generate sequence both recursively and iteratively.
I am generating a List of points that have only integer components using GenerateSolidThetaZero function. My goal is to rotate these discrete points using an angle theta in radians and the new point after the rotation should still have integer components. The problem is I do not want any two points mapping to the same value. I want the same number of unique points before and after the rotation. I used the round function to remedy this problem a bit but I will still get some non-unique mappings. Basically I just want to find a way to rotate these points and preserve as much of the structure as possible(losing the least amount of points as possible). I am willing to use any library. Any help or guidance would be great.
Note: In my code the radius is 2 and 13 points are generated. After the rotation of Pi/6 I end up losing 4 points due to those points mapping to the same value another point already mapped to.
public class pointcheck{
// this HashSet will be used to check if a point is already in the rotated list
public static HashSet<Point> pointSet = new HashSet<Point>();
public static void main(String args[]) {
//generates sort of circular solid with param being the radius
ArrayList<Point> solid_pointList = GenerateSolidThetaZero(2);
//used to store original point as first part of pair and rotated point as second part of pair
ArrayList<Pair> point_pair = new ArrayList<Pair>();
//goes through all points in Solid_pointList adds each point to Point List with its corresponding rotated angle
for(Point t : solid_pointList){
point_pair.add(new Pair(t,rotation_about_origin(t,Math.PI / 6)));
}
for(Pair t : point_pair){
System.out.println(t.getFirst() + " " + t.getSecond());
}
System.out.println(pointSet.size());
}
//takes the point we want to rotate and then the angle to rotate it by
public static Point rotation_about_origin(Point P, double theta){
Point new_P = null;
double old_X = P.x;
double old_Y = P.y;
double cos_theta = Math.cos(theta);
double sin_theta = Math.sin(theta);
double new_X = old_X * cos_theta - old_Y * sin_theta;
double new_Y = old_X * sin_theta + old_Y * cos_theta;
new_P = new Point((int)Math.round(new_X),(int)Math.round(new_Y));
//if new_p is already in rotated solid
if(pointSet.contains(new_P))
System.out.println("Conflict " + P + " " + new_P);
else
//add new_P to pointSet so we know a point already rotated to that spot
pointSet.add(new_P);
return new_P;
}
private static ArrayList<Point> GenerateSolidThetaZero(int r){
int rsq = r * r;
ArrayList<Point> solidList=new ArrayList<Point>();
for (int x=-r;x<=r;x++)
for (int y=-r;y<=r;y++)
if (x*x + y*y <= rsq)
solidList.add(new Point(x,y));
return solidList;
}
public static class Pair<F,S>{
private F first; //first member of pair
private S second; //second member of pair
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public void setFirst(F first) {
this.first = first;
}
public void setSecond(S second) {
this.second = second;
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
}
}//end of pointcheck class
How would I be able to rotate the points using angles that aren't using integer multiples of 90? Where should I translate a point after rotation if the mapping is already taken?
The rotated disk will cover the exact same pixels as the original one. Therefore, you actually want to solve an assignment problem from original pixels to rotated pixels.
The cost for assigning an original pixel (ox, oy) to a corresponding pixel (cx, cy) can be expressed with a potential. For example, the distance:
E_o,c = length(R(ox, oy, theta) - (cx, cy))
, where R is the rotation operator. Alternatively, you could also try other norms, e.g. the quadratic distance.
Then, the problem is finding the correspondences that minimize the overall energy:
min_C Sum_{o \in O} E_o,c
An algorithm that solves this exactly is the Hungarian Algorithm. However, it is quite expensive if you have a large number of pixels.
Instead, here is an idea of an approximation:
In the target pixels, instead of having only the color, also store the rotated position. Then, rotate the original pixels sequentially as you did before. Round the rotated position and check if the according pixel is still occupied.
If not, store the rotated (unrounded) position along with the new color.
If it is occupied, check if the energy would decrease if you swapped the correspondences. If it does, swap them, which leaves you with the former pixel. In any case, you have an unmapped original pixel. Store this pixel in a list.
After this procedure, you have a partial correspondence map and a list of unmapped pixels. Pick any of the unmapped pixels. Analyze the target pixel's neighbors. There will probably always be an unoccupied pixel (although I have no proof for that). If so, choose this one. If not, check all neighboring pixels for the best energy decrease and swap. Continue until the list is empty.
The approximation algorithm is just an idea and I have no proof that it will actually work. But it sounds as if it is worth a try. And it will definitely be faster than the Hungarian algorithm. Though, this approximation will only work with Lp-norms having p>=1 for the potential definition.
What is the formula for calculating the position of 3D point after it has been rotated around another 3D point a certain radians/degrees? I am using Java / LWLJGL.
Could someone just fill in the blanks in the following?
public Vector3f rotate(Vector3f origin, Vector3f rotation)
{
Vector3f ret = new Vector3f();
ret.x = __________;
ret.y = __________;
ret.z = __________;
}
Consider your fixed point has coordinates (a,b,c) and moving object (x1,y1,z1) at time t1 and at (x2,y2,z2) at time t2.
option 1
you can consider projection on x-yplane and projection on y-z plane and calculate angle in that 2D space.
option 2
you can consider two vectors. say vector A and B
A=(x1-a)i+(y1-b)j+(z1-c)k
B=(x2-a)i+(y2-b)j+(z2-c)k
Now use dot product of A and B
A . B = |A||B|cos(angle)
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
}