How to calculate a bullet's trajectory on an isometric plane? - lua

I'd need some help from an "iso guru". I am fiddling on a game where there are two cannons placed on an isometric grid. When one cannon fires a bullet, it should fly in a curved trajectory, like shown below. While this would be an easy task on an x/y plane, I have no clue how to calculate a curved path (with variable height) on an isometric plane.
Could someone point me into the right direction please? I'd need to fire bullets from one field to any given other, while the bullets' flying altitude (the "strength" of the curve) depends on the given shot power.
Any hints? :(
Image: http://postimg.org/image/6lcqnwcrr/

This may help. The trajectory function takes some trajectory parameters (velocity, elevation, starting position and gravity) and returns a function that calcs the y position from the x position in world space.
The converter returns a function that converts between world and screen co-ords for a given projection angle.
What follows is an example of it being used to calculate the trajectory for some points in screen space.
It's really for indication purposes. It has a bunch of potential divide by zeroes but it generates trajectories that look ok for sensible elevations, projections and velocities.
-- A trajectory in world space
function trajectory(v,elevation,x0,y0,g)
x0 = x0 or 0
y0 = y0 or 0
local th = math.rad(elevation or 45)
g = g or 9.81
return function(x)
x = x-x0
local a = x*math.tan(th)
local b = (g*x^2)/(2*(v*math.cos(th))^2)
return y0+a-b
end
end
-- convert between screen and world
function converter(iso)
iso = math.rad(iso or 0)
return function(toscreen,x,y)
if toscreen then
y = y+x*math.sin(iso)
x = x*math.cos(iso)
else
x = x/math.cos(iso)
y = y-x*math.sin(iso)
end
return x,y
end
end
-- velocity 60m/s at an angle of 70 deg
t = trajectory(60,70,0,0)
-- iso projection of 30 deg
c = converter(30)
-- x in screen co-ords
for x = 0,255 do
local xx = c(false,x,0) -- x in world co-ords
local y = t(xx) -- y in world co-ords
local _,yy = c(true,xx,y) -- y in screen co-ords
local _,y0 = c(true,xx,0) --ground in screen co-ords
yy = math.floor(yy) -- not needed
if yy>y0 then print(x,yy) end -- if it's above ground
end

If there are no lateral forces you can use the 2D equation for ballistic motion in XZ plane (so y=0 at all time), then rotate via a 3D transformation around z axis to account for actual orientation of canon in 3D space. This transformation matrix is very simple, you can unfold the multiplication (write out the terms multiplied) to get the 3D equations.

Related

Moving an object with a rotation

I'm trying to make a freecam sort of tool for a rather obscure game. I'm trying to figure out how to move the XY position of the freecam based on the car's rotation value, but I'm struggling to do so. I've tried using Calculating X Y movement based on rotation angle? and modifying it a bit, but it doesn't work as intended. The game's rotation uses a float that ranges from -1 to 1, -1 being 0 degrees and 1 being 360 degrees.
Putting rot at -1 corresponds to X+
Putting rot at 0 corresponds to Z+
Putting rot at 1 corresponds to X-
Here's my cheat engine code:
speed = 10000
local yaw = math.rad((180*(getAddressList().getMemoryRecordByDescription('rot').Value))+180)
local px = getAddressList().getMemoryRecordByDescription('playerx').Value
local py = getAddressList().getMemoryRecordByDescription('playery').Value
local pz = getAddressList().getMemoryRecordByDescription('playerz').Value
local siny = math.sin(yaw) -- Sine of Horizontal (Yaw)
local cosy = math.cos(yaw) -- Cosine of Horizontal (Yaw)
getAddressList().getMemoryRecordByDescription('playerx').Value = ((getAddressList().getMemoryRecordByDescription('playerx').Value)+(cosy*speed))
getAddressList().getMemoryRecordByDescription('playerz').Value = ((getAddressList().getMemoryRecordByDescription('playerz').Value)+(siny*speed))
print(yaw)

Estimating distance from camera to ground plane point

How can I calculate distance from camera to a point on a ground plane from an image?
I have the intrinsic parameters of the camera and the position (height, pitch).
Is there any OpenCV function that can estimate that distance?
You can use undistortPoints to compute the rays backprojecting the pixels, but that API is rather hard to use for your purpose. It may be easier to do the calculation "by hand" in your code. Doing it at least once will also help you understand what exactly that API is doing.
Express your "position (height, pitch)" of the camera as a rotation matrix R and a translation vector t, representing the coordinate transform from the origin of the ground plane to the camera. That is, given a point in ground plane coordinates Pg = [Xg, Yg, Zg], its coordinates in camera frame are given by
Pc = R * Pg + t
The camera center is Cc = [0, 0, 0] in camera coordinates. In ground coordinates it is then:
Cg = inv(R) * (-t) = -R' * t
where inv(R) is the inverse of R, R' is its transpose, and the last equality is due to R being an orthogonal matrix.
Let's assume, for simplicity, that the the ground plane is Zg = 0.
Let K be the matrix of intrinsic parameters. Given a pixel q = [u, v], write it in homogeneous image coordinates Q = [u, v, 1]. Its location in camera coordinates is
Qc = Ki * Q
where Ki = inv(K) is the inverse of the intrinsic parameters matrix. The same point in world coordinates is then
Qg = R' * Qc + Cg
All the points Pg = [Xg, Yg, Zg] that belong to the ray from the camera center through that pixel, expressed in ground coordinates, are then on the line
Pg = Cg + lambda * (Qg - Cg)
for lambda going from 0 to positive infinity. This last formula represents three equations in ground XYZ coordinates, and you want to find the values of X, Y, Z and lambda where the ray intersects the ground plane. But that means Zg=0, so you have only 3 unknowns. Solve them (you recover lambda from the 3rd equation, then substitute in the first two), and you get Xg and Yg of the solution to your problem.

converting gps coordinates to opengl word in AR

i have a list of gps coordinates (long,lat) and i have my current position (long,lat).
i found out that by subtracting the two coordinates i find the relative coordinates from my position, and that coordinates i use in my AR app to draw the pois in the opengl world.
the problem is that far-away coordinates will still be too far to "see", so i want an equation to translate everything to be close to my position, but with their original relative position.
double kGpsToOpenglCoorRatio = 1000;
- (void)convertGlobalCoordinatesToLocalCoordinates:(double)latitude x_p:(double *)x_p longitude:(double)longitude y_p:(double *)y_p {
*x_p = ((latitude - _userLocation.coordinate.latitude) * kGpsToOpenglCoorRatio);
*y_p = ((longitude - _userLocation.coordinate.longitude) * kGpsToOpenglCoorRatio);
}
i tried applying Square root in order to give them a "distance limit", but their positions got messed up relatively to their original position.
This might be because GPS uses a spherical(ish) coordinate system, and you're trying to directly map it to a cartesian coordinate system (a plane).
What you could to do is convert your GPS coordinates to a local reference plane, rather than map them directly. If you consider your own location the origin of your coordinate system, you can get the polar coordinates of the points on the ground plane relative to the origin and true north by using great circle distance (r) and bearing (theta) between your location and the remote coordinate, and then covert that to cartesian coordinates using (x,y) = (r*cos(theta), r*sin(theta)).
Better again for your case, once you have the great circle bearing, you can just foreshorten r (the distance). That will drag the points closer to you in both x and y, but they'll still be at the correct relative bearing, you'll just need to indicate this somehow.
Another approach is to scale the size of the objects you're visualizing so that they get larger with distance to compensate for perspective. This way you can just directly use the correct position and orientation.
This page has the bearing/distance algorithms: http://www.movable-type.co.uk/scripts/latlong.html
I ended up solving it using the equation of the gps coordinate intercepted with the circle i want all the pois to appear on, it works perfectly. I didn't use bearings anywhere.
here is the code if anyone interested:
- (void)convertGlobalCoordinatesToLocalCoordinates:(double)latitude x_p:(double *)x_p longitude:(double)longitude y_p:(double *)y_p {
double x = (latitude - _userLocation.coordinate.latitude) * kGpsToOpenglCoorRatio;
double y = (longitude - _userLocation.coordinate.longitude) * kGpsToOpenglCoorRatio;
y = (y == 0 ? y = 0.0001 : y);
x = (x == 0 ? x = 0.0001 : x);
double slope = x / ABS(y);
double outY = sqrt( kPoiRadius / (1+pow(slope,2)) );
double outX = slope * outY;
if (y < 0) {
outY = -1 * outY;
}
*x_p = outX;
*y_p = outY;
}

OpenGL: How to lathe a 2D shape into 3D?

I have an OpenGL program (written in Delphi) that lets user draw a polygon. I want to automatically revolve (lathe) it around an axis (say, Y asix) and get a 3D shape.
How can I do this?
For simplicity, you could force at least one point to lie on the axis of rotation. You can do this easily by adding/subtracting the same value to all the x values, and the same value to all the y values, of the points in the polygon. It will retain the original shape.
The rest isn't really that hard. Pick an angle that is fairly small, say one or two degrees, and work out the coordinates of the polygon vertices as it spins around the axis. Then just join up the points with triangle fans and triangle strips.
To rotate a point around an axis is just basic Pythagoras. At 0 degrees rotation you have the points at their 2-d coordinates with a value of 0 in the third dimension.
Lets assume the points are in X and Y and we are rotating around Y. The original 'X' coordinate represents the hypotenuse. At 1 degree of rotation, we have:
sin(1) = z/hypotenuse
cos(1) = x/hypotenuse
(assuming degree-based trig functions)
To rotate a point (x, y) by angle T around the Y axis to produce a 3d point (x', y', z'):
y' = y
x' = x * cos(T)
z' = x * sin(T)
So for each point on the edge of your polygon you produce a circle of 360 points centered on the axis of rotation.
Now make a 3d shape like so:
create a GL 'triangle fan' by using your center point and the first array of rotated points
for each successive array, create a triangle strip using the points in the array and the points in the previous array
finish by creating another triangle fan centered on the center point and using the points in the last array
One thing to note is that usually, the kinds of trig functions I've used measure angles in radians, and OpenGL uses degrees. To convert degrees to radians, the formula is:
degrees = radians / pi * 180
Essentially the strategy is to sweep the profile given by the user around the given axis and generate a series of triangle strips connecting adjacent slices.
Assume that the user has drawn the polygon in the XZ plane. Further, assume that the user intends to sweep around the Z axis (i.e. the line X = 0) to generate the solid of revolution, and that one edge of the polygon lies on that axis (you can generalize later once you have this simplified case working).
For simple enough geometry, you can treat the perimeter of the polygon as a function x = f(z), that is, assume there is a unique X value for every Z value. When we go to 3D, this function becomes r = f(z), that is, the radius is unique over the length of the object.
Now, suppose we want to approximate the solid with M "slices" each spanning 2 * Pi / M radians. We'll use N "stacks" (samples in the Z dimension) as well. For each such slice, we can build a triangle strip connecting the points on one slice (i) with the points on slice (i+1). Here's some pseudo-ish code describing the process:
double dTheta = 2.0 * pi / M;
double dZ = (zMax - zMin) / N;
// Iterate over "slices"
for (int i = 0; i < M; ++i) {
double theta = i * dTheta;
double theta_next = (i+1) * dTheta;
// Iterate over "stacks":
for (int j = 0; j <= N; ++j) {
double z = zMin + i * dZ;
// Get cross-sectional radius at this Z location from your 2D model (was the
// X coordinate in the 2D polygon):
double r = f(z); // See above definition
// Convert 2D to 3D by sweeping by angle represented by this slice:
double x = r * cos(theta);
double y = r * sin(theta);
// Get coordinates of next slice over so we can join them with a triangle strip:
double xNext = r * cos(theta_next);
double yNext = r * sin(theta_next);
// Add these two points to your triangle strip (heavy pseudocode):
strip.AddPoint(x, y, z);
strip.AddPoint(xNext, yNext, z);
}
}
That's the basic idea. As sje697 said, you'll possibly need to add end caps to keep the geometry closed (i.e. a solid object, rather than a shell). But this should give you enough to get you going. This can easily be generalized to toroidal shapes as well (though you won't have a one-to-one r = f(z) function in that case).
If you just want it to rotate, then:
glRotatef(angle,0,1,0);
will rotate it around the Y-axis. If you want a lathe, then this is far more complex.

physics game programming box2d - orientating a turret-like object using torques

This is a problem I hit when trying to implement a game using the LÖVE engine, which covers box2d with Lua scripting.
The objective is simple: A turret-like object (seen from the top, on a 2D environment) needs to orientate itself so it points to a target.
The turret is on the x,y coordinates, and the target is on tx, ty. We can consider that x,y are fixed, but tx, ty tend to vary from one instant to the other (i.e. they would be the mouse cursor).
The turret has a rotor that can apply a rotational force (torque) on any given moment, clockwise or counter-clockwise. The magnitude of that force has an upper limit called maxTorque.
The turret also has certain rotational inertia, which acts for angular movement the same way mass acts for linear movement. There's no friction of any kind, so the turret will keep spinning if it has an angular velocity.
The turret has a small AI function that re-evaluates its orientation to verify that it points to the right direction, and activates the rotator. This happens every dt (~60 times per second). It looks like this right now:
function Turret:update(dt)
local x,y = self:getPositon()
local tx,ty = self:getTarget()
local maxTorque = self:getMaxTorque() -- max force of the turret rotor
local inertia = self:getInertia() -- the rotational inertia
local w = self:getAngularVelocity() -- current angular velocity of the turret
local angle = self:getAngle() -- the angle the turret is facing currently
-- the angle of the like that links the turret center with the target
local targetAngle = math.atan2(oy-y,ox-x)
local differenceAngle = _normalizeAngle(targetAngle - angle)
if(differenceAngle <= math.pi) then -- counter-clockwise is the shortest path
self:applyTorque(maxTorque)
else -- clockwise is the shortest path
self:applyTorque(-maxTorque)
end
end
... it fails. Let me explain with two illustrative situations:
The turret "oscillates" around the targetAngle.
If the target is "right behind the turret, just a little clock-wise", the turret will start applying clockwise torques, and keep applying them until the instant in which it surpasses the target angle. At that moment it will start applying torques on the opposite direction. But it will have gained a significant angular velocity, so it will keep going clockwise for some time... until the target will be "just behind, but a bit counter-clockwise". And it will start again. So the turret will oscillate or even go in round circles.
I think that my turret should start applying torques in the "opposite direction of the shortest path" before it reaches the target angle (like a car braking before stopping).
Intuitively, I think the turret should "start applying torques on the opposite direction of the shortest path when it is about half-way to the target objective". My intuition tells me that it has something to do with the angular velocity. And then there's the fact that the target is mobile - I don't know if I should take that into account somehow or just ignore it.
How do I calculate when the turret must "start braking"?
Think backwards. The turret must "start braking" when it has just enough room to decelerate from its current angular velocity to a dead stop, which is the same as the room it would need to accelerate from a dead stop to its current angular velocity, which is
|differenceAngle| = w^2*Inertia/2*MaxTorque.
You may also have some trouble with small oscillations around the target if your step time is too large; that'll require a little more finesse, you'll have to brake a little sooner, and more gently. Don't worry about that until you see it.
That should be good enough for now, but there's another catch that may trip you up later: deciding which way to go. Sometimes going the long way around is quicker, if you're going that way already. In that case you have to decide which way takes less time, which is not difficult, but again, cross that bridge when you come to it.
EDIT:
My equation was wrong, it should be Inertia/2*maxTorque, not 2*maxTorque/Inertia (that's what I get for trying to do algebra at the keyboard). I've fixed it.
Try this:
local torque = maxTorque;
if(differenceAngle > math.pi) then -- clockwise is the shortest path
torque = -torque;
end
if(differenceAngle < w*w*Inertia/(2*MaxTorque)) then -- brake
torque = -torque;
end
self:applyTorque(torque)
This seems like a problem that can be solved with a PID controller. I use them in my work to control a heater output to set a temperature.
For the 'P' component, you apply a torque that is proportional to the difference between the turret angle and the target angle i.e.
P = P0 * differenceAngle
If this still oscillates too much (it will a bit) then add an 'I' component,
integAngle = integAngle + differenceAngle * dt
I = I0 * integAngle
If this overshoots too much then add a 'D' term
derivAngle = (prevDifferenceAngle - differenceAngle) / dt
prevDifferenceAngle = differenceAngle
D = D0 * derivAngle
P0, I0 and D0 are constants that you can tune to get the behaviour that you want (i.e. how fast the turrets respond etc.)
Just as a tip, normally P0 > I0 > D0
Use these terms to determine how much torque is applied i.e.
magnitudeAngMomentum = P + I + D
EDIT:
Here is an application written using Processing that uses PID. It actually works fine without I or D. See it working here
// Demonstration of the use of PID algorithm to
// simulate a turret finding a target. The mouse pointer is the target
float dt = 1e-2;
float turretAngle = 0.0;
float turretMass = 1;
// Tune these to get different turret behaviour
float P0 = 5.0;
float I0 = 0.0;
float D0 = 0.0;
float maxAngMomentum = 1.0;
void setup() {
size(500, 500);
frameRate(1/dt);
}
void draw() {
background(0);
translate(width/2, height/2);
float angVel, angMomentum, P, I, D, diffAngle, derivDiffAngle;
float prevDiffAngle = 0.0;
float integDiffAngle = 0.0;
// Find the target
float targetX = mouseX;
float targetY = mouseY;
float targetAngle = atan2(targetY - 250, targetX - 250);
diffAngle = targetAngle - turretAngle;
integDiffAngle = integDiffAngle + diffAngle * dt;
derivDiffAngle = (prevDiffAngle - diffAngle) / dt;
P = P0 * diffAngle;
I = I0 * integDiffAngle;
D = D0 * derivDiffAngle;
angMomentum = P + I + D;
// This is the 'maxTorque' equivelant
angMomentum = constrain(angMomentum, -maxAngMomentum, maxAngMomentum);
// Ang. Momentum = mass * ang. velocity
// ang. velocity = ang. momentum / mass
angVel = angMomentum / turretMass;
turretAngle = turretAngle + angVel * dt;
// Draw the 'turret'
rotate(turretAngle);
triangle(-20, 10, -20, -10, 20, 0);
prevDiffAngle = diffAngle;
}
Ok I believe I got the solution.
This is based on Beta's idea, but with some necessary tweaks. Here it goes:
local twoPi = 2.0 * math.pi -- small optimisation
-- returns -1, 1 or 0 depending on whether x>0, x<0 or x=0
function _sign(x)
return x>0 and 1 or x<0 and -1 or 0
end
-- transforms any angle so it is on the 0-2Pi range
local _normalizeAngle = function(angle)
angle = angle % twoPi
return (angle < 0 and (angle + twoPi) or angle)
end
function Turret:update(dt)
local tx, ty = self:getTargetPosition()
local x, y = self:getPosition()
local angle = self:getAngle()
local maxTorque = self:getMaxTorque()
local inertia = self:getInertia()
local w = self:getAngularVelocity()
local targetAngle = math.atan2(ty-y,tx-x)
-- distance I have to cover
local differenceAngle = _normalizeAngle(targetAngle - angle)
-- distance it will take me to stop
local brakingAngle = _normalizeAngle(_sign(w)*2.0*w*w*inertia/maxTorque)
local torque = maxTorque
-- two of these 3 conditions must be true
local a,b,c = differenceAngle > math.pi, brakingAngle > differenceAngle, w > 0
if( (a and b) or (a and c) or (b and c) ) then
torque = -torque
end
self:applyTorque(torque)
end
The concept behind this is simple: I need to calculate how much "space" (angle) the turret needs in order to stop completely. That depends on how fast the turret moves and how much torque can it apply to itself. In a nutshell, that's what I calculate with brakingAngle.
My formula for calculating this angle is slightly different from Beta's. A friend of mine helped me out with the physics, and well, they seem to be working. Adding the sign of w was my idea.
I had to implement a "normalizing" function, which puts any angle back to the 0-2Pi zone.
Initially this was an entangled if-else-if-else. Since the conditions where very repetitive, I used some boolean logic in order to simplify the algorithm. The downside is that, even if it works ok and it is not complicated, it doesn't transpire why it works.
Once the code is a little bit more depurated I'll post a link to a demo here.
Thanks a lot.
EDIT: Working LÖVE sample is now available here. The important stuff is inside actors/AI.lua (the .love file can be opened with a zip uncompressor)
You could find an equation for angular velocity vs angular distance for the rotor when accelerating torque is applied, and find the same equation for when the braking torque is applied.
Then modify the breaking equation such that it intesects the angular distance axis at the required angle. With these two equations you can calculate the angular distance at which they intersect which would give you the breaking point.
Could be totally wrong though, not done any like this for a long time. Probably a simpler solution. I'm assuming that acceleration is not linear.
A simplified version of this problem is pretty simple to solve. Assume the motor has infinite torque, ie it can change velocity instantaneously. This is obviously not physically accurate but makes the problem much simpler to solve and in the end isn't a problem.
Focus on a target angular velocity not a target angle.
current_angle = "the turrets current angle";
target_angle = "the angle the turret should be pointing";
dt = "the timestep used for Box2D, usually 1/60";
max_omega = "the maximum speed a turret can rotate";
theta_delta = target_angle - current_angle;
normalized_delta = normalize theta_delta between -pi and pi;
delta_omega = normalized_deta / dt;
normalized_delta_omega = min( delta_omega, max_omega );
turret.SetAngularVelocity( normalized_delta_omega );
The reason this works is the turret automatically tries to move slower as it reaches its target angle.
The infinite torque is masked by the fact that the turret doesn't try to close the distance instantaneously. Instead it tries to close the distance in one timestep. Also since the range of -pi to pi is pretty small the possibly insane accelerations never show themselves. The maximum angular velocity keep the turret's rotations looking realistic.
I've never worked out the real equation for solving with torque instead of angular velocity, but I imagine it will look a lot like the PID equations.

Resources