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;
}
I'm writing a very basic raycaster for a 3D scene with triangulated objects and everything worked fine until I decided to try casting rays from points other than the origin of the scene (0/0/0).
However, when I changed to origin of the ray to (0/1/0) the intersection test suddenly returned a wrong intersection point for one of the triangles.
I'm deliberately "shooting" the rays into the direction of the center of the triangle, so obviously this should be the intersection point. I just simply don't know what's exactly leading to the wrong results in my code.
(I'm not using Möller-Trumbore at the moment because I'd like to start out with a simpler, more basic approach, but I will switch to Möller-Trumbore when optimizing the code.)
These are the coordinates of my the three vertices of the above mentioned triangle:
-2.0/2.0/0.0 | 0.0/3.0/2.0 | 2.0/2.0/0.0
This is the center of the triangle:
0.0/2.3333333333333335/0.6666666666666666
This is my ray (origin + t * Direction):
Origin: 0.0/1.0/0.0
Direction (normalized): 0.0/0.894427190999916/0.4472135954999579
This is the obviously wrong intersection point my program calculated (before checking and finding out that the point is not even on the triangle:
0.0/5.0/1.9999999999999996
So yeah, it's not hard to see (even without a calculator) that the ray should hit the triangle at its center at roughly t = 1.5. My code, however, returns the value 4.472135954999579 for t.
Here's my code for the intersection check:
public Vector intersectsWithTriangle(Ray ray, Triangle triangle) {
boolean intersects = false;
Vector triangleNormal = triangle.getNormalVector();
double normalDotRayDirection = triangleNormal.dotProduct(ray.getDirection());
if(Math.abs(normalDotRayDirection) == 0) {
// parallel
return null;
}
double d = triangleNormal.dotProduct(triangle.getV1AsVector());
double t = (triangleNormal.dotProduct(ray.getOrigin()) + d) / normalDotRayDirection;
// Check if triangle is behind ray
if (t < 0) return null;
// Get point of intersection between ray and triangle
Vector intersectionPoint = ray.getPosAt(t);
// Check if point is inside the triangle
if(isPointInTriangle(intersectionPoint, triangle, triangleNormal)) {
intersects = true;
return intersectionPoint;
}
return null;
}
Any ideas what's wrong with the line that calculates t?
If the ray is given by o + t*v and the triangle plane is defined by normal vector n and point p, then we are looking for t s.t. n*(o + t*v) = n*p which gives t = (n*p - n*o)/(n*v). So you seem to have a sign error and the correct computation for t should be:
double t = (d - triangleNormal.dotProduct(ray.getOrigin())) / normalDotRayDirection;
As long as the ray origin was (0,0,0) the wrong sign did not matter.
I am trying to figure out how to implement the following method in java;
** Point rot90()** Query for a new Cartesian Point equivalent to this Cartesian point rotated by 90 degrees
I have no idea how to go about creating this method. However, I do believe that pulling the point (x,y) and outputting new point (y,x*-1) is equivalent to rotating 90 degrees. Basically the old y coordinate becomes the nee x coordinate and the new y coordinate is the old x coordinate multiplied by negative 1. Any thoughts on how to set up this method would be greatly appreciated. Thanks.
this is what I have so far
public Point rot90(){
Point rotated = new Point();
rotated.yCoord = xCoord*-1;
rotated.xCoord = yCoord;
return rotated;
}
I know this doesn't work as pointed out when I try to compile it. Any suggestions?
Your method needs an argument.
public Point rot90(Point p){
Point rotated = new Point();
rotated.yCoord = -p.xCoord;
rotated.xCoord = p.yCoord;
return rotated;
}
If your Point class has a constructor that can take coordinates then you can make it shorter.
public Point rot90(Point p){
return new Point(p.yCoord, -p.xCoord);
}
What exactly is the problem? Code not working or the result not as expected?
In the complex plane interpretation of Euclidean geometry, rotation (x,y) by 90° is, by definition of the complex unit, the multiplication of x+i·y by i. Since
i·(x+i·y)=-y+i·x,
the rotated point has the coordinates (-y,x), and your code implements the rotation by 270° =^= -90°.
In general, the rotation by an angle a amounts to the multiplication
(cos(a)+i*sin(a)) * (x+i*y)
Note that the screen coordinate system is the mirror image of the Cartesian plane, the y axis points down. Thus angle orientation is reversed.
In a Java 2D game, I have a rectangular sprite of a tank. The sprite can rotate in any angle, and travel in the direction of that angle.
This sprite needs to have a bounding box, so I can detect collision to it.
This bounding box needs to:
Follow the sprite around the screen.
Rotate when the sprite rotates.
Obviously it should be invisible, but right now I'm drawing the box on the screen to see if it works. It doesn't.
My problem is this:
When the sprite travels parallel to the x axis or y axis, the box follows correctly and keeps 'wrapping' the sprite precisely.
But when the sprites travles diagonaly, the box doesn't follow the sprite correctly.
Sometimes it moves too much along the x axis and too little along the y axis. Sometimes the opposite. And maybe sometimes too much both or too little on both. Not sure.
Could you look at my code and tell me if you see anything wrong?
(Please note: The bounding box most of the time is actually just two arrays of coordinates, each one containing 4 values. The coordinates are used to form a Polygon when collision is checked, or when the box is drawn to the screen).
Relevant code from the Entity class, the superclass of Tank:
int[] xcoo = new int[4]; // coordinates of 4 vertices of the bounding box.
int[] ycoo = new int[4];
double x,y; // current position of the sprite.
double dx,dy; // how much to move the sprite, and the vertices of the bounding box.
double angle; // current angle of movement and rotation of sprite and bounding-box.
// Returns a Polygon object, that's the bounding box.
public Polygon getPolyBounds(){ return new Polygon(xcoo,ycoo,xcoo.length) ; }
public void move(){
// Move sprite
x += dx;
y += dy;
// Move vertices of bounding box.
for(int i=0;i<4;i++){
xcoo[i] += dx;
ycoo[i] += dy;
}
// Code to rotate the bounding box according to the angle, will be added later.
// ....
}
Relevant code from the Board class, the class that runs most of the game.
This is from the game-loop.
// keysPressed1 is an array of flags to tell which key is currently pressed.
// if left arrow is pressed
if(keysPressed1[0]==true)
tank1.setAngle(tank1.getAngle()-3);
// if right arrow is pressed
if(keysPressed1[1]==true)
tank1.setAngle(tank1.getAngle()+3);
// if up arrow is pressed (sets the direction to move, based on angle).
if(keysPressed1[2]==true){
tank1.setDX(2 * Math.cos(Math.toRadians(tank1.getAngle())));
tank1.setDY(2 * Math.sin(Math.toRadians(tank1.getAngle())));
tank1.move(); // should move both the sprite, and it's bounding box.
}
Thanks a lot for your help. If you need me to explain something about the code so you can help me, please say so.
Your sprite is using doubles and your bounding box is using ints, see these declarations:
int[] xcoo = new int[4];
double x, y
And the following updates:
(double dx, dy, showing it is a double)
x += dx
xcoo[i] += dx
In the latter (the bounding box) you are adding an int to a double which causes it to drop it's decimal places as it is being cast to an integer.
Hence why they do not follow the sprite exactly, as an int can never follow a double.
To solve this you need xcoo, ycoo and corresponding methods to work with double instead of int.
Update: So Polygon only takes Integers appereantly, to solve that take a look at the following question: Polygons with Double Coordinates
You should be using Path2D.Double
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
}