Fix and Improve TerrainPicking (#1049)
* Fix TerrainPicking by not adding "ghost" collisions to the CollisionResults (which weren't removed). Improve TerrainPicking by allowing to do more ray tracing (previously, it stopped on the first hit, now it will stop on the first hit within range). * Upgraded TerrainTestCollision to support multiple collisions and print the collision results detailed. MultiCollision can easily be turned on/off in simpleInitApp(). During testing, I noticed a bug where in very rare cases the first collision isn't what is expected but the back side of the clicked mountain. It has to be validated if this is due to the following changes or was already present. * Added Basic Unit Tests for Collision * TerrainPicker: Change API to return int to conform with collideWith * TerrainQuad: Conform with Picker now returning the number of collisions and allow to set multipleCollisions true or false. * TerrainPicking: Fixed a bug where the perpendicular collision always returned true, no matter the result of checkTriangles. Also add support for multiple collisions (which is toggleable to the old behavior, because the picker can early out then). * Try to fix Travis Build * Fixed a Regression which occurred due to Multi Collision Handling: The method used to provide duplicate results, which is why I commented it out. This lead to corner-cases not colliding at all anymore, thus I added a unit-test and removed the commented code and instead made addCollision de-duplicate entries.fix-openal-soft-deadlink
parent
1ffd11fae4
commit
3ec89ce499
@ -0,0 +1,38 @@ |
||||
package com.jme3.terrain.collision; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.system.JmeDesktopSystem; |
||||
import com.jme3.system.JmeSystem; |
||||
|
||||
/** |
||||
* This class provides some utility functions to properly test the jMonkeyEngine.<br> |
||||
* Thus it contains simple methods to get and create a headless assetManager amongst other things.<br> |
||||
* In comparison to {@link BaseTest} it provides a DesktopAssetManager capable of loading image formats using AWT, which |
||||
* however makes those tests unsuitable for headless ci testing. This requires jme3-desktop to be a testRuntime dependency. |
||||
* |
||||
* @author MeFisto94 |
||||
*/ |
||||
public abstract class BaseAWTTest { |
||||
private AssetManager assetManager; |
||||
|
||||
static { |
||||
//JmeSystem.setSystemDelegate(new JmeDesktopSystem());
|
||||
} |
||||
|
||||
public AssetManager getAssetManager() { |
||||
if (assetManager == null) { |
||||
assetManager = createAssetManager(); |
||||
} |
||||
|
||||
return assetManager; |
||||
} |
||||
|
||||
private AssetManager createAssetManager() { |
||||
/* Desktop.cfg supports the following additional file formats at the time of writing: |
||||
LOADER com.jme3.texture.plugins.AWTLoader : jpg, bmp, gif, png, jpeg |
||||
LOADER com.jme3.audio.plugins.OGGLoader : ogg |
||||
*/ |
||||
return JmeSystem.newAssetManager(BaseTest.class.getResource("/com/jme3/asset/Desktop.cfg")); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,28 @@ |
||||
package com.jme3.terrain.collision; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.system.TestUtil; |
||||
|
||||
/** |
||||
* This class provides some utility functions to properly test the jMonkeyEngine.<br> |
||||
* Thus it contains simple methods to get and create a headless assetManager amongst other things.<br> |
||||
* If you need support for image/texture formats (png, tga, jpg, ...) see {@link BaseAWTTest} |
||||
* |
||||
* @author MeFisto94 |
||||
*/ |
||||
public abstract class BaseTest { |
||||
private AssetManager assetManager; |
||||
|
||||
public AssetManager getAssetManager() { |
||||
if (assetManager == null) { |
||||
assetManager = createAssetManager(); |
||||
} |
||||
|
||||
return assetManager; |
||||
} |
||||
|
||||
private AssetManager createAssetManager() { |
||||
return TestUtil.createAssetManager(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,87 @@ |
||||
package com.jme3.terrain.collision; |
||||
|
||||
import com.jme3.collision.CollisionResults; |
||||
import com.jme3.math.Ray; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.terrain.geomipmap.TerrainQuad; |
||||
import com.jme3.terrain.heightmap.AbstractHeightMap; |
||||
import com.jme3.terrain.heightmap.ImageBasedHeightMap; |
||||
import com.jme3.texture.Texture; |
||||
import org.junit.Assert; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class TerrainCollisionTest extends BaseAWTTest { |
||||
TerrainQuad quad; |
||||
|
||||
@Before |
||||
public void initQuad() { |
||||
Texture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); |
||||
AbstractHeightMap map = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); |
||||
map.load(); |
||||
quad = new TerrainQuad("terrain", 65, 513, map.getHeightMap()); |
||||
} |
||||
|
||||
/** |
||||
* Due to a previous bug, when no collision should happen, the CollisionResults struct was still populated, leading |
||||
* to an incoherency of data and ghost collisions when passing a non-empty CR. |
||||
*/ |
||||
@Test |
||||
public void testNoCollision() { |
||||
Ray r = new Ray(new Vector3f(0f, 40f, 0f), Vector3f.UNIT_Y.negate()); |
||||
r.setLimit(0.1f); |
||||
CollisionResults cr = new CollisionResults(); |
||||
long l = System.nanoTime(); |
||||
int cw = quad.collideWith(r, cr); |
||||
System.out.println((System.nanoTime() - l) + " ns"); |
||||
|
||||
Assert.assertEquals(0, cw); |
||||
Assert.assertEquals(0, cr.size()); |
||||
Assert.assertEquals(null, cr.getClosestCollision()); |
||||
Assert.assertEquals(null, cr.getFarthestCollision()); |
||||
} |
||||
|
||||
@Test |
||||
public void testPerpendicularCollision() { |
||||
Ray r = new Ray(new Vector3f(0f, 40f, 0f), Vector3f.UNIT_Y.negate()); |
||||
CollisionResults cr = new CollisionResults(); |
||||
int cw = quad.collideWith(r, cr); |
||||
|
||||
Assert.assertEquals(1, cw); |
||||
Assert.assertEquals(1, cr.size()); |
||||
Assert.assertEquals(new Vector3f(0f, 28f, 0f), cr.getClosestCollision().getContactPoint()); |
||||
Assert.assertEquals(new Vector3f(-0.5144958f, 0.6859944f, 0.5144958f), cr.getClosestCollision().getContactNormal()); |
||||
Assert.assertEquals(12, cr.getClosestCollision().getDistance(), 0.01d); |
||||
Assert.assertEquals(0, cr.getClosestCollision().getTriangleIndex()); |
||||
} |
||||
|
||||
@Test |
||||
public void testMultiCollision() { |
||||
// Ray parameters obtained by using TerrainTestCollision (manual inspection of a feasible ray and commenting out setLocalScale(2)
|
||||
Ray r = new Ray(new Vector3f(-38.689114f, 35.622643f, -40.222355f), new Vector3f(0.68958646f, 0.0980845f, 0.7175304f)); |
||||
|
||||
CollisionResults cr = new CollisionResults(); |
||||
long l = System.nanoTime(); |
||||
int cw = quad.collideWith(r, cr); |
||||
System.out.println((System.nanoTime() - l) + " ns"); |
||||
Assert.assertEquals(6, cw); |
||||
Assert.assertEquals(6, cr.size()); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testPreventRegression() { |
||||
// This test is as the multi collision changes lead to a regression where sometimes a collision was ignored
|
||||
// Ray parameters obtained by using TerrainTestCollision (manual inspection of a feasible ray and commenting out setLocalScale(2))
|
||||
Ray r = new Ray(new Vector3f(101.61858f, 78.35965f, 17.645157f), new Vector3f(-0.4188528f, -0.56462675f, 0.71116734f)); |
||||
|
||||
CollisionResults cr = new CollisionResults(); |
||||
quad.collideWith(r, cr); |
||||
|
||||
Assert.assertEquals(3, cr.size()); |
||||
Assert.assertEquals(68.1499f, cr.getClosestCollision().getDistance(), 0.01f); |
||||
Assert.assertEquals(new Vector3f(73.07381f, 39.88039f, 66.11114f), cr.getClosestCollision().getContactPoint()); |
||||
Assert.assertEquals(new Vector3f(0.9103665f, 0.33104235f, -0.24828176f), cr.getClosestCollision().getContactNormal()); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue