Howdy, monkeys! I’m having a problem with click shooting, which I stole the code from this tutorial… I can shoot anything BUT the terrain if I click on it… However, with just a normal raycast from the camera (with the camera’s direction) I can shoot everything (including the terrain)…
Sooo, does anyone know why that is, and if I can have it shoot everything (including terrain)? (don’t worry, I remembered to have it collide with the rootNode both times ) Thanks!
I wrote up a test case (combo of TestTerrainAdvanced and HelloPicking). Right click to send a ray from the click, press space to just send a ray from the middle of the camera… Try to shoot both the shootables and the terrain with both methods:
[java]
package test;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
import com.jme3.bounding.BoundingBox;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
/**
-
Uses the terrain’s lighting texture with normal maps and lights.
-
@author bowens
*/
public class TerrainWontClickShootTestCase extends SimpleApplication {private TerrainQuad terrain;
Material matTerrain;
Material matWire;
boolean wireframe = false;
private Geometry mark;
private float dirtScale = 16;
private float darkRockScale = 32;
private float pinkRockScale = 32;
private float riverRockScale = 80;
private float grassScale = 32;
private float brickScale = 128;
private float roadScale = 200;
private Node shootables;public static void main(String[] args) {
TerrainWontClickShootTestCase app = new TerrainWontClickShootTestCase();
app.start();
}@Override
public void initialize() {
super.initialize();}
@Override
public void simpleInitApp() {
setupKeys();
initMark();
shootables = new Node(“Shootables”);
rootNode.attachChild(shootables);
shootables.attachChild(makeCube(“a Dragon”, -2f, 0f, 1f));
shootables.attachChild(makeCube(“a tin can”, 1f, -2f, 0f));
shootables.attachChild(makeCube(“the Sheriff”, 0f, 1f, -2f));
shootables.attachChild(makeCube(“the Deputy”, 1f, 0f, -4f));// First, we load up our textures and the heightmap texture for the terrain // TERRAIN TEXTURE material matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md"); matTerrain.setBoolean("useTriPlanarMapping", false); matTerrain.setFloat("Shininess", 0.0f); // ALPHA map (for splat textures) matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png")); matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png")); // this material also supports 'AlphaMap_2', so you can get up to 12 diffuse textures // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); Texture heightMapImage = assetManager.loadTexture(hmKey); // DIRT texture, Diffuse textures 0 to 3 use the first AlphaMap Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", dirt); matTerrain.setFloat("DiffuseMap_0_scale", dirtScale); // DARK ROCK texture Texture darkRock = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); darkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_1", darkRock); matTerrain.setFloat("DiffuseMap_1_scale", darkRockScale); // PINK ROCK texture Texture pinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock.PNG"); pinkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_2", pinkRock); matTerrain.setFloat("DiffuseMap_2_scale", pinkRockScale); // RIVER ROCK texture, this texture will use the next alphaMap: AlphaMap_1 Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); riverRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_3", riverRock); matTerrain.setFloat("DiffuseMap_3_scale", riverRockScale); // GRASS texture Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_4", grass); matTerrain.setFloat("DiffuseMap_4_scale", grassScale); // BRICK texture Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); brick.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_5", brick); matTerrain.setFloat("DiffuseMap_5_scale", brickScale); // ROAD texture Texture road = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); road.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_6", road); matTerrain.setFloat("DiffuseMap_6_scale", roadScale); // diffuse textures 0 to 3 use AlphaMap // diffuse textures 4 to 7 use AlphaMap_1 // diffuse textures 8 to 11 use AlphaMap_2 // NORMAL MAPS Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMapDirt.setWrap(WrapMode.Repeat); Texture normalMapPinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock_normal.png"); normalMapPinkRock.setWrap(WrapMode.Repeat); Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMapGrass.setWrap(WrapMode.Repeat); Texture normalMapRoad = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMapRoad.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMapDirt); matTerrain.setTexture("NormalMap_1", normalMapPinkRock); matTerrain.setTexture("NormalMap_2", normalMapPinkRock); matTerrain.setTexture("NormalMap_4", normalMapGrass); matTerrain.setTexture("NormalMap_6", normalMapRoad); // WIREFRAME material (used to debug the terrain, only useful for this test case) matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); matWire.getAdditionalRenderState().setWireframe(true); matWire.setColor("Color", ColorRGBA.Green); createSky(); // CREATE HEIGHTMAP AbstractHeightMap heightmap = null; try { heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.3f); heightmap.load(); heightmap.smooth(0.9f, 1); } catch (Exception e) { e.printStackTrace(); } /*
-
Here we create the actual terrain. The tiles will be 65x65, and the total size of the
-
terrain will be 513x513. It uses the heightmap we created to generate the height values.
/
/* -
Optimal terrain patch size is 65 (64x64).
-
The total size is up to you. At 1025 it ran fine for me (200+FPS), however at
-
size=2049 it got really slow. But that is a jump from 2 million to 8 million triangles…
*/
terrain = new TerrainQuad(“terrain”, 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain.addControl(control);
terrain.setMaterial(matTerrain);
terrain.setModelBound(new BoundingBox());
terrain.updateModelBound();
terrain.setLocalTranslation(0, -100, 0);
terrain.setLocalScale(1f, 1f, 1f);
rootNode.attachChild(terrain);DirectionalLight light = new DirectionalLight(); light.setDirection((new Vector3f(-0.1f, -0.1f, -0.1f)).normalize()); rootNode.addLight(light); cam.setLocation(new Vector3f(0, 10, -10)); cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y); flyCam.setMoveSpeed(100); flyCam.setDragToRotate(true);
}
private void setupKeys() {
inputManager.addMapping(“wireframe”, new KeyTrigger(KeyInput.KEY_T));
inputManager.addListener(actionListener, “wireframe”);
inputManager.addMapping(“shoot”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, “shoot”);
inputManager.addMapping(“clickShoot”, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addListener(actionListener, “clickShoot”);
}
private ActionListener actionListener = new ActionListener() {public void onAction(String name, boolean pressed, float tpf) { if (name.equals("wireframe") && !pressed) { wireframe = !wireframe; if (!wireframe) { terrain.setMaterial(matWire); } else { terrain.setMaterial(matTerrain); } }else if(name.equals("shoot")){ if(pressed){ // 1. Reset results list. CollisionResults results = new CollisionResults(); // 2. Aim the ray from cam loc to cam direction. Ray ray = new Ray(cam.getLocation(), cam.getDirection()); // 3. Collect intersections between Ray and Shootables in results list. rootNode.collideWith(ray, results); // 4. Print the results System.out.println("----- Collisions? " + results.size() + "-----"); for (int i = 0; i < results.size(); i++) { // For each hit, we know distance, impact point, name of geometry. float dist = results.getCollision(i).getDistance(); Vector3f pt = results.getCollision(i).getContactPoint(); String hit = results.getCollision(i).getGeometry().getName(); System.out.println("* Collision #" + i); System.out.println(" You shot " + hit + " at " + pt + ", " + dist + " wu away."); } // 5. Use the results (we mark the hit object) if (results.size() > 0) { // The closest collision point is what was truly hit: CollisionResult closest = results.getClosestCollision(); // Let's interact - we mark the hit with a red dot. mark.setLocalTranslation(closest.getContactPoint()); rootNode.attachChild(mark); } else { // No hits? Then remove the red mark. rootNode.detachChild(mark); } } }else if(name.equals("clickShoot")){ if(pressed){ CollisionResults results = new CollisionResults(); // 2. Aim the ray from cam loc to cam direction. Vector2f click2d = inputManager.getCursorPosition(); Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone(); Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d); // Aim the ray from the clicked spot forwards. Ray ray = new Ray(click3d, dir); // 3. Collect intersections between Ray and Shootables in results list. rootNode.collideWith(ray, results); // 4. Print the results System.out.println("----- Collisions? " + results.size() + "-----"); for (int i = 0; i < results.size(); i++) { // For each hit, we know distance, impact point, name of geometry. float dist = results.getCollision(i).getDistance(); Vector3f pt = results.getCollision(i).getContactPoint(); String hit = results.getCollision(i).getGeometry().getName(); System.out.println("* Collision #" + i); System.out.println(" You shot " + hit + " at " + pt + ", " + dist + " wu away."); } // 5. Use the results (we mark the hit object) if (results.size() > 0) { // The closest collision point is what was truly hit: CollisionResult closest = results.getClosestCollision(); // Let's interact - we mark the hit with a red dot. mark.setLocalTranslation(closest.getContactPoint()); rootNode.attachChild(mark); } else { // No hits? Then remove the red mark. rootNode.detachChild(mark); } } } }
};
private void createSky() {
Texture west = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_west.jpg”);
Texture east = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_east.jpg”);
Texture north = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_north.jpg”);
Texture south = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_south.jpg”);
Texture up = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_up.jpg”);
Texture down = assetManager.loadTexture(“Textures/Sky/Lagoon/lagoon_down.jpg”);Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down); sky.setLocalTranslation(new Vector3f(1000,1000,1000)); rootNode.attachChild(sky);
}
protected void initMark() {
Sphere sphere = new Sphere(30, 30, 0.2f);
mark = new Geometry(“BOOM!”, sphere);
Material mark_mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mark_mat.setColor(“Color”, ColorRGBA.Red);
mark.setMaterial(mark_mat);
}
protected Geometry makeCube(String name, float x, float y, float z) {
Box box = new Box(1, 1, 1);
Geometry cube = new Geometry(name, box);
cube.setLocalTranslation(x, y, z);
Material mat1 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat1.setColor(“Color”, ColorRGBA.randomColor());
cube.setMaterial(mat1);
return cube;
}
}
[/java]