www.jpct.net
jPCTAE  a 3d engine for Android => Support => Topic started by: Kaiidyn on February 09, 2011, 11:01:35 am

So I got my terrain now.
What I need now is something like polygon picking?
I want to be able to tap the terrain at any position and return the worldspace coordinates of where I tapped the terrain.
I think the most logical way to do this is to check which polygon is being tapped on, and return it's (x y and z) position..
I was looking at http://www.jpct.net/wiki/index.php/Picking but the 'fast way' does not work on android ( ? ) as world.getVisibilityList() does not exist..

You can get the polygon from a CollisionListener implementation if you need it. Keep in mind that doing a collision detection on a high polygon terrain isn't particularly fast. You might want to experiment with an OcTree (might take some time to generate on Android, currently there's no support for serializing octrees created by the desktop version) for collision detection.

Ok, got something working, however i get coordinates like
x: 0.18257418
y: 0.91287094
z: 0.36514837
public void collision(CollisionEvent ce) {
if (ce.getType()==CollisionEvent.TYPE_TARGET) {
PolygonManager pm = terrain.getPolygonManager();
SimpleVector polypos = pm.getTransformedNormal(ce.getPolygonIDs()[0]);
Log.d(polypos.x+":"+polypos.y+":"+polypos.z);
}
}
edit: got it working by changing getTransformedNormal to getTransformedVertex :D

New problem, this worked well when my player is 30 units high, now that I downscaled it to 1 unit high to make my landscape look bigger it snaps to a vertex (long distance :o)
Is it possible to get a SimpleVector on the terrain where I actually touch it? (even between vertices).

calcMinDistance() calculates the distance. The intersection point is position vector + distance*direction vector.

the position vector, is that the vector of my box, or the polypos ?
how do i get the direction i need for this?
as i assume its not the direction i get with Interact2D.reproject2D3DWS

calcMinDistance takes two vectors. The first one is the position, the second one the direction vector.

i know that, but how do i get the value of the direction vector?

Which value? I don't get the question. The wiki already states how to create the direction vector for picking and reading your post, i assume that you already did this with success... ???

The method I've always used to determine the "click point" works like this (I've not done this in an Android project, but the concept should be the same):
1) Use Interact2D.reproject2D3D to convert 2D click coordinates into 3D camera space
2) Use SimpleVector.matMul and SimpleVector.add to converted that to world space
3) Use SimpleVector.calcSub and SimpleVector.normalize to calculate the direction vector
4) Use World.calcMinDistance to obtain the distance
5) Use SimpleVector.add to obtain the 3D coordinates of the "click point".
So in an applet, I've used this procedure as follows:
// Get the mouse's coordinates on the applet window:
int x = e.getX();
int y = e.getY();
// Get the 3D coordinates in Camera space:
SimpleVector position = new SimpleVector( Interact2D.reproject2D3D(
camera, buffer, x, y ) );
// Convert the coordinates to World space:
position.matMul( camera.getBack().invert3x3() );
position.add( camera.getPosition() );
// Determine the direction from the camera position to the click point:
SimpleVector direction = position.calcSub( camera.getPosition() ).normalize();
// Calculate the distance to whatever was clicked on:
float distance = world.calcMinDistance( position, direction, 10000 );
// Check if we clicked on something:
if( distance == Object3D.COLLISION_NONE )
{
// Nope, didn't click on anything.
}
else
{
// Calculate the exact 3D coordinates for the point that was clicked:
SimpleVector collisionPoint = new SimpleVector( direction );
collisionPoint.scalarMul( distance );
collisionPoint.add( position );
}

Interesting...i stared at your code for a while being sure that it can't be correct. I wrote a test case and it actually is correct...however, it's "one step beyond". The example code in the wiki starts the calculation at the camera's position. You start the calculation at the camera's position + direction vector. So the results for distance will be different but the intersection point is the same. I think you came up with this, because it feels more natural to see the result of Interact2D.reproject2D3D as a position vector (which it actually is). However, it can be seen as a direction vector too, because the camera in camera space is always located at (0,0,0). In the wiki and other examples, i'm making use of this fact but i admit that it's not easy to understand why that works at first glance. Anyway, you can replace the matMulstep now. Newer version of jPCT have a method that returns the reprojected vector in world space.

I've just noticed that the code in the wiki was missing the normalize() calls for the direction vector. Again, the actual outcome (i.e. the intersection point) is the same, but the distance is correct only with normalize(). I've changed that in the wiki.

Thanks paul for your reply...
It works :) i have no idea how and why though,
I really need to study these kinds of maths more..
;D

it can be seen as a direction vector too, because the camera in camera space is always located at (0,0,0).
Doh! Talk about being redundant. I always seem to come up with overlycomplicated ways to do something simple..
So taking that into account and considering the new method you mentioned, the procedure becomes much simpler:
1) Use Interact2D.reproject2D3DWS and SimpleVector.normalize to convert 2D click coordinates into a direction vector in world space.
2) Use World.calcMinDistance to obtain the distance to the "click point".
3) Use SimpleVector.add to obtain the 3D coordinates of the "click point".
Using my earlier example, the code would look something like this:
// Get the mouse's coordinates on the applet window:
int x = e.getX();
int y = e.getY();
// Convert that into a direction vector in world space:
SimpleVector direction = new SimpleVector( Interact2D.reproject2D3DWS(
camera, buffer, x, y ) ).normalize();
// Calculate the distance to whatever was clicked on:
float distance = world.calcMinDistance( camera.getPosition(), direction, 10000 );
// Check if we clicked on something:
if( distance == Object3D.COLLISION_NONE )
{
// Nope, didn't click on anything.
}
else
{
// Calculate the exact 3D coordinates for the point that was clicked:
SimpleVector collisionPoint = new SimpleVector( direction );
collisionPoint.scalarMul( distance );
collisionPoint.add( camera.getPosition() );
}

Ok, so I got the goto position now..
What I need next is for the player to move toward this position but keeping terrain height on x,z in consideration.
I have been thinking about a way to do this, but fact is, I have no clue how something like this works.

You can use the same collision detection approach that are using for picking to detect the height of the terrain simply by shooting a ray from above down to the terrain and calculate the intersection point. Alien Runner does this for example. That's what i would do with a terrain on the desktop. On Android, it might be a better idea to keep some simplified data structure storing the average height of a "tile" of the terrain and interpolate between those. It's not as accurate though, but should be much faster.

I'm not sure the procedure you used to create your terrain, so this might not apply, but in my RobotBuilder project, I have the terrain built as a tiled grid with a height for each vertice. I can accurately know the height at a given 2D coordinate on the map by interpolating between the two horizontal and two vertical vertices, as long as the remaining "cattycorner" vertice isn't completely different than the other three (which I've made sure of when I originally created the terrain). I can provide some example code if that is hard to visualize from my description.

I used my heightmap2serialized object generator :p (current name terragen :\ ) to create the terrain,
some example code would not hurt (I even prefer getting some :) )
Terragen download (source) (http://93.95.149.126/Terragen.rar)

I still dont got this 'waling towards' a vector to work.
and have no clue on how to do this .. :( would prefer an example ... :)

Finally figured it out.. Is is being done using Lerp
float speed = (0.5f * ticks);
float timeToGetThere = 1.0f / playerPos.distance(gotoPos) * speed;
playerPos = lerp(playerPos, gotoPos, timeToGetThere);
player.clearTranslation();
player.translate(playerPos);
Lerp function:
private SimpleVector lerp(SimpleVector start, SimpleVector end, double time){
float x = (float) (start.x + (end.x  start.x) * time);
float y = (float) (start.y + (end.y  start.y) * time);
float z = (float) (start.z + (end.z  start.z) * time);
return new SimpleVector(x, y, z);
}
What I still need though is to get the height based on terrain... at playerPos.x, playerPos.z

The direction vector in
this.terrain.calcMinDistance(SimpleVector.create(x,y,z), direction, 100);
to cast this down, should it be 0,1,0?

Yes.

wow, that was fast :p lol ok thanks :)
Got my getTerrainHeightAtXZ function finally working now :D
public float getTerrainHeightAtXZ(float x, float y, float z){
direction = SimpleVector.create(0,1,0);
distance = this.terrain.calcMinDistance(SimpleVector.create(x,y,z), direction, 1000);
if(distance != Object3D.COLLISION_NONE){
collisionPoint = direction;
collisionPoint.scalarMul( distance );
collisionPoint.add( SimpleVector.create(x,y,z) );
Log.d(collisionPoint.y + "");
return collisionPoint.y;
}
return 0;
}
Thanks for all the help :)