Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;

import java.util.ArrayList;
import java.util.List;

/**
* Creates a terrain object and a collision node to go with it. Then
* drops several balls from the sky that collide with the terrain
Expand All @@ -83,7 +86,7 @@ public class TerrainTestCollision extends SimpleApplication {
protected BitmapText hintText;
PointLight pl;
Geometry lightMdl;
Geometry collisionMarker;
List<Geometry> collisionMarkers;
private BulletAppState bulletAppState;
Geometry collisionSphere;
Geometry collisionBox;
Expand All @@ -103,6 +106,7 @@ public void initialize() {

@Override
public void simpleInitApp() {
collisionMarkers = new ArrayList<>();
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
Expand Down Expand Up @@ -142,6 +146,8 @@ public void simpleInitApp() {
terrain.setLocked(false); // unlock it so we can edit the height
rootNode.attachChild(terrain);

// if set to false, only the first collision is returned and collision is slightly faster.
terrain.setSupportMultipleCollisions(true);

/**
* Create PhysicsRigidBodyControl for collision
Expand Down Expand Up @@ -227,15 +233,19 @@ public void update() {
super.update();
}

private void createCollisionMarker() {
Sphere s = new Sphere(6, 6, 1);
collisionMarker = new Geometry("collisionMarker");
collisionMarker.setMesh(s);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Orange);
collisionMarker.setMaterial(mat);
rootNode.attachChild(collisionMarker);
private void createCollisionMarkers(int num) {
for (int i = 0; i < num; i++) {
Sphere s = new Sphere(6, 6, 1);
Geometry collisionMarker = new Geometry("collisionMarker");
collisionMarker.setMesh(s);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", i == 0 ? ColorRGBA.Orange : ColorRGBA.Blue);
collisionMarker.setMaterial(mat);
rootNode.attachChild(collisionMarker);
collisionMarkers.add(collisionMarker);
}
}

private ActionListener actionListener = new ActionListener() {

public void onAction(String binding, boolean keyPressed, float tpf) {
Expand All @@ -247,24 +257,35 @@ public void onAction(String binding, boolean keyPressed, float tpf) {
terrain.setMaterial(matRock);
}
} else if (binding.equals("shoot") && !keyPressed) {

Vector3f origin = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.0f);
Vector3f direction = cam.getWorldCoordinates(new Vector2f(settings.getWidth() / 2, settings.getHeight() / 2), 0.3f);
direction.subtractLocal(origin).normalizeLocal();


Ray ray = new Ray(origin, direction);
CollisionResults results = new CollisionResults();
int numCollisions = terrain.collideWith(ray, results);
if (numCollisions > 0) {
CollisionResult hit = results.getClosestCollision();
if (collisionMarker == null) {
createCollisionMarker();

if (terrain.collideWith(ray, results) > 0) {
CollisionResult hit = results.getClosestCollision(); // sorts the collection before printing
printCollisions(results);

// Remove old markers.
for (Geometry g: collisionMarkers) {
g.removeFromParent();
}
collisionMarkers.clear();

createCollisionMarkers(results.size());

// Position Closest Collision
Vector2f loc = new Vector2f(hit.getContactPoint().x, hit.getContactPoint().z);
float height = terrain.getHeight(loc);
System.out.println("collide " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance());
collisionMarker.setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z));
System.out.println("Closest Collision: " + hit.getContactPoint() + ", height: " + height + ", distance: " + hit.getDistance());
collisionMarkers.get(0).setLocalTranslation(new Vector3f(hit.getContactPoint().x, height, hit.getContactPoint().z));

// Position Rest: When getClosestCollision has been called, the results are sorted, and thus 0 is closest.
for (int i = 1; i < results.size(); i++) {
collisionMarkers.get(i).setLocalTranslation(results.getCollision(i).getContactPoint());
}
}
} else if (binding.equals("cameraDown") && !keyPressed) {
getCamera().lookAtDirection(new Vector3f(0, -1, 0), Vector3f.UNIT_Y);
Expand Down Expand Up @@ -302,4 +323,14 @@ private void testCollision(Vector3f oldLoc) {
selectedCollisionObject.setLocalTranslation(oldLoc);
}
}

private void printCollisions(CollisionResults cr) {
System.out.println("================ Collision Results ================");
for (int i = 0; i < cr.size(); i++) {
CollisionResult res = cr.getCollision(i);
System.out.println("Result " + i);
System.out.println("\t\t" + res.toString());
}
System.out.println("================ END Collision Results ================");
}
}
4 changes: 4 additions & 0 deletions jme3-terrain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ if (!hasProperty('mainClass')) {

dependencies {
compile project(':jme3-core')
testCompile project(':jme3-core')
testCompile project(':jme3-desktop')
testCompile project(':jme3-core').sourceSets.test.output
testCompile project(':jme3-testdata')
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public class TerrainQuad extends Node implements Terrain {
private int maxLod = -1;
private BoundingBox affectedAreaBBox; // only set in the root quad

private TerrainPicker picker;
private TerrainPicker picker = new BresenhamTerrainPicker(this);
private Vector3f lastScale = Vector3f.UNIT_XYZ;

protected NeighbourFinder neighbourFinder;
Expand Down Expand Up @@ -274,20 +274,7 @@ protected void cacheTerrainTransforms() {
}

private int collideWithRay(Ray ray, CollisionResults results) {
if (picker == null)
picker = new BresenhamTerrainPicker(this);

Vector3f intersection = picker.getTerrainIntersection(ray, results);
if (intersection != null) {
if (ray.getLimit() < Float.POSITIVE_INFINITY) {
if (results.getClosestCollision().getDistance() <= ray.getLimit())
return 1; // in range
else
return 0; // out of range
} else
return 1;
} else
return 0;
return picker.getTerrainIntersection(ray, results);
}

/**
Expand Down Expand Up @@ -1915,5 +1902,23 @@ else if(((TerrainPatch) s).getQuadrant() == 4)

return hm;
}

/**
* When colliding with this terrain, is a report of all collisions wanted or only the closest collision?<br>
* If only the closest collision is required, the collision calculation will be faster.<br>
* Note: If no collision happens, it takes as long as a collision with multipleCollisions on would take.
*
* @param set Whether to support multiple collisions or not
*/
public void setSupportMultipleCollisions(boolean set) {
if (picker == null) {
// This is so that it doesn't fail at the IllegalStateException because null !instanceof Anything
throw new NullPointerException("TerrainPicker is null");
} else if (picker instanceof BresenhamTerrainPicker) {
((BresenhamTerrainPicker)picker).setSupportMultipleCollisions(set);
} else {
throw new IllegalStateException("The underlying picking implementation does not support multiple collisions");
}
}
}

Loading