I am learning JME3 and I managed to create my own height map and modifying some of the example code, etc. Now, I created a very simple 4-wall roofless room with Blender, exported it as a Wavefront .Obj file and loaded it unto my scene (I attacked it to the terrain
node.
Now, my terrain
has a collision detection applied so the player can move and jump around, but it can also walk right through the walls of my model. All the examples I can find loads an already pre-built scene, and I'm still clueless as to why the player goes right through the loaded model?
Sorry for the big code, but I couldn't see how else I could do otherwise. The physics is applied at the section /** 6. Add physics: */
:
public class Main extends SimpleApplication
implements ActionListener {
private BulletAppState bulletAppState;
private RigidBodyControl landscape;
private CharacterControl player;
private Vector3f walkDirection = new Vector3f();
private boolean left = false, right = false, up = false, down = false;
private TerrainQuad terrain;
private Material mat_terrain;
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(1366, 768);
settings.setFullscreen(true);
Main app = new Main();
app.setSettings(settings);
app.setShowSettings(false);
app.start();
}
@Override
public void simpleInitApp() {
/** Set up Physics */
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
flyCam.setMoveSpeed(200);
setUpKeys();
/** 1. Create terrain material and load four textures into it. */
mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
/** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/terrain/island_1_alpha1.png"));
/** 1.2) Add GRASS texture into the red layer (Tex1). */
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex1", grass);
mat_terrain.setFloat("Tex1Scale", 64f);
/** 1.3) Add DIRT texture into the green layer (Tex2) */
Texture dirt = assetManager.loadTexture("Textures/rocks.jpg");
dirt.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex2", dirt);
mat_terrain.setFloat("Tex2Scale", 32f);
/** 1.4) Add ROAD texture into the blue layer (Tex3) */
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
rock.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex3", rock);
mat_terrain.setFloat("Tex3Scale", 128f);
/** 2. Create the height map */
AbstractHeightMap heightmap = null;
Texture heightMapImage = assetManager.loadTexture("Textures/terrain/island_1.png");
heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
heightmap.load();
/** 3. We have prepared material and heightmap.
* Now we create the actual terrain:
* 3.1) Create a TerrainQuad and name it "my terrain".
* 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
* 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
* 3.4) As LOD step scale we supply Vector3f(1,1,1).
* 3.5) We supply the prepared heightmap itself.
*/
terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
/** 4. We give the terrain its material, position & scale it, and attach it. */
terrain.setMaterial(mat_terrain);
terrain.setLocalTranslation(0, -170, 0);
terrain.setLocalScale(2f, 1f, 2f);
rootNode.attachChild(terrain);
/** 4.5. Load some models */
Spatial building = assetManager.loadModel("Models/building1.obj");
Material mat_default = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
building.setMaterial(mat_default);
building.setLocalTranslation(90, 117, 90);
building.setLocalScale(5f, 5f, 5f);
terrain.attachChild(building);
/** 4.6. Load Sky */
rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
/** 4.7. Load water */
// we create a water processor
SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);
waterProcessor.setReflectionScene(rootNode);
// we set the water plane
Vector3f waterLocation = new Vector3f(0, -58, 0);
waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y, waterLocation.dot(Vector3f.UNIT_Y)));
viewPort.addProcessor(waterProcessor);
// we set wave properties
waterProcessor.setWaterDepth(50); // transparency of water
waterProcessor.setDistortionScale(0.05f); // strength of waves
waterProcessor.setWaveSpeed(0.05f); // speed of waves
// we define the wave size by setting the size of the texture coordinates
Quad quad = new Quad(1000, 1000);
quad.scaleTextureCoordinates(new Vector2f(10f, 10f));
// we create the water geometry from the quad
Geometry water = new Geometry("water", quad);
water.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
water.setLocalTranslation(-500, -58, 500);
water.setShadowMode(ShadowMode.Receive);
water.setMaterial(waterProcessor.getMaterial());
rootNode.attachChild(water);
/** 5. The LOD (level of detail) depends on were the camera is: */
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
/** 6. Add physics: */
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass zero.*/
CollisionShape terrainShape = CollisionShapeFactory.createMeshShape(terrain);
landscape = new RigidBodyControl(terrainShape, 0);
terrain.addControl(landscape);
terrain.addControl(new RigidBodyControl(CollisionShapeFactory.createMeshShape(building), 0));
// We set up collision detection for the player by creating
// a capsule collision shape and a CharacterControl.
// The CharacterControl offers extra settings for
// size, stepheight, jumping, falling, and gravity.
// We also put the player in its starting position.
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
player = new CharacterControl(capsuleShape, 0.05f);
player.setJumpSpeed(50);
player.setFallSpeed(70);
player.setGravity(100);
player.setPhysicsLocation(new Vector3f(50, 100, 100));
// We attach the scene and the player to the rootnode and the physics space,
// to make them appear in the game world.
bulletAppState.getPhysicsSpace().add(terrain);
bulletAppState.getPhysicsSpace().add(player);
}
/** We over-write some navigational key mappings here, so we can
* add physics-controlled walking and jumping: */
private void setUpKeys() {
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(this, "Left");
inputManager.addListener(this, "Right");
inputManager.addListener(this, "Up");
inputManager.addListener(this, "Down");
inputManager.addListener(this, "Jump");
}
/** These are our custom actions triggered by key presses.
* We do not walk yet, we just keep track of the direction the user pressed. */
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals("Left")) {
if (value) {
left = true;
} else {
left = false;
}
} else if (binding.equals("Right")) {
if (value) {
right = true;
} else {
right = false;
}
} else if (binding.equals("Up")) {
if (value) {
up = true;
} else {
up = false;
}
} else if (binding.equals("Down")) {
if (value) {
down = true;
} else {
down = false;
}
} else if (binding.equals("Jump")) {
player.jump();
}
}
/**
* This is the main event loop--walking happens here.
* We check in which direction the player is walking by interpreting
* the camera direction forward (camDir) and to the side (camLeft).
* The setWalkDirection() command is what lets a physics-controlled player walk.
* We also make sure here that the camera moves with player.
*/
@Override
public void simpleUpdate(float tpf) {
Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
walkDirection.set(0, 0, 0);
if (left) {
walkDirection.addLocal(camLeft);
}
if (right) {
walkDirection.addLocal(camLeft.negate());
}
if (up) {
walkDirection.addLocal(camDir);
}
if (down) {
walkDirection.addLocal(camDir.negate());
}
player.setWalkDirection(walkDirection);
cam.setLocation(player.getPhysicsLocation());
}
}
So, why isn't my model applied to the collision detection?
I have found the answer, here. The solution to the problem is this :
- Create a CollisionShape. Create a PhysicsControl by supplying the
- CollisionShape and mass. E.g. com.jme3.bullet.control.RigidBodyControl
- Add the PhysicsControl to the Spatial. Add the PhysicsControl to the
- physicsSpace object. Attach the Spatial to the rootNode, as usual.
- (Optional) Implement the PhysicsCollisionListener interface to respond to PhysicsCollisionEvents if desired.
Therefore, replace
terrain.attachChild(building);
by
rootNode.attachChild(building);
and adding
bulletAppState.getPhysicsSpace().add(building);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With