I'm looking for a tutorial that allows me to make a simple line tracing app, no other fancy stuff such as collision. If I can get an object to follow a line drawn on the screen at the end of this week that would be wonderful.
After getting familiar with android dev, creating a few apps (calculators, converters), I think I'm ready to step it up a bit with a game containing a main loop.
I think this is exactly what I'm looking for: http://www.rengelbert.com/tutorial.php?id=182
Here is the demo: http://www.rengelbert.com/swf/LineDrawing.html
Your question is actually quite vague and it would help if you actually supplied some code snippets, variables, formulas, to help us understand your scenario. I'm going to make the following assumptions to help me guide an answer:
I have a line segment defined by (x1, y1) - (x2, y2)
I want to make an animation of an object that follows the line segment
The object needs to be orientated the correct direction
Lets assume the object moves at a speed of 1 pixel per second
Okay, now we have established the parameters, we can provide some Java code:
// Define the line segment.
double x1 = /* ... insert value here */;
double y1 = /* ... insert value here */;;
double x2 = /* ... insert value here */;;
double y2 = /* ... insert value here */;;
// Determine both the direction and the length of the line segment.
double dx = x2 - x1;
double dy = y2 - y1;
double length = Math.sqrt(dx * dx + dy * dy); // length of the line segment
double orientation = Math.atan2(dy, dx);
// Now for any time 't' between 0 and length, let's calculate the object position.
double x = x1 + t * dx / length;
double y = y1 + t * dy / length;
showObjectAt(x, y, orientation);
As to following a tutorial on building a game loop for your application, I highly recommend you follow the series on http://www.mybringback.com/ particularly Travis' Android tutorial on working with the SurfaceView object at http://www.mybringback.com/tutorial-series/3266/android-the-basics-28-introduction-to-the-surfaceview/
Related
Is there a way to check if I touched the object on the screen ? As I understand the HitResult class allows me to check if I touched the recognized and maped surface. But I want to check this I touched the object that is set on that surface.
ARCore doesn't really have a concept of an object, so we can't directly provide that. I suggest looking at ray-sphere tests for a starting point.
However, I can help with getting the ray itself (to be added to HelloArActivity):
/**
* Returns a world coordinate frame ray for a screen point. The ray is
* defined using a 6-element float array containing the head location
* followed by a normalized direction vector.
*/
float[] screenPointToWorldRay(float xPx, float yPx, Frame frame) {
float[] points = new float[12]; // {clip query, camera query, camera origin}
// Set up the clip-space coordinates of our query point
// +x is right:
points[0] = 2.0f * xPx / mSurfaceView.getMeasuredWidth() - 1.0f;
// +y is up (android UI Y is down):
points[1] = 1.0f - 2.0f * yPx / mSurfaceView.getMeasuredHeight();
points[2] = 1.0f; // +z is forwards (remember clip, not camera)
points[3] = 1.0f; // w (homogenous coordinates)
float[] matrices = new float[32]; // {proj, inverse proj}
// If you'll be calling this several times per frame factor out
// the next two lines to run when Frame.isDisplayRotationChanged().
mSession.getProjectionMatrix(matrices, 0, 1.0f, 100.0f);
Matrix.invertM(matrices, 16, matrices, 0);
// Transform clip-space point to camera-space.
Matrix.multiplyMV(points, 4, matrices, 16, points, 0);
// points[4,5,6] is now a camera-space vector. Transform to world space to get a point
// along the ray.
float[] out = new float[6];
frame.getPose().transformPoint(points, 4, out, 3);
// use points[8,9,10] as a zero vector to get the ray head position in world space.
frame.getPose().transformPoint(points, 8, out, 0);
// normalize the direction vector:
float dx = out[3] - out[0];
float dy = out[4] - out[1];
float dz = out[5] - out[2];
float scale = 1.0f / (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
out[3] = dx * scale;
out[4] = dy * scale;
out[5] = dz * scale;
return out;
}
If you're calling this several times per frame see the comment about the getProjectionMatrix and invertM calls.
Apart from Mouse Picking with Ray Casting, cf. Ian's answer, the other commonly used technique is a picking buffer, explained in detail (with C++ code) here
The trick behind 3D picking is very simple. We will attach a running
index to each triangle and have the FS output the index of the
triangle that the pixel belongs to. The end result is that we get a
"color" buffer that doesn't really contain colors. Instead, for each
pixel which is covered by some primitive we get the index of this
primitive. When the mouse is clicked on the window we will read back
that index (according to the location of the mouse) and render the
select triangle red. By combining a depth buffer in the process we
guarantee that when several primitives are overlapping the same pixel
we get the index of the top-most primitive (closest to the camera).
So in a nutshell:
Every object's draw method needs an ongoing index and a boolean for whether this draw renders the pixel buffer or not.
The render method converts the index into a grayscale color and the scene is rendered
After the whole rendering is done, retrieve the pixel color at the touch position GL11.glReadPixels(x, y, /*the x and y of the pixel you want the colour of*/). Then translate the color back to an index and the index back to an object. Voilà, you have your clicked object.
To be fair, for a mobile usecase you should probably read a 10x10 rectangle, iterate trough it and pick the first found non-background color - because touches are never that precise.
This approach works independently of the complexity of your objects
I am developing Mind mapping tool for android. I am creating this type of layout for my mind map http://www.examtime.com/files/2013/08/How-to-create-an-online-mind-map.jpg. Is there any way to create a these type of lines(as created in image) to connect objects at run time. Please Help
You should look into cubic bezier curves (splines). There is no easy way to do this simply with a Canvas. Here's an overview:
Bezier curves use 2 points (P0 - the origin & P1 - the destination), and 2 vectors (V0 - the direction the curve leaves P0, V1 - the direction the curve enters P1). For our purposes, P0, P1, V0, V1 should all be of the type PointF.
We will use t to denote the position on the path. When t = 0, the position is at P0, when t = 1, the position is at P1. Any value of t between 0 and 1 will be along the path. 0 <= t <= 1.
Now, just as an example, lets look at the curve that connects the "Nobody's perfect" node to the "Tidy up later" node.
In this case, P0 will be the right middle side of "Nobody's perfect", and P1 will be the bottom middle side of "Tidy up later".
We'll make both vectors perpendicular to the nodes they're leaving / entering, and so V0's value will be {P1.x - P0.x, 0}. This vector will point right, and will have the strength equivalent to the distance between the two nodes. In a similar manner, we'll construct the V1 vector pointing up towards the node: {0, P0.y - P1.y}
Now that you have the vectors and the points, you'll want to start drawing the curve. To do this, you'll iterate through t using some small stepping value that's dividable by 1, for example, 0.1, 0.025, 0.001, etc. Let's call this value "step" Each iteration will generate a point on the curve, and you'll want to connect a line between each of these points.
Here's a code sample for this part:
PointF start, end;
for (float t = 0; t < 1; t += step)
{
start = getBezierPosition(t);
end = getBezierPosition(t + step)
canvas.drawLine(start.x, start.y, end.x, end.y, paint);
}
Now, the hard part - calculating the position of the bezier curve at position t:
private PointF getBezierPosition(float t)
{
PointF result = new PointF();
float oneMinusT, x, y;
oneMinusT = 1 - t;
x = oneMinusT * oneMinusT * oneMinusT * P0.x +
3 * oneMinusT * oneMinusT * t * V0.x +
3 * oneMinusT * t * t * V1.x +
t * t * t * P1.x;
y = oneMinusT * oneMinusT * oneMinusT * P0.y +
3 * oneMinusT * oneMinusT * t * V0.y +
3 * oneMinusT * t * t * V1.y +
t * t * t * P1.y;
result.set(x, y);
return result;
}
This is the formula for a cubic bezier curve. You can learn more about it here.
I'll let you implement the logic of determining the locations of P0 & P1, and the directions of V0 & V1 (keep in mind that they should always be perpendicular to the node they're leaving / entering for best results). You'll need to play around with the logic to determine what side of the node the P is on, and it's most likely going to be linked to the logic you apply to positioning the nodes in the 1st place.
Also, for best results, try playing around with the stroke width of the paint you're drawing with. For example, When t = 0, use a stroke width of size 5, and when t = 1, use a stroke size of 3. Make sure to iterate smoothly between them (so if t = 0.5, the stroke width will be 4).
I'll admit, this stuff is a bit heavy on the mathematics, and might not be in the comfort zone of a beginner, but if you want to achieve dynamic curves like you showed in that picture, I'm afraid you're going to get your hands a bit dirty.
Best of luck man! Let me know how it all turned out :)
Currently I'm writing an augmented reality app and I have some problems to get the objects on my screen. It's very frustrating for me that I'm not able to transform gps-points to the correspending screen-points on my android device. I've read many articles and many other posts on stackoverflow (I've already asked similar questions) but I still need your help.
I did the perspective projection which is explained in wikipedia.
What do I have to do with the result of the perspective projection to get the resulting screenpoint?
The Wikipedia article also confused me when I read it some time ago. Here is my attempt to explain it differently:
The Situation
Let's simplify the situation. We have:
Our projected point D(x,y,z) - what you call relativePositionX|Y|Z
An image plane of size w * h
A half-angle of view α
... and we want:
The coordinates of B in the image plane (let's call them X and Y)
A schema for the X-screen-coordinates:
E is the position of our "eye" in this configuration, which I chose as origin to simplify.
The focal length f can be estimated knowing that:
tan(α) = (w/2) / f (1)
A bit of Geometry
You can see on the picture that the triangles ECD and EBM are similar, so using the Side-Splitter Theorem, we get:
MB / CD = EM / EC <=> X / x = f / z (2)
With both (1) and (2), we now have:
X = (x / z) * ( (w / 2) / tan(α) )
If we go back to the notation used in the Wikipedia article, our equation is equivalent to:
b_x = (d_x / d_z) * r_z
You can notice we are missing the multiplication by s_x / r_x. This is because in our case, the "display size" and the "recording surface" are the same, so s_x / r_x = 1.
Note: Same reasoning for Y.
Practical Use
Some remarks:
Usually, α = 45deg is used, which means tan(α) = 1. That's why this term doesn't appear in many implementations.
If you want to preserve the ratio of the elements you display, keep f constant for both X and Y, ie instead of calculating:
X = (x / z) * ( (w / 2) / tan(α) ) and Y = (y / z) * ( (h / 2) / tan(α) )
... do:
X = (x / z) * ( (min(w,h) / 2) / tan(α) ) and Y = (y / z) * ( (min(w,h) / 2) / tan(α) )
Note: when I said that "the "display size" and the "recording
surface" are the same", that wasn't quite true, and the min
operation is here to compensate this approximation, adapting the
square surface r to the potentially-rectangular surface s.
Note 2: Instead of using min(w,h) / 2, Appunta uses screenRatio=
(getWidth()+getHeight())/2 as you noticed. Both solutions preserve the elements
ratio. The focal, and thus the angle of view, will simply be a bit different,
depending on the screen's own ratio. You can actually use any function you want to
define f.
As you may have noticed on the picture above, the screen coordinates are here defined between [-w/2 ; w/2] for X and [-h/2 ; h/2] for Y, but you probably want [0 ; w] and [0 ; h] instead. X += w/2 and Y += h/2 - Problem solved.
Conclusion
I hope this will answer your questions. I'll stay near if it needs editions.
Bye!
< Self-promotion Alert > I actually made some time ago an article
about 3D projection and rendering. The implementation is in
Javascript, but it should be quite easy to translate.
So this is my second question today, I might be pushing my luck
In short making a 3D first Person, where you can move about and look around.
In My OnDrawFrame I am using
Matrix.setLookAtM(mViewMatrix, 0, eyeX , eyeY, eyeZ , lookX , lookY , lookZ , upX, upY, upZ);
To move back, forth, sidestep left etc I use something like this(forward code listed)
float v[] = {mRenderer.lookX - mRenderer.eyeX,mRenderer.lookY - mRenderer.eyeY, mRenderer.lookZ - mRenderer.eyeZ};
mRenderer.eyeX += v[0] * SPEED_MOVE;
mRenderer.eyeZ += v[2] * SPEED_MOVE;
mRenderer.lookX += v[0] * SPEED_MOVE;
mRenderer.lookZ += v[2] * SPEED_MOVE;
This works
Now I want to look around and I tried to port my iPhone openGL 1.0 code. This is left/right
float v[] = {mRenderer.lookX - mRenderer.eyeX,mRenderer.lookY - mRenderer.eyeY, mRenderer.lookZ - mRenderer.eyeZ};
if (x > mPreviousX )
{
mRenderer.lookX += ((Math.cos(SPEED_TURN / 2) * v[0]) - (Math.sin(SPEED_TURN / 2) * v[2]));
mRenderer.lookZ += ((Math.sin(SPEED_TURN / 2) * v[0]) + (Math.cos(SPEED_TURN / 2) * v[2]));
}
else
{
mRenderer.lookX -= (Math.cos(SPEED_TURN / 2) *v[0] - Math.sin(SPEED_TURN / 2) * v[2]);
mRenderer.lookZ -= (Math.sin(SPEED_TURN / 2) *v[0] + Math.cos(SPEED_TURN / 2) * v[2]);
}
This works for like 35 degrees and then goes mental?
Any ideas?
First of all I would suggest not to trace the look vector but rather forward vector, then in lookAt method use eye+forward to generate look vector. This way you can loose the update on the look completely when moving, and you don't need to compute the v vector (mRenderer.eyeX += forward.x * SPEED_MOVE;...)
To make things more simple I suggest that you normalize the vectors forward and up whenever you change them (and I will consider as you did in following methods).
Now as for rotation there are 2 ways. Either use right and up vectors to move the forward (and up) which is great for small turning (I'd say about up to 10 degrees and is capped at 90 degrees) or compute the current angle, add any angle you want and recreate the vectors.
The first mentioned method on rotating is quite simple:
vector forward = forward
vector up = up
vector right = cross(forward, up) //this one might be the other way around as up, forward :)
//going left or right:
forward = normalized(forward + right*rotationSpeedX)
//going up or down:
forward = normalized(forward + up*rotationSpeedY)
vector right = cross(forward, up) //this one might be the other way around
vector up = normalized(cross(forward, right)) //this one might be the other way around
//tilt left or right:
up = normalized(up + right*rotationZ)
The second method needs a bit trigonometry:
Normally to compute an angle you could just call atan(forward.z/forward.x) and add some if statements since the produced result is only in 180 degrees angle (I am sure you will be able to find some answers on the web to get rotation from vector though). The same goes with up vector for getting the vertical rotation. Then after you get the angles you can easily just add some degrees to the angles and recreate the vectors with sin and cos. There is a catch though, if you rotate the camera in such way, that forward faces straight up(0,1,0) you need to get the first rotation from up vector and the second from forward vector but you can avoid all that if you cap the maximum vertical angle to something like +- 85 degrees (and there are many games that actually do that). The second thing is if you use this approach your environment must support +-infinitive or this atan(forward.z/forward.x) will brake if forward.x == 0.
And some addition about the first approach. Since I see you are trying to move around the 2D space your forward vector to use with movement speed should be normalized(forward.x, 0, forward.z), it is important to normalize it or you will be moving slower if camera tilts up or down more.
Second thing is when you rotate left/right you might want to force up vector to (0,1,0) + normalize right vector and lastly recreate the up vector from forward and right. Again you then should cap the vertical rotation (up.z should be larger then some small value like .01)
It turned out my rotation code was wrong
if (x > mPreviousX )
{
mRenderer.lookX = (float) (mRenderer.eyeX + ((Math.cos(SPEED_TURN / 2) * v[0]) - (Math.sin(SPEED_TURN / 2) * v[2])));
mRenderer.lookZ = (float) (mRenderer.eyeZ + ((Math.sin(SPEED_TURN / 2) * v[0]) + (Math.cos(SPEED_TURN / 2) * v[2])));
}
else
{
mRenderer.lookX = (float) (mRenderer.eyeX + ((Math.cos(-SPEED_TURN / 2) * v[0]) - (Math.sin(-SPEED_TURN / 2) * v[2])));
mRenderer.lookZ = (float) (mRenderer.eyeZ + ((Math.sin(-SPEED_TURN / 2) * v[0]) + (Math.cos(-SPEED_TURN / 2) * v[2])));
}
I have an application of augmented reality in which I have stored information such us metro, gas stations, places of interest, etc. with the corresponding latitude and longitude.
Now, according to the orientation of the device, I would show a marker for each site in the camera view of the device. Similar to Layar and Wikitude.
It takes three days searching without stopping and have not found anyone to explain how to solve this problem.
Since information on this topic is very sparse, and I recently solved this problem on the iPhone, I thought I would share my method for anyone that can make it work with Android (there's nothing really specific to iPhone in this answer except for the Math functions sin, cos, and fmod, which can be found in java.lang.Math). These are the steps I took:
Obtain your own lat/lon and your current compass heading (lat1, lon1 and heading). On the iPhone, CLLocation returns these in degrees, but for these calculations they MUST be in radians (i.e. multiply by PI/180)
Obtain lat/lon of Points of Interest (POI) in radians (lat2 and lon2).
Calculate the distance between lat1/lon1 and lat2/lon2 using formula found here: http://www.movable-type.co.uk/scripts/latlong.html
Calculate angle to lat2/lon2 in relation to north. This is also described in the link above but I had a little bit of trouble getting this to work, here is C code for this:
double latDelta = (lat2 - lat1);
double lonDelta = (lon2 - lon1);
double y = sin(lonDelta) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2)* cos(lonDelta);
double angle = atan2(y, x); //not finished here yet
double headingDeg = compass.currentHeading;
double angleDeg = angle * 180/PI;
double heading = headingDeg*PI/180;
angle = fmod(angleDeg + 360, 360) * PI/180; //normalize to 0 to 360 (instead of -180 to 180), then convert back to radians
angleDeg = angle * 180/PI;
Using standard trigonometry, I calculate x and y. Remember, these coordinates are in 3D space, so we are not finished here yet because you still have to map them to 2D:
x = sin(angle-heading) * distance;
z = cos(angle-heading) * distance; //typically, z faces into the screen, but in our 2D map, it is a y-coordinate, as if you are looking from the bottom down on the world, like Google Maps
Finally, using the projection formula, you can calculate screen x ( I didn't do y because it was not necessary for my project, but you would need to get accelerator data and figure out if the device is perpendicular to the ground). The projection formula is found here (scroll to the very bottom): http://membres.multimania.fr/amycoders/tutorials/3dbasics.html
double screenX = (x * 256) / z
Now you can use this x coordinate to move an image or a marker on your screen. Remember a few points:
Everything must be in radians
The angle from you to the POI relative to North is angleBeteweenPoints - currentHeading
(For some reason I can't properly format the code on this computer, so if anyone wants to edit this answer, feel free).