diff --git a/.gitignore b/.gitignore
index 32858aa..3bded21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,12 @@
+# backups
+*.*~
+
+# binary / temporary
*.class
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
+build/
+dist/
+nbproject/private/
# Package Files #
*.jar
diff --git a/OpenAL64.dll b/OpenAL64.dll
new file mode 100644
index 0000000..6f2a2fe
Binary files /dev/null and b/OpenAL64.dll differ
diff --git a/README.md b/README.md
index 10afc84..9758e88 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,12 @@
-# roadtrip
-Driving game
+# RoadTrip
+A game about a journey involving vehicles and obstacles.
+
+## Status
+### DONE
+* Physics-based vehicle
+* Hilly ground
+
+### TODO
+* Orbit camera
+* Rigid objects (fences, houses)
+* People
diff --git a/assets/Materials/Generated/TestMap-terrain_TestMapQuad1Patch1.j3m b/assets/Materials/Generated/TestMap-terrain_TestMapQuad1Patch1.j3m
new file mode 100644
index 0000000..4544313
--- /dev/null
+++ b/assets/Materials/Generated/TestMap-terrain_TestMapQuad1Patch1.j3m
@@ -0,0 +1,20 @@
+Material MyMaterial : Common/MatDefs/Terrain/TerrainLighting.j3md {
+ MaterialParameters {
+ DiffuseMap_0_scale : 16.0625
+ WardIso : true
+ DiffuseMap : Flip Repeat /Textures/dirt.jpg
+ AlphaMap : /Textures/terrain-alpha/TestMap-terrain-TestMap-alphablend0.png
+ Shininess : 0.01
+ }
+ AdditionalRenderState {
+ FaceCull Back
+ Wireframe Off
+ DepthWrite On
+ PolyOffset 0.0 0.0
+ AlphaTestFalloff 0.0
+ Blend Off
+ PointSprite Off
+ ColorWrite On
+ DepthTest On
+ }
+}
diff --git a/assets/Scenes/TestMap.j3o b/assets/Scenes/TestMap.j3o
new file mode 100644
index 0000000..fa70d15
Binary files /dev/null and b/assets/Scenes/TestMap.j3o differ
diff --git a/assets/Scenes/TestMap.j3odata b/assets/Scenes/TestMap.j3odata
new file mode 100644
index 0000000..9573a71
--- /dev/null
+++ b/assets/Scenes/TestMap.j3odata
@@ -0,0 +1,3 @@
+#
+#Thu Dec 29 22:27:37 CET 2016
+ORIGINAL_PATH=TestMap.j3o
diff --git a/assets/Textures/dirt.jpg b/assets/Textures/dirt.jpg
new file mode 100644
index 0000000..e8eedca
Binary files /dev/null and b/assets/Textures/dirt.jpg differ
diff --git a/assets/Textures/skybox.jpg b/assets/Textures/skybox.jpg
new file mode 100644
index 0000000..d278e9a
--- /dev/null
+++ b/assets/Textures/skybox.jpg
@@ -0,0 +1,2178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 94842419-Full.jpg Photo by opog | Photobucket
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..de49210
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project RoadTrip.
+
+
+
diff --git a/bulletjme64.dll b/bulletjme64.dll
new file mode 100644
index 0000000..41f098c
Binary files /dev/null and b/bulletjme64.dll differ
diff --git a/lwjgl64.dll b/lwjgl64.dll
new file mode 100644
index 0000000..240dd2c
Binary files /dev/null and b/lwjgl64.dll differ
diff --git a/manifest.mf b/manifest.mf
new file mode 100644
index 0000000..1574df4
--- /dev/null
+++ b/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/nbproject/assets-impl.xml b/nbproject/assets-impl.xml
new file mode 100644
index 0000000..0a47d8d
--- /dev/null
+++ b/nbproject/assets-impl.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
new file mode 100644
index 0000000..ed1d6c9
--- /dev/null
+++ b/nbproject/build-impl.xml
@@ -0,0 +1,1412 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set test.src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No tests executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+
+
+
+
+
+ java -cp "${run.classpath.with.dist.jar}" ${main.class}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+ Must select some files in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+ Must select one file in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
new file mode 100644
index 0000000..10921e6
--- /dev/null
+++ b/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=ffdc6a51
+build.xml.script.CRC32=972047c8
+build.xml.stylesheet.CRC32=28e38971@1.56.1.46
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=ffdc6a51
+nbproject/build-impl.xml.script.CRC32=60ad5220
+nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644
index 0000000..236eeb6
--- /dev/null
+++ b/nbproject/project.properties
@@ -0,0 +1,89 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=RoadTrip
+application.vendor=dejvino
+assets.compress=true
+assets.excludes=**/*.j3odata,**/*.mesh,**/*.skeleton,**/*.mesh.xml,**/*.skeleton.xml,**/*.scene,**/*.material,**/*.obj,**/*.mtl,**/*.3ds,**/*.dae,**/*.blend,**/*.blend*[0-9]
+assets.folder.name=assets
+assets.jar.name=assets.jar
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/RoadTrip.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.RoadTrip-assets=assets
+includes=**
+jar.compress=false
+javac.classpath=\
+ ${libs.jme3.classpath}:\
+ ${libs.jme3-libraries-physics.classpath}:\
+ ${libs.jme3-libraries-physics-native.classpath}:\
+ ${libs.jme3-test-data.classpath}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.7
+javac.target=1.7
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+main.class=roadtrip.RoadTrip
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+obfuscate.options=-keep public class * extends com.jme3.app.Application{public *;}\n-keep public class * extends com.jme3.system.JmeSystemDelegate{public *;}\n-keep public class * implements com.jme3.renderer.Renderer{public *;}\n-keep public class * implements com.jme3.asset.AssetLoader{public *;}\n-keep public class * implements com.jme3.asset.AssetLocator{public *;}\n-keep public class * implements de.lessvoid.nifty.screen.ScreenController{public *;}\n-dontwarn\n-dontnote\n
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.jme3.classpath}:\
+ ${libs.jme3-libraries.classpath}:\
+ ${libs.jme3-test-data.classpath}:\
+ ${libs.jme3-libraries-physics.classpath}:\
+ ${libs.jme3-libraries-physics-native.classpath}:\
+ ${file.reference.RoadTrip-assets}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644
index 0000000..8bd527f
--- /dev/null
+++ b/nbproject/project.xml
@@ -0,0 +1,20 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+
+
+
+
+
+ RoadTrip
+
+
+
+
+
+
+
+
+
diff --git a/src/roadtrip/PhysicsTestHelper.java b/src/roadtrip/PhysicsTestHelper.java
new file mode 100644
index 0000000..16d8488
--- /dev/null
+++ b/src/roadtrip/PhysicsTestHelper.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package roadtrip;
+
+import com.jme3.app.Application;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class PhysicsTestHelper {
+
+ /**
+ * creates a simple physics test world with a floor, an obstacle and some test boxes
+ * @param rootNode
+ * @param assetManager
+ * @param space
+ */
+ public static void createPhysicsTestWorld(Node rootNode, AssetManager assetManager, PhysicsSpace space) {
+ AmbientLight light = new AmbientLight();
+ light.setColor(ColorRGBA.LightGray);
+ rootNode.addLight(light);
+
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+
+ Box floorBox = new Box(140, 0.25f, 140);
+ Geometry floorGeometry = new Geometry("Floor", floorBox);
+ floorGeometry.setMaterial(material);
+ floorGeometry.setLocalTranslation(0, -5, 0);
+// Plane plane = new Plane();
+// plane.setOriginNormal(new Vector3f(0, 0.25f, 0), Vector3f.UNIT_Y);
+// floorGeometry.addControl(new RigidBodyControl(new PlaneCollisionShape(plane), 0));
+ floorGeometry.addControl(new RigidBodyControl(0));
+ rootNode.attachChild(floorGeometry);
+ space.add(floorGeometry);
+
+ //movable boxes
+ for (int i = 0; i < 12; i++) {
+ Box box = new Box(0.25f, 0.25f, 0.25f);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ boxGeometry.setLocalTranslation(i, 5, -3);
+ //RigidBodyControl automatically uses box collision shapes when attached to single geometry with box mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ rootNode.attachChild(boxGeometry);
+ space.add(boxGeometry);
+ }
+
+ //immovable sphere with mesh collision shape
+ Sphere sphere = new Sphere(8, 8, 1);
+ Geometry sphereGeometry = new Geometry("Sphere", sphere);
+ sphereGeometry.setMaterial(material);
+ sphereGeometry.setLocalTranslation(4, -4, 2);
+ sphereGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(sphere), 0));
+ rootNode.attachChild(sphereGeometry);
+ space.add(sphereGeometry);
+
+ }
+
+ public static void createPhysicsTestWorldSoccer(Node rootNode, AssetManager assetManager, PhysicsSpace space) {
+ AmbientLight light = new AmbientLight();
+ light.setColor(ColorRGBA.LightGray);
+ rootNode.addLight(light);
+
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+
+ Box floorBox = new Box(20, 0.25f, 20);
+ Geometry floorGeometry = new Geometry("Floor", floorBox);
+ floorGeometry.setMaterial(material);
+ floorGeometry.setLocalTranslation(0, -0.25f, 0);
+// Plane plane = new Plane();
+// plane.setOriginNormal(new Vector3f(0, 0.25f, 0), Vector3f.UNIT_Y);
+// floorGeometry.addControl(new RigidBodyControl(new PlaneCollisionShape(plane), 0));
+ floorGeometry.addControl(new RigidBodyControl(0));
+ rootNode.attachChild(floorGeometry);
+ space.add(floorGeometry);
+
+ //movable spheres
+ for (int i = 0; i < 5; i++) {
+ Sphere sphere = new Sphere(16, 16, .5f);
+ Geometry ballGeometry = new Geometry("Soccer ball", sphere);
+ ballGeometry.setMaterial(material);
+ ballGeometry.setLocalTranslation(i, 2, -3);
+ //RigidBodyControl automatically uses Sphere collision shapes when attached to single geometry with sphere mesh
+ ballGeometry.addControl(new RigidBodyControl(.001f));
+ ballGeometry.getControl(RigidBodyControl.class).setRestitution(1);
+ rootNode.attachChild(ballGeometry);
+ space.add(ballGeometry);
+ }
+ {
+ //immovable Box with mesh collision shape
+ Box box = new Box(1, 1, 1);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ boxGeometry.setLocalTranslation(4, 1, 2);
+ boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0));
+ rootNode.attachChild(boxGeometry);
+ space.add(boxGeometry);
+ }
+ {
+ //immovable Box with mesh collision shape
+ Box box = new Box(1, 1, 1);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ boxGeometry.setLocalTranslation(4, 3, 4);
+ boxGeometry.addControl(new RigidBodyControl(new MeshCollisionShape(box), 0));
+ rootNode.attachChild(boxGeometry);
+ space.add(boxGeometry);
+ }
+ }
+
+ /**
+ * creates a box geometry with a RigidBodyControl
+ * @param assetManager
+ * @return
+ */
+ public static Geometry createPhysicsTestBox(AssetManager assetManager) {
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Box box = new Box(0.25f, 0.25f, 0.25f);
+ Geometry boxGeometry = new Geometry("Box", box);
+ boxGeometry.setMaterial(material);
+ //RigidBodyControl automatically uses box collision shapes when attached to single geometry with box mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ return boxGeometry;
+ }
+
+ /**
+ * creates a sphere geometry with a RigidBodyControl
+ * @param assetManager
+ * @return
+ */
+ public static Geometry createPhysicsTestSphere(AssetManager assetManager) {
+ Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ material.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
+ Sphere sphere = new Sphere(8, 8, 0.25f);
+ Geometry boxGeometry = new Geometry("Sphere", sphere);
+ boxGeometry.setMaterial(material);
+ //RigidBodyControl automatically uses sphere collision shapes when attached to single geometry with sphere mesh
+ boxGeometry.addControl(new RigidBodyControl(2));
+ return boxGeometry;
+ }
+
+ /**
+ * creates an empty node with a RigidBodyControl
+ * @param manager
+ * @param shape
+ * @param mass
+ * @return
+ */
+ public static Node createPhysicsTestNode(AssetManager manager, CollisionShape shape, float mass) {
+ Node node = new Node("PhysicsNode");
+ RigidBodyControl control = new RigidBodyControl(shape, mass);
+ node.addControl(control);
+ return node;
+ }
+
+ /**
+ * creates the necessary inputlistener and action to shoot balls from teh camera
+ * @param app
+ * @param rootNode
+ * @param space
+ */
+ public static void createBallShooter(final Application app, final Node rootNode, final PhysicsSpace space) {
+ ActionListener actionListener = new ActionListener() {
+
+ public void onAction(String name, boolean keyPressed, float tpf) {
+ Sphere bullet = new Sphere(32, 32, 0.4f, true, false);
+ bullet.setTextureMode(TextureMode.Projected);
+ Material mat2 = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+ key2.setGenerateMips(true);
+ Texture tex2 = app.getAssetManager().loadTexture(key2);
+ mat2.setTexture("ColorMap", tex2);
+ if (name.equals("shoot") && !keyPressed) {
+ Geometry bulletg = new Geometry("bullet", bullet);
+ bulletg.setMaterial(mat2);
+ bulletg.setShadowMode(ShadowMode.CastAndReceive);
+ bulletg.setLocalTranslation(app.getCamera().getLocation());
+ RigidBodyControl bulletControl = new RigidBodyControl(10);
+ bulletg.addControl(bulletControl);
+ bulletControl.setLinearVelocity(app.getCamera().getDirection().mult(25));
+ bulletg.addControl(bulletControl);
+ rootNode.attachChild(bulletg);
+ space.add(bulletControl);
+ }
+ }
+ };
+ app.getInputManager().addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ app.getInputManager().addListener(actionListener, "shoot");
+ }
+}
diff --git a/src/roadtrip/RoadTrip.java b/src/roadtrip/RoadTrip.java
new file mode 100644
index 0000000..94ea63f
--- /dev/null
+++ b/src/roadtrip/RoadTrip.java
@@ -0,0 +1,272 @@
+package roadtrip;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.control.VehicleControl;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
+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.Cylinder;
+
+/**
+ *
+ * @author dejvino
+ */
+public class RoadTrip extends SimpleApplication implements ActionListener {
+
+ final int WEAK = 1;
+ final int TRUCK = 2;
+ final int SPORT = 3;
+
+ final int carType = SPORT;
+
+ private BulletAppState bulletAppState;
+ private VehicleControl vehicle;
+ private float accelerationForce = 200.0f;
+ private float brakeForce = 100.0f;
+ private float steeringValue = 0;
+ private float accelerationValue = 0;
+ private Vector3f jumpForce = new Vector3f(0, 3000, 0);
+
+ public static void main(String[] args) {
+ RoadTrip app = new RoadTrip();
+ app.start();
+ }
+
+ Spatial map;
+ Spatial car;
+
+ @Override
+ public void simpleInitApp() {
+ bulletAppState = new BulletAppState();
+ stateManager.attach(bulletAppState);
+ bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
+ setupKeys();
+ buildPlayer();
+
+ map = assetManager.loadModel("Scenes/TestMap.j3o");
+ rootNode.attachChild(map);
+ getPhysicsSpace().addAll(map);
+
+ vehicle.setPhysicsLocation(new Vector3f(5f, 30f, 5f));
+ }
+
+ private PhysicsSpace getPhysicsSpace(){
+ return bulletAppState.getPhysicsSpace();
+ }
+
+ private void setupKeys() {
+ inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_H));
+ inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_K));
+ inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_U));
+ inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_J));
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
+ inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addListener(this, "Lefts");
+ inputManager.addListener(this, "Rights");
+ inputManager.addListener(this, "Ups");
+ inputManager.addListener(this, "Downs");
+ inputManager.addListener(this, "Space");
+ inputManager.addListener(this, "Reset");
+ }
+
+ private void buildPlayer() {
+ Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
+ mat.getAdditionalRenderState().setWireframe(true);
+ mat.setColor("Color", ColorRGBA.Red);
+
+ //create a compound shape and attach the BoxCollisionShape for the car body at 0,1,0
+ //this shifts the effective center of mass of the BoxCollisionShape to 0,-1,0
+ CompoundCollisionShape compoundShape = new CompoundCollisionShape();
+ BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.4f, 0.5f, 3.6f));
+ compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
+
+ if (carType == TRUCK) {
+ BoxCollisionShape boxCabin = new BoxCollisionShape(new Vector3f(1.4f, 0.8f, 1.0f));
+ compoundShape.addChildShape(boxCabin, new Vector3f(0, 2, 2f));
+ } else if (carType == SPORT) {
+ BoxCollisionShape boxCabin = new BoxCollisionShape(new Vector3f(1.2f, 0.6f, 2.0f));
+ compoundShape.addChildShape(boxCabin, new Vector3f(0, 2, -1f));
+ }
+
+ //create vehicle node
+ Node vehicleNode=new Node("vehicleNode");
+ vehicle = new VehicleControl(compoundShape, 500);
+ vehicleNode.addControl(vehicle);
+
+ //setting suspension values for wheels, this can be a bit tricky
+ //see also https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&hl=en
+ float stiffness = 30.0f;//200=f1 car
+ float compValue = .1f; //(should be lower than damp)
+ float dampValue = .2f;
+ vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
+ vehicle.setSuspensionStiffness(stiffness);
+ vehicle.setMaxSuspensionForce(10000.0f);
+
+ //Create four wheels and add them at their locations
+ Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0
+ Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0
+ float radius = 1.0f;
+ float restLength = 0.3f;
+ float yOff = 0.5f;
+ float xOff = 1.6f;
+ float zOff = 2f;
+
+ Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 0.2f, true);
+
+ Node node1 = new Node("wheel 1 node");
+ Geometry wheels1 = new Geometry("wheel 1", wheelMesh);
+ node1.attachChild(wheels1);
+ wheels1.rotate(0, FastMath.HALF_PI, 0);
+ wheels1.setMaterial(mat);
+ vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node2 = new Node("wheel 2 node");
+ Geometry wheels2 = new Geometry("wheel 2", wheelMesh);
+ node2.attachChild(wheels2);
+ wheels2.rotate(0, FastMath.HALF_PI, 0);
+ wheels2.setMaterial(mat);
+ vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),
+ wheelDirection, wheelAxle, restLength, radius, true);
+
+ Node node3 = new Node("wheel 3 node");
+ Geometry wheels3 = new Geometry("wheel 3", wheelMesh);
+ node3.attachChild(wheels3);
+ wheels3.rotate(0, FastMath.HALF_PI, 0);
+ wheels3.setMaterial(mat);
+ vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ Node node4 = new Node("wheel 4 node");
+ Geometry wheels4 = new Geometry("wheel 4", wheelMesh);
+ node4.attachChild(wheels4);
+ wheels4.rotate(0, FastMath.HALF_PI, 0);
+ wheels4.setMaterial(mat);
+ vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ vehicleNode.attachChild(node1);
+ vehicleNode.attachChild(node2);
+ vehicleNode.attachChild(node3);
+ vehicleNode.attachChild(node4);
+
+ if (carType == TRUCK) {
+ Node node5 = new Node("wheel 5 node");
+ Geometry wheels5 = new Geometry("wheel 5", wheelMesh);
+ node5.attachChild(wheels5);
+ wheels5.rotate(0, FastMath.HALF_PI, 0);
+ wheels5.setMaterial(mat);
+ vehicle.addWheel(node5, new Vector3f(-xOff, yOff, 2.1f* -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ Node node6 = new Node("wheel 6 node");
+ Geometry wheels6 = new Geometry("wheel 6", wheelMesh);
+ node6.attachChild(wheels6);
+ wheels6.rotate(0, FastMath.HALF_PI, 0);
+ wheels6.setMaterial(mat);
+ vehicle.addWheel(node6, new Vector3f(xOff, yOff, 2.1f* -zOff),
+ wheelDirection, wheelAxle, restLength, radius, false);
+
+ vehicleNode.attachChild(node5);
+ vehicleNode.attachChild(node6);
+ }
+
+ rootNode.attachChild(vehicleNode);
+
+ getPhysicsSpace().add(vehicle);
+ vehicle.setPhysicsLocation(new Vector3f(5f, 30f, 5f));
+
+ vehicle.getWheel(0).setFrictionSlip(0.8f);
+ vehicle.getWheel(1).setFrictionSlip(0.8f);
+ vehicle.getWheel(2).setFrictionSlip(0.6f);
+ vehicle.getWheel(3).setFrictionSlip(0.6f);
+
+ if (carType == TRUCK) {
+ vehicle.getWheel(4).setFrictionSlip(0.6f);
+ vehicle.getWheel(5).setFrictionSlip(0.6f);
+
+ accelerationForce = 1400f;
+ brakeForce = 200f;
+ } else if (carType == SPORT) {
+ accelerationForce = 20000f;
+ brakeForce = 200f;
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ cam.setLocation(new Vector3f(-10f, 35f, -10f));
+ cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);
+ }
+
+ public void onAction(String binding, boolean value, float tpf) {
+ float steerMax = 0.5f;
+ if (carType == TRUCK) {
+ steerMax = 0.7f;
+ }
+ if (binding.equals("Lefts")) {
+ if (value) {
+ steeringValue += steerMax;
+ } else {
+ steeringValue += -steerMax;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Rights")) {
+ if (value) {
+ steeringValue += -steerMax;
+ } else {
+ steeringValue += steerMax;
+ }
+ vehicle.steer(steeringValue);
+ } else if (binding.equals("Ups")) {
+ if (value) {
+ accelerationValue += accelerationForce;
+ } else {
+ accelerationValue -= accelerationForce;
+ }
+ vehicle.accelerate(2, accelerationValue);
+ vehicle.accelerate(3, accelerationValue);
+ if (carType == TRUCK) {
+ vehicle.accelerate(4, accelerationValue);
+ vehicle.accelerate(5, accelerationValue);
+ }
+ } else if (binding.equals("Downs")) {
+ float b;
+ if (value) {
+ b = brakeForce;
+ } else {
+ b = 0f;
+ }
+ vehicle.brake(0, b);
+ vehicle.brake(1, b);
+ } else if (binding.equals("Space")) {
+ if (value) {
+ vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
+ }
+ } else if (binding.equals("Reset")) {
+ if (value) {
+ System.out.println("Reset");
+ vehicle.setPhysicsLocation(Vector3f.ZERO);
+ vehicle.setPhysicsRotation(new Matrix3f());
+ vehicle.setLinearVelocity(Vector3f.ZERO);
+ vehicle.setAngularVelocity(Vector3f.ZERO);
+ vehicle.resetSuspension();
+ } else {
+ }
+ }
+ }
+}