diff --git a/sdk/jme3-documentation/build.xml b/sdk/jme3-documentation/build.xml new file mode 100644 index 000000000..ed032b03b --- /dev/null +++ b/sdk/jme3-documentation/build.xml @@ -0,0 +1,59 @@ + + + + + + Builds, tests, and runs the project com.jme3.gde.docs. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/jme3-documentation/manifest.mf b/sdk/jme3-documentation/manifest.mf new file mode 100644 index 000000000..4da1af9df --- /dev/null +++ b/sdk/jme3-documentation/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: com.jme3.gde.docs +OpenIDE-Module-Implementation-Version: 0 +OpenIDE-Module-Localizing-Bundle: com/jme3/gde/docs/Bundle.properties + diff --git a/sdk/jme3-documentation/nbproject/build-impl.xml b/sdk/jme3-documentation/nbproject/build-impl.xml new file mode 100644 index 000000000..9a239380c --- /dev/null +++ b/sdk/jme3-documentation/nbproject/build-impl.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + You must set 'suite.dir' to point to your containing module suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/jme3-documentation/nbproject/genfiles.properties b/sdk/jme3-documentation/nbproject/genfiles.properties new file mode 100644 index 000000000..c13246ac8 --- /dev/null +++ b/sdk/jme3-documentation/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=04f2ab5a +build.xml.script.CRC32=20380530 +build.xml.stylesheet.CRC32=a56c6a5b@2.50.1 +# 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=04f2ab5a +nbproject/build-impl.xml.script.CRC32=0cdd5704 +nbproject/build-impl.xml.stylesheet.CRC32=238281d1@2.50.1 diff --git a/sdk/jme3-documentation/nbproject/project.properties b/sdk/jme3-documentation/nbproject/project.properties new file mode 100644 index 000000000..0db107241 --- /dev/null +++ b/sdk/jme3-documentation/nbproject/project.properties @@ -0,0 +1,6 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +license.file=../license-jme.txt +nbm.homepage=http://www.jmonkeyengine.com +nbm.module.author=Normen Hansen, Ruth Kusterer, many others +spec.version.base=3.0.0 diff --git a/sdk/jme3-documentation/nbproject/project.xml b/sdk/jme3-documentation/nbproject/project.xml new file mode 100644 index 000000000..b65ce7047 --- /dev/null +++ b/sdk/jme3-documentation/nbproject/project.xml @@ -0,0 +1,22 @@ + + + org.netbeans.modules.apisupport.project + + + com.jme3.gde.docs + + + + org.netbeans.modules.javahelp + + + + 1 + 2.27.1 + + + + + + + diff --git a/sdk/jme3-documentation/nbproject/suite.properties b/sdk/jme3-documentation/nbproject/suite.properties new file mode 100644 index 000000000..29d7cc9bd --- /dev/null +++ b/sdk/jme3-documentation/nbproject/suite.properties @@ -0,0 +1 @@ +suite.dir=${basedir}/.. diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/Bundle.properties b/sdk/jme3-documentation/src/com/jme3/gde/docs/Bundle.properties new file mode 100644 index 000000000..95f7cb075 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/Bundle.properties @@ -0,0 +1,6 @@ +OpenIDE-Module-Display-Category=jMonkeyEngine +OpenIDE-Module-Long-Description=\ + This plugin contains the whole documentation for jME3 and SDK and is updated from the jME3 wiki for each version of the SDK. \ + It allows you to search through all jME3 documentation available. +OpenIDE-Module-Name=jME3 and SDK Documentation +OpenIDE-Module-Short-Description=Documentation for jME3 and SDK diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/core-about.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-about.html new file mode 100644 index 000000000..999a153c5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-about.html @@ -0,0 +1,88 @@ + + + + About jMonkeyEngine SDK + + + + +

About jMonkeyEngine SDK

+

+ The jMonkeyEngine SDK (or "jMonkeyPlatform") is a game development environment for jMonkeyEngine 3. +

+

+ The jMonkeyEngine SDK is based on the NetBeans Platform so you automatically have access to all of the + developer tools of the NetBeans IDE to create, compile and deploy Java applications. + On top of that, the jMonkeyEngine SDK provides you with specialized plugins for creating jMonkeyEngine 3 game content. +

+

Documentation

+

+ This help contains info about most of the functions of the jMonkeyEngine SDK, take a look at the + "Introduction" section of the help to get started with creating projects. The sections + on model and material handling will give you information on how you can manage and edit + your game assets. Finally theres information about how jMonkeyPlatform helps you + edit your code and deploy your application to multiple platforms. +

+

Getting started

+

+ If you are completely new to jMonkeyEngine we suggest you go through the jMonkeyEngine3 tutorials + first, they will give you an idea on how jMonkeyEngine works and contain lots of little + good-to-knows that will help you getting your Application up and running in no time. +

+

+ To see some code examples of jMonkeyEngine3 functions, press the "New Project" button and + create a new "JME3Tests" project. To start coding, create a new "BasicGame" project. +

+

+ Have fun coding!
+ + + + + + +

+ + + \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/core-sceneviewer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-sceneviewer.html new file mode 100644 index 000000000..4b06c79f3 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-sceneviewer.html @@ -0,0 +1,54 @@ + + + + + + + + +

The OpenGL Window

+
+ +

+ The OpenGL window and the SceneExplorer window are shared among plugins to save system resources. This means that you will have to keep an eye on what plugin is using the scene right now and what you are actually modifying in these windows. +

+ +

+ Most plugins will deliver their own UI elements to modify the scene so the SceneExplorer is more of a global tool. The simple SceneComposer however heavily relies on its functions as other plugins might too in the future. + +

+ +
+ + + \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/core-updating.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-updating.html new file mode 100644 index 000000000..9134de7bf --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/core-updating.html @@ -0,0 +1,66 @@ + + + + + + + + +

Automatically Updating jMonkeyEngine SDK

Getting stable updates

+

The jMonkeyPlatform software includes an automatic web update feature. To run an update, simply go to Help→Check for Updates and you will get the most current stable update of the SDK and the engine. By default the IDE will check every week for new update and inform you about them with a symbol in the lower right.

Testing the nightly version

You can test the nightly version with all the latest untested features to give feedback to the developers. Be warned however, the changes in the nightly versions might break your current game project if heavy changes have been committed.

To make sure that you do not break your current development environment it is recommended to use a separate application and settings directory for the nightly version.

Then start the new application and have your SDK being updated to the most current nightly version:

+ + \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-about.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-about.html new file mode 100644 index 000000000..da16e98fb --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-about.html @@ -0,0 +1,29 @@ + + + + About jme3-documentation + + + + +

About jme3-documentation

+

+ +

+ + + diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-hs.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-hs.xml new file mode 100644 index 000000000..d96d76fc5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-hs.xml @@ -0,0 +1,32 @@ + + + + + jme3-documentation Help + + com.jme3.gde.docs.about + + + + + TOC + + javax.help.TOCView + docs-toc.xml + + + + Search + + javax.help.SearchView + JavaHelpSearch + + diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-idx.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-idx.xml new file mode 100644 index 000000000..ad50ac530 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-idx.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-map.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-map.xml new file mode 100644 index 000000000..1ae16e5cf --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-map.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-toc.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-toc.xml new file mode 100644 index 000000000..434c34f1d --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/docs-toc.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-float-array.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-float-array.png new file mode 100644 index 000000000..3194726b0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-float-array.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-heightmap.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-heightmap.png new file mode 100644 index 000000000..eaa8f6049 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme2/terrain-from-heightmap.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/300px-surface_normal.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/300px-surface_normal.png new file mode 100644 index 000000000..3723ad341 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/300px-surface_normal.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/1.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/1.jpg new file mode 100644 index 000000000..7886f41fc Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/1.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/2.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/2.jpg new file mode 100644 index 000000000..0fc95724d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/2.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/220px-trefoil_knot_arb.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/220px-trefoil_knot_arb.png new file mode 100644 index 000000000..8974e487e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/220px-trefoil_knot_arb.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3.jpg new file mode 100644 index 000000000..e966d4faf Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html new file mode 100644 index 000000000..9cab3308f --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html @@ -0,0 +1,91 @@ + +

Models and Scenes

+
+ +

+ +Like Shapes, 3D models are also made up of Meshes, but models are more complex than Shapes. While Shapes are built into jME3, you typically create models in external 3D Mesh Editors. +

+ +
+ +

Using Models and Scenes with jME3

+
+ +

+ +To use 3D models in a jME3 application: +

+
    +
  1. Export the 3D model in Ogre XML or Wavefront OBJ format. Export Scenes as Ogre DotScene format.
    +
  2. +
  3. Save the files into a subdirectory of your jME3 project's assets directory.
    +
  4. +
  5. In your code, you use the Asset Manager to load models as Spatials into a jME application.
    Spatial model = assetManager.loadModel(
    +    "Models/MonkeyHead/MonkeyHead.mesh.xml" );
    +
    +
  6. +
  7. (For the release build:) Use the jMonkeyEngine SDK to convert models to .j3o format. You don't need this step as long you still develop and test the aplication within the jMonkeyEngine SDK.
    +
  8. +
+ +
+ +

Creating Models and Scenes

+
+ +

+ +To create 3D models and scenes, you need a 3D Mesh Editor such as , with an OgreXML Exporter plugin. +

+ +

+Tip: Learn how to create for more complex models, it looks more professional. +

+ +

+3D model editors are third-party products, so please consult their documentation for instructions how to use them. Here is an example workflow for Blender users: + +

+ + +

+ +To export your models as Ogre XML meshes with materials: +

+
    +
  1. Open the menu File > Export > OgreXML Exporter to open the exporter dialog.
    +
  2. +
  3. In the Export Materials field: Give the material the same name as the model. For example, the model something.mesh.xml goes with something.material, plus (optionally) something.skeleton.xml, and some JPG files.
    +
  4. +
  5. In the Export Meshes field: Select a target subdirectory of your assets/Models/ directory. E.g. assets/Models/something/.
    +
  6. +
  7. Activate the following exporter settings:
    +
      +
    • Copy Textures: YES
      +
    • +
    • Rendering Materials: YES
      +
    • +
    • Flip Axis: YES
      +
    • +
    • Require Materials: YES
      +
    • +
    • Skeleton name follows mesh: YES
      +
    • +
    +
  8. +
  9. Click export.
    +
  10. +
+ +

+ +You can now use the jMonkeyEngine SDK to load and view models. You can create scenes from them and write cde that loads them into your application. +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/4.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/4.jpg new file mode 100644 index 000000000..913e1c5a0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/4.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/5.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/5.jpg new file mode 100644 index 000000000..3fe9f2499 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/5.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/6.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/6.jpg new file mode 100644 index 000000000..c2fc1a6cc Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/6.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/animation.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/animation.html new file mode 100644 index 000000000..bfc4d21b6 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/animation.html @@ -0,0 +1,402 @@ + +

Animation in jME3

+
+ +

+ +In 3D games, you do not only load static 3D models, you also want to be able to trigger animations in the model from the Java code. +

+ +
+ +

Requirements

+
+ +

+ +JME3 only loads and plays animated models, it does not create them. +

+ +

+What is required for an animated model? (See also: Animation terminology) +

+
    +
  1. For each model, you have to segment the model into a skeleton (bone rigging).
    +
  2. +
  3. For each motion, you have to specify how the animation distorts parts of the model (skinning).
    +
  4. +
  5. For each animation, you have to specify a series of snapshots of how the bones are positioned (keyframes).
    +
  6. +
  7. One model can contain several animations. You give every animation a name when you save it in the mesh editor.
    +
  8. +
+ +

+ +Unless you download free models, or buy them from a 3D artist, you must create your animated models in an external mesh editor (for example, Blender) yourself. +

+ + +

+ +What is required in your JME3-based Java class? +

+ + +
+ +

Code Samples

+
+ + +
+ +

Controlling Animations

+
+ +
+ +

The Animation Control

+
+ +

+ +Create one com.jme3.animation.AnimControl object in your JME3 application for each animated model that you want to control. You have to register each animated model to one of these Animation Controls. The control object gives you access to the available animation sequences in the model. +

+
  AnimControl playerControl; // you need one Control per model
+  Node player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml"); // load a model
+  playerControl = player.getControl(AnimControl.class); // get control over this model
+  playerControl.addListener(this); // add listener
+ +
+ +

Animation Channels

+
+ +

+ +An Animation Control has several Animation Channels (com.jme3.animation.AnimChannel). Each channel can play one animation sequence at a time. +

+ +

+There often are situations where you want to run several animation sequences at the same time, e.g. "shooting while walking" or "boxing while jumping". In this case, you create several channels, assign an animation to each, and play them in parallel. +

+
  AnimChannel channel_walk = playerControl.createChannel();
+  AnimChannel channel_jump = playerControl.createChannel();
+  ...
+ +

+To reset a Control, call control.clearChannels(); +

+ +
+ +

Animation Control Properties

+
+ +

+ +The following information is available for an AnimControl. + +

+
+ + + + + + + + + + + + + + + + + + +
AnimControl PropertyUsage
createChannel()Returns a new channel, controlling all bones by default.
getNumChannels()The number of channels registered to this Control.
getChannel(0)Gets individual channels by index number. At most getNumChannels().
clearChannels()Clear all channels in this control.
addListener(animEventListener)
+removeListener(animEventListener)
+clearListeners()
Adds or removes listeners to receive animation related events.
+
+ + + + + + + + + + + + + + + + + + +
AnimControl PropertyUsage
setAnimations(aniHashMap)Sets the animations that this AnimControl is capable of playing. The animations must be compatible with the skeleton given in the constructor.
addAnim(boneAnim)
+removeAnim(boneAnim)
Adds or removes an animation from this Control.
getAnimationNames()A String Collection of names of all animations that this Control can play for this model.
getAnim("anim")Retrieve an animation from the list of animations.
getAnimationLength("anim")Returns the length of the given named animation in seconds
+
+ + + + + + + + + + + + +
AnimControl PropertyUsage
getSkeleton()The Skeleton object controlled by this Control.
getTargets()The Skin objects controlled by this Control, as Mesh array.
getAttachmentsNode("bone")Returns the attachment node of a bone. Attach models and effects to this node to make them follow this bone's motions.
+ +
+ +

Animation Channel Properties

+
+ +

+ +The following properties are set per AnimChannel. + +

+
+ + + + + + + + + + + + + + + + + + +
AnimChannel PropertyUsage
setLoopMode(LoopMode.Loop); From now on, the animation on this channel will repeat from the beginning when it ends.
setLoopMode(LoopMode.DontLoop); From now on, the animation on this channel will play once, and the freeze at the last keyframe.
setLoopMode(LoopMode.Cycle); From now on, the animation on this channel will play forward, then backward, then again forward, and so on.
setSpeed(1f); From now on, play this animation slower (<1f) or faster (>1f), or with default speed (1f).
setTime(1.3f); Fast-forward or rewind to a certain moment in time of this animation.
+ +

+ +The following information is available for a channel. + +

+
+ + + + + + + + + + + + + + + + + + +
AnimChannel PropertyUsage
getAnimationName()The name of the animation playing on this channel. Returns null when no animation is playing.
getLoopMode()The current loop mode on this channel. The returned com.jme3.animation enum can be LoopMode.Loop, LoopMode.DontLoop, or LoopMode.Cycle.
getAnimMaxTime()The total length of the animation on this channel. Or 0f if nothing is playing.
getTime()How long the animation on this channel has been playing. It returns 0f if the channel has not started playing yet, or a value up to getAnimMaxTime().
getControl()The AnimControl that belongs to this AnimChannel.
+ +

+ +Use the following methods to add or remove individual bones to an AnimChannel. This is useful when you play two animations in parallel on two channels, and each controls a subset of the bones (e.g. one the arms, and the other the legs). + +

+
+ + + + + + + + + + + + + + + +
AnimChannel MethodsUsage
addAllBones()Add all the bones of the model's skeleton to be influenced by this animation channel. (default)
addBone("bone1")
+addBone(bone1)
Add a single bone to be influenced by this animation channel.
addToRootBone("bone1")
+addToRootBone(bone1)
Add a series of bones to be influenced by this animation channel: Add all bones, starting from the given bone, to the root bone.
addFromRootBone("bone1")
+addFromRootBone(bone1)
Add a series of bones to be influenced by this animation channel: Add all bones, starting from the given root bone, going towards the children bones.
+ +
+ +

Playing Animations

+
+ +

+ +Animations are played by channel. Note: Whether the animation channel plays continuously or only once, depends on the Loop properties you have set. + +

+
+ + + + + + +
Channel MethodUsage
channel_walk.setAnim("Walk",0.50f); Start the animation named "Walk" on channel channel_walk.
+The float value specifies the time how long the animation should overlap with the previous one on this channel. If set to 0f, then no blending will occur and the new animation will be applied instantly.
+ +

+ +Tip: Use the AnimEventLister below to react at the end or start of an animation cycle. +

+ +
+ +

Usage Example

+
+ +

+ +In this short example, we define the space key to trigger playing the "Walk" animation on channel2. +

+
  public void simpleInitApp() {
+    ...
+    inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
+    inputManager.addListener(actionListener, "Walk");
+    ...
+  }
+ 
+  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Walk") && !keyPressed) {
+        if (!channel2.getAnimationName().equals("Walk")) {
+          channel2.setLoopMode(LoopMode.Loop);
+          channel2.setAnim("Walk", 0.50f);
+        }
+      }
+    }
+  };
+ +
+ +

Animation Event Listener

+
+ +

+ +A jME3 application that contains animations can implement the com.jme3.animation.AnimEventListener interface. +

+
public class HelloAnimation extends SimpleApplication
+                     implements AnimEventListener { ... }
+ +

+This optional Listener enables you to respond to animation start and end events, onAnimChange() and onAnimCycleDone(). +

+ +
+ +

Responding to Animation End

+
+ +

+ +The onAnimCycleDone() event is invoked when an animation cycle has ended. For non-looping animations, this event is invoked when the animation is finished playing. For looping animations, this event is invoked each time the animation loop is restarted. +

+ +

+You have access to the following objects: +

+ +
  public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+    // test for a condition you are interested in, e.g. ...
+    if (animName.equals("Walk")) {
+      // respond to the event here, e.g. ...
+      channel.setAnim("Stand", 0.50f);
+    }
+  }
+ +
+ +

Responding to Animation Start

+
+ +

+ +The onAnimChange() event is invoked every time before an animation is set by the user to be played on a given channel (channel.setAnim()). +

+ +

+You have access to the following objects +

+ +
  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+    // test for a condition you are interested in, e.g. ...
+    if (animName.equals("Walk")) {
+      // respond to the event here, e.g. ...
+      channel.setAnim("Reset", 0.50f);
+    }
+  }
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html new file mode 100644 index 000000000..7b1f61053 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html @@ -0,0 +1,353 @@ + +

Application States

+
+ +

+ +The com.jme3.app.state.AppState class is a customizable jME3 interface that allows you to control the global game logic ??? the overall game mechanics. (To control the behaviour of a Spatial, see Custom Controls instead. Controls and AppStates can be used together.) +

+ +
+ +

Overview

+
+ +
+ +

Use Case Examples

+
+ +

+ +There are situations during your game development where you think: +

+ + +

+ +You can! This is what AppStates are there for. An AppState class is subset of (or an extension to) your application. Every AppState class has access to all fields in your main application (AssetManager, ViewPort, StateManager, InputManager, RootNode, GuiNode, etc) and hooks into the main update loop. An AppState can contain: +

+ + +
+ +

Supported Features

+
+ +

+ +Each AppState lets you define what happens in the following situations: +

+ + +

+ +

Tip: AppStates are extremely handy to swap out, or pause/unpause whole sets of other AppStates. For example, an InGameState (loads in-game GUI, activates click-to-shoot input mappings, inits game content, starts game loop) versus MainScreenState (stops game loop, saves and detaches game content, switches to menu screen GUI, switches to click-to-select input mappings). +

+

+ +
+ +

Usage

+
+ +

+ +To implement game logic: +

+
    +
  1. Create one AbstractAppState instance for each set of game mechanics.
    +
  2. +
  3. Implement game behaviour in the AppState's update() method.
    +
      +
    • You can pass custom data as arguments in the constructor.
      +
    • +
    • The AppState has access to everything inside the app's scope via the Application app object.
      +
    • +
    +
  4. +
  5. Attach the AppState to the AppStateManager (stateManager.attach(myAppState);).
    +
  6. +
  7. Activate and deactivate (unpause and pause) the AppStates that you need.
    +
  8. +
+ +

+ +When you add several AppStates to one Application and activate them, their initialize() methods and update() loops are executed in the order in which the AppStates were added to the AppStateManager. +

+ +
+ +

Code Samples

+
+ +

+ +JME3 comes with a BulletAppState that implements Physical behaviour (using the jBullet library). You, for example, could write an Artificial Intelligence AppState to control all your enemy units. Existing examples in the code base include: +

+ + +
+ +

AppState

+
+ +

+ +The AppState interface lets you initialize sets of objects, and hook a set of continously executing code into the main loop. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AppState MethodUsage
initialize(asm,app)When this AppState is added to the game, the RenderThread initializes the AppState and then calls this method. You can modify the scene graph from here (e.g. attach nodes). To get access to the main app, call:
super.initialize(stateManager, app);
+this.app = (SimpleApplication) app;
+
cleanup()This method is executed after you remove the AppState from the game. Here you implement clean-up code for when this state is detached. You can modify the scene graph from here (e.g. detach nodes).
update(float tpf)Here you implement the behaviour that you want to hook into the simpleUpdate() loop while this state is attached to the game. You can modify the scene graph from here.
isInitialized()Your implementations of this interface should return the correct respective boolean value. (See AbstractAppState)
setActive(true)
+setActive(false)
Temporarily enables or disables an AppState. (See AbstractAppState)
isActive()Test whether AppState is enabled or disabled. Your implementation should consider the boolean. (See AbstractAppState)
stateAttached(asm)
+stateDetached(asm)
The AppState knows when it is attached to, or detached from, the AppStateManager, and triggers these two methods. Don't modify the scene graph from here! (Typically not used.)
render(RenderManager rm)Renders the state, plus your optional customizations. (Typically not used.)
postRender()Called after all rendering commands are flushed, including your optional customizations. (Typically not used.)
+ +
+ +

AbstractAppState

+
+ +

+ +The AbstractAppState class already implements some common methods (isInitialized(), setActive(), isActive()) and makes creation of custom AppStates a bit easier. We recommend you extend AbstractAppState and override the remaining AppState methods: initialize(), setEnabled(), cleanup(). +

+ +

+Definition: + +

+
public class MyAppState extends AbstractAppState {
+ 
+    private SimpleApplication app;
+ 
+    private Node x = new Node("x");  // some custom class fields...    
+    public Node getX(){ return x; }  // some custom methods... 
+ 
+    @Override
+    public void initialize(AppStateManager stateManager, Application app) {
+      super.initialize(stateManager, app); 
+      this.app = (SimpleApplication)app;          // cast to a more specific class
+ 
+      // init stuff that is independent of whether state is PAUSED or RUNNING
+      this.app.getRootNode().attachChild(getX()); // modify scene graph...
+      this.app.doSomething();                     // call custom methods...
+   }
+ 
+   @Override
+    public void cleanup() {
+      super.cleanup();
+      // unregister all my listeners, detach all my nodes, etc...
+      this.app.getRootNode().detachChild(getX()); // modify scene graph...
+      this.app.doSomethingElse();                 // call custom methods...
+    }
+ 
+    @Override
+    public void setEnabled(boolean enabled) {
+      // Pause and unpause
+      super.setEnabled(enabled);
+      if(enabled){
+        // init stuff that is in use while this state is RUNNING
+        this.app.getRootNode().attachChild(getX()); // modify scene graph...
+        this.app.doSomethingElse();                 // call custom methods...
+      } else {
+        // take away everything not needed while this state is PAUSED
+        ...
+      }
+    }
+ 
+    // Note that update is only called while the state is both attached and enabled.
+    @Override
+    public void update(float tpf) {
+      // do the following while game is RUNNING
+      this.app.getRootNode().getChild("blah").scale(tpf); // modify scene graph...
+      x.setUserData(...);                                 // call some methods...
+    }
+ 
+}
+ +
+ +

Pausing and Unpausing

+
+ +

+ +You define what an AppState does when Paused or Unpaused, in the setEnabled() and update() methods. Call myState.setEnabled(false) on all states that you want to pause. Call myState.setEnabled(true) on all states that you want to unpause. +

+ +
+ +

AppStateManager

+
+ +

+ +The com.jme3.app.state.AppStateManager holds the list of AppStates for an application. AppStateManager ensures that active AppStates can modify the scene graph, and that the update() loops of active AppStates is executed. There is one AppStateManager per application. You typically attach several AppStates to one AppStateManager, but the same state can only be attached once. + +

+
+ + + + + + + + + +
AppStateManager MethodUsage
hasState(myState)Is AppState object 'myState' attached?
getState(MyAppState.class)Returns the first attached state that is an instance of a subclass of MyAppState.class.
+ +

+ +The AppStateManager's render(), postRender(), cleanup() methods are internal, ignore them, users never call them directly. + +

+ + +
+ +

Best Practices

+
+ +
+ +

Communication Among AppStates

+
+ +

+ +You can only access other AppStates (read from and write to them) from certain places: From a Control's update() method, from an AppState's update() method, and from the SimpleApplication's simpleUpdate() loop. Don't mess with the AppState from other places, because from other methods you have no control over the order of modifications; the game can go out of sync because you can't know when (during which half-finished step of another state change) your modification will be performed. +

+ +

+You can use custom accessors to get data from AppStates, to set data in AppStates, or to trigger methods in AppStates. + +

+
this.app.getStateManager().getState(MyAppState.class).doSomeCustomStuffInThisState();
+ +
+ +

Initialize Familiar Class Fields

+
+ +

+ +To access class fields of the SimpleApplication the way you are used to, initialize them to local variables, as shown in the following AppState template: +

+
private SimpleApplication app;
+private Node              rootNode;
+private AssetManager      assetManager;
+private AppStateManager   stateManager;
+private InputManager      inputManager;
+private ViewPort          viewPort;
+private BulletAppState    physics;
+ 
+public class MyAppState extends AbstractAppState {
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+    this.app = (SimpleApplication) app; // can cast Application to something more specific
+    this.rootNode     = this.app.getRootNode();
+    this.assetManager = this.app.getAssetManager();
+    this.stateManager = this.app.getStateManager();
+    this.inputManager = this.app.getInputManager();
+    this.viewPort     = this.app.getViewPort();
+    this.physics      = this.stateManager.getState(BulletAppState.class);
+  }
+}
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/appstatesdemo.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/appstatesdemo.html new file mode 100644 index 000000000..234742140 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/appstatesdemo.html @@ -0,0 +1,382 @@ + +

Simple AppStates Demo

+
+ +

+ +This demo is a simple example of how you use AppStates to toggle between a StartScreen and a SettingsScreen (press RETURN) while the game is paused, and start the game by switching to a GameRunning state (press BACKSPACE). +

+ +

+There are four files, Main.java, GameRunningState.java, StartScreenState.java, SettingsScreenState.java. +

+ +
+ +

Main.java

+
+
package chapter04.appstatedemo;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.Trigger;
+ 
+/**
+ * This demo shows a simple "game" with three AppStates. Instead of game content, 
+ * it just displays three cubes on different backgrounds.
+ * <ul>
+ * <li>StartScreenState: This state is enabled 
+ *     when the user starts the application, or the the game is paused. 
+ *     Press BACKSPACE to return to the game, press RETURN to go to Settings.</li>
+ * <li>GameRunningState: This state shows the game content and is enabled while the game is running. 
+ *     Press BACKSPACE to pause and return to the start screen.</li>
+ * <li>SettingsScreenState: This Settings screen state can be reached from the start screen
+ *     Press RETURN to toggle it on and off.</li>
+ * </ul>
+ */
+public class Main extends SimpleApplication {
+ 
+  private Trigger pause_trigger = new KeyTrigger(KeyInput.KEY_BACK);
+  private Trigger save_trigger = new KeyTrigger(KeyInput.KEY_RETURN);
+  private boolean isRunning = false; // starts at startscreen
+  private GameRunningState gameRunningState;
+  private StartScreenState startScreenState;
+  private SettingsScreenState settingsScreenState;
+ 
+ 
+  /** Start the jMonkeyEngine application */
+  public static void main(String[] args) {
+    Main app = new Main();
+    app.start();
+  }
+ 
+  /**
+   * initialize the scene here
+   */
+  @Override
+  public void simpleInitApp() {
+    setDisplayFps(false);
+    setDisplayStatView(false);
+ 
+    gameRunningState    = new GameRunningState(this);
+    startScreenState    = new StartScreenState(this);
+    settingsScreenState = new SettingsScreenState(this);
+ 
+    stateManager.attach(startScreenState);
+ 
+    inputManager.addMapping("Game Pause Unpause", pause_trigger);
+    inputManager.addListener(actionListener, new String[]{"Game Pause Unpause"});
+    inputManager.addMapping("Toggle Settings", save_trigger);
+    inputManager.addListener(actionListener, new String[]{"Toggle Settings"});
+  }
+ 
+  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean isPressed, float tpf) {
+      System.out.println("key" + name);
+      if (name.equals("Game Pause Unpause") && !isPressed) {
+        if (isRunning) {
+          stateManager.detach(gameRunningState);
+          stateManager.attach(startScreenState);
+          System.out.println("switching to startscreen...");
+ 
+        } else {
+          stateManager.detach(startScreenState);
+          stateManager.attach(gameRunningState);
+          System.out.println("switching to game...");
+        }
+        isRunning = !isRunning;
+      } else if (name.equals("Toggle Settings") && !isPressed && !isRunning) {
+        if (!isRunning && stateManager.hasState(startScreenState)) {
+          stateManager.detach(startScreenState);
+          stateManager.attach(settingsScreenState);
+          System.out.println("switching to settings...");
+        } else if (!isRunning && stateManager.hasState(settingsScreenState)) {
+          stateManager.detach(settingsScreenState);
+          stateManager.attach(startScreenState);
+          System.out.println("switching to startscreen...");
+        }
+      }
+    }
+  };
+ 
+  @Override
+  public void simpleUpdate(float tpf) {}
+ 
+}
+ 
+ +
+ +

GameRunningState.java

+
+
package chapter04.appstatedemo;
+ 
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+ 
+/**
+ * A template how to create an Application State. This example state simply
+ * changes the background color depending on the camera position.
+ */
+public class GameRunningState extends AbstractAppState {
+ 
+  private ViewPort viewPort;
+  private Node rootNode;
+  private Node guiNode;
+  private AssetManager assetManager;
+  private Node localRootNode = new Node("Game Screen RootNode");
+  private Node localGuiNode = new Node("Game Screen GuiNode");
+  private final ColorRGBA backgroundColor = ColorRGBA.Blue;
+ 
+  public GameRunningState(SimpleApplication app){
+    this.rootNode     = app.getRootNode();
+    this.viewPort      = app.getViewPort();
+    this.guiNode       = app.getGuiNode();
+    this.assetManager  = app.getAssetManager();  
+  }
+ 
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+ 
+    /** Load this scene */
+    viewPort.setBackgroundColor(backgroundColor);
+ 
+    Box mesh = new Box(Vector3f.ZERO, 1, 1, 1);
+    Geometry geom = new Geometry("Box", mesh);
+    Material mat = new Material(assetManager,
+            "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.Green);
+    geom.setMaterial(mat);
+    geom.setLocalTranslation(1, 0, 0);
+    localRootNode.attachChild(geom);
+ 
+    /** Load the HUD*/
+    BitmapFont guiFont = assetManager.loadFont(
+            "Interface/Fonts/Default.fnt");
+    BitmapText displaytext = new BitmapText(guiFont);
+    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
+    displaytext.move(10, displaytext.getLineHeight() + 20, 0);
+    displaytext.setText("Game running. Press BACKSPACE to pause and return to the start screen.");
+    localGuiNode.attachChild(displaytext);
+  }
+ 
+  @Override
+  public void update(float tpf) {
+    /** the action happens here */
+    Vector3f v = viewPort.getCamera().getLocation();
+    viewPort.setBackgroundColor(new ColorRGBA(v.getX() / 10, v.getY() / 10, v.getZ() / 10, 1));
+    rootNode.getChild("Box").rotate(tpf, tpf, tpf);
+  }
+ 
+  @Override
+  public void stateAttached(AppStateManager stateManager) {
+    rootNode.attachChild(localRootNode);
+    guiNode.attachChild(localGuiNode);
+    viewPort.setBackgroundColor(backgroundColor);
+  }
+ 
+  @Override
+  public void stateDetached(AppStateManager stateManager) {
+    rootNode.detachChild(localRootNode);
+    guiNode.detachChild(localGuiNode);
+ 
+  }
+ 
+}
+ +
+ +

SettingsScreenState.java

+
+
package chapter04.appstatedemo;
+ 
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+ 
+/**
+ * A template how to create an Application State. This example state simply
+ * changes the background color depending on the camera position.
+ */
+public class SettingsScreenState extends AbstractAppState {
+ 
+  private ViewPort viewPort;
+  private Node rootNode;
+  private Node guiNode;
+  private AssetManager assetManager;
+  private Node localRootNode = new Node("Settings Screen RootNode");
+  private Node localGuiNode = new Node("Settings Screen GuiNode");
+  private final ColorRGBA backgroundColor = ColorRGBA.DarkGray;
+ 
+  public SettingsScreenState(SimpleApplication app) {
+    this.rootNode     = app.getRootNode();
+    this.viewPort      = app.getViewPort();
+    this.guiNode       = app.getGuiNode();
+    this.assetManager  = app.getAssetManager();
+  }
+ 
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+ 
+    /** Load this scene */
+    viewPort.setBackgroundColor(backgroundColor);
+ 
+    Box mesh = new Box(new Vector3f(-1, -1, 0), .5f, .5f, .5f);
+    Geometry geom = new Geometry("Box", mesh);
+    Material mat = new Material(assetManager,
+            "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.Red);
+    geom.setMaterial(mat);
+    geom.setLocalTranslation(1, 0, 0);
+    localRootNode.attachChild(geom);
+ 
+    /** Load the HUD */
+    BitmapFont guiFont = assetManager.loadFont(
+            "Interface/Fonts/Default.fnt");
+    BitmapText displaytext = new BitmapText(guiFont);
+    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
+    displaytext.move(10, displaytext.getLineHeight() + 20, 0);
+    displaytext.setText("Settings screen. Press RETURN to save "
+            + "and return to start screen.");
+    localGuiNode.attachChild(displaytext);
+  }
+ 
+  @Override
+  public void update(float tpf) {
+     /** the action happens here */
+  }
+ 
+  @Override
+  public void stateAttached(AppStateManager stateManager) {
+    rootNode.attachChild(localRootNode);
+    guiNode.attachChild(localGuiNode);
+    viewPort.setBackgroundColor(backgroundColor);
+  }
+ 
+  @Override
+  public void stateDetached(AppStateManager stateManager) {
+    rootNode.detachChild(localRootNode);
+    guiNode.detachChild(localGuiNode);
+  }
+ 
+}
+ +
+ +

StartScreenState.java

+
+
package chapter04.appstatedemo;
+ 
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.font.BitmapFont;
+import com.jme3.font.BitmapText;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+ 
+/**
+ * A template how to create an Application State. This example state simply
+ * changes the background color depending on the camera position.
+ */
+public class StartScreenState extends AbstractAppState {
+ 
+  private ViewPort viewPort;
+  private Node rootNode;
+  private Node guiNode;
+  private AssetManager assetManager;
+  private Node localRootNode = new Node("Start Screen RootNode");
+  private Node localGuiNode = new Node("Start Screen GuiNode");
+  private final ColorRGBA backgroundColor = ColorRGBA.Gray;  
+ 
+public StartScreenState(SimpleApplication app){
+    this.rootNode     = app.getRootNode();
+    this.viewPort     = app.getViewPort();
+    this.guiNode      = app.getGuiNode();
+    this.assetManager = app.getAssetManager();  
+  }
+ 
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+ 
+    /** Init this scene */
+    viewPort.setBackgroundColor(backgroundColor);
+ 
+    Box mesh = new Box(new Vector3f(-1,1,0), .5f,.5f,.5f);
+    Geometry geom = new Geometry("Box", mesh);
+    Material mat = new Material(assetManager,
+            "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.Yellow);
+    geom.setMaterial(mat);
+    geom.setLocalTranslation(1, 0, 0);
+    localRootNode.attachChild(geom);
+ 
+    /** Load a HUD */
+    BitmapFont guiFont = assetManager.loadFont(
+            "Interface/Fonts/Default.fnt");
+    BitmapText displaytext = new BitmapText(guiFont);
+    displaytext.setSize(guiFont.getCharSet().getRenderedSize());
+    displaytext.move( 10, displaytext.getLineHeight() + 20,  0);
+    displaytext.setText("Start screen. Press BACKSPACE to resume the game, "
+            + "press RETURN to edit Settings.");
+    localGuiNode.attachChild(displaytext);
+  }
+ 
+  @Override
+  public void update(float tpf) {
+    /** the action happens here */
+  }
+ 
+  @Override
+  public void stateAttached(AppStateManager stateManager) {
+    rootNode.attachChild(localRootNode);
+    guiNode.attachChild(localGuiNode);
+    viewPort.setBackgroundColor(backgroundColor);
+  }
+ 
+  @Override
+  public void stateDetached(AppStateManager stateManager) {
+    rootNode.detachChild(localRootNode);
+    guiNode.detachChild(localGuiNode);
+  }
+ 
+}
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/asset_manager.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/asset_manager.html new file mode 100644 index 000000000..aee4d2691 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/asset_manager.html @@ -0,0 +1,320 @@ + +

AssetManager

+
+ +

+ +By assets we mean multi-media files, such as 3D models, materials, textures, scenes, custom shaders, music and sound files, and custom fonts. JME3 has an integrated asset manager that helps you keep your project assets organized. Think of the asset manager as the filesystem of your game, independent of the actual deployment platform. By default, store your assets in the MyGame/assets/ directory of your project. +

+ +

+Advantages of the AssetManager: +

+ + +

+ +Advanced users can write a custom build and packaging script, and can register custom paths to the AssetManager, but this is up to you then. +

+ +
+ +

Context

+
+
jMonkeyProjects/MyGame/assets/    # You store assets in subfolders here! <------
+jMonkeyProjects/MyGame/build/     # SDK generates built classes here (*)
+jMonkeyProjects/MyGame/build.xml  # You customize Ant build script here
+jMonkeyProjects/MyGame/nbproject/ # SDK stores default build.xml and meta data (*)
+jMonkeyProjects/MyGame/dist/      # SDK generates executable distribution here (*)
+jMonkeyProjects/MyGame/src/       # You store Java sources here
+jMonkeyProjects/MyGame/test/      # You store test classes here (optional)
+(*) Managed by jMonkeyEngine SDK -- don't edit!
+ +

+See also Best Practices. +

+ +
+ +

Usage

+
+ +

+ +The assetManager object is an com.jme3.asset.AssetManager instance that every com.jme3.app.Application can access. It maintains a root that also includes your project's classpath by default, so you can load any asset that's on the classpath, that is, the top level of your project directory. +

+ +

+You can use the inherited assetManager object directly, or use the accessor app.getAssetManager(). +

+ +

+Here is an example how you load assets using the AssetManager. This lines loads a default Material from the built-in Common/ directory: +

+
Material mat = (Material) assetManager.loadAsset(
+    new AssetKey("Common/Materials/RedColor.j3m"));
+ +

+This Material is "somewhere" in the jME3 JAR; the default Asset Manager is configured to handle a Common/??? path correctly, so you don't have to specify the whole path when referring to built-in assets (such as default Materials). +

+ +

+Additionally, you can configure the Asset Manager and add any path to its root. This means, you can load assets from any project directory you specify. The next example shows how you load assets from your project's assets directory. +

+ +
+ +

Asset Directory

+
+ +

+ +By default, jME3 searches for models in a directory named assets. +

+ +

+

In Java projects created with the jMonkeyEngine SDK, an assets folder is created by default in your project directory. If you are using any other IDE, or the command line, you simply create an assets directory manually (see the Codeless Project tip below). +

+

+ +

+This is our recommended directory structure for storing assets: +

+
jMonkeyProjects/MyGame/src/...           # Packages, .java source code.
+jMonkeyProjects/MyGame/assets/...        # The assets directory:
+jMonkeyProjects/MyGame/assets/Interface/   # .font, .jpg, .png, .xml
+jMonkeyProjects/MyGame/assets/MatDefs/     # .j3md
+jMonkeyProjects/MyGame/assets/Materials/   # .j3m
+jMonkeyProjects/MyGame/assets/Models/      # .j3o
+jMonkeyProjects/MyGame/assets/Scenes/      # .j3o
+jMonkeyProjects/MyGame/assets/Shaders/     # .j3f, .vert, .frag
+jMonkeyProjects/MyGame/assets/Sounds/      # .ogg, .wav
+jMonkeyProjects/MyGame/assets/Textures/    # .jpg, .png; also .mesh.xml+.material, .mtl+.obj, .blend (!) 
+ +

+These subdirectories are just the most common examples. +

+ +

+

You can rename/delete/add (sub)directories inside the assets directory in any way you like. Note however that there is no automatic refactoring for asset paths in the SDK, so if you modify them late in the development process, you have to refactor all paths manually. +

+

+ +

+Examples: You can rename assets/Sounds to assets/Audio, you can delete assets/MatDefs if you don't use it, you can create assets/AIscripts, etc. You can rename/move the assets/Textures directory or its subdirectories, but then you have to re-export all models, and re-convert them all to .j3o, so plan ahead! +

+ +

+

Store textures in assets/Textures/ before you work with them in a mesh editor! Export and save 3D model files (.mesh.xml+.material, .mtl+.obj, .blend) into the assets/Textures/ (!) before you convert the model to binary format (.j3o)! This ensures that texture paths correctly point to the assets/Textures directory.
+After the conversion, you move the .j3o file into the assets/Models/ or assets/Scenes/ directories. This way, you can reuse textures, your binaries consistently link the correct textures, and the assets/Models and assets/Scenes directories don't become cluttered. +

+

+ +
+ +

Example Code: Loading Assets

+
+ +

+ +Creating a material instance with the definition "Unshaded.j3md": + +

+
Material mat_brick = new Material( 
+    assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ +

+Applying a texture to the material: + +

+
mat_brick.setTexture("ColorMap", 
+    assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
+ +

+Loading a font: + +

+
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+ +

+Loading a model: + +

+
Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+ +

+Loading a scene from an Ogre3D dotScene file stored inside a zip: + +

+
assetManager.registerLocator("town.zip", ZipLocator.class);
+Spatial scene = assetManager.loadModel("main.scene");
+rootNode.attachChild(scene);
+ +

+Alternatively to ZipLocator, there is also a HttpZipLocator that can stream models from a zip file online: +

+
assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip", 
+                             HttpZipLocator.class);
+Spatial scene = assetManager.loadModel("main.scene");
+rootNode.attachChild(scene);
+ +

+jME3 also offers a ClasspathLocator, ZipLocator, FileLocator, HttpZipLocator, and UrlLocator (see com.jme3.asset.plugins). +

+ +

+

The custom build script does not automatically include all ZIP files in the executable build. See "Cannot Locate Resource" solution below. +

+

+ +
+ +

Common AssetManager Tasks

+
+
+ + + + + + + + + + + + +
Task? Solution!
Load a model with materials Use the asset manager's loadModel() method and attach the Spatial to the rootNode.
Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.mesh.xml");
+rootNode.attachChild(elephant);
+
Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.j3o");
+rootNode.attachChild(elephant);
+
Load a model without materials If you have a model without materials, you have to add a default material to make it visible.
Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+teapot.setMaterial(mat);
+rootNode.attachChild(teapot);
+
Load a scene You load scenes just like you load models:
Spatial scene = assetManager.loadModel("Scenes/house/main.scene");
+rootNode.attachChild(scene);
+
+ +
+ +

NullPointerException: Cannot locate resource?

+
+ +

+ +Problem: +

+ +

+My game runs fine when I run it right from the jMonkeyEngine SDK. But when I run the stand-alone executables (.jar, .jnlp .exe, .app), a DesktopAssetManager error message occurs in the console, and it quits? +

+
com.jme3.asset.DesktopAssetManager loadAsset
+WARNING: Cannot locate resource: Scenes/town/main.scene
+com.jme3.app.Application handleError
+SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
+java.lang.NullPointerException
+ +

+Reason: +

+ +

+If you use the default build script, original models and scenes (.mesh.xml, .obj, .blend, .zip), are excluded from the distribution automatically. A stand-alone executable includes converted .j3o files (models and scenes) only. The default build script makes sure to bundle existing .j3o files in the distribution, but you need to remember to convert the models (from mesh.xml???>.j3o, or .obj???>.j3o, etc) yourself. +

+ +

+Solution +

+ +

+Before building the executable, you must use the jMonkeyEngine SDK's context menu action to convert 3D models to .j3o binary format. +

+
    +
  1. Save your original models (.mesh.xml, .scene, .blend, or .obj files, plus textures) into assets/Textures/. (!)
    +
  2. +
  3. Open the jME3 project in the jMonkeyEngine SDK.
    +
  4. +
  5. Browse to the assets directory in the Projects window.
    +
  6. +
  7. Right-click an original model in assets/Textures/, and choose "Convert to JME3 binary".
    +
  8. +
  9. The converted file appears in the same directory as the original file. It has the same name and a .j3o suffix.
    +
  10. +
  11. Move the .j3o file into the assets/Models/ or assets/Scenes/ directory.
    +
  12. +
  13. Use the assetManager's load() method to load the .j3o file.
    +
  14. +
+ +

+ +This ensures that the model's Texture paths keep working between your 3D mesh editor and JME3. +

+ +

+

If you must load custom assets from a non-.j3o ZIP file, you must manually ammend the default build script to copy ZIP files into your distribution. ZIPs are skipped by default. +

+

+ +
+ +

Asset Handling For Other IDEs: Codeless Projects

+
+ +

+ +Problem: +

+ +

+I use another IDE than jMonkeyEngine SDK for coding (Eclipse, IntelliJ, text editor). Where is my asset folder and .j3o converter? +

+ +

+Solution: +

+ +

+You can code in any IDE, but you must create a so-called codeless project in the jMonkeyEngine SDK to maintain assets. A code-less jMonkeyEngine project does not meddle with your sources or custom build scripts. You merely use it to convert models to .j3o binaries. + +

+
    +
  1. Create your (Eclipse or whatever) project as you like.
    +
  2. +
  3. Create a directory in your project folder and name it, for example, assets.
    +Store your assets there as described above.
    +
  4. +
  5. Download and install the jMonkeyEngine SDK.
    +
  6. +
  7. In the SDK, go to File ??? Import Projects ??? External Project Assets.
    +
  8. +
  9. Select your (Eclipse or whatever) project and your assets folder in the Import Wizard.
    +
  10. +
  11. You can now open this (Eclipse or whatever) project in the jMonkeyEngine SDK.
    +Convert assets as described above.
    +
  12. +
+ +

+ +

If you don't use the SDK for some reason, you can still convert models to j3o format: Load any model in Ogre3D or Wavefront format with the AssetManager.loadModel() as a spatial. Then save the spatial as j3o file using BinaryExporter. +

+

+ +

+

Use file version control and let team members check out the project. Your developers open the project in Eclipse (etc) as they are used to. Additionally to their graphic tools, ask your graphic designers to install the jMonkeyEngine SDK, and to check out the codeless project that you just prepared. This makes it easy for non-coding team member to browse and preview game assets, to arrange scenes, and to convert files. At the same time, non-coders don't accidentally mess with code, and developers don't accidentally mess with assets. :) +

+

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html new file mode 100644 index 000000000..2ba08092f --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html @@ -0,0 +1,270 @@ + +

Audio in jME3

+
+ +

+ +Place audio files in the assets/Sound/ directory of your project. jME3 supports Ogg Vorbis audio compression (.ogg) and uncompressed PCM Wave (.wav) formats. You can use for example to convert from other formats. +

+ +
+ +

Audio Terminology

+
+ + +
+ +

Creating Audio Nodes: Streamed or Buffered

+
+ +

+ +The main jME audio class to look at is com.jme3.audio.AudioNode. When creating a new audio node you need to declare whether how you want to load this sound: + +

+ + +
+ +

Getting AudioNode Properties

+
+
+ + + + + + + + + + + + +
AudioNode MethodUsage
getStatus()Returns either Status.Playing, Status.Stopped, or Status.Paused.
getVolume()Returns the volume.
getPitch()Returns the pitch.
+ +

+ +Note: There are other obvious getters to poll the status of all corresponding setters listed here. +

+ +
+ +

Setting AudioNode Properties

+
+
+ + + + + + + + + + + + + + + + + + + + + +
AudioNode MethodUsage
setTimeOffset(0.5f)Play the sound starting at a 0.5 second offset from the beginning. Default is 0.
setPitch(1)Makes the sound play in a higher or lower pitch. Default is 1. 2 is twice as high, .5f is half as high.
setVolume(1)Sets the volume gain. 1 is the default volume, 2 is twice as loud, etc. 0 is silent/mute.
setRefDistance(50f)The reference distance controls how far a sound can still be heard at 50% of its original volume (this is assuming an exponential fall-off!). A sound with a high RefDist can be heard loud over wide distances; a sound with a low refDist can only be heard when the listener is close by. Default is 10 world units.
setMaxDistance(100f) The 'maximum attenuation distance' specifies how far from the source the sound stops growing more quiet (sounds in nature don't do that). Set this to a smaller value to keep the sound loud even at a distance; set this to higher value to let the sound fade out quickly. Default is 20 world units.
setLooping(false)Configures the sound so that, if it is played, it plays once and stops. No looping is the default.
+ +
+ +

Looping & Ambient Sounds

+
+
+ + + + + + + + + +
AudioNode MethodUsage
setPositional(false)
+setDirectional(false)
All 3D effects switched off. This sound is global and plays in headspace (it appears to come from everywhere). Good for environmental ambient sounds and background music.
setLooping(true)Configures the sound to be a loop: After the sound plays, it repeats from the beginning, until you call stop() or pause(). Good for music and ambient background noises.
+Looping does not work on streamed sounds.
+ +
+ +

Positional 3D Sounds

+
+
+ + + + + + + + + +
AudioNode MethodUsage
setPositional(true)
+setLocalTranslation(???)
Activates 3D audio: The sound appears to come from a certain position, where it is loudest. Position the AudioNode in the 3D scene, or move it with mobile players or NPCs.
setReverbEnabled(true)Reverb is a 3D echo effect that only makes sense with positional AudioNodes. Use Audio Environments to make scenes sound as if they were "outdoors", or "indoors" in a large or small room, etc. The reverb effect is defined by the com.jme3.audio.Environment that the audioRenderer is in. See "Setting Audio Environment Properties" below.
+ +

+ +

Positional 3D sounds require an AudioListener object in the scene (representing the player's ears). +

+

+ +
+ +

Directional 3D Sounds

+
+
+ + + + + + + + + +
AudioNode MethodUsage
setDirectional(true)
+setDirection(???)
Activates 3D audio: This sound can only be heard from a certain direction. Specify the direction and angle in the 3D scene if you have setDirectional() true. Use this to restrict noises that should not be heard, for example, through a wall.
setInnerAngle()
+setOuterAngle()
Set the angle in degrees for the directional audio. The angle is relative to the direction. Note: By default, both angles are 360?? and the sound can be heard from all directions!
+ +

+ +

Directional 3D sounds require an AudioListener object in the scene (representing the player's ears). +

+

+ +
+ +

Play, Pause, Stop

+
+ +

+ +You play, pause, and stop a node called myAudioNode by using the respective of the following three methods: +

+
myAudioNode.play();
+
myAudioNode.pause();
+
myAudioNode.stop();
+ +

+Note: Whether an Audio Node plays continuously or only once, depends on the Loop properties you have set above! +

+ +

+You can also start playing instances of an AudioNode. Use the playInstance() method if you need to play the same AudioNode multiple times, possibly simulatenously. Note that changes to the parameters of the original AudioNode do not affect the instances that are already playing! +

+
myAudioNode.playInstance();
+ +
+ +

The Audio Listener

+
+ +

+ +The default AudioListener object listener in SimpleApplication is the user's ear in the scene. If you use 3D audio (positional or directional sounds), you must move the AudioListener with the player: For example, for a first-person player, you move the listener with the camera. For a third-person player, you move the listener with the player avatar Geometry. +

+
  @Override
+  public void simpleUpdate(float tpf) {
+    // first-person: keep the audio listener moving with the camera
+    listener.setLocation(cam.getLocation());
+    listener.setRotation(cam.getRotation());
+  }
+ +
+ +

Setting Audio Environment Properties

+
+ +

+ +Optionally, You can choose from the following environmental presets from com.jme3.audio.Environment. This presets influence subtle echo effects (reverb) that evoke associations of different environments in your users. That is, it makes you scene sound "indoors" or "outdoors" etc. You use Audio Environments together with setReverbEnabled(true) on positional AudioNodes (see above). + +

+
+ + + + + + + + + + + + + + + + + + +
EnvironmentdensitydiffusiongaingainHfdecayTimedecayHfreflGainreflDelaylateGainlateDelay
Garage 1.00f1.0f1.0f1.00f0.90f0.5f0.751f0.0039f0.661f0.0137f
Dungeon 0.75f1.0f1.0f0.75f1.60f1.0f0.950f0.0026f0.930f0.0103f
Cavern 0.50f1.0f1.0f0.50f2.25f1.0f0.908f0.0103f0.930f0.0410f
AcousticLab 0.50f1.0f1.0f1.00f0.28f1.0f0.870f0.0020f0.810f0.0080f
Closet 1.00f1.0f1.0f1.00f0.15f1.0f0.600f0.0025f0.500f0.0006f
+
    +
  1. Activate a Environment preset
    +
      +
    • Either use a default, e.g. make you scene sounds like a dungeon environment:
      audioRenderer.setEnvironment(new Environment(Environment.Dungeon));
      +
      +
    • +
    • Or activate custom environment settings in the Environment constructor:
      audioRenderer.setEnvironment(
      +        new Environment( density, diffusion, gain, gainHf, decayTime, decayHf,
      +                reflGain, reflDelay, lateGain, lateDelay ) );
      +
      +
    • +
    +
  2. +
  3. Activate 3D audio for certain sounds:
    footstepsAudio.setPositional(true);
    +footstepsAudio.setReverbEnabled(true);
    +
    +
  4. +
+ +

+ +

A sound engineer can create a custom com.???jme3.???audio.Environment object and specify custom environment values such as density, diffusion, gain, decay, delay??? You can find many examples of custom audio environment presets here. +

+

+ +

+Advanced users find more info about OpenAL and its features here: . +

+ +

+

It depends on the hardware whether audio effects are supported (if not, you get the message OpenAL EFX not available! Audio effects won't work.) +

+

+
+ sound, + documentation, + environment +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio_environment_presets.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio_environment_presets.html new file mode 100644 index 000000000..e611f609a --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio_environment_presets.html @@ -0,0 +1,213 @@ + +

Audio Environment Presets

+
+ +

+ +Use these presets together with Audio Nodes to create different "moods" for sounds. Environment effects make your audio sound as if the listener were in various places that have different types of echoes. +

+ +

+Usage: + +

+
Environment Generic = new Environment(
+    new float[]{ 0, 7.5f, 1f, -1000, -100, 0, 1.49f, 0.83f, 1f, -2602,
+                 0.007f, 0f, 0f, 0f, 200, 0.011f, 0f, 0f, 0f, 0.250f,
+                 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+audioRenderer.setEnvironment(myEnvironment);
+ +
+ +

Castle

+
+
CastleSmallRoom    = new Environment ( new float[]{ 26, 8.3f, 0.890f, -1000, -800, -2000, 1.22f, 0.83f, 0.31f, -100, 0.022f, 0f, 0f, 0f, 600, 0.011f, 0f, 0f, 0f, 0.138f, 0.080f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleShortPassage = new Environment ( new float[]{ 26, 8.3f, 0.890f, -1000, -1000, -2000, 2.32f, 0.83f, 0.31f, -100, 0.007f, 0f, 0f, 0f, 200, 0.023f, 0f, 0f, 0f, 0.138f, 0.080f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleMediumroom   = new Environment ( new float[]{ 26, 8.3f, 0.930f, -1000, -1100, -2000, 2.04f, 0.83f, 0.46f, -400, 0.022f, 0f, 0f, 0f, 400, 0.011f, 0f, 0f, 0f, 0.155f, 0.030f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleLongpassage  = new Environment ( new float[]{ 26, 8.3f, 0.890f, -1000, -800, -2000, 3.42f, 0.83f, 0.31f, -100, 0.007f, 0f, 0f, 0f, 300, 0.023f, 0f, 0f, 0f, 0.138f, 0.080f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleLargeroom    = new Environment ( new float[]{ 26, 8.3f, 0.820f, -1000, -1100, -1800, 2.53f, 0.83f, 0.50f, -700, 0.034f, 0f, 0f, 0f, 200, 0.016f, 0f, 0f, 0f, 0.185f, 0.070f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleHall         = new Environment ( new float[]{ 26, 8.3f, 0.810f, -1000, -1100, -1500, 3.14f, 0.79f, 0.62f, -1500, 0.056f, 0f, 0f, 0f, 100, 0.024f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleCupboard     = new Environment ( new float[]{ 26, 8.3f, 0.890f, -1000, -1100, -2000, 0.67f, 0.87f, 0.31f, 300, 0.010f, 0f, 0f, 0f, 1100, 0.007f, 0f, 0f, 0f, 0.138f, 0.080f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+CastleCourtyard    = new Environment ( new float[]{ 26, 8.3f, 0.420f, -1000, -700, -1400, 2.13f, 0.61f, 0.23f, -1300, 0.160f, 0f, 0f, 0f, -300, 0.036f, 0f, 0f, 0f, 0.250f, 0.370f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x1f} );
+CastleAlcove       = new Environment ( new float[]{ 26, 8.3f, 0.890f, -1000, -600, -2000, 1.64f, 0.87f, 0.31f, 00, 0.007f, 0f, 0f, 0f, 300, 0.034f, 0f, 0f, 0f, 0.138f, 0.080f, 0.250f, 0f, -5f, 5168.6f, 139.5f, 0f, 0x20} );
+ +
+ +

Warehouse, Factory

+
+
FactoryAlcove       = new Environment ( new float[]{ 26, 1.8f, 0.590f, -1200, -200, -600, 3.14f, 0.65f, 1.31f, 300, 0.010f, 0f, 0f, 0f, 000, 0.038f, 0f, 0f, 0f, 0.114f, 0.100f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryShortpassage = new Environment ( new float[]{ 26, 1.8f, 0.640f, -1200, -200, -600, 2.53f, 0.65f, 1.31f, 0, 0.010f, 0f, 0f, 0f, 200, 0.038f, 0f, 0f, 0f, 0.135f, 0.230f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} ) );
+FactoryMediumroom   = new Environment ( new float[]{ 26, 1.9f, 0.820f, -1200, -200, -600, 2.76f, 0.65f, 1.31f, -1100, 0.022f, 0f, 0f, 0f, 300, 0.023f, 0f, 0f, 0f, 0.174f, 0.070f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryLongpassage  = new Environment ( new float[]{ 26, 1.8f, 0.640f, -1200, -200, -600, 4.06f, 0.65f, 1.31f, 0, 0.020f, 0f, 0f, 0f, 200, 0.037f, 0f, 0f, 0f, 0.135f, 0.230f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryLargeroom    = new Environment ( new float[]{ 26, 1.9f, 0.750f, -1200, -300, -400, 4.24f, 0.51f, 1.31f, -1500, 0.039f, 0f, 0f, 0f, 100, 0.023f, 0f, 0f, 0f, 0.231f, 0.070f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryHall         = new Environment ( new float[]{ 26, 1.9f, 0.750f, -1000, -300, -400, 7.43f, 0.51f, 1.31f, -2400, 0.073f, 0f, 0f, 0f, -100, 0.027f, 0f, 0f, 0f, 0.250f, 0.070f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryCupboard     = new Environment ( new float[]{ 26, 1.7f, 0.630f, -1200, -200, -600, 0.49f, 0.65f, 1.31f, 200, 0.010f, 0f, 0f, 0f, 600, 0.032f, 0f, 0f, 0f, 0.107f, 0.070f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactoryCourtyard    = new Environment ( new float[]{ 26, 1.7f, 0.570f, -1000, -1000, -400, 2.32f, 0.29f, 0.56f, -1300, 0.140f, 0f, 0f, 0f, -800, 0.039f, 0f, 0f, 0f, 0.250f, 0.290f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+FactorySmallroom    = new Environment ( new float[]{ 26, 1.8f, 0.820f, -1000, -200, -600, 1.72f, 0.65f, 1.31f, -300, 0.010f, 0f, 0f, 0f, 500, 0.024f, 0f, 0f, 0f, 0.119f, 0.070f, 0.250f, 0f, -5f, 3762.6f, 362.5f, 0f, 0x20} );
+ +
+ +

Ice Palace

+
+
IcepalaceAlcove       = new Environment ( new float[]{ 26, 2.7f, 0.840f, -1000, -500, -1100, 2.76f, 1.46f, 0.28f, 100, 0.010f, 0f, 0f, 0f, -100, 0.030f, 0f, 0f, 0f, 0.161f, 0.090f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceShortpassage = new Environment ( new float[]{ 26, 2.7f, 0.750f, -1000, -500, -1100, 1.79f, 1.46f, 0.28f, -600, 0.010f, 0f, 0f, 0f, 100, 0.019f, 0f, 0f, 0f, 0.177f, 0.090f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} ) );
+IcepalaceMediumroom   = new Environment ( new float[]{ 26, 2.7f, 0.870f, -1000, -500, -700, 2.22f, 1.53f, 0.32f, -800, 0.039f, 0f, 0f, 0f, 100, 0.027f, 0f, 0f, 0f, 0.186f, 0.120f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceLongpassage  = new Environment ( new float[]{ 26, 2.7f, 0.770f, -1000, -500, -800, 3.01f, 1.46f, 0.28f, -200, 0.012f, 0f, 0f, 0f, 200, 0.025f, 0f, 0f, 0f, 0.186f, 0.040f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceLargeroom    = new Environment ( new float[]{ 26, 2.9f, 0.810f, -1000, -500, -700, 3.14f, 1.53f, 0.32f, -1200, 0.039f, 0f, 0f, 0f, 000, 0.027f, 0f, 0f, 0f, 0.214f, 0.110f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceHall         = new Environment ( new float[]{ 26, 2.9f, 0.760f, -1000, -700, -500, 5.49f, 1.53f, 0.38f, -1900, 0.054f, 0f, 0f, 0f, -400, 0.052f, 0f, 0f, 0f, 0.226f, 0.110f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceCupboard     = new Environment ( new float[]{ 26, 2.7f, 0.830f, -1000, -600, -1300, 0.76f, 1.53f, 0.26f, 100, 0.012f, 0f, 0f, 0f, 600, 0.016f, 0f, 0f, 0f, 0.143f, 0.080f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceCourtyard    = new Environment ( new float[]{ 26, 2.9f, 0.590f, -1000, -1100, -1000, 2.04f, 1.20f, 0.38f, -1000, 0.173f, 0f, 0f, 0f, -1000, 0.043f, 0f, 0f, 0f, 0.235f, 0.480f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+IcepalaceSmallroom    = new Environment ( new float[]{ 26, 2.7f, 0.840f, -1000, -500, -1100, 1.51f, 1.53f, 0.27f, -100, 0.010f, 0f, 0f, 0f, 300, 0.011f, 0f, 0f, 0f, 0.164f, 0.140f, 0.250f, 0f, -5f, 12428.5f, 99.6f, 0f, 0x20} );
+ +
+ +

Space Station

+
+
SpacestationAlcove       = new Environment ( new float[]{ 26, 1.5f, 0.780f, -1000, -300, -100, 1.16f, 0.81f, 0.55f, 300, 0.007f, 0f, 0f, 0f, 000, 0.018f, 0f, 0f, 0f, 0.192f, 0.210f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationMediumroom   = new Environment ( new float[]{ 26, 1.5f, 0.750f, -1000, -400, -100, 3.01f, 0.50f, 0.55f, -800, 0.034f, 0f, 0f, 0f, 100, 0.035f, 0f, 0f, 0f, 0.209f, 0.310f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationShortpassage = new Environment ( new float[]{ 26, 1.5f, 0.870f, -1000, -400, -100, 3.57f, 0.50f, 0.55f, 0, 0.012f, 0f, 0f, 0f, 100, 0.016f, 0f, 0f, 0f, 0.172f, 0.200f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationLongpassage  = new Environment ( new float[]{ 26, 1.9f, 0.820f, -1000, -400, -100, 4.62f, 0.62f, 0.55f, 0, 0.012f, 0f, 0f, 0f, 200, 0.031f, 0f, 0f, 0f, 0.250f, 0.230f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationLargeroom    = new Environment ( new float[]{ 26, 1.8f, 0.810f, -1000, -400, -100, 3.89f, 0.38f, 0.61f, -1000, 0.056f, 0f, 0f, 0f, -100, 0.035f, 0f, 0f, 0f, 0.233f, 0.280f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationHall         = new Environment ( new float[]{ 26, 1.9f, 0.870f, -1000, -400, -100, 7.11f, 0.38f, 0.61f, -1500, 0.100f, 0f, 0f, 0f, -400, 0.047f, 0f, 0f, 0f, 0.250f, 0.250f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationCupboard     = new Environment ( new float[]{ 26, 1.4f, 0.560f, -1000, -300, -100, 0.79f, 0.81f, 0.55f, 300, 0.007f, 0f, 0f, 0f, 500, 0.018f, 0f, 0f, 0f, 0.181f, 0.310f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+SpacestationSmallroom    = new Environment ( new float[]{ 26, 1.5f, 0.700f, -1000, -300, -100, 1.72f, 0.82f, 0.55f, -200, 0.007f, 0f, 0f, 0f, 300, 0.013f, 0f, 0f, 0f, 0.188f, 0.260f, 0.250f, 0f, -5f, 3316.1f, 458.2f, 0f, 0x20} );
+ +
+ +

Wooden Hut or Ship

+
+
WoodenAlcove           = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -1800, -1000, 1.22f, 0.62f, 0.91f, 100, 0.012f, 0f, 0f, 0f, -300, 0.024f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenShortpassage     = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -1800, -1000, 1.75f, 0.50f, 0.87f, -100, 0.012f, 0f, 0f, 0f, -400, 0.024f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenMediumroom       = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -2000, -1100, 1.47f, 0.42f, 0.82f, -100, 0.049f, 0f, 0f, 0f, -100, 0.029f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenLongpassage      = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -2000, -1000, 1.99f, 0.40f, 0.79f, 000, 0.020f, 0f, 0f, 0f, -700, 0.036f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenLargeroom        = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -2100, -1100, 2.65f, 0.33f, 0.82f, -100, 0.066f, 0f, 0f, 0f, -200, 0.049f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenHall             = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -2200, -1100, 3.45f, 0.30f, 0.82f, -100, 0.088f, 0f, 0f, 0f, -200, 0.063f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenCupboard         = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -1700, -1000, 0.56f, 0.46f, 0.91f, 100, 0.012f, 0f, 0f, 0f, 100, 0.028f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenSmallroom        = new Environment ( new float[]{ 26, 7.5f, 1f, -1000, -1900, -1000, 0.79f, 0.32f, 0.87f, 00, 0.032f, 0f, 0f, 0f, -100, 0.029f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+WoodenCourtyard        = new Environment ( new float[]{ 26, 7.5f, 0.650f, -1000, -2200, -1000, 1.79f, 0.35f, 0.79f, -500, 0.123f, 0f, 0f, 0f, -2000, 0.032f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 4705f, 99.6f, 0f, 0x3f} );
+ +
+ +

Sport

+
+
SportEmptystadium      = new Environment ( new float[]{ 26, 7.2f, 1f, -1000, -700, -200, 6.26f, 0.51f, 1.10f, -2400, 0.183f, 0f, 0f, 0f, -800, 0.038f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x20} );
+SportSquashcourt       = new Environment ( new float[]{ 26, 7.5f, 0.750f, -1000, -1000, -200, 2.22f, 0.91f, 1.16f, -700, 0.007f, 0f, 0f, 0f, -200, 0.011f, 0f, 0f, 0f, 0.126f, 0.190f, 0.250f, 0f, -5f, 7176.9f, 211.2f, 0f, 0x20} );
+SportSmallswimmingpool = new Environment ( new float[]{ 26, 36.2f, 0.700f, -1000, -200, -100, 2.76f, 1.25f, 1.14f, -400, 0.020f, 0f, 0f, 0f, -200, 0.030f, 0f, 0f, 0f, 0.179f, 0.150f, 0.895f, 0.190f, -5f, 5000f, 250f, 0f, 0x0} );
+SportLargeswimmingpool = new Environment ( new float[]{ 26, 36.2f, 0.820f, -1000, -200, 0, 5.49f, 1.31f, 1.14f, -700, 0.039f, 0f, 0f, 0f, -600, 0.049f, 0f, 0f, 0f, 0.222f, 0.550f, 1.159f, 0.210f, -5f, 5000f, 250f, 0f, 0x0} );
+SportGymnasium         = new Environment ( new float[]{ 26, 7.5f, 0.810f, -1000, -700, -100, 3.14f, 1.06f, 1.35f, -800, 0.029f, 0f, 0f, 0f, -500, 0.045f, 0f, 0f, 0f, 0.146f, 0.140f, 0.250f, 0f, -5f, 7176.9f, 211.2f, 0f, 0x20} );
+SportFullstadium       = new Environment ( new float[]{ 26, 7.2f, 1f, -1000, -2300, -200, 5.25f, 0.17f, 0.80f, -2000, 0.188f, 0f, 0f, 0f, -1100, 0.038f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x20} );
+ +
+ +

Pipes

+
+
Sewerpipe    = new Environment ( new float[]{ 21, 1.7f, 0.800f, -1000, -1000, 0, 2.81f, 0.14f, 1f, 429, 0.014f, 0f, 0f, 0f, 1023, 0.021f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+PipeSmall    = new Environment ( new float[]{ 26, 50.3f, 1f, -1000, -900, -1300, 5.04f, 0.10f, 0.10f, -600, 0.032f, 0f, 0f, 0f, 800, 0.015f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x3f} );
+PipeLongthin = new Environment ( new float[]{ 26, 1.6f, 0.910f, -1000, -700, -1100, 9.21f, 0.18f, 0.10f, -300, 0.010f, 0f, 0f, 0f, -300, 0.022f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x0} );
+PipeLarge    = new Environment ( new float[]{ 26, 50.3f, 1f, -1000, -900, -1300, 8.45f, 0.10f, 0.10f, -800, 0.046f, 0f, 0f, 0f, 400, 0.032f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x3f} );
+PipeResonant = new Environment ( new float[]{ 26, 1.3f, 0.910f, -1000, -700, -1100, 6.81f, 0.18f, 0.10f, -300, 0.010f, 0f, 0f, 0f, 00, 0.022f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x0} );
+ +
+ +

Moods

+
+
Heaven    = new Environment ( new float[]{ 26, 19.6f, 0.940f, -1000, -200, -700, 5.04f, 1.12f, 0.56f, -1230, 0.020f, 0f, 0f, 0f, 200, 0.029f, 0f, 0f, 0f, 0.250f, 0.080f, 2.742f, 0.050f, -2f, 5000f, 250f, 0f, 0x3f} );
+Hell      = new Environment ( new float[]{ 26, 100f, 0.570f, -1000, -900, -700, 3.57f, 0.49f, 2f, -10000, 0.020f, 0f, 0f, 0f, 300, 0.030f, 0f, 0f, 0f, 0.110f, 0.040f, 2.109f, 0.520f, -5f, 5000f, 139.5f, 0f, 0x40} );
+Memory    = new Environment ( new float[]{ 26, 8f, 0.850f, -1000, -400, -900, 4.06f, 0.82f, 0.56f, -2800, 0f, 0f, 0f, 0f, 100, 0f, 0f, 0f, 0f, 0.250f, 0f, 0.474f, 0.450f, -10f, 5000f, 250f, 0f, 0x0} );
+Drugged   = new Environment ( new float[]{ 23, 1.9f, 0.500f, -1000, 0, 0, 8.39f, 1.39f, 1f, -115, 0.002f, 0f, 0f, 0f, 985, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 1f, -5f, 5000f, 250f, 0f, 0x1f} );
+Dizzy     = new Environment ( new float[]{ 24, 1.8f, 0.600f, -1000, -400, 0, 17.23f, 0.56f, 1f, -1713, 0.020f, 0f, 0f, 0f, -613, 0.030f, 0f, 0f, 0f, 0.250f, 1f, 0.810f, 0.310f, -5f, 5000f, 250f, 0f, 0x1f} );
+Psychotic = new Environment ( new float[]{ 25, 1f, 0.500f, -1000, -151, 0, 7.56f, 0.91f, 1f, -626, 0.020f, 0f, 0f, 0f, 774, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 4f, 1f, -5f, 5000f, 250f, 0f, 0x1f} );
+ +
+ +

Car Racing

+
+
DrivingCommentator    = new Environment ( new float[]{ 26, 3f, 0f, 1000, -500, -600, 2.42f, 0.88f, 0.68f, -1400, 0.093f, 0f, 0f, 0f, -1200, 0.017f, 0f, 0f, 0f, 0.250f, 1f, 0.250f, 0f, -10f, 5000f, 250f, 0f, 0x20} );
+DrivingPitgarage       = new Environment ( new float[]{ 26, 1.9f, 0.590f, -1000, -300, -500, 1.72f, 0.93f, 0.87f, -500, 0f, 0f, 0f, 0f, 200, 0.016f, 0f, 0f, 0f, 0.250f, 0.110f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x0} );
+DrivingIncarRacer      = new Environment ( new float[]{ 26, 1.1f, 0.800f, -1000, 0, -200, 0.17f, 2f, 0.41f, 500, 0.007f, 0f, 0f, 0f, -300, 0.015f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 10268.2f, 251f, 0f, 0x20} );
+DrivingIncarSports     = new Environment ( new float[]{ 26, 1.1f, 0.800f, -1000, -400, 0, 0.17f, 0.75f, 0.41f, 0, 0.010f, 0f, 0f, 0f, -500, 0f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 10268.2f, 251f, 0f, 0x20} );
+DrivingIncarLuxury     = new Environment ( new float[]{ 26, 1.6f, 1f, -1000, -2000, -600, 0.13f, 0.41f, 0.46f, -200, 0.010f, 0f, 0f, 0f, 400, 0.010f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 10268.2f, 251f, 0f, 0x20} );
+DrivingFullgrandstand  = new Environment ( new float[]{ 26, 8.3f, 1f, -1000, -1100, -400, 3.01f, 1.37f, 1.28f, -900, 0.090f, 0f, 0f, 0f, -1500, 0.049f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 10420.2f, 250f, 0f, 0x1f} );
+DrivingEmptygrandstand = new Environment ( new float[]{ 26, 8.3f, 1f, -1000, 0, -200, 4.62f, 1.75f, 1.40f, -1363, 0.090f, 0f, 0f, 0f, -1200, 0.049f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 10420.2f, 250f, 0f, 0x1f} );
+DrivingTunnel          = new Environment ( new float[]{ 26, 3.1f, 0.810f, -1000, -800, -100, 3.42f, 0.94f, 1.31f, -300, 0.051f, 0f, 0f, 0f, -300, 0.047f, 0f, 0f, 0f, 0.214f, 0.050f, 0.250f, 0f, -5f, 5000f, 155.3f, 0f, 0x20} );
+ +
+ +

City

+
+
CityIndoors   = new Environment ( new float[]{ 16, 7.5f, 0.500f, -1000, -800, 0, 1.49f, 0.67f, 1f, -2273, 0.007f, 0f, 0f, 0f, -1691, 0.011f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+CityStreets   = new Environment ( new float[]{ 26, 3f, 0.780f, -1000, -300, -100, 1.79f, 1.12f, 0.91f, -1100, 0.046f, 0f, 0f, 0f, -1400, 0.028f, 0f, 0f, 0f, 0.250f, 0.200f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x20} );
+CitySubway    = new Environment ( new float[]{ 26, 3f, 0.740f, -1000, -300, -100, 3.01f, 1.23f, 0.91f, -300, 0.046f, 0f, 0f, 0f, 200, 0.028f, 0f, 0f, 0f, 0.125f, 0.210f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x20} );
+CityMuseum    = new Environment ( new float[]{ 26, 80.3f, 0.820f, -1000, -1500, -1500, 3.28f, 1.40f, 0.57f, -1200, 0.039f, 0f, 0f, -0f, -100, 0.034f, 0f, 0f, 0f, 0.130f, 0.170f, 0.250f, 0f, -5f, 2854.4f, 107.5f, 0f, 0x0} );
+CityLibrary   = new Environment ( new float[]{ 26, 80.3f, 0.820f, -1000, -1100, -2100, 2.76f, 0.89f, 0.41f, -900, 0.029f, 0f, 0f, -0f, -100, 0.020f, 0f, 0f, 0f, 0.130f, 0.170f, 0.250f, 0f, -5f, 2854.4f, 107.5f, 0f, 0x0} );
+CityUnderpass = new Environment ( new float[]{ 26, 3f, 0.820f, -1000, -700, -100, 3.57f, 1.12f, 0.91f, -800, 0.059f, 0f, 0f, 0f, -100, 0.037f, 0f, 0f, 0f, 0.250f, 0.140f, 0.250f, 0f, -7f, 5000f, 250f, 0f, 0x20} );
+CityAbandoned = new Environment ( new float[]{ 26, 3f, 0.690f, -1000, -200, -100, 3.28f, 1.17f, 0.91f, -700, 0.044f, 0f, 0f, 0f, -1100, 0.024f, 0f, 0f, 0f, 0.250f, 0.200f, 0.250f, 0f, -3f, 5000f, 250f, 0f, 0x20} );
+ +
+ +

Small Indoor Rooms

+
+
Room         = new Environment ( new float[]{ 2, 1.9f, 1f, -1000, -454, 0, 0.40f, 0.83f, 1f, -1646, 0.002f, 0f, 0f, 0f, 53, 0.003f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Bathroom     = new Environment ( new float[]{ 3, 1.4f, 1f, -1000, -1200, 0, 1.49f, 0.54f, 1f, -370, 0.007f, 0f, 0f, 0f, 1030, 0.011f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Livingroom   = new Environment ( new float[]{ 4, 2.5f, 1f, -1000, -6000, 0, 0.50f, 0.10f, 1f, -1376, 0.003f, 0f, 0f, 0f, -1104, 0.004f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Paddedcell   = new Environment ( new float[]{ 1, 1.4f, 1f, -1000, -6000, 0, 0.17f, 0.10f, 1f, -1204, 0.001f, 0f, 0f, 0f, 207, 0.002f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Stoneroom    = new Environment ( new float[]{ 5, 11.6f, 1f, -1000, -300, 0, 2.31f, 0.64f, 1f, -711, 0.012f, 0f, 0f, 0f, 83, 0.017f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+ +
+ +

Medium-Sized Indoor Rooms

+
+
Workshop     = new Environment ( new float[]{ 26, 1.9f, 1f, -1000, -1700, -800, 0.76f, 1f, 1f, 0, 0.012f, 0f, 0f, 0f, 100, 0.012f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x0} );
+Schoolroom   = new Environment ( new float[]{ 26, 1.86f, 0.690f, -1000, -400, -600, 0.98f, 0.45f, 0.18f, 300, 0.017f, 0f, 0f, 0f, 300, 0.015f, 0f, 0f, 0f, 0.095f, 0.140f, 0.250f, 0f, -5f, 7176.9f, 211.2f, 0f, 0x20} );
+Practiseroom = new Environment ( new float[]{ 26, 1.86f, 0.870f, -1000, -800, -600, 1.12f, 0.56f, 0.18f, 200, 0.010f, 0f, 0f, 0f, 300, 0.011f, 0f, 0f, 0f, 0.095f, 0.140f, 0.250f, 0f, -5f, 7176.9f, 211.2f, 0f, 0x20} );
+Outhouse     = new Environment ( new float[]{ 26, 80.3f, 0.820f, -1000, -1900, -1600, 1.38f, 0.38f, 0.35f, -100, 0.024f, 0f, 0f, -0f, -400, 0.044f, 0f, 0f, 0f, 0.121f, 0.170f, 0.250f, 0f, -5f, 2854.4f, 107.5f, 0f, 0x0} );
+Caravan      = new Environment ( new float[]{ 26, 8.3f, 1f, -1000, -2100, -1800, 0.43f, 1.50f, 1f, 0, 0.012f, 0f, 0f, 0f, 600, 0.012f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x1f} );
+Dustyroom    = new Environment ( new float[]{ 26, 1.8f, 0.560f, -1000, -200, -300, 1.79f, 0.38f, 0.21f, -600, 0.002f, 0f, 0f, 0f, 200, 0.006f, 0f, 0f, 0f, 0.202f, 0.050f, 0.250f, 0f, -10f, 13046f, 163.3f, 0f, 0x20} );
+Chapel       = new Environment ( new float[]{ 26, 19.6f, 0.840f, -1000, -500, 0, 4.62f, 0.64f, 1.23f, -700, 0.032f, 0f, 0f, 0f, -200, 0.049f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0.110f, -5f, 5000f, 250f, 0f, 0x3f} );
+ +
+ +

Large Indoor Rooms

+
+
Auditorium     = new Environment ( new float[]{ 6, 21.6f, 1f, -1000, -476, 0, 4.32f, 0.59f, 1f, -789, 0.020f, 0f, 0f, 0f, -289, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Concerthall    = new Environment ( new float[]{ 7, 19.6f, 1f, -1000, -500, 0, 3.92f, 0.70f, 1f, -1230, 0.020f, 0f, 0f, 0f, -02, 0.029f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Cave           = new Environment ( new float[]{ 8, 14.6f, 1f, -1000, 0, 0, 2.91f, 1.30f, 1f, -602, 0.015f, 0f, 0f, 0f, -302, 0.022f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x1f} );
+Arena          = new Environment ( new float[]{ 9, 36.2f, 1f, -1000, -698, 0, 7.24f, 0.33f, 1f, -1166, 0.020f, 0f, 0f, 0f, 16, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Hangar         = new Environment ( new float[]{ 10, 50.3f, 1f, -1000, -1000, 0, 10.05f, 0.23f, 1f, -602, 0.020f, 0f, 0f, 0f, 198, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+DomeTomb       = new Environment ( new float[]{ 26, 51.8f, 0.790f, -1000, -900, -1300, 4.18f, 0.21f, 0.10f, -825, 0.030f, 0f, 0f, 0f, 450, 0.022f, 0f, 0f, 0f, 0.177f, 0.190f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x0} );
+DomeSaintPauls = new Environment ( new float[]{ 26, 50.3f, 0.870f, -1000, -900, -1300, 10.48f, 0.19f, 0.10f, -1500, 0.090f, 0f, 0f, 0f, 200, 0.042f, 0f, 0f, 0f, 0.250f, 0.120f, 0.250f, 0f, -5f, 2854.4f, 20f, 0f, 0x3f} );
+ +
+ +

Hallways, Alleys

+
+
Carpettedhallway = new Environment ( new float[]{ 11, 1.9f, 1f, -1000, -4000, 0, 0.30f, 0.10f, 1f, -1831, 0.002f, 0f, 0f, 0f, -1630, 0.030f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Hallway          = new Environment ( new float[]{ 12, 1.8f, 1f, -1000, -300, 0, 1.49f, 0.59f, 1f, -1219, 0.007f, 0f, 0f, 0f, 441, 0.011f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Stonecorridor    = new Environment ( new float[]{ 13, 13.5f, 1f, -1000, -237, 0, 2.70f, 0.79f, 1f, -1214, 0.013f, 0f, 0f, 0f, 395, 0.020f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Alley            = new Environment ( new float[]{ 14, 7.5f, 0.300f, -1000, -270, 0, 1.49f, 0.86f, 1f, -1204, 0.007f, 0f, 0f, 0f, -4, 0.011f, 0f, 0f, 0f, 0.125f, 0.950f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+ +
+ +

Outdoors

+
+
Backyard      = new Environment ( new float[]{ 26, 80.3f, 0.450f, -1000, -1200, -600, 1.12f, 0.34f, 0.46f, -700, 0.069f, 0f, 0f, -0f, -300, 0.023f, 0f, 0f, 0f, 0.218f, 0.340f, 0.250f, 0f, -5f, 4399.1f, 242.9f, 0f, 0x0} );
+Plain         = new Environment ( new float[]{ 19, 42.5f, 0.210f, -1000, -2000, 0, 1.49f, 0.50f, 1f, -2466, 0.179f, 0f, 0f, 0f, -1926, 0.100f, 0f, 0f, 0f, 0.250f, 1f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Rollingplains = new Environment ( new float[]{ 26, 80.3f, 0f, -1000, -3900, -400, 2.13f, 0.21f, 0.46f, -1500, 0.300f, 0f, 0f, -0f, -700, 0.019f, 0f, 0f, 0f, 0.250f, 1f, 0.250f, 0f, -5f, 4399.1f, 242.9f, 0f, 0x0} );
+Deepcanyon    = new Environment ( new float[]{ 26, 80.3f, 0.740f, -1000, -1500, -400, 3.89f, 0.21f, 0.46f, -1000, 0.223f, 0f, 0f, -0f, -900, 0.019f, 0f, 0f, 0f, 0.250f, 1f, 0.250f, 0f, -5f, 4399.1f, 242.9f, 0f, 0x0} );
+Creek         = new Environment ( new float[]{ 26, 80.3f, 0.350f, -1000, -1500, -600, 2.13f, 0.21f, 0.46f, -800, 0.115f, 0f, 0f, -0f, -1400, 0.031f, 0f, 0f, 0f, 0.218f, 0.340f, 0.250f, 0f, -5f, 4399.1f, 242.9f, 0f, 0x0} );
+Valley        = new Environment ( new float[]{ 26, 80.3f, 0.280f, -1000, -3100, -1600, 2.88f, 0.26f, 0.35f, -1700, 0.263f, 0f, 0f, -0f, -800, 0.100f, 0f, 0f, 0f, 0.250f, 0.340f, 0.250f, 0f, -5f, 2854.4f, 107.5f, 0f, 0x0} );
+Forest        = new Environment ( new float[]{ 15, 38f, 0.300f, -1000, -3300, 0, 1.49f, 0.54f, 1f, -2560, 0.162f, 0f, 0f, 0f, -229, 0.088f, 0f, 0f, 0f, 0.125f, 1f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Mountains     = new Environment ( new float[]{ 17, 100f, 0.270f, -1000, -2500, 0, 1.49f, 0.21f, 1f, -2780, 0.300f, 0f, 0f, 0f, -1434, 0.100f, 0f, 0f, 0f, 0.250f, 1f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x1f} );
+Quarry        = new Environment ( new float[]{ 18, 17.5f, 1f, -1000, -1000, 0, 1.49f, 0.83f, 1f, -10000, 0.061f, 0f, 0f, 0f, 500, 0.025f, 0f, 0f, 0f, 0.125f, 0.700f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x3f} );
+Parkinglot    = new Environment ( new float[]{ 20, 8.3f, 1f, -1000, 0, 0, 1.65f, 1.50f, 1f, -1363, 0.008f, 0f, 0f, 0f, -1153, 0.012f, 0f, 0f, 0f, 0.250f, 0f, 0.250f, 0f, -5f, 5000f, 250f, 0f, 0x1f} );
+ +
+ +

Water

+
+
Underwater     = new Environment ( new float[]{ 22, 1.8f, 1f, -1000, -4000, 0, 1.49f, 0.10f, 1f, -449, 0.007f, 0f, 0f, 0f, 1700, 0.011f, 0f, 0f, 0f, 0.250f, 0f, 1.180f, 0.348f, -5f, 5000f, 250f, 0f, 0x3f} );
+Smallwaterroom = new Environment ( new float[]{ 26, 36.2f, 0.700f, -1000, -698, 0, 1.51f, 1.25f, 1.14f, -100, 0.020f, 0f, 0f, 0f, 300, 0.030f, 0f, 0f, 0f, 0.179f, 0.150f, 0.895f, 0.190f, -7f, 5000f, 250f, 0f, 0x0} );
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/blomsky.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/blomsky.png new file mode 100644 index 000000000..cb5ba7a9f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/blomsky.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bloom_and_glow.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bloom_and_glow.html new file mode 100644 index 000000000..980ff0c08 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bloom_and_glow.html @@ -0,0 +1,290 @@ + +

Bloom and Glow

+
+ +

+ +Bloom is a popular shader effect in 3D games industry. It usually consist in displaying a glowing halo around light sources or bright areas of a scene. +In practice, the bright areas are extracted from the rendered scene, blurred and finally added up to the render. +

+ +

+Those images gives an idea of what bloom does. The left image has no bloom effect, the right image does.
+ + +

+ +
+ +

Bloom Usage

+
+
    +
  1. Create a FilterPostProcessor
    +
  2. +
  3. Create a BloomFilter
    +
  4. +
  5. Add the filter to the processor
    +
  6. +
  7. Add the processor to the viewPort
    +
  8. +
+
 FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
+ BloomFilter bloom=new BloomFilter();
+ fpp.addFilter(bloom);
+ viewPort.addProcessor(fpp);
+ +

+Here are the parameters that you can tweak : +

+
+ + + + + + + + + + + + + + + +
Parameter Method Default Description
blur scale setBlurScale(float) 1.5f the scale of the bloom effect, but be careful, high values does artifacts
exposure Power setExposurePower(float) 5.0f the glowing channel color is raised to the value power
exposure cut-off setExposureCutOff(float) 0.0f the threshold of color to bloom during extraction
bloom intensity setBloomIntensity(float) 2.0f the resulting bloom value is multiplied by this intensity
+ +

+ +You'll probably need to adjust those parameters depending on your scene. +

+ +
+ +

Bloom with a glow map

+
+ +

+ +Sometimes, you want to have more control over what glows and does not glow. +The bloom filter supports a glow map or a glow color. +

+ +
+ +
Creating a glow-map
+
+ +

+ +Let's take the hover tank example bundled with JME3 test data.
+ +Here you can see the diffuse map of the tank, and the associated glow map that only contains the parts of the texture that will glow and their glowing color:
+ + + +

+ +

+Glow maps work with Lighting.j3md, Particles.j3md and SolidColor.j3md material definitions. +The tank material looks like this : + +

+
Material My Material : Common/MatDefs/Light/Lighting.j3md {
+     MaterialParameters {
+        SpecularMap : Models/HoverTank/tank_specular.png
+        Shininess : 8
+        NormalMap : Models/HoverTank/tank_normals.png
+        DiffuseMap : Models/HoverTank/tank_diffuse.png
+        GlowMap : Models/HoverTank/tank_glow_map_highres.png
+        UseMaterialColors : true
+        Ambient  : 0.0 0.0 0.0 1.0
+        Diffuse  : 1.0 1.0 1.0 1.0
+        Specular : 1.0 1.0 1.0 1.0
+     }
+}
+ +

+ +The glow map is defined here : GlowMap : Models/HoverTank/tank_glow_map_highres.png +

+ +
+ +
Usage
+
+
    +
  1. Create a FilterPostProcessor
    +
  2. +
  3. Create a BloomFilter with the GlowMode.Objects parameter
    +
  4. +
  5. Add the filter to the processor
    +
  6. +
  7. Add the processor to the viewPort
    +
  8. +
+
  FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
+  BloomFilter bf=new BloomFilter(BloomFilter.GlowMode.Objects);
+  fpp.addFilter(bf);
+  viewPort.addProcessor(fpp);
+ +

+Here is the result :
+ + +

+ +
+ +

Bloom with a glow color

+
+ +

+ +Sometimes you need an entire object to glow, not just parts of it. +In this case you'll need to use the glow color parameter. +

+ +
+ +
Usage
+
+
    +
  1. Create a material for your object and set the GlowColor parameter
    +
  2. +
  3. Create a FilterPostProcessor
    +
  4. +
  5. Create a BloomFilter with the GlowMode.Objects parameter
    +
  6. +
  7. Add the filter to the processor
    +
  8. +
  9. Add the processor to the viewPort
    +
  10. +
+
    Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/SolidColor.j3md");
+    mat.setColor("Color", ColorRGBA.Green);
+    mat.setColor("GlowColor", ColorRGBA.Green);
+    fpp=new FilterPostProcessor(assetManager);
+    bloom= new BloomFilter(BloomFilter.GlowMode.Objects);        
+    fpp.addFilter(bloom);
+    viewPort.addProcessor(fpp);
+ +

+Here is the result on Oto's plasma ball (before and after) :
+ + + +

+ +
+ +

Hints and tricks

+
+ +
+ +
Increasing the blur range and reducing fps cost
+
+ +

+ +The glow render is sampled on a texture that has the same dimensions as the viewport. +You can reduce the size of the bloom sampling just by using the setDownSamplingFactor method like this :
+ + +

+
 BloomFilter bloom=new BloomFilter();
+ bloom.setDownSamplingFactor(2.0f); 
+ +

+ +In this example the sampling size is divided by 4 (width/2,height/2), resulting in less work to blur the scene. +The resulting texture is then up sampled to the screen size using hardware bilinear filtering. this results in a wider blur range. +

+ +
+ +
Using classic bloom combined with a glow map
+
+ +

+let's say you want a global bloom on your scene, but you have also a glowing object on it. +You can use only one bloom filter for both effects like that +

+
BloomFilter bloom=new BloomFilter(BloomFilter.GlowMode.SceneAndObjects);
+ +

+However, note that both effects will share the same values of attribute, and sometimes, it won't be what you need. +

+ +
+ +
Making your home brewed material definition support Glow
+
+ +

+ +Let's say you have made a custom material on your own, and that you want it to support glow maps and glow color. +In your material definition you need to add those lines in the MaterialParameters section : +

+
 MaterialParameters {
+        
+        ....
+
+        // Texture of the glowing parts of the material
+        Texture2D GlowMap
+        // The glow color of the object
+        Color GlowColor
+    }
+ +

+Then add the following technique : + +

+
    Technique Glow {
+
+        LightMode SinglePass
+
+        VertexShader GLSL100:   Common/MatDefs/Misc/SimpleTextured.vert
+        FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+        }
+
+        Defines {
+            HAS_GLOWMAP : GlowMap
+            HAS_GLOWCOLOR : GlowColor
+        }
+    }
+ +

+Then you can use this material with the BloomFilter +

+ +
+ +
Make a glowing object stop to glow
+
+ +

+ +If you are using a glow map, remove the texture from the material. + +

+
material.clearTextureParam("GlowMap");
+ +

+If you are using a glow color, set it to black + +

+
material.setColor("GlowColor",ColorRGBA.Black);
+
+ documentation, + effect, + light +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/border1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/border1.png new file mode 100644 index 000000000..c9ad886e3 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/border1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bullet_multithreading.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bullet_multithreading.html new file mode 100644 index 000000000..e97525654 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bullet_multithreading.html @@ -0,0 +1,65 @@ + +

Multithreading Bullet Physics in jme3

+
+ +
+ +

Introduction

+
+ +

+ +Since bullet is not (yet) multithreaded or GPU accelerated, the jME3 implementation allows to run each physics space on a separate thread that is executed in parallel to rendering. +

+ +
+ +

How is it handled in jme3 and bullet?

+
+ +

+ +A SimpleApplication with a BulletAppState allows setting the threading type via +

+
setThreadingType(ThreadingType type);
+ +

+ where ThreadingType can be either SEQUENTIAL or PARALLEL. By default, it's SEQUENTIAL. +

+ +

+You can activate PARALLEL threading in the simpleInitApp() method: +

+
bulletAppState = new BulletAppState();
+bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+stateManager.attach(bulletAppState);
+ +

+Now the physics update happens in parallel to render(), that is, after the user's changes in the update() call have been applied. During update() the physics update loop pauses. This way the loop logic is still maintained: the user can set and change values in physics and scenegraph objects before render() and physicsUpdate() are called in parallel. This allows you to use physics methods in update() as if it was single-threaded. +

+
+ + + + + + + + + +
PARALLELSEQUENTIAL
1. update(), 2. render() and physics update().1. update(), 2. render(), 3. physics update().
Physics Debug View is rendered inaccurately (out of sync)Physics Debug View is rendered accurately.
+ +

+ +

You can add more physics spaces by using multiple PARALLEL bulletAppStates. You would do that if you have sets physical objects that never collide (for example, underground bolders and flying cannon balls above ground), so you put those into separate physics spaces, which improves performances (less collisions to check!). +

+

+
+ documentation, + physics, + threading +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bump-mapped-sphere.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bump-mapped-sphere.png new file mode 100644 index 000000000..22629d27e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/bump-mapped-sphere.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/butterfly-particle-emitter.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/butterfly-particle-emitter.png new file mode 100644 index 000000000..0a62a7b31 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/butterfly-particle-emitter.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/camera.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/camera.html new file mode 100644 index 000000000..3a5c04a2c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/camera.html @@ -0,0 +1,174 @@ + +

The jME3 Camera

+
+ +
+ +

Default Camera

+
+ +

+ +The default com.jme3.renderer.Camera object is cam in com.jme3.app.Application. +

+ +

+The camera object is created with the following defaults: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodUsage
cam.getLocation(), setLocation()The camera position
cam.getRotation(), setRotation()The camera rotation
cam.getLeft(), setLeft()The left axis of the camera
cam.getUp(), setUp()The up axis of the camera, usually Vector3f(0,1,0)
cam.getDirection(), setDirection()The vector the camera is facing
cam.getAxes(), setAxes(left,up,dir)One accessor for the three properties left/up/direction.
cam.getFrame(), setFrame(loc,left,up,dir)One accessor for the four properties location/left/up/direction.
cam.resize(width, height, fixAspect)Resize an existing camera object while keeping all other settings. Set fixAspect to true to adjust the aspect ratio (?)
cam.setFrustum( near, far, left, right, top, bottom )The frustum is defined by the near/far plane, left/right plane, top/bottom plane (all distances as float values)
cam.setFrustumPerspective( fovY, aspect ratio, near, far)The frustum is defined by view angle along the Y axis (in degrees), aspect ratio, and the near/far plane.
cam.lookAt(target,up)Turn the camera to look at Coordinate target, and rotate it around the up axis.
cam.setParallelProjection(false)Normal perspective
cam.setParallelProjection(true)Parallel projection perspective
cam.getScreenCoordinates()?
+ +

+Tip: After you change view port, frustum, or frame, call cam.update(); +

+ +
+ +

FlyBy Camera

+
+ +

+ +The flyby camera is an AppState that extends the default camera in com.jme3.app.SimpleApplication. It is preconfigured to respond to the WASD keys for walking forwards and backwards, and strafing to the sides; move the mouse to rotate the camera ("Mouse Look"), scroll the mouse wheel for zooming in or out. The QZ keys raise or lower the camera vertically. + +

+
+ + + + + + + + + + + + + + + +
MethodUsage
flyCam.setEnabled(true);Activate the flyby cam
flyCam.setMoveSpeed(10);Control the move speed
flyCam.setRotationSpeed(10);Control the rotation speed
flyCam.setDragToRotate(true)Forces the player to keep mouse button pressed to rotate camera, typically used for Applets. If false (default), all mouse movement will be captured and interpreted as rotations.
+ +
+ +

Chase Camera

+
+ +

+ +jME3 also supports a Chase Cam that can follow a moving target Spatial (com.jme3.input.ChaseCamera). Click and hold the mouse button to rotate around the target. +

+
flyCam.setEnabled(false);
+ChaseCamera chaseCam = new ChaseCamera(cam, target, inputManager);
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodUsage
chaseCam.setSmoothMotion(true);Interpolates a smoother acceleration/deceleration when the camera moves.
chaseCam.setChasingSensitivity(5f)The lower the chasing sensitivity, the slower the camera will follow the target when it moves.
chaseCam.setTrailingSensitivity(0.5f)The lower the traling sensitivity, the slower the camera will begin to go after the target when it moves. Default is 0.5;
chaseCam.setRotationSensitivity(5f)The lower the sensitivity, the slower the camera will rotate around the target when the mosue is dragged. Default is 5.
chaseCam.setTrailingRotationInertia(0.1f)This prevents the camera to stop too abruptly when the target stops rotating before the camera has reached the target's trailing position. Default is 0.1f.
chaseCam.setDefaultDistance(40);The default distance to the target at the start of the application.
chaseCam.setMaxDistance(40);The maximum zoom distance. Default is 40f.
chaseCam.setMinDistance(1);The minimum zoom distance. Default is 1f.
chaseCam.setMinVerticalRotation(-FastMath.PI/2);The minimal vertical rotation angle of the camera around the target. Default is 0.
chaseCam.setDefaultVerticalRotation(-FastMath.PI/2);The default vertical rotation angle of the camera around the target at the start of the application.
chaseCam.setDefaultHorizontalRotation(-FastMath.PI/2);The default horizontal rotation angle of the camera around the target at the start of the application.
+
+ camera, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html new file mode 100644 index 000000000..e982c4f86 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html @@ -0,0 +1,653 @@ + +

Capture Audio/Video to a File

+
+ +

+ +So you've made your cool new JMonkeyEngine3 game and you want to +create a demo video to show off your hard work. Or maybe you want to +make a cutscene for your game using the physics and characters in the +game itself. Screen capturing is the most straightforward way to do +this, but it can slow down your game and produce low-quality video and +audio as a result. A better way is to record video and audio directly +from the game while it is running. +

+ +

+

Combine this method with jMonkeyEngine's +Cinematics +feature to record high-quality game trailers! +

+

+ +
+ +

Simple Way

+
+ +

+ +First off, if all you need is to record video at 30fps with no sound, then look +no further than jMonkeyEngine 3's built in VideoRecorderAppState +class. +

+ +

+Add the following code to your simpleInitApp() method. +

+
stateManager.attach(new VideoRecorderAppState()); //start recording
+ +

+The game will run slow, but the recording will be in high-quality and +normal speed. The video files will be stored in your user home +directory, if you want to save to another file, specify it in the +VideoRecorderAppState constructor. Recording starts when the state is +attached and ends when the application quits or the state is detached. +

+ +

+That's all! +

+ +
+ +

Advanced Way

+
+ +

+ +

This way of A/V recording is still in development. +It works for all of jMonkeyEngine's test cases. +If you experience any problems or +if something isn't clear, please . ??? bortreb +

+

+ +

+If you want to record audio as well, record at different framerates, +or record from multiple viewpoints at once, then there's a full +solution for doing this already made for you here: +

+ +

+ +

+ +

+ +

+ +

+Download the archive in your preferred format, extract, +add the jars to your project, and you are ready to go. +

+ +

+The javadoc is here: + +

+ +

+To capture video and audio you use the +com.aurellem.capture.Capture class, which has two methods, +captureAudio() and captureVideo(), and the +com.aurellem.capture.IsoTimer class, which sets the audio and +video framerate. +

+ +

+The steps are: +

+
yourApp.setTimer(new IsoTimer(desiredFramesPerSecond));
+ +

+This causes jMonkeyEngine to take as much time as it needs to fully +calculate every frame of the video and audio. You will see your game +speed up and slow down depending on how computationally demanding your +game is, but the final recorded audio and video will be perfectly +sychronized and will run at exactly the fps which you specified. +

+
captureVideo(yourApp, targetVideoFile);
+captureAudio(yourApp, targetAudioFile);
+ +

+These will cause the app to record audio and video when it is run. +Audio and video will stop being recorded when the app stops. Your +audio will be recorded as a 44,100 Hz linear PCM wav file, while the +video will be recorded according to the following rules: +

+ +

+1.) (Preferred) If you supply an empty directory as the file, then +the video will be saved as a sequence of .png files, one file per +frame. The files start at 0000000.png and increment from there. +You can then combine the frames into your preferred +container/codec. If the directory is not empty, then writing +video frames to it will fail, and nothing will be written. +

+ +

+2.) If the filename ends in ".avi" then the frames will be encoded as +a RAW stream inside an AVI 1.0 container. The resulting file +will be quite large and you will probably want to re-encode it to +your preferred container/codec format. Be advised that some +video payers cannot process AVI with a RAW stream, and that AVI +1.0 files generated by this method that exceed 2.0GB are invalid +according to the AVI 1.0 spec (but many programs can still deal +with them.) Thanks to + +for his excellent work which made the AVI file writer option possible. +

+ +

+3.) Any non-directory file ending in anything other than ".avi" will +be processed through Xuggle. Xuggle provides the option to use +many codecs/containers, but you will have to install it on your +system yourself in order to use this option. Please visit + to learn how to do this. +

+ +

+Note that you will not hear any sound if you choose to record sound to +a file. +

+ +
+ +

Basic Example

+
+ +

+ +Here is a complete example showing how to capture both audio and video +from one of jMonkeyEngine3's advanced demo applications. +

+
import java.io.File;
+import java.io.IOException;
+ 
+import jme3test.water.TestPostWater;
+ 
+import com.aurellem.capture.Capture;
+import com.aurellem.capture.IsoTimer;
+import com.jme3.app.SimpleApplication;
+ 
+ 
+/**
+ * Demonstrates how to use basic Audio/Video capture with a
+ * jMonkeyEngine application. You can use these techniques to make
+ * high quality cutscenes or demo videos, even on very slow laptops.
+ * 
+ * @author Robert McIntyre
+ */
+ 
+public class Basic {
+ 
+    public static void main(String[] ignore) throws IOException{
+	File video = File.createTempFile("JME-water-video", ".avi");
+	File audio = File.createTempFile("JME-water-audio", ".wav");
+ 
+	SimpleApplication app = new TestPostWater();
+	app.setTimer(new IsoTimer(60));
+	app.setShowSettings(false);
+ 
+	Capture.captureVideo(app, video);
+	Capture.captureAudio(app, audio);
+ 
+	app.start();
+ 
+	System.out.println(video.getCanonicalPath());
+	System.out.println(audio.getCanonicalPath());
+    }
+}
+ +
+ +

How it works

+
+ +

+ +A standard JME3 application that extends SimpleApplication or +Application tries as hard as it can to keep in sync with +user-time. If a ball is rolling at 1 game-mile per game-hour in the +game, and you wait for one user-hour as measured by the clock on your +wall, then the ball should have traveled exactly one game-mile. In +order to keep sync with the real world, the game throttles its physics +engine and graphics display. If the computations involved in running +the game are too intense, then the game will first skip frames, then +sacrifice physics accuracy. If there are particuraly demanding +computations, then you may only get 1 fps, and the ball may tunnel +through the floor or obstacles due to inaccurate physics simulation, +but after the end of one user-hour, that ball will have traveled one +game-mile. +

+ +

+When we're recording video, we don't care if the game-time syncs with +user-time, but instead whether the time in the recorded video +(video-time) syncs with user-time. To continue the analogy, if we +recorded the ball rolling at 1 game-mile per game-hour and watched the +video later, we would want to see 30 fps video of the ball rolling at +1 video-mile per user-hour. It doesn't matter how much user-time it +took to simulate that hour of game-time to make the high-quality +recording. +

+ +

+The IsoTimer ignores real-time and always reports that the same amount +of time has passed every time it is called. That way, one can put code +to write each video/audio frame to a file without worrying about that +code itself slowing down the game to the point where the recording +would be useless. +

+ +
+ +

Advanced Example

+
+ +

+ +The package from aurellem.com was made for AI research and can do more +than just record a single stream of audio and video. You can use it +to: +

+ +

+1.) Create multiple independent listeners that each hear the world +from their own perspective. +

+ +

+2.) Process the sound data in any way you wish. +

+ +

+3.) Do the same for visual data. +

+ +

+Here is a more advanced example, which can also be found along with +other examples in the jmeCapture.jar file included in the +distribution. +

+
package com.aurellem.capture.examples;
+ 
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+ 
+import javax.sound.sampled.AudioFormat;
+ 
+import org.tritonus.share.sampled.FloatSampleTools;
+ 
+import com.aurellem.capture.AurellemSystemDelegate;
+import com.aurellem.capture.Capture;
+import com.aurellem.capture.IsoTimer;
+import com.aurellem.capture.audio.CompositeSoundProcessor;
+import com.aurellem.capture.audio.MultiListener;
+import com.aurellem.capture.audio.SoundProcessor;
+import com.aurellem.capture.audio.WaveFileWriter;
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.Listener;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.events.AbstractCinematicEvent;
+import com.jme3.cinematic.events.MotionTrack;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+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.system.AppSettings;
+import com.jme3.system.JmeSystem;
+ 
+/**
+ * 
+ * Demonstrates advanced use of the audio capture and recording
+ * features.  Multiple perspectives of the same scene are
+ * simultaneously rendered to different sound files.
+ * 
+ * A key limitation of the way multiple listeners are implemented is
+ * that only 3D positioning effects are realized for listeners other
+ * than the main LWJGL listener.  This means that audio effects such
+ * as environment settings will *not* be heard on any auxiliary
+ * listeners, though sound attenuation will work correctly.
+ * 
+ * Multiple listeners as realized here might be used to make AI
+ * entities that can each hear the world from their own perspective.
+ * 
+ * @author Robert McIntyre
+ */
+ 
+public class Advanced extends SimpleApplication {
+ 
+	/**
+	 * You will see three grey cubes, a blue sphere, and a path which
+	 * circles each cube.  The blue sphere is generating a constant
+	 * monotone sound as it moves along the track.  Each cube is
+	 * listening for sound; when a cube hears sound whose intensity is
+	 * greater than a certain threshold, it changes its color from
+	 * grey to green.
+	 * 
+	 *  Each cube is also saving whatever it hears to a file.  The
+	 *  scene from the perspective of the viewer is also saved to a
+	 *  video file.  When you listen to each of the sound files
+	 *  alongside the video, the sound will get louder when the sphere
+	 *  approaches the cube that generated that sound file.  This
+	 *  shows that each listener is hearing the world from its own
+	 *  perspective.
+	 * 
+	 */
+	public static void main(String[] args) {
+		Advanced app = new Advanced();
+		AppSettings settings = new AppSettings(true);
+		settings.setAudioRenderer(AurellemSystemDelegate.SEND);
+		JmeSystem.setSystemDelegate(new AurellemSystemDelegate());
+		app.setSettings(settings);
+		app.setShowSettings(false);
+		app.setPauseOnLostFocus(false);
+ 
+ 
+		try {
+			Capture.captureVideo(app, File.createTempFile("advanced",".avi"));
+			Capture.captureAudio(app, File.createTempFile("advanced", ".wav"));
+		}
+		catch (IOException e) {e.printStackTrace();}
+ 
+		app.start();
+	}
+ 
+ 
+	private Geometry bell;
+	private Geometry ear1;
+	private Geometry ear2;
+	private Geometry ear3;
+	private AudioNode music;
+	private MotionTrack motionControl;
+	private IsoTimer motionTimer = new IsoTimer(60);
+ 
+	private Geometry makeEar(Node root, Vector3f position){
+		Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+		Geometry ear = new Geometry("ear", new Box(1.0f, 1.0f, 1.0f));
+		ear.setLocalTranslation(position);
+		mat.setColor("Color", ColorRGBA.Green);
+		ear.setMaterial(mat);
+		root.attachChild(ear);
+		return ear;
+	} 
+ 
+	private Vector3f[] path = new Vector3f[]{
+			// loop 1
+			new Vector3f(0, 0, 0),
+			new Vector3f(0, 0, -10),
+			new Vector3f(-2, 0, -14),
+			new Vector3f(-6, 0, -20),
+			new Vector3f(0, 0, -26),
+			new Vector3f(6, 0, -20),
+			new Vector3f(0, 0, -14),
+			new Vector3f(-6, 0, -20),
+			new Vector3f(0, 0, -26),
+			new Vector3f(6, 0, -20),
+			// loop 2
+			new Vector3f(5, 0, -5),
+			new Vector3f(7, 0, 1.5f),
+			new Vector3f(14, 0, 2),
+			new Vector3f(20, 0, 6),
+			new Vector3f(26, 0, 0),
+			new Vector3f(20, 0, -6),
+			new Vector3f(14, 0, 0),
+			new Vector3f(20, 0, 6),
+			new Vector3f(26, 0, 0),
+			new Vector3f(20, 0, -6),
+			new Vector3f(14, 0, 0),
+			// loop 3
+			new Vector3f(8, 0, 7.5f),
+			new Vector3f(7, 0, 10.5f),
+			new Vector3f(6, 0, 20),
+			new Vector3f(0, 0, 26),
+			new Vector3f(-6, 0, 20),
+			new Vector3f(0, 0, 14),
+			new Vector3f(6, 0, 20),
+			new Vector3f(0, 0, 26),
+			new Vector3f(-6, 0, 20),
+			new Vector3f(0, 0, 14),
+			// begin ellipse
+			new Vector3f(16, 5, 20),
+			new Vector3f(0, 0, 26),
+			new Vector3f(-16, -10, 20),
+			new Vector3f(0, 0, 14),
+			new Vector3f(16, 20, 20),
+			new Vector3f(0, 0, 26),
+			new Vector3f(-10, -25, 10),
+			new Vector3f(-10, 0, 0),
+			// come at me!
+			new Vector3f(-28.00242f, 48.005623f, -34.648228f),
+			new Vector3f(0, 0 , -20),
+	};
+ 
+	private void createScene() {
+		Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+		bell = new Geometry( "sound-emitter" , new Sphere(15,15,1));
+		mat.setColor("Color", ColorRGBA.Blue);
+		bell.setMaterial(mat);
+		rootNode.attachChild(bell);
+ 
+		ear1 = makeEar(rootNode, new Vector3f(0, 0 ,-20));
+		ear2 = makeEar(rootNode, new Vector3f(0, 0 ,20));
+		ear3 = makeEar(rootNode, new Vector3f(20, 0 ,0));
+ 
+		MotionPath track = new MotionPath();
+ 
+		for (Vector3f v : path){
+			track.addWayPoint(v);
+		}
+		track.setCurveTension(0.80f);
+ 
+		motionControl = new MotionTrack(bell,track);
+		// for now, use reflection to change the timer... 
+		// motionControl.setTimer(new IsoTimer(60));
+ 
+		try {
+			Field timerField;
+			timerField = AbstractCinematicEvent.class.getDeclaredField("timer");
+			timerField.setAccessible(true);
+			try {timerField.set(motionControl, motionTimer);} 
+			catch (IllegalArgumentException e) {e.printStackTrace();} 
+			catch (IllegalAccessException e) {e.printStackTrace();}
+		} 
+		catch (SecurityException e) {e.printStackTrace();} 
+		catch (NoSuchFieldException e) {e.printStackTrace();}
+ 
+ 
+		motionControl.setDirectionType(MotionTrack.Direction.PathAndRotation);
+		motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));
+		motionControl.setInitialDuration(20f);
+		motionControl.setSpeed(1f);
+ 
+		track.enableDebugShape(assetManager, rootNode);
+		positionCamera();
+	}
+ 
+ 
+	private void positionCamera(){
+		this.cam.setLocation(new Vector3f(-28.00242f, 48.005623f, -34.648228f));
+		this.cam.setRotation(new Quaternion(0.3359635f, 0.34280345f, -0.13281013f, 0.8671653f));
+	}
+ 
+	private void initAudio() {
+		org.lwjgl.input.Mouse.setGrabbed(false);	
+		music = new AudioNode(assetManager, "Sound/Effects/Beep.ogg", false);
+ 
+		rootNode.attachChild(music);
+		audioRenderer.playSource(music);
+		music.setPositional(true);
+		music.setVolume(1f);
+		music.setReverbEnabled(false);
+		music.setDirectional(false);
+		music.setMaxDistance(200.0f);
+		music.setRefDistance(1f);
+		//music.setRolloffFactor(1f);
+		music.setLooping(false);
+		audioRenderer.pauseSource(music); 
+	}
+ 
+	public class Dancer implements SoundProcessor {
+		Geometry entity;
+		float scale = 2;
+		public Dancer(Geometry entity){
+			this.entity = entity;
+		}
+ 
+		/**
+		 * this method is irrelevant since there is no state to cleanup.
+		 */
+		public void cleanup() {}
+ 
+ 
+		/**
+		 * Respond to sound!  This is the brain of an AI entity that 
+		 * hears its surroundings and reacts to them.
+		 */
+		public void process(ByteBuffer audioSamples, int numSamples, AudioFormat format) {
+			audioSamples.clear();
+			byte[] data = new byte[numSamples];
+			float[] out = new float[numSamples];
+			audioSamples.get(data);
+			FloatSampleTools.byte2floatInterleaved(data, 0, out, 0, 
+					numSamples/format.getFrameSize(), format);
+ 
+			float max = Float.NEGATIVE_INFINITY;
+			for (float f : out){if (f > max) max = f;}
+			audioSamples.clear();
+ 
+			if (max > 0.1){entity.getMaterial().setColor("Color", ColorRGBA.Green);}
+			else {entity.getMaterial().setColor("Color", ColorRGBA.Gray);}
+		}
+	}
+ 
+	private void prepareEar(Geometry ear, int n){
+		if (this.audioRenderer instanceof MultiListener){
+			MultiListener rf = (MultiListener)this.audioRenderer;
+ 
+			Listener auxListener = new Listener();
+			auxListener.setLocation(ear.getLocalTranslation());
+ 
+			rf.addListener(auxListener);
+			WaveFileWriter aux = null;
+ 
+			try {aux = new WaveFileWriter(File.createTempFile("advanced-audio-" + n, ".wav"));} 
+			catch (IOException e) {e.printStackTrace();}
+ 
+			rf.registerSoundProcessor(auxListener, 
+					new CompositeSoundProcessor(new Dancer(ear), aux));
+ 
+		}   
+	}
+ 
+ 
+	public void simpleInitApp() {
+		this.setTimer(new IsoTimer(60));
+		initAudio();
+ 
+		createScene();
+ 
+		prepareEar(ear1, 1);
+		prepareEar(ear2, 1);
+		prepareEar(ear3, 1);
+ 
+		motionControl.play();
+ 
+	}
+ 
+	public void simpleUpdate(float tpf) {
+		motionTimer.update();
+		if (music.getStatus() != AudioNode.Status.Playing){
+			music.play();
+		}
+		Vector3f loc = cam.getLocation();
+		Quaternion rot = cam.getRotation();
+		listener.setLocation(loc);
+		listener.setRotation(rot);
+		music.setLocalTranslation(bell.getLocalTranslation());
+	}
+ 
+}
+ +

+ + + + + + +The is needed to display this content. + + + +

+ +
+ +

Using Advanced features to Record from more than one perspective at once

+
+ +

+ + + + + + +The is needed to display this content. + + + +

+ +
+ +

More Information

+
+ +

+ +This is the old page showing the first version of this idea + +

+ +

+All source code can be found here: +

+ +

+ +

+ +

+ +

+ +

+More information on the modifications to OpenAL to support multiple +listeners can be found here. +

+ +

+ +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html new file mode 100644 index 000000000..56b7cc44d --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html @@ -0,0 +1,439 @@ + +

JME3 Cinematics

+
+ +

+ +JME3 cinematics (com.jme.cinematic) allow you to remote control nodes and cameras in a 3D game: You can script and and play cinematic scenes. Combined with screen recording software, you use cinematics to create and movies/trailers of your game. Internally, Cinematics are implemented as AppStates. +

+ +

+Short overview of the cinematic process: +

+
    +
  1. Plan the script of your movie.
    +Write down a timeline (e.g. on paper) of which character should be at which spot at which time.
    +
  2. +
  3. Attach the scene objects that you want to remote-control to one Node.
    +This Node can be the rootNode, or a Node that is attached to the rootNode.
    +
  4. +
  5. Create a Cinematic object for this movie scene. The Cinematic will contain and manage the movie script.
    +
  6. +
  7. For each line in your script (for each frame in your timeline), add a CinematicEvent to the Cinematic.
    +
  8. +
+ +
+ +

Sample Code

+
+ + +
+ +

How to Use a Cinematic

+
+ +

+ +A Cinematic is like a movie script for a node. +

+
Cinematic cinematic = new Cinematic(sceneNode, duration);
+cinematic.addCinematicEvent(starttime1, track1);
+cinematic.addCinematicEvent(starttime2, track2);
+cinematic.addCinematicEvent(starttime2, track3);
+...
+stateManager.attach(cinematic);
+
    +
  1. Create one Cinematic per scripted scene.
    +
      +
    • sceneNode is the node containing the scene (can be the rootNode).
      +
    • +
    • duration is the duration of the whole scene in seconds.
      +
    • +
    • Each Cinematic is a set of CinematicEvents, that are triggered at a given moment on the timeline.
      +
    • +
    +
  2. +
  3. Create one CinematicEvent for each line of your movie script.
    +
      +
    • track is one motion of a moving object. You can add several tracks. More details below.
      +
    • +
    • starttime is the time when this particular cinematic event starts on the timeline. Specify the start time in seconds since the beginning of the cinematic.
      +
    • +
    +
  4. +
  5. Attach the Cinematic to the SimpleApplication's stateManager.
    +
  6. +
  7. Play, stop and pause the Cinematic from your code.
    +
  8. +
+
+ + + + + + + + + + + + +
MethodUsage
cinematic.play()Starts playing the cinematic from the start, or from where it was paused.
cinematic.stop()Stops playing the cinematic and rewinds it.
cinematic.pause()Pauses the cinematic.
+ +
+ +

Tracks (CinematicEvents)

+
+ +

+ +Just like a movie script consists of lines with instructions to the actors, each Cinematic consists of a series of tracks. +

+ +

+Here is the list of available CinematicEvents that you use as tracks. Each track remote-controls scene objects in a different way: + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Tracks (CinematicEvents)Description
MotionTrackUse a MotionTrack to move a Spatial non-linearly over time. A MotionTrack is based on a list of waypoints in a MotionPath. The curve goes through each waypoint, and you can adjust the tension of the curve to modify the roundedness of the path. This is the motion interpolation you are going to use in most cases.
PositionTrackUse a PositionTrack to move a Spatial linearly over time. This linear interpolation results in straight motion segments between the way points. Use this to make the remote-controlled objects zig-zag from one way point to the other in a straight line.
RotationTrackUse a RotationTrack to change the rotation of a Spatial over time. It spins the Spatial to the given angle in the given amount of time by linearly interpolating the rotation.
ScaleTrackUse a ScaleTrack to change the size of a Spatial over time. It resizes the Spatial in the given amount of time by linearly interpolating the scale.
SoundTrackUse a SoundTrack to play a sound at a given time for the given duration.
GuiTrackDisplays a Nifty GUI at a given time for the given duration. Use it to display subtitles or HUD elements. Bind the Nifty GUI XML to the cinematic using cinematic.bindUi("path/to/nifty/file.xml");
AnimationTrackUse this to start playing a model animation at a given time (a character walking animation for example)
+ +

+ +The jMonkey team can add more types of tracks, just ask in the forum. +

+ +
+ +

MotionTrack

+
+ +

+ +A MotionTrack moves a Spatial along a complex path. + +

+
MotionTrack track = new MotionTrack(thingNode, path);
+ +

+Details of the constructor: +

+ + +

+ +To create a MotionTrack, do the following: +

+
    +
  1. Create a MotionPath
    +
  2. +
  3. Create a MotionTrack based on the MotionPath.
    +
  4. +
  5. Configure your MotionTrack (see below).
    +
  6. +
  7. Add the MotionTrack to a Cinematic.
    +
  8. +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
MotionTrack configuration methodUsage
track.setLoopMode(LoopMode.Loop)Sets whether the animation along this path should loop (LoopMode.Loop) or play only once (LoopMode.DontLoop).
track.setDirectionType(MotionTrack.Direction.None)Sets the direction behavior type of the controled node. Direction.None deactivates this feature. You can choose from the following options: LookAt, Path, PathAndRotation, Rotation.
track.setDirectionType(MotionTrack.Direction.LookAt)The spatial turns (rotates) to keep facing a certain point while moving. Specify the point with the setLookAt() method.
track.setDirectionType(MotionTrack.Direction.Path)The spatial always faces in the direction of the path while moving.
track.setDirectionType(MotionTrack.Direction.PathAndRotation)The spatial faces the direction of the path, plus an added rotation. Use together with the setRotation() method.
track.setDirectionType(MotionTrack.Direction.Rotation)The spatial spins (rotates) while moving. You describe the spin by a custom quaternion. Use together with the setRotation() method.
track.setLookAt(teapot.getWorldTranslation(), Vector3f.UNIT_Y)The spatial always faces towards this location. Use together with MotionTrack.Direction.LookAt.
track.setRotation(quaternion)Sets the rotation. Use together with MotionTrack.Direction.Rotation or MotionTrack.Direction.PathAndRotation.
+ +

+ +Tip: Most likely you remote-control more than one object in your scene. Give the tracks and paths useful names such as dragon_track, dragon_path, hero_track, hero_path, etc. +

+ +
+ +

PositionTrack

+
+ +

+ +A PositionTrack moves a Spatial in a straight line from its current position to the end position. + +

+
PositionTrack track = new PositionTrack(
+    thingNode, endPosition, duration, loopMode);
+ +

+Details of the constructor: +

+ + +

+ +The start location is always the current location of the Spatial. +

+ +
+ +

RotationTrack

+
+ +

+ +A RotationTrack remote-controls the rotation of a spatial. + +

+
RotationTrack thingRotationControl = new RotationTrack(
+    thingNode, endRotation,  duration, loopMode);
+ +

+Details of the constructor: +

+ + +
+ +

ScaleTrack

+
+ +

+ +A ScaleTrack remote-controls whether a spatial grows or shrinks. +

+
ScaleTrack thingScaleControl = new ScaleTrack(
+    thingNode, endScale,  duration, loopMode);
+ +

+Details of the constructor: +

+ + +
+ +

SoundTrack

+
+ +

+ +A SoundTrack plays a sound as part of the cinematic. + +

+
SoundTrack( audioPath, isStream, duration, loopMode )
+ +

+ +Details of the constructor: +

+ + +
+ +

GuiTrack

+
+ +

+ +A GuiTrack shows or hide a NiftyGUI as part of a cinematic. + +

+
GuiTrack( screen, duration, loopMode )
+ +

+ +You must use this together with bindUI() to specify the Nifty GUI XML file that you want to load: + +

+
cinematic.bindUi("Interface/subtitle.xml");
+ +

+Details of the constructor: +

+ + +
+ +

AnimationTrack

+
+ +

+ +An AnimationTrack triggers an animation as part of a cinematic. + +

+
AnimationTrack( thingNode, animationName, duration, loopMode )
+ +

+ +Details of the constructor: +

+ + +
+ +

Customizations

+
+ +

+ +You can extend individual CinematicEvents. The shows how to extend a GuiTrack to script subtitles. See how the subtitles are used in the . +

+ +

+You can also create new CinematicEvent by extending . An AbstractCinematicEvent implements the CinematicEvent interface and provides duration, time, speed, etc??? management. Look at the is to use this for a custom fadeIn/fadeOut effect in combination with a com.jme3.post.filters.FadeFilter. +

+ +
+ +

Interacting with Cinematics

+
+ +
+ +

CinematicEventListener

+
+
CinematicEventListener cel = new CinematicEventListener() {
+  public void onPlay(CinematicEvent cinematic) {
+    chaseCam.setEnabled(false);
+    System.out.println("play");
+  }
+ 
+  public void onPause(CinematicEvent cinematic) {
+    chaseCam.setEnabled(true);
+    System.out.println("pause");
+  }
+ 
+  public void onStop(CinematicEvent cinematic) {
+    chaseCam.setEnabled(true);
+    System.out.println("stop");
+  }
+}
+cinematic.addListener(cel);
+ +
+ +

Physics Interaction

+
+ +

+ +Upcoming. +

+ +
+ +

More Information

+
+ +

+See also: + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/collision_and_intersection.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/collision_and_intersection.html new file mode 100644 index 000000000..91cbb0e04 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/collision_and_intersection.html @@ -0,0 +1,241 @@ + +

Collision and Intersection

+
+ +

+ +The term collision can be used to refer to physical interactions (where physical objects collide, push and bump off one another), and also to non-physical intersections in 3D space. This article is about the non-physical (mathematical) collisions. +

+ +

+Non-physical collision detection is interesting because it uses less computing resources than physical collision detection. The non-physical calculations are faster because they do not have any side effects such as pushing other objects or bumping off of them. Tasks such as mouse picking are easily implemented using using mathematical techniques such as ray casting and intersections. Experienced developers optimize their games by finding ways to simulate certain (otherwise expensive physical) interactions in a non-physical way. +

+ +

+Example: One example for an optimization is a physical vehicle's wheels. You could make the wheels fully physical disks, and have jME calculate every tiny force ??? sounds very accurate? It's total overkill and too slow for a racing game. A more performant solution is to cast four invisible rays down from the vehicle and calculate the intersections with the floor. These non-physical wheels require (in the simplest case) only four calculations per tick to achieve an effect that players can hardly distinguish from the real thing. +

+ +
+ +

Collidable

+
+ +

+ +The interface com.jme3.collision.Collidable declares one method that returns how many collisions were found between two Collidables: collideWith(Collidable other, CollisionResults results). +

+ +
+ + + + + + + + + + + + + + + +
CollisionResults MethodUsage
size() Returns the number of CollisionResult objects.
getClosestCollision() Returns the CollisionResult with the lowest distance.
getFarthestCollision()Returns the CollisionResult with the farthest distance.
getCollision(i) Returns the CollisionResult at index i.
+ +

+A CollisionResult object contains information about the second party of the collision event. +

+
+ + + + + + + + + + + + + + + + + + + + + +
CollisionResult MethodUsage
getContactPoint()Returns the contact point coordinate on the second party, as Vector3f.
getContactNormal()Returns the Normal vector at the contact point, as Vector3f.
getDistance()Returns the distance between the Collidable and the second party, as float.
getGeometry()Returns the Geometry of the second party.
getTriangle(t)Binds t to the triangle t on the second party's mesh that was hit.
getTriangleIndex()Returns the index of the triangle on the second party's mesh that was hit.
+ +
+ +

Code Sample

+
+ +

+ +Assume you have two collidables a and b and want to detect collisions between them. The collision parties can be Geometries, Nodes with Geometries attached (including the rootNode), Planes, Quads, Lines, or Rays. An important restriction is that you can only collide geometry vs bounding volumes or rays. (This means for example that a must be of Type Node or Geometry and b respectively of Type BoundingBox, BoundingSphere or Ray.) +

+ +

+The following code snippet can be triggered by listeners (e.g. after an input action such as a click), or timed in the update loop. +

+
  // Calculate detection results
+  CollisionResults results = new CollisionResults();
+  a.collideWith(b, results);
+  System.out.println("Number of Collisions between" + 
+      a.getName()+ " and " + b.getName() + ": " + results.size());
+  // Use the results
+  if (results.size() > 0) {
+    // how to react when a collision was detected
+    CollisionResult closest  = results.getClosestCollision();
+    System.out.println("What was hit? " + closest.getGeometry().getName() );
+    System.out.println("Where was it hit? " + closest.getContactPoint() );
+    System.out.println("Distance? " + closest.getDistance() );
+  } else {
+    // how to react when no collision occured
+  }
+}
+ +

+You can also loop over all results and trigger different reactions depending on what was hit and where it was hit. In this example, we simply print info about them. +

+
  // Calculate Results
+  CollisionResults results = new CollisionResults();
+  a.collideWith(b, results);
+  System.out.println("Number of Collisions between" + a.getName()+ " and "
+   + b.getName() " : " + results.size());
+  // Use the results
+  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   party = results.getCollision(i).getGeometry().getName();
+    int        tri = results.getCollision(i).getTriangleIndex();
+    Vector3f  norm = results.getCollision(i).getTriangle(new Triangle()).getNormal();
+    System.out.println("Details of Collision #" + i + ":");
+    System.out.println("  Party " + party + " was hit at " + pt + ", " + dist + " wu away.");
+    System.out.println("  The hit triangle #" + tri + " has a normal vector of " + norm);
+  }
+ +

+Knowing the distance of the collisions is useful for example when you intersect Lines and Rays with other objects. + +

+ +
+ +

Bounding Volumes

+
+ +

+ +A com.jme3.bounding.BoundingVolume is an interface for dealing with containment of a collection of points. All BoundingVolumes are Collidable and are used as optimization to calculate non-physical collisions more quickly: It's always faster to calculate an intersection between simple shapes like spheres and boxes than between complex shapes like models. +

+ +

+jME3 computes bounding volumes for all objects. These bounding volumes are later used for frustum culling, which is making sure only objects visible on-screen are actually sent for rendering. +

+ +

+All fast-paced action and shooter games use BoundingVolumes as an optimization. Wrap all complex models into simpler shapes ??? in the end, you get equally useful collision detection results, but faster. +

+ +

+Supported types: + +

+ + +

+ +

Note: If you are looking for bounding volumes for physical objects, use CollisionShapes. +

+ +

+ +
+ +

Usage

+
+ +

+ +For example you can use Bounding Volumes on custom meshes, or complex non-physical shapes. + +

+
mesh.setBound(new BoundingSphere());
+mesh.updateBound();
+ +
+ +

Mesh and Scene Graph Collision

+
+ +

+ +One of the supported Collidables are meshes and scene graph objects. To execute a collision detection query against a scene graph, use Spatial.collideWith(). This will traverse the scene graph and return any mesh collisions that were detected. Note that the first collision against a particular scene graph may take a long time, this is because a special data structure called needs to be generated for the meshes. At a later point, the mesh could change and the BIH tree would become out of date, in that case, call on the changed mesh to update the BIH tree. +

+ +
+ +

Intersection

+
+ +

+ +A com.jme3.math.Ray is an infinite line with a beginning, a direction, and no end; whereas a com.jme3.math.Line is an infinite line with only a direction (no beginning, no end). +

+ +

+Rays are used to perform line-of-sight calculations. This means you can detect what users were "aiming at" when they clicked or pressed a key. You can also use this to detect whether game characters can see something (or someone) or not. +

+ + +

+ +

These simple but powerful ray-surface intersection tests are called Ray Casting. As opposed to the more advanced Ray Tracing technique, Ray Casting does not follow the ray's reflection after the first hit ??? the ray just goes straight on. +

+

+ +

+Learn the details of how to implement Mouse Picking here. +

+
+ +

+TODO: +

+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/combo_moves.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/combo_moves.html new file mode 100644 index 000000000..85d69ff90 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/combo_moves.html @@ -0,0 +1,270 @@ + +

Combo Moves

+
+ +

+The ComboMoves class allows you to define combinations of inputs that trigger special actions. Entering an input combo correctly can bring the player incremental rewards, such as an increased chance to hit, an increased effectiveness, or decreased change of being blocked, whatever the game designer chooses. +

+ +

+Combos are usually a series of inputs, in a fixed order: For example a keyboard combo can look like: "press Down, then Down+Right together, then Right". +

+ +

+Usage: +

+
    +
  1. Create input triggers
    +
  2. +
  3. Define combos
    +
  4. +
  5. Detect combos in ActionListener
    +
  6. +
  7. Execute combos in update loop
    +
  8. +
+ +

+ +Copy the two classes ComboMoveExecution.java and ComboMove.java into your application and adjust them to your package paths. +

+ +
+ +

Example Code

+
+ + +
+ +

Create Input Triggers

+
+ +

+ +First you define your game's inputs as you usually do: Implement the com.jme3.input.controls.ActionListener interface for your class, and add triggers mappings such as com.jme3.input.controls.KeyTrigger and com.jme3.input.KeyInput. +

+ +

+For example: +

+
inputManager.addMapping("Left",    new KeyTrigger(KeyInput.KEY_LEFT));
+inputManager.addMapping("Right",   new KeyTrigger(KeyInput.KEY_RIGHT));
+inputManager.addMapping("Up",      new KeyTrigger(KeyInput.KEY_UP));
+inputManager.addMapping("Down",    new KeyTrigger(KeyInput.KEY_DOWN));
+inputManager.addMapping("Attack1", new KeyTrigger(KeyInput.KEY_SPACE));
+...
+inputManager.addListener(this, "Left", "Right", "Up", "Down", "Attack1");
+ +
+ +

Define Combos

+
+ +

+ +For each of your combo moves, you specify the series of inputs that will trigger it. The order in which you define them is the order the player has to press them for the step to be recorded. When all steps have been recorded, the combo is triggered. +

+ +

+The following example shows how a fireball combo move is triggered by pressing the navigation keys for "down, down+right, right", in this order. +

+
ComboMove fireball = new ComboMove("Fireball");
+fireball.press("Down").notPress("Right").done();
+fireball.press("Right", "Down").done();
+fireball.press("Right").notPress("Down").done();
+fireball.notPress("Right", "Down").done();
+fireball.setUseFinalState(false);
+ +

+Also create a ComboMoveExecution object for each ComboMove. You need it later to execute the detected combo. +

+
ComboMoveExecution fireballExec = new ComboMoveExecution(fireball);
+ +
+ +

ComboMove Class Methods

+
+ +

+ +Use the following ComboMove methods to specify the combo: + +

+
+ + + + + + + + + + + + + + + + + + + + + +
ComboMove MethodDescription
press("A").done();
+press("A","B").done();
Combo step is recorded if A is entered.
+Combo step is recorded if A and B are entered simultaneously.
notPress("A").done();
+notPress("A","B").done();
Combo step is recorded if A is released.
+Combo step is recorded if A and B are both released.
press("A").notPress("B").done();Combo step is recorded if A is entered, and not B
press("A").notPress("B").timeElapsed(0.11f).done();Combo step is recorded a certain time after A and not B is entered.
+etc, etc ???
setPriority(0.5f);If there is an ambiguity, a high-priority combo will trigger instead of a low-priority combo. This prevents that a similar looking combo step "hijacks" another Combo. Use only once per ComboMove.
setUseFinalState(false);
+setUseFinalState(true);
This is the final command of the series.
+False: Do not wait on a final state, chain combo steps. (?)
+True: This is the final state, do not chain combo steps. (?)
+ +

+ +The press() and notPress() methods accept sets of Input Triggers, e.g. fireball.press("A","B","C").done(). +

+ +

+The following getters give you more information about the game state: + +

+
+ + + + + + + + + + + + +
ComboMove MethodUsage
getCastTime()Returns the time since the last step has been recorded. (?)
getMoveName()Returns the string of the current combo
getPriority()Returns the priority of this move
+ +
+ +

Detect Combos in ActionListener

+
+ +

+ +Now that you have specified the combo steps, you want to detect them. You do that in the onAction() method that you get from the ActionListener interface. +

+ +

+Create a HashSet pressMappings to track curently pressed mappings, and a ComboMove object currentMove to track the current move. +

+ +

+We also track the cast time of a combo to determine if it has timed out (see update loop below). +

+
private HashSet<String> pressedMappings = new HashSet<String>();
+private ComboMove currentMove = null;
+private float currentMoveCastTime = 0;
+private float time = 0;
+...
+ 
+public void onAction(String name, boolean isPressed, float tpf) {
+    // Record pressed mappings
+    if (isPressed){
+        pressedMappings.add(name);
+    }else{
+        pressedMappings.remove(name);
+    }
+ 
+    // The pressed mappings have changed: Update ComboExecution objects
+    List<ComboMove> invokedMoves = new ArrayList<ComboMove>();
+    if (fireballExec.updateState(pressedMappings, time)){
+        invokedMoves.add(fireball);
+    }
+    // ... add more ComboExecs here...
+ 
+    // If any ComboMoves have been sucessfully triggered:
+    if (invokedMoves.size() > 0){
+        // identify the move with highest priority
+        float priority = 0;
+        ComboMove toExec = null;
+        for (ComboMove move : invokedMoves){
+            if (move.getPriority() > priority){
+                priority = move.getPriority();
+                toExec = move;
+            }
+        }
+        if (currentMove != null && currentMove.getPriority() > toExec.getPriority()){
+            return; // skip lower-priority moves
+        }
+ 
+        // If a ComboMove has been identified, store it in currentMove
+        currentMove = toExec;
+        currentMoveCastTime = currentMove.getCastTime();
+    }
+}
+ +
+ +

Execute Combos in the Update Loop

+
+ +

+ +Now that you have detected the current move, you want to execute it. You do that in the update loop. +

+
@Override
+public void simpleUpdate(float tpf){
+    time += tpf;
+    fireballExec.updateExpiration(time); 
+    // ... update more ComboExecs here....
+ 
+    if (currentMove != null){
+        currentMoveCastTime -= tpf;
+        if (currentMoveCastTime <= 0){
+            System.out.println("THIS COMBO WAS TRIGGERED: " + currentMove.getMoveName());
+            // TODO: for each combo, implement special actions here
+            currentMoveCastTime = 0;
+            currentMove = null;
+        }
+    }
+}
+ +

+Test currentMove.getMoveName() and proceed to call methods that implement any special actions and bonuses. This is up to you and depends individually on your game. +

+ +
+ +

Why Combos?

+
+ +

+ +Depending on the game genre, the designer can reward the players' intrinsical or extrinsical skills: + +

+ +
+ keyinput, + input, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html new file mode 100644 index 000000000..9e9ce874c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html @@ -0,0 +1,391 @@ + +

Custom Controls

+
+ +

+A com.jme3.scene.control.Control is a customizable jME3 interface that allows you to cleanly steer the behaviour of game entities (Spatials), such as artificially intelligent behaviour in NPCs, traps, automatic alarms and doors, animals and pets, self-steering vehicles or platforms ??? anything that moves and interacts. Several instances of customs Controls together implement the behaviours of a type of Spatial. +

+ +

+To control global game behaviour see Application States ??? you often use AppStates and Control together. +

+ + +

+ +To control the behaviour of spatials: +

+
    +
  1. Create one control for each type of behavior. When you add several controls to one spatial, they will be executed in the order they were added.
    +For example, one NPC can be controlled by a PhysicsControl instance and an AIControl instance.
    +
  2. +
  3. Define the custom control and implement its behaviour in the Control's update method:
    +
      +
    • You can pass arguments into your custom control.
      +
    • +
    • In the control class, the object spatial gives you access to the spatial and subspatials that the control is attached to.
      +
    • +
    • Here you modify the spatial's transformation (move, scale, rotate), play animations, check its environement, define how it acts and reacts.
      +
    • +
    +
  4. +
  5. Add an instance of the Control to a spatial to give it this behavior. The spatial's game state is updated automatically from now on.
    spatial.addControl(myControl);
    +
    +
  6. +
+ +

+ +To implement game logic for a type of spatial, you will either extend AbstractControl (most common case), or implement the Control interface, as explained in this article. +

+ +
+ +

Usage

+
+ +

+ +Use Controls to implement the behaviour of types of game entities. +

+ + +

+ +Examples: You can write +

+ + +

+ +The possibilities are endless. :-) +

+ +
+ +

Example Code

+
+ +

+ +Other examples include the built-in RigidBodyControl in JME's physics integration, the built-in TerrainLODControl that updates the terrain's level of detail depending on the viewer's perspective, etc. +

+ +

+Existing examples in the code base include: +

+ + +
+ +

AbstractControl Class

+
+ +

+ +

The most common way to create a Control is to create a class that extends AbstractControl. +

+

+ +

+The AbstractControl can be found under com.jme3.scene.control.AbstractControl. This is a default abstract class that implements the Control interface. +

+ + +

+ +Usage: Your custom subclass implements the three methods controlUpdate(), controlRender(), setSpatial(), and cloneForSpatial() as shown here: +

+
public class MyControl extends AbstractControl implements Savable, Cloneable {
+  private int index; // can have custom fields -- example 
+ 
+  public MyControl(){} // empty serialization constructor
+ 
+  /** Optional custom constructor with arguments that can init custom fields.
+    * Note: you cannot modify the spatial here yet! */
+  public MyControl(int i){ 
+    // index=i; // example 
+  } 
+ 
+  /** This is your init method. Optionally, you can modify 
+    * the spatial from here (transform it, initialize userdata, etc). */
+  @Override
+  public void setSpatial(Spatial spatial) {
+    super.setSpatial(spatial);
+    // spatial.setUserData("index", i); // example
+  }
+ 
+ 
+  /** Implement your spatial's behaviour here.
+    * From here you can modify the scene graph and the spatial
+    * (transform them, get and set userdata, etc).
+    * This loop controls the spatial while the Control is enabled. */
+  @Override
+  protected void controlUpdate(float tpf){
+    if(spatial != null) {
+      // spatial.rotate(tpf,tpf,tpf); // example behaviour
+    }
+  }
+ 
+  @Override
+  public Control cloneForSpatial(Spatial spatial){
+    final MyControl control = new MyControl();
+    /* Optional: use setters to copy userdata into the cloned control */
+    // control.setIndex(i); // example
+    control.setSpatial(spatial);
+    return control;
+  }
+ 
+  @Override
+  protected void controlRender(RenderManager rm, ViewPort vp){
+     /* Optional: rendering manipulation (for advanced users) */
+  }
+ 
+  @Override
+  public void read(JmeImporter im) throws IOException {
+      super.read(im);
+      // im.getCapsule(this).read(...);
+  }
+ 
+  @Override
+  public void write(JmeExporter ex) throws IOException {
+      super.write(ex);
+      // ex.getCapsule(this).write(...);
+  }
+ 
+}
+ +

+See also: +

+ + +
+ +

The Control Interface

+
+ +

+ +

In the less common case that you want to create a Control that also extends another class, create a custom interface that extends jME3's Control interface. Your class can become a Control by implement the Control interface, and at the same time extend another class. +

+

+ +

+The Control interface can be found under com.jme3.scene.control.Control. It has the following method signatures: +

+ + +

+ +Usage example: +1. Create a custom control interface + +

+
public interface MyControlInterface extends Control {
+    public void setSomething(int x); // optionally, add custom methods
+}
+ +

+ +2. Create custom Controls implementing your Control interface. + +

+
public class MyControl extends MyCustomClass implements MyControlInterface {
+ 
+    protected Spatial spatial;
+ 
+    protected boolean enabled = true;
+ 
+    public MyControl() { } // empty serialization constructor
+ 
+    public MyControl(int x) { // custom constructor
+        super(x);
+    }
+ 
+    @Override
+    public void update(float tpf) {
+        if (enabled && spatial != null) {
+            // Write custom code to control the spatial here!
+        }
+    }
+ 
+    @Override
+    public void render(RenderManager rm, ViewPort vp) {
+        // optional for advanced users, e.g. to display a debug shape
+    }
+ 
+    @Override
+    public Control cloneForSpatial(Spatial spatial) {
+        MyControl control = new MyControl();
+        // set custom properties
+        control.setSpatial(spatial);
+        control.setEnabled(isEnabled()); 
+        // set some more properties here...
+        return control;
+    }
+ 
+    @Override
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+ 
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+ 
+    @Override
+    public void setSomething(int z) {
+        // You can add custom methods ...
+    }
+ 
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(enabled, "enabled", true);
+        oc.write(spatial, "spatial", null);
+        // write custom variables ....
+    }
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+        enabled = ic.readBoolean("enabled", true);
+        spatial = (Spatial) ic.readSavable("spatial", null);
+        // read custom variables ....
+    }
+}
+ +
+ +

Best Practices

+
+ +

+ +Tip: Use the getControl() accessor to get Control objects from Spatials. No need to pass around lots of object references. +Here an example from the code: + +

+
public class CharacterAnimControl implements Control {
+  ...
+  public void setSpatial(Spatial spatial) {
+    ...
+    animControl      = spatial.getControl(AnimControl.class);
+    characterControl = spatial.getControl(CharacterControl.class);
+    ...
+  }
+}
+ +

+ +Tip: You can create custom Control interfaces so a set of different Controls provide the same methods and can be accessed with the interface class type. + +

+
public interface ManualControl extends Control {
+    public void steerX(float value);
+    public void steerY(float value);
+    public void moveX(float value);
+    public void moveY(float value);
+    public void moveZ(float value);
+   ...
+}
+ +

+ +Then you create custom sub-Controls and implement the methods accordingly to the context: + +

+
public class ManualVehicleControl   extends ManualControl {...}
+ +

+ and + +

+
public class ManualCharacterControl extends ManualControl {...}
+ +

+ +Then add the appropriate controls to spatials: + +

+
characterSpatial.addControl(new ManualCharacterControl());
+...
+vehicleSpatial.addControl(new ManualVehicleControl());
+...
+ +

+ +Tip: Use the getControl() method on a Spatial to get a specific Control object, and activate its behaviour! + +

+
ManualControl c = mySpatial.getControl(ManualControl.class);
+c.steerX(steerX);
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_meshes.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_meshes.html new file mode 100644 index 000000000..de53563d4 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_meshes.html @@ -0,0 +1,323 @@ + +

Custom Mesh Shapes

+
+ +

+ +Use the Mesh class to create custom shapes that go beyond Quad, Box, Cylinder, and Sphere, even procedural shapes are possible. Thank you to KayTrance for providing the sample code! +In this tutorial, we (re)create a very simple rectangular mesh, and we have a look at different ways of coloring it. A flat rectangle may not look useful because it's exactly the same as a com.jme3.scene.shape.Quad. We choose this simple example in order to show you how to build any shape out of triangles ??? without the distractions of more complex shapes. +

+ + +
+ +

Polygon Meshes

+
+ +

+Polygon meshes are made up of triangles. The corners of the triangles are vertices. So, when ever you create a new shape, you break it down into triangles. +Let's look at a cube. A cube is made up of 6 rectangles. Each rectangle can be broken down into two triangles. This means you need 12 triangles to create a cube mesh. You also need to know the 8 corner coordinates (vertices). The trick is that you have to specify the vertices in a certain order: Each triangle separately, counter-clockwise. +Sounds worse than it is ??? here is an example: + +

+ +
+ +

Creating a Quad Mesh

+
+ +

+Okay, we want to create a Quad. A quad has four vertices, and is made up of two triangles. +The base class for creating meshes is com.jme3.scene.Mesh. + +

+
Mesh mesh = new Mesh();
+ +

+ +If you create your own Mesh-based class, replace mesh by this in the following examples: + +

+
public class MyMesh extends Mesh {  }
+ +
+ +

Vertices

+
+ +

+To define your own shape, determine its vertex positions in space. Store them in an array using com.jme3.math.Vector3f. For a Quad, we need four vertices: Bottom left, bottom right, top left, top right. We name the array vertices[]. + +

+
Vector3f [] vertices = new Vector3f[4];
+vertices[0] = new Vector3f(0,0,0);
+vertices[1] = new Vector3f(3,0,0);
+vertices[2] = new Vector3f(0,3,0);
+vertices[3] = new Vector3f(3,3,0);
+ +
+ +

Texture Coordinates

+
+ +

+Next, define the Quad's 2D texture coordinates for each vertex, in the same order: Bottom left, bottom right, top left, top right. We name this array texCoord[] + +

+
Vector2f[] texCoord = new Vector2f[4];
+texCoord[0] = new Vector2f(0,0);
+texCoord[1] = new Vector2f(1,0);
+texCoord[2] = new Vector2f(0,1);
+texCoord[3] = new Vector2f(1,1);
+ +
+ +

Connecting the Dots

+
+ +

+Next we turn the unrelated coordinates into triangles ??? We define the order in which the mesh is constructed. Think of these indexes as coming in groups of three. Each group of indexes describes one triangle. Note that you must specify the vertices counter-clockwise! + +

+
int [] indexes = { 2,0,1, 1,3,2 };
+ +
2\2--3
+| \  | Counter-clockwise
+|  \ |
+0--1\1
+ +
+ +

Setting the Mesh Buffer

+
+ +

+The Mesh data is stored in a buffer. +

+
    +
  1. Using com.jme3.util.BufferUtils, we create three buffers for the three types of information we have:
    +
      +
    • vertex positions,
      +
    • +
    • texture coordinates,
      +
    • +
    • indices.
      +
    • +
    +
  2. +
  3. We assign the data to the appropriate type of buffer inside the mesh object. The three buffer types are taken from an enum in com.jme3.scene.VertexBuffer.Type.
    +
  4. +
  5. The third parameter describes the number of components of the values. Vertex postions are 3 float values, texture coordinates are 2 float values, and the indices are 3 ints representing 3 vertices in a triangle.
    +
  6. +
  7. In order for jMonkey to correctly show the mesh in the scene, it needs to know the bounds of our new mesh. This can easily be achieved by calling the updateBound() method on it.
    +
  8. +
+
mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
+mesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
+mesh.setBuffer(Type.Index,    3, BufferUtils.createIntBuffer(indexes));
+mesh.updateBound();
+ +

+ +Our Mesh is ready! Now we want to see it. + +

+ +
+ +

Using the Mesh in a Scene

+
+ +

+We create a com.jme3.scene.Geometry, apply a simple color material to it, and attach it to the rootNode to make it appear in the scene. + +

+
Geometry geo = new Geometry("OurMesh", mesh);
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+mat.setColor("Color", ColorRGBA.Blue);
+geo.setMaterial(mat);
+rootNode.attachChild(geo);
+ +

+ +Ta-daa! + +

+ +
+ +

Dynamic Meshes

+
+ +

+If modifying a mesh dynamically in a way which would change the model's bounds then you need to call updateModelBound() on the Geometry object containing the mesh after calling updateBounds() on the mesh object. There is a warning on updateModelBounds about not usually needing to use it but that can be ignored in this special case. +

+ +
+ +

Optional Mesh Features

+
+ +

+There are more vertex buffers in a Mesh than the three shown above. For an overview, see also mesh. + +

+ +
+ +

Example: Vertex Colors

+
+ +

+Vertex coloring is a simple way of coloring meshes. Instead of just assigning one solid color, each vertex (corner) has a color assigned. The faces between the vertices are then colored with a gradient. You can use the same mesh mesh object that you defined above. + +

+
Geometry geo = new Geometry ("ColoredMesh", mesh);
+Material matVC = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+matVC.setBoolean("VertexColor", true);
+ +

+ +You create a float array color buffer: +

+ + +

+Loop over the colorArray buffer to quickly set some RGBA value for each vertex. As usual, RGBA color values range from 0.0f to 1.0f. Note that the color values in this example are arbitrarily chosen. It's just a quick loop to give every vertex a different RGBA value (a purplish gray, purple, a greenish gray, green, see screenshot), without writing too much code. For your own mesh, you'd assign meaningful values for the color buffer depending on which color you want your mesh to have. + +

+
for(int i = 0; i < 4; i++){
+   // Red value (is increased by .2 on each next vertex here)
+   colorArray[colorIndex++]= 0.1f+(.2f*i);
+   // Green value (is reduced by .2 on each next vertex)
+   colorArray[colorIndex++]= 0.9f-(0.2f*i);
+   // Blue value (remains the same in our case)
+   colorArray[colorIndex++]= 0.5f;
+   // Alpha value (no transparency set here)
+   colorArray[colorIndex++]= 1.0f;
+}
+ +

+ +Next, set the color buffer. An RGBA color value contains four float components, thus the parameter 4. + +

+
mesh.setBuffer(Type.Color, 4, colorArray);
+geo.setMaterial(matVC);
+ +

+ +Now you see a gradient color extending from each vertex. + +

+ +
+ +

Example: Shaded Mesh with Normals

+
+ +

+The examples used the mesh together with the Unshaded.j3md material. If you want to use the Mesh with a Phong illuminated material (such as Lighting.j3md), the mesh needs to include information about its normals. (The Normals encode in which direction a mesh polygon is facing, which is important for calculating light and shadow.) + +

+
float[] normals = new float[12];
+normals = new float[]{0,0,1, 0,0,1, 0,0,1, 0,0,1};
+mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
+ +

+ +You need as many normals as the polygon has vertices. For a flat quad, they point all in the same direction. In this case, the direction is the Z unit vector (Vector3f.UNIT_Z), this means the quad is facing the camera. +

+ +
+ +

Example: Point Mode

+
+ +

+Alternatively, you can show the vertices as colored points instead of coloring the faces. + +

+
Geometry coloredMesh = new Geometry ("ColoredMesh", cMesh);
+...
+mesh.setMode(Mesh.Mode.Points);
+mesh.setPointSize(10f);
+mesh.updateBound();
+mesh.setStatic();
+Geometry points = new Geometry("Points", mesh);
+points.setMaterial(mat);
+rootNode.attachChild(points);
+rootNode.attachChild(geo);
+ +

+ +This will result in a 10 px dot being rendered for each of the four vertices. The dot has the vertex color you specified above. The Quad's faces are not rendered at all. This can be used for a special debugging or editing mode. +

+ +
+ +

Debugging Tip: Culling

+
+ +

+ +By default, jME3 optimizes a mesh by culling (not drawing) its backfaces. It determines which side the front or backface of a mesh is by the order of the vertices: The frontface is the one where the vertices are specified counter-clockwise. +

+ +

+This means for you that your custom mesh is invisible when seen from "behind" or from the inside. This may not be a problem, often this is even intended because it's faster. The player will not look at the inside of most things anyway. For example, if your custom mesh is a closed polyhedron, or a flat wallpaper-like object, then rendering the backfaces (the inside of the pillar, the back of the painting, etc) would indeed be a waste of resources. +

+ +

+In case however that your usecase requires the backfaces to be visible, you have two options: +

+ + +

+ +See also: Spatial ??? contains more info about how to debug custom meshes (that do not render as expected) by changing the default culling behaviour. +

+
+ spatial, + node, + mesh, + geometry, + scenegraph +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debug-shapes.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debug-shapes.png new file mode 100644 index 000000000..69f7ff585 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debug-shapes.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html new file mode 100644 index 000000000..e39b35879 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html @@ -0,0 +1,275 @@ + +

Debugging

+
+ +

+ +When you deal with complex game engine features like animations or physics it is handy to get feedback from the engine how it interpreted the current state. Is the physical object's collision shape really where you think it is? Is the skeleton of the animated character moving like you think it should? This document shows you how to activate visual debug aides. +

+ +

+What if you just want to quickly write code that loads models and brings them in their start position? You may not want to hunt for a sample model, convert it, add lights, and load materials. Instead you use "hasslefree" simple shapes, and a "hasslefree" unshaded material or wireframe: No model, no light source, no materials are needed to see them in your test scene. +

+ +

+If you ever have problems with objects appearing in the wrong spot, with the wrong scale, or wrong orientation, simply attach debug shapes to your scene to have a point of reference in 3D space ??? just like a giant ruler. If your code positions the debug shapes correctly, but models remain invisible when you apply the same code to them, you know that the problem must be either the model (where is its origin coordinate?), or the light (too dark? too bright? missing?), or the model's material (missing?) ??? and not the positioning code. +

+ +

+Here are some different debug shapes: +

+ +

+ +

+ +
+ +

Debug Shapes

+
+ +
+ +

Coordinate Axes

+
+ +

+ +The coordinate axes (com.jme3.scene.debug.Arrow) help you see the cardinal directions (X,Y,Z) from their center point. Scale the arrows to use them as a "ruler" for a certain length. +

+
private void attachCoordinateAxes(Vector3f pos){
+  Arrow arrow = new Arrow(Vector3f.UNIT_X);
+  arrow.setLineWidth(4); // make arrow thicker
+  putShape(arrow, ColorRGBA.Red).setLocalTranslation(pos);
+ 
+  arrow = new Arrow(Vector3f.UNIT_Y);
+  arrow.setLineWidth(4); // make arrow thicker
+  putShape(arrow, ColorRGBA.Green).setLocalTranslation(pos);
+ 
+  arrow = new Arrow(Vector3f.UNIT_Z);
+  arrow.setLineWidth(4); // make arrow thicker
+  putShape(arrow, ColorRGBA.Blue).setLocalTranslation(pos);
+}
+ 
+private Geometry putShape(Mesh shape, ColorRGBA color){
+  Geometry g = new Geometry("coordinate axis", shape);
+  Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+  mat.getAdditionalRenderState().setWireframe(true);
+  mat.setColor("Color", color);
+  g.setMaterial(mat);
+  rootNode.attachChild(g);
+  return g;
+}
+ +
+ +

Wireframe Grid

+
+ +

+ +Use a wireframe grid (com.jme3.scene.debug.Grid) as a ruler or simple floor. +

+
private void attachGrid(Vector3f pos, float size, ColorRGBA color){
+  Geometry g = new Geometry("wireframe grid", new Grid(size, size, 0.2f) );
+  Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+  mat.getAdditionalRenderState().setWireframe(true);
+  mat.setColor("Color", color);
+  g.setMaterial(mat);
+  g.center().move(pos);
+  rootNode.attachChild(g);
+  return g;
+}
+ +
+ +

Wireframe Cube

+
+ +

+ +Use a wireframe cube (com.jme3.scene.debug.WireBox) as a stand-in object to see whether your code scales, positions, or orients, loaded models right. +

+
public void attachWireBox(Vector3f pos, float size, ColorRGBA color){
+  Geometry g = new Geometry("wireframe cube", new WireBox(size, size, size));
+  Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+  mat.getAdditionalRenderState().setWireframe(true);
+  mat.setColor("Color", color);
+  g.setMaterial(mat);
+  g.setLocalTranslation(pos);
+  rootNode.attachChild(g);
+  return g;
+}
+ +
+ +

Wireframe Sphere

+
+ +

+ +Use a wireframe sphere (com.jme3.scene.debug.WireSphere) as a stand-in object to see whether your code scales, positions, or orients, loaded models right. +

+
private void attachWireSphere(Vector3f pos, float size, ColorRGBA color){
+  Geometry g = new Geometry("wireframe sphere", new WireSphere(size));
+  Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+  mat.getAdditionalRenderState().setWireframe(true);
+  mat.setColor("Color", color);
+  g.setMaterial(mat);
+  g.setLocalTranslation(pos);
+  rootNode.attachChild(g);
+  return g;
+}
+ +
+ +

Wireframe for Physics

+
+ +

+ +You can display a wireframe of the (usually invisible) collision shape around all physical objects. Use this for debugging when analyzing unexpected behaviour. Does not work with DETACHED physics, please switch to PARALLEL or SEQUENTIAL for debugging. +

+
physicsSpace.enableDebug(assetManager);
+ +
+ +

Wireframe for Animations

+
+ +

+ +Making the skeleton visible inside animated models can be handy for debugging animations. The control object is an AnimControl, player is the loaded model. +

+
     SkeletonDebugger skeletonDebug = 
+         new SkeletonDebugger("skeleton", control.getSkeleton());
+     Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+     mat.setColor("Color", ColorRGBA.Green);
+     mat.getAdditionalRenderState().setDepthTest(false);
+     skeletonDebug.setMaterial(mat);
+     player.attachChild(skeletonDebug);
+ +
+ +

Example: Toggle Wireframe on Model

+
+ +

+ +We assume that you have loaded a model with a material mat. +

+ +

+Then you can add a switch to toggle the model's wireframe on and off, like this: + +

+
    +
  1. Create a key input trigger that switches between the two materials: E.g. we toggle when the T key is pressed:
        inputManager.addMapping("toggle wireframe", new KeyTrigger(KeyInput.KEY_T));
    +    inputManager.addListener(actionListener, "toggle wireframe");
    +
    +
  2. +
  3. Now add the toggle action to the action listener
      private ActionListener actionListener = new ActionListener() {
    +    @Override
    +    public void onAction(String name, boolean pressed, float tpf) {
    +      // toggle wireframe
    +      if (name.equals("toggle wireframe") && !pressed) {
    +        wireframe = !wireframe; // toggle boolean
    +        mat.getAdditionalRenderState().setWireframe(wireframe); 
    +      }
    +      // else ... other input tests.
    +    }
    +  };
    +
    +
  4. +
  5. Alternatively you could traverse over the whole scene and toggle for all Geometry objects in there if you don't want to create a new SceneProcessor
      private ActionListener actionListener = new ActionListener() {
    +    boolean wireframe = false; 
    + 
    +    @Override
    +    public void onAction(String name, boolean pressed, float tpf) {
    +      // toggle wireframe
    +      if (name.equals("toggle wireframe") && !pressed) {
    +        wireframe = !wireframe; // toggle boolean
    +        rootNode.depthFirstTraversal(new SceneGraphVisitor() {
    +          public void visit(Spatial spatial) {
    +            if (spatial instanceof Geometry)
    +              ((Geometry)spatial).getMaterial().getAdditionalRenderState().setWireframe(wireframe);
    +          }
    +        }); 
    +      }
    +      // else ... other input tests.
    +    }
    +  };
    +
    +
  6. +
+ +

+TIP :: To set the line width of wireframe display, use mesh.setLineWidth(lineWidth). Default line width is 1. +

+ +
+ +

Example: Toggle Wireframe on the scene

+
+ +

+ +To display the wireframe of the entire scene instead on one material at a time, first create the following Scene Processor +

+
public class WireProcessor implements SceneProcessor {    
+ 
+    RenderManager renderManager;
+    Material wireMaterial;
+ 
+    public WireProcessor(AssetManager assetManager) {
+        wireMaterial = new Material(assetManager, "/Common/MatDefs/Misc/Unshaded.j3md");
+        wireMaterial.setColor("Color", ColorRGBA.Blue);
+        wireMaterial.getAdditionalRenderState().setWireframe(true);
+    }
+ 
+    public void initialize(RenderManager rm, ViewPort vp) {
+        renderManager = rm;
+    }
+ 
+    public void reshape(ViewPort vp, int w, int h) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+ 
+    public boolean isInitialized() {
+        return renderManager != null;
+    }
+ 
+    public void preFrame(float tpf) {        
+    }
+ 
+    public void postQueue(RenderQueue rq) {
+        renderManager.setForcedMaterial(wireMaterial);
+    }
+ 
+    public void postFrame(FrameBuffer out) {
+        renderManager.setForcedMaterial(null);
+    }
+ 
+    public void cleanup() {
+        renderManager.setForcedMaterial(null);
+    }
+ 
+}
+ +

+Then attach the scene processor to the GUI Viewport. +

+
getViewPort().addProcessor(new WireProcessor());
+ +
+ +

See also

+
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/dof-blur.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/dof-blur.png new file mode 100644 index 000000000..41c560f9f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/dof-blur.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/drop-shadows.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/drop-shadows.png new file mode 100644 index 000000000..a3537504b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/drop-shadows.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html new file mode 100644 index 000000000..4dbac81aa --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html @@ -0,0 +1,321 @@ + +

jME3 Special Effects Overview

+
+ +

+ +jME3 supports several types of special effects: Post-Processor Filters, SceneProcessors, and Particle Emitters (also known as particle systems). This list contains screenshots and links to sample code that demonstrates how to add the effect to a scene. +

+ +
+ +

Sample Code

+
+ + +
+ +

Particle Emitter

+
+
public class MyGame extends SimpleApplication {
+  public void simpleInitApp() {
+    ParticleEmitter pm = new ParticleEmitter("my particle effect", Type.Triangle, 60);
+    Material pmMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+    pmMat.setTexture("Texture", assetManager.loadTexture("Effects/spark.png"));
+    pm.setMaterial(pmMat);
+    pm.setImagesX(1);
+    pm.setImagesY(1);
+    rootNode.attachChild(pm); // attach one or more emitters to any node
+  }
+}
+ +
+ +

Scene Processor

+
+
public class MyGame extends SimpleApplication {
+    private BasicShadowRenderer bsr;
+ 
+    public void simpleInitApp() {
+        bsr = new BasicShadowRenderer(assetManager, 1024);
+        bsr.setDirection(new Vector3f(.3f, -0.5f, -0.5f));
+        viewPort.addProcessor(bsr); // add one or more sceneprocessor to viewport
+    }
+ +
+ +

Post-Processor Filter

+
+
public class MyGame extends SimpleApplication {
+    private FilterPostProcessor fpp; // one FilterPostProcessor per app
+    private SomeFilter sf;           // one or more Filters per app
+ 
+    public void simpleInitApp() {
+        fpp = new FilterPostProcessor(assetManager);
+        viewPort.addProcessor(fpp); // add one FilterPostProcessor to viewPort
+ 
+        sf = new SomeFilter();
+        fpp.addFilter(sf);  // add one or more Filters to FilterPostProcessor
+    }
+ +
+ +

Water

+
+ +

+ + +The jMonkeyEngine's "SeaMonkey" WaterFilter simulates ocean waves, foam, including cool underwater caustics. +Use the SimpleWaterProcessor (SceneProcessor) for small, limited bodies of water, such as puddles, drinking troughs, pools, fountains. +

+ +

+See also the announcement with video. +

+ + +

+ +

+ + +
+ +

Environment Effects

+
+ +
+ +

Depth of Field Blur

+
+ +

+ + +

+ + +
+ +

Fog

+
+ + +
+ +

Light Scattering

+
+ + +
+ +

Vegetation

+
+ + +
+ +

Light and Shadows

+
+ +

+ +

+ +
+ +

Bloom and Glow

+
+ + +
+ +

Light

+
+ + +

+ + +

+ +
+ +

Shadow

+
+ + +
+ +

Special: Glass, Metal, Dissolve, Toon

+
+ +

+ + + +

+ +
+ +

Toon Effect

+
+ + +
+ +

Fade in / Fade out

+
+ + +
+ +

User Contributed

+
+ +

+ + +

+ +

+ShaderBlow - GLSL Shader Library +

+ + +

+ +Thanks for your awesome contributions! Keep them coming! +

+ +
+ +

Particle Emitters: Explosions, Fire, Smoke

+
+ +

+ + +Particle emitter effects are highly configurable and can have any texture. They can simulate smoke, dust, leaves, meteors, snowflakes, mosquitos, fire, explosions, clusters, embers, sparks??? +

+ +
+ +

+See also: +

+ +
+ documentation, + effect, + light, + water +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/elephant-pointlights.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/elephant-pointlights.png new file mode 100644 index 000000000..5803ec7b0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/elephant-pointlights.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/endless_terraingrid.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/endless_terraingrid.html new file mode 100644 index 000000000..60c93a6c7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/endless_terraingrid.html @@ -0,0 +1,115 @@ + +

Endless Terrain

+
+ +

+TerrainGrid is an extension built on top of the TerraMonkey tools like TerrainQuad and HeightMap, that provides "infinite" Terrain paging routines.
+ +Thanks to G??bor (@anthyon) and Brent (@sploreg) for this contribution! + +

+ +
+ +

Sample Code

+
+ +

+The classes with source code can be found in the org.jme3.terrain.geomipmapping and org.jme3.terrain.heightmap packages. Also there are 3 tests prepared in the jme3test.terrain package: +

+ + +
+ +

Specification

+
+ +

+TerrainGrid is made up of the TerrainGrid class, and the HeightMapGrid and TerrainGridListener interfaces. +

+ + +

+
+ +Multiple listeners can be added to the TerrainGrid, they will be called in the order of addition, so it???s possible to have multiple changes to the material before completing the load of the tile. +
+ +HeightMapGrid adds the possibility of loading terrain tiles on demand instead of having a simple height array. There???s no predefined way of how to store these tiles, it only takes care of loading one HeightMap object at given location at a time. + +

+ +
+ +

Motivation

+
+ +

+After playing around with the terrain in jME3, soon comes the requirement of having larger explorable lands. Increasing the size of one TerrainQuad leads to more memory usage, while it will still be easy to reach the worlds boundaries. That???s why TerrainGrid was designed. It extends the TerraindQuad class and uses 4 HeightMaps (dark blue) as the four sub-quad. This means that a terrain of size 513 will use tiles of 257. Also an LRUCache is built into the terrain package, so surrounding tiles (green) can be pre-cached on a different thread, lowering the loading time. The quads are updated as the camera approaches the boundary of the light blue section. + +

+ +
+ +

Rationale

+
+ +

+The design of the TerrainGrid system was chosen carefully, so that minimal effort needs to be taken to switch from previous TerrainQuad uses. It has the same constructors with the small exception that instead of an array of heightmap it takes a HeightMapGrid instance. All other parameters are forwarded down to the underlying TerrainQuad system. +There exist also two basic HeightMapGrid implementations: +

+ + +
+ +

Usage

+
+
    +
  1. instantiate a TerrainGrid object
    +
  2. +
  3. set material, listeners, translation, scale, etc.
    +
  4. +
  5. add a LODControl instance to the object
    +
  6. +
  7. call initialize with the camera location
    +
  8. +
  9. (optional) add it to the physicsSpace as you would a TerrainQuad
    +
  10. +
+ +

+Further information about terrain and TerrainQuad can be found in the wiki at: +

+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/explosion-5.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/explosion-5.png new file mode 100644 index 000000000..fc06d84eb Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/explosion-5.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/fade.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/fade.html new file mode 100644 index 000000000..ebaea7f62 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/fade.html @@ -0,0 +1,50 @@ + +

Fade-in / Fade-out Effect

+
+ +

+You can use a fade in/fade out effect to make smooth transitions, for example between game levels. The effect fades in from black to the initialized scene, or fades out from the scene to black. +The effect uses com.jme3.post.FilterPostProcessor and com.jme3.post.filters.FadeFilter. + +

+ +
+ +

Setting up

+
+
    +
  1. Create one FilterPostProcessor object per application.
    +
  2. +
  3. Create a FadeFilter object.
    +
  4. +
  5. Give the FadeFilter constructor the fade duration in seconds as parameter. If you use the parameter-less constructor, the duration is 1 sec by default.
    +
  6. +
  7. Add the FadeFilter to the FilterPostProcessor.
    +
  8. +
  9. Add the FilterPostProcessor to the default viewPort.
    +
  10. +
+
private FilterPostProcessor fpp;
+private FadeFilter fade;
+public void simpleInitApp() {
+  ...
+  fpp = new FilterPostProcessor(assetManager);
+  fade = new FadeFilter(2); // e.g. 2 seconds
+  fpp.addFilter(fade);
+  viewPort.addProcessor(fpp);
+  ...
+}
+ +
+ +

Fading in and out

+
+ +

+Now call the fade.fadeIn() and fade.fadeOut() methods to trigger the effect. +You can also change the fade duration using fade.setDuration(). +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/gui-layout-draft.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/gui-layout-draft.png new file mode 100644 index 000000000..3ba334e54 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/gui-layout-draft.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/headless_server.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/headless_server.html new file mode 100644 index 000000000..74d8031e5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/headless_server.html @@ -0,0 +1,95 @@ + +

jME3 Headless Server

+
+ +

+ +When adding multiplayer to your game, you may find that your server needs to know about game state (e.g. where are players, objects? Was that a direct hit? etc.) You can code all this up yourself, but there's an easier way. +

+ +

+It's very easy to change your current (client) game to function as a server as well. +

+ +
+ +

What Does Headless Mean?

+
+ +

+ +A headless server??? +

+ + +
+ +

Client Code

+
+ +

+ +First, let's take a look at the default way of creating a new game (in its simplest form): +

+
public static void main(String[] args) {
+  Application app = new Main();
+  app.start();
+}
+ +
+ +

Headless Server Code

+
+ +

+ +Now, with a simple change you can start your game in Headless mode. This means that all input and audio/visual output will be ignored. That's a good thing for a server. +

+
import com.jme3.system.JmeContext;
+import com.jme3.system.JmeContext.Type;
+ 
+public static void main(String[] args) {
+  Application app = new Main();
+  app.start(JmeContext.Type.Headless);
+}
+ +
+ +

Next steps

+
+ +

+ +Okay, so you can now start your game in a headless 'server mode', where to go from here? + +

+ +
+ server, + spidermonkey, + headless, + network, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hinges_and_joints.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hinges_and_joints.html new file mode 100644 index 000000000..2991944e8 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hinges_and_joints.html @@ -0,0 +1,235 @@ + +

Physical Hinges and Joints

+
+ +

+ +The jMonkeyEngine3 has built-in support for via the com.jme3.bullet package. +

+ +

+Game Physics are not only employed to calculate collisions, but they can also simulate hinges and joints. Think of pulley chains, shaky rope bridges, swinging pendulums, or (trap)door and chest hinges. Physics are a great addition to e.g. an action or puzzle game. +

+ +

+In this example, we will create a pendulum. The joint is the (invisible) connection between the pendulum body and the hook. You will see that you can use what you learn from the simple pendulum and apply it to other joint/hinge objects (rope bridges, etc). +

+ +
+ +

Sample Code

+
+ + +
+ +

Overview of this Physics Application

+
+
    +
  1. Create a SimpleApplication with a BulletAppState
    +
      +
    • This gives us a PhysicsSpace for PhysicsControls
      +
    • +
    +
  2. +
  3. For the pendulum, we use a Spatial with a PhysicsControl, and we apply physical forces to them.
    +
      +
    • The parts of the "pendulum" are Physics Control'ed Spatials with Collision Shapes.
      +
    • +
    • We create a fixed hookNode and a dynamic pendulumNode.
      +
    • +
    +
  4. +
  5. We can "crank the handle" and rotate the joint like a hinge, or we can let loose and expose the joints freely to gravity.
    +
      +
    • For physical forces we will use the method joint.enableMotor();
      +
    • +
    +
  6. +
+ +
+ +

Creating a Fixed Node

+
+ +

+ +The hookNode is the fixed point from which the pendulum hangs. It has no mass. +

+
Node hookNode=PhysicsTestHelper.createPhysicsTestNode(
+    assetManager, new BoxCollisionShape(new Vector3f( .1f, .1f, .1f)),0);
+hookNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f,0,0f));
+ 
+rootNode.attachChild(hookNode);
+getPhysicsSpace().add(hookNode);
+ +

+For a rope bridge, there would be two fixed nodes where the bridge is attached to the mountainside. +

+ +
+ +

Creating a Dynamic Node

+
+ +

+ +The pendulumNode is the dynamic part of the construction. It has a mass. +

+
Node pendulumNode=PhysicsTestHelper.createPhysicsTestNode(
+    assetManager, new BoxCollisionShape(new Vector3f( .3f, .3f, .3f)),1);
+pendulumNode.getControl(RigidBodyControl.class).setPhysicsLocation(new Vector3f(0f,-1,0f));
+rootNode.attachChild(pendulumNode);
+getPhysicsSpace().add(pendulumNode);
+ +

+For a rope bridge, each set of planks would be one dynamic node. +

+ +
+ +

Understanding DOF, Joints, and Hinges

+
+ +

+ +A PhysicsHingeJoint is an invisible connection between two nodes ??? here between the pendulum body and the hook. Why are hinges and joints represented by the same class? Hinges and joints have something in common: They constrain the mechanical degree of freedom (DOF) of another object. +

+ +

+Consider a free falling, "unchained" object in physical 3D space: It has 6 DOFs: +

+ + +

+ +Now consider some examples of objects with joints: +

+ + +

+ +You'll understand that, when creating any type of joint, it is important to correctly specify the DOFs that the joint restricts, and the DOFs that the joint allows. For the typical DOF of a ragDoll character's limbs, jME even offers a special joint, ConeJoint. +

+ +
+ +

Creating the Joint

+
+ +

+ +You create the HingeJoint after you have created the nodes that are to be chained together. In the code snippet you see that the HingeJoint constructor requires the two node objects. You also have to specify axes and pivots ??? they are the degrees of freedom that you just heard about. +

+
private HingeJoint joint;
+...
+  public void simpleInitApp() {
+    ...
+    // hookNode and pendulumNode are created here...
+    ...
+ 
+    joint=new HingeJoint(hookNode.getControl(RigidBodyControl.class), // A
+                     pendulumNode.getControl(RigidBodyControl.class), // B
+                     new Vector3f(0f, 0f, 0f),  // pivot point local to A
+                     new Vector3f(0f, 1f, 0f),  // pivot point local to B 
+                     Vector3f.UNIT_Z,           // DoF Axis of A (Z axis)
+                     Vector3f.UNIT_Z  );        // DoF Axis of B (Z axis)
+ +

+ +The pivot point's position will be at (0,0,0) in the global 3D space. In A's local space that is at (0,0,0) and in B's local space (remember B's position was set to (0,-1,0)) that is at (0,1,0). +

+ +

+Specify the following parameters for each joint: +

+ + +

+ +Remember to add all joint objects to the physicsSpace, just like you would do with any physical objects. +

+
bulletAppState.getPhysicsSpace().add(joint);
+ +

+Tip: If you want the joint to be visible, attach a geometry to the dynamic node, and translate it to its start position. +

+ +
+ +

Apply Physical Forces

+
+ +

+ +You can apply forces to dynamic nodes (the ones that have a mass), and see how other joined ("chained") objects are dragged along. +

+ +

+Alternatively, you can also apply forces to the joint itself. In a game, you may want to spin an automatic revolving door, or slam a door closed in a spooky way, or dramatically open the lid of a treasure chest. +

+ +

+The method to call on the joint is enableMotor(). +

+
joint.enableMotor(true, 1, .1f);
+joint.enableMotor(true, -1, .1f);
+
    +
  1. Switch the motor on by supplying true
    +
  2. +
  3. Specify the velocity with which the joint should rotate around the specified axis.
    +
      +
    • Use positive and negative numbers to change direction.
      +
    • +
    +
  4. +
  5. Specify the impulse for this motor. Heavier masses need a bigger impulse to be moved.
    +
  6. +
+ +

+ +When you disable the motor, the chained nodes are exposed to gravity again: + +

+
joint.enableMotor(false, 0, 0);
+
+ documentation, + physics, + joint +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/house-directionallight.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/house-directionallight.png new file mode 100644 index 000000000..3cd00edf6 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/house-directionallight.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hud.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hud.html new file mode 100644 index 000000000..2a4025a0b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/hud.html @@ -0,0 +1,220 @@ + +

Head-Up Display (HUD)

+
+ +

+ + +

+ +

+A HUD (Head-Up Display) is part of a game's visual user interface. It's an overlay that displays additional information as (typically) 2-dimensional text or icons on the screen, on top of the 3D scene. Not all games have, or need a HUD. To avoid breaking the immersion and cluttering the screen, only use a HUD if it is the only way to convey certain information. +

+ +

+HUDs are used to supply players with essential information about the game state. +

+ + +

+ +You have two options how to create HUDs. + +

+
+ + + + + + + + + +
OptionProsCons
Attach elements to default guiNode:Easy to learn. jMonkeyEngine built-in API for attaching plain images and bitmap text.Only basic features.
+You will have to write custom controls / buttons / effects if you need them.
Use advanced Nifty GUI integration:Full-featured interactive user interface.
+Includes buttons, effects, controls.
+Supports XML and Java layouts.
Steeper learning curve.
+ +

+ +Using the GUI Node is the default approach in jme3 to create simple HUDs. If you just quickly want to display a line of text, or a simple icon on the screen, use the no-frills GUI Node, it's easier. +

+ +
+ +

Simple HUD: GUI Node

+
+ +

+ +You already know the rootNode that holds the 3-dimensional scene graph. jME3 also offers a 2-dimension (orthogonal) node, the guiNode. +

+ +

+This is how you use the guiNode for HUDs: +

+ + +

+The BitmapTexts and Pictures appear as 2 dimensional element on the screen. +

+ +

+

By default, the guiNode has some scene graph statistics attached. To clear the guiNode before you attach your own GUI elements, use the following methods: +

+
setDisplayStatView(false); setDisplayFps(false);
+ +

+ +

+

+ +
+ +

Displaying Pictures in the HUD

+
+ +

+ +A simple image can be displayed using com.jme3.ui.Picture. +

+
Picture pic = new Picture("HUD Picture");
+pic.setImage(assetManager, "Textures/ColoredTex/Monkey.png", true);
+pic.setWidth(settings.getWidth()/2);
+pic.setHeight(settings.getHeight()/2);
+pic.setPosition(settings.getWidth()/4, settings.getHeight()/4);
+guiNode.attachChild(pic);
+ +

+When you set the last boolean in setImage() to true, the alpha channel of your image is rendered transparent/translucent. +

+ +
+ +

Displaying Text in the HUD

+
+ +

+ +You use com.jme3.font.BitmapText to display text on the screen. +

+
BitmapText hudText = new BitmapText(guiFont, false);          
+hudText.setSize(guiFont.getCharSet().getRenderedSize());      // font size
+hudText.setColor(ColorRGBA.Blue);                             // font color
+hudText.setText("You can write any string here");             // the text
+hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position
+guiNode.attachChild(hudText);
+ +

+The BitmapFont object guiFont is a default font provided by SimpleApplication. Copy you own fonts as .fnt plus .png files into the assets/Interface/Fonts directory and load them like this: +

+
BitmapFont myFont = assetManager.loadFont("Interface/Fonts/Console.fnt");
+hudText = new BitmapText(myFont, false);
+ +
+ +

Positioning HUD Elements

+
+ + +
+ +

Displaying Geometries in the HUD

+
+ +

+ +It is technically possible to attach Quads and 3D Geometries to the HUD. They show up as flat, static GUI elements. The size unit for the guiNode is pixels, not world units. If you attach a Geometry that uses a lit Material, you must add a light to the guiNode. +

+ +

+

If you don't see an attached object in the GUI, check it's position and material (add a light to guiNode). Also verify whether it is not too tiny to be seen. For comparison: A 1 world-unit wide cube is only 1 pixel wide when attached to the guiNode! You may need to scale it bigger. +

+

+ +
+ +

Keeping the HUD Up-To-Date

+
+ +

+ +Use the update loop to keep the content up-to-date. +

+
public void simpleUpdate(float tpf) {
+  ...
+  hudText.setText("Score: " + score);
+  ...
+  picture.setImage(assetManager, "Interface/statechange.png", true);
+  ...
+}
+ +
+ +

Advanced HUD: Nifty GUI

+
+ +

+ +The recommended approach to create HUDs is using Nifty GUI. +

+
    +
  1. Lay out the GUI in one or several Nifty XML or Java files.
    +
  2. +
  3. Write the controller classes in Java.
    +
  4. +
  5. Load the XML file with the controller object in your game's simpleInit() method.
    +
  6. +
+ +

+ +The advantage of Nifty GUI is that it is well integrated into jME and the jMonkeyEngine SDK, and that it offers all the features that you expect from a professional modern user interface. +

+ +

+For HUDs, you basically follow the same instructions as for creating a normal Nifty GUI, you just don't pause the game while the HUD is up. +

+ +
+ +

See also

+
+ +
+ gui, + display, + documentation, + hud +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/inner1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/inner1.png new file mode 100644 index 000000000..ec0ce9d68 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/inner1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html new file mode 100644 index 000000000..6068ef1ce --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html @@ -0,0 +1,388 @@ + +

Input Handling

+
+ +

+ +Users interact with your jME3 application with different input devices ??? the mouse, the keyboard, or a joystick. To respond to inputs we use the inputManager object in SimpleApplication. +

+ +

+This is how you add interaction to your game: + +

+
    +
  1. For each action, choose the trigger(s) (a key or mouse click etc)
    +
  2. +
  3. For each action, add a trigger mapping to the inputManager
    +
  4. +
  5. Create at least one listener in SimpleApplication
    +
  6. +
  7. For each action, register its mappings to a listener
    +
  8. +
  9. Implement each action in the listener
    +
  10. +
+ +
+ +

Code Samples

+
+ + +
+ +

1. Choose Trigger

+
+ +

+ +Choose one or several key/mouse events for the interaction. We use KeyTrigger, MouseAxisTrigger, MouseButtonTrigger, JoyAxisTrigger and JoyButtonTrigger constants from the com.jme3.input.controls package. +

+ +

+Note: The MouseAxis and JoyAxis triggers go along the X axis (right/left) or Y axis (up/down). These Triggers come with extra booleans for the negative half of the axis (left, down). Remember to write code that listens to the negative (true) and positive (false) axis! + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Trigger Code
Mouse button: Left Click MouseButtonTrigger(MouseInput.BUTTON_LEFT)
Mouse button: Right Click MouseButtonTrigger(MouseInput.BUTTON_RIGHT)
Mouse button: Middle Click MouseButtonTrigger(MouseInput.BUTTON_MIDDLE)
Mouse movement: Right MouseAxisTrigger(MouseInput.AXIS_X, true)
Mouse movement: Left MouseAxisTrigger(MouseInput.AXIS_X, false)
Mouse movement: Up MouseAxisTrigger(MouseInput.AXIS_Y, true)
Mouse movement: Down MouseAxisTrigger(MouseInput.AXIS_Y, false)
Mouse wheel: Up MouseAxisTrigger(MouseInput.AXIS_WHEEL,false)
Mouse wheel: Down MouseAxisTrigger(MouseInput.AXIS_WHEEL,true)
NumPad: 1, 2, 3, ??? KeyTrigger(KeyInput.KEY_NUMPAD1) ???
Keyboard: 1, 2 , 3, ??? KeyTrigger(KeyInput.KEY_1) ???
Keyboard: A, B, C, ??? KeyTrigger(KeyInput.KEY_A) ???
Keyboard: Spacebar KeyTrigger(KeyInput.KEY_SPACE)
Keyboard: Shift KeyTrigger(KeyInput.KEY_RSHIFT),
+KeyTrigger(KeyInput.KEY_LSHIFT)
Keyboard: F1, F2, ??? KeyTrigger(KeyInput.KEY_F1) ???
Keyboard: Return, Enter KeyTrigger(KeyInput.KEY_RETURN),
+KeyTrigger(KeyInput.KEY_NUMPADENTER)
Keyboard: PageUp, PageDown KeyTrigger(KeyInput.KEY_PGUP),
+KeyTrigger(KeyInput.KEY_PGDN)
Keyboard: Delete, Backspace KeyTrigger(KeyInput.KEY_BACK),
+KeyTrigger(KeyInput.KEY_DELETE)
Keyboard: Escape KeyTrigger(KeyInput.KEY_ESCAPE)
Keyboard: Arrows KeyTrigger(KeyInput.KEY_DOWN),
+KeyTrigger(KeyInput.KEY_UP)
+KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT)
Joystick Button: JoyButtonTrigger(0, JoyInput.AXIS_POV_X),
+JoyButtonTrigger(0, JoyInput.AXIS_POV_Y) ?
Joystick Movement: Right JoyAxisTrigger(0, JoyInput.AXIS_POV_X, true)
Joystick Movement: Left JoyAxisTrigger(0, JoyInput.AXIS_POV_X, false)
Joystick Movement: Forward JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, true)
Joystick Movement: Backward JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, false)
+ +

+ +In your IDE, use code completion to quickly look up Trigger literals. In the jMonkeyEngine SDK for example, press ctrl-space or ctrl-/ after KeyInput.| to choose from the list of all keys. +

+ +
+ +

2. Remove Default Trigger Mappings

+
+
inputManager.deleteMapping( SimpleApplication.INPUT_MAPPING_MEMORY );
+
+ + + + + + + + + + + + + + + +
Default MappingKeyDescription
INPUT_MAPPING_HIDE_STATSF5Hides the statistics in the bottom left.
INPUT_MAPPING_CAMERA_POSKEY_CPrints debug output about the camera.
INPUT_MAPPING_MEMORYKEY_MPrints debug output for memtory usage.
INPUT_MAPPING_EXITKEY_ESCAPECloses the application by calling stop();. Typically you do not remove this, unless you replace it by another way of quitting gracefully.
+ +
+ +

3. Add Custom Trigger Mapping

+
+ +

+ +When initializing the application, add a Mapping for each Trigger. +

+ +

+Give the mapping a meaningful name. The name should reflect the action, not the button/key (because buttons/keys can change). Here some examples: +

+
inputManager.addMapping("Pause Game", new KeyTrigger(KeyInput.KEY_P));
+inputManager.addMapping("Rotate",     new KeyTrigger(KeyInput.KEY_SPACE));
+...
+ +

+There are cases where you may want to provide more then one trigger for one action. For example, some users prefer the WASD keys to navigate, while others prefer the arrow keys. Add several triggers for one mapping, by separating the Trigger objects with commas: +

+
inputManager.addMapping("Left",  new KeyTrigger(KeyInput.KEY_A), 
+                                 new KeyTrigger(KeyInput.KEY_LEFT)); // A and left arrow
+inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D), 
+                                 new KeyTrigger(KeyInput.KEY_RIGHT)); // D and right arrow
+                                 ...
+ +
+ +

4. Create Listeners

+
+ +

+ +The jME3 input manager supports two types of event listeners for inputs: AnalogListener and ActionListener. You can use one or both listeners in the same application. Add one or both of the following code snippets to your main SimpleApplication-based class to activate the listeners. +

+ +

+Note: The two input listeners do not know, and do not care, which actual key was pressed. They only know which named input mapping was triggered. +

+ +
+ +

ActionListener

+
+ +

+ +com.jme3.input.controls.ActionListener +

+ +
private ActionListener actionListener = new ActionListener() {
+  public void onAction(String name, boolean keyPressed, float tpf) {
+     /** TODO: test for mapping names and implement actions */
+  }
+};
+ +
+ +

AnalogListener

+
+ +

+ +com.jme3.input.controls.AnalogListener +

+ +
private AnalogListener analogListener = new AnalogListener() {
+  public void onAnalog(String name, float keyPressed, float tpf) {
+     /** TODO: test for mapping names and implement actions */
+  }
+};
+ +
+ +

4. Register Mappings to Listeners

+
+ +

+ +To activate the mappings, you must register them to a Listener. Write your registration code after the code block where you have added the mappings to the inputManager. +

+ +

+In the following example, you register the "Pause Game" mapping to the actionListener object, because pausing a game is in "either/or" decision. +

+
inputManager.addListener(actionListener, new String[]{"Pause Game"});
+ +

+In the following example, you register navigational mappings to the analogListener object, because walking is a continuous action. Players typically keep the key pressed to express continuity, for example when they want to "walk on" or "accelerate". +

+
inputManager.addListener(analogListener, new String[]{"Left", "Right"});
+ +

+As you see, you can add several listeners in one String array. You can call the addListener() method more than once, each time with a subset of your list, if that helps you keep you code tidy. Again, the Listeners do not care about actual which keys are configured, you only register named trigger mappings. +

+ +

+

Did you register an action, but it does not work? Check the string's capitalization and spelling, it's case sensitive! +

+

+ +
+ +

5. Implement Actions in Listeners

+
+ +

+ +You specify the action to be triggered where it says TODO in the Listener code snippets. Typically, you write a series of if/else conditions, testing for all the mapping names, and then calling the respective action. +

+ +

+Make use of the distinction between if and else if in this conditional. +

+ + +
+ +

ActionListener

+
+ +

+ +In the most common case, you want an action to be triggered once, in the moment when the button or key trigger is released. For example, when the player presses a key to open a door, or clicks to pick up an item. For these cases, use an ActionListener and test for && !keyPressed, like shown in the following example. +

+
private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+ 
+      if (name.equals("Pause Game") && !keyPressed) { // test?
+        isRunning = !isRunning;                       // action!
+      } 
+ 
+      if ...
+ 
+    }
+  };
+ +
+ +

AnalogListener

+
+ +

+ +The following example shows how you define actions with an AnalogListener. Thiese actions are triggered continuously, as long (intensity value) as the named key or mouse button is down. Use this listeners for semi-automatic weapons and navigational actions. +

+
private AnalogListener analogListener = new AnalogListener() {
+    public void onAnalog(String name, float value, float tpf) {
+ 
+      if (name.equals("Rotate")) {         // test?
+        player.rotate(0, value*speed, 0);  // action!
+      } 
+ 
+      if ...
+ 
+    }
+  };
+ +
+ +

Let Users Remap Keys

+
+ +

+ +It is likely that your players have different keyboard layouts, are used to "reversed" mouse navigation, or prefer different navigational keys than the ones that you defined. You should create an options screen that lets users customize their mouse/key triggers for your mappings. Replace the trigger literals in the inputManager.addMapping() lines with variables, and load sets of triggers when the game starts. +

+ +

+The abstraction of separating triggers and mappings has the advantage that you can remap triggers easily. Your code only needs to remove and add some trigger mappings. The core of the code (the listeners and actions) remains unchanged. +

+
+ keyinput, + input, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/j3m_material_files.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/j3m_material_files.html new file mode 100644 index 000000000..1568064d4 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/j3m_material_files.html @@ -0,0 +1,362 @@ + +

Saving and Loading Materials with .j3m Files

+
+ +

+ +In the Material Definitions article you learned how to configure Materials programmatically in Java code. If you have certain commonly used Materials that never change, you can clean up the amount of Java code that clutters your init method, by moving material settings into .j3m files. Then later in your code, you only need to call one setter instead of several to apply the material. +

+ +

+If you want to colorize simple shapes (one texture all around), then .j3m are the most easily customizable solution. J3m files can contain texture mapped materials, but as usual you have to create the textures in an external editor, especially if you use UV-mapped textures. +

+ +
+ +

Writing the .j3m File

+
+
    +
  1. For every Material, create a file and give it a name that describes it: e.g. SimpleBump.j3m
    +
  2. +
  3. Place the file in your project's assets/Materials/ directory, e.g. MyGame/src/assets/Materials/SimpleBump.j3m
    +
  4. +
  5. Edit the file and add content using the following Syntax, e.g.:
    Material shiny bumpy rock : Common/MatDefs/Light/Lighting.j3md {
    +     MaterialParameters {
    +         Shininess: 8.0
    +         NormalMap: Textures/bump_rock_normal.png
    +         UseMaterialColors : true
    +         Ambient  : 0.0 0.0 0.0 1.0
    +         Diffuse  : 1.0 1.0 1.0 1.0
    +         Specular : 0.0 0.0 0.0 1.0
    +     }
    +}
    +
    +
  6. +
+ +

+ +How to this file is structured: +

+
    +
  1. Header
    +
      +
    1. Material is a fixed keyword, keep it.
      +
    2. +
    3. shiny bumpy rock is a descriptive string that you can make up. Choose a name to help you remember for what you intend to use this material.
      +
    4. +
    5. After the colon, specify on which Material definition you base this Material.
      +
    6. +
    +
  2. +
  3. Now look up the choosen Material Definition's parameters and their parameter types from the Material table. Add one line for each parameter.
    +
      +
    • For example: The series of four numbers in the example above represent RGBA color values.
      +
    • +
    +
  4. +
  5. Check the detailed syntax reference below if you are unsure.
    +
  6. +
+ +

+ +

In the jMonkeyEngine SDK, use File???New File???Material???Empty Material File to create .j3m files. You can edit .j3m files directly in the SDK. On the other hand, they are plain text files, so you can also create them in any plain text editor. +

+

+ +
+ +

How to Use .j3m Materials

+
+ +

+ +This is how you use the prepared .j3m Material on a Spatial. Since you have saved the .j3m file to your project's Assets directory, the .j3m path is relative to MyGame/src/assets/???. +

+
myGeometry.setMaterial(assetManager.loadMaterial("Materials/SimpleBump.j3m"));
+ +

+Tip: In the jMonkeyEngine SDK, open Windows>Palette and drag the JME Material: Set J3M snippet into your code. +

+ +
+ +

Syntax Reference for .j3m Files

+
+ +
+ +

Paths

+
+ +

+ +Make sure to get the paths to the textures (.png, .jpg) and material definitions (.j3md) right. + +

+ + +
+ +

Data Types

+
+ +

+ +All data types (except Color) are specified in com.jme3.shader.VarType. +"Color" is specified as Vector4 in J3MLoader.java. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NamejME Java class.j3m file syntax
Float (basic Java type) a float (e.g. 0.72) , no comma or parentheses
Vector2 com.jme3.math.Vector2f Two floats, no comma or parentheses
Vector3 com.jme3.math.Vector3f Three floats, no comma or parentheses
Vector4 com.jme3.math.Vector4f Four floats, no comma or parentheses
Texture2D com.jme3.texture.Texture2D Path to texture in assets directory, no quotation marks
Texture3D com.jme3.texture.Texture3D Same as texture 2D except it is interpreted as a 3D texture
TextureCubeMap com.jme3.texture.TextureCubeMap Same as texture 2D except it is interpreted as a cubemap texture
Boolean (basic Java type) true or false
Int (basic Java type) Integer number, no comma or parentheses
Color com.jme3.math.ColorRGBA Four floats, no comma or parentheses
FloatArray (Currently not supported in J3M)
Vector2Array (Currently not supported in J3M)
Vector3Array (Currently not supported in J3M)
Vector4Array (Currently not supported in J3M)
Matrix3 (Currently not supported in J3M)
Matrix4 (Currently not supported in J3M)
Matrix3Array (Currently not supported in J3M)
Matrix4Array (Currently not supported in J3M)
TextureBuffer (Currently not supported in J3M)
TextureArray (Currently not supported in J3M)
+ +
+ +

Flip and Repeat Syntax

+
+ + +
+ +

Syntax for Additional Render States

+
+ + +

+ +See the javadoc for a detailed explanation of render states. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypePurpose
(Boolean) Enable wireframe rendering mode
(Enum: FaceCullMode) Set face culling mode (Off, Front, Back, FrontAndBack)
(Boolean) Enable writing depth to the depth buffer
(Boolean) Enable depth testing
(Enum: BlendMode) Set the blending mode
(Float) Set the alpha testing alpha falloff value (if set, it will enable alpha testing)
(Float, Float) Set the polygon offset factor and units
(Boolean) Enable color writing
(Boolean) Enable point sprite rendering for point meshes
+ +
+ +

Examples

+
+ +
+ +

Example 1: Shiny

+
+
Spatial signpost = (Spatial) assetManager.loadAsset(
+    new OgreMeshKey("Models/Sign Post/Sign Post.mesh.xml", null));
+signpost.setMaterial( assetManager.loadMaterial(
+    new AssetKey("Models/Sign Post/Sign Post.j3m")));
+TangentBinormalGenerator.generate(signpost);
+rootNode.attachChild(signpost);
+ +

+The file assets/Models/Sign Post/Sign Post.j3m contains: +

+
Material Signpost : Common/MatDefs/Light/Lighting.j3md {
+    MaterialParameters {
+         Shininess: 4.0
+         DiffuseMap:  Models/Sign Post/Sign Post.jpg
+         NormalMap:   Models/Sign Post/Sign Post_normal.jpg
+         SpecularMap: Models/Sign Post/Sign Post_specular.jpg
+         UseMaterialColors : true
+         Ambient  : 0.5 0.5 0.5 1.0
+         Diffuse  : 1.0 1.0 1.0 1.0
+         Specular : 1.0 1.0 1.0 1.0
+    }
+}
+ +

+The JPG files are in the same directory, assets/Models/Sign Post/???. +

+ +
+ +

Example 2: Repeating Texture

+
+
Material mat = assetManager.loadMaterial(
+    "Textures/Terrain/Pond/Pond.j3m");
+mat.setColor("Ambient", ColorRGBA.DarkGray);
+mat.setColor("Diffuse", ColorRGBA.White);
+mat.setBoolean("UseMaterialColors", true);
+ +

+The file assets/Textures/Terrain/Pond/Pond.j3m contains: +

+
Material Pong Rock : Common/MatDefs/Light/Lighting.j3md {
+     MaterialParameters {
+         Shininess: 8.0
+         DiffuseMap: Repeat Textures/Terrain/Pond/Pond.png
+         NormalMap:  Repeat Textures/Terrain/Pond/Pond_normal.png
+     }
+}
+ +

+The PNG files are in the same directory, assets/Textures/Terrain/Pond/ +

+ +
+ +

Example 3: Transparent

+
+ +

+ +The file assets/Models/Tree/Leaves.j3m contains: +

+
Material Leaves : Common/MatDefs/Light/Lighting.j3md {
+
+    Transparent On
+
+    MaterialParameters {
+        DiffuseMap : Models/Tree/Leaves.png
+        UseAlpha : true
+        AlphaDiscardThreshold : 0.5
+        UseMaterialColors : true
+        Ambient : .5 .5 .5 .5
+        Diffuse : 0.7 0.7 0.7 1
+        Specular : 0 0 0 1
+        Shininess : 16
+    }
+    AdditionalRenderState {
+        Blend Alpha
+        AlphaTestFalloff 0.50
+        FaceCull Off
+    }
+}
+ +

+The PNG file is in the same directory, assets/Models/Tree/??? +

+ +
+ +

Related Links

+
+ +
+ material, + texture, + file, + sdk, + wireframe, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3_shaders.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3_shaders.html new file mode 100644 index 000000000..620a71d4b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3_shaders.html @@ -0,0 +1,467 @@ + +

JME3 and Shaders

+
+ +

+
+ + +

+ +
+ +

Shaders Basics

+
+ +

+Shaders are sets of instructions that are executed on the GPU. They are used to take advantage of hardware acceleration available on the GPU for rendering purposes.
+ +This paper only covers Vertex and Fragment shaders because they are the only ones supported by JME3 for the moment. But be aware that there are some other types of shaders (geometry, tessellation,???).
+ +There are multiple frequently used languages that you may encounter to code shaders but as JME3 is based on OpenGL, shaders in JME use GLSL and any example in this paper will be written in GLSL.
+ +
+ + +

+ +
+ +

How Does it work?

+
+ +

+To keep it Simple: The Vertex shader is executed once for each vertex in the view, then the Fragment shader (also called the Pixel shader) is executed once for each pixel on the screen.
+ +The main purpose of the Vertex shader is to compute the screen coordinate of a vertex (where this vertex will be displayed on screen) while the main purpose of the Fragment shader is to compute the color of a pixel.
+ +This is a very simplified graphic to describe the call stack:
+ +
+ +The main program sends mesh data to the vertex shader (vertex position in object space, normals, tangents, etc..). The vertex shader computes the screen position of the vertex and sends it to the Fragment shader. The fragment shader computes the color, and the result is displayed on screen or in a texture. +
+ + +

+ +
+ +

Variables scope

+
+ +

+There are different types of scope for variables in a shader : +

+ + +

+There is a large panel of variable types to be used, for more information about it I recommend reading the GLSL specification .
+ +
+ + +

+ +
+ +

Spaces and Matrices

+
+ +

+To understand the coming example you must know about the different spaces in 3D computer graphics, and the matrices used to translate coordinate from one space to another.
+ +
+ +The engine passes the object space coordinates to the vertex shader. We need to compute its position in projection space. To do that we transform the object space position by the WorldViewProjectionMatrix which is a combination of the World, View, Projection matrices (who would have guessed?).
+ +
+ + +

+ +
+ +

Simple example : rendering a solid color on an object

+
+ +

+Here is the simplest application to shaders, rendering a solid color.
+ +Vertex Shader :
+ + +

+
//the global uniform World view projection matrix
+//(more on global uniforms below)
+uniform mat4 g_WorldViewProjectionMatrix;
+//The attribute inPosition is the Object space position of the vertex
+attribute vec3 inPosition;
+void main(){
+    //Transformation of the object space coordinate to projection space
+    //coordinates.
+    //- gl_Position is the standard GLSL variable holding projection space
+    //position. It must be filled in the vertex shader
+    //- To convert position we multiply the worldViewProjectionMatrix by
+    //by the position vector.
+    //The multiplication must be done in this order.
+    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
+}
+ +

+ +Fragment Shader :
+ + +

+
void main(){
+    //returning the color of the pixel (here solid blue)
+    //- gl_FragColor is the standard GLSL variable that holds the pixel
+    //color. It must be filled in the Fragment Shader.
+    gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
+}
+ +

+ +For example applying this shader to a sphere would render a solid blue sphere on screen.
+ +
+ + +

+ +
+ +

How to use shaders in JME3

+
+ +

+You probably heard that JME3 is ???shader oriented???, but what does that mean?
+ +Usually to use shaders you must create what is called a program. This program specify the vertex shader and the fragment shader to use.
+ +JME3 encloses this in the material system. Every material in JME3 uses shaders.
+ +For example let???s have a look at the SolidColor.j3md file :
+ + +

+
MaterialDef Solid Color {
+    //This is the complete list of user defined uniforms to be used in the
+    //shaders
+    MaterialParameters {
+        Vector4 Color
+    }
+    Technique {
+        //This is where the vertex and fragment shader files are
+        //specified
+        VertexShader GLSL100:   Common/MatDefs/Misc/SolidColor.vert
+        FragmentShader GLSL100: Common/MatDefs/Misc/SolidColor.frag
+        //This is where you specify which global uniform you need for your
+        //shaders
+        WorldParameters {
+            WorldViewProjectionMatrix
+        }
+    }
+    Technique FixedFunc {
+    }
+}
+ +

+ +For more information on JME3 material system, i suggest you read this .
+ +
+ + +

+ +
+ +

JME3 Global uniforms

+
+ +

+JME3 can expose pre-computed global uniforms to your shaders. You must specify the one that are required for your shader in the WorldParameters section of the material definition file (.j3md).
+ +Note that in the shader the uniform names will be prefixed by a ???g_???.
+ +In the example above, WorldViewProjectionMatrix is declared as uniform mat4 g_WorldViewProjectionMatrix in the shader.
+ +The complete list of global uniforms that can be used in JME3 can be found .
+ +
+ + +

+ +
+ +

JME3 Lighting Global uniforms

+
+ +

+JME3 uses some global uniforms for lighting : +

+ + +

+these uniforms are passed to the shader without having to declare them in the j3md file, but you have to specify in the technique definition " LightMode MultiPass" see lighting.j3md for more information. +
+ + +

+ +
+ +

JME3 attributes

+
+ +

+Those are different attributes that are always passed to your shader.
+ +You can find a complete list of those attribute in the Type enum of the VertexBuffer .
+ +Note that in the shader the attributes names will be prefixed by an ???in???.
+ +
+ +When the enumeration lists some usual types for each attribute (for example texCoord specifies two floats) then that is the format expected by all standard JME3 shaders that use that attribute. When writing your own shaders though you can use alternative formats such as placing three floats in texCoord simply by declaring the attribute as vec3 in the shader and passing 3 as the component count into the mesh setBuffer call. +

+ +
+ +

User's uniforms

+
+ +

+At some point when making your own shader you'll need to pass your own uniforms
+ +Any uniform has to be declared in the material definition file (.j3md) in the "MaterialParameters" section.
+ + +

+
    MaterialParameters {
+        Vector4 Color
+        Texture2D ColorMap
+    }
+ +

+You can also pass some define to your vertex/fragment programs to know if an uniform as been declared.
+ +You simply add it in the Defines section of your Technique in the definition file.
+ + +

+
    Defines {
+        COLORMAP : ColorMap
+    }
+ +

+For integer and floating point parameters, the define will contain the value that was set.
+ +For all other types of parameters, the value 1 is defined.
+ +If no value is set for that parameter, the define is not declared in the shader.
+ +

+ +

+Those material parameters will be sent from the engine to the shader as follows, +there are setXXXX methods for any type of uniform you want to pass.
+ + +

+
   material.setColor("Color", new ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f); // red color
+   material.setTexture("ColorMap", myTexture); // bind myTexture for that sampler uniform
+ +

+To use this uniform in the shader, you need to declare it in the .frag or .vert files (depending on where you need it). +You can make use of the defines here and later in the code: +Note that the "m_" prefix specifies that the uniform is a material parameter.
+ + +

+
   uniform vec4 m_Color;
+   #ifdef COLORMAP
+     uniform sampler2D m_ColorMap;
+   #endif
+ +

+The uniforms will be populated at runtime with the value you sent. +

+ +
+ +

Example: Adding Color Keying to the Lighting.j3md Material Definition

+
+ +

+Color Keying is useful in games involving many players. It consists of adding some
+ +player-specific color on models textures.
+ +The easiest way of doing this is to use a keyMap which will contain the amount of
+ +color to add in its alpha channel.
+ +Here I will use this color map:
+ +to blend color on this texture:
+ +
+ +We need to pass 2 new parameters to the Lighting.j3md definition, MaterialParameters section : + +

+
// Keying Map
+Texture2D KeyMap
+ 
+// Key Color 
+Color KeyColor
+ +

+Below, add a new Define in the main Technique section: + +

+
KEYMAP : KeyMap
+ +

+In the Lighting.frag file, define the new uniforms: + +

+
#ifdef KEYMAP
+  uniform sampler2D m_KeyMap;
+  uniform vec4 m_KeyColor;
+#endif
+ +

+Further, when obtaining the diffuseColor from the DiffuseMap texture, check +if we need to blend it: + +

+
    #ifdef KEYMAP
+      vec4 keyColor = texture2D(m_KeyMap, newTexCoord);
+      diffuseColor.rgb = (1.0-keyColor.a) * diffuseColor.rgb + keyColor.a * m_KeyColor.rgb;
+    #endif
+ +

+This way, a transparent pixel in the KeyMap texture doesn't modify the color.
+ +A black pixel replaces it for the m_KeyColor and values in between are blended.
+ +
+ +A result preview can be seen here: +

+ +
+ +

Step by step

+
+ +
    // A cube
+    Box box= new Box(Vector3f.ZERO, 1f,1f,1f);
+    Geometry cube = new Geometry("box", box);
+    Material mat = new Material(assetManager,"Path/To/My/materialDef.j3md");
+    cube.setMaterial(mat);
+    rootNode.attachChild(cube);
+ +

+ +
+ + +

+ +
+ +

JME3 and OpenGL 3 & 4 compatibility

+
+ +

+GLSL 1.0 to 1.2 comes with built in attributes and uniforms (ie, gl_Vertex, gl_ModelViewMatrix, etc???).
+Those attributes are deprecated since GLSL 1.3 (opengl 3), hence JME3 global uniforms and attributes. Here is a list of deprecated attributes and their equivalent in JME3
+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
GLSL 1.2 attributesJME3 equivalent
gl_Vertex inPosition
gl_Normal inNormal
gl_Color inColor
gl_MultiTexCoord0 inTexCoord
gl_ModelViewMatrix g_WorldViewMatrix
gl_ProjectionMatrix g_ProjectionMatrix
gl_ModelViewProjectionMatrix g_WorldViewProjectionMatrix
gl_NormalMatrix g_NormalMatrix
+ +
+ +

Useful links

+
+ +

+ +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders-1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders-1.png new file mode 100644 index 000000000..127d16b3d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders-1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders.png new file mode 100644 index 000000000..6e240a0af Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/jme3andshaders.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-scattering-filter.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-scattering-filter.png new file mode 100644 index 000000000..371760a55 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-scattering-filter.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-sources.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-sources.png new file mode 100644 index 000000000..ac703063f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light-sources.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html new file mode 100644 index 000000000..3861d8965 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html @@ -0,0 +1,380 @@ + +

Light and Shadow

+
+ +

+ + +

+ +

+Light and Shadow are two separate things in 3D engines, although we percieve them together in real life: +

+ + +

+ +

A light source with a direction or location is required for all Geometries with Lighting.j3md-based Materials. An ambient light is not sufficient. In a scene with no appropriate light sources, Geometries with Lighting.j3md-based Materials do not render. Only Geometries with Unshaded.j3md-based Materials are visible independent of any light sources. +

+

+ +
+ +

Light Sources and Colors

+
+ +

+ + +

+ +

+You can add several types of light sources to a scene using rootNode.addLight(mylight). +

+ +

+The available light sources in com.???jme3.???light are: +

+ + +

+ +You control the color and intensity of each light source. Typically you set the color to white (new ColorRGBA(1.0f,1.0f,1.0f,1.0f) or ColorRGBA.White), which makes all scene elements appear in their natural color. +

+ +

+You can choose to use lights in other colors than white, or darker colors. This influences the scene's atmosphere and will make the scene appear colder (e.g. ColorRGBA.Cyan) or warmer (ColorRGBA.Yellow), brighter (higher values) or darker (lower values). +

+ +

+You can get a list of all lights added to a Spatial by calling getWorldLightList() (includes inherited lights) or getLocalLightList() (only directly added lights), and iterating over the result. +

+ +
+ +

PointLight

+
+ +

+ + +

+ +

+A PointLight has a location and shines from there in all directions as far as its radius reaches. The light intensity decreases with increased distance from the light source. A PointLight can at the moment not be used for casting shadows (using the PssmShadowRenderer - read more about this below). +

+ +

+Typical example: Lamp, lightbulb, torch, candle. +

+
PointLight lamp_light = new PointLight();
+lamp_light.setColor(ColorRGBA.Yellow);
+lamp_light.setRadius(4f);
+lamp_light.setPosition(new Vector3f(lamp_geo.getLocalTranslation()));
+rootNode.addLight(lamp_light);
+ +
+ +

DirectionalLight

+
+ +

+ + +

+ +

+A DirectionalLight has no position, only a direction. It sends out parallel beams of light and is considered "infinitely" far away. You typically have one directional light per scene. A DirectionalLight can be used together with shadows. +

+ +

+Typically example: Sun light. +

+
DirectionalLight sun = new DirectionalLight();
+sun.setColor(ColorRGBA.White);
+sun.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal());
+rootNode.addLight(sun);
+ +
+ +

SpotLight

+
+ +

+ + +

+ +

+A SpotLight sends out a distinct beam or cone of light. A SpotLight has a direction, a position, distance (range) and two angles. The inner angle is the central maximum of the light cone, the outer angle the edge of the light cone. Everything outside the light cone's angles is not affected by the light. +

+ +

+Typical Example: Flashlight +

+
SpotLight spot = new SpotLight();
+spot.setSpotRange(100f);                           // distance
+spot.setSpotInnerAngle(15f * FastMath.DEG_TO_RAD); // inner light cone (central beam)
+spot.setSpotOuterAngle(35f * FastMath.DEG_TO_RAD); // outer light cone (edge of the light)
+spot.setColor(ColorRGBA.White.mult(1.3f));         // light color
+spot.setPosition(cam.getLocation());               // shine from camera loc
+spot.setDirection(cam.getDirection());             // shine forward from camera loc
+rootNode.addLight(spot);
+ +

+If you want the spotlight to follow the flycam, repeat the setDirection(???) and setPosition(???) calls in the update loop, and kee syncing them with the camera position and direction. +

+ +
+ +

AmbientLight

+
+ +

+ +An AmbientLight simply influences the brightness and color of the scene globally. It has no direction and no location and shines equally everywhere. An AmbientLight does not cast any shadows, and it lights all sides of Geometries evenly, which makes 3D objects look unnaturally flat; this is why you typically do not use an AmbientLight alone without one of the other lights. +

+ +

+Typical example: Regulate overall brightness, tinge the whole scene in a warm or cold color. +

+
AmbientLight al = new AmbientLight();
+al.setColor(ColorRGBA.White.mult(1.3f));
+rootNode.addLight(al);
+ +

+

You can increase the brightness of a light source gradually by multiplying the light color to values greater than 1.0f.
+Example: mylight.setColor(ColorRGBA.White.mult(1.3f)); +

+

+ +
+ +

Light Follows Spatial

+
+ +

+ +You can use a com.jme3.scene.control.LightControl to make a SpotLight or PointLight follow a Spatial. This can be used for a flashlight being carried by a character, or for car headlights, or an aircraft's spotlight, etc. +

+
PointLight myLight = new PointLight();
+rootNode.addLight(myLight);
+LightControl lightControl = new LightControl(myLight);
+spatial.addControl(lightControl); // this spatial controls the position of this light.
+ +

+Obviously, this does apply to AmbientLights which have no position. +

+ +
+ +

Simple Lighting

+
+ +

+ +Full sample code: +

+ + +

+ +For Simple Lighting we use Geometries with Materials based on Lighting.j3md (learn more about Materials here). Lighting.j3md-based materials dynamically support Shininess, and Ambient, Diffuse, and Specular light if there is a light source present. Note that this lighting method alone does not make the Geometry cast a shadow onto other Geometries automatically (see below for how to add drop shadows etc). +

+ +

+ +

+
Geometry teapot = (Geometry) assetManager.loadModel("Models/Teapot/Teapot.obj");
+TangentBinormalGenerator.generate(teapot.getMesh(), true);
+Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+mat.setBoolean("m_UseMaterialColors", true);
+mat.setColor("m_Ambient",  ColorRGBA.Orange);
+mat.setColor("m_Diffuse",  ColorRGBA.Orange);
+mat.setColor("m_Specular", ColorRGBA.White);
+mat.setFloat("m_Shininess", 12);
+rootNode.attachChild(teapot);
+ +

+The above example uses material colors and no textures. You can of course also use Lighting.j3md to create a lit Material that uses Texture Maps. The following example uses Shininess, Diffuse Map and Normal Map (a.k.a Bump Map). +

+ +

+ +

+
Sphere rock = new Sphere(32,32, 2f);
+Geometry shiny_rock = new Geometry("Shiny rock", rock);
+rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
+TangentBinormalGenerator.generate(rock);           // for lighting effect
+Material mat_lit = new Material(
+    assetManager, "Common/MatDefs/Light/Lighting.j3md");
+mat_lit.setTexture("m_DiffuseMap",                 // surface color
+    assetManager.loadTexture("Textures/Terrain/Pond/Pond.png"));
+mat_lit.setTexture("m_NormalMap",                  // surface bumps
+    assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
+mat_lit.setFloat("m_Shininess", 5f);               // surface smoothness [1,128]
+shiny_rock.setMaterial(mat_lit);
+rootNode.attachChild(shiny_rock);
+ +

+These light effects update live when the object or light source moves. If you shine a colored PointLight at this object, you will see a light reflection tinged in the color of the PointLight. +

+ +
+ +

BasicShadowRenderer

+
+ +

+ +Full code sample +

+ + +

+ +Use the Shadow Renderer to make Geometries with Lighting.j3md-based Materials cast and receive basic drop shadows. This fast and simple implementation of a shadow effect is good for scenes with flat floors, but looks less realistic on uneven terrains. To use it, you add a jME SceneProcessor named com.jme3.shadow.BasicShadowRenderer to the viewPort. +

+ +

+ +

+
BasicShadowRenderer bsr;
+...
+public void simpleInitApp() {
+    ...
+    bsr = new BasicShadowRenderer(assetManager, 256);
+    bsr.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
+    viewPort.addProcessor(bsr);
+    ...
+ +

+Shadow calculations (cast and receive) have a performance impact, therefor we recommend to use them smartly. Switch off the default shadow mode for the whole scene graph, and then specify the shadow behaviour individually for every scene node that needs shadows: You specifiy whether it casts shadows, receives shadows, both (slower), or neither (faster). +

+
rootNode.setShadowMode(ShadowMode.Off);        // reset all
+wall.setShadowMode(ShadowMode.CastAndReceive); // normal behaviour (slow)
+floor.setShadowMode(ShadowMode.Receive);       // can't see shadow cast below floor anyway...
+airplane.setShadowMode(ShadowMode.Cast);       // nothing casts shadows onto airplane anyway...
+ghost.setShadowMode(ShadowMode.Off);           // ghost is translucent anyway...
+ +
+ +

Parallel-Split Shadow Map

+
+ +

+ +Full sample code +

+ + +

+ +The more advanced PSSM shadow renderer can cast real-time shadows, even on curved surfaces such as terrains. It is a bit more resource hungry than the BasicShadowRenderer. To activate PSSM drop shadows, add a jME SceneProcessor named com.jme3.shadow.PssmShadowRenderer to the viewPort. PSSM stands for the Parallel-Split Shadow Map technique. +

+ +

+ +

+
private PssmShadowRenderer pssmRenderer;
+...
+public void simpleInitApp() {
+    ....
+    pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
+    pssmRenderer.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
+    viewPort.addProcessor(pssmRenderer);
+ +

+The constructor expects the following values: +

+ + +

+ +You can set the following properties on the pssmRenderer object: +

+ + +

+ +As said above, it's more efficient to specify individual shadow behaviour for each Geometry. +

+
teapot.setShadowMode(ShadowMode.CastAndReceive);
+terrain.setShadowMode(ShadowMode.Receive); 
+ +
+ +

Screen Space Ambient Occlusion

+
+ +

+ +Full sample code +

+ + +

+ +Ambient Occlusion refers to the shadows that nearby objects cast on each other under an ambient lighting. It???s an approximation of how light radiates in a real life scene. To activate Ambient Occlusion shadows, add a jME SceneProcessor named com.jme3.post.SSAOFilter to the viewPort. SSAO stands for the Screen Space Ambient Occlusion technique. +

+
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.92f, 0.33f, 0.61f);
+fpp.addFilter(ssaoFilter);
+viewPort.addProcessor(fpp);
+ +

+ + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loading_screen.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loading_screen.html new file mode 100644 index 000000000..35dad5582 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loading_screen.html @@ -0,0 +1,567 @@ + +

Nifty Loading Screen (Progress Bar)

+
+ +

+ +There is a good tutorial about creating a nifty progress bar here: + +

+ +

+This example will use the existing hello terrain as an example. +It will require these 2 images inside Assets/Interface/ (save them as border.png and inner.png respectively) +

+ +

+ + +

+ +

+This is the progress bar at 90%: +

+ +

+ +

+ +

+nifty_loading.xml + +

+
<?xml version="1.0" encoding="UTF-8"?>
+<nifty>
+    <useStyles filename="nifty-default-styles.xml" />
+    <useControls filename="nifty-default-controls.xml" />
+ 
+    <controlDefinition name = "loadingbar" controller = "jme3test.TestLoadingScreen">
+        <image filename="Interface/border.png" childLayout="absolute" 
+               imageMode="resize:15,2,15,15,15,2,15,2,15,2,15,15">
+            <image id="progressbar" x="0" y="0" filename="Interface/inner.png" width="32px" height="100%"
+                   imageMode="resize:15,2,15,15,15,2,15,2,15,2,15,15" />
+        </image>
+    </controlDefinition>
+ 
+    <screen id="start" controller = "jme3test.TestLoadingScreen">
+        <layer id="layer" childLayout="center">
+            <panel id = "panel2" height="30%" width="50%" align="center" valign="center" childLayout="vertical"
+                   visibleToMouse="true">
+                <control id="startGame" name="button" backgroundColor="#0000" label="Load Game" align="center">
+                    <interact onClick="showLoadingMenu()" />
+                </control>
+            </panel>
+        </layer>
+    </screen>
+ 
+    <screen id="loadlevel" controller = "jme3test.TestLoadingScreen">
+        <layer id="loadinglayer" childLayout="center" backgroundColor="#000000">
+            <panel id = "loadingpanel" childLayout="vertical" align="center" valign="center" height="32px" width="70%">
+                <control name="loadingbar" align="center" valign="center" width="100%" height="100%" />
+                <control id="loadingtext" name="label" align="center" 
+                         text="                                                  "/>
+            </panel>
+        </layer>
+    </screen>
+ 
+    <screen id="end" controller = "jme3test.TestLoadingScreen">
+    </screen>
+ 
+</nifty>
+ +
+ +

Understanding Nifty XML

+
+ +

+ +The progress bar and text is done statically using nifty XML. +A custom control is created, which represents the progress bar. + +

+
    <controlDefinition name = "loadingbar" controller = "jme3test.TestLoadingScreen">
+        <image filename="Interface/border.png" childLayout="absolute" 
+               imageMode="resize:15,2,15,15,15,2,15,2,15,2,15,15">
+            <image id="progressbar" x="0" y="0" filename="Interface/inner.png" width="32px" height="100%"
+                   imageMode="resize:15,2,15,15,15,2,15,2,15,2,15,15"/>
+        </image>
+    </controlDefinition>
+ +

+This screen simply displays a button in the middle of the screen, which could be seen as a simple main menu UI. + +

+
    <screen id="start" controller = "jme3test.TestLoadingScreen">
+        <layer id="layer" childLayout="center">
+            <panel id = "panel2" height="30%" width="50%" align="center" valign="center" childLayout="vertical"
+                   visibleToMouse="true">
+                <control id="startGame" name="button" backgroundColor="#0000" label="Load Game" align="center">
+                    <interact onClick="showLoadingMenu()" />
+                </control>
+            </panel>
+        </layer>
+    </screen>
+ +

+This screen displays our custom progress bar control with a text control + +

+
    <screen id="loadlevel" controller = "jme3test.TestLoadingScreen">
+        <layer id="loadinglayer" childLayout="center" backgroundColor="#000000">
+            <panel id = "loadingpanel" childLayout="vertical" align="center" valign="center" height="32px" width="400px">
+                <control name="loadingbar" align="center" valign="center" width="400px" height="32px" />
+                <control id="loadingtext" name="label" align="center"
+                          text="                                                  "/>
+            </panel>
+        </layer>
+    </screen>
+ +
+ +

Creating the bindings to use the Nifty XML

+
+ +

+There are 3 main ways to update a progress bar. To understand why these methods are necessary, an understanding of the graphics pipeline is needed. +

+ +

+Something like this in a single thread will not work: + +

+
load_scene();
+update_bar(30%);
+load_characters();
+update_bar(60%);
+load_sounds();
+update_bar(100%);
+ +

+ +If you do all of this in a single frame, then it is sent to the graphics card only after the whole code block has executed. By this time the bar has reached 100% and the game has already begun ??? for the user, the progressbar on the screen would not have visibly changed. +

+ +

+The 2 main good solutions are: +

+
    +
  1. Updating explicitly over many frames
    +
  2. +
  3. Multi-threading
    +
  4. +
+ +
+ +

Updating progress bar over a number of frames

+
+ +

+ +The idea is to break down the loading of the game into discrete parts + +

+
package jme3test;
+ 
+import com.jme3.niftygui.NiftyJmeDisplay;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.elements.Element;
+import de.lessvoid.nifty.input.NiftyInputEvent;
+import de.lessvoid.nifty.screen.Screen;
+import de.lessvoid.nifty.screen.ScreenController;
+import de.lessvoid.nifty.tools.SizeValue;
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import de.lessvoid.nifty.controls.Controller;
+import de.lessvoid.nifty.elements.render.TextRenderer;
+import de.lessvoid.xml.xpp3.Attributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import jme3tools.converters.ImageToAwt;
+ 
+public class TestLoadingScreen extends SimpleApplication implements ScreenController, Controller {
+ 
+    private NiftyJmeDisplay niftyDisplay;
+    private Nifty nifty;
+    private Element progressBarElement;
+    private TerrainQuad terrain;
+    private Material mat_terrain;
+    private float frameCount = 0;
+    private boolean load = false;
+    private TextRenderer textRenderer;
+ 
+    public static void main(String[] args) {
+        TestLoadingScreen app = new TestLoadingScreen();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+        flyCam.setEnabled(false);
+        niftyDisplay = new NiftyJmeDisplay(assetManager,
+                inputManager,
+                audioRenderer,
+                guiViewPort);
+        nifty = niftyDisplay.getNifty();
+ 
+        nifty.fromXml("Interface/nifty_loading.xml", "start", this);
+ 
+        guiViewPort.addProcessor(niftyDisplay);
+    }
+ 
+    @Override
+    public void simpleUpdate(float tpf) {
+ 
+        if (load) { //loading is done over many frames
+            if (frameCount == 1) {
+                Element element = nifty.getScreen("loadlevel").findElementByName("loadingtext");
+                textRenderer = element.getRenderer(TextRenderer.class);
+ 
+                mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+                mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+                setProgress(0.2f, "Loading grass");
+ 
+            } else if (frameCount == 2) {
+                Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+                grass.setWrap(WrapMode.Repeat);
+                mat_terrain.setTexture("Tex1", grass);
+                mat_terrain.setFloat("Tex1Scale", 64f);
+                setProgress(0.4f, "Loading dirt");
+ 
+            } else if (frameCount == 3) {
+                Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ 
+                dirt.setWrap(WrapMode.Repeat);
+                mat_terrain.setTexture("Tex2", dirt);
+                mat_terrain.setFloat("Tex2Scale", 32f);
+                setProgress(0.5f, "Loading rocks");
+ 
+            } else if (frameCount == 4) {
+                Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ 
+                rock.setWrap(WrapMode.Repeat);
+ 
+                mat_terrain.setTexture("Tex3", rock);
+                mat_terrain.setFloat("Tex3Scale", 128f);
+                setProgress(0.6f, "Creating terrain");
+ 
+            } else if (frameCount == 5) {
+                AbstractHeightMap heightmap = null;
+                Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+                heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+ 
+                heightmap.load();
+                terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
+                setProgress(0.8f, "Positioning terrain");
+ 
+            } else if (frameCount == 6) {
+                terrain.setMaterial(mat_terrain);
+ 
+                terrain.setLocalTranslation(0, -100, 0);
+                terrain.setLocalScale(2f, 1f, 2f);
+                rootNode.attachChild(terrain);
+                setProgress(0.9f, "Loading cameras");
+ 
+            } else if (frameCount == 7) {
+                List<Camera> cameras = new ArrayList<Camera>();
+                cameras.add(getCamera());
+                TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+                terrain.addControl(control);
+                setProgress(1f, "Loading complete");
+ 
+            } else if (frameCount == 8) {
+                nifty.gotoScreen("end");
+                nifty.exit();
+                guiViewPort.removeProcessor(niftyDisplay);
+                flyCam.setEnabled(true);
+                flyCam.setMoveSpeed(50);
+            }
+ 
+            frameCount++;
+        }
+    }
+ 
+    public void setProgress(final float progress, String loadingText) {
+        final int MIN_WIDTH = 32;
+        int pixelWidth = (int) (MIN_WIDTH + (progressBarElement.getParent().getWidth() - MIN_WIDTH) * progress);
+        progressBarElement.setConstraintWidth(new SizeValue(pixelWidth + "px"));
+        progressBarElement.getParent().layoutElements();
+ 
+        textRenderer.setText(loadingText);
+    }
+ 
+    public void showLoadingMenu() {
+        nifty.gotoScreen("loadlevel");
+        load = true;
+    }
+ 
+    @Override
+    public void onStartScreen() {
+    }
+ 
+    @Override
+    public void onEndScreen() {
+    }
+ 
+    @Override
+    public void bind(Nifty nifty, Screen screen) {
+        progressBarElement = nifty.getScreen("loadlevel").findElementByName("progressbar");
+    }
+ 
+    // methods for Controller
+    @Override
+    public boolean inputEvent(final NiftyInputEvent inputEvent) {
+        return false;
+    }
+ 
+    @Override
+    public void bind(Nifty nifty, Screen screen, Element elmnt, Properties prprts, Attributes atrbts) {
+        progressBarElement = elmnt.findElementByName("progressbar");
+    }
+ 
+    @Override
+    public void init(Properties prprts, Attributes atrbts) {
+    }
+ 
+    public void onFocus(boolean getFocus) {
+    }
+}
+ +

+Note: +

+ + +
+ +

Using multithreading

+
+ +

+For more info on multithreading: +

+ +

+Make sure to change the XML file to point the controller to TestLoadingScreen1 + +

+
package jme3test;
+ 
+import com.jme3.niftygui.NiftyJmeDisplay;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.elements.Element;
+import de.lessvoid.nifty.input.NiftyInputEvent;
+import de.lessvoid.nifty.screen.Screen;
+import de.lessvoid.nifty.screen.ScreenController;
+import de.lessvoid.nifty.tools.SizeValue;
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import de.lessvoid.nifty.controls.Controller;
+import de.lessvoid.nifty.elements.render.TextRenderer;
+import de.lessvoid.xml.xpp3.Attributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import jme3tools.converters.ImageToAwt;
+ 
+public class TestLoadingScreen1 extends SimpleApplication implements ScreenController, Controller {
+ 
+    private NiftyJmeDisplay niftyDisplay;
+    private Nifty nifty;
+    private Element progressBarElement;
+    private TerrainQuad terrain;
+    private Material mat_terrain;
+    private boolean load = false;
+    private ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(2);
+    private Future loadFuture = null;
+    private TextRenderer textRenderer;
+ 
+    public static void main(String[] args) {
+        TestLoadingScreen1 app = new TestLoadingScreen1();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+        flyCam.setEnabled(false);
+        niftyDisplay = new NiftyJmeDisplay(assetManager,
+                inputManager,
+                audioRenderer,
+                guiViewPort);
+        nifty = niftyDisplay.getNifty();
+ 
+        nifty.fromXml("Interface/nifty_loading.xml", "start", this);
+ 
+        guiViewPort.addProcessor(niftyDisplay);
+    }
+ 
+    @Override
+    public void simpleUpdate(float tpf) {
+        if (load) {
+            if (loadFuture == null) {
+                //if we have not started loading yet, submit the Callable to the executor
+                loadFuture = exec.submit(loadingCallable);
+            }
+            //check if the execution on the other thread is done
+            if (loadFuture.isDone()) {
+                //these calls have to be done on the update loop thread, 
+                //especially attaching the terrain to the rootNode
+                //after it is attached, it's managed by the update loop thread 
+                // and may not be modified from any other thread anymore!
+                nifty.gotoScreen("end");
+                nifty.exit();
+                guiViewPort.removeProcessor(niftyDisplay);
+                flyCam.setEnabled(true);
+                flyCam.setMoveSpeed(50);
+                rootNode.attachChild(terrain);
+                load = false;
+            }
+        }
+    }
+    //this is the callable that contains the code that is run on the other thread.
+    //since the assetmananger is threadsafe, it can be used to load data from any thread
+    //we do *not* attach the objects to the rootNode here!
+    Callable<Void> loadingCallable = new Callable<Void>() {
+ 
+        public Void call() {
+ 
+            Element element = nifty.getScreen("loadlevel").findElementByName("loadingtext");
+            textRenderer = element.getRenderer(TextRenderer.class);
+ 
+            mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+            mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+            //setProgress is thread safe (see below)
+            setProgress(0.2f, "Loading grass");
+ 
+            Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+            grass.setWrap(WrapMode.Repeat);
+            mat_terrain.setTexture("Tex1", grass);
+            mat_terrain.setFloat("Tex1Scale", 64f);
+            setProgress(0.4f, "Loading dirt");
+ 
+            Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+ 
+            dirt.setWrap(WrapMode.Repeat);
+            mat_terrain.setTexture("Tex2", dirt);
+            mat_terrain.setFloat("Tex2Scale", 32f);
+            setProgress(0.5f, "Loading rocks");
+ 
+            Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+ 
+            rock.setWrap(WrapMode.Repeat);
+ 
+            mat_terrain.setTexture("Tex3", rock);
+            mat_terrain.setFloat("Tex3Scale", 128f);
+            setProgress(0.6f, "Creating terrain");
+ 
+            AbstractHeightMap heightmap = null;
+            Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+            heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+ 
+            heightmap.load();
+            terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
+            setProgress(0.8f, "Positioning terrain");
+ 
+            terrain.setMaterial(mat_terrain);
+ 
+            terrain.setLocalTranslation(0, -100, 0);
+            terrain.setLocalScale(2f, 1f, 2f);
+            setProgress(0.9f, "Loading cameras");
+ 
+            List<Camera> cameras = new ArrayList<Camera>();
+            cameras.add(getCamera());
+            TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+            terrain.addControl(control);
+            setProgress(1f, "Loading complete");
+ 
+            return null;
+        }
+    };
+ 
+    public void setProgress(final float progress, final String loadingText) {
+        //since this method is called from another thread, we enqueue the changes to the progressbar to the update loop thread
+        enqueue(new Callable() {
+ 
+            public Object call() throws Exception {
+                final int MIN_WIDTH = 32;
+                int pixelWidth = (int) (MIN_WIDTH + (progressBarElement.getParent().getWidth() - MIN_WIDTH) * progress);
+                progressBarElement.setConstraintWidth(new SizeValue(pixelWidth + "px"));
+                progressBarElement.getParent().layoutElements();
+ 
+                textRenderer.setText(loadingText);
+                return null;
+            }
+        });
+ 
+    }
+ 
+    public void showLoadingMenu() {
+        nifty.gotoScreen("loadlevel");
+        load = true;
+    }
+ 
+    @Override
+    public void onStartScreen() {
+    }
+ 
+    @Override
+    public void onEndScreen() {
+    }
+ 
+    @Override
+    public void bind(Nifty nifty, Screen screen) {
+        progressBarElement = nifty.getScreen("loadlevel").findElementByName("progressbar");
+    }
+ 
+    // methods for Controller
+    @Override
+    public boolean inputEvent(final NiftyInputEvent inputEvent) {
+        return false;
+    }
+ 
+    @Override
+    public void bind(Nifty nifty, Screen screen, Element elmnt, Properties prprts, Attributes atrbts) {
+        progressBarElement = elmnt.findElementByName("progressbar");
+    }
+ 
+    @Override
+    public void init(Properties prprts, Attributes atrbts) {
+    }
+ 
+    public void onFocus(boolean getFocus) {
+    }
+ 
+ 
+    @Override
+    public void stop() {
+        super.stop();
+        //the pool executor needs to be shut down so the application properly exits.
+        exec.shutdown();
+    }
+}
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loadingscreen.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loadingscreen.png new file mode 100644 index 000000000..fe65da522 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/loadingscreen.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/localization.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/localization.html new file mode 100644 index 000000000..66b9b2b7c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/localization.html @@ -0,0 +1,183 @@ + +

Localizing jME 3 Games

+
+ +
+ +

Scope

+
+ +

+ +Localizing an application can mean several things: + +

+ + +

+ +There are tools that assist you with localizing Java Swing GUIs. jME3 applications do not typically have a Swing GUI, so those tools are not of much help. Just stick to the normal Java rules about using Bundle Properties: +

+ +
+ +

Preparing the Localization

+
+ +

+ +Tip: The jMonkeyEngine SDK supports opening and editing Bundle.properties files. Also note the Tools > Localization menu. +

+ +

+To prepare the application for localization, you have to first identify all hard-coded messages. + +

+
    +
  1. Find every line in your jME3 game where you hard-coded message strings, e.g.
    +
    System.out.print("Hello World!");
    +UiText.setText("Score: "+score);
    +
    +
  2. +
  3. Create one file named Bundle.properties in each directory where there are Java file that contain messages.
    +
  4. +
  5. For every hard-coded message, you add one line to the Bundle.properties file: First specify a unique key that identifies this string; then an equal sign; and the literal string itself.
    +
    greeting=Hello World!
    +score.display=Score: 
    +
    +
  6. +
  7. In the source code, replace every occurence of a hard-coded message with the appropriate Resource Bundle call to its unique key:
    System.out.print(ResourceBundle.getBundle("Bundle").getString("greeting"));
    +UiText.setText(ResourceBundle.getBundle("Bundle").getString("score.display")+score);
    +
    +
  8. +
+ +

+ +The language used in the Bundle.properties files will be the default language for your game. + +

+ +
+ +

Translating the Messages

+
+ +

+ +Each additional language comes in a set of files that is marked with a (usually) two-letter suffix. Common locales are de for German, en for English, fr for French, ja for Japanese, pt for Portuguese, etc. +

+ +

+To translate the messages to another language, for example, German: + +

+
    +
  1. Make a copy of the Bundle.properties files.
    +
  2. +
  3. Name the copy Bundle_de.properties for German. Note the added suffix _de.
    +
  4. +
  5. Translate all strings (text on the right side of the equal sign) in the Bundle_de.properties to German.
    greeting=Hallo Welt!
    +score.display=Spielstand: 
    + +

    + Important: Do not modify any of the keys (text to the left of the equal sign)! +

    +
    +
  6. +
  7. To test the German localization, start the application from the command line with -Duser.language=de. Note the parameter de.
    +
  8. +
+ +

+ +Tip: In the jMonkeyEngine SDK, you set this VM Option in the Project properties under Run. Here you can also save individual run configuraions for each language you want to test. +

+ +

+To get the full list of language suffixes use + +

+
System.out.println(Arrays.toString(Locale.getISOLanguages()));
+ +
+ +

Which Strings Not to Translate

+
+ +

+ +Important: In the Bundle.properties file, do not include any strings that are asset paths, node or geometry names, input mappings, or material layers. +

+ + +

+ +Only localize messages and UI text! +

+ +
+ +

Common Localization Problems

+
+ +

+ +Typical problems include: +

+ + +
+ +

More Documentation

+
+ +

+ + +

+ +

+ + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/logging.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/logging.html new file mode 100644 index 000000000..2bec43b83 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/logging.html @@ -0,0 +1,120 @@ + +

Logging and Monitoring

+
+ +
+ +

Logging Like a Newbie

+
+ +

+ +Many developers just use System.out.println() to print diagnostic strings to the terminal. The problem with that is that before the release, you have to go through all your code and make certain you removed all these println() calls. You do not want your customers to see them, and needlessly worry about ominous outdated debugging diagnostics. +

+ +
+ +

Logging Like a Pro

+
+ +

+ +Instead of println(), use the standard Java logger from java.util.logging. It has many advantages for professional game development: +

+ + +

+ +To print comments like a pro, you use the following logger syntax. +

+
    +
  1. Declare the logger object once per file. In the following code, replace HelloWorld by the name of the class where you are using this line.
    private static final Logger logger = Logger.getLogger(HelloWorld.class.getName());
    +
    +
  2. +
  3. Declare the info that you want to include in the message. The variables (here a, b, c) can be any printable Java object.
    +Example: Vector3f a = cam.getLocation();
    +
  4. +
  5. Put the variables in a new Object array. Refer to the variables as {0},{1},{2} etc in the message string. Variables are numbered in the order you put them into the Object array.
    +
  6. +
  7. Add the logger line and specify the log level:
    +
      +
    • Usecase 1: During debugging, a developer uses a warning to remind himself of a bug:
      logger.log(Level.WARNING, "why is {0} set to {1} again?!", 
      +                      new Object[]{a , b});
      +
      +
    • +
    • Usecase 2: For the release, you inform the customer of a problem and how to solve it.
      logger.log(Level.SEVERE, "MyGame error: {0} must not be {1} after {2}! Adjust flux generator settings.", 
      +                      new Object[]{a , b , c});
      +
      +
    • +
    +
  8. +
+ +

+ +

As you see in the examples, you should phrase potentially "customer facing" errors in a neutral way and offer a reason and a solution for the error (if you don't, it has no value to your customer). If your deveopment team uses WARNINGs as replacement for casual printlns, make sure you deactivate them for the release. +

+

+ +

+More details about here. +

+ +
+ +

Switching the Logger on and off

+
+ +

+ +In the release version you will deactivate the logging output to the terminal. +

+ +

+To deactivate the default logger for a release, you set the log level to only report severe messages: + +

+
Logger.getLogger(??????).setLevel(Level.SEVERE);
+ +

+During development or a beta test, you can tune down the default logger, and set the log level to only report warnings: + +

+
Logger.getLogger(??????).setLevel(Level.WARNING);
+ +

+To activate full logging, e.g. for debugging and testing, use the fine level: + +

+
Logger.getLogger(??????).setLevel(Level.FINE);
+ +
+ +

Advanced Error Handling

+
+ +

+ +When an uncaught exception reaches certain parts of the jME3 system then the default response is to log the error and then exit the application. This is because an error happening every frame will rapidly fill logs with repeated failings and potentially mask or over-write the original cause of the problem or even the application may continue for a while and then suffer other errors caused by the first and make the root cause hard to determine. +

+ +

+This behaviour can be partially modified by overriding the method handleError in SimpleApplication, for example to display a custom message to users, or to provide users with information on how to report a bug or even to change the way that the error is logged. +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/making_the_camera_follow_a_character.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/making_the_camera_follow_a_character.html new file mode 100644 index 000000000..3560dd8e3 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/making_the_camera_follow_a_character.html @@ -0,0 +1,177 @@ + +

Making the Camera Follow a 3rd-Person Character

+
+ +

+ +When players steer a game character with 1st-person view, they directly steer the camera (flyCam.setEnabled(true);), and they never see the walking character itself. In a game with 3rd-person view, however, the players see the character walk, and you (the game developer) want to make the camera follow the character around when it walks. +

+ +

+There are two ways how the camera can do that: +

+ + +

+ +Important: Using third-person view requires you to deactivate the default flyCam (first-person view). This means that you have to configure your own navigation (key inputs and analogListener) that make your player character walk. For moving a physical player character, use player.setWalkDirection(), for a non-pysical character you can use player.move(). +

+ +
+ +

Code Samples

+
+ +

+Press the WASD or arrow keys to move. Drag with the left mouse button to rotate. +

+ + +
+ +

Camera Node

+
+ +

+To make the camera follow a target node, add this camera node code to your init method (e.g. simpleInitApp()). The target spatial is typically the player node. +

+
// Disable the default flyby cam
+flyCam.setEnabled(false);
+//create the camera Node
+camNode = new CameraNode("Camera Node", cam);
+//This mode means that camera copies the movements of the target:
+camNode.setControlDir(ControlDirection.SpatialToCamera);
+//Attach the camNode to the target:
+target.attachChild(camNode);
+//Move camNode, e.g. behind and above the target:
+camNode.setLocalTranslation(new Vector3f(0, 5, -5));
+//Rotate the camNode to look at the target:
+camNode.lookAt(target.getLocalTranslation(), Vector3f.UNIT_Y);
+ +

+Important: Where the example says camNode.setLocalTranslation(new Vector3f(0, 5, -5));, you have to supply your own start position for the camera. This depends on the size of your target (the player character) and its position in your particular scene. Optimally, you set this to a spot a bit behind and above the target. + +

+
+ + + + + + + + + +
MethodsDescription
setControlDir(ControlDirection.SpatialToCamera)User input steers the target spatial, and the camera follows the spatial.
+The spatial's transformation is copied over the camera's transformation.
+Example: Use with CharacterControlled spatial.
setControlDir(ControlDirection.CameraToSpatial)User input steers the camera, and the target spatial follows the camera.
+The camera's transformation is copied over the spatial's transformation. Use with first-person flyCam.
+ +

+ +Code sample: +

+ + +
+ +

Chase Camera

+
+ +

+ +To activate the chase camera, add the following code to your init method (e.g. simpleInitApp()). The target spatial is typically the player node. You will be able to rotate the target by dragging (keeping the left mouse button pressed and moving the mouse). +

+
// Disable the default flyby cam
+flyCam.setEnabled(false);
+// Enable a chase cam for this target (typically the player).
+ChaseCamera chaseCam = new ChaseCamera(cam, target, inputManager);
+chaseCam.setSmoothMotion(true);
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescription
setInvertVerticalAxis(true)Invert the camera's vertical rotation Axis
setInvertHorizontalAxis(true)Invert the camera's horizontal rotation Axis
setTrailingEnabled(true)Camera follows the target and flies around and behind when the target moves towards the camera. Trailing only works with smooth motion enabled. (Default)
setTrailingEnabled(false)Camera follows the target, but does not rotate around the target when the target changes direction.
setSmoothMotion(true)Activate SmoothMotion when trailing. This means the camera seems to accelerate and fly after the character, when it has caught up, it slows down again.
setSmoothMotion(false)Disable smooth camera motion. Disabling SmoothMotion also disables trailing.
setLookAtOffset(Vector3f.UNIT_Y.mult(3))Camera looks at a point 3 world units above the target.
setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE))Enable rotation by keeping the middle mouse button pressed (like in Blender). This disables the rotation on right and left mouse button click.
setToggleRotationTrigger(new MouseButtonTrigger(
+MouseInput.BUTTON_MIDDLE),
+new KeyTrigger(KeyInput.KEY_SPACE))
Activate mutiple triggers for the rotation of the camera, e.g. spacebar and middle mouse button, etc.
setRotationSensitivity(5f)How fast the camera rotates. Use values around <1.0f (all bigger values are ignored).
+ +

+ +Code sample: +

+ + +
+ +

Which to Choose?

+
+ +

+ +What is the difference of the two code samples above? + +

+
+ + + + + + + + + + + + +
CameraNodeChaseCam
Camera follows immediately, flies at same speed as target.Camera moves smoothly and accelerates and decelerates, flies more slowly than the target and catches up.
Camera stays attached to the target at a constant distance.Camera orbits the target and approaches slowly.
Drag-to-Rotate rotates the target and the camera. You always see the target from behind.Drag-to-Rotate rotates only the camera. You can see the target from various sides.
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/material_definitions.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/material_definitions.html new file mode 100644 index 000000000..3eea553e6 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/material_definitions.html @@ -0,0 +1,193 @@ + +

How to Use Material Definitions (.j3md)

+
+ +

+ +All Geometries need a Material to be visible. Every Material is based on a Material Definition. Material definitions provide the "logic" for the material, and a shader draws the material according to the parameters specified in the definition. The J3MD file abstracts the shader and its configuration away from the user, allowing a simple interface where the user can simply set a few parameters on the material to change its appearance and the way its handled by the shaders. +

+ +

+The most common Material Definitions are included in the engine, advanced users can create custom ones. In this case you will also be interested in the in-depth developer specification of the jME3 material system. +

+ +

+Example: + +

+
Spatial myGeometry = assetManager.loadModel("Models/Teapot/Teapot.j3o");
+Material mat = new Material(assetManager,  // Create new material and...
+    "Common/MatDefs/Misc/Unshaded.j3md");  // ... specify a Material Definition file, here "Unshaded.j3md"!
+mat.setColor("Color", ColorRGBA.Blue);     // Set one or more material parameters.
+myGeometry.setMaterial(mat);               // Use material on this Geometry.
+ +

+

If you use one custom material with certain settings very often, learn about storing material settings in j3m Material Files. You either use the jMonkeyEngine SDK to create .j3m files (user-friendly), or you write .j3m files in a text editor (IDE-independent). +

+

+ +
+ +

Preparing a Material

+
+ +

+ +In the Materials Overview list: +

+
    +
  1. Choose a Material Definition that has the features that you need.
    +
      +
    • Tip: If you don't know, start with Unshaded.j3md or Lighting.j3md.
      +
    • +
    +
  2. +
  3. Look at the applicable parameters of the Material Definition and determine which parameters you need to achieve the desired effect (e.g. "glow" or "color"). Most parameters are optional!
    +
  4. +
  5. Create and save the necessary Texture files to your assets/Textures directory.
    +
      +
    • E.g. mytex_diffuse.png as ColorMap / DiffuseMap, mytex_normal.png as NormalMap, mytex_alpha.png as AlphaMap, etc???
      +
    • +
    +
  6. +
  7. Determine the required values to achieve the effect that you want.
    +
      +
    • E.g. set colors, floats, booleans, etc???
      +
    • +
    +
  8. +
+ +
+ +

Using a Material

+
+ +

+ +In your Java code, +

+
    +
  1. Create a Material object based on the chosen Material Definition (.j3md file):
    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    +
    +
  2. +
  3. Configure your Material by setting the appropriate values listed in the Materials Overview table.
    mat.setColor("Color", ColorRGBA.Yellow ); // and more
    +
    +
  4. +
  5. Apply your prepared Material to a Geometry:
    myGeometry.setMaterial(mat);
    +
    +
  6. +
  7. (Optional) Adjust the texture scale of the mesh:
    myGeometryMesh.scaleTextureCoordinates(new Vector2f(2f, 2f));
    +
    +
  8. +
+ +

+ +For details see also: How to Use Materials +

+ +
+ +

Examples

+
+ +

+ +Here are examples of the methods that set the different data types: +

+ + +

+ +A simpled textured material. + +

+
Material mat = new Material(assetManager, 
+    "Common/MatDefs/Misc/Unshaded.j3md");
+mat.setTexture("ColorMap", assetManager.loadTexture(
+    "Interface/Logo/Monkey.jpg"));
+ +

+A textured material with a color bleeding through transparent areas. + +

+
Material mat = new Material(assetManager, 
+    "Common/MatDefs/Misc/Unshaded.j3md");
+mat.setTexture("ColorMap", assetManager.loadTexture(
+    "Textures/ColoredTex/Monkey.png"));
+mat.setColor("Color", ColorRGBA.Blue);
+ +

+You can test these examples within the following code snippet. It creates a box and applies the material: +

+
 Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+Geometry geom = new Geometry("Box", b);
+// ... insert Material definition...
+geom.setMaterial(mat);
+rootNode.attachChild(geom);
+ +

+

You can find these and other common code snippets in the jMonkeyEngine SDK Code Palette. Drag and drop them into your source code. +

+

+ +
+ +

Creating a Custom Material Definition

+
+ +

+ +First read the developer specification of the jME3 material system (.j3md,.j3m). Also check out the engine source code and have a look at how some Material Definitions are implemented. +

+ +

+You can create your own Material Definitions and place them in your project's assets/MatDefs directory. + +

+
    +
  1. Find the existing MatDefs in engine/src/core-data/Common/MatDefs/.
    +
  2. +
  3. Open a Something.j3md file in a text editor. You see that this .j3md file defines Material Parameters and Techniques.
    +
      +
    • Material Parameters are the ones that you set in Materials, as shown in the examples above.
      +
    • +
    • The Techniques rely on VertexShaders and FragmentShaders: You find those in the files Something.vert and Something.frag in the same directory.
      +
    • +
    +
  4. +
  5. Learn about GLSL (OpenGL Shading Language) to understand the .vert and .frag syntax, then write your own.
    +
  6. +
+ +
+ +

Related Links

+
+ +
+ Material, + SDK, + MatDef, + file, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/materials_overview.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/materials_overview.html new file mode 100644 index 000000000..977147478 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/materials_overview.html @@ -0,0 +1,402 @@ + +

Material Definition Properties

+
+ +

+ +In jMonkeyEngine 3, colors and textures are represented as Material objects. + +

+ + +

+ +

Find out quickly How to Use Materials, including the most commonly used code samples and RenderStates.
+Or find more background info on How to use Material Definitions. +

+

+ +
+ +

All Materials Definition Properties

+
+ +

+ +The following Materials table shows you the Material Definitions that jMonkeyEngine 3 supports. +

+ +

+

Looks confusing?
+1) Start learning about Unshaded.j3md and Lighting.j3md, they cover 90% of the cases.
+2) Use the SDK's visual material editor to try out and save material settings easily.
+3) The SDK's Palette contains drag&drop code snippets for loading materials. +

+

+ +

+Most Material parameters are optional. For example, it is okay to specify solely the DiffuseMap and NormalMap when using Lighting.j3md, and leave the other texture maps empty. In this case, you are only using a subset of the possible features, but that's fine if it already makes in the material look the way that you want. You can always add more texture maps later. +

+ +
+ +

Unshaded Coloring and Textures

+
+ +

+ +jMonkeyEngine supports illuminated and unshaded Material Definitions. +

+ + +

+ +"Unshaded" materials look somewhat abstract because they ignore lighting and shading. Unshaded Materials work even if the scene does not include a light source. These Materials can be single-colored or textured. For example, they are used for cards and tiles, for the sky, billboards and UI elements, for toon-style games, or for testing. + +

+
+ + + + + + +
Standard Unshaded Material Definition Usage Material Parameters
Common/MatDefs/Misc/Unshaded.j3md Standard, non-illuminated Materials.
+Use this for simple coloring and texturing, glow, and transparency.
+See also: Hello Material
Texture Maps
+setTexture("ColorMap", assetManager.loadTexture(""));
+ setBoolean("SeparateTexCoord",true);
+setTexture("LightMap", assetManager.loadTexture(""));
+Colors
+setColor("Color", ColorRGBA.White);
+setBoolean("VertexColor",true);
+Glow
+setTexture("GlowMap", assetManager.loadTexture(""));
+setColor("GlowColor", ColorRGBA.White);
+ +

+ +Other useful, but less commonly used material definitions: + +

+
+ + + + + + + + + + + + + + + +
Special Unshaded Material Definitions Usage Material Parameters
Common/MatDefs/Misc/Sky.j3md A solid skyblue, or use with a custom SkyDome texture.
+See also: Sky
setTexture("TextureCubeMap", assetManager.loadTexture(""));
+ setBoolean("SphereMap",true);
+setVector3("NormalScale", new Vector3f(0,0,0));
Common/MatDefs/Terrain/Terrain.j3md Splat textures for e.g. terrains.
+See also: Hello Terrain
setTexture("Texture1", assetManager.loadTexture("")); (red)
+ setFloat("Texture1Scale",1f);
+ setTexture("Texture2", assetManager.loadTexture("")); (green)
+ setFloat("Texture2Scale",1f);
+setTexture("Texture3", assetManager.loadTexture("")); (blue)
+ setFloat("Texture3Scale",1f);
+setTexture("Alpha", assetManager.loadTexture(""));
Common/MatDefs/Terrain/HeightBasedTerrain.j3mdA multi-layered texture for terrains.
+Specify four textures and a Vector3f describing the region in which each texture should appear:
+X = start height,
+Y = end height,
+Z = texture scale.
+Texture regions can overlap.
+For example: Specify a seafloor texture for the lowest areas,
+a sandy texture for the beaches,
+a grassy texure for inland areas,
+and a rocky texture for mountain tops.
setFloat("terrainSize",512f);
+setTexture("region1ColorMap", assetManager.loadTexture(""));
+setTexture("region2ColorMap", assetManager.loadTexture(""));
+setTexture("region3ColorMap", assetManager.loadTexture(""));
+setTexture("region4ColorMap", assetManager.loadTexture(""));
+setVector3("region1", new Vector3f(0,0,0));
+ setVector3("region2", new Vector3f(0,0,0));
+ setVector3("region3", new Vector3f(0,0,0));
+ setVector3("region4", new Vector3f(0,0,0));
+Settings for steep areas:
+setTexture("slopeColorMap", assetManager.loadTexture(""));
+ setFloat("slopeTileFactor",1f);
Common/MatDefs/Misc/Particle.j3md Used with texture masks for particle effects, or for point sprites.
+The Quadratic value scales the particle for perspective view ().
+Does support an optional colored glow effect.
+See also: Hello Effects
setTexture("Texture", assetManager.loadTexture(""));
+setTexture("GlowMap", assetManager.loadTexture(""));
+setColor("GlowColor", ColorRGBA.White);
+ setFloat("Quadratic",1f);
+ setBoolean("PointSprite",true);
+ +
+ +

Phong Illuminated

+
+ +

+ +jMonkeyEngine supports illuminated and unshaded Material Definitions. +

+ + +

+ +Illuminated materials require a light source added to at least one of their parent nodes! (e.g. rootNode.) Illuminated materials are darker on the sides facing away from light sources. They use Phong illumination model (default), or the Ward isotropic gaussian specular shader (WardIso) which looks more like plastic. They do not cast drop shadows unless you use a FilterPostProcessor. + +

+
+ + + + + + +
Standard Illuminated Material Definition Usage Material Parameters
Common/MatDefs/Light/Lighting.j3md Commonly used Material with Phong illumination.
+Use this material together with DiffuseMap, SpecularMap, BumpMap (NormalMaps, ParalaxMap) textures.
+Supports shininess, transparency, and plain material colors (Diffuse, Ambient, Specular colors).
+See also: Hello Material
Texture Maps
+setTexture("DiffuseMap", assetManager.loadTexture(""));
+setBoolean("UseAlpha",true);1)
+setTexture("NormalMap", assetManager.loadTexture(""));
+setBoolean("LATC",true); 2)
+setTexture("SpecularMap", assetManager.loadTexture(""));
+ setFloat("Shininess",64f);
+setTexture("ParallaxMap", assetManager.loadTexture(""));
+setTexture("AlphaMap", assetManager.loadTexture(""));
+ setFloat("AlphaDiscardThreshold",1f);
+setTexture("ColorRamp", assetManager.loadTexture(""));
+Glow
+setTexture("GlowMap", assetManager.loadTexture(""));
+setColor("GlowColor", ColorRGBA.White);
+Performance and quality
+setBoolean("VertexLighting",true);
+ setBoolean("UseVertexColor",true);
+ setBoolean("LowQuality",true);
+ setBoolean("HighQuality",true);
+Material Colors
+ setBoolean("UseMaterialColors",true);
+setColor("Diffuse", ColorRGBA.White);
+ setColor("Ambient", ColorRGBA.White);
+setColor("Specular", ColorRGBA.White);
+Tangent shading:
+ setBoolean("VTangent",true);
+ setBoolean("Minnaert",true);3)
+setBoolean("WardIso",true);4)
+
+ + + + + + + + + +
Special Illuminated Material Definitions Usage Material Parameters
Common/MatDefs/Terrain/TerrainLighting.j3mdSame kind of multi-layered splat texture as Terrain.j3md, but with illumination and shading.
+Typically used for terrains, but works on any mesh.
+For every 3 splat textures, you need one alpha map.
+You can use a total of 11 texture maps in the terrain's splat texture:
+Note that diffuse and normal maps all count against that.
+For example, you can use a maximum of 9 diffuse textures, two of which can have normal maps;
+or, five textures with both diffuse and normal maps.
Texture Splat Maps
+ setTexture("DiffuseMap", assetManager.loadTexture(""));
+ setFloat("DiffuseMap_0_scale",1f);
+setTexture("NormalMap", assetManager.loadTexture(""));
+setTexture("DiffuseMap_1", assetManager.loadTexture(""));
+ setFloat("DiffuseMap_1_scale",1f);
+setTexture("NormalMap_1", assetManager.loadTexture(""));
+setTexture("DiffuseMap_2", assetManager.loadTexture(""));
+ setFloat("DiffuseMap_2_scale",1f);
+setTexture("NormalMap_2", assetManager.loadTexture(""));
+setTexture("DiffuseMap_3", assetManager.loadTexture(""));
+ setFloat("DiffuseMap_3_scale",1f);
+setTexture("NormalMap_3", assetManager.loadTexture(""));
+etc, up to 11.
+Alpha Maps
+setTexture("AlphaMap", assetManager.loadTexture(""));
+setTexture("AlphaMap_1", assetManager.loadTexture(""));
+setTexture("AlphaMap_2", assetManager.loadTexture(""));
+Glowing
+setTexture("GlowMap", assetManager.loadTexture(""));
+setColor("GlowColor", ColorRGBA.White);
+Miscellaneous
+setColor("Diffuse", ColorRGBA.White);
+setColor("Ambient", ColorRGBA.White);
+setFloat("Shininess",64f);
+setColor("Specular", ColorRGBA.White);
+setTexture("SpecularMap", assetManager.loadTexture(""));
+setBoolean("WardIso",true);
+ setBoolean("useTriPlanarMapping",true);
+ setBoolean("isTerrainGrid",true);
Common/MatDefs/Light/Reflection.j3md Reflective glass material with environment map (CubeMap/SphereMap). See also: setTexture("Texture", assetManager.loadTexture(""));
+ setBoolean("SphereMap",true);
+ +
+ +

Other: Test and Debug

+
+
+ + + + + + +
Material Definition Usage
Common/MatDefs/Misc/ShowNormals.j3md A color gradient calculated from the model's surface normals. You can use this built-in material to debug the generation of normals in meshes, to preview models that have no material and no lights, or as fall-back default material. This built-in material has no parameters.
+ +
+ +

RenderStates

+
+ +
+ +

Transparency

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Material OptionDescriptionExample
getAdditionalRenderState().setBlendMode(BlendMode.Off);This is the default, no transparency.Use for all opaque objects like walls, floors, people???
getAdditionalRenderState().setBlendMode(BlendMode.Alpha);Interpolates the background pixel with the current pixel by using the current pixel's alpha.Use this for normal every-day translucency: Frosted window panes, ice, glass, alpha-blended vegetation textures???
getAdditionalRenderState().setDepthWrite(false);Disables writing of the pixel's depth value to the depth buffer.Use this on Materials if you have several transparent/translucent objects obscuring one another, but you want to see through both.
getAdditionalRenderState().setAlphaFallOff(0.5f);
+getAdditionalRenderState().setAlphaTest(true)
Enables Alpha Testing with a "AlphaDiscardThreshold" in the AlphaMap.Activate Alpha Testing for (partially) transparent objects such as foliage, hair, etc.
+Deactivate Alpha Testing for gradually translucent objects, such as colored glass, smoked glass, ghosts.
getAdditionalRenderState().setBlendMode(BlendMode.Additive);Additive alpha blending adds colors in a commutative way, i.e. the result does not depend on the order of transparent layers, since it adds the scene's background pixel color to the current pixel color. This is useful if you have lots of transparent textures overlapping and don't care about the order.
+Note: Viewed in front of a white background, Additive textures become fully transparent!
This is the default for Particle.j3md-based textures that have a black color background.
getAdditionalRenderState().setBlendMode(BlendMode.AlphaAdditive);Same as "Additive", except first it multiplies the current pixel color by the pixel alpha.This can be used for particle effects that have alpha as background.
getAdditionalRenderState().setBlendMode(BlendMode.Color);Blends by color.Generally useless.
getAdditionalRenderState().setBlendMode(BlendMode.Modulate);Multiplies the background pixel by the current pixel.?
getAdditionalRenderState().setBlendMode(BlendMode.ModulateX2);Same as "Modulate", except the result is doubled.?
getAdditionalRenderState().setBlendMode(BlendMode.PremultAlpha);Pre-multiplied alpha blending. E.g. if the color of the object has already been multiplied by its alpha, this is used instead of "Alpha" blend mode.For use with Premult Alpha textures.
+ +

+ +If the DiffuseMap has an alpha channel, use: + +

+
mat.setBoolean("UseAlpha",true);
+ +

+Later, put the Geometry (not the Material!) in the appropriate render queue +

+ + +
+ +

Culling

+
+
+ + + + + + + + + + + + + + + +
Material OptionUsageExample
getAdditionalRenderState().setFaceCullMode(FaceCullMode.Back); Activates back-face culling. Mesh faces that are facing away from the camera are not rendered, which saves time. *Backface culling is activated by default as a major optimization.* The invisible backsides and insides of models are not calculated.
getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off); No meshes are culled. Both mesh faces are rendered, even if they face away from the camera. Slow.Sometimes used to debug custom meshes if you messed up some of the polygon sides, or for special shadow effects.
getAdditionalRenderState().setFaceCullMode(FaceCullMode.Front); Activates front-face culling. Mesh faces facing the camera are not rendered.No example ??? Typically not used because you wouldn't see anything meaningful.
getAdditionalRenderState().setFaceCullMode(FaceCullMode.FrontAndBack)Culls both backfaces and frontfaces.Use this as an efficient way to make an object temporarily invisible, while keeping all its other in-game properties (such as node attachment, collision shapes, interactions, etc) active.
+ +
+ +

Miscellaneous

+
+
+ + + + + + + + + +
getAdditionalRenderState().setColorWrite(false);Disable writing the color of pixels.Use this together with setDepthWrite(true) to write pixels only to the depth buffer, for example.
getAdditionalRenderState().setPointSprite(true);Enables point-sprite mode, e.g. meshes with "Mode.Points" will be rendered as textured sprites. Note that gl_PointCoord must be set in the shader.Point sprites are used internally for hardware accelerated particle effects.
getAdditionalRenderState().setPolyOffset();Enable polygon offset.Use this when you have meshes that have triangles really close to each over (e.g. ), it will shift the depth values to prevent .
+ +

+ +Related Links + +

+ +
+ material, + texture, + MatDefs, + light, + culling, + RenderStates, + documentation +
+ +
+
+
1) +UseAlpha specifies whether DiffuseMap uses the alpha channel
+
2) +LATC Specifies whether NormalMap is BC5/ATI2n/LATC/3Dc-compressed
+
3) +Minnaert is a shader type.
+
4) +WardIso is a shader type.
+
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mesh.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mesh.html new file mode 100644 index 000000000..96246b203 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mesh.html @@ -0,0 +1,171 @@ + +

Polygon Meshes

+
+ +

+ + +

+ +

+All visible game elements in a scene, whether it is a Model or a Shape, are made up of polygon meshes. JME3 has a com.jme3.scene.Mesh class that represents all meshes. + +

+ + +

+ +You can use default Shapes as meshes; load 3D models (i.e. meshes created in external applications); or create free-form custom meshes programmatically. +

+ +
+ +

Vertex Buffer

+
+ +

+ +The VertexBuffer contains a particular type of geometry data used by Meshes. Every VertexBuffer set on a Mesh is sent as an attribute to the vertex shader to be processed. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Vertex Buffer TypeDescription
Type.Position Position of the vertex (3 floats)
Type.Index Specifies the index buffer, must contain integer data.
Type.TexCoord Texture coordinate
Type.TexCoord2 Texture coordinate #2
Type.Normal Normal vector, normalized.
Type.Tangent Tangent vector, normalized.
Type.Binormal Binormal vector, normalized.
Type.Color Color and Alpha (4 floats)
Type.Size The size of the point when using point buffers.
Type.InterleavedData Specifies the source data for various vertex buffers when interleaving is used.
Type.BindPosePosition Inital vertex position, used with animation.
Type.BindPoseNormal Inital vertex normals, used with animation
Type.BoneWeight Bone weights, used with animation
Type.BoneIndex Bone indices, used with animation
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mesh methodDescription
setLineWidth(1)
setPointSize(4.0f)
setBound(boundingVolume)
setStatic()Locks the mesh so you cannot modify it anymore, thus optimizing its data (faster).
setDynamic()Unlocks the mesh so you can modified it, but this will un-optimize the data (slower).
setMode(Mesh.Mode.Points) Used to set mesh modes, see below
getId()
getTriangle(int,tri)
scaleTextureCoordinates(Vector2f)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Mesh ModeDescription
Mesh.Mode.PointsShow only corner points
Mesh.Mode.LinesShow lines
Mesh.Mode.LineLoop?
Mesh.Mode.LineStrip?
Mesh.Mode.Triangles?
Mesh.Mode.TriangleStrip?
Mesh.Mode.TriangleFan?
Mesh.Mode.Hybrid?
+
+ spatial, + node, + mesh, + geometry, + scenegraph +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/monkey_zone.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/monkey_zone.html new file mode 100644 index 000000000..8e435e6c0 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/monkey_zone.html @@ -0,0 +1,341 @@ + +

Monkey Zone: Multi-player Sample Project

+
+ +

+MonkeyZone is an multi-player demo game provided by the jME core developer team. +

+ + +

+This open-source demo: +

+
    +
  1. showcases one possible way to implement a game with jME3, and
    +
  2. +
  3. helps the jME team verify the jME3 API in terms of usability.
    +
  4. +
+ +

+The game idea is based on ???BattleZone??? arcade game from the 1980s, a first-person shooter the with real-time strategy elements. +The game was written using the jMonkeyEngine SDK, and it's based off the BasicGame project template. It took us one week to create a playable pre-alpha, including networking. +The project design follows best practices that make it possible to edit maps, vehicles, etc, in jMonkeyEngine SDK without having to change the code ??? This allows 3D graphic designers to contribute models more easily. (If you feel like contributing assets or working on parts of the game code, drop us a note!) + +

+ +
+ +

Implementation

+
+ +

+MonkeyZone is a multi-player game with a physics simulation. Both, clients and server, run the physics simulation. The clients send input data from the player group to the server, where they control the entities, and also broadcast to the clients. Additionally, the server sends regular syncronization data for all objects in the game to prevent drifting. +When a human user or an AI performs an action (presses a button), the actual logic is done on the server. The results are broadcast as data messages to the entities. When the entity is controlled by an AI, the actual AI code (that determines where the entity should move, and when it should perform an action) is executed on the client. +
+
+ +The way MonkeyZone is implemented is just one of the many possible ways to do a game like this in jME. Some things might be done more efficiently, some might be done in another way completely. MonkeyZone tries to do things the way that are most appropriate to implement the game at hand and it shows nicely how jME3 and the standard Java API can make game development easier and faster. Also note that the way MonkeyZone is designed is not scalable to a MMO style game, it will only work in a FPS style environment where the whole game world can be loaded at once. + +

+ +
+ +

Terminology

+
+ +

+The game uses certain terms that might be familiar to you but maybe used in another way, so heres a quick rundown on the terms being used. +

+ + +
+ +

Manager Classes

+
+ +

+The WorldManager does the main work of organizing players, entities and the world and synchronizing them between the server and client. Both client and server use this class. Some other managers like ClientEffectsManager only exist on the client or server and manage e.g. effects display. +The gameplay is largely controlled by the ServerGameManager which does gameplay logic on the server, combined with the actions issued by the AI and user on the client (see below) it implements the gameplay. It extensively uses the functions exposed by the WorldManager to perform actions and gather data. This is also the class where the actions of the players are actually executed on the server to determine the outcome (ray testing for shooting etc.). + +

+ +
+ +

Use of Controls

+
+ +

+Controls are used extensively in MonkeyZone for many aspects of the game. When a player enters an entity, the Spatials Controls are configured based on the player that enters. For example when the human user enters an entity, Controls that update the user interface (DefaultHUDControl) or user input (UserInputControl) are added to the current entity Spatial. + +

+ +
+ +

...As entity capabilities

+
+ +

+Controls attached to Spatials are generally used like an ???array of capabilities??? that the entity posesses. So when an entity has a VehicleControl its expected to be a vehicle, when its got a CharacterControl its expected to be a character. +Other Controls work completely on their own, like CharacterAnimControl which just uses the CharacterControl of the entity to check if the character is running, jumping etc. and then animates the entity if it has an AnimControl. + +

+ +
+ +

... to abstract

+
+ +

+Furthermore theres special interfaces for Controls that allow abstraction of different Controls into one base interface. For example ManualControl and AutonomousControl are interfaces for controls that manage the movement of a spatial in a generalized way. This way AI code and e.g. the UserInputControl only have to check for a valid AutonomousControl or ManualControl on the spatial to control and move it. The details of the movement are handled by classes like ManualVehicleControl and AutonomousCharacterControl. + +

+ +
+ +

... for AI functions

+
+ +

+A special Control called CommandControl handles the Commands that can be executed by user controlled players, see below. + +

+ +
+ +

Artificial Intelligence

+
+ +

+MonkeyZone includes simple AI functions based on a command queue. + +

+ +
+ +

Commands

+
+ +

+To implement autonomous AI players MonkeyZone uses a system of Commands that are managed by a CommandControl that is attached to each AI player entity controlled by the user. This CommandControl manages a list of Commands that are executed based on priority. For example theres a MoveCommand, a FollowCommand and an AttackCommand, Commands can however implement more complete behavior than that, e.g. the complete logic for a scavenging entity. +

+ + +
+ +

Triggers

+
+ +

+The SphereTrigger is a TriggerControl that is also attached to each AI players current entity. It consists of a GhostControl that checks the overlapping entities around the entity its attached to. It can be assigned a command that is checked with every entity entering the SphereTrigger and executed if applicable (e.g. normal ???attack enemy??? mode). + +

+ +
+ +

NavMesh

+
+ +

+For each map a navigation mesh is generated that allows the entities to navigate the terrain. Autonomous entities automatically get a NavigationControl based on the current map. The AutonomousControl implementations automatically recognize the NavigationControl attached to the Spatial and use it for navigation. The NavMeshNavigationControl implementation contains a reference to the levels NavMesh and implements a navigation algorithm similar to the A* algorithm. + +

+ +
+ +

Networking

+
+ +

+Networking is realized in the PhysicsSyncManager which we hope to extend to a state where it can serve as a general sync system for physics based network games. +The sync manager basically puts a timestamp on every message sent from the server and then buffers all arriving messages on the client within a certain time window. This allows to compensate for messages arriving too soon or too late within the constraints of the buffer, a future version might step the clients physics space different to compensate for network delays without ???snapping???. + +

+ +
+ +

Use of jMonkeyEngine SDK tools

+
+ +

+All assets used in the game, like entity models and loaded maps can be preconfigured and edited using the jMonkeyEngine SDK. For example, to add a new vehicle type, a vehicle is created in the jMonkeyEngine SDK vehicle editor and UserData like Speed, HitPoints etc. is applied directly in the editor. When the model is loaded in the game it is automatically configured based on these settings, the same accounts for maps that are loaded, special Nodes that mark e.g. player start locations are recognized automatically etc. + +

+ +
+ +

UserData

+
+ +

+Entities (Nodes and Geometries) that are loaded from disk have certain UserData like HitPoints, Speed etc. that is used to configure the entity at runtime. The jMonkeyEngine SDK allows adding and editing this UserData, so entity properties are editable visually. + +

+ +
+ +

Physics

+
+ +

+VehicleControls, CharacterControls and RigidBodyControls with mesh collision shape for terrain and objects are generated in the jMonkeyEngine SDK and saved in the entity j3o file. When an entity is loaded, the type of entity is identified based on the available controls and UserData and it is configured accordingly. + +

+ +
+ +

API Info

+
+ +
+ +

Designer Infos

+
+ +

+Editable UserData of entity Spatials: +

+ + +

+Entity Spatial marking Node names: +

+ + +

+Level Spatial marking Node names: +

+ + +
+ +

Developer Infos

+
+ +

+Programmatic UserData of entities: +

+ + +

+Programmatic PlayerData: +

+ + +
+ +

The Future

+
+ +

+Have a look at the code and feel free to ask about it, if you want any new features, you are free to implement them. ;) +MonkeyZone is hosted at GoogleCode, where you can check out the jMonkeyEngine SDK-ready project via svn: +

+
    +
  1. jMonkeyEngine SDK???Team???Subversion???Checkout,
    +
  2. +
  3. Enter the SVN URL
    +
  4. +
  5. Download, open, and build the project
    +
  6. +
  7. Run the server first (com.jme3.monkeyzone.ServerMain), and then a client (com.jme3.monkeyzone.ClientMain).
    +
  8. +
+ +
+ +

Troubleshooting

+
+
    +
  1. After download, errors could appear because jme3tools.navmesh.util\NavMeshGenerator.java import com.jme3.terrain.Terrain is not known, you should correct this by setting Project Properties > Libraries > Add Library > jme3-libraries-terrain
    +
  2. +
+
+ network, + basegame, + physics, + inputs, + spidermonkey +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/motionpath.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/motionpath.html new file mode 100644 index 000000000..4f310715d --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/motionpath.html @@ -0,0 +1,121 @@ + +

MotionPath

+
+ +

+ +A MotionPath describes the motion of a spatial between waypoints. The path can be linear or rounded. You use MotionPaths to remote-control a spatial, or the camera. +

+ +

+Tip: If you want to remote-control a whole cutscene with several spatials moving at various times, then we recommened you use MotionPaths together with Cinematics. +

+ +
+ +

Sample Code

+
+ + +
+ +

What Are Way Points?

+
+ +

+ +When shooting a movie scene, the director tells actors where to walk, for example, by drawing a series of small crosses on the floor. Cameramen often mount the camera on rails (so called dolly track) so they can follow along complex scenes more easily. +

+ +

+In JME3, you use MotionPaths to specify a series of positions for a character or the camera. The MotionPath automatically updates the transformation of the spatial in each frame to make it move from one point to the next. +

+ + +

+The final shape of the path is computed using a linear interpolation or a spline interpolation on the way points. +

+ +
+ +

Create a MotionPath

+
+ +

+ +Create a Motionpath object and add way points to it. + +

+
MotionPath path = new MotionPath();
+path.addWayPoint(new Vector3f(10, 3, 0));
+path.addWayPoint(new Vector3f(8, -2, 1));
+...
+ +

+You can configure the path as follows. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
MotionPath Method Usage
path.setCycle(true)Sets whether the motion along this path should be closed (true) or open-ended (false).
path.addWayPoint(vector)Adds individual waypoints to this path. The order is relevant.
path.removeWayPoint(vector)
+removeWayPoint(index)
Removes a way point from this path. You can specify the point that you want to remove as vector or as integer index.
path.setCurveTension(0.83f)Sets the tension of the curve (Catmull-Rom Spline). A value of 0.0f results in a straight linear line, 1.0 a very round curve.
path.getNbWayPoints()Returns the number of waypoints in this path.
path.enableDebugShape(assetManager,rootNode)Shows a line that visualizes the path. Use this during development and for debugging so you see what you are doing.
path.disableDebugShape()Hides the line that visualizes the path. Use this for the release build.
+ +
+ +

MotionPathListener

+
+ +

+ +You can hook interactions into a playing MotionPath. Register a MotionPathListener to the MotionPath to track whether way points have been reached, and then trigger a custom action. The onWayPointReach() method of the interface gives you access to the MotionTrack object control, and an integer value representing the current wayPointIndex. +

+ +

+In this example, you just print the status at every way point. In a game you could trigger actions here: Transformations, animations, sounds, game actions (attack, open door, etc). +

+
path.addListener( new MotionPathListener() {
+  public void onWayPointReach(MotionTrack control, int wayPointIndex) {
+    if (path.getNbWayPoints() == wayPointIndex + 1) {
+      println(control.getSpatial().getName() + " has finished moving. ");
+    } else {
+      println(control.getSpatial().getName() + " has reached way point " + wayPointIndex);
+    }
+  }
+});
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse-picking.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse-picking.png new file mode 100644 index 000000000..d3b1c8b3a Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse-picking.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html new file mode 100644 index 000000000..2af3a7477 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html @@ -0,0 +1,150 @@ + +

Mouse Picking

+
+ +

+ +Mouse picking means that the user clicks an object in the scene to select it, or to interact with it otherwise. Games use picking to implement aiming and shooting, casting spells, picking up objects, selecting targets, dragging and moving objects, etc. Mouse picking can be done using fixed crosshairs, or using the mouse pointer. +

+ +

+ +

+ +

+See Input Handling for details on how to define the necessary input triggers, input mappings, and input listeners. +

+ +
+ +

Pick a Target Using Fixed Crosshairs

+
+ +

+ +The following pick target input mapping implements an action that determines what a user clicked. It assumes that the mouse pointer is invisible and there are crosshairs painted in the center of the screen. It assumes that the user aims the crosshairs at an object in the scene and clicks. You use Ray Casting to identify the geometry that was picked by the user. Use use this method together with a first-person flyCam. + +

+
    +
  1. Activate the first-person camera: flyCam.setEnabled(true);
    +
  2. +
  3. Keep mouse pointer invisible using inputManager.setCursorVisible(false).
    +
  4. +
  5. Map the pick target action to a MouseButtonTrigger.
    +
  6. +
  7. Implement the action in the Listener.
    +
  8. +
+ +

+The following example rotates Spatials named "Red Box" or "Blue Box" when they are clicked. Modify this code to do whatever your game needs to do with the identified target (shoot it, take it, move it, etc). +

+
  private AnalogListener analogListener = new AnalogListener() {
+    public void onAnalog(String name, float intensity, float tpf) {
+        if (name.equals("pick target")) {
+         // Reset results list.
+         CollisionResults results = new CollisionResults();
+         // Aim the ray from camera location in camera direction
+         // (assuming crosshairs in center of screen).
+         Ray ray = new Ray(cam.getLocation(), cam.getDirection());
+         // Collect intersections between ray and all nodes in results list.
+         rootNode.collideWith(ray, results);
+         // Print the results so we see what is going on
+         for (int i = 0; i < results.size(); i++) {
+           // For each ???hit???, we know distance, impact point, geometry.
+           float dist = results.getCollision(i).getDistance();
+           Vector3f pt = results.getCollision(i).getContactPoint();
+           String target = results.getCollision(i).getGeometry().getName();
+           System.out.println("Selection #" + i + ": " + target + " at " + pt + ", " + dist + " WU away.");
+         }
+         // 5. Use the results -- we rotate the selected geometry.
+         if (results.size() > 0) {
+           // The closest result is the target that the player picked:
+           Geometry target = results.getClosestCollision().getGeometry();
+           // Here comes the action:
+           if(target.getName().equals("Red Box"))
+             target.rotate(0, - intensity, 0);
+           else if(target.getName().equals("Blue Box"))
+             target.rotate(0, intensity, 0);
+         }
+        } // else if ...
+    }
+  };
+ +
+ +

Pick a Target Using the Mouse Pointer

+
+ +

+ +The following pick target input mapping implements an action that determines what a user clicked. It assumes that the mouse pointer is visible, and the user aims the cursor at an object in the scene. You use ray casting to determine the geometry that was picked by the user. +

+ +

+Note: Picking with a visible pouse pointer implies that your application can no longer use the default flyCam where the MouseAxisTrigger rotates the camera. You have to deactivate the flyCam mappings and provide custom mappings. Either different inputs rotate the camera, or the camera is fixed. + +

+
    +
  1. Map the pick target action to a MouseButtonTrigger.
    +
  2. +
  3. Make the mouse pointer visible using inputManager.setCursorVisible(true).
    +
  4. +
  5. Remap the inputs for camera rotation, or deactivate camera rotation.
    +
  6. +
  7. Implement the action in the Listener.
    +
  8. +
+ +

+ +The following example rotates Spatials named "Red Box" or "Blue Box" when they are clicked. Modify this code to do whatever your game needs to do with the identified target (shoot it, take it, move it, etc). +

+
private AnalogListener analogListener = new AnalogListener() {
+    public void onAnalog(String name, float intensity, float tpf) {
+      if (name.equals("pick target")) {
+        // Reset results list.
+        CollisionResults results = new CollisionResults();
+        // Convert screen click to 3d position
+        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).normalizeLocal();
+        // Aim the ray from the clicked spot forwards.
+        Ray ray = new Ray(click3d, dir);
+        // Collect intersections between ray and all nodes in results list.
+        rootNode.collideWith(ray, results);
+        // (Print the results so we see what is going on:)
+        for (int i = 0; i < results.size(); i++) {
+          // (For each ???hit???, we know distance, impact point, geometry.)
+          float dist = results.getCollision(i).getDistance();
+          Vector3f pt = results.getCollision(i).getContactPoint();
+          String target = results.getCollision(i).getGeometry().getName();
+          System.out.println("Selection #" + i + ": " + target + " at " + pt + ", " + dist + " WU away.");
+        }
+        // Use the results -- we rotate the selected geometry.
+        if (results.size() > 0) {
+          // The closest result is the target that the player picked:
+          Geometry target = results.getClosestCollision().getGeometry();
+          // Here comes the action:
+          if (target.getName().equals("Red Box")) {
+            target.rotate(0, -intensity, 0);
+          } else if (target.getName().equals("Blue Box")) {
+            target.rotate(0, intensity, 0);
+          }
+        }
+      } // else if ...
+    }
+  };
+
+ documentation, + node, + ray, + click, + collision, + keyinput, + input +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multiple_camera_views.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multiple_camera_views.html new file mode 100644 index 000000000..363a81b6f --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multiple_camera_views.html @@ -0,0 +1,233 @@ + +

Multiple Camera Views

+
+ +

+ +You can split the screen and look into the 3D scene from different camera angles at the same time. E.g. you can have two rootnodes with different scene graphs, and two viewPorts, each of which can only see its own subset of the scene with its own subset of port-processing filters, so you get two very different views of the scene. +

+ +

+The packages used in this example are com.jme3.renderer.Camera and com.jme3.renderer.ViewPort. You can get the full sample code here: +

+ +
+ +

How to resize and Position ViewPorts

+
+ +

+ +The default viewPort is as big as the window. If you have several, they must be of different sizes, either overlapping or adjacent to one another. How do you tell jME which of the ViewPorts should appear where on the screen, and how big they should be? +

+ +

+Imagine the window as a 1.0f x 1.0f rectangle. The default cam's viewPort is set to + +

+
cam.setViewPort(0f, 1f, 0f, 1f);
+ +

+ +This setting makes the ViewPort take up the whole rectangle. +

+ +

+The four values are read in the following order: +

+
cam.setViewPort(x1,x2 , y1,y2);
+ + +

+ +Here are a few examples: + +

+
cam1.setViewPort( 0.0f , 1.0f   ,   0.0f , 1.0f );
+cam2.setViewPort( 0.5f , 1.0f   ,   0.0f , 0.5f );
+ +

+These viewport parameters are, (in this order) the left-right extend, and the bottom-top extend of a views's rectangle on the screen. +

+
0.0 , 1.0       1.0 , 1.0
+       +-----+-----+
+       |cam1       |
+       |           |
+       |     +-----+
+       |     |     |
+       |     |cam2 |
+       +-----+-----+
+0.0 , 0.0       1.0 , 0.0
+ +

+Example: Cam2's rectangle is int he bottom right: It extends from mid (x1=0.5f) bottom (y1=0.0f), to right (x2=1.0f) mid (y2=0.5f) +

+ +

+

If you scale the views in a way so that the aspect ratio of a ViewPort is different than the window's aspect ratio, then the ViewPort appears distorted. In these cases, you must recreate (not clone) the ViewPort's cam object with the right aspect ratio. For example: Camera cam5 = new Camera(100,100); +

+

+ +
+ +

Four-Time Split Screen

+
+ +

+ +In this example, you create four views (2x2) with the same aspect ratio as the window, but each is only half the width and height. +

+ +
+ +

Set up the First View

+
+ +

+ +You use the preconfigured Camera cam and viewPort from SimpleApplication for the first view. It's in the bottom right. + +

+
cam.setViewPort(.5f, 1f, 0f, 0.5f); // Resize the viewPort to half its size, bottom right.
+ +

+ +Optionally, place the main camera in the scene and rotate it in its start position. + +

+
cam.setLocation(new Vector3f(3.32f, 4.48f, 4.28f));
+cam.setRotation(new Quaternion (-0.07f, 0.92f, -0.25f, -0.27f));
+ +
+ +

Set Up Three More Views

+
+ +

+ +Here is the outline for how you create the three other cams and viewPorts (.) In the code snippet, cam_n stand for cam_2 - cam_4, respectively, same for view_n. + +

+
    +
  1. Clone the first cam to reuse its settings
    +
  2. +
  3. Resize and position the cam's viewPort with setViewPort().
    +
  4. +
  5. (Optionally) Move the cameras in the scene and rotate them so they face what you want to see.
    +
  6. +
  7. Create a ViewPort for each camera
    +
  8. +
  9. Reset the camera's enabled statuses
    +
  10. +
  11. Attach the Node to be displayed to this ViewPort.
    +The camera doesn't have to look at the rootNode, but that is the most common use case.
    +
  12. +
+ +

+ +Here is the abstract code sample for camera n: + +

+
Camera cam_n    = cam.clone();
+cam_n.setViewPort(...); // resize the viewPort
+cam_n.setLocation(new Vector3f(...));
+cam_n.setRotation(new Quaternion(...));
+ 
+ViewPort view_n = renderManager.createMainView("View of camera #n", cam_n);
+view_n.setClearEnabled(true);
+view_n.attachScene(rootNode);
+view_n.setBackgroundColor(ColorRGBA.Black);
+ +

+To visualize what you do, use the following drawing of the viewport positions: +

+
0.0 , 1.0       1.0 , 1.0
+       +-----+-----+
+       |     |     |
+       |cam3 |cam4 |
+       +-----------+
+       |     |     |
+       |cam2 |cam1 |
+       +-----+-----+
+0.0 , 0.0       1.0 , 0.0
+ +

+This are the lines of code that set the four cameras to create a four-times split screen. +

+
cam1.setViewPort( 0.5f , 1.0f  ,  0.0f , 0.5f);
+...
+cam2.setViewPort( 0.0f , 0.5f  ,  0.0f , 0.5f);
+...
+cam3.setViewPort( 0.0f , 0.5f  ,  0.5f , 1.0f);
+...
+cam4.setViewPort( 0.5f , 1.0f  ,  0.5f , 1.0f);
+ +
+ +

Picture in Picture

+
+ +

+ +The following code snippet sets up two views, one covers the whole screen, and the second is a small view in the top center. +

+
       +-----+-----+
+       |   |cam|   |
+       |   | 2 |   |
+       +   +---+   +
+       |           |
+       |    cam    |
+       +-----+-----+
+
// Setup first full-window view
+cam.setViewPort(0f, 1f, 0f, 1f);
+cam.setLocation(new Vector3f(3.32f, 4.48f, 4.28f));
+cam.setRotation(new Quaternion(-0.07f, 0.92f, -0.25f, -0.27f));
+ 
+// Setup second, smaller PiP view
+Camera cam2 = cam.clone();
+cam2.setViewPort(.4f, .6f, 0.8f, 1f);
+cam2.setLocation(new Vector3f(-0.10f, 1.57f, 4.81f));
+cam2.setRotation(new Quaternion(0.00f, 0.99f, -0.04f, 0.02f));
+ViewPort viewPort2 = renderManager.createMainView("PiP", cam2);
+viewPort2.setClearFlags(true, true, true);
+viewPort2.attachScene(rootNode);
+ +
+ +

ViewPort Settings

+
+ +

+ +You can customize the camera and the viewPort of each view individually. For example, each view can have a different background color: + +

+
viewPort.setBackgroundColor(ColorRGBA.Blue);
+ +

+ +You have full control to determine which Nodes the camera can see! It can see the full rootNode??? + +

+
viewPort1.attachScene(rootNode);
+ +

+ +??? or you can give each camera a special node whose content it can see: + +

+
viewPort2.attachScene(spookyGhostDetectorNode);
+
+ camera, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html new file mode 100644 index 000000000..1b083c6f7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html @@ -0,0 +1,254 @@ + +

The jME3 Threading Model

+
+ +

+ +jME3 is similar to Swing in that for speed and efficiency all changes to the world must be made in a single update thread. This is happening automatically if using Controls and AppSates update metod or simpleUpdate however whenever you pass work to another thread you need to hand the results back to the main jME3 thread before making any changes to the scene graph. +

+
    public void rotateGeography(final Geography goe, final Quaternian rot) {
+        mainApp.enqueue(new Callable<Spatial>() {
+
+            public Spatial call() throws Exception {
+                return geo.rotate(rot);
+            }
+            
+        });
+    }
+ +

+Note that this example does not fetch the returned value by calling get() on the Future object returned from enqueue(). This means that the example method rotateGeography() will return immediately and will not wait for the rotation to be processed before continuing. +

+ +

+If the processing thread needs to wait or needs the return value then get() or the other methods in the returned Future object such as isDone() can be used. +

+ +
+ +

Multithreading Optimization

+
+ +

+ +First, make sure you know what Application States and Custom Controls are. +

+ +

+More complex games may feature complex mathematical operations or artificially intelligent calculations (such as path finding for several NPCs). If you make many time-intensive calls on the same thread (in the update loop), they will block one another, and thus slow down the game to a degree that makes it unplayable. If your game requires long running tasks, you should run them concurrently on separate threads, which speeds up the application considerably. +

+ +

+Often multithreading means having separate detached logical loops going on in parallel, which communicate about their state. (For example, one thread for AI, one Sound, one Graphics). However we recommend to use a global update loop for game logic, and do multithreading within that loop when it is appropriate. This approach scales way better to multiple cores and does not break up your code logic. +

+ +

+Effectively, each for-loop in the main update loop might be a chance for multithreading, if you can break it up into self-contained tasks. +

+ +
+ +

Java Multithreading

+
+ +

+ +The java.util.concurrent package provides a good foundation for multithreading and dividing work into tasks that can be executed concurrently (hence the name). The three basic components are the Executor (supervises threads), Callable Objects (the tasks), and Future Objects (the result). You can , I will give just a short introduction. + +

+ + +
+ +

Multithreading in jME3

+
+ +

+ +So how do we implement multithreading in jME3? +

+ +

+Let's take the example of a Control that controls an NPC Spatial. The NPC Control has to compute a lengthy pathfinding operation for each NPC. If we would execute the operations directly in the simpleUpdate() loop, it would block the game each time a NPC wants to move from A to B. Even if we move this behaviour into the update() method of a dedicated NPC Control, we would still get annoying freeze frames, because it still runs on the same update loop thread. +

+ +

+To avoid slowdown, we decide to keep the pathfinding operations in the NPC Control, but execute it on another thread. +

+ +
+ +

Executor

+
+ +

+ +You create the executor object in a global AppState (or the initSimpleApp() method), in any case in a high-level place where multiple controls can access it. +

+
/* This constructor creates a new executor with a core pool size of 4. */
+ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);
+ +

+Pool size means the executor will keep four threads alive at any time. Having more threads in the pool means that more tasks can run concurrently. But a bigger pool only results in a speed gain if the PC can handle it! Allocating a pool that is uselessly large just wastes memory, so you need to find a good compromise: About the same to double the size of the number of cores in the computer makes sense. +

+ +

+!!! Executor needs to be shut down when the application ends, in order to make the process die properly +In your simple application you can override the destroy method and shutdown the executor: + +

+
    @Override
+    public void destroy() {
+        super.destroy();
+        executor.shutdown();
+    }
+ +
+ +

Control Class Fields

+
+ +

+ +In the NPC Control, we create the individual objects that the thread manipulates. In our example case (the pathfinding control), the task is about locations and path arrays, so we need the following variables: +

+
//The vector to store the desired location in:
+Vector3f desiredLocation = new Vector3f();
+//The MyWayList object that contains the result waylist:
+MyWayList wayList = null;
+//The future that is used to check the execution status:
+Future future = null;
+ +

+Here we also created the Future variable to track the state of this task. +

+ +
+ +

Control Update() Method

+
+ +

+ +Next let's look at the update() call of the Control where the time-intensive task starts. In our example, the task is the findWay Callable (which contains the pathfinding process). So instead of spelling out the pathfinding process in the Control's update() loop, we start the process via future = executor.submit(findWay);. +

+
public void update(float tpf) {
+    try{
+        //If we have no waylist and not started a callable yet, do so!
+        if(wayList == null && future == null){
+            //set the desired location vector, after that we should not modify it anymore
+            //because its being accessed on the other thread!
+            desiredLocation.set(getGoodNextLocation());
+            //start the callable on the executor
+            future = executor.submit(findWay);    //  Thread starts!
+        }
+        //If we have started a callable already, we check the status
+        else if(future != null){
+            //Get the waylist when its done
+            if(future.isDone()){
+                wayList = future.get();
+                future = null;
+            }
+            else if(future.isCancelled()){
+                //Set future to null. Maybe we succeed next time...
+                future = null;
+            }
+        }
+    } 
+    catch(Exception e){ 
+      Exceptions.printStackTrace(e);
+    }
+    if(wayList != null){
+        //.... Success! Let's process the wayList and move the NPC...
+    }
+}
+ +

+Note how this logic makes its decision based on the Future object. +

+ +

+Remember not to mess with the class fields after starting the thread, because they are being accessed and modified on the new thread. In more obvious terms: You cannot change the "desired location" of the NPC while the path finder is calculating a different path. You have to cancel the current Future first. +

+ +
+ +

The Callable

+
+ +

+ +The next code sample shows the Callable that is dedicated to performing the long-running task (here, wayfinding). This is the task that used to block the rest of the application, and is now executed on a thread of its own. You implement the task in the Callable always in an inner method named call(). +

+ +

+The task code in the Callable should be self-contained! It should not write or read any data of objects that are managed by the scene graph or OpenGL thread directly. Even reading locations of Spatials can be problematic! So ideally all data that is needed for the wayfinding process should be available to the new thread when it starts already, possibly in a cloned version so no concurrent access to the data happens. +

+ +

+In reality, you might need access to the game state. If you must read or write a current state from the scene graph, you must have a clone of the data in your thread. There are only two ways: + +

+ + +

+ +These two ways are thread-safe, they don't mess up the game logic, and keep the Callable code readable. +

+
// A self-contained time-intensive task:
+private Callable<MyWayList> findWay = new Callable<MyWayList>(){
+    public MyWayList call() throws Exception {
+ 
+        //Read or write data from the scene graph -- via the execution queue:
+        Vector3f location = application.enqueue(new Callable<Vector3f>() {
+        ????????public Vector3f call() throws Exception {
+        ????????????????//we clone the location so we can use the variable safely on our thread
+        ????????????????return mySpatial.getLocalTranslation().clone();
+        ????????}
+        }).get();
+ 
+        // This world class allows safe access via synchronized methods
+        Data data = myWorld.getData(); 
+ 
+        //... Now process data and find the way ...
+ 
+        return wayList;
+    }
+};
+ +
+ +

Conclusion

+
+ +

+ +The cool thing about this approach is that every entity creates one self-contained Callable for the Executor, and they are all executed in parallel. In theory, you can have one thread per entity without changing anything else but the settings of the executor. + +

+
+ loop, + game, + performance, + state, + states, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html new file mode 100644 index 000000000..0515d2edb --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html @@ -0,0 +1,493 @@ + +

SpiderMonkey: Multi-Player Networking

+
+ +

+ +This document introduces you to the SpiderMonkey networking API. You use this API when you develop games where several players compete with one another in real time. A multi-player game is made up of several clients connecting to a server: + +

+ + +

+ +Each Client keeps the Server informed about its player's moves and actions. The Server centrally maintains the game state and broadcasts the state info back to all connected clients. This network synchronization allows all clients to share the same game world. Each client then displays the game state to one player from this player's perspective. +

+ +
+ +

SpiderMonkey API Overview

+
+ +

+ +The SpiderMonkey API is a set of interfaces and helper classes in the 'com.jme3.network' package. For most users, this package and the 'message' package is all they need to worry about. (The 'base' and 'kernel' packages only come into play when implementing custom network transports or alternate client/server protocols, which is now possible). +

+ +

+The SpiderMonkey API assists you in creating a Server, Clients, and Messages. Once a Server instance is created and started, the Server accepts remote connections from Clients, and you can send and receive Messages. Client objects represent the client-side of the client-server connection. Within the Server, these Client objects are referred to as HostedConnections. HostedConnections can hold application-defined client-specific session attributes that the server-side listeners and services can use to track player information, etc. + +

+
+ + + + + + +
Seen from the Client Seen from the Server
com.jme3.network.Client == com.jme3.network.HostedConnection
+ +

+ +You can register several types of listeners to be notified of changes. +

+ + +
+ +

Client and Server

+
+ +
+ +

Creating a Server

+
+ +

+ +The game server is a "headless" com.jme3.app.SimpleApplication: +

+
public class ServerMain extends SimpleApplication {
+  public static void main(String[] args) {
+    ServerMain app = new ServerMain();
+    app.start(JmeContext.Type.Headless); // headless type for servers!
+  }
+}
+ +

+

A Headless SimpleApplication executes the simpleInitApp() method and runs the update loop normally. But the application does not open a window, and it does not listen to user input. This is the typical behavior for a server application. +

+

+ +

+Create a com.jme3.network.Server in the simpleInitApp() method and specify a communication port, for example 6143. +

+
  public void simpleInitApp() {
+    ...
+    Server myServer = Network.createServer(6143);
+    myServer.start();
+    ...
+  }
+ +

+When you run this app on a host, the server is ready to accept clients. Let's create a client next. +

+ +
+ +

Creating a Client

+
+ +

+ +A game client is a standard com.jme3.app.SimpleApplication. +

+
public class ClientMain extends SimpleApplication {
+  public static void main(String[] args) {
+    ClientMain app = new ClientMain();
+    app.start(JmeContext.Type.Display); // standard display type
+  }
+}
+ +

+

A standard SimpleApplication in Display mode executes the simpleInitApp() method, runs the update loop, opens a window for the rendered video output, and listens to user input. This is the typical behavior for a client application. +

+

+ +

+ +Create a com.jme3.network.Client in the simpleInitApp() method and specify the servers IP address, and the same communication port as for the server, here 6143. +

+
public void simpleInitApp() {
+   ...
+   Client myClient = Network.connectToServer("localhost", 6143);
+   myClient.start();
+   ...
+ +

+The server address can be in the format "localhost" or "127.0.0.1" (for local testing), or an IP address of a remote host in the format ???123.456.78.9???. In this example, we assume the server is running on the localhost. +

+ +

+When you run this client, it connects to the server. +

+ +
+ +

Getting Info About a Client

+
+ +

+ +The server refers to a connected client as com.jme3.network.HostedConnection objects. The server can get info about clients as follows: + +

+
+ + + + + + + + + + + + +
AccessorPurpose
myServer.getConnections()Server gets a collection of all connected HostedConnection objects (all connected clients).
myServer.getConnections().size()Server gets the number of all connected HostedConnection objects (number of clients).
myServer.getConnection(0)Server gets the first (0), second (1), etc, connected HostedConnection object (one client).
+ +

+ +Your game can define its own game data based on whatever criteria you want, typically these include player ID and state. If the server needs to look up player/client-specific information, you can store this information directly on the HostedConnection object. The following examples read and write a custom Java object MyState in the HostedConnection object conn: + +

+
+ + + + + + + + + +
AccessorPurpose
conn.setAttribute("MyState", new MyState()); Server can change an attribute of the HostedConnection.
MyState state = conn.getAttribute("MyState") Server can read an attribute of the HostedConnection.
+ +
+ +

Messaging

+
+ +
+ +

Creating Message Types

+
+ +

+ +Each message represents data that you want to transmit between client and server. Common message examples include transformation updates or game actions. For each message type, create a message class that extends com.jme3.network.AbstractMessage. Use the @Serializable annotation from com.jme3.network.serializing.Serializable and create an empty default constructor. Custom constructors, fields, and methods are up to you and depend on the message data that you want to transmit. +

+
@Serializable
+public class HelloMessage extends AbstractMessage {
+  private String hello;       // custom message data
+  public HelloMessage() {}    // empty constructor
+  public HelloMessage(String s) { hello = s; } // custom constructor
+}
+ +

+You must register each message type to the com.jme3.network.serializing.Serializer, in both server and client! +

+
Serializer.registerClass(HelloMessage.class);
+ +
+ +

Responding to Messages

+
+ +

+ +After a Message was received, a Listener responds to it. The listener can access fields of the message, and send messages back, start new threads, etc. There are two listeners, one on the server, one on the client. For each message type, you implement the responses in either Listeners??? messageReceived() method. +

+ +
+ +

ClientListener.java

+
+ +

+ +Create one ClientListener.java and make it extend com.jme3.network.MessageListener. + +

+
public class ClientListener implements MessageListener<Client> {
+  public void messageReceived(Client source, Message message) {
+    if (message instanceof HelloMessage) {
+      // do something with the message
+      HelloMessage helloMessage = (HelloMessage) message;
+      System.out.println("Client #"+source.getId()+" received: '"+helloMessage.getSomething()+"'");
+    } // else...
+  }
+ +

+For each message type, register a client listener to the client. +

+
myClient.addMessageListener(new ClientListener(), HelloMessage.class);
+ +
+ +

ServerListener.java

+
+ +

+ +Create one ServerListener.java and make it extend com.jme3.network.MessageListener. +

+
public class ServerListener implements MessageListener<HostedConnection> {
+  public void messageReceived(HostedConnection source, Message message) {
+    if (message instanceof HelloMessage) {
+      // do something with the message
+      HelloMessage helloMessage = (HelloMessage) message;
+      System.out.println("Server received '" +helloMessage.getSomething() +"' from client #"+source.getId() );
+    } // else....
+  }
+ +

+For each message type, register a server listener to the server: +

+
myServer.addMessageListener(new ServerListener(), HelloMessage.class);
+ +
+ +

Creating and Sending Messages

+
+ +

+ +Let's create a new message of type HelloMessage: +

+
Message message = new HelloMessage("Hello World!");
+ +

+Now the client can send this message to the server: +

+
myClient.send(message);
+ +

+Or the server can broadcast this message to all HostedConnection (clients): +

+
Message message = new HelloMessage("Welcome!");
+myServer.broadcast(message);
+ +

+Or the server can send the message to a specific subset of clients (e.g. to HostedConnection conn1, conn2, and conn3): +

+
myServer.broadcast( Filters.in( conn1, conn2, conn3 ), message );
+ +

+Or the server can send the message to all but a few selected clients (e.g. to all HostedConnections but conn4): + +

+
myServer.broadcast( Filters.notEqualTo( conn4 ), message );
+ +

+The last two broadcasting methods use com.jme3.network.Filters to select a subset of recipients. If you know the exact list of recipients, always send the messages directly to them using the Filters; avoid flooding the network with unnecessary broadcasts to all. +

+ +
+ +

Identification and Rejection

+
+ +

+ +The ID of the Client and HostedConnection are the same at both ends of a connection. The ID is given out authoritatively by the Server. +

+
... myClient.getId() ...
+ +

+A server has a game version and game name property. Each client expects to communicate with a server with a certain game name and version. Test first whether the game name matches, and then whether game version matches, before sending any messages! If they do not match, you should refuse to connect, because unmatched clients and servers will likely miscommunicate. +

+ +

+

Typically, your networked game defines its own attributes (such as player ID) based on whatever criteria you want. If you want to look up player/client-specific information beyond the game version, you can set this information directly on the Client/HostedConnection object (see Getting Info About a Client). +

+

+ +
+ +

Closing Clients and Server Cleanly

+
+ +
+ +

Closing a Client

+
+ +

+ +You must override the client's destroy() method to close the connection cleanly when the player quits the client: +

+
  @Override
+  public void destroy() {
+      ... // custom code
+      myClient.close();
+      super.destroy();
+  }
+ +
+ +

Closing a Server

+
+ +

+ +You must override the server's destroy() method to close the connection when the server quits: +

+
  @Override
+  public void destroy() {
+      ... // custom code
+      myServer.close();
+      super.destroy();
+  }
+ +
+ +

Kicking a Client

+
+ +

+ +The server can kick a HostedConnection to make it disconnect. You should provide a String with further info (an explanation to the user what happened, e.g. "Shutting down for maintenance") for the server to send along. This info message can be used (displayed to the user) by a ClientStateListener. (See below) +

+
conn.close("We kick cheaters.");
+ +
+ +

Listening to Connection Notification

+
+ +

+ +The server and clients are notified about connection changes. +

+ +
+ +

ClientStateListener

+
+ +

+ +The com.jme3.network.ClientStateListener notifies the Client when the Client has fully connected to the server (including any internal handshaking), and when the Client is kicked (disconnected) from the server. + +

+
+ + + + + + + + + +
ClientStateListener interface method Purpose
public void clientConnected(Client c){} Implement here what happens as soon as this client has fully connected to the server.
public void clientDisconnected(Client c, DisconnectInfo info){} Implement here what happens after the server kicks this client. For example, display the DisconnectInfo to the user.
+ +

+ +First implement the ClientStateListener interface in the Client class. Then register it to myClient in MyGameClient's simpleInitApp() method: +

+
myClient.addClientStateListener(this);
+ +
+ +

ConnectionListener

+
+ +

+ +The com.jme3.network.ConnectionListener notifies the Server whenever new HostedConnections (clients) come and go. The listener notifies the server after the Client connection is fully established (including any internal handshaking). + +

+
+ + + + + + + + + +
ConnectionListener interface method Purpose
public void connectionAdded(Server s, HostedConnection c){} Implemenent here what happens after a new HostedConnection has joined the Server.
public void connectionRemoved(Server s, HostedConnection c){} Implement here what happens after a HostedConnection has left. E.g. a player has quit the game and the server removes his character.
+ +

+ +First implement the ConnectionListener interface in the Server class. Then register it to myServer in MyGameServer's simpleInitApp() method. + +

+
myServer.addConnectionListener(this);
+ +
+ +

UDP versus TCP

+
+ +

+ +SpiderMonkey supports both UDP (unreliable, fast) and TCP (reliable, slow) transport of messages. +

+
message1.setReliable(true); // TCP
+message2.setReliable(false); // UDP
+ + +
+ +

Important: Use Multi-Threading

+
+ +

+ +

You cannot modify the scenegraph directly from the network thread. A common example for such a modification is when you synchronize the player's position in the scene. You have to use Java Multithreading. +

+

+ +

+Multithreading means that you create a Callable. A Callable is a Java class representing any (possibly time-intensive) self-contained task that has an impact on the scene graph (such as positioning the player). You enqueue the Callable in the Executor of the client's OpenGL thread. The Callable ensures to executes the modification in sync with the update loop. +

+
app.enqueue(callable);
+ +

+Learn more about using multithreading in jME3 here. +

+ +

+For general advice, see the articles and by the Valve Developer Community. +

+ +
+ +

Troubleshooting

+
+ +

+ +If you have set up a server in your home network, and the game clients cannot reach the server from the outside, it's time to learn about . +

+
+ documentation, + network, + spidermonkey +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-13.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-13.png new file mode 100644 index 000000000..fd6531a07 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-13.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-example.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-example.png new file mode 100644 index 000000000..cf76d1a35 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-example.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-panels.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-panels.png new file mode 100644 index 000000000..a2eeea864 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-panels.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-simple-demo.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-simple-demo.png new file mode 100644 index 000000000..62b7e2776 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui-simple-demo.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui.png new file mode 100644 index 000000000..6b36f7565 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-gui.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-screen-layer-panel.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-screen-layer-panel.png new file mode 100644 index 000000000..468700983 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty-screen-layer-panel.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html new file mode 100644 index 000000000..f27afeb10 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html @@ -0,0 +1,195 @@ + +

Creating JME3 User Interfaces with Nifty GUI

+
+ +

+ +

+ +

+You may want your players to press a button to save a game, you want a scrolling text field for highscores, a text label to display the score, drop-downs to select keymap preferences, or checkboxes to specify multi-media options. Usually you solve these tasks by using Swing controls. Although it is possible to embed a jME3 canvas in a Swing GUI, a 3D game typically runs full-screen, or in a window of its own. +

+ +

+This document introduces you to , a Java library for building interactive graphical user interfaces (GUIs) for games or similar applications. Nifty GUI (the de.lessvoid.nifty package) is well integrated with jME3 through the com.jme3.niftygui package. You define the base GUI layout in XML, and control it dynamically from your Java code. The necessary JAR libraries are included in your jME3 download, you do not need to install anything extra. (Just make sure they are on the classpath.) + +

+ + +
+ +

Tutorial Overview

+
+ +

+ +Learn to add a Nifty GUI to your jME3 game by going through this multi-part tutorial: +

+
    +
  1. Understand the Nifty GUI Concepts described on this page.
    +
  2. +
  3. Browse this short list of Best Practices
    +
  4. +
  5. Lay out your graphical user interface:
    + +
  6. +
  7. Integrate the GUI into the game:
    + +
  8. +
  9. Interact with the GUI from Java
    +
  10. +
+ +
+ +

Must Know: Nifty GUI Concepts

+
+ +

+ + +

+ +

+Nifty GUIs are made up of the following elements: + +

+ + +
+ +

Resources

+
+ + +
+ +

JME-Nifty Sample Code

+
+ + +
+ +

External Documentation

+
+ +

+ +Learn more from the NiftyGUI page: +

+ + +
+ +

Next Steps

+
+ +

+ +Now that you understand the concepts and know where to find more information, learn how to lay out a simple graphical user interface. Typically, you start doing this in XML. +

+ + +
+ +

Nifty Logging (Nifty 1.3.1)

+
+ +

+If you want to disable the nifty log lines, add this code after you created nifty: + +

+
Logger.getLogger("de.lessvoid.nifty").setLevel(Level.SEVERE); 
+Logger.getLogger("NiftyInputEventHandlingLog").setLevel(Level.SEVERE); 
+
+ gui, + documentation, + nifty, + hud +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html new file mode 100644 index 000000000..eca0d3330 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html @@ -0,0 +1,321 @@ + +

Interacting with the GUI from Java

+
+
    +
  1. Nifty GUI Concepts
    +
  2. +
  3. Nifty GUI Best Practices
    +
  4. +
  5. Nifty GUI XML Layout or Nifty GUI Java Layout
    +
  6. +
  7. Nifty GUI Overlay or Nifty GUI Projection
    +
  8. +
  9. Nifty GUI Java Interaction
    +
  10. +
+ +

+ +In the previous parts of the tutorial, you created a two-screen user interface. But it is still static, and when you click the buttons, nothing happens yet. The purpose of the GUI is to communicate with your Java classes: Your game needs to know what the users clicked, which settings they chose, which values they entered into a field, etc. Similarly, the user needs to know what the currently game state is (score, health, etc). +

+ +
+ +

Connect GUI to Java Controller

+
+ +

+ +To let a Nifty screen communicate with the Java application, you register a ScreenController to every NiftyGUI screen. You create a ScreenController by creating a Java class that implements the de.lessvoid.nifty.screen.ScreenController interface and its abtract methods. +

+ +

+Pro Tip: Since you are writing a jME3 application, you can additionally make the ScreenController class extend the AbstractAppState class! This gives the ScreenController access to the application object and to the update loop! +

+
package tutorial;
+ 
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.screen.Screen;
+import de.lessvoid.nifty.screen.ScreenController;
+ 
+public class MyStartScreen extends AbstractAppState implements ScreenController {
+ 
+  private Nifty nifty;
+  private Screen screen;
+  private SimpleApplication app;
+ 
+  /** custom methods */ 
+ 
+  public MyStartScreen(String data) { 
+    /** Your custom constructor, can accept arguments */ 
+  } 
+ 
+  /** Nifty GUI ScreenControl methods */ 
+ 
+  public void bind(Nifty nifty, Screen screen) {
+    this.nifty = nifty;
+    this.screen = screen;
+  }
+ 
+  public void onStartScreen() { }
+ 
+  public void onEndScreen() { }
+ 
+  /** jME3 AppState methods */ 
+ 
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+    this.app=(SimpleApplication)app;
+  }
+ 
+  @Override
+  public void update(float tpf) { 
+    /** jME update loop! */ 
+  }
+ 
+}
+ +

+The name and package of your custom ScreenController class (here tutorial.MyStartScreen) goes into the controller parameter of the respective XML screen it belongs to. For example: +

+
<nifty>
+  <screen id="start" controller="tutorial.MyStartScreen">
+      <!-- layer and panel code ... -->
+  </screen>
+</nifty>
+ +

+Or the same in a Java syntax, respectively: +

+
  nifty.addScreen("start", new ScreenBuilder("start") {{
+      controller(new tutorial.MyStartScreen())}});
+ +

+Now the Java class MyStartScreen and this GUI screen (start) are connected. For this example you can also connect the hud screen to MyStartScreen. +

+ +
+ +

Make GUI and Java Interact

+
+ +

+ +In most cases, you will want to pass game data in and out of the ScreenController. Note that you can pass any custom arguments from your Java class into your ScreenController constructor (public MyStartScreen(GameData data) {}). +

+ +

+Use any combination of the three following approaches to make Java classes interact with the GUI. +

+ +
+ +

GUI Calls a Void Java Method

+
+ +

+ +This is how you respond to an GUI interaction such as clicks in XML GUIs: +

+
    +
  1. Add visibleToMouse="true" to the parent element!
    +
  2. +
  3. Embed the <interact /> element into the parent element.
    +
  4. +
  5. Specify the Java methods that you want to call when the users performs certain actions, such as clicking.
    +Example: <interact onClick="startGame(hud)" />
    +
  6. +
+ +

+ +Or this is how you respond to an GUI interaction such as clicks in Java GUIs: +

+
    +
  1. Add visibleToMouse(true); to the parent element!
    +
  2. +
  3. Embed one of the interact???() elements into the parent element
    +
  4. +
  5. Specify the Java method that you want to call after the interaction.
    +Example: interactOnClick("startGame(hud)");
    +
  6. +
+ +

+ +In the following example, we call the startGame() method when the player clicks the Start button, and quitGame() when the player clicks the Quit button. +

+
        <panel id="panel_bottom_left" height="50%" width="50%" valign="center" childLayout="center">  
+          <control name="button" label="Start" id="StartButton" align="center" valign="center" 
+          visibleToMouse="true" > 
+            <interact onClick="startGame(hud)"/>
+          </control>
+        </panel>
+ 
+        <panel id="panel_bottom_right" height="50%" width="50%" valign="center" childLayout="center">  
+          <control name="button" label="Quit" id="QuitButton" align="center" valign="center" 
+          visibleToMouse="true" > 
+            <interact onClick="quitGame()"/>
+          </control>
+        </panel>
+ +

+Or the same in a Java syntax, respectively: +

+
control(new ButtonBuilder("StartButton", "Start") {{
+  alignCenter();
+  valignCenter();
+  height("50%");
+  width("50%");
+  visibleToMouse(true);
+  interactOnClick("startGame(hud)");
+}});
+...
+ 
+control(new ButtonBuilder("QuitButton", "Quit") {{
+  alignCenter();
+  valignCenter();
+  height("50%");
+  width("50%");
+  visibleToMouse(true);
+  interactOnClick("quitGame()");
+}});
+ +

+Back in the MyStartScreen class, you specify what the startGame() and quitGame() methods do. As you see, you can pass String arguments (here hud) in the method call. You also see that you have access to the app object. +

+
public class MyStartScreen implements ScreenController {
+  ...
+ 
+  /** custom methods */ 
+  public void startGame(String nextScreen) {
+    nifty.gotoScreen(nextScreen);  // switch to another screen
+    // start the game and do some more stuff...
+  }
+ 
+  public void quitGame() {
+    app.stop(); 
+  }
+ 
+  ...
+}
+ +

+The startGame() example simply switches the GUI to the hud screen when the user clicks Start. Of course, in a real game, you would perform more steps here: Load the game level, switch to in-game input and navigation handling, set a custom running boolean to true, attach custom in-game AppStates ??? and lots more. +

+ +

+The quitGame() example shows that you have access to the application app object because you made the ScreenController extend AbstractAppState. (If you're creating code from this example, note that you'll need to make sure app is initialized before you can successfully call its methods.) +

+ +
+ +

GUI Gets Return Value from Java Method

+
+ +

+ +When the Nifty GUI is initialized, you can get data from Java. In this example, the Java class getPlayerName() in MyStartScreen defines the Text that is displayed in the textfield before the words 's Cool Game. +

+ +

+First define a Java method in the screen controller, in this example, getPlayerName(). +

+
public class MySettingsScreen implements ScreenController {
+  ...
+  public String getPlayerName(){
+    return System.getProperty("user.name");
+  }
+}
+ +

+Nifty uses ${CALL.getPlayerName()} to get the return value of the getPlayerName() method from your ScreenController Java class. +

+
<text text="${CALL.getPlayerName()}'s Cool Game" font="Interface/Fonts/Default.fnt" width="100%" height="100%" />
+ +

+Or the same in a Java syntax, respectively: +

+
text(new TextBuilder() {{
+  text("${CALL.getPlayerName()}'s Cool Game");
+  font("Interface/Fonts/Default.fnt");
+  height("100%");
+  width("100%");
+}});
+ +

+You can use this for Strings and numeric values (e.g. when you read settings from a file, you display the results in the GUI) and also for methods with side effects. +

+ +
+ +

Java Modifies Nifty Elements and Events

+
+ +

+ +You can also alter the appearance and functions of your nifty elements from Java. Make certain that the element that you want to alter has its id="name" attribute set, so you can identy and address it. +

+ +

+Here's an example of how to change an image called playerhealth: +

+
// load or create new image
+NiftyImage img = nifty.getRenderEngine().createImage("Interface/Images/face2.png", false);
+// find old image
+Element niftyElement = nifty.getCurrentScreen().findElementByName("playerhealth");
+// swap old with new image
+niftyElement.getRenderer(ImageRenderer.class).setImage(img);
+ +

+The same is valid for other elements, for example a text label "score": +

+
// find old text
+Element niftyElement = nifty.getCurrentScreen().findElementByName("score");
+// swap old with new text
+niftyElement.getRenderer(TextRenderer.class).setText("124");
+ +

+Similarly, to change the onClick() event of an element, create an ElementInteraction object: +

+
Element niftyElement = nifty.getCurrentScreen().findElementByName("myElement");
+niftyElement.getElementInteraction().getPrimary().setOnMouseOver(new NiftyMethodInvoker(nifty, "myCustomMethod()", this));
+ +

+For this to work, there already needs to be a (possibly inactive) <interact /> tag inside your xml element: + +

+
<interact onClick="doNothing()"/>
+ +
+ +

Next Steps

+
+ +

+ +You're done with the basic Nifty GUI for jME3 tutorial. You can proceed to advanced topics and learn how add controls and effects: +

+ +
+ gui, + documentation, + input, + control, + hud, + nifty +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_layout.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_layout.html new file mode 100644 index 000000000..6454f4eae --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_layout.html @@ -0,0 +1,642 @@ + +

Laying Out the GUI in Java

+
+
    +
  1. Nifty GUI Concepts
    +
  2. +
  3. Nifty GUI Best Practices
    +
  4. +
  5. Nifty GUI XML Layout or Nifty GUI Java Layout
    +
  6. +
  7. Nifty GUI Overlay or Nifty GUI Projection
    +
  8. +
  9. Interact with the GUI from Java
    +
  10. +
+ +

+ +Work in progress You can "draw" the GUI to the screen by writing Java code ??? alternatively to using XML. Typically you lay out the static base GUI in XML, and use Java commands if you need to change the GUI dynamically at runtime. In theory, you can also lay out the whole GUI in Java (but we don't cover that here). +

+ +
+ +

Sample Code

+
+ +

+ +Sample project +

+ + +

+ +Just so you get a quick picture what Nifty GUI's Java Syntax looks like, here is the most basic example. It creates a screen with a layer and a panel that contains a button. +

+
package mygame;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.niftygui.NiftyJmeDisplay;
+import de.lessvoid.nifty.Nifty;
+import de.lessvoid.nifty.builder.ScreenBuilder;
+import de.lessvoid.nifty.builder.LayerBuilder;
+import de.lessvoid.nifty.builder.PanelBuilder;
+import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
+import de.lessvoid.nifty.screen.DefaultScreenController;
+ 
+/**
+ * @author iamcreasy  
+*/
+public class Main extends SimpleApplication {
+ 
+    public static void main(String[] args) {
+        Main app = new Main();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
+            assetManager, inputManager, audioRenderer, guiViewPort);
+    Nifty nifty = niftyDisplay.getNifty();
+    guiViewPort.addProcessor(niftyDisplay);
+    flyCam.setDragToRotate(true);
+ 
+    nifty.loadStyleFile("nifty-default-styles.xml");
+    nifty.loadControlFile("nifty-default-controls.xml");
+ 
+    // <screen>
+    nifty.addScreen("Screen_ID", new ScreenBuilder("Hello Nifty Screen"){{
+        controller(new DefaultScreenController()); // Screen properties       
+ 
+        // <layer>
+        layer(new LayerBuilder("Layer_ID") {{
+            childLayoutVertical(); // layer properties, add more...
+ 
+            // <panel>
+            panel(new PanelBuilder("Panel_ID") {{
+               childLayoutCenter(); // panel properties, add more...               
+ 
+                // GUI elements
+                control(new ButtonBuilder("Button_ID", "Hello Nifty"){{
+                    alignCenter();
+                    valignCenter();
+                    height("5%");
+                    width("15%");
+                }});
+ 
+                //.. add more GUI elements here              
+ 
+            }});
+            // </panel>
+          }});
+        // </layer>
+      }}.build(nifty));
+    // </screen>
+ 
+    nifty.gotoScreen("Screen_ID"); // start the screen
+    }
+}
+ +
+ +

Implement Your GUI Layout

+
+ +

+ + +

+ +

+In this tutorial, you recreate the same screen as in the Nifty GUI XML example. +

+ +

+Create an Screen.Java file in the assets/Interfaces/ directory of your project. One Java file can contain several, or even all screens. As a reminder: Nifty displays one screen at a time; a screen contains several layers on top of one another; each layer contains panels that are embedded into another; the panels contain the actual content (text, images, or controls). +

+ +
+ +

Make Screens

+
+ +

+ +The following minimal Java file contains a start screen and a HUD screen. (Neither has been defined yet.) +

+
nifty.addScreen("start", new ScreenBuilder("start"){{
+    controller(new DefaultScreenController());
+    // <!-- ... -->
+  }}.build(nifty));
+ 
+nifty.addScreen("hud", new ScreenBuilder("hud"){{
+    controller(new DefaultScreenController());
+    // <!-- ... -->
+  }}.build(nifty));
+ +

+Every Nifty GUI must have a start screen. The others (in this example, the HUD screen) are optional. +

+ +
+ +

Make Layers

+
+ +

+ +The following Java code shows how we add layers to the start screen and HUD screen: +

+
nifty.addScreen("start", new ScreenBuilder("start"){{
+        controller(new DefaultScreenController());
+ 
+         // layer added
+         layer(new LayerBuilder("background") {{
+            childLayoutCenter();
+            backgroundColor("#000f");  
+ 
+            // <!-- ... -->
+         }});
+ 
+         layer(new LayerBuilder("foreground") {{
+                childLayoutVertical();
+                backgroundColor("#0000");        
+ 
+            // <!-- ... -->
+         }});
+         // layer added
+ 
+      }}.build(nifty));
+ +

+Repeat the same, but use +

+
nifty.addScreen("hud", new ScreenBuilder("hud"){{
+ +

+ for the HUD screen. +

+ +

+In a layer, you can now add panels and arrange them. Panels are containers that mark the areas where you want to display text, images, or controls (buttons etc) later. +

+ +
+ +

Make Panels

+
+ +

+ +A panel is the inner-most container (that will contain the actual content: text, images, or controls). You place panels inside layers. The following panels go into in the start screen: +

+
    nifty.addScreen("start", new ScreenBuilder("start") {{
+        controller(new DefaultScreenController());
+        layer(new LayerBuilder("background") {{
+            childLayoutCenter();
+            backgroundColor("#000f");
+            // <!-- ... -->
+        }});
+ 
+        layer(new LayerBuilder("foreground") {{
+                childLayoutVertical();
+                backgroundColor("#0000");
+ 
+            // panel added
+            panel(new PanelBuilder("panel_top") {{
+                childLayoutCenter();
+                alignCenter();
+                backgroundColor("#f008");
+                height("25%");
+                width("75%");
+            }});
+ 
+            panel(new PanelBuilder("panel_mid") {{
+                childLayoutCenter();
+                alignCenter();
+                backgroundColor("#0f08");
+                height("50%");
+                width("75%");
+            }});
+ 
+            panel(new PanelBuilder("panel_bottom") {{
+                childLayoutHorizontal();
+                alignCenter();
+                backgroundColor("#00f8");
+                height("25%");
+                width("75%");
+ 
+                panel(new PanelBuilder("panel_bottom_left") {{
+                    childLayoutCenter();
+                    valignCenter();
+                    backgroundColor("#44f8");
+                    height("50%");
+                    width("50%");
+                }});
+ 
+                panel(new PanelBuilder("panel_bottom_right") {{
+                    childLayoutCenter();
+                    valignCenter();
+                    backgroundColor("#88f8");
+                    height("50%");
+                    width("50%");
+                }});
+            }}); // panel added
+        }});
+ 
+    }}.build(nifty));
+ +

+The following panels go into in the hud screen: +

+
    nifty.addScreen("hud", new ScreenBuilder("hud") {{
+        controller(new DefaultScreenController());
+ 
+        layer(new LayerBuilder("background") {{
+            childLayoutCenter();
+            backgroundColor("#000f");
+            // <!-- ... -->
+        }});
+ 
+        layer(new LayerBuilder("foreground") {{
+            childLayoutHorizontal();
+            backgroundColor("#0000");
+ 
+            // panel added
+            panel(new PanelBuilder("panel_left") {{
+                childLayoutVertical();
+                backgroundColor("#0f08");
+                height("100%");
+                width("80%");
+                // <!-- spacer -->
+            }});
+ 
+            panel(new PanelBuilder("panel_right") {{
+                childLayoutVertical();
+                backgroundColor("#00f8");
+                height("100%");
+                width("20%");
+ 
+                panel(new PanelBuilder("panel_top_right1") {{
+                    childLayoutCenter();
+                    backgroundColor("#00f8");
+                    height("15%");
+                    width("100%");
+                }});
+ 
+                panel(new PanelBuilder("panel_top_right2") {{
+                    childLayoutCenter();
+                    backgroundColor("#44f8");
+                    height("15%");
+                    width("100%");
+                }});
+ 
+                panel(new PanelBuilder("panel_bot_right") {{
+                    childLayoutCenter();
+                    valignCenter();
+                    backgroundColor("#88f8");
+                    height("70%");
+                    width("100%");
+                }});
+            }}); // panel added
+        }});
+    }}.build(nifty));
+ +

+Try the sample. Remember to activate a screen using nifty.gotoScreen("start"); or hud respectively. +The result should look as follows: +

+ +

+ +

+ +
+ +

Adding Content to Panels

+
+ +

+ +See also on the Nifty GUI site. +

+ +
+ +

Add Images

+
+ +

+ +The start-background.png image is a fullscreen background picture. In the start screen, add the following image element: + +

+
    nifty.addScreen("start", new ScreenBuilder("start") {{
+        controller(new DefaultScreenController());
+        layer(new LayerBuilder("background") {{
+            childLayoutCenter();
+            backgroundColor("#000f");
+ 
+            // add image
+            image(new ImageBuilder() {{
+                filename("Interface/tutorial/start-background.png");
+            }});
+ 
+        }});
+ +

+The hud-frame.png image is a transparent frame that we use as HUD decoration. In the hud screen, add the following image element: + +

+
    nifty.addScreen("hud", new ScreenBuilder("hud") {{
+        controller(new DefaultScreenController());
+ 
+        layer(new LayerBuilder("background") {{
+            childLayoutCenter();
+            backgroundColor("#000f");
+ 
+            // add image
+            image(new ImageBuilder() {{
+                filename("Interface/tutorial/hud-frame.png");
+            }});
+ 
+        }});
+ +

+The face1.png image is an image that you want to use as a status icon. +In the hud screen's foreground layer, add the following image element: + +

+
                panel(new PanelBuilder("panel_top_right2") {{
+                    childLayoutCenter();
+                    backgroundColor("#44f8");
+                    height("15%");
+                    width("100%");
+ 
+                    // add image
+                    image(new ImageBuilder() {{
+                        filename("Interface/tutorial/face1.png");
+                        valignCenter();
+                        alignCenter();
+                        height("50%");
+                        width("30%");
+                    }});
+ 
+                }});
+ +

+ +This image is scaled to use 50% of the height and 30% of the width of its container. +

+ +
+ +

Add Static Text

+
+ +

+ +The game title is a typical example of static text. In the start screen, add the following text element: + +

+
           // panel added
+            panel(new PanelBuilder("panel_top") {{
+                childLayoutCenter();
+                alignCenter();
+                backgroundColor("#f008");
+                height("25%");
+                width("75%");
+ 
+                // add text
+                text(new TextBuilder() {{
+                    text("My Cool Game");
+                    font("Interface/Fonts/Default.fnt");
+                    height("100%");
+                    width("100%");
+                }});
+ 
+            }});
+ +

+For longer pieces of static text, such as an introduction, you can use wrap="true". Add the following text element to the Start screen: +

+
            panel(new PanelBuilder("panel_mid") {{
+                childLayoutCenter();
+                alignCenter();
+                backgroundColor("#0f08");
+                height("50%");
+                width("75%");
+                // add text
+                text(new TextBuilder() {{
+                    text("Here goes some text describing the game and the rules and stuff. "+
+                         "Incidentally, the text is quite long and needs to wrap at the end of lines. ");
+                    font("Interface/Fonts/Default.fnt");
+                    wrap(true);
+                    height("100%");
+                    width("100%");
+                }});
+ 
+            }});
+ +

+The font used is jME3's default font "Interface/Fonts/Default.fnt" which is included in the jMonkeyEngine.JAR. You can add your own fonts to your own assets/Interface directory. +

+ +
+ +

Add Controls

+
+ +

+ +Before you can use any control, you must load a Control Definition first. Add the following two lines before your screen definitions: +

+
    nifty.loadStyleFile("nifty-default-styles.xml");
+    nifty.loadControlFile("nifty-default-controls.xml");
+ +
+ +

Label Control

+
+ +

+ +Use label controls for text that you want to edit dynamically from Java. One example for this is the score display. +In the hud screen's foreground layer, add the following text element: +

+
                panel(new PanelBuilder("panel_top_right1") {{
+                    childLayoutCenter();
+                    backgroundColor("#00f8");
+                    height("15%");
+                    width("100%");
+ 
+                    control(new LabelBuilder(){{
+                        color("#000"); 
+                        text("123"); 
+                        width("100%"); 
+                        height("100%");
+                    }});
+ +

+Note that the width and height do not scale the bitmap font, but make indirectly certain it is centered. If you want a different size for the font, you need to provide an extra bitmap font (they come with fixes sizes and don't scale well). +

+ +
+ +

Button Control

+
+ +

+ +Our GUI plan asks for two buttons on the start screen. You add the Start and Quit buttons to the bottom panel of the start screen using the <control> element: +

+
                panel(new PanelBuilder("panel_bottom_left") {{
+                    childLayoutCenter();
+                    valignCenter();
+                    backgroundColor("#44f8");
+                    height("50%");
+                    width("50%");
+ 
+                    // add control
+                    control(new ButtonBuilder("StartButton", "Start") {{
+                      alignCenter();
+                      valignCenter();
+                      height("50%");
+                      width("50%");
+                    }});
+ 
+                }});
+ 
+                panel(new PanelBuilder("panel_bottom_right") {{
+                    childLayoutCenter();
+                    valignCenter();
+                    backgroundColor("#88f8");
+                    height("50%");
+                    width("50%");
+ 
+                    // add control
+                    control(new ButtonBuilder("QuitButton", "Quit") {{
+                      alignCenter();
+                      valignCenter();
+                      height("50%");
+                      width("50%");
+                    }});
+ 
+                }});
+ +

+Note that these controls don't do anything yet ??? we'll get to that soon. +

+ +
+ +

Other Controls

+
+ +

+ +Nifty additionally offers many customizable controls such as check boxes, text fields, menus, chats, tabs, ??? See also on the Nifty GUI site. +

+ +
+ +

Intermediate Result

+
+ +

+ +When you preview this code in the jMonkeyEngine SDK, our tutorial demo should looks as follows: A start screen with two buttons, and a game screen with a simple HUD frame and a blue cube (which stands for any jME3 game content). +

+ +

+Tip: Remove all lines that set background colors, you only needed them to see the arrangement. +

+ +

+ +

+ +
+ +

Nifty Java Settings

+
+ +

+ +Before initializing the nifty screens, you set up properties and register media. +

+
+ + + + + + + + + + + + + + + + + + +
Nifty Method Description
registerSound("mysound", "Interface/abc.wav");
registerMusic("mymusic", "Interface/xyz.ogg");
registerMouseCursor("mypointer", "Interface/abc.png", 5, 4);
registerEffect(?); ?
setDebugOptionPanelColors(true); Highlight all panels, makes it easier to arrange them.
+ +

+ +Example: +

+
nifty.registerMouseCursor("hand", "Interface/mouse-cursor-hand.png", 5, 4);
+ +
+ +

Next Steps

+
+ +

+ +Integrate the GUI into the game. Typically, you will overlay the GUI. +

+ +
+ gui, + documentation, + nifty, + hud +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_overlay.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_overlay.html new file mode 100644 index 000000000..e0df90f6e --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_overlay.html @@ -0,0 +1,92 @@ + +

Integrating Nifty GUI: Overlay

+
+
    +
  1. Nifty GUI Concepts
    +
  2. +
  3. Nifty GUI Best Practices
    +
  4. +
  5. Nifty GUI XML Layout or Nifty GUI Java Layout
    +
  6. +
  7. Nifty GUI Overlay or Nifty GUI Projection
    +
  8. +
  9. Interact with the GUI from Java
    +
  10. +
+ +

+ + +

+ +

+Typically, you define a key (for example escape) that switches the GUI on and off. The GUI can be a StartScreen, OptionsScreen, CharacterCreationScreen, etc. While the GUI is up, you pause the running game, and then overlay the GUI. You also must switch to a different set of user inputs while the game is paused, so the player can use the mouse pointer and keyboard to interact with the GUI. +

+ +

+You can also project the GUI as a texture onto a mesh texture (but then you cannot click to select). +On this page, we look at the overlay variant, which is more commonly used in games. +

+ +
+ +

Sample Code

+
+ + +
+ +

Overlaying the User Interface Over the Screen

+
+ +

+ +This code shows you how to overlay anything on the screen with the GUI. This is the most common usecase. +

+
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
+    assetManager, inputManager, audioRenderer, guiViewPort);
+/** Create a new NiftyGUI object */
+Nifty nifty = niftyDisplay.getNifty();
+/** Read your XML and initialize your custom ScreenController */
+nifty.fromXml("Interface/tutorial/step2/screen.xml", "start");
+// nifty.fromXml("Interface/helloworld.xml", "start", new MySettingsScreen(data));
+// attach the Nifty display to the gui view port as a processor
+guiViewPort.addProcessor(niftyDisplay);
+// disable the fly cam
+flyCam.setDragToRotate(true);
+ +

+Currently you do not have a ScreenController ??? we will create one in the next exercise. As soon as you have a screen controller, you will use the commented variant of the XML loading method: +

+
nifty.fromXml("Interface/helloworld.xml", "start", new MySettingsScreen());
+ +

+The MySettingsScreen class is a custom de.lessvoid.nifty.screen.ScreenController in which you will implement your GUI behaviour. +

+ +
+ +

Next Steps

+
+ +

+ +Now that you have layed out and integrated the GUI in your app, you want to respond to user input and display the current game. Time to create a ScreenController! +

+ +
+ gui, + documentation, + nifty, + hud +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_popup_menu.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_popup_menu.html new file mode 100644 index 000000000..157376a25 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_popup_menu.html @@ -0,0 +1,94 @@ + +

Nifty GUI: Create a PopUp Menu

+
+ +

+ +Even though you create and populate the popup menu in Java, you still need a "placeholder" in your XML file. +The popup element needs to be placed outside of any screen! +

+
<useControls filename="nifty-default-controls.xml"/>
+...
+<popup id="niftyPopupMenu" childLayout="absolute-inside"
+       controller="ControllerOfYourChoice" width="10%">
+  <interact onClick="closePopup()" onSecondaryClick="closePopup()" onTertiaryClick="closePopup()" />
+  <control id="#menu" name="niftyMenu" />
+</popup>
+...
+ +

+A brief explanation of some the attributes above: +

+ + +

+ +The Java code within your defined ScreenController implementation: +

+
private Element popup;
+...
+public void createMyPopupMenu(){
+  popup = nifty.createPopup("niftyPopupMenu");
+  Menu myMenu = popup.findNiftyControl("#menu", Menu.class);
+  myMenu.setWidth(new SizeValue("100px")); // must be set
+  myMenu.addMenuItem("Click me!", "menuItemIcon.png", 
+    new menuItem("menuItemid", "blah blah")); // menuItem is a custom class
+  nifty.subscribe(
+    nifty.getCurrentScreen(), 
+    myMenu.getId(), 
+    MenuItemActivatedEvent.class, 
+    new MenuItemActivatedEventSubscriber());
+}
+ 
+public void showMenu() { // the method to trigger the menu
+  // If this is a menu that is going to be used many times, then
+  // call this in your constructor rather than here   
+  createMyPopupMenu() 
+  // call the popup to screen of your choice:
+  nifty.showPopup(nifty.getCurrentScreen(), popup.getId(), null); 
+}
+ 
+private class menuItem {
+  public String id;
+  public String name;
+  public menuItem(String id, String name){
+    this.id= id;
+    this.name = name;
+  }
+}
+ + +

+ +To handle menu item events (i.e. calling a method when you click on a menu item), you register (subscribe) a EventTopicSubscriber<MenuItemActivatedEvent> class implementation to a nifty screen and element. +

+
  private class MenuItemActivatedEventSubscriber 
+    implements EventTopicSubscriber<MenuItemActivatedEvent> {
+ 
+    @Override
+    public void onEvent(final String id, final MenuItemActivatedEvent event) {
+    	menuItem item = (menuItem) event.getItem();
+       if ("menuItemid".equals(item.id)) {
+		//do something !!!
+       }
+    }
+  };
+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_projection.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_projection.html new file mode 100644 index 000000000..d96c37fe2 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_projection.html @@ -0,0 +1,111 @@ + +

Integrating Nifty GUI: Projection

+
+
    +
  1. Nifty GUI Concepts
    +
  2. +
  3. Nifty GUI Best Practices
    +
  4. +
  5. Nifty GUI XML Layout or Nifty GUI Java Layout
    +
  6. +
  7. Nifty GUI Overlay or Nifty GUI Projection
    +
  8. +
  9. Interact with the GUI from Java
    +
  10. +
+ +

+ + +

+ +

+Typically you define a key (for example escape) to switch the GUI on and off. Then you overlay the running game with the GUI (you will most likely pause the game then). +

+ +

+Alternatively, you can also project the GUI as a texture onto a mesh textures inside the game. Allthough this looks cool and "immersive", this approach is rarely used since it is difficult to record clicks this way. You can only interact with this projected GUI by keyboard, or programmatically. You can select input fields using the arrow keys, and trigger actions using the return key. +

+ +

+This GUI projection variant is less commonly used than the GUI overlay variant. Usecases for GUI projection are, for example, a player avatar using an in-game computer screen. +

+ +
+ +

Sample Code

+
+ + +
+ +

Projecting the User Interface Onto a Texture

+
+ +

+ +You can project the Nifty GUI onto a texture, load the texture into a material, and assign it to a Geometry (Quads or Boxes are best). +

+
/** Create a special viewport for the Nifty GUI */
+ViewPort niftyView = renderManager.createPreView("NiftyView", new Camera(1024, 768));
+niftyView.setClearEnabled(true);
+/** Create a new NiftyJmeDisplay for the integration */
+NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
+  assetManager,  inputManager,  audioRenderer,  niftyView);
+/** Create a new NiftyGUI object */
+Nifty nifty = niftyDisplay.getNifty();
+/** Read your XML and initialize your custom ScreenController */
+nifty.fromXml("Interface/helloworld.xml", "start", new MySettingsScreen(data));
+ 
+/** Prepare a framebuffer for the texture niftytex */
+niftyView.addProcessor(niftyDisplay);
+FrameBuffer fb = new FrameBuffer(1024, 768, 0);
+fb.setDepthBuffer(Format.Depth);
+Texture2D niftytex = new Texture2D(1024, 768, Format.RGB8);
+fb.setColorTexture(niftytex);
+niftyView.setClearEnabled(true);
+niftyView.setOutputFrameBuffer(fb);
+ 
+/** This is the 3D cube we project the GUI on */
+Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+Geometry geom = new Geometry("Box", b);
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+mat.setTexture("m_ColorMap", niftytex); /** Here comes the texture! */
+geom.setMaterial(mat);
+rootNode.attachChild(geom);
+ +

+The MySettingsScreen class is a custom de.lessvoid.nifty.screen.ScreenController in which you implement your GUI behaviour. The variable data contains an object that you use to exchange state info with the game. See Nifty GUI Java Interaction for details on how to create this class. +

+ +

+Run the code sample. You select buttons on this GUI with the arrow keys and then press return. Note that clicking on the texture will not work! +

+ +
+ +

Next Steps

+
+ +

+ +Now that you have layed out and integrated the GUI in your app, you want to respond to user input and display the current game. +

+ +
+ gui, + documentation, + nifty, + hud, + texture +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html new file mode 100644 index 000000000..61c385d43 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html @@ -0,0 +1,349 @@ + +

Nifty GUI 1.3 - Usecase Scenarios

+
+ +

+ +This document contains typical NiftyGUI usecase scenarios, such as adding effects, game states, and creating typical game screens. +

+ +

+Requirements: These tips assume that you have read and understood the Creating JME3 User Interfaces with Nifty GUI tutorial, and have already laid out a basic GUI that interacts with your JME3 application. Here you learn how you integrate the GUI better, and add effects and advanced controls. +

+ +
+ +

Switch Game States

+
+ +

+ +In a JME game, you typically have three game states: +

+
    +
  1. Stopped: The game is stopped, a StartScreen is displayed.
    +
  2. +
  3. Running: The game is running, the in-game HudScreen is displayed.
    +
  4. +
  5. Paused: The game is paused, a PausedScreen is displayed.
    +
  6. +
+ +

+ +(Aside: Additionally, the Stopped state often contains a LoadScreen, LogonScreen, OptionsScreen, CharacterCreationScreen, HighScoreScreen, CreditsScreen, etc. Some games let you access the OptionsScreen in the Paused state as well. The Running state can also contain an InventoryScreen, ItemShopScreen, StatsScreen, SkillScreen, etc.) +

+ +

+In JME, game states are implemented as custom AppState objects. Write each AppState so it brings its own input mappings, rootNode content, update loop behaviour, etc with it. +

+
    +
  1. Stopped: StartScreen AppState + GuiInputs AppState
    +
  2. +
  3. Paused: PausedScreen AppState + GuiInputs AppState
    +
  4. +
  5. Running: HudScreen AppState + InGameInputs AppState + BulletAppState (jme physics), ???
    +
  6. +
+ +

+ +When the player switches between game states, you detach one set of AppStates, and attach another. For example, when the player pauses the running game, you use a boolean switch to pause the game loop and deactivate the game inputs (shooting, navigation). The screen is overlayed with a PausedScreen, which contains a visible mouse pointer and a Continue button. When the player clicks Continue, the mouse pointer is deactivated, the in-game input and navigational mappings are activated, and the game loop continues. +

+ +
+ +

Get Access to Application and Update Loop

+
+ +

+ +Since you are writing a jME3 application, you can additionally make any ScreenController class extend the AbstractAppState class. +This gives the ScreenController access to the application object and to the update loop! +

+
public class StartScreenState extends AbstractAppState {
+ 
+  private ViewPort viewPort;
+  private Node rootNode;
+  private Node guiNode;
+  private AssetManager assetManager;
+  private Node localRootNode = new Node("Start Screen RootNode");
+  private Node localGuiNode = new Node("Start Screen GuiNode");
+  private final ColorRGBA backgroundColor = ColorRGBA.Gray;  
+ 
+public StartScreenState(SimpleApplication app){
+    this.rootNode     = app.getRootNode();
+    this.viewPort     = app.getViewPort();
+    this.guiNode      = app.getGuiNode();
+    this.assetManager = app.getAssetManager();  
+  }
+ 
+  @Override
+  public void initialize(AppStateManager stateManager, Application app) {
+    super.initialize(stateManager, app);
+    /** init the screen */    
+  }
+ 
+  @Override
+  public void update(float tpf) {
+    /** any main loop action happens here */
+  }
+ 
+  @Override
+  public void stateAttached(AppStateManager stateManager) {
+    rootNode.attachChild(localRootNode);
+    guiNode.attachChild(localGuiNode);
+    viewPort.setBackgroundColor(backgroundColor);
+  }
+ 
+  @Override
+  public void stateDetached(AppStateManager stateManager) {
+    rootNode.detachChild(localRootNode);
+    guiNode.detachChild(localGuiNode);
+  }
+ 
+}
+ +
+ +

Know Your Variables

+
+
+ + + + + + + + + + + + + + + +
VariableDescription
${CALL.myMethod()} Calls a method in the current ScreenController and gets the method's return String. The method can also be void and have a side effect, e.g. play a sound etc.
${ENV.HOME} Returns the path to user's home directory.
${ENV.key} Looks up key in the environment variables. Use it like Java's System.getEnv("key").
${PROP.key} looks up key in the Nifty properties. Use Nifty.setGlobalproperties(properties) and Nifty.getGlobalproperties("key"). Or SystemGetProperties(key);
+ +

+ +See also: +

+ +
+ +

Use ScreenControllers for Mutally Exclusive Functionality

+
+ +

+ +Technically you are free to create one ScreenController class for each screen, or reuse the same ScreenController for all or some of them. In the end it may be best to create individual ScreenControllers for functionality that is mutually exclusive. +

+ +

+For example, create a MyHudScreen.java for the hud screen, and a MyStartScreen.java for the start screen. +

+ + +
+ +

Create a "Loading..." Screen

+
+ +

+ +Get the full Loading Screen tutorial here. +

+ +
+ +

Create a Popup Menu

+
+ +

+ +Get the full Nifty GUI PopUp Menu tutorial here. +

+ +
+ +

Add Visual Effects

+
+ +

+ +You can register effects to screen elements. +

+ + +

+ +Here is an example that moves a panel when the startScreen opens. You place an < effect > tag inside the element that you want to be affected. +

+
<panel height="25%" width="35%" ...>
+  <effect>
+    <onStartScreen name="move" mode="in" direction="top" length="300" startDelay="0" inherit="true"/>
+  </effect>
+</panel>
+ +

+Learn more from the NiftyGUI page: +

+ + +
+ +

Add Sound Effects

+
+ +

+ +Playing sounds using Nifty is also possible with a playSound effect as trigger. Remember to first register the sound that you want to play: +

+
<registerSound id="myclick" filename="Interface/sounds/ButtonClick.ogg" />
+...
+<label>
+  <effect>
+    <onClick name="playSound" sound="myclick"/>
+  </effect>
+</label>
+ +
+ +

Pass ClickLoc From Nifty to Java

+
+ +

+ +After a mouse click, you may want to record the 2D clickLoc and send this info to your Java application. Typical ScreenController methods however only have a String argument. You'd have to convert the String to ints. +

+ +

+To pass the clickLoc as two ints, you can use the special (int x, int y) syntax in the ScreenController: + +

+
  public void clicked(int x, int y) {
+    // here you can use the x and y of the clickLoc
+  }
+ +

+ +In the Nifty GUI screen code (e.g. XML file) you must call the (int x, int y) method without any parameters! + +

+
<interact onClick="clicked()"/>  
+ +

+ +You can name the method (here clicked) what ever you like, as long as you keep the argument syntax. +

+ +
+ +

Load Several XML Files

+
+ +

+ +The basic Nifty GUI example showed how to use the nifty.fromXML() method to load one XML file containing all Nifty GUI screens. +The following code sample shows how you can load several XML files into one nifty object. Loading several files with nifty.addXml() allows you to split up each screen into one XML file, instead of all into one hard-to-read XML file. +

+
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
+Nifty nifty = niftyDisplay.getNifty();
+nifty.addXml("Interface/Screens/OptionsScreen.xml");
+nifty.addXml("Interface/Screens/StartScreen.xml");
+nifty.gotoScreen("startScreen");
+StartScreenControl screenControl = (StartScreenControl) nifty.getScreen("startScreen").getScreenController();
+OptionsScreenControl optionsControl = (OptionsScreenControl) nifty.getScreen("optionsScreen").getScreenController();
+stateManager.attach(screenControl);
+stateManager.attach(optionsControl);
+guiViewPort.addProcessor(niftyDisplay);
+ +
+ +

Register additional explicit screen controllers

+
+ +

+ +In addition to the nifty.addXml() methods to attach many nifty XML files, there exists a nifty.registerScreenController() method to explicitly attach more screen controllers. +

+ +

+The following code sample shows how you can explicitly attach several screen controllers before adding the XML file to nifty, which would otherwise cause nifty to implicitly instantiate the screen controller class. +

+
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager, inputManager, audioRenderer, viewPort);
+Nifty nifty = niftyDisplay.getNifty();
+ 
+nifty.registerScreenController(new OptionsScreenController(randomConstructorArgument));
+nifty.addXml("Interface/Screens/OptionsScreen.xml");
+ +
+ +

Design Your Own Styles

+
+ +

+ +By default, your Nifty XML screens use the built.in styles: +

+
 <useStyles filename="nifty-default-styles.xml" /> 
+ +

+But you can switch to a set of custom styles in your game project's asset directory like this: +

+
 <useStyles filename="Interface/Styles/myCustomStyles.xml" /> 
+ +

+Inside myCustomStyles.xml you define styles like this: +

+
<?xml version="1.0" encoding="UTF-8"?>
+<nifty-styles>
+  <useStyles filename="Interface/Styles/Font/myCustomFontStyle.xml" />
+  <useStyles filename="Interface/Styles/Button/myCustomButtonStyle.xml" />
+  <useStyles filename="Interface/Styles/Label/myCustomLabelStyle.xml" />
+  ...
+</nifty-styles>
+ +

+Learn more about how to create styles by looking at the for ???nifty-style-black???. Copy it as a template and change it to create your own style. + +

+
+ +

+Learn more from the NiftyGUI page: +

+ +
+ gui, + documentation, + nifty, + hud, + click, + state, + states, + sound, + effect +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html new file mode 100644 index 000000000..87cd6cb5a --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html @@ -0,0 +1,391 @@ + +

Laying out the GUI in XML

+
+
    +
  1. Nifty GUI Concepts
    +
  2. +
  3. Nifty GUI Best Practices
    +
  4. +
  5. Nifty GUI XML Layout or Nifty GUI Java Layout
    +
  6. +
  7. Nifty GUI Overlay or Nifty GUI Projection
    +
  8. +
  9. Interact with the GUI from Java
    +
  10. +
+ +

+ +You can "draw" the GUI to the screen by writing XML code (alternatively you can also use Java). +

+ +
+ +

Plan Your GUI Layout

+
+ +

+ + +

+ +

+In this tutorial, you want to create two game screens: An out-of-game StartScreen that the players see before the game starts; and an in-game that displays info during the game. Before writing code, you plan the GUI layout, either on paper or in a graphic application. +

+ +

+The StartScreen contains: +

+ + +

+ +The HUD contains: +

+ + +
+ +

Implement Your GUI Layout

+
+ +

+ + +

+ +

+Create an empty screen.xml file in the assets/Interfaces/ directory of your project. One XML file can contain several, or even all screens. As a reminder: Nifty displays one screen at a time; a screen contains several layers on top of one another; each layer contains panels that are embedded into another; the panels contain the actual content (text, images, or controls). +

+ +
+ +

Make Screens

+
+ +

+ +The following minimal XML file contains a start screen and a HUD screen. (Neither has been defined yet.) +

+
<?xml version="1.0" encoding="UTF-8"?>
+<nifty xmlns="http://nifty-gui.sourceforge.net/nifty-1.3.xsd"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd">
+  <screen id="start">
+    <!-- ... -->
+  </screen>
+  <screen id="hud">
+    <!-- ... -->
+  </screen>
+</nifty>
+ +

+Every Nifty GUI must have a start screen. The others (in this example, the HUD screen) are optional. +

+ +

+Note: In the following examples, the XML schema header is abbreviated to just <nifty>. +

+ +
+ +

Make Layers

+
+ +

+ +The following minimal XML file shows how we added layers to the start screen and HUD screen: +

+
<nifty>
+  <screen id="start">
+    <layer id="background" backgroundColor="#000f">
+      <!-- ... -->
+    </layer>
+    <layer id="foreground" backgroundColor="#0000" childLayout="vertical">
+      <!-- ... -->
+    </layer>
+  </screen>
+  <screen id="hud">
+    <layer id="background" backgroundColor="#000f">
+      <!-- ... -->
+    </layer>
+    <layer id="foreground" backgroundColor="#0000" childLayout="horizontal">
+      <!-- ... -->
+    </layer>
+  </screen>
+</nifty>
+ +

+In a layer, you can now add panels and arrange them. Panels are containers that mark the areas where you want to display text, images, or controls (buttons etc) later. +

+ +
+ +

Make Panels

+
+ +

+ +A panel is the inner-most container (that will contain the actual content: text, images, or controls). You place panels inside layers. The following panels go into in the start screen's foreground layer: +

+
      <panel id="panel_top" height="25%" width="75%" align="center" childLayout="center"
+             backgroundColor="#f008">  
+      </panel>
+      <panel id="panel_mid" height="50%" width="75%" align="center" childLayout="center"
+             backgroundColor="#0f08">  
+      </panel>
+      <panel id="panel_bottom" height="25%" width="75%" align="center" childLayout="horizontal"
+             backgroundColor="#00f8">  
+        <panel id="panel_bottom_left" height="50%" width="50%" valign="center" childLayout="center" 
+             backgroundColor="#44f8">  
+        </panel>
+        <panel id="panel_bottom_right" height="50%" width="50%" valign="center" childLayout="center"
+             backgroundColor="#88f8">  
+        </panel>
+      </panel>
+ +

+The following panels go into in the hud screen's foreground layer: +

+
      <panel id="panel_left" width="80%" height="100%" childLayout="vertical" 
+      backgroundColor="#0f08">  
+        <!-- spacer -->
+      </panel>
+      <panel id="panel_right" width="20%" height="100%" childLayout="vertical" 
+      backgroundColor="#00f8" >  
+        <panel id="panel_top_right1" width="100%" height="15%" childLayout="center"
+             backgroundColor="#00f8">  
+        </panel>
+        <panel id="panel_top_right2" width="100%" height="15%" childLayout="center"
+             backgroundColor="#44f8">  
+        </panel>
+        <panel id="panel_bot_right" width="100%" height="70%" valign="center"
+             backgroundColor="#88f8">  
+        </panel>
+      </panel>
+ +

+The result should look as follows: +

+ +

+ +

+ +
+ +

Adding Content to Panels

+
+ +

+ +See also on the Nifty GUI site. +

+ +
+ +

Add Images

+
+ +

+ +The start-background.png image is a fullscreen background picture. In the start screen, add the following image element: + +

+
    <layer id="background" childLayout="center">
+        <image filename="Interface/tutorial/step2/start-background.png"></image>
+    </layer>
+ +

+The hud-frame.png image is a transparent frame that we use as HUD decoration. In the hud screen, add the following image element: + +

+
    <layer id="background" childLayout="center">
+        <image filename="Interface/tutorial/step2/hud-frame.png"></image>
+    </layer>
+ +

+In order to make the hud-frame.png independent of the screen resolution you are using, you could use the imageMode attribute on the image element +

+
    <layer id="background" childLayout="center">
+        <image filename="Interface/tutorial/step2/hud-frame.png" imageMode="resize:40,490,110,170,40,560,40,270,40,560,40,40" width="100%" height="100%"/>
+    </layer>
+ +

+The face1.png image is an image that you want to use as a status icon. +In the hud screen's foreground layer, add the following image element: + +

+
        <panel id="panel_bottom_left" height="75%" width="20%" valign="center" childLayout="center">  
+            <image filename="Interface/tutorial/step2/face1.png" 
+                valign="center" align="center" height="50%" width="30%" >
+            </image>
+        </panel>
+ +

+ +This image is scaled to use 50% of the height and 30% of the width of its container. +

+ +
+ +

Add Static Text

+
+ +

+ +The game title is a typical example of static text. In the start screen, add the following text element: + +

+
      <panel id="panel_top" height="25%" width="75%" align="center" childLayout="center">  
+          <text text="My Cool Game" font="Interface/Fonts/Default.fnt" width="100%" height="100%" />
+      </panel>
+ +

+For longer pieces of static text, such as an introduction, you can use wrap="true". Add the following text element to the Start screen: +

+
      <panel id="panel_mid" height="50%" width="75%" align="center" childLayout="center">       
+        <text text="Here goes some text describing the game and the rules and stuff. Incidentally, 
+         the text is quite long and needs to wrap at the end of lines. ..." 
+        font="Interface/Fonts/Default.fnt" width="100%" height="100%" wrap="true" />
+      </panel>
+ +

+The font used is jME3's default font "Interface/Fonts/Default.fnt" which is included in the jMonkeyEngine.JAR. You can add your own fonts to your own assets/Interface directory. +

+ +
+ +

Add Controls

+
+ +

+ +Before you can use any control, you must load a Control Definition first. Add the following two lines before your screen definitions: +

+
  <useControls filename="nifty-default-controls.xml" />
+  <useStyles filename="nifty-default-styles.xml" />
+ +
+ +

Label Control

+
+ +

+ +Use label controls for text that you want to edit dynamically from Java. One example for this is the score display. +In the hud screen's foreground layer, add the following text element: +

+
        <panel id="panel_top_right" height="100%" width="15%" childLayout="center">  
+            <control name="label" color="#000" text="123" width="100%" height="100%" />
+        </panel>
+ +

+Note that the width and height do not scale the bitmap font, but indirectly make certain it is centered. If you want a different size for the font, you need to provide an extra bitmap font (they come with fixed sizes and don't scale well). +

+ +
+ +

Button Control

+
+ +

+ +Our GUI plan asks for two buttons on the start screen. You add the Start and Quit buttons to the bottom panel of the start screen using the <control> element: +

+
        <panel id="panel_bottom_left" height="50%" width="50%" valign="center" childLayout="center">  
+          <control name="button" label="Start" id="StartButton" align="center" valign="center"> 
+          </control>
+        </panel>
+        <panel id="panel_bottom_right" height="50%" width="50%" valign="center" childLayout="center">  
+          <control name="button" label="Quit" id="QuitButton" align="center" valign="center"> 
+          </control>
+        </panel>
+ +

+Note that these controls don't do anything yet ??? we'll get to that soon. +

+ +
+ +

Other Controls

+
+ +

+ +Nifty additionally offers many customizable controls such as check boxes, text fields, menus, chats, tabs, ??? See also on the Nifty GUI site. +

+ +
+ +

Intermediate Result

+
+ +

+ +When you preview this code in the jMonkeyEngine SDK, our tutorial demo should looks as follows: A start screen with two buttons, and a game screen with a simple HUD frame and a blue cube (which stands for any jME3 game content). +

+ +

+ +

+ +

+Compare this result with the layout draft above. +

+ +
+ +

Next Steps

+
+ +

+ +Integrate the GUI into the game. Typically, you will overlay the GUI. +

+ +
+ gui, + documentation, + nifty, + hud +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nobloomsky.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nobloomsky.png new file mode 100644 index 000000000..129e9d888 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nobloomsky.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nurbs_3-d_surface.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nurbs_3-d_surface.png new file mode 100644 index 000000000..b22f7090d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nurbs_3-d_surface.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved.jpg new file mode 100644 index 000000000..53e69c31a Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved2.png new file mode 100644 index 000000000..cf2f87355 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogre_solved2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogrecompatibility.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogrecompatibility.html new file mode 100644 index 000000000..4c963194a --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ogrecompatibility.html @@ -0,0 +1,75 @@ + +

Working Blender and OgreXML Versions

+
+ +

+Here you can find working combinations of Blender and the OgreXML exporter, with any tips or bugs associated with each. + +

+
+ + + + + + + + + + + + + + + +
Blender Version OgreXML Exporter Version Notes
2.6.3 Root bone, no transforms on object, no envelopes
2.6.2 Root bone, no transforms on object, no envelopes
2.6.1 ?
2.6.0 ?
+ +
+ +

Tips

+
+ +

+ +Tips for exporting animations through OgreXML correctly: +

+ + +

+ +Test Character - +

+ +

+ + +

+ +
+ +

Troubleshooting

+
+ +

+ +Q: My animation is stretched. +

+ +

+A: Use the exporting tips provided above +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open-game-finder-1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open-game-finder-1.png new file mode 100644 index 000000000..31deb239f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open-game-finder-1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open_game_finder.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open_game_finder.html new file mode 100644 index 000000000..6547f9bb7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/open_game_finder.html @@ -0,0 +1,196 @@ + +

Open Game Finder

+
+ +

+The Open Game Finder (OGF) by Mark Schrijver can be plugged into any Java game. OGF enables you to find other people playing the same multiplayer game, and join them. +

+ + +

+Both on the client and the server side of OGF is written purely in Java. OGF has a pluggable architecture and comes with a full set of plugins to get the job done. You can add your own plugins, or replace existing plugins to make them more in line with your game. OGF uses NiftyGUI as the main GUI plugin. + +

+ +
+ +

Installation

+
+
    +
  1. Go to
    +
  2. +
  3. Download Client-1.0-bin.zip and Server-1.0-bin.zip
    +
  4. +
  5. Unzip the two files to, for example, your jMonkeyProjects directory.
    +
  6. +
+ +
+ +

Setting up the Database

+
+ +

+The OGF server uses an embedded Apache Derby database. You have to install the database, this means creating the data files and adding the tables. You can do this straight from the command line by running a script file. +

+ + +
+ +

Running the server

+
+ +

+Change into the OGF-Server directory and run the server: +

+ + +

+The server is now running and ready to accept connections.
+ +Note: In the alpha release, the server runs on localhost. In the final release, you will be able to configure the host! + +

+ +
+ +

Running the client

+
+
    +
  1. Change into the OGF-Client directory and run the client:
    +
      +
    • On Windows: Run startClient.bat
      +
    • +
    • On Linux and MacOS X: Run java -jar lib/Client-1.0.jar in the Terminal.
      +
    • +
    +
  2. +
  3. If a Display Settings window appears, you can keep the defaults and click OK.
    +
  4. +
+ +

+A client is now running, connects to the server, and displays a registration/login window. + +Note: You can run several clients on localhost for testing. + +

+ +
+ +

Client: 1. Registration

+
+ +

+If clients use OGF for the first time, they need to register. +On the main screen of the client: +

+
    +
  1. Click Register
    +
  2. +
  3. Choose a user name and password (repeat the password).
    +
  4. +
  5. Select an Avatar image.
    +
  6. +
  7. Click register to complete the registration.
    +
  8. +
+ +

+The client registers the account and opens the chat window directly. + +

+ +
+ +

Client: 2. Login

+
+ +

+If returning clients are already registered to an OGF server, they can log in. +On the main screen of the client: +

+
    +
  1. Enter a user name and password that you previously registered.
    +
  2. +
  3. Click Login
    +
  4. +
+ +

+The client logs you in and opens the chat window. + +

+ +
+ +

Client: 3. Chat

+
+ +

+The chat window shows a list of all users logged in to the server. Logged-in users can send public messages, and can receive public messages from others. + +

+ +
+ +

Connecting to a Game

+
+ +

+Q: I want to gather players using the OGF client to connect to the game server. How do I start my multiplayer game?
+ +A: The following sample code demos the typical use case: +
+ +In a JME3 Application's init method: +

+
    +
  1. Create a com.ractoc.opengamefinder.client.GUIContainer object.
    +
  2. +
  3. Create a game instance using the GUIContainer (via a ClientFactory).
    +
  4. +
  5. Check the com.ractoc.pffj.api.BasePluginMessageResult for success or failure.
    +
  6. +
+ +

+After this, continue writing your JME3 init method. + +

+ +
+ +

Configuration

+
+ +
+ network +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otoglow.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otoglow.png new file mode 100644 index 000000000..23d005bf3 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otoglow.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otonobloom.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otonobloom.png new file mode 100644 index 000000000..d1124e6c8 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/otonobloom.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle.png new file mode 100644 index 000000000..439cc45d8 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle_emitters.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle_emitters.html new file mode 100644 index 000000000..9e909daa1 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/particle_emitters.html @@ -0,0 +1,241 @@ + +

Particle Emmitter Settings

+
+ +

+ +You cannot create a 3D model for delicate things like fire, smoke, or explosions. Particle Emitters are quite an efficient solution to create these kinds of effects: The emitter renders a series of flat orthogonal images and manipulates them in a way that creates the illusion of a anything from a delicate smoke cloud to individual flames, etc. +Creating an effect involves some trial and error to get the settings just right, and it's worth exploring the expressiveness of the options described below. +

+ +

+

Use the Scene Explorer in the SDK to design and preview effects. +

+

+ +

+ +

+ +
+ +

Create an Emitter

+
+
    +
  1. Create one emitter for each effect:
    ParticleEmitter explosion = new ParticleEmitter(
    +"My explosion effect", ParticleMesh.Type.Triangle, 30);
    +
    +
  2. +
  3. Attach the emitter to the rootNode and position it in the scene:
    rootNode.attachChild(explosion);
    +explosion.setLocalTranslation(bomb.getLocalTranslation());
    +
    +
  4. +
  5. Trigger the effect by calling
    explosion.emitAllParticles()
    +
    +
  6. +
  7. End the effect by calling
    explosion.killAllParticles()
    +
    +
  8. +
+ +

+Choose one of the following mesh shapes +

+ + +
+ +

Configure Parameters

+
+ +

+Not all of these parameters are required for all kinds of effects. If you don't specify one of them, a default value will be used. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameter Method Default Description
number setNumParticles() The maximum number of particles visible at the same time. Specified by user in constructor.
emission rate setParticlesPerSec() 20 Density of the effect, how many new particles are emitted per second.
+Set to zero to control the start/end of the effect.
+Set to a number for a constantly running effect.
size setStartSize(), setEndSize() 0.2f, 2f The radius of the scaled sprite image. Set both to same value for constant size effect.
+Set to different values for shrink/grow effect.
color setStartColor(), setEndColor() gray Controls how the opaque (non-black) parts of the texture are colorized.
+Set both to the same color for single-colored effects (e.g. fog, debris).
+Set both to different colors for a gradient effect (e.g. fire).
direction/velocity getParticleInfluencer(). setInitialVelocity(initialVelocity) Vector3f(0,0,0) A vector specifying the initial direction and speed of particles. The longer the vector, the faster.
fanning out getParticleInfluencer(). setVelocityVariation(variation) 0.2f How much the direction (setInitialVelocity()) can vary among particles. Use a value between 1 and 0 to create a directed swarm-like cloud of particles.
+1 = Maximum variation, particles emit in random 360?? directions (e.g. explosion, butterflies).
+0.5f = particles are emitted within 180?? of the initial direction.
+0 = No variation, particles fly in a straight line in direction of start velocity (e.g. lasergun blasts).
direction
+(pick one)
setFacingVelocity() false true = Flying particles pitch in the direction they're flying (e.g. missiles).
+false = Particles keep flying rotated the way they started (e.g. debris).
direction
+(pick one)
setFaceNormal() Vector3f.NAN Vector3f = Flying particles face in the given direction (e.g. horizontal shockwave faces up = Vector3f.UNIT_Y).
+Vector3f.NAN = Flying particles face the camera.
lifetime setLowLife(), setHighLife() 3f, 7f The time period before a particle fades is set to a random value between minimum and maximum; minimum must be smaller than maximum. A minimum < 1f makes the effect more busy, a higher minimum looks more steady. Use a maximum < 1f for short bursts, and higher maxima for long lasting swarms or smoke. Set maximum and minimum to similar values to create an evenly spaced effect (e.g. fountain), set the to very different values to create a distorted effect (e.g. fire with individual long flames).
spinning setRotateSpeed() 0f 0 = Flying particles don't spin while flying (e.g. smoke, insects, controlled projectiles).
+> 0 = How fast particle spins while flying (e.g. debris, shuriken, missiles out of control).
rotation setRandomAngle() false true = The particle sprite is rotated at a random angle when it is emitted (e.g. explosion, debris).
+false = Particles fly straight like you drew them in the sprite texture (e.g. insects).
gravity setGravity() Vector3f(0.0f,0.1f,0.0f) Particles fall in the direction of the vector (e.g. debris, sparks).
+(0,0,0) = Particles keep flying in start direction (e.g. flames, zero-gravity explosion.)
start area setShape(new EmitterSphereShape( Vector3f.ZERO, 2f));EmitterPointShape()By default, particles are emitted from the emitters location (a point). You can increase the emitter shape to occupy a sphere, so that the start point of new particles can be anywhere inside the sphere, which makes the effect a bit more irregular.
+ +

+Build up you effect by specifying one parameter after the other. If you change several parameters at the same time, it's difficult to tell which of the values caused which outcome. +

+ +
+ +

Create an Effect Material

+
+ +

+ + +

+ +

+Use the common Particle.j3md Material Definition and a texture to specify the shape of the particles. The shape is defined by the texture you provide and can be anything ??? debris, flames, smoke, mosquitoes, leaves, butterflies??? be creative. +

+
    Material flash_mat = new Material(
+        assetManager, "Common/MatDefs/Misc/Particle.j3md");
+    flash_mat.setTexture("Texture",
+        assetManager.loadTexture("Effects/Explosion/flash.png"));
+    flash.setMaterial(flash_mat);
+    flash.setImagesX(2); // columns
+    flash.setImagesY(2); // rows
+    flash.setSelectRandomImage(true);
+ +

+The effect texture can be one image, or contain a sprite animation ??? a series of slightly different pictures in equally spaced rows and columns. If you choose the sprite animation: +

+ + +

+ +Examples: Have a look at the following default textures and you will see how you can create your own sprite textures after the same fashion. +

+ +
+ +

Default Particle Textures

+
+ +

+The Material is used together with grayscale texture: The black parts will be transparent and the white parts will be opaque (colored). +The following effect textures are available by default from test-data.jar. You can also load your own textures from your assets directory. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Texture Path Dimension Preview
Effects/Explosion/Debris.png 3*3
Effects/Explosion/flame.png 2*2
Effects/Explosion/flash.png 2*2
Effects/Explosion/roundspark.png 1*1
Effects/Explosion/shockwave.png 1*1
Effects/Explosion/smoketrail.png 1*3
Effects/Explosion/spark.png 1*1
Effects/Smoke/Smoke.png 1*15
+ +

+ +Tip: Use the setStartColor()/setEndColor() settings described above to colorize the white and gray parts of textures. +

+ +
+ +

Usage Example

+
+
    ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+    Material mat_red = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
+    mat_red.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
+    fire.setMaterial(mat_red);
+    fire.setImagesX(2); fire.setImagesY(2); // 2x2 texture animation
+    fire.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
+    fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+        fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0,2,0));
+    fire.setStartSize(1.5f);
+    fire.setEndSize(0.1f);
+    fire.setGravity(0,0,0);
+    fire.setLowLife(0.5f);
+    fire.setHighLife(3f);
+    fire.getParticleInfluencer().setVelocityVariation(0.3f);
+    rootNode.attachChild(fire);
+ +

+ +Browse the full source code of all here. +

+
+ +

+ +See also: Effects Overview +

+
+ documentation, + effect +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics-vehicle.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics-vehicle.png new file mode 100644 index 000000000..4cc958d68 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics-vehicle.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html new file mode 100644 index 000000000..3b1595a04 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html @@ -0,0 +1,724 @@ + +

Physics: Gravity, Collisions, Forces

+
+ +

+ +A physics simulation is used in games and applications where objects are exposed to physical forces: Think of games like pool billiard and car racing simulators. Massive objects are pulled by gravity, forces cause objects to gain momentum, friction slows them down, solid objects collide and bounce off one another, etc. Action and Adventure games also make use of physics to implement solid obstacles, falling, and jumping. +

+ +

+The jMonkeyEngine3 has built-in support for (based on ) via the com.jme3.bullet package. This article focuses mostly on the RigidBodyControl, but also introduces you to others. +

+ +

+If you are looking for info on how to respond to physics events such as collisions, read about Physics Listeners. +

+ +
+ +

Technical Overview

+
+ +

+ +Bullet physics runs internally at 60fps by default. This rate is not dependent on the actual framerate and it does not lock the framerate at 60fps. Instead, when the actual fps is higher than the physics framerate the system will display interpolated positions for the physics objects. When the framerate is lower than the physics framerate, the physics space will be stepped multiple times per frame to make up for the missing calculations. You create a Bullet PhysicsSpace in jME3 with a com.jme3.bullet.BulletAppState. +

+ +

+Internally, the updating and syncing of the actual physics objects happens in the following way: +

+
    +
  1. collision callbacks (BulletAppState.update())
    +
  2. +
  3. user update (simpleUpdate in main loop, update() in Controls and AppStates)
    +
  4. +
  5. physics to scenegraph syncing and applying (updateLogicalState())
    +
  6. +
  7. stepping physics (before or in parallel to Application.render())
    +
  8. +
+ +

+ +When you use this physics simulation, values correspond to the following units: +

+ + +
+ +

Sample Code

+
+ +

+ +Full code samples are here: +

+ + +
+ +

Physics Application

+
+ +

+ +A short overview of how to write a jME application with Physics capabilities: +

+ +

+Do the following once per application to gain access to the physicsSpace object: +

+
    +
  1. Make your application extend com.jme3.app.SimpleApplication.
    +
  2. +
  3. Create a BulletAppState field:
    private BulletAppState bulletAppState;
    +
    +
  4. +
  5. Initialize your bulletAppState and attach it to the state manager:
    public void simpleInitApp() {
    +    bulletAppState = new BulletAppState();
    +    stateManager.attach(bulletAppState);
    +
    +
  6. +
+ +

+ +

In your application, you can always access the BulletAppState via the ApplicationStateManager: +

+
BulletAppState bas = app.getStateManager().getState(BulletAppState.class);
+ +

+ +

+

+ +

+For each Spatial that you want to be physical: +

+
    +
  1. Create a CollisionShape.
    +
  2. +
  3. Create the PhysicsControl from the CollisionShape and a mass value.
    +
  4. +
  5. Add the PhysicsControl to its Spatial.
    +
  6. +
  7. Add the PhysicsControl to the PhysicsSpace.
    +
  8. +
  9. Attach the Spatial to the rootNode (as usual).
    +
  10. +
  11. (Optional) Implement the PhysicsCollisionListener interface to respond to PhysicsCollisionEvents.
    +
  12. +
+ +

+ +Let's look at the details: +

+ +
+ +

Create a CollisionShape

+
+ +

+ +A CollisionShape is a simplified shape for which physics are easier to calculate than for the true shape of the model. This simplication approach speeds up the simulation greatly. +

+ +

+Before you can create a Physics Control, you must create a CollisionShape from the com.jme3.bullet.collision.shapes package. (Read the tip under "PhysicsControls Code Samples" how to use default CollisionShapes for Boxes and Spheres.) + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Non-Mesh CollisionShape Usage Examples
BoxCollisionShape() Box-shaped behaviour, does not roll. Oblong or cubic objects like bricks, crates, furniture.
SphereCollisionShape() Spherical behaviour, can roll. Compact objects like apples, soccer balls, cannon balls, compact spaceships.
CylinderCollisionShape() Tube-shaped and disc-shaped behaviour, can roll on one side. Oblong objects like pillars.
+Disc-shaped objects like wheels, plates.
CompoundCollisionShape() A CompoundCollisionShape allows custom combinations of shapes. Use the addChildShape() method on the compound object to add other shapes to it and position them relative to one another. A car with wheels (1 box + 4 cylinders), etc.
CapsuleCollisionShape() A built-in compound shape of a vertical cylinder with one sphere at the top and one sphere at the bottom. Typically used with CharacterControls: A cylinder-shaped body does not get stuck at corners and vertical obstacles; the rounded top and bottom do not get stuck on stair steps and ground obstacles. Persons, animals.
SimplexCollisionShape() A physical point, line, triangle, or rectangle Shape, defined by one to four points.Guardrails
PlaneCollisionShape() A 2D plane. Very fast. Flat solid floor or wall.
+ +

+ +All non-mesh CollisionShapes can be used for dynamic, kinematic, as well as static Spatials. (Code samples see below) + +

+
+ + + + + + + + + + + + + + + +
Mesh CollisionShapes Usage Examples
MeshCollisionShape A mesh-accurate shape for static or kinematic Spatials. Can have complex shapes with openings and appendages.
+Limitations: Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape. This Shape does not work with dynamic Spatials.
A whole static game level model.
HullCollisionShape A less accurate shape for dynamic Spatials that cannot easily be represented by a CompoundShape.
+Limitations: The shape is convex (behaves as if you gift-wrapped the object), i.e. openings, appendages, etc, are not individually represented.
A dynamic 3D model.
GImpactCollisionShape A mesh-accurate shape for dynamic Spatials. It uses .
+Limitations: CPU intensive, use sparingly! We recommend using HullCollisionShape (or CompoundShape) instead to improve performance. Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape.
Complex dynamic objects (like spiders) in Virtual Reality or scientific simulations.
HeightfieldCollisionShape A mesh-accurate shape optimized for static terrains. This shape is much faster than other mesh-accurate shapes.
+Limitations: Requires heightmap data. Collisions between two mesh-accurate shapes cannot be detected, only non-mesh shapes can collide with this shape.
Static terrains.
+ +

+ +On a CollisionShape, you can apply a few properties +

+
+ + + + + + +
CollisionShape Method Property Examples
setScale(new Vector3f(2f,2f,2f)) You can change the scale of collisionshapes (whether it be, Simple or Mesh). You cannot change the scale of a CompoundCollisionShape however. A sphere collision shape, will change its radius based on the X component of the vector passed in. You must scale a collision shape before attaching it to the physicsSpace, or you must readd it to the physicsSpace each time the scale changes. Scale a player in the Y axis by 2:
+new Vector3f(1f,2f,1f)
+ +

+ +The mesh-accurate shapes can use a CollisionShapeFactory as constructor (code samples see below). +

+ +

+

Pick the simplest and most applicable shape for the mesh for what you want to do: If you give a box a sphere collision shape, it will roll; if you give a ball a box collision shape, it will sit on a slope. If the shape is too big, the object will seem to float; if the shape is too small it will seem to sink into the ground. During development and debugging, you can make collision shapes visible by adding the following line after the bulletAppState initialization: +

+
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ +

+ +

+

+ +
+ +

CollisionShape Code Samples

+
+ + +
+ +

Create PhysicsControl

+
+ +

+ +BulletPhysics are available in jME3 through PhysicsControls classes from the com.jme3.bullet.control package. jME3's PhysicsControl classes directly extend BulletPhysics objects and are the recommended way to use physics in a jME3 application. PhysicsControls are flexible and can be added to any Spatial to make it act according to physical properties. + +

+
+ + + + + + + + + +
Standard PhysicsControls Usage Examples
RigidBodyControlThe most commonly used PhysicsControl. You can use it for dynamic objects (solid objects that freely affected by collisions, forces, or gravity), for static objects (solid but not affected by any forces), or kinematic objects (remote-controlled solid objects). Impacting projectiles, moving obstacles like crates, rolling and bouncing balls, elevators, flying aircaft or space ships.
+Solid immobile floors, walls, static obstacles.
GhostControlUse for collision and intersection detection between physical objects. A GhostControl itself is non-solid and invisible. GhostControl moves with the Spatial it is attached to. Use GhostControls to implement custom game interactions by adding it to a visible Geometry. A monster's "aggro radius", CharacterControl collisions, motion detectors, photo-electric alarm sensors, poisonous or radioactive perimeters, life-draining ghosts, etc.
+
+ + + + + + + + + + + + +
Special PhysicsControls Usage Examples
VehicleControl
+PhysicsVehicleWheel
Special Control used for "terrestrial" vehicles with suspension and wheels. Cars, tanks, hover crafts, ships, motorcycles???
CharacterControlSpecial Control used for Walking Characters.Upright walking persons, animals, robots???
RagDollControlSpecial Control used for collapsing, flailing, or falling characters Falling persons, animals, robots, "Rag dolls"
+ +

+ +Click the links for details on the special PhysicsControls. This article is about RigidBodyControl. +

+ +
+ +

PhysicsControls Code Samples

+
+ +

+ +The PhysicsControl constructors expect a Collision Shape and a mass (a float in kilogram). The most commonly used PhysicsControl is the RigidBodyControl: +

+
RigidBodyControl myThing_phys = 
+    new RigidBodyControl( myThing_shape , 123.0f ); // dynamic
+
RigidBodyControl myDungeon_phys = 
+    new RigidBodyControl( myDungeon_shape , 0.0f ); // static 
+ +

+

When you create the PhysicsControl, the mass value makes an important distinction: Set the mass to a non-zero value to create a dynamic object that can fall or roll, etc. Set the mass value to zero to create a static object, such as floor, wall, etc. If you give your floor a mass, it will fall out of the scene! +

+

+ +

+The following creates a box Geometry with the correct default BoxCollisionShape: +

+
Box b = new Box(1,1,1);
+Geometry box_geo = new Geometry("Box", b);
+box_geo.addControl(new RigidBodyControl( 1.0f )); // explicit non-zero mass, implicit BoxCollisionShape
+ +

+ +The following creates a MeshCollisionShape for a whole loaded (static) scene: + +

+
...
+gameLevel.addControl(new RigidBodyControl(0.0f)); // explicit zero mass, implicit MeshCollisionShape
+ +

+

Spheres and Boxes automatically fall back on the correct default CollisionShape if you do not specify a CollisionShape in the RigidBodyControl constructor. Complex static objects can fall back on MeshCollisionShapes, unless it is a Node, in which case it will become a CompoundCollisionShape containing a MeshCollisionShape +

+

+ +
+ +

Add PhysicsControl to Spatial

+
+ +

+ +For each physical Spatial in the scene: +

+
    +
  1. Add a PhysicsControl to a Spatial.
    myThing_geo.addControl(myThing_phys);
    +
    +
  2. +
  3. Remember to also attach the Spatial to the rootNode, as always!
    +
  4. +
+ +
+ +

Add PhysicsControl to PhysicsSpace

+
+ +

+ +The PhysicsSpace is an object in BulletAppState that is like a rootNode for Physics Controls. + +

+ + +

+ +

You can either add the PhysicsControl to the PhysicsSpace, or add the PhysicsControl to the Geometry and then add the Geometry to the PhysicsSpace. jME3 understands both and the outcome is the same. +

+

+ +
+ +

Changing the Scale of a PhysicsControl

+
+ +

+To change the scale of a PhysicsControl you must change the scale of the collisionshape which belongs to it. +

+ +

+MeshCollisionShapes can have a scale correctly set, but it only works when being constructed on a geometry (not a node). CompoundCollisionShapes cannot be scaled at this time(the type obtained when creating a CollisionShape from a Node i.e using imported models). +

+ +

+When you import a model from blender, it often comes as a Node (even if it only contains 1 mesh), which is by de-facto automatically converted to a CompoundCollisionShape. So when you try to scale this it won't work! Below illustrates an example, of how to scale an imported model: +

+
// Doesn't scale
+// This modified version contains Node -> Geometry (name = "MonkeyHeadGeom")
+Spatial model = assetManager.loadModel("Models/MonkeyHead.j3o"); model.addControl(new RigidBodyControl(0));
+// Won't work as this is now a CompoundCollisionShape containing a MeshCollisionShape
+model.getControl(RigidBodyControl.class).getCollisionShape().setScale(new Vector3f(2, 2, 2)); 
+bulletAppState.getPhysicsSpace().add(model);
+ 
+// Works fine
+Spatial model = assetManager.loadModel("Models/MonkeyHead.j3o"); // Same Model
+ // IMPORTANT : You must navigate to the Geometry for this to work
+Geometry geom = ((Geometry) ((Node) model).getChild("MonkeyHeadGeom"));
+geom.addControl(new RigidBodyControl(0));
+// Works great (scaling of a MeshCollisionShape)	
+geom.getControl(RigidBodyControl.class).getCollisionShape().setScale(new Vector3f(2, 2, 2));
+bulletAppState.getPhysicsSpace().add(geom);
+ +

+With the corresponding output below: + + + +

+ +
+ +

PhysicsSpace Code Samples

+
+ +

+ +The PhysicsSpace also manages global physics settings. Typically, you can leave the defaults, and you don't need to change the following settings, but it's good to know what they are for: + +

+
+ + + + + + + + + + + + + + + + + + +
bulletAppState.getPhysicsSpace() MethodUsage
setGravity(new Vector3f(0, -9.81f, 0));Specifies the global gravity.
setAccuracy(1f/60f);Specifies physics accuracy. The higher the accuracy, the slower the game. Increase value if objects are passing through one another, or bounce oddly.
setMaxSubSteps(4);Compensates low FPS: Specifies the maximum amount of extra steps that will be used to step the physics when the game fps is below the physics fps. This maintains determinism in physics in slow (low-fps) games. For example a maximum number of 2 can compensate for framerates as low as 30 fps (physics has a default accuracy of 60 fps). Note that setting this value too high can make the physics drive down its own fps in case its overloaded.
setWorldMax(new Vector3f(10000f, 10000f, 10000f));
+setWorldMin(new Vector3f(-10000f, -10000f, -10000f));
Specifies the size of the physics space as two opposite corners (only applies to AXIS_SWEEP broadphase).
setCcdMotionThreshold()The amount of motion in 1 physics tick to trigger the continuous motion detection in moving objects that push one another. Rarely used, but necessary if your moving objects get stuck or roll through one another.
+ +
+ +

Specify Physical Properties

+
+ +

+ +After you have registered, attached, and added everything, you can adjust physical properties or apply forces. +

+ +

+On a RigidBodyControl, you can set the following physical properties. + +

+
+ + + + + + + + + + + + + + + +
RigidBodyControl Method Property Examples
setGravity(new Vector3f(0f,-9.81f,0f)) You can change the gravity of individual physics objects after they were added to the PhysicsSpace. Gravity is a vector pointing from this Spatial towards the source of gravity. The longer the vector, the stronger is gravity.
+If gravity is the same absolute direction for all objects (e.g. on a planet surface), set this vector globally on the PhysicsSpace object and not individually.
+If the center of gravity is relative (e.g. towards a black hole) then setGravity() on each Spatial to constantly adjust the gravity vectors at each tick of their update() loops.
For planet earth:
+new Vector3f(0f,-9.81f,0f)
setMass(1f) Sets the mass in kilogram. Dynamic objects have masses > 0.0f. Heavy dynamic objects need more force to be moved and light ones move with small amounts of force.
+Static immobile objects (walls, floors, including buildings and terrains) must have a mass of zero!
Person: 60f, ball: 1.0f
+Floor: 0.0f (!)
setFriction(1f) Friction.
+Slippery objects have low friction. The ground has high friction.
Ice, slides: 0.0f
+Soil, concrete, rock: 1.0f
setRestitution(0.0f) Bounciness. By default objects are not bouncy (0.0f). For a bouncy rubber object set this > 0.0f.
+Both the object and the surface must have non-zero restitution for bouncing to occur.
+This setting has an impact on performance, so use it sparingly.
Brick: 0.0f
+Rubber ball: 1.0f
+ +

+ +On a RigidBodyControl, you can apply the following physical forces: + +

+
+ + + + + + + + + + + + +
RigidBodyControl Method Motion
setPhysicsLocation()Positions the objects. Do not use setLocalTranslation() for physical objects. Important: Make certain not to make CollisionShapes overlap when positioning them.
setPhysicsRotation()Rotates the object. Do not use setLocalRotate() for physical objects.
setKinematic(true) By default, RigidBodyControls are dynamic (kinematic=false) and are affected by forces. If you set kinematic=true, the object is no longer affected by forces, but it still affects others. A kinematic is solid, and must have a mass.
+(See detailed explanation below.)
+ +
+ +

Kinematic vs Dynamic vs Static

+
+ +

+ +All physical objects??? +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Property Static Kinematic Dynamic
ExamplesImmobile obstacles: Floors, walls, buildings, ???Remote-controlled solid objects: Airships, meteorites, elevators, doors; networked or remote-controlled NPCs; invisible "airhooks" for hinges and joints.Interactive objects: Rolling balls, movable crates, falling pillars, zero-g space ship???
Does it have a mass?no, 0.0fyes1), >0.0f yes, >0.0f
How does it move?neversetLocalTranslation();setLinearVelocity(); applyForce();
+setWalkDirection(); for CharacterControl
How to place in scene?setPhysicsLocation();
+setPhysicsRotation()
setLocalTranslation();
+setLocalRotation();
setPhysicsLocation();
+ setPhysicsRotation()
Can it move and push others?noyesyes
Is is affected by forces?
+(Falls when it mid-air? Can be pushed by others?)
nonoyes
How to activate this behaviour? setMass(0f);
+setKinematic(false);
setMass(1f);
+setKinematic(true);
setMass(1f);
+setKinematic(false);
+ +
+ +

When Do I Use Kinematic Objects?

+
+ + +

+ +

The position of a kinematic RigidBodyControl is updated automatically depending on its spatial's translation. You move Spatials with a kinematic RigidBodyControl programmatically, that means you write translation and rotation code in the update loop. You describe the motion of kinematic objects either by using methods such as setLocalTranslation() or move(), or by using a MotionPath. +

+

+ +
+ +

Forces: Moving Dynamic Objects

+
+ +

+ +Use the following methods to move dynamic physical objects. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
PhysicsControl Method Motion
setLinearVelocity(new Vector3f(0f,0f,1f)) Set the linear speed of this object.
setAngularVelocity(new Vector3f(0f,0f,1f)) Set the rotational speed of the object; the x, y and z component are the speed of rotation around that axis.
applyCentralForce(???) Move (push) the object once with a certain moment, expressed as a Vector3f.
applyForce(???) Move (push) the object once with a certain moment, expressed as a Vector3f. Optionally, you can specify where on the object the pushing force hits.
applyTorque(???) Rotate (twist) the object once around its axes, expressed as a Vector3f.
applyImpulse(???) An idealised change of momentum. This is the kind of push that you would use on a pool billiard ball.
applyTorqueImpulse(???) An idealised change of momentum. This is the kind of push that you would use on a pool billiard ball.
clearForces()Cancels out all forces (force, torque) etc and stops the motion.
+ +

+ +

It is technically possible to position PhysicsControls using setLocalTranslation(), e.g. to place them in their start position in the scene. However you must be very careful not to cause an "impossible state" where one physical object overlaps with another! Within the game, you typically use the setters shown here exclusively. +

+

+ +

+PhysicsControls also support the following advanced features: + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
PhysicsControl Method Property
setCollisionShape(collisionShape)Changes the collision shape after creation.
setCollideWithGroups()
+setCollisionGroup()
+addCollideWithGroup(COLLISION_GROUP_01)
+removeCollideWithGroup(COLLISION_GROUP_01)
Collision Groups are integer bit masks ??? enums are available in the CollisionObject. All physics objects are by default in COLLISION_GROUP_01. Two objects collide when the collideWithGroups set of one contains the Collision Group of the other. Use this to improve performance by grouping objects that will never collide in different groups (the the engine saves times because it does not need to check on them).
setDamping(float, float)The first value is the linear threshold and the second the angular. This simulates dampening of forces, for example for underwater scenes.
setAngularFactor(1f)Set the amount of rotation that will be applied. A value of zero will cancel all rotational force outcome. (?)
setSleepingThreshold(float,float)Sets the sleeping thresholds which define when the object gets deactivated to save resources. The first value is the linear threshold and the second the angular. Low values keep the object active when it barely moves (slow precise performance), high values put the object to sleep immediately (imprecise fast performance). (?)
setCcdMotionThreshold(0f) Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection in movign obejcts that push one another. This avoids the problem of fast objects moving through other objects. Set to zero to disable (default).
setCcdSweptSphereRadius(.5f)Bullet does not use the full collision shape for continuous collision detection, instead it uses a "swept sphere" shape to approximate a motion, which can be imprecise and cause strange behaviours such as objects passign through one another or getting stuck. Only relevant for fast moving dynamic bodies.
+ +

+ +

You can setApplyPhysicsLocal(true) for an object to make it move relatively to its local physics space. You would do that if you need a physics space that moves with a node (e.g. a spaceship with artificial gravity surrounded by zero-g space). By default, it's set to false, and all movement is relative to the world. +

+

+ +
+ +

Best Practices

+
+ + + + + +
+ physics, + documentation, + control +
+ +
+
+
1) +Inertia is calculated for kinematic objects, and you need mass to do that.
+
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics_listeners.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics_listeners.html new file mode 100644 index 000000000..15d91dcb2 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics_listeners.html @@ -0,0 +1,259 @@ + +

Physics Listeners

+
+ +

+You can control physical objects (push them around) by applying physical forces to them. Typically, you also want to respond to the resulting collisions, e.g. by substracting health points or by playing a sound. To specify how the game responds to such physics events, you use Physics Listeners. +

+ +
+ +

Sample Code

+
+ + +
+ +

PhysicsGhostObjects

+
+ +

+ +Attach a com.jme3.bullet.control.GhostControl to any Spatial to turn it into a PhysicsGhostObject. Ghost objects automatically follow their spatial and detect collisions. The attached ghost itself is invisible and non-solid (!) and doesn't interfere with your game otherwise, it only passively reports collisions. +

+ +

+You can leave the GhostControl non-solid and invisible and attach it to an (invisible) Node in the scene to create something like a motion detector. But a GhostControl also works fine when added to spatials that are solid (with RigidBodyControl) and visible (with Geometry). One use case for GhostControls is to check for collisions among CharacterControls when the characters are walking. +

+ +

+The shape of the ghost depends on the CollisionShape that you gave the GhostControl. This means that the GhostControl's shape can be different from the RigidBodyControl's shape. For example, the non-solid ghost shape can be bigger than the solid shape of the Spatial (so you can "feel" ahead). +

+
GhostControl ghost = new GhostControl(
+  new BoxCollisionShape(new Vector3f(1,1,1)));  // a box-shaped ghost
+Node node = new Node("a ghost-controlled thing");
+node.addControl(ghost);                         // the ghost follows this node
+// Optional: Add a Geometry, or other controls, to the node if you need to
+...
+// attach everything to activate it
+rootNode.attachChild(node);
+getPhysicsSpace().add(ghost);
+
+ + + + + + + + + + + + +
Ghost methodsUsage
getOverlappingObjects()Returns the List of objects that are currently colliding (overlapping) with the ghost.
getOverlappingCount()Returns the number of currently colliding objects.
getOverlapping(i)Get PhysicsCollisionObject number i.
+ +
+ +

Physics Tick Listener

+
+ +

+ +The jBullet Physics implementation is stepped at a constant 60 physics ticks per second frame rate. +Applying forces or checking for overlaps only has an effect right at a physics update cycle, which is not every frame. If you do physics interactions at arbitrary spots in the simpleUpdate() loop, calls will be dropped at irregular intervals, because they happen out of cycle. +

+ +
+ +

When (Not) to Use Tick Listener?

+
+ +

+ +When you write game mechanics that apply forces, you must implement a tick listener (com.jme3.bullet.PhysicsTickListener) for it. The tick listener makes certain the forces are not dropped, but applied in time for the next physics tick. +

+ +

+Also, when you check for overlaps of two physical objects using a GhostControl, you cannot just go ghost.getOverLappingObjects() somewhere outside the update loop. You have to make certain 1 physics tick has passed before the overlapping objects list is filled with data. Again, the PhysicsTickListener does the timing for you. +

+ +

+When your game mechanics however just poll the current state (e.g. getPhysicsLocation()) of physical objects, or if you only use the GhostControl like a sphere trigger inside an update loop, then you don't need an extra PhysicsTickListener. +

+ +
+ +

How to Listen to Physics Ticks

+
+ +

+ +Here's is the declaration of an examplary Physics Control that listens to ticks. (The example shows a RigidBodyControl, but it can also be GhostControl.) +

+
public class MyCustomControl
+    extends RigidBodyControl implements PhysicsTickListener { ... }
+ +

+When you implement the interface, you have to implement physicsTick() and preTick() methods. +

+ + +

+The tpf value is time per frame in seconds. You can use it as a factor to time actions so they run equally on slow and fast machines. +

+
@override
+public void prePhysicsTick(PhysicsSpace space, float tpf){
+  // apply state changes ...
+}
+@override
+public void physicsTick(PhysicsSpace space, float tpf){
+  // poll game state ...
+}
+ +
+ +

Physics Collision Listener

+
+ +
+ +

When (Not) to Use Collision Listener

+
+ +

+ +If you do not implement the Collision Listener interface (com.jme3.bullet.collision.PhysicsCollisionListener), a collisions will just mean that physical forces between solid objects are applied automatically. If you just want "Balls rolling, bricks falling" you do not need a listener. +

+ +

+If however you want to respond to a collision event (com.jme3.bullet.collision.PhysicsCollisionEvent) with a custom action, then you need to implement the PhysicsCollisionListener interface. Typical actions triggered by collisions include: +

+ + +
+ +

How to Listen to Collisions

+
+ +

+ +You need to add the PhysicsCollisionListener to the physics space before collisions will be listened for. Here's an example of a Physics Control that uses a collision listener. (The example shows a RigidBodyControl, but it can also be GhostControl.) +

+
public class MyCustomControl extends RigidBodyControl
+    implements PhysicsCollisionListener {
+    public MyCustomControl() {
+        bulletAppState.getPhysicsSpace().addCollisionListener(this);
+        ...
+    }
+ +

+To respond to the PhysicsCollisionEvent you now have to override the collision() method in MyCustomControl. This gives you access to the event object. Mostly you will be interested in the identity of any two nodes that collided: event.getNodeA() and event.getNodeB(). +

+ +

+After you identify the colliding nodes, specify the action to trigger when this pair collides. Note that you cannot know which one will be Node A or Node B, you have to deal with either variant. +

+
    public void collision(PhysicsCollisionEvent event) {
+        if ( event.getNodeA().getName().equals("player") ) {
+            final Node node = event.getNodeA();
+            /** ... do something with the node ... */
+        } else if ( event.getNodeB().getName().equals("player") ) {
+            final Node node = event.getNodeB();
+            /** ... do something with the node ... */
+        }
+    }
+ +

+ +

Note that after the collision() method ends, the PhysicsCollisionEvent is cleared. You must get all objects and values you need within the collision() method. +

+

+ +
+ +

Reading Details From a PhysicsCollisionEvent

+
+ +

+ +The PhysicsCollisionEvent event gives you access to detailed information about the collision. You already know the event objects can identify which nodes collided, but it even knows how hard they collided: +

+
+ + + + + + + + + + + + + + + + + + + + + +
Method Purpose
getObjectA()
+getObjectB()
The two participants in the collision. You cannot know in advance whether some node will be recorded as A or B, you always have to consider both cases.
getAppliedImpulse() A float value representing the collision impulse
getAppliedImpulseLateral1() A float value representing the lateral collision impulse
getAppliedImpulseLateral2() A float value representing the lateral collision impulse
getCombinedFriction() A float value representing the collision friction
getCombinedRestitution() A float value representing the collision restitution (bounciness)
+ +

+Note that after the collision method has been called the object is not valid anymore so you should copy any data you want to keep into local variables. +

+ +
+ +

Collision Groups

+
+ +

+ +You can improve performance by resricting the number of tests that collision detection has to perform. If you have a case where you are only interested in collisions between certain objects but not others, you can assign sets of physical obejcts to different collision groups. +

+ +

+For example, for a click-to-select, you only care if the selection ray collides with a few selectable objects such as dropped weapons or powerups (one group), but not with non-selectables such as floors or walls (different group). +

+
myNode.getControl(RigidBodyControl.class).setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
+myNode.getControl(RigidBodyControl.class).setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);
+
+ documentation, + physics, + collision, + forces, + interaction +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html new file mode 100644 index 000000000..17ea8cb43 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html @@ -0,0 +1,295 @@ + +

Rendering Water as Post-Process Effect

+
+ +

+ +The awesome SeaMonkey WaterFilter is highly configurable. It can render any type of water and also simulates the underwater part of the effect, including light effects called caustics. The effect is based on published on gamedev.net. Here's a video: +

+ +

+ +

+ +

+

The SeaMonkey WaterFilter is ideal for oceans and lakes, and especially for under-water scenes. If you only need a small simple water surface, such as a water trough or a shallow fountain, the SimpleWaterProcessor may already be all you need. +

+

+ +
+ +

The Theory

+
+ +

+ +The effect is part of a deferred rendering process, taking advantage of the pre-computed position buffer and back buffer (a texture representing the screen???s pixels position in view space, and a texture of the rendered scene). +

+ +

+After some calculation, this allows to reconstruct the position in world space for each pixel on the screen. "If a pixel is under a given water height, let???s render it as a blue pixel!" Blue pixel? Not exactly, we want waves, we want ripples, we want foam, we want reflection and refraction. +

+ +

+The GameDev.net article describes how those effects are achieved, but the main idea is to generate waves from a height map, create ripples from a normal map, blend in the foam texture when the water depth is below a certain height, compute the refraction color with a clever color extinction algorithm, and then, display the reflection and specular effect by computing a Fresnel term (like in the simple water effect). In addition, this effect allows to blend the water shore with the ground to avoid the hard edges of classic water effects based on grids or quads. +

+ +
+ +

How Did We Implement it in jME3?

+
+ +

+ +jME3 default behavior is to use a forward rendering process, so there is no position buffer rendered that we can take advantage of. But while rendering the main scene to a frame buffer in the FilterPostPorcessor, we can write the hardware depth buffer to a texture, with nearly no additional cost. +

+ +

+There are several ways of reconstructing the world space position of a pixel from the depth buffer. The computational cost is higher than just fetching the position from a position buffer, but the bandwidth and the memory required is a lot lower. +

+ +

+Now we have the rendered scene in a texture, and we can reconstruct the position in world space of each pixel. We???re good to go! +

+ +

+??? Nehon +

+ +
+ +

Sample Code

+
+ +

+ +There are two test cases in the jME3 repository: + +

+ + +
+ +

Using the Water Filter

+
+ +

+ +In the simpleInitApp() method, you attach your scene to the rootNode, typically a terrain with a sky. Remember to add a directional light, since the water relies on the light direction vector. The WaterFilter constructor expects a node with the scene attached that should be reflected in the water, and vector information from the light source's direction. +

+ +

+This is how you use the water filter post-processor code in your code: +

+
private FilterPostProcessor fpp;
+private WaterFilter water;
+private Vector3f lightDir = new Vector3f(-4.9f, -1.3f, 5.9f); // same as light source
+private float initialWaterHeight = 0.8f; // choose a value for your scene
+...
+ 
+public void simpleInitApp() {
+  ...
+  fpp = new FilterPostProcessor(assetManager);
+  water = new WaterFilter(rootNode, lightDir);
+  water.setWaterHeight(initialWaterHeight);
+  fpp.addFilter(water);
+  viewPort.addProcessor(fpp);
+  ...
+}
+ +

+Usually you make the water reflect everything attached to the rootNode. But you can also give a custom node (a subnode of the rootNode) to the WaterFilter constructor that has only a subset of scene nodes attached. This would be a relevant optimization if you have lots of nodes that are far away from the water, or covered, and will never be reflected. +

+ +
+ +

Optional: Waves

+
+ +

+ +If you want waves, set the water height in the update loop. We reuse the initialWaterHeight variable, and repeatedly reset the waterHeight value according to time. This causes the waves. +

+
private float time = 0.0f;
+private float waterHeight = 0.0f; 
+ 
+@Override
+public void simpleUpdate(float tpf) {
+  super.simpleUpdate(tpf);
+  time += tpf;
+  waterHeight = (float) Math.cos(((time * 0.6f) % FastMath.TWO_PI)) * 1.5f;
+  water.setWaterHeight(initialWaterHeight + waterHeight);
+}
+ +
+ +

Optional: Water Wave and Color Effects

+
+ +

+ + +

+ +

+All these effects are optional. Every setter also has a getter. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Water method exampleEffects: Waves Default
water.setWaterHeight(-6);Use this waterheight method for causing waves.0.0f
water.setMaxAmplitude(0.3f);How high the highest waves are.1.0f
water.setWaveScale(0.008f);Sets the scale factor of the waves height map. The smaller the value, the bigger the waves! 0.005f
water.setWindDirection(new Vector2f(0,1))Sets the wind direction, which is the direction where the waves moveVector2f(0.0f, -1.0f)
water.setSpeed(0.7f);How fast the waves move. Set it to 0.0f for still water.1.0f
water.setHeightTexture( (Texture2D)
+manager.loadTexture("Textures/waveheight.png") )
This height map describes the shape of the waves"Common/MatDefs/Water/Textures/heightmap.jpg"
water.setNormalTexture( (Texture2D)
+manager.loadTexture("Textures/wavenormals.png") )
This normal map describes the shape of the waves"Common/MatDefs/Water/Textures/gradient_map.jpg"
water.setUseRipples(false);Switches the ripples effect on or off.true
water.setNormalScale(0.5f)Sets the normal scaling factors to apply to the normal map. The higher the value, the more small ripples will be visible on the waves.1.0f
+
+ + + + + + + + + + + + + + + + + + + + + +
Water method example Effects: ColorDefault
water.setLightDirection(new Vector3f(-0.37f,-0.50f,-0.78f))Usually you set this to the same as the light source's direction. Use this to set the light direction if the sun is moving.Value given to WaterFilter() constructor.
water.setLightColor(ColorRGBA.White)Usually you set this to the same as the light source's color.RGBA.White
water.setWaterColor(ColorRGBA.Brown.mult(2.0f));Sets the main water color.greenish blue
+Vector3f(0.0f,0.5f,0.5f,1.0f)
water.setDeepWaterColor(ColorRGBA.Brown);Sets the deep water color.dark blue
+Vector3f(0.0f, 0.0f,0.2f,1.0f)
water.setWaterTransparency(0.2f);Sets how fast colors fade out. use this to control how clear (e.g. 0.05f) or muddy (0.2f) water is. 0.1f
water.setColorExtinction(new Vector3f(10f,20f,30f));Sets At what depth the refraction color extincts. The three values are RGB (red, green, blue) in this order. Play with these parameters to "muddy" the water.Vector3f(5f,20f,30f)
+
+ + + + + + + + + + + + +
Water method example Effects: ShoreDefault
water.setCenter(Vector3f.ZERO);
+water.setRadius(260);
Limit the water filter to a semisphere with the given center and radius. Use this for lakes and smaller bodies of water. Skip this for oceans.unused
water.setShoreHardness(1.0f);Sets how soft the transition between shore and water should be. High values mean a harder transition between shore and water.0.1f
water.setUseHQShoreline(false);Renders shoreline with better quality ?true
+
+ + + + + + + + + + + + + + + +
Water method example Effects: FoamDefault
water.setUseFoam(false);Switches the white foam on or offtrue
water.setFoamHardness(0.5f)Sets how much the foam will blend with the shore to avoid a hard edged water plane.1.0f
water.setFoamExistence(new Vector3f(0.5f,5f,1.0f))The three values describe what depth foam starts to fade out, at what depth it is completely invisible, at what height foam for waves appears (+ waterHeight).Vector3f(0.45f,4.35f,1.0f)
water.setFoamTexture( (Texture2D)
+manager.loadTexture("Textures/foam.png") )
This foam texture will be used with WrapMode.Repeat"Common/MatDefs/Water/Textures/foam.jpg"
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Water method example Effects: LightDefault
water.setSunScale(1f);Sets how big the sun should appear in the light's specular effect on the water.3.0f
water.setUseSpecular(false)Switches specular effect on or offtrue
water.setShininess(0.8f)Sets the shininess of the water reflections0.7f
water.setUseRefraction(true)Switches the refraction effect on or off.true
water.setRefractionConstant(0.2f);The lower the value, the less reflection can be seen on water. This is a constant related to the index of refraction (IOR) used to compute the fresnel term.0.3f
water.setRefractionStrength(-0.1)This value modifies the current Fresnel term. If you want to weaken reflections use bigger value. If you want to empasize them, use a value smaller than 0.0.0f
water.setReflectionMapSize(256)Sets the size of the reflection map. The higher, the better the quality, but the slower the effect.512
+ +
+ +

Sound Effects

+
+ +

+ +You should also add audio nodes with water sounds to complete the effect. +

+
AudioNode waves = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", false);
+waves.setLooping(true);
+audioRenderer.playSource(waves);
+ +

+ +See also: audio. +

+
+ +

+See also: +

+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.html new file mode 100644 index 000000000..c9d85febc --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.html @@ -0,0 +1,217 @@ + +

Ragdoll Physics

+
+ +

+ +The jMonkeyEngine3 has built-in support for via the com.jme3.bullet package. Physics are not only responsible for handing collisions, but they also make hinges and joints possible. One special example of physical joints are ragdoll physics, shown here. +

+ +

+ +

+ +
+ +

Sample Code

+
+ + +
+ +

Preparing the Physics Game

+
+
    +
  1. Create a SimpleApplication with a BulletAppState
    +
      +
    • This gives us a PhysicsSpace for PhysicControls
      +
    • +
    +
  2. +
  3. Add a physical floor (A box collision shape with mass zero)
    +
  4. +
+ +
+ +

Creating the Ragdoll

+
+ +

+ +A ragdoll is a simple "person" (dummy) that you build out of cylinder collision shapes. The ragdoll has 11 limbs: 1 for shoulders, 1 for the body, 1 for hips; plus 2 arms and 2 legs that are made up of two limbs each. In your game, you will likely replace the cylinders with your own (better looking) limb models. In this example here we just use simple cylinders. +

+ +
+ +

Limbs

+
+ +

+ +Since you're just creating the ragdoll for this example, all the limbs have the same shape, and you can write a simple helper method to create them. The function returns a PhysicsControl with CollisionShape with the width, height, location, and rotation (vertical or horizontal) that you specify. You choose a CapsuleCollisionShape (a cylinder with rounded top and bottom) so the limbs collide smoothly against one another. +

+
private Node createLimb(float width, float height, Vector3f location, boolean rotate) {
+        int axis = rotate ? PhysicsSpace.AXIS_X : PhysicsSpace.AXIS_Y;
+        CapsuleCollisionShape shape = new CapsuleCollisionShape(width, height, axis);
+        Node node = new Node("Limb");
+        RigidBodyControl rigidBodyControl = new RigidBodyControl(shape, 1);
+        node.setLocalTranslation(location);
+        node.addControl(rigidBodyControl);
+        return node;
+}
+ +

+You write a custom helper method to initialize the limbs. Look at the screenshot above for orientation. +

+ +
Node shoulders = createLimb(0.2f, 1.0f, new Vector3f( 0.00f, 1.5f, 0), true);
+Node     uArmL = createLimb(0.2f, 0.5f, new Vector3f(-0.75f, 0.8f, 0), false);
+Node     uArmR = createLimb(0.2f, 0.5f, new Vector3f( 0.75f, 0.8f, 0), false);
+Node     lArmL = createLimb(0.2f, 0.5f, new Vector3f(-0.75f,-0.2f, 0), false);
+Node     lArmR = createLimb(0.2f, 0.5f, new Vector3f( 0.75f,-0.2f, 0), false);
+Node      body = createLimb(0.2f, 1.0f, new Vector3f( 0.00f, 0.5f, 0), false);
+Node      hips = createLimb(0.2f, 0.5f, new Vector3f( 0.00f,-0.5f, 0), true);
+Node     uLegL = createLimb(0.2f, 0.5f, new Vector3f(-0.25f,-1.2f, 0), false);
+Node     uLegR = createLimb(0.2f, 0.5f, new Vector3f( 0.25f,-1.2f, 0), false);
+Node     lLegL = createLimb(0.2f, 0.5f, new Vector3f(-0.25f,-2.2f, 0), false);
+Node     lLegR = createLimb(0.2f, 0.5f, new Vector3f( 0.25f,-2.2f, 0), false);
+ +

+You now have the outline of a person. But if you ran the application now, the individual limbs would fall down independently of one another ??? the ragdoll is still lacking joints. +

+ +
+ +

Joints

+
+ +

+ +As before, you write a small helper method. This time its purpose is to quickly join two limbs A and B at the connection point that we specify. +

+ + +

+ +Use the helper method to connect all limbs with joints where they belong, at one end of the limb. +

+
join(body,  shoulders, new Vector3f( 0.00f,  1.4f, 0));
+join(body,       hips, new Vector3f( 0.00f, -0.5f, 0));
+join(uArmL, shoulders, new Vector3f(-0.75f,  1.4f, 0));
+join(uArmR, shoulders, new Vector3f( 0.75f,  1.4f, 0));
+join(uArmL,     lArmL, new Vector3f(-0.75f,  0.4f, 0));
+join(uArmR,     lArmR, new Vector3f( 0.75f,  0.4f, 0));
+join(uLegL,      hips, new Vector3f(-0.25f, -0.5f, 0));
+join(uLegR,      hips, new Vector3f( 0.25f, -0.5f, 0));
+join(uLegL,     lLegL, new Vector3f(-0.25f, -1.7f, 0));
+join(uLegR,     lLegR, new Vector3f( 0.25f, -1.7f, 0));
+ +

+Now the ragdoll is connected. If you ran the app now, the doll would collapse, but the limbs would stay together. +

+ +
+ +

Attaching Everything to the Scene

+
+ +

+ +We create one (non-physical) Node named ragDoll, and attach all other nodes to it. +

+
ragDoll.attachChild(shoulders);
+ragDoll.attachChild(body);
+ragDoll.attachChild(hips);
+ragDoll.attachChild(uArmL);
+ragDoll.attachChild(uArmR);
+ragDoll.attachChild(lArmL);
+ragDoll.attachChild(lArmR);
+ragDoll.attachChild(uLegL);
+ragDoll.attachChild(uLegR);
+ragDoll.attachChild(lLegL);
+ragDoll.attachChild(lLegR);
+ +

+To use the ragdoll in a scene, we attach its main node to the rootNode, and to the PhysicsSpace. +

+
rootNode.attachChild(ragDoll);
+bulletAppState.getPhysicsSpace().addAll(ragDoll);
+ +
+ +

Applying Forces

+
+ +

+ +To pull the doll up, you could add an input handler that triggers the following action: +

+
Vector3f upforce = new Vector3f(0, 200, 0);
+shoulders.applyContinuousForce(true, upforce);
+ +

+We can use the action to pick the doll up and put it back on its feet, or what ever. Read more about Forces here. +

+ +
+ +

Detecting Collisions

+
+ +

+ +Read the Responding to a PhysicsCollisionEvent chapter in the general physics documentation on how to detect collisions. You can detect collisions between limbs or between limbs and the floor, and trigger game events. +

+ +
+ +

Best Practices

+
+ +

+ +If you experience weird behaviour in a ragdoll ??? such as exploding into pieces and then reassembling ??? check your collision shapes. Verify you did not position the limbs too close to one another when assmebling the ragdoll. You typically see physical nodes being ejected when their collision shapes intersect, which puts physics in an impossible state. +

+
+ documentation, + physics, + character, + NPC, + forces, + collisions +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.png new file mode 100644 index 000000000..d7335e3a1 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/ragdoll.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/read_graphic_card_capabilites.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/read_graphic_card_capabilites.html new file mode 100644 index 000000000..75115007e --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/read_graphic_card_capabilites.html @@ -0,0 +1,72 @@ + +

Read Graphic Card Capabilites

+
+ +

+ +When different people test your new game, you may get feedback that the game doesn't run on their hardware, or that some details don't look as expected. You need to detect which fetaures the user's hardware supports, and offer a replacement for non-supported features on olde hardware (or deactivate them automatically). +

+ +

+You can read (and print) the capabilities of the user's graphic card using the com.jme3.renderer.Caps class: +

+
Collection<Caps> caps = renderer.getCaps();
+Logger.getLogger(HelloWorld.class.getName()).log(Level.INFO, ???Caps: {0}???, caps.toString());
+ +

+Note: Replace HelloWorld by the name of the class where you are using this line. +

+ +
+ +

Examples

+
+ +

+ +A newer graphic card has modern capabilities, for example OpenGL 2.1 and NonPowerOfTwoTextures: +

+
INFO: Running on jMonkeyEngine 3.0.0 
+INFO: Using LWJGL 2.8.2
+INFO: Selected display mode: 1280 x 720 x 0 @0Hz
+INFO: Adapter: null
+INFO: Driver Version: null
+INFO: Vendor: ATI Technologies Inc.
+INFO: OpenGL Version: 2.1 ATI-7.14.5
+INFO: Renderer: AMD Radeon HD 6770M OpenGL Engine
+INFO: GLSL Ver: 1.20
+INFO: Timer resolution: 1.000 ticks per second
+INFO: Capabilities: [FrameBuffer, FrameBufferMRT, FrameBufferMultisample, 
+OpenGL20, OpenGL21, ARBprogram, GLSL100, GLSL110, GLSL120, 
+VertexTextureFetch, TextureArray, FloatTexture, 
+FloatColorBuffer, FloatDepthBuffer, PackedFloatTexture, SharedExponentTexture, PackedFloatColorBuffer, 
+TextureCompressionLATC, NonPowerOfTwoTextures, MeshInstancing]
+ +

+Here is an example of the capabilities of an semi-old graphic card that only supports OpenGL 2.0. If you use OpenGL 2.1 features you need to decide whether to branch to a low-quality replacement of the unsupported features (if you still want to support this card); or whether the game will not start at all and displays an error message explaining the user what capabilities his hardware is missing to be able to play the game. +

+
INFO: Running on jMonkey Engine 3 
+INFO: Using LWJGL 2.7.1
+INFO: Selected display mode: 1024 x 768 x 0 @0Hz
+INFO: Adapter: null
+INFO: Driver Version: null
+INFO: Vendor: ATI Technologies Inc.
+INFO: OpenGL Version: 2.0 ATI-1.6.36
+INFO: Renderer: ATI Radeon X1600 OpenGL Engine
+INFO: GLSL Ver: 1.20
+INFO: Timer resolution: 1.000 ticks per second
+INFO: Capabilities: [FrameBuffer, FrameBufferMRT, FrameBufferMultisample,
+OpenGL20, ARBprogram, GLSL100, GLSL110, GLSL120, 
+VertexTextureFetch, FloatTexture, 
+TextureCompressionLATC, NonPowerOfTwoTextures]
+ +

+This next example is lacking NonPowerOfTwoTextures, this tells you that this user's graphic card cannot handle textures with sizes that are not square powers of two (such as "128x128"). +

+
INFO: Capabilities: [FrameBuffer, FrameBufferMRT, FrameBufferMultisample, 
+OpenGL20, ARBprogram, GLSL100, GLSL110, GLSL120, 
+VertexTextureFetch, FloatTexture, TextureCompressionLATC]
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/remote-controlling_the_camera.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/remote-controlling_the_camera.html new file mode 100644 index 000000000..9275d7dd6 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/remote-controlling_the_camera.html @@ -0,0 +1,59 @@ + +

Remote-Controlling the Camera

+
+ +
+ +

Positioning the Camera

+
+ +

+ +You can steer the camera using Cinematics: +

+
    +
  1. Create a Cinematic.
    +
  2. +
  3. Create a CameraNode and bind the camera object to the Cinematic. Note that we also give the camera node a name in this step.
    CameraNode camNode = cinematic.bindCamera("topView", cam);
    +
    +
  4. +
  5. Position the camera node in its start location.
    +
  6. +
  7. Use activateCamera() to give the control of the camera to this node. You now see the scene from this camera's point of view. For example to see through the camera node named "topView", 6 seconds after the start of the cinematic, you'd write
    cinematic.activateCamera(6, "topView");
    +
    +
  8. +
+ +
+ +

Code Sample

+
+
flyCam.setEnabled(false);
+Cinematic cinematic = new Cinematic(rootNode, 20);
+ 
+CameraNode camNodeTop = cinematic.bindCamera("topView", cam);
+camNodeTop.setControlDir(ControlDirection.SpatialToCamera);
+camNodeTop.getControl(0).setEnabled(false);
+ 
+CameraNode camNodeSide = cinematic.bindCamera("sideView", cam);
+camNodeSide.setControlDir(ControlDirection.CameraToSpatial);
+camNodeSide.getControl(0).setEnabled(false);
+ +
+ +

Moving the Camera

+
+ +

+ +If desired, attach the camNode to a MotionTrack to let it travel along waypoints. This is demonstrated in the . +

+
+ camera, + documentation, + cinematics +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/save_and_load.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/save_and_load.html new file mode 100644 index 000000000..e2f012c9c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/save_and_load.html @@ -0,0 +1,193 @@ + +

Saving and Loading Games (.j3o)

+
+ +

+ +Spatials (that is Nodes and Geometries) can contain audio and light nodes, particle emitters, controls, and user data (player score, health, inventory, etc). For your game distribution, you must convert all original models to a faster binary format. You save individual Spatials as well as scenes using com.jme3.export.binary.BinaryExporter. +

+ +

+The jMonkeyEngine's binary file format is called .j3o. You can convert, view and edit .j3o files and their materials in the jMonkeyEngine SDK and compose scenes (this does not include editing meshes). For the conversion, you can either use the BinaryExporters, or a context menu in the SDK. +

+ +

+

The jMonkeyEngine's serialization system is the com.jme3.export.Savable interface. JME3's BinaryExporter can write standard Java objects, JME3 objects, and primitive data types that are included in a spatial's user data. If you use custom game data classes, see below how to make them "Savable". +

+

+ +

+There is also a com.jme3.export.xml.XMLExporter and com.jme3.export.xml.XMLImporter that similarly converts jme3 spatials to an XML format. But you wouldn't use that to load models at runtime (quite slow). +

+ +
+ +

Sample Code

+
+ + +
+ +

Saving a Node

+
+ +

+ +The following example overrides stop() in SimpleApplication to save the rootNode to a file when the user quits the application. The saved rootNode is a normal .j3o binary file that you can open in the SDK. +

+ +

+

Note that when you save a model that has textures, the references to those textures are stored as absolute paths, so when loading the j3o file again, the textures have to be accessible at the exact location (relative to the assetmanager root, by default the assets directory) they were loaded from. This is why the SDK manages the conversion on the project level. +

+

+
  /* This is called when the user quits the app. */
+  @Override
+  public void stop() {
+    String userHome = System.getProperty("user.home");
+    BinaryExporter exporter = BinaryExporter.getInstance();
+    File file = new File(userHome+"/Models/"+"MyModel.j3o");
+    try {
+      exporter.save(rootNode, file);
+    } catch (IOException ex) {
+      Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error: Failed to save game!", ex);
+    }
+    super.stop(); // continue quitting the game
+  }
+ +
+ +

Loading a Node

+
+ +

+ +The following example overrides simpleInitApp() in SimpleApplication to load Models/MyModel.j3o when the game is initialized. +

+
  @Override
+  public void simpleInitApp() {
+     String userHome = System.getProperty("user.home");
+     assetManager.registerLocator(userHome, FileLocator.class);
+     Node loadedNode = (Node)assetManager.loadModel("Models/MyModel.j3o");
+     loadedNode.setName("loaded node");
+     rootNode.attachChild(loadedNode);
+  }
+ 
+ +

+

Here you see why we save user data inside spatials ??? so it can be saved and loaded together with the .j3o file. If you have game data outside Spatials, you have to remember to save() and load(), and get() and set() it yourself. +

+

+ +
+ +

Custom Savable Class

+
+ +

+ +JME's BinaryExporter can write standard Java objects (String, ArrayList, buffers, etc), JME objects (Savables, such as Material), and primitive data types (int, float, etc). If you are using any custom class together with a Spatial, then the custom class must implement the com.jme3.export.Savable interface. There are two common cases where this is relevant: + +

+ + +

+If your custom classes (the user data or the Controls) do not implement Savable, then the BinaryImporter/BinaryExporter cannot save the Spatial! +

+ +

+So every time you create a custom Control or custom user data class, remember to implement Savable: +

+
import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.material.Material;
+import java.io.IOException;
+ 
+public class MyCustomClass implements Savable {
+    private int      someIntValue;   // some custom user data
+    private float    someFloatValue; // some custom user data
+    private Material someJmeObject;  // some custom user data
+ 
+    ...
+    // your other code...
+    ...
+ 
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule capsule = ex.getCapsule(this);
+        capsule.write(someIntValue,   "someIntValue",   1);
+        capsule.write(someFloatValue, "someFloatValue", 0f);
+        capsule.write(someJmeObject,  "someJmeObject",  new Material());
+    }
+ 
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule capsule = im.getCapsule(this);
+        someIntValue   = capsule.readInt(    "someIntValue",   1);
+        someFloatValue = capsule.readFloat(  "someFloatValue", 0f);
+        someJmeObject  = capsule.readSavable("someJmeObject",  new Material());
+    }
+}
+ +

+To make a custom class savable: +

+
    +
  1. Implement Savable and add the write() and read() methods as shown in the example above.
    +
  2. +
  3. Do the following for each non-temporary class field:
    +
      +
    • Add one line that write()s the data to the JmeExport output capsule.
      +
        +
      • Specify the variable to save, give it a String name (can be the same as the variable name), and specify a default value.
        +
      • +
      +
    • +
    • Add one line that read???()s the data to the JmeImport input capsule.
      +
        +
      • On the left side of the assignment, specify the class field that you are restoring
        +
      • +
      • On the right side, use the appropriate capsule.read???() method for the data type. Specify the String name of the variable (must be the same as you used in the write() method), and again specify a default value.
        +
      • +
      +
    • +
    +
  4. +
+ +

+ +

As with all serialization, remember that if you ever change data types in custom classes, the updated read() methods will no longer be able to read your old files. +

+

+
+ convert, + j3o, + models, + load, + save, + documentation, + serialization, + import, + export, + spatial, + node, + mesh, + geometry, + scenegraph, + sdk +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/screenshots.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/screenshots.html new file mode 100644 index 000000000..3261f3ba7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/screenshots.html @@ -0,0 +1,27 @@ + +

Taking Screenshots

+
+ +

+ +The com.jme3.app.state.ScreenshotAppState enables your users to take screenshots of the running game. +

+ +

+You activate this feature as follows in your simpleInitApp() method: + +

+
ScreenshotAppState screenShotState = new ScreenshotAppState();
+this.stateManager.attach(screenShotState);
+ +

+The default screenshot key is KeyInput.KEY_SYSRQ, alos known as "System Request / Print Screen" key. On Mac keyboards, this key does not exist, so on Mac OS you take screenshots using Command+Shift+3 (fullscreen) or Command+Shift+4 (windowed: press space to select a window and then click). +

+ +

+The screenshot is saved to the user directory. + +

+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_glass.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_glass.jpg new file mode 100644 index 000000000..8974aa3c6 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_glass.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light1.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light1.jpg new file mode 100644 index 000000000..383b4edfb Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light1.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light2.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light2.jpg new file mode 100644 index 000000000..62885b7c7 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_light2.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_matcap.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_matcap.jpg new file mode 100644 index 000000000..043193cac Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shaderblow_matcap.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-ani.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-ani.gif new file mode 100644 index 000000000..6e9a6ab4b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-ani.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-textured-ani.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-textured-ani.gif new file mode 100644 index 000000000..74b9d7d34 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shading-textured-ani.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow-sponza-ssao.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow-sponza-ssao.png new file mode 100644 index 000000000..892fb411e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow-sponza-ssao.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow.png new file mode 100644 index 000000000..de7ec9496 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shadow.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shape.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shape.html new file mode 100644 index 000000000..6c1bb987b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/shape.html @@ -0,0 +1,199 @@ + +

Shapes

+
+ +

+The simplest type of Meshes are the built-in JME Shapes. You can create Shapes without using the AssetManager. +

+ +
+ +

3D shapes

+
+ +

+ + + +

+ + + +

+ + + +

+ + + +
+ +

Non-3D shapes

+
+ + +
+ +

com.jme3.math versus com.jme3.shape?

+
+ +

+ +Do not mix up these visible com.jme3.shapes with similarly named classes from the com.jme3.math package. Choose the right package when letting your IDE fill in the import statements! +

+ + +

+These maths objects are invisible and are used for collision testing (ray casting) or to describe motion paths. They cannot be wrapped into a Geometry. +

+ +
+ +

Usage

+
+ +
+ +

Basic Usage

+
+ +

+ +To add a shape to the scene: +

+
    +
  1. Create the base mesh shape.
    +
  2. +
  3. Wrap the mesh into a Geometry.
    +
  4. +
  5. Assign a Material to the Geometry.
    +
  6. +
  7. Attach the Geometry to the rootNode to make it visible.
    +
  8. +
+ +

+ +

Create one static shape as mesh and use it in several geometries, or clone() the geometries. +

+

+ +
+ +

Complex Shapes

+
+ +

+ +You can compose more complex custom Geometries out of simple Shapes. Think of the buildings in games like Angry Birds, or the building blocks in Second Life ("prims") and in Tetris ("Tetrominos"). +

+
    +
  1. Create a Node. By default it is located at the origin (0/0/0) ??? leave the Node there for now.
    +
  2. +
  3. Create your shapes and wrap each into a Geometry, as just described.
    +
  4. +
  5. Attach each Geometry to the Node.
    +
  6. +
  7. Arrange the Geometries around the Node (using setLocalTranslation()) so that the Node is in the center of the new constellation. The central Node is the pivot point for transformations (move/scale/rotate).
    +
  8. +
  9. Move the pivot Node to its final location in the scene. Moving the pivot Node moves the attached constellation of Geometries with it.
    +
  10. +
+ +

+The order is important: First arrange around origin, then transform. Otherwise, transformations are applied around the wrong center (pivot). Of course, you can attach your constellation to other pivot Nodes to create even more complex shapes (a chair, a furnished room, a house, a city, ???), but again, arrange them around the origin first before you transform them. Obviously, such composed Geometries are simpler than hand-sculpted meshes from a mesh editor. +

+ +
+ +

Code Examples

+
+ +

+ +Create the Mesh shape: + +

+
Sphere mesh = new Sphere(32, 32, 10, false, true);
+
Dome mesh = new Dome(Vector3f.ZERO, 2, 4, 1f,false); // Pyramid
+
Dome mesh = new Dome(Vector3f.ZERO, 2, 32, 1f,false); // Cone
+
Dome mesh = new Dome(Vector3f.ZERO, 32, 32, 1f,false); // Small hemisphere
+
Dome mesh = new Dome(Vector3f.ZERO, 32, 32, 1000f,true); // SkyDome
+
PQTorus mesh = new PQTorus(5,3, 2f, 1f, 32, 32); // Spiral torus
+
PQTorus mesh = new PQTorus(3,8, 2f, 1f, 32, 32); // Flower torus
+ +

+ +Use one of the above examples together with the following geometry in a scene: + +

+
Geometry geom = new Geometry("A shape", mesh); // wrap shape into geometry
+Material mat = new Material(assetManager,      
+    "Common/MatDefs/Misc/ShowNormals.j3md");   // create material
+geom.setMaterial(mat);                         // assign material to geometry
+// if you want, transform (move, rotate, scale) the geometry.
+rootNode.attachChild(geom);                    // attach geometry to a node
+ +
+ +

See also

+
+ +

+ +* Optimization ??? The GeometryBatchFactory class combines several of your shapes with the same texture into one mesh with one texture. +

+
+ spatial, + node, + mesh, + geometry, + scenegraph +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/simplewater.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/simplewater.png new file mode 100644 index 000000000..85b4a7e36 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/simplewater.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/sky.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/sky.html new file mode 100644 index 000000000..d968832ca --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/sky.html @@ -0,0 +1,102 @@ + +

How to add a Sky to your Scene

+
+ +

+ +
+ +

+ +

+Here is an example for how you add a static horizon (a background landscape and a sky) to a scene. +Having a discernable horizon with a suitable landscape (or space, or ocean, or whatever) in the background makes scenes look more realistic than just a single-colored "sky" background. +

+ +
+ +

Adding the Sky

+
+ +

+ +Adding a sky is extremely easy using the com.jme3.util.SkyFactory. +

+
rootNode.attachChild(SkyFactory.createSky(
+            assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
+ +

+To add a sky you need to supply: +

+
    +
  1. The assetManager object to use
    +
  2. +
  3. A cube or sphere map texture of the sky
    +
  4. +
  5. Set the boolean to true if you are using a sphere map texture. For a cube map, use false.
    +Tip: Cube map is the default. You would know if you had created a sphere map.
    +
  6. +
+ +

+ +Internally, the SkyFactory calls the following methods: +

+
    +
  1. sky.setQueueBucket(Bucket.Sky); makes certain the sky is rendered in the right order, behind everything else.
    +
  2. +
  3. sky.setCullHint(Spatial.CullHint.Never); makes certain that the sky is never culled.
    +
  4. +
  5. The SkyFactory uses the internal jME3 material definition Sky.j3md. This Material definition works with sphere and cube maps.
    +
  6. +
+ +
+ +

Creating the Textures

+
+ +

+ +As the sky texture we use the sample BrightSky.dds file from jme3test-test-data. +

+ +

+How to create a sky textures? + +

+ + +

+ +Box or Sphere? +

+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spatial.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spatial.html new file mode 100644 index 000000000..29a7e5aae --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spatial.html @@ -0,0 +1,277 @@ + +

Spatial

+
+ +

+ +This is an introduction to the concept of Spatials, the elements of the 3D scene graph. The scene graph is a data structure that manages all objects in your 3D world. For example, the scene graph keeps track of the 3D models that you load and position. When you extend a Java class from com.jme3.app.SimpleApplication, you automatically inherit the scene graph and its rootNode. +

+ +

+The rootNode is the central element of the scene graph. Even if the scene graph is empty, it always contains at least the rootNode. We attach Spatials to the rootNode. Attached Spatials are always in a parent-child relationship. Every time you attach a Spatial to something, it is implicitly detached from its previous parent. A Spatial can have only one parent. A Spatial can have several children. +

+ +

+If you think you need to understand the scene graph concept better, please read Scenegraph for dummies first. +

+ +
+ +

Node versus Geometry

+
+ +

+ +In your Java code, a Spatial is either an instance of com.jme3.scene.Node or a com.jme3.scene.Geometry instance. You use the two types of Spatials for different purposes: +

+ +

+ + +

+
+ + + + + + + + + + + + + + + + + + + + + +
com.jme3.scene.Spatial
Purpose: A Spatial is an abstract data structure that stores user data and transformations (= translation, rotation, scale) of elements of the 3D scene graph. Spatials can be saved and loaded using the Asset Manager.
com.jme3.scene.Geometry com.jme3.scene.Node
Visibility: A Geometry represents a visible 3D object in the scene graph. A Node is an invisible "handle" for a group of Spatials in the scene graph.
Purpose: Use Geometries to represent an object's look: Every Geometry contains a polygon mesh and a material, specifying its shape, color, texture, and opacity/transparency.
+You attach Geometries to Nodes.
Use Nodes to structure and group Geometries and other Nodes. Every Node is attached to one parent node, and each node can have zero or more children (Nodes or Geometries) attached to itself.
+When you transform (move, rotate, etc) a parent node, all its children are transformed (moved, rotated, etc).
Content: Transformations; custom user data;
+mesh and material;
Transformations; custom user data;
+no mesh, no material.
Examples: Box, sphere, player, building, terrain, vehicle, missiles, NPCs, etc??? rootNode, guiNode, audioNode, a custom grouping node such as vehicleNode or shipNode with passengers attached, etc.
+ +

+ +

You never create a Spatial with Spatial s = new Spatial();! A Spatial is an abstract concept, like a mammal (there is no actual creature called "mammal" walking around here). You create either a com.jme3.scene.Node or com.jme3.scene.Geometry instance. Some methods, however, require a Spatial type as argument: This is because they are able to accept both Nodes and Geometries as arguments. In this case, you simply cast a Node or Geometry to Spatial. +

+

+ +
+ +

Mesh

+
+ +

+ +The polygon Mesh inside a Geometry can be one of three things: + +

+ + +
+ +

What is a Clone?

+
+ +

+ +Cloned spatials share the same mesh, while each cloned spatial can have its own local transformation (translation, rotation, and scale) in the scene. This means you only use clone() on spatials whose meshes never change. The most common use case for cloning is when you use several Spatials that are based on the same Shapes. +

+ +

+The second use case is: When you load a model using loadModel() from the AssetManager, you may get a clone()ed object. In particular: +

+ + +

+Usually there is no need to manually use any of the clone() methods on models. Using the Asset Manager's loadModel() method will automatically do the right thing for your models. +

+ +

+

"Box worlds" are not made up of statically cloned Box() shapes, this would still be too slow for large worlds. To learn how to make real fast box worlds, search the web for voxelization techniques. +

+

+ +
+ +

How to Add Fields and Methods to a Spatial

+
+ +

+ +You can include custom user data ???that is, custom Java objects and methods??? in Nodes and Geometries. This is very useful for maintaining information about a game element, such as health, budget, ammunition, inventory, equipment, etc for players, or landmark locations for terrains, and much more. +

+ +

+

You want to add custom accessor methods to a spatial? Do not extend Node or Geometry, use Custom Controls instead. You want to add custom fields to a spatial? Do not extend Node or Geometry, use the built-in setUserData() method instead. Where ever the Spatial is accessible, you can easily access the object's class fields (user data) and accessors (control methods) this way. +

+

+ +

+This first example adds an integer field named health to the Spatial player_node, and initializes it to 100. + +

+
player_node.setUserData("health", 100);
+ +

+The second example adds a set of custom accessor methods to the player object. You create a custom PlayerControl() class and you add this control to the Spatial: +

+
player_node.addControl(new PlayerControl());
+ +

+In your PlayerControl() class, you define custom methods that set and get your user data in the spatial object. For example, the control could add accessors that set and get the player's health: +

+
public int getHealth() {
+  return (Integer)spatial.getUserData("health");
+}
+public void setHealth(int h) {
+  spatial.setUserData("health",h);
+}
+ +

+Elsewhere in your code, you can access this data wherever you have access to the Spatial player_node. +

+
health = player_node.getControl(PlayerControl.class).getHealth();
+...
+player_node.getControl(PlayerControl.class).setHealth(99);
+ + +

+ +This is how you list all data keys that are already defined for one Spatial: +

+
for(String key : spatial.getUserDataKeys()){
+    System.out.println(spatial.getName()+"'s keys: "+key);
+}
+ +
+ +

How to Access a Named Sub-Mesh

+
+ +

+ +Often after you load a scene or model, you need to access a part of it as an individual Geometry in the scene graph. Maybe you want to swap a character's weapon, or you want to play a door-opening animation. First you need to know the unique name of the sub-mesh. + +

+
    +
  1. Open the model in a 3D mesh editor, or in the jMonkeyEngine SDK's Scene Composer.
    +
  2. +
  3. Find out the existing names of sub-meshes in the model.
    +
  4. +
  5. Assign unique names to sub-meshes in the model if neccessary.
    +
  6. +
+ +

+ +In the following example, the Node house is the loaded model. The sub-meshes in the Node are called its children. The String, here door 12, is the name of the mesh that you are searching. +

+
Geometry submesh = (Geometry) houseScene.getChild("door 12");
+ +
+ +

What is Culling?

+
+ +

+There are two types of culling: Face culling, and view frustrum culling. +

+ +

+Face culling refers to not drawing certain polygons of a mesh. The "inside" of the mesh (the so called backface) is never visible to the player, and as an optimization, game engines skip calculating backfaces by default. You may want to deactivate Face Culling while debugging custom meshes, so you can see them in case you turned them inside-out by accident. +

+ +

+You can switch the com.jme3.material.RenderState.FaceCullMode to +

+ + +

+ +Example: + +

+
material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.FrontAndBack);
+ +

+View frustum culling refers to not drawing (and not even calculating) certain whole models in the scene. At any given moment, half of the scene is behind the player and out of sight anyway. View frustum culling is an optimization to not calculate scene elements that are not visible ??? elements that are "outside the view frustrum". +

+ +

+The decision what is visible and what not, is done automatically by the engine (CullHint.Dynamic). Optionally, you can manually control whether the engine culls individual spatials (and children) from the scene graph: +

+ + +

+ +Example: + +

+
spatial.setCullHint(CullHint.Never); // always drawn
+ +
+ +

See also

+
+ +
+ spatial, + node, + mesh, + geometry, + scenegraph +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spotlight.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spotlight.png new file mode 100644 index 000000000..96d525c31 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/spotlight.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/swing_canvas.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/swing_canvas.html new file mode 100644 index 000000000..46c7f135d --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/swing_canvas.html @@ -0,0 +1,188 @@ + +

JME3 Canvas in a Swing GUI

+
+ +

+3D games are typically played full-screen, or in a window that takes over the mouse and all inputs. However it is also possible to embed a jME 3 canvas in a standard Swing application.
+
+ +This can be useful when you create some sort of interactive 3D viewer with a user interface that is more complex than just a HUD: For instance an interactive scientific demo, a level editor, or a game character designer.
+
+ +

+ + +

+Here is the full code sample. + +

+ +
+ +

Extending SimpleApplication

+
+ +

+You start out just the same as for any jME3 game: The base application, here SwingCanvasTest, extends com.jme3.app.SimpleApplication. As usual, you use simpleInitApp() to initialize the scene, and simpleUpdate() as event loop.
+
+ +The camera's default behaviour in SimpleApplication is to capture the mouse, which doesn't make sense in a Swing window. You have to deactivate and replace this behaviour by flyCam.setDragToRotate(true); when you initialize the application: + +

+
public void simpleInitApp() {
+  // activate windowed input behaviour
+  flyCam.setDragToRotate(true);
+  // Set up inputs and load your scene as usual
+  ...
+}
+ +

+ +In short: The first thing that is different is the main() method. We don't call start() on the SwingCanvasTest object as usual. Instead we create a Runnable() that creates and opens a standard Swing jFrame. In the runnable, we also create our SwingCanvasTest game with special settings, create a Canvas for it, and add that to the jFrame. Then we call startCanvas(). + +

+ +
+ +

Main() and Runnable()

+
+ +

+The Swing isn't thread-safe and doesn't allow us to keep the jME3 canvas up-to-date. This is why we create a runnable for the jME canvas and queue it in the AWT event thread, so it can be invoked "later" in the loop, when Swing is ready with updating its own stuff.
+
+ +In the SwingCanvasTest's main() method, create a queued runnable(). It will contain the jME canvas and the Swing frame. + +

+
  public static void main(String[] args) {
+    java.awt.EventQueue.invokeLater(new Runnable() {
+      public void run() {
+         // ... see below ...
+      }
+    });
+  }
+ +

+ +

Note that you have to use app.enqueue() when modifying objects in the scene from the AWT Event Queue like you have to use java.awt.EventQueue.invokeLater() from other threads (e.g. the update loop) when changing swing elements. This can get hairy quickly if you don???t have a proper threading model planned so you might want to use NiftyGUI as it is embedded in the update loop thread and is also cross-platform compatible (e.g. android etc.). +

+ +

+ +
+ +

Creating the Canvas

+
+ +

+Here in the run() method, we start the jME application, create its canvas, create a Swing frame, and add everything together.
+
+ +Specify the com.jme3.system.AppSettings so jME knows the size of the Swing panel that we put it into. The application will not ask the user for display settings, you have to specify them in advance. + +

+
AppSettings settings = new AppSettings(true);
+settings.setWidth(640);
+settings.setHeight(480);
+ +

+ +We create our canvas application SwingCanvasTest, and give it the settings. We manually create a canvas for this game and configure the com.jme3.system.JmeCanvasContext. The method setSystemListener() makes sure that the listener receives events relating to context creation, update, and destroy. + +

+
SwingCanvasTest canvasApplication = new SwingCanvasTest();
+canvasApplication.setSettings(settings);
+canvasApplication.createCanvas(); // create canvas!
+JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication.getContext();
+ctx.setSystemListener(canvasApplication);
+Dimension dim = new Dimension(640, 480);
+ctx.getCanvas().setPreferredSize(dim);
+ +

+ +Note that we have not called start() on the application, as we would usually do in the main() method. We will call startCanvas() later instead. + +

+ +
+ +

Creating the Swing Frame

+
+ +

+Inside the run() method, you create the Swing window as you would usually do. Create an empty jFrame and add() components to it, or create a custom jFrame object in another class file (for example, by using the NetBeans GUI builder) and create an instance of it here. +Which ever you do, let's call the jFrame window. + +

+
JFrame window = new JFrame("Swing Application");
+window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ +

+ +We create a standard JPanel inside the JFrame. Give it any Layout you wish ??? here we use a simple Flow Layout. Where the code sample says "Some Swing Component", this is where you add your buttons and controls.
+
+ +The important step is to add() the canvas component into the panel, like all the other Swing components. + +

+
JPanel panel = new JPanel(new FlowLayout()); // a panel
+// add all your Swing components ...
+panel.add(new JButton("Some Swing Component"));
+...
+// add the JME canvas
+panel.add(ctx.getCanvas());
+ +

+ +OK, the jFrame and the panel are ready. We add the panel into the jFrame, and pack everything together. Set the window's visibility to true make it appear. + +

+
window.add(panel);
+window.pack();
+window.setVisible(true);
+ +

+ +Remember that we haven't called start() on the jME appliation yet? For the canvas, there is a special startCanvas() method that you must call now: + +

+
canvasApplication.startCanvas();
+ +

+ +Clean, build, and run! + +

+ +
+ +

Navigation

+
+ +

+Remember, to navigate in the scene, click and drag (!) the mouse, or press the WASD keys. Depending on your game you may even want to define custom inputs to handle navigation in this untypical environment. + +

+
+ documentation, + gui +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_diffuse_ss.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_diffuse_ss.png new file mode 100644 index 000000000..ebe5aaae1 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_diffuse_ss.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_glow_map_ss.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_glow_map_ss.png new file mode 100644 index 000000000..4a16d83a5 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tank_glow_map_ss.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tanlglow1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tanlglow1.png new file mode 100644 index 000000000..4c9ee2ef9 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/tanlglow1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/teapot-phong-illuminated.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/teapot-phong-illuminated.png new file mode 100644 index 000000000..28f9bdbee Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/teapot-phong-illuminated.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain-lod-high-medium-low.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain-lod-high-medium-low.png new file mode 100644 index 000000000..3dd0ce790 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain-lod-high-medium-low.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html new file mode 100644 index 000000000..834117c2c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html @@ -0,0 +1,308 @@ + +

TerraMonkey - The jMonkeyEngine Terrain System

+
+ +

+ +The goal of TerraMonkey is to provide a base implementation that will be usable for 80% of people's goals, while providing tools and a good foundation for the other 20% to build off of. Check out the videos in the following announcements: +

+ + +
+ +

Overview

+
+ +

+ + +

+ +

+TerraMonkey is a GeoMipMapping quad tree of terrain tiles that supports real time editing and texture splatting. That's a mouth full! Lets look at each part: +

+ + +
+ +

Current Features:

+
+ + +
+ +

Planned Features:

+
+ + +
+ +

Sample Code

+
+ + +
+ +

Geo Mip Mapping

+
+ +

+ + +

+ +

+You have seen GeoMipMapping implemented in games before. This is where the farther away terrain has fewer polygons, and as you move closer, more polygons fill in. The whole terrain is divided into a grid of patches, and each one has its own level of detail (LOD). The GeoMipMapping algorithm looks at each patch, and its neighbours, to determine how to render the geometry. It will seam the edges between two patches with different LOD. +

+ +

+GeoMipMapping often leads to "popping" where you see the terrain switch from one LOD to another. TerraMonkey has been designed so you can swap out different LOD calculation algorithms based on what will look best for your game. You can do this with the LodCalculator interface. +

+ +

+GeoMipMapping in TerraMonkey has been split into several parts: the terrain quad tree, and the LODGeomap. The geomap deals with the actual LOD and seaming algorithm. So if you want a different data structure for your terrain system, you can re-use this piece of code. The quad tree (TerrainQuad and TerrainPatch) provide a means to organize the LODGeomaps, notify them of their neighbour's LOD change, and to update the geometry when the LOD does change. To change the LOD it does this by changing the index buffer of the triangle strip, so the whole geometry doesn't have to be re-loaded onto the video card. If you are eager, you can read up more detail how GeoMipMapping works here: +

+ +
+ +

Terrain Quad Tree

+
+ +

+ +TerraMonkey is a quad tree. Each node is a TerrainQuad, and each leaf is a TerrainPatch. A TerrainQuad has either 4 child TerrainQuads, or 4 child TerrainPatches. The TerrainPatch holds the actual mesh geometry. This structure is almost exactly the same as JME2's TerrainPage system. Except now each leaf has a reference to its neighbours, so it doesn't ever have to traverse the tree to get them. +

+ +
+ +

Texture Splatting

+
+ +

+ +When you "slap" a texture on a mesh, the whole mesh looks the same. For big meshes (such as terrains) that is undesirable because it looks very boring (your whole landscape would be all rock, or all grass, or all sand). Texture Splatting is a technique that lets you "paint" several textures into one combined texure. Each of the splat textures has an opacity value so you can define where it is visible in the final overall texture. +

+ +

+The default material for TerraMonkey is TerrainLighting.j3md. This material combines several texture maps to produce the final custom texture. Remember, Diffuse Maps are the main textures that define the look; optionally, each Diffuse Map can be enhanced with a Normal Map; Alpha Maps describe the opacity of each Diffuse Map used (one color ???red, green, blue, or alpha??? stands for one Diffuse Map's opacity); Glow and Specular Maps define optional effects. +

+ +

+

We recommend to , and not do it manually. If you are simply curious about how the SDK's terrain texture plugin works, or if you indeed want to generate materials manually, then read on for the implementation details. +

+

+ +

+Here are the names of TerrainLighting.j3md's material properties: +

+ +

+ +

+ + +

+ +Note: DiffuseMap_0_scale is a float value (e.g. 1.0f); you must specify one scale per Diffuse Map. +

+ +

+Video cards support a maximum of 16 Splat textures total. This means you can only use a subset of material properties at the same time! +

+ +

+Adhere to the following constraints: + +

+ + +

+ +Here are some common examples what this means: + +

+ + +

+ +You can hand-paint Alpha, Diffuse, Glow, and Specular maps in a drawing program, like Photoshop. Define each splat texture in the Alpha Map in either Red, Green, Blue, or Alpha (=RGBA). The JmeTests project bundled in the SDK includes some image files that show you how this works. The example images show a terrain heightmap next to its Alpha Map (which has been prepare for 3 Diffuse Maps), and one examplary Diffuse/Normal Map pair. +

+ +
+ +

Code Sample: Terrain.j3md

+
+ +

+ +This example shows the simpler material definition Terrain.j3md, which only supports 1 Alpha Map, 3 Diffuse Maps, 3 Normal Maps, and does not support Phong illumination. It makes the exmaple shorter ??? TerrainLighting.j3md works accordingly (The list of material properties see above. Links to extended sample code see above.) +

+ +

+First, we load our textures and the heightmap texture for the terrain +

+
// Create material from Terrain Material Definition
+matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+// Load alpha map (for splat textures)
+matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+// load heightmap image (for the terrain heightmap)
+Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
+// load grass texture
+Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+grass.setWrap(WrapMode.Repeat);
+matRock.setTexture("Tex1", grass);
+matRock.setFloat("Tex1Scale", 64f);
+// load dirt texture
+Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
+dirt.setWrap(WrapMode.Repeat);
+matRock.setTexture("Tex2", dirt);
+matRock.setFloat("Tex2Scale", 32f);
+// load rock texture
+Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
+rock.setWrap(WrapMode.Repeat);
+matRock.setTexture("Tex3", rock);
+matRock.setFloat("Tex3Scale", 128f);
+ +

+We create the heightmap from the heightMapImage. +

+
AbstractHeightMap heightmap = null;
+heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 1f);
+heightmap.load();
+ +

+Next we create the actual terrain. +

+ +
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
+terrain.setMaterial(matRock);
+terrain.setLocalScale(2f, 1f, 2f); // scale to make it less steep
+List<Camera> cameras = new ArrayList<Camera>();
+cameras.add(getCamera());
+TerrainLodControl control = new TerrainLodControl(terrain, cameras);
+terrain.addControl(control);
+rootNode.attachChild(terrain);
+ +

+PS: As an alternative to an image-based height map, you can also generate a Hill hightmap: +

+
heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain_collision.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain_collision.html new file mode 100644 index 000000000..b714789af --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain_collision.html @@ -0,0 +1,324 @@ + +

Terrain Collision

+
+ +

+ +This tutorial expands the HelloTerrain tutorial and makes the terrain solid. You combine what you learned in Hello Terrain and Hello Collision and add a CollisionShape to the terrain. The terrain's CollisionShape lets the first-person player (who is also a CollisionShape) collide with the terrain, i.e. walk on it and stand on it. +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+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.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Node;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.ArrayList;
+import java.util.List;
+import jme3tools.converters.ImageToAwt;
+ 
+/**
+ * This demo shows a terrain with collision detection, 
+ * that you can walk around in with a first-person perspective.
+ * This code combines HelloCollision and HelloTerrain.
+ */
+public class HelloTerrainCollision 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) {
+    HelloTerrainCollision app = new HelloTerrainCollision();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    /** Set up Physics */
+    bulletAppState = new BulletAppState();
+    stateManager.attach(bulletAppState);
+    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ 
+    flyCam.setMoveSpeed(100);
+    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/splat/alphamap.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/Terrain/splat/dirt.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/road.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/splat/mountains512.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, -100, 0);
+    terrain.setLocalScale(2f, 1f, 2f);
+    rootNode.attachChild(terrain);
+ 
+    /** 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((Node) terrain);
+    landscape = new RigidBodyControl(terrainShape, 0);
+    terrain.addControl(landscape);
+ 
+    // 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(20);
+    player.setFallSpeed(30);
+    player.setGravity(30);
+    player.setPhysicsLocation(new Vector3f(-10, 10, 10));
+ 
+    // 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());
+  }
+}
+ +

+To try this code, create a New Project ??? JME3 ??? BasicGame using the default settings. Paste the sample code over the pregenerated Main.java class. Chnage the package to "mygame" if necessary. Open the Project Properties, Libraries, and add the jme3-test-data library to make certain you have all the files. +

+ +

+Compile and run the code. You should see a terrain. You can use the WASD keys and the mouse to run up and down the hills. +

+ +
+ +

Understanding the Code

+
+ +
+ +

The Terrain Code

+
+ +

+ +Read Hello Terrain for details of the following parts that we reuse: +

+
    +
  1. The AbstractHeightMap is an efficient way to describe the shape of the terrain.
    +
  2. +
  3. The Terrain.j3md-based Material and its texture layers let you colorize rocky mountain, grassy valleys, and a paved path criss-crossing over the landscape.
    +
  4. +
  5. The TerrainQuad is the finished terrain Spatial that you attach to the rootNode.
    +
  6. +
+ +
+ +

The Collision Detection Code

+
+ +

+Read Hello Collision for details of the following parts that we reuse: +

+
    +
  1. The BulletAppState lines activate physics.
    +
  2. +
  3. The ActionListener (onAction()) lets you reconfigure the input handling for the first-person player, so it takes collision detection into account.
    +
  4. +
  5. The custom setUpKeys() method loads your reconfigured input handlers. They now don't just walk blindly, but calculate the walkDirection vector that we need for collision detection.
    +
  6. +
  7. simpleUpdate() uses the walkDirection vector and makes the character walk, while taking obstacles and solid walls/floor into account.
    player.setWalkDirection(walkDirection);
    +
    +
  8. +
  9. The RigidBodyControl landscape is the CollisionShape of the terrain.
    +
  10. +
  11. The physical first-person player is a CapsuleCollisionShape with a CharacterControl.
    +
  12. +
+ +
+ +

Combining the Two

+
+ +

+ +Here are the changed parts to combine the two: + +

+
    +
  1. The CollisionShapeFactory creates the CollisionShape terrainShape for the terrain node.
    +
  2. +
  3. Out of the terrainShape, you create a static (zero-mass) RigidBodyControl landscape.
    +
  4. +
  5. Add the landscape control to the terrain to make it physical.
    +
  6. +
+
/** 6. Add physics: */ 
+    CollisionShape terrainShape =
+            CollisionShapeFactory.createMeshShape((Node) terrain);
+    landscape = new RigidBodyControl(terrainShape, 0);
+    terrain.addControl(landscape);  
+ +

+You attach the terrain and the first-person player to the rootNode, and to the physics space, to make them appear in the game world. +

+
    bulletAppState.getPhysicsSpace().add(terrain);
+    bulletAppState.getPhysicsSpace().add(player);
+ +
+ +

Conclusion

+
+ +

+ +You see that you can combine snippets of sample code (such as HelloTerrain and HelloCollision), and create a new application from it that combines two features into soemthing new. +

+ +

+You should spawn high up in the area and fall down to the map, giving you a few seconds to survey the area. Then walk around and see how you like the lay of the land. +

+
+ +

+See also: + +

+ +
+ terrain, + collision +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/toon-dino.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/toon-dino.png new file mode 100644 index 000000000..f4a14ce80 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/toon-dino.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/underwater2.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/underwater2.jpg new file mode 100644 index 000000000..01bb0b907 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/underwater2.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/update_loop.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/update_loop.html new file mode 100644 index 000000000..acc20a7da --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/update_loop.html @@ -0,0 +1,87 @@ + +

Main Update Loop

+
+ +

+ +Extending your application from com.jme3.app.SimpleApplication provides you with an update loop. This is where you implement your game logic (game mechanics). +

+ +

+Some usage examples: Here you remote-control NPCs (computer controlled characters), generate game events, and respond to user input. +

+ +

+To let you see the main update loop in context, understand that the SimpleApplication does the following: +

+ + +
+ +

Usage

+
+ +

+ +In a trivial SimpleApplication (such as a Hello World tutorial), all code is either in the simpleInitApp() (initialization) or simpleUpdate() (behaviour) method ??? or in a helper method/class that is called from one of these two. This trivial approach will make your main class very long, hard to read, and hard to maintain. You don't need to load the whole scene at once, and you don't need to run all conditionals tests all the time. +

+ +

+It's a best practice to modularize your game mechanics and spread out initialization and update loop code over several Java objects: +

+ +
+ basegame, + control, + input, + init, + keyinput, + loop, + states, + state +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/vehicles.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/vehicles.html new file mode 100644 index 000000000..391a85742 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/vehicles.html @@ -0,0 +1,341 @@ + +

Controlling a Physical Vehicle

+
+ +

+ +For physical vehicles, jME's uses the jBullet ray-cast vehicle. In this vehicle implementation, the physical chassis 'floats' along on four non-physical vertical rays. +

+ +

+Internally, each wheel casts a ray down, and using the ray's intersection point, jBullet calculates the suspension length, and the suspension force. The suspension force is applied to the chassis, keeping it from hitting the ground. The friction force is calculated for each wheel where the ray intersects with the ground. Friction is applied as a sideways and forwards force. 1) +

+ +

+This article shows how you use this vehicle implementation in a jME3 application. +

+ +

+ +

+ +
+ +

Sample Code

+
+ +

+ +Full code samples are here: + +

+ + +
+ +

Overview of this Physics Application

+
+ +

+ +The goal is to create a physical vehicle with wheels that can be steered and that interacts (collides with) with the floor and obstacles. + +

+
    +
  1. Create a SimpleApplication with a BulletAppState
    +
      +
    • This gives us a PhysicsSpace for PhysicsNodes
      +
    • +
    +
  2. +
  3. Create a VehicleControl + CompoundCollisionShape for the physical vehicle behaviour
    +
      +
    1. Set physical properties of the vehicle, such as suspension.
      +
    2. +
    +
  4. +
  5. Create a VehicleNode for the car model
    +
      +
    1. Create a box plus 4 cylinders as wheels (using vehicle.addWheel()).
      +
    2. +
    3. Add the VehicleControl behaviour to the VehicleNode geometry.
      +
    4. +
    +
  6. +
  7. Create a RigidBodyControl and CollisionShape for the floor
    +
  8. +
  9. Map key triggers and add input listeners
    +
      +
    • Navigational commands Left, Right, Foward, Brake.
      +
    • +
    +
  10. +
  11. Define the steering actions to be triggered by the key events.
    +
      +
    • vehicle.steer()
      +
    • +
    • vehicle.accelerate()
      +
    • +
    • vehicle.brake()
      +
    • +
    +
  12. +
+ +
+ +

Creating the Vehicle Chassis

+
+ +

+ +The vehicle that we create here in the example is just a "box on wheels", a basic vehicle shape that you can replace with a fancy car model, as demonstrated in . +

+ +

+Every physical object must have a collision shape, that we prepare first. For the vehicle, we choose a compound collision shape that is made up of a box-shaped body of the right size for the vehicle. We will add the wheels later. +

+
CompoundCollisionShape compoundShape = new CompoundCollisionShape();
+BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1.2f, 0.5f, 2.4f));
+ +

+Best Practice: We attach the BoxCollisionShape (the vehicle body) to the CompoundCollisionShape at a Vector of (0,1,0): This shifts the effective center of mass of the BoxCollisionShape downwards to 0,-1,0 and makes a moving vehicle more stable! +

+
compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
+ +

+Any kind of geometry can make up the visible part of the vehicle, here we use a wireframe box. We create a node that we use to group the geometry. +

+
Node vehicleNode=new Node("vehicleNode");
+vehicle = new VehicleControl(compoundShape, 400);
+vehicleNode.addControl(vehicle);
+ +

+We initialize the Vehicle Control with the compound shape, and set its mass to a heavy value, 400f. The Vehicle Control represents the car's physical behaviour. +

+
vehicle = new VehicleControl(compoundShape, 400);
+ +

+Finally we add the behaviour (VehicleControl) to the visible Geometry (node). +

+
vehicleNode.addControl(vehicle);
+ +

+We configure the physical properties of the vehicle's suspension: Compresion, Damping, Stiffness, and MaxSuspenionForce. Picking workable values for the wheel suspension can be tricky ??? for background info have a look at these . For now, let's work with the following values: +

+
float stiffness = 60.0f;//200=f1 car
+float compValue = .3f; //(should be lower than damp)
+float dampValue = .4f;
+vehicle.setSuspensionCompression(compValue * 2.0f * FastMath.sqrt(stiffness));
+vehicle.setSuspensionDamping(dampValue * 2.0f * FastMath.sqrt(stiffness));
+vehicle.setSuspensionStiffness(stiffness);
+vehicle.setMaxSuspensionForce(10000.0f);
+ +

+We now have a node vehicleNode with a visible "car" geometry, which acts like a vehicle. One thing that's missing are wheels. +

+ +
+ +

Adding the Wheels

+
+ +

+ +We create four wheel Geometries and add them to the vehicle. Our wheel geometries are simple, non-physical discs (flat Cylinders), they are just visual decorations. Note that the physical wheel behaviour (the com.jme3.bullet.objects.VehicleWheel objects) is created internally by the vehicle.addWheel() method. +

+ +

+The addWheel() method sets following properties: +

+ + +

+ +We initialize a few variables that we will reuse when we add the four wheels. yOff, etc, are the particular wheel offsets for our small vehicle model. +

+
Vector3f wheelDirection = new Vector3f(0, -1, 0);
+Vector3f wheelAxle = new Vector3f(-1, 0, 0);
+float radius = 0.5f;
+float restLength = 0.3f;
+float yOff = 0.5f;
+float xOff = 1f;
+float zOff = 2f;
+ +

+We create a Cylinder mesh shape that we use to create the four visible wheel geometries. +

+
Cylinder wheelMesh = new Cylinder(16, 16, radius, radius * 0.6f, true);
+ +

+For each wheel, we create a Node and a Geometry. We attach the Cylinder Geometry to the Node. We rotate the wheel by 90?? around the Y axis. We set a material to make it visible. Finally we add the wheel (plus its properties) to the vehicle. +

+
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);
+ +

+The three next wheels are created in the same fashion, only the offsets are different. Remember to set the Boolean parameter correctly to indicate whether it's a front wheel. +

+
...
+vehicle.addWheel(node2, new Vector3f(xOff, yOff, zOff),
+  wheelDirection, wheelAxle, restLength, radius, true);
+...
+vehicle.addWheel(node3, new Vector3f(-xOff, yOff, -zOff),
+  wheelDirection, wheelAxle, restLength, radius, false);
+...
+vehicle.addWheel(node4, new Vector3f(xOff, yOff, -zOff),
+  wheelDirection, wheelAxle, restLength, radius, false);
+ +

+Attach the wheel Nodes to the vehicle Node to group them, so they move together. +

+
vehicleNode.attachChild(node1);
+vehicleNode.attachChild(node2);
+vehicleNode.attachChild(node3);
+vehicleNode.attachChild(node4);
+ +

+As always, attach the vehicle Node to the rootNode to make it visible, and add the Vehicle Control to the PhysicsSpace to make the car physical. +

+
rootNode.attachChild(vehicleNode);
+getPhysicsSpace().add(vehicle);
+ +

+Not shown here is that we also created a Material mat. +

+ +
+ +

Steering the Vehicle

+
+ +

+ +Not shown here is the standard way how we map the input keys to actions (see full code sample). Also refer to Input Handling). +

+ +

+In the ActionListener, we implement the actions that control the vehicle's direction and speed. For the four directions (accelerate=up, brake=down, left, right), we specify how we want the vehicle to move. + +

+ +
public void onAction(String binding, boolean value, float tpf) {
+  if (binding.equals("Lefts")) {
+      if (value) { steeringValue += .5f; } else { steeringValue += -.5f; }
+      vehicle.steer(steeringValue);
+  } else if (binding.equals("Rights")) {
+      if (value) { steeringValue += -.5f; } else { steeringValue += .5f; }
+      vehicle.steer(steeringValue);
+  } else if (binding.equals("Ups")) {
+      if (value) {
+        accelerationValue += accelerationForce;
+      } else {
+        accelerationValue -= accelerationForce;
+      }
+      vehicle.accelerate(accelerationValue);
+  } else if (binding.equals("Downs")) {
+      if (value) { vehicle.brake(brakeForce); } else { vehicle.brake(0f); }
+  } 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 {
+    }
+  }
+}
+ +

+For your reference, this is how we initialized the constants for this example: +

+
private final float accelerationForce = 1000.0f;
+private final float brakeForce = 100.0f;
+private float steeringValue = 0;
+private float accelerationValue = 0;
+private Vector3f jumpForce = new Vector3f(0, 3000, 0);
+ +

+Remember, the standard input listener code that maps the actions to keys can be found in the code samples. +

+ +
+ +

Detecting Collisions

+
+ +

+ +Read the Responding to a PhysicsCollisionEvent chapter in the general physics documentation on how to detect collisions. You would do this if you want to react to collisions with custom events, such as adding points or substracting health. +

+ +
+ +

Best Practices

+
+ +

+ +This example shows a very simple but functional vehicle. For a game you would implement steering behaviour and acceleration with values that are typical for the type of vehicle that you want to simulate. Instead of a box, you load a chassis model. You can consider using an AnalogListener to respond to key events in a more sophisticated way. +

+ +

+For a more advanced example, look at . +

+
+ documentation, + physics, + vehicle, + collision +
+ +
+
+
1) +
+
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html new file mode 100644 index 000000000..25d3269b8 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html @@ -0,0 +1,429 @@ + +

Walking Character

+
+ +

+ +In the Hello Collision tutorial and the code sample you have seen how to create collidable landscapes and walk around in a first-person perspective. The first-person camera is enclosed by a collision shape and is steered by a CharacterControl. +

+ +

+Other games however require a third-person perspective of the character: In these cases you use a CharacterControl on a Spatial. This example also shows how to set up custom navigation controls, so you can press WASD to make the third-person character walk; and how to implement dragging the mouse to rotate. +

+ +
+ +

Sample Code

+
+ +

+ +Several related code samples can be found here: +

+ + +

+ +The code in this tutorial is a combination of these samples. + +

+ +
+ +

CharacterControl

+
+ +

+ +When you load a character model, give it a RigidBodyControl, and use forces to push it around, you do not get the desired behaviour: RigidBodyControl'ed objects (such as cubes and spheres) roll or tip over when pushed by physical forces. This is not the behaviour that you expect of a walking character. JME3's BulletPhysics integration offers a special CharacterControl with a setWalkDirection() method. You use it to create simple characters that treat floors and walls as solid, and always stays locked upright while moving. +

+ +

+This code sample creates a simple upright Character: +

+
// Load any model
+Node myCharacter = (Node) assetManager.loadModel("Models/myCharacterModel.mesh.xml");
+rootNode.attachChild(myCharacter);
+// Create a appropriate physical shape for it
+CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
+CharacterControl myCharacter_phys = new CharacterControl(capsuleShape, 0.01f);
+// Attach physical properties to model and PhysicsSpace
+myCharacter.addControl(myCharacter_phys);
+bulletAppState.getPhysicsSpace().add(myCharacter_phys);
+ +

+

The BulletPhysics CharacterControl only collides with "real" PhysicsControls (RigidBody). It does not detect collisions with other CharacterControls! If you need additional collision checks, add GhostControls to your characters and create a custom collision listener to respond. (The JME3 team may implement a better CharacterControl one day.) +

+

+ +

+A CharacterControl is a special kinematic object with restricted movement. CharacterControls have a fixed "upward" axis, this means they do not topple over when walking over an obstacle (as opposed to RigidBodyControls), which simulates a being's ability to balance upright. A CharacterControl can jump and fall along its upward axis, and it can scale steps of a certain height/steepness. + +

+
+ + + + + + + + + + + + + + + + + + + + + +
CharacterControl Method Property
setUpAxis(1) Fixed upward axis. Values: 0 = X axis , 1 = Y axis , 2 = Z axis.
+Default: 1, because for characters and vehicles, up is typically along the Y axis.
setJumpSpeed(10f) Jump speed (movement along upward-axis)
setFallSpeed(20f) Fall speed (movement opposite to upward-axis)
setMaxSlope(1.5f) How steep the slopes and steps are that the character can climb without considering them an obstacle. Higher obstacles need to be jumped. Vertical height in world units.
setGravity(1f) The intensity of gravity for this CharacterControl'ed entity. Tip: To change the direction of gravity for a character, modify the up axis.
setWalkDirection(new Vector3f(0f,0f,0.1f)) (CharacterControl only) Make a physical character walk continuously while checking for floors and walls as solid obstacles. This should probably be called "setPositionIncrementPerSimulatorStep". This argument is neither a direction nor a velocity, but the amount to increment the position each physics tick: vector length = accuracy*speed in m/s.
+Use setWalkDirection(Vector3f.ZERO) to stop a directional motion.
+ +

+ +For best practices on how to use setWalkDirection(), see the Navigation Inputs example below. +

+ +
+ +

Walking Character Demo

+
+ +
+ +

Code Skeleton

+
+
public class WalkingCharacterDemo extends SimpleApplication
+        implements ActionListener, AnimEventListener {
+ 
+  public static void main(String[] args) {
+    WalkingCharacterDemo app = new WalkingCharacterDemo();
+    app.start();
+  }
+ 
+  public void simpleInitApp() { }
+ 
+  public void simpleUpdate(float tpf) { }
+ 
+  public void onAction(String name, boolean isPressed, float tpf) { }
+ 
+  public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) { }
+ 
+  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { }
+ +
+ +

Overview

+
+ +

+ +To create a walking character: +

+
    +
  1. (Unless you already have it) Activate physics in the scene by adding a BulletAppState.
    +
  2. +
  3. Init the scene by loading the game level model (terrain or floor/buildings), and giving the scene a MeshCollisionShape.
    +
  4. +
  5. Create the animated character:
    +
      +
    1. Load an animated character model.
      +
    2. +
    3. Add a CharacterControl to the model.
      +
    4. +
    +
  6. +
  7. Set up animation channel and controllers.
    +
  8. +
  9. Add a ChaseCam or CameraNode.
    +
  10. +
  11. Handle navigational inputs.
    +
  12. +
+ +
+ +

Activate Physics

+
+
private BulletAppState bulletAppState;
+...
+public void simpleInitApp() {
+    bulletAppState = new BulletAppState();
+    //bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
+    stateManager.attach(bulletAppState);
+    ...
+}
+ +
+ +

Initialize the Scene

+
+ +

+ +In the simpleInitApp() method you initialize the scene and give it a MeshCollisionShape. The sample in the jme3 sources uses a custom helper class that simply creates a flat floor and drops some cubes and spheres on it: +

+
public void simpleInitApp() {
+  ...
+  PhysicsTestHelper.createPhysicsTestWorld(rootNode,
+      assetManager, bulletAppState.getPhysicsSpace());
+  ...
+ +

+In a real game, you would load a scene model here instead of a test world. You can load a model from a local or remote zip file, and scale and position it: +

+
private Node gameLevel;
+..
+public void simpleInitApp() {
+  ...
+  //assetManager.registerLocator("quake3level.zip", ZipLocator.class);
+  assetManager.registerLocator(
+  "http://jmonkeyengine.googlecode.com/files/quake3level.zip",
+    HttpZipLocator.class);
+  MaterialList matList = (MaterialList) assetManager.loadAsset("Scene.material");
+  OgreMeshKey key = new OgreMeshKey("main.meshxml", matList);
+  gameLevel = (Node) assetManager.loadAsset(key);
+  gameLevel.setLocalTranslation(-20, -16, 20);
+  gameLevel.setLocalScale(0.10f);
+  gameLevel.addControl(new RigidBodyControl(0));
+  rootNode.attachChild(gameLevel);
+  bulletAppState.getPhysicsSpace().addAll(gameLevel);
+  ...
+ +

+Also, add a light source to be able to see the scene. +

+
  AmbientLight light = new AmbientLight();
+  light.setColor(ColorRGBA.White.mult(2));
+  rootNode.addLight(light);
+ +
+ +

Create the Animated Character

+
+ +

+ +You create an animated model, such as Oto.mesh.xml. +

+
    +
  1. Place the "Oto" model into the assets/Models/Oto/ directory of your project.
    +
  2. +
  3. Create the CollisionShape and adjust the capsule radius and height to fit your character model.
    +
  4. +
  5. Create the CharacterControl and adjust the stepheight (here 0.05f) to the height that the character can climb up without jumping.
    +
  6. +
  7. Load the visible model. Make sure its start position does not overlap with scene objects.
    +
  8. +
  9. Add the CharacterControl to the model and register it to the physicsSpace.
    +
  10. +
  11. Attach the visible model to the rootNode.
    +
  12. +
+
private CharacterControl character;
+private Node model;
+...
+public void simpleInitApp() {
+  ...
+  CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
+  character = new CharacterControl(capsule, 0.05f);
+  character.setJumpSpeed(20f);
+  model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+  model.addControl(character);
+  bulletAppState.getPhysicsSpace().add(character);
+  rootNode.attachChild(model);
+  ...
+ +

+

Did you know? A CapsuleCollisionShape is a cylinder with rounded top and bottom. A capsule rotated upright is a good collision shape for a humanoid character since its roundedness reduces the risk of getting stuck on obstacles. +

+

+ +
+ +

Set Up AnimControl and AnimChannels

+
+ +

+ +Create several AnimChannels, one for each animation that can happen simultaneously. In this example, you create one channel for walking and one for attacking. (Because the character can attack with its arms and walk with the rest of the body at the same time.) +

+
private AnimChannel animationChannel;
+private AnimChannel attackChannel;
+private AnimControl animationControl;
+...
+public void simpleInitApp() {
+  ...
+  animationControl = model.getControl(AnimControl.class);
+  animationControl.addListener(this);
+  animationChannel = animationControl.createChannel();
+  attackChannel = animationControl.createChannel();
+  attackChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));
+  attackChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));
+  attackChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));
+  ...
+ +

+The attackChannel only controls one arm, while the walking channels controls the whole character. +

+ +
+ +

Add ChaseCam / CameraNode

+
+
private ChaseCamera chaseCam;
+ 
+...
+ 
+public void simpleInitApp() {
+  ...
+  flyCam.setEnabled(false);
+  chaseCam = new ChaseCamera(cam, model, inputManager);
+  ...
+ +
+ +

Handle Navigation

+
+ +

+ +Configure custom key bindings for WASD keys that you will use to make the character walk. Then calculate the vector where the user wants the character to move. Note the use of the special setWalkDirection() method below. +

+
// track directional input, so we can walk left-forward etc
+private boolean left = false, right = false, up = false, down = false;
+...
+ 
+public void simpleInitApp() {
+  ...
+  // configure mappings, e.g. the WASD keys
+  inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
+  inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
+  inputManager.addMapping("CharForward", new KeyTrigger(KeyInput.KEY_W));
+  inputManager.addMapping("CharBackward", new KeyTrigger(KeyInput.KEY_S));
+  inputManager.addMapping("CharJump", new KeyTrigger(KeyInput.KEY_RETURN));
+  inputManager.addMapping("CharAttack", new KeyTrigger(KeyInput.KEY_SPACE));
+  inputManager.addListener(this, "CharLeft", "CharRight");
+  inputManager.addListener(this, "CharForward", "CharBackward");
+  inputManager.addListener(this, "CharJump", "CharAttack");
+  ...
+}
+ +

+Respond to the key bindings by setting variables that track in which direction you will go. This allows us to steer the character forwards and to the left at the same time. Note that no actual walking happens here yet! We just track the input. +

+
@Override
+public void onAction(String binding, boolean value, float tpf) {
+  if (binding.equals("CharLeft")) {
+      if (value) left = true;
+      else left = false;
+  } else if (binding.equals("CharRight")) {
+      if (value) right = true;
+      else right = false;
+  } else if (binding.equals("CharForward")) {
+      if (value) up = true;
+      else up = false;
+  } else if (binding.equals("CharBackward")) {
+      if (value) down = true;
+      else down = false;
+  } else if (binding.equals("CharJump"))
+      character.jump();
+  if (binding.equals("CharAttack"))
+    attack();
+}
+ +

+The player can attack and walk at the same time. Attack() is a custom method that triggers an attack animation in the arms. Here you should also add custom code to play an effect and sound, and to determine whether the hit was successful. +

+
private void attack() {
+    attackChannel.setAnim("Dodge", 0.1f);
+    attackChannel.setLoopMode(LoopMode.DontLoop);
+}
+ +

+Finally, the update loop looks at the directional variables and moves the character accordingly. Since this is a special kinematic CharacterControl, we use the setWalkDirection() method. +

+ +

+The variable airTime tracks how long the character is off the ground (e.g. when jumping or falling) and adjusts the walk and stand animations acccordingly. +

+
private Vector3f walkDirection = new Vector3f(0,0,0); // stop
+ 
+private float airTime = 0;
+ 
+public void simpleUpdate(float tpf) {
+  Vector3f camDir = cam.getDirection().clone().multLocal(0.25f);
+  Vector3f camLeft = cam.getLeft().clone().multLocal(0.25f);
+  camDir.y = 0;
+  camLeft.y = 0;
+  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());
+ 
+  if (!character.onGround()) {
+      airTime = airTime + tpf;
+  } else {
+      airTime = 0;
+  }
+ 
+  if (walkDirection.length() == 0) {
+      if (!"stand".equals(animationChannel.getAnimationName())) {
+        animationChannel.setAnim("stand", 1f);
+      }
+  } else {
+      character.setViewDirection(walkDirection);
+      if (airTime > .3f) {
+        if (!"stand".equals(animationChannel.getAnimationName())) {
+          animationChannel.setAnim("stand");
+        }
+      } else if (!"Walk".equals(animationChannel.getAnimationName())) {
+        animationChannel.setAnim("Walk", 0.7f);
+      }
+    }
+  character.setWalkDirection(walkDirection); // THIS IS WHERE THE WALKING HAPPENS
+}
+ +

+This method resets the walk animation. +

+
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+        if (channel == attackChannel) channel.setAnim("stand");
+}
+ 
+public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { }
+
+ documentation, + physics, + input, + animation, + character, + NPC, + collision +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post-muddy.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post-muddy.png new file mode 100644 index 000000000..cf85799ca Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post-muddy.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post.png new file mode 100644 index 000000000..21c7d8710 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-post.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-reflection-muddy.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-reflection-muddy.png new file mode 100644 index 000000000..29d04ff4c Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water-reflection-muddy.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.html new file mode 100644 index 000000000..6d0ab2da5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.html @@ -0,0 +1,217 @@ + +

Simple Water

+
+ +

+ +jMonkeyEngine offers a SimpleWaterProcessor that turns any quad (flat rectangle) into a reflective water surface with waves. You can use this quad for simple limited water surfaces such as water troughs, shallow fountains, puddles, shallow water in channels. The SimpleWaterProcessor has less performance impact on your game than the full featured SeaMonkey WaterFilter; the main difference is that the SimpleWaterProcessor does not include under-water effects. +

+ +

+Here is some background info for JME3's basic water implementation: +

+ + +

+ + +

+ +
+ +

SimpleWaterProcessor

+
+ +

+ +A JME3 scene with water can use a com.jme3.water.SimpleWaterProcessor (which implements the SceneProcessor interface). +

+ +

+To achieve a water effect, JME3 uses shaders and a special material, Common/MatDefs/Water/SimpleWater.j3md. The water surface is a quad, and we use normal map and dU/dV map texturing to simulate the waves. + +

+
    +
  1. Every frame, we render to three texture maps:
    +
      +
    • For the water surface (reflection), we take a snapshot of the environment, flip it upside down, and clip it to the visible water surface. Note that we do not actually use a "water texture" color map: The "texture" of the water is solely a distorted reflection.
      +
    • +
    • For the "wavy" distortion (refraction), we use the derivative of a normal map, a dU/dV map.
      +
    • +
    • For the fogginess of water (depth) we use a depth map from the terrains z-buffer.
      +
    • +
    +
  2. +
  3. In the shaders, we add all of the texture maps together.
    +
      +
    • For the "bumpy" displacement of the waves, we use a normal map and a du/dv map that are shifted against each other over time to create the wave effect.
      +
    • +
    • For the light reflection vectors on the water surface, we use the Fresnel formula, together with normal vectors.
      +
    • +
    • We add specular lighting.
      +
    • +
    +
  4. +
  5. (For the underwater caustics effect, we use splatted textures. ??? WIP/TODO)
    +
  6. +
+ +
+ +

Usage

+
+ +

+ + + +

+
    +
  1. Create a mainScene Node
    +
      +
    1. Attach the mainScene Node to the rootNode
      +
    2. +
    +
  2. +
  3. Load your scene Spatial
    +
      +
    1. Add a light source to the scene Spatial
      +
    2. +
    3. Attach the scene Spatial to the mainScene Node
      +
    4. +
    +
  4. +
  5. Load your sky Geometry
    +
      +
    1. Attach the sky Geometry to the mainScene Node
      +
    2. +
    +
  6. +
  7. Create the SimpleWaterProcessor waterProcessor
    +
      +
    1. Set the processor's ReflectionScene to the mainScene Spatial (!)
      +
    2. +
    3. Set the processor's Plane to where you want your water surface to be
      +
    4. +
    5. Set the processor's WaterDepth, DistortionScale, and WaveSpeed
      +
    6. +
    7. Attach the processor to the viewPort
      +
    8. +
    +
  8. +
  9. Create a Quad quad
    +
      +
    1. Set the quad's TextureCoordinates to specify the size of the waves
      +
    2. +
    +
  10. +
  11. Create a water Geometry from the Quad
    +
      +
    1. Set the water's translation and rotation (same Y value as Plane above!)
      +
    2. +
    3. Set the water's material to the processor's output material
      +
    4. +
    5. Attach the water Geometry to the rootNode. (Not to the mainScene!)
      +
    6. +
    +
  12. +
+ +
+ +

Sample Code

+
+ +

+ +The sample code can be found in jme3/src/jme3test/water/TestSimpleWater.java and jme3/src/jme3test/water/TestSceneWater.java. +

+ +

+Here is the most important part of the code: +

+
// we create a water processor
+SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);
+waterProcessor.setReflectionScene(mainScene);
+ 
+// we set the water plane
+Vector3f waterLocation=new Vector3f(0,-6,0);
+waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y, waterLocation.dot(Vector3f.UNIT_Y)));
+viewPort.addProcessor(waterProcessor);
+ 
+// we set wave properties
+waterProcessor.setWaterDepth(40);         // 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(400,400);
+quad.scaleTextureCoordinates(new Vector2f(6f,6f));
+ 
+// 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(-200, -6, 250);
+water.setShadowMode(ShadowMode.Receive);
+water.setMaterial(waterProcessor.getMaterial());
+rootNode.attachChild(water);
+ +
+ +

Settings

+
+ +

+ +You can lower the render size to gain higher performance: + +

+
waterProcessor.setRenderSize(128,128);
+ +

+The deeper the water, the more transparent. (?) + +

+
waterProcessor.setWaterDepth(40);
+ +

+A higher distortion scale makes bigger waves. + +

+
waterProcessor.setDistortionScale(0.05f);
+ +

+A lower wave speed makes calmer water. + +

+
waterProcessor.setWaveSpeed(0.05f);
+ +

+If your scene does not have a lightsource, you can set the light direction for the water: + +

+
waterProcessor.setLightDirection( new Vector3f(0.55f, -0.82f, 0.15f));
+ +

+Instead of creating a quad and specifying a plane, you can get a default waterplane from the processor: + +

+
Geometry waterPlane = waterProcessor.createWaterGeometry(10, 10);
+waterPlane.setLocalTranslation(-5, 0, 5);
+waterPlane.setMaterial(waterProcessor.getMaterial());
+ +

+You can offer a switch to set the water Material to a static texture ??? for users with slow PCs. + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.png new file mode 100644 index 000000000..7824b26e3 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/water.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android.html new file mode 100644 index 000000000..bf26c4f43 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android.html @@ -0,0 +1,305 @@ + +

Android Support in the jMonkeyEngine

+
+ +

+ +This is a draft of a feature that is work in progress. If you have questions or suggestions, please leave a comment on the ! +

+ +
+ +

Requirements

+
+ +
+ +

Developer Requirements

+
+ + +
+ +

User Requirements

+
+ + +
+ +

Features

+
+ +
+ +

JMonkeyEngine3 Android Integration

+
+ + +
+ +

jMonkeyEngine SDK Android Integration

+
+ +

+ +Mobile deployment is a "one-click" option next to Desktop/WebStart/Applet deployment in the jMonkeyEngine SDK. +

+ + +
+ +

Beta Instructions

+
+
    +
  1. Make sure you have installed the Android Project Support into the jMonkeyEngine SDK.
    +
  2. +
  3. Open the jMonkeyEngine SDK Options>Mobile and enter the path to your Android SDK directory, and click OK. E.g. /opt/android-sdk
    +
  4. +
+ +
+ +

Activate Android Deployment

+
+
    +
  1. Open an existing JME3 project, or create a new JME3 project.
    +
  2. +
  3. Right-click the project node in the Projects Window and open the Properties.
    +
  4. +
  5. In the Application>Mobile Properties, enable Mobile Deployment, and select an Android target.
    +This creates a "mobile" folder in your projects directory. This folder contains a complete android project with correct settings to run the application using the AndroidHarness.
    +
  6. +
  7. (Restart the jMonkeyEngine)
    +
  8. +
  9. A Mobile Files node appears in the Project window.
    +It lets you edit the MainActivitity.java, the AndroidManifest.xml, and build.properties.
    +
  10. +
+ +

+ + +

+ +

+The Android deployment option creates a separate sub-project for android and makes the main project and associated libraries available to the sub-project as libraries. The sub-project can be edited using NBAndroid (see below) or using Eclipse or any other IDE that supports standard android projects. Normally you do not need to edit the android project files. Exceptions are described further below. The libraries are first added to the android sub-project when the main project is built for the first time. +

+ +
+ +

Build and Run

+
+ +

+ +Open your game project in the jMonkeyEngine SDK. +

+ +

+Building +

+
    +
  1. Right-click the project node to build your project.
    +An APK file is created in the "dist" folder.
    +
  2. +
+ +

+ +Running +

+
    +
  1. Right-click the project node and choose Set Main Project.
    +
  2. +
  3. Select the "Android Device" build configuration next to the Run toolbar button.
    +
  4. +
  5. Make sure "Compile on Save" is disabled in your project preferences.
    +
  6. +
  7. Run the application on a connected phone by right-clicking it and selecting "Run" or press the "Play" button in the toolbar.
    +
  8. +
  9. The application will run and output its log to the "Output" window of the SDK.
    +
  10. +
  11. When finished testing, click the "x" next to the run status in the lower right to quit the logging output.
    +
  12. +
+ +

+ +The default android run target uses the default device set in the Android configuration utility. If you set that to a phone you can run the application directly on your phone, the emulator does not support OpenGLES 2.0 and cannot run the engine. +

+ +

+Optionally, download +

+ +

+During build, the libraries and main jar file from the main project are copied to the android project libs folder for access. During this operation the desktop-specific libraries are replaced with android specific JARs. +

+ +

+Be aware that logging has been identified as having a significant performance hit in Android applications. If getting poor performance please try turning logging either down or off and retesting. +

+ +
+ +

Installing NBAndroid

+
+ +

+Activating the nbandroid plugin in the jMonkeyEngine SDK is optional, but recommended. You do not need the nbandroid plugin for Android support to work, however nbandroid will not interfere and will in fact allow you to edit the android source files and project more conveniently. To be able to edit, extend and code android-specific code in Android projects, install NBAndroid from the update center: +

+
    +
  1. Open Tools???Plugins???Settings
    +
  2. +
  3. Go to Tools???Plugins???Available Plugins.
    +
  4. +
  5. Install the NbAndroid plugin. (Will show up as Android)
    +
  6. +
+ +
+ +

Notes

+
+ + +
+ +

Android Considerations

+
+ +

+ +You can use the jMonkeyEngine SDK to save (theoretically) any jMonkeyEngine app as Android app. But the application has to be prepared for the fact that Android devices have a smaller screen resolution, touchscreens instead of mouse buttons, and (typically) no keyboards. + +

+ + +

+ +Best Practice: Ideally, you write the core application code in a way that it checks for the environment it's being run on, and automatically adapts the device's limitations by switching off effects, changing input mechanisms etc. Learn how to read graphic card capabilites here. +

+ +
+ +

Using Android specific functions

+
+ +

+As described above, you should always try to design your application as platform independent as possible. If your game becomes a sudden hit on android, why not release it on Facebook as an applet as well? When your application is designed in a platform independent way, you don't have to do much more but check a checkbox to deploy your application as an applet. But what if you want to for example access the camera of an android device? Inevitably you will have to use android specific api to access the camera. +

+ +

+Since the main project is not configured to access the android api directly, you have to install NBAndroid (see above) to be able to edit the created android project in the SDK. After installing, click the "open project" button and navigate to the "mobile" folder inside the main project folder (it should show up with an android "a" icon) and open it. +

+ +

+ +Although you will use android specific api, using a camera is not exactly android specific and so you should try to design this part of the application as platform independent as possible as well. As an example, if you want to use the phones camera as an image input stream for a texture, you can create e.g. the AppState that manages the image and makes it available to the application inside the main project (no android code is needed). Then in the android part of the code you make a connection to the camera and update the image in the AppState. This also allows you to easily support cameras on other platforms in the same way or fallback to something else in case the platform doesn't support a camera. +

+ +

+Note that you have to build the whole project once to make (new) classes in the main project available to the android part. +

+ +
+ +

More Info

+
+ +

+There be no proper guidanceof runing on androidf +The SDK will later provide tools to adapt the material and other graphics settings of the Android deployment version automatically. + +

+ +
+ documentation, + sdk, + android, + deployment, + tool +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android_access.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android_access.png new file mode 100644 index 000000000..9eb076142 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/android_access.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-animation.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-animation.png new file mode 100644 index 000000000..93b7a036b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-animation.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-assets-models.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-assets-models.png new file mode 100644 index 000000000..1770d777c Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-assets-models.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-effect-fire.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-effect-fire.png new file mode 100644 index 000000000..93fc10140 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-effect-fire.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png new file mode 100644 index 000000000..d18e81b68 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-physics.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-physics.png new file mode 100644 index 000000000..060b52495 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-physics.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-picking.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-picking.png new file mode 100644 index 000000000..3c2c6ebeb Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-picking.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-scene.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-scene.png new file mode 100644 index 000000000..fb02d63ab Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-scene.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-terrain.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-terrain.png new file mode 100644 index 000000000..5d6bb8ce9 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-terrain.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_animation.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_animation.html new file mode 100644 index 000000000..0fea73b32 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_animation.html @@ -0,0 +1,361 @@ + +

jMonkeyEngine 3 Tutorial (7) - Hello Animation

+
+ +

+ +Previous: Hello Material, +Next: Hello Picking +

+ +

+This tutorial shows how to add an animation controller and channels, and how to respond to user input by triggering an animation in a loaded model. +

+ +

+ +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+ +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.AnimEventListener;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+ 
+/** Sample 7 - how to load an OgreXML model and play an animation,
+ * using channels, a controller, and an AnimEventListener. */
+public class HelloAnimation extends SimpleApplication
+  implements AnimEventListener {
+  private AnimChannel channel;
+  private AnimControl control;
+  Node player;
+  public static void main(String[] args) {
+    HelloAnimation app = new HelloAnimation();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    viewPort.setBackgroundColor(ColorRGBA.LightGray);
+    initKeys();
+    DirectionalLight dl = new DirectionalLight();
+    dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
+    rootNode.addLight(dl);
+    player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+    player.setLocalScale(0.5f);
+    rootNode.attachChild(player);
+    control = player.getControl(AnimControl.class);
+    control.addListener(this);
+    channel = control.createChannel();
+    channel.setAnim("stand");
+  }
+ 
+  public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+    if (animName.equals("Walk")) {
+      channel.setAnim("stand", 0.50f);
+      channel.setLoopMode(LoopMode.DontLoop);
+      channel.setSpeed(1f);
+    }
+  }
+ 
+  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+    // unused
+  }
+ 
+  /** Custom Keybinding: Map named actions to inputs. */
+  private void initKeys() {
+    inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
+    inputManager.addListener(actionListener, "Walk");
+  }
+  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Walk") && !keyPressed) {
+        if (!channel.getAnimationName().equals("Walk")) {
+          channel.setAnim("Walk", 0.50f);
+          channel.setLoopMode(LoopMode.Loop);
+        }
+      }
+    }
+  };
+}
+ +
+ +

Creating and Loading Animated Models

+
+ +

+ +You create animated models with a tool such as Blender. Take some time and learn how to create your own models in these . For now, download and use a free model, such as the one included here as an example (, and ). +

+ +

+Loading an animated model is pretty straight-forward, just as you have learned in the previous chapters. Animated Ogre models come as a set of files: The model is in Oto.mesh.xml, and the animation details are in Oto.skeleton.xml, plus the usual files for materials and textures. Check that all files of the model are together in the same Model subdirectory. +

+
  public void simpleInitApp() {
+    /* Displaying the model requires a light source */
+    DirectionalLight dl = new DirectionalLight();
+    dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
+    rootNode.addLight(dl);
+    /* load and attach the model as usual */
+    player = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+    player.setLocalScale(0.5f); // resize
+    rootNode.attachChild(player);
+    ...
+    }
+ +

+Don't forget to add a light source to make the material visible. +

+ +
+ +

Animation Controler and Channel

+
+ +

+ +After you load the animated model, you register it to the Animation Controller. +

+ +
  private AnimChannel channel;
+  private AnimControl control;
+ 
+  public void simpleInitApp() {
+    ...
+    /* Load the animation controls, listen to animation events,
+     * create an animation channel, and bring the model in its default position.  */
+    control = player.getControl(AnimControl.class);
+    control.addListener(this);
+    channel = control.createChannel();
+    channel.setAnim("stand");
+    ...
+ +

+

In response to a question about animations on different channels interefering with each other, Nehon on the jME forum , +

+ +

+"You have to consider channels as part of the skeleton that are animated. The default behavior is to use the whole skeleton for a channel. +In your example the first channel plays the walk anim, then the second channel plays the dodge animation. +Arms and feet are probably not affected by the doge animation so you can see the walk anim for them, but the rest of the body plays the dodge animation. +

+ +

+Usually multiple channels are used to animate different part of the body. For example you create one channel for the lower part of the body and one for the upper part. This allow you to play a walk animation with the lower part and for example a shoot animation with the upper part. This way your character can walk while shooting. +

+ +

+In your case, where you want animations to chain for the whole skeleton, you just have to use one channel." +

+

+ +
+ +

Responding to Animation Events

+
+ +

+ +Add implements AnimEventListener to the class declaration. This interface gives you access to events that notify you when a sequence is done, or when you change from one sequence to another, so you can respond to it. In this example, you reset the character to a standing position after a Walk cycle is done. +

+
public class HelloAnimation extends SimpleApplication
+                         implements AnimEventListener {
+  ...
+ 
+  public void onAnimCycleDone(AnimControl control, 
+                              AnimChannel channel, String animName) {
+    if (animName.equals("Walk")) {
+      channel.setAnim("stand", 0.50f);
+      channel.setLoopMode(LoopMode.DontLoop);
+      channel.setSpeed(1f);
+    }
+  }
+  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+    // unused
+  }
+ +
+ +

Trigger Animations After User Input

+
+ +

+ +There are ambient animations like animals or trees that you may want to trigger in the main event loop. In other cases, animations are triggered by user interaction, such as key input. You want to play the Walk animation when the player presses a certain key (here the spacebar), at the same time as the avatar performs the walk action and changes its location. +

+
    +
  1. Initialize a new input controller (in simpleInitApp()).
    +
      +
    • Write the initKey() convenience method and call it from simpleInitApp().
      +
    • +
    +
  2. +
  3. Add a key mapping with the name as the action you want to trigger.
    +
      +
    • Here for example, you map Walk to the Spacebar key.
      +
    • +
    +
  4. +
  5. Add an input listener for the Walk action.
    +
  6. +
+
  private void initKeys() {
+    inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
+    inputManager.addListener(actionListener, "Walk");
+  }
+ +

+To use the input controller, you need to implement the actionLister: +Test for each action by name, and set the channel to the corresponding animation to run. + +

+ +
  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+        if (name.equals("Walk") && !keyPressed) {
+            if (!channel.getAnimationName().equals("Walk")){
+                channel.setAnim("Walk", 0.50f);
+                channel.setLoopMode(LoopMode.Cycle);
+            }
+        }
+    }
+  };
+ +
+ +

Exercises

+
+ +
+ +

Exercise 1: Two Animations

+
+ +

+ +Make a mouse click trigger another animation sequence! +

+
    +
  1. Create a second channel in the controller
    +
  2. +
  3. Create a new key trigger mapping and action (see: Hello Input)
    +
  4. +
  5. Tip: Do you want to find out what animation sequences are available in the model? Use:
    for (String anim : control.getAnimationNames()) { System.out.println(anim); }
    +
    +
  6. +
+ +
+ +

Exercise 2: Revealing the Skeleton (1)

+
+ +

+ +Open the skeleton.xml file in a text editor of your choice. You don't have to be able to read or write these xml files (Blender does that for you) ??? but it is good to know how skeletons work. "There's no magic to it!" +

+ + +
+ +

Exercise 3: Revealing the Skeleton (2)

+
+ +

+ +Add the following import statements for the SkeletonDebugger and Material classes: +

+
     import com.jme3.scene.debug.SkeletonDebugger;
+     import com.jme3.material.Material;
+ +

+Add the following code snippet to simpleInitApp() to make the bones (that you just read about) visible! +

+
     SkeletonDebugger skeletonDebug = 
+         new SkeletonDebugger("skeleton", control.getSkeleton());
+     Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+     mat.setColor("Color", ColorRGBA.Green);
+     mat.getAdditionalRenderState().setDepthTest(false);
+     skeletonDebug.setMaterial(mat);
+     player.attachChild(skeletonDebug);
+ +

+Can you identify individual bones in the skeleton? +

+ +
+ +

Conclusion

+
+ +

+ +Now you can load animated models, identify stored animations, and trigger animations by using onAnimCycleDone() and onAnimChange(). You also learned that you can play several animations simultaneously, by starting each in a channel of its own. This could be useful if you ever want to animate the lower and upper part of the characters body independently, for example the legs run, while the arms use a weapon. +

+ +

+Now that your character can walk, wouldn't it be cool if it could also pick up things, or aim a weapon at things, or open doors? Time to reveal the secrets of mouse picking! + +

+
+ +

+See also: +

+
+ beginner, + intro, + animation, + documentation, + keyinput, + input, + node, + model +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html new file mode 100644 index 000000000..db43825c0 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html @@ -0,0 +1,501 @@ + +

jMonkeyEngine 3 Tutorial (3) - Hello Assets

+
+ +

+ +Previous: Hello Node, +Next: Hello Update Loop +

+ +

+In this tutorial we will learn to load 3D models and text into the scene graph, using the jME Asset Manager. You will also learn how to determine the correct paths, and which file formats to use. +

+ +

+ +

+ +

+

Trouble finding the files to run this sample? To get the assets (3D models) used in this example, add the included jme3-test-data.jar to your classpath. In project created with the jMonkeyEngine SDK (recommended), simply right-click your project, choose "Properties", go to "Libraries", press "Add Library" and add the preconfigured "jme3-test-data" library. +

+

+ +
+ +

Code Sample

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+ 
+/** Sample 3 - how to load an OBJ model, and OgreXML model, 
+ * a material/texture, or text. */
+public class HelloAssets extends SimpleApplication {
+ 
+    public static void main(String[] args) {
+        HelloAssets app = new HelloAssets();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+ 
+        Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.obj");
+        Material mat_default = new Material( 
+            assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+        teapot.setMaterial(mat_default);
+        rootNode.attachChild(teapot);
+ 
+        // Create a wall with a simple texture from test_data
+        Box box = new Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
+        Spatial wall = new Geometry("Box", box );
+        Material mat_brick = new Material( 
+            assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat_brick.setTexture("ColorMap", 
+            assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
+        wall.setMaterial(mat_brick);
+        wall.setLocalTranslation(2.0f,-2.5f,0.0f);
+        rootNode.attachChild(wall);
+ 
+        // Display a line of text with a default font
+        guiNode.detachAllChildren();
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        BitmapText helloText = new BitmapText(guiFont, false);
+        helloText.setSize(guiFont.getCharSet().getRenderedSize());
+        helloText.setText("Hello World");
+        helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
+        guiNode.attachChild(helloText);
+ 
+        // Load a model from test_data (OgreXML + material + texture)
+        Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+        ninja.scale(0.05f, 0.05f, 0.05f);
+        ninja.rotate(0.0f, -3.0f, 0.0f);
+        ninja.setLocalTranslation(0.0f, -5.0f, -2.0f);
+        rootNode.attachChild(ninja);
+        // You must add a light to make the model visible
+        DirectionalLight sun = new DirectionalLight();
+        sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
+        rootNode.addLight(sun);
+ 
+    }
+}
+ +

+Build and run the code sample. You should see a green Ninja with a colorful teapot standing behind a wall. The text on the screen should say "Hello World". +

+ +
+ +

The Asset Manager

+
+ +

+ +By game assets we mean all multi-media files, such as models, materials, textures, whole scenes, custom shaders, music and sound files, and custom fonts. JME3 comes with a handy AssetManager object that helps you access your assets. +The AssetManager can load files from: +

+ + +

+ +The following is the recommended directory structure for storing assets in your project directoy: +

+
MyGame/assets/Interface/
+MyGame/assets/MatDefs/
+MyGame/assets/Materials/
+MyGame/assets/Models/
+MyGame/assets/Scenes/
+MyGame/assets/Shaders/
+MyGame/assets/Sounds/
+MyGame/assets/Textures/
+MyGame/build.xml            <-- Ant build script
+MyGame/src/...              <-- Java sources go here
+MyGame/...
+ +

+This is just a suggested best practice, and it's what you get by default when creating a new Java project in the jMokeyEngine SDK. You can create an assets directory and technically name the subdirectories whatever you like. +

+ +
+ +

Loading Textures

+
+ +

+ +Place your textures in a subdirectory of assets/Textures/. Load the texture into the material before you set the Material. The following code sample is from the simpleInitApp() method and loads a simple wall model: +

+
// Create a wall with a simple texture from test_data
+Box box = new Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
+Spatial wall = new Geometry("Box", box );
+Material mat_brick = new Material( 
+    assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+mat_brick.setTexture("ColorMap", 
+    assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"));
+wall.setMaterial(mat_brick);
+wall.setLocalTranslation(2.0f,-2.5f,0.0f);
+rootNode.attachChild(wall);
+ +

+In this case, you create your own Material and apply it to a Geometry. You base Materials on default material descriptions (such as "Unshaded.j3md"), as shown in this example. +

+ +
+ +

Loading Text and Fonts

+
+ +

+ +This example displays the text "Hello World" in the default font at the bottom edge of the window. You attach text to the guiNode ??? this is a special node for flat (orthogonal) display elements. You display text to show the game score, player health, etc. +The following code sample goes into the simpleInitApp() method. +

+
// Display a line of text with a default font
+guiNode.detachAllChildren();
+guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+BitmapText helloText = new BitmapText(guiFont, false);
+helloText.setSize(guiFont.getCharSet().getRenderedSize());
+helloText.setText("Hello World");
+helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
+guiNode.attachChild(helloText);
+ +

+Tip: Clear existing text in the guiNode by detaching all its children. +

+ +
+ +

Loading a Model

+
+ +

+ +Export your 3D model in OgreXML format (.mesh.xml, .scene, .material, .skeleton.xml) and place it in a subdirectory of assets/Models/. The following code sample goes into the simpleInitApp() method. +

+
// Load a model from test_data (OgreXML + material + texture)
+Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
+ninja.scale(0.05f, 0.05f, 0.05f);
+ninja.rotate(0.0f, -3.0f, 0.0f);
+ninja.setLocalTranslation(0.0f, -5.0f, -2.0f);
+rootNode.attachChild(ninja);
+// You must add a directional light to make the model visible!
+DirectionalLight sun = new DirectionalLight();
+sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f).normalizeLocal());
+rootNode.addLight(sun);
+ +

+Note that you do not need to create a Material if you exported the model with a material. Remember to add a light source, as shown, otherwise the material (and the whole model) is not visible! +

+ +
+ +

Loading Assets From Custom Paths

+
+ +

+ +What if your game relies on user supplied model files, that are not included in the distribution? If a file is not located in the default location (e.g. assets directory), you can register a custom Locator and load it from any path. +

+ +

+Here is a usage example of a ZipLocator that is registered to a file town.zip in the top level of your project directory: +

+
    assetManager.registerLocator("town.zip", ZipLocator.class);
+    Spatial scene = assetManager.loadModel("main.scene");
+    rootNode.attachChild(scene);
+ +

+Here is a HttpZipLocator that can download zipped models and load them: +

+
    assetManager.registerLocator(
+      "http://jmonkeyengine.googlecode.com/files/wildhouse.zip", 
+      HttpZipLocator.class);
+    Spatial scene = assetManager.loadModel("main.scene");
+    rootNode.attachChild(scene);
+ +

+JME3 offers ClasspathLocator, ZipLocator, FileLocator, HttpZipLocator, and UrlLocator (see com.jme3.asset.plugins). +

+ +
+ +

Creating Models and Scenes

+
+ +

+ +To create 3D models and scenes, you need a 3D Mesh Editor with an OgreXML Exporter plugin. For example, you can . +You use the SDK to load models, convert models and create scenes from them. +

+ +

+If you use Blender, export your models as Ogre XML meshes with materials as follows: +

+
    +
  1. Open the menu File > Export > OgreXML Exporter to open the exporter dialog.
    +
  2. +
  3. In the Export Materials field: Give the material the same name as the model. For example, the model something.mesh.xml goes with something.material, plus (optionally) something.skeleton.xml and some JPG texture files.
    +
  4. +
  5. In the Export Meshes field: Select a subdirectory of your assets/Models/ directory. E.g. assets/Models/something/.
    +
  6. +
  7. Activate the following exporter settings:
    +
      +
    • Copy Textures: YES
      +
    • +
    • Rendering Materials: YES
      +
    • +
    • Flip Axis: YES
      +
    • +
    • Require Materials: YES
      +
    • +
    • Skeleton name follows mesh: YES
      +
    • +
    +
  8. +
  9. Click export.
    +
  10. +
+ +
+ +

Model File Formats

+
+ +

+ +JME3 can load Ogre XML models + materials, Ogre DotScenes, as well as Wavefront OBJ+MTL models. The loadModel() code works with these files when you run the code directly from the jMonkeyEngine SDK. +

+ +

+If you build the executables using the default build script, then the original model files (XML, OBJ, etc) are not included. When you run the executable, you get an error message if you try to load any models directly: +

+
com.jme3.asset.DesktopAssetManager loadAsset
+WARNING: Cannot locate resource: Models/Ninja/Ninja.mesh.xml
+com.jme3.app.Application handleError
+SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
+java.lang.NullPointerException
+ +

+Loading the XML/OBJ files directly is only acceptable during the development phase. If your graphic designer pushes updated files to the asset directory, you can quickly review the latest version in your development environment. +

+ +

+For testing and for the final release build, you use .j3o files exclusively. J3o is an optimized binary format for jME3 applications, and .j3o files are automatically included in the distributable JAR file by the build script. When you do QA test builds or are ready to release, use the SDK to convert all .obj/.scene/.xml/.blend files to .j3o files, and only load the .j3o versions. +

+ +

+Open your JME3 Project in the jMonkeyEngine SDK. +

+
    +
  1. Right-click a .Blend, .OBJ, or .mesh.xml file in the Projects window, and choose "convert to JME3 binary".
    +
  2. +
  3. The .j3o file appears next to the .mesh.xml file and has the same name.
    +
  4. +
  5. Change all your loadModel() lines accordingly. For example:
    Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");
    +
    +
  6. +
+ +

+ +If your executable gets a runtime exception, make sure you have converted all models to .j3o! +

+ +
+ +

Loading Models and Scenes

+
+
+ + + + + + + + + + + + +
Task? Solution!
Load a model with materials Use the asset manager's loadModel() method and attach the Spatial to the rootNode.
Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.mesh.xml");
+rootNode.attachChild(elephant);
+
Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.j3o");
+rootNode.attachChild(elephant);
+
Load a model without materials If you have a model without materials, you have to give it a material to make it visible.
Spatial teapot = assetManager.loadModel("Models/Teapot/Teapot.j3o");
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md"); // default material
+teapot.setMaterial(mat);
+rootNode.attachChild(teapot);
+
Load a scene You load scenes just like you load models:
Spatial scene = assetManager.loadModel("Scenes/town/main.scene");
+rootNode.attachChild(scene);
+
Spatial scene = assetManager.loadModel("Scenes/town/main.j3o");
+rootNode.attachChild(scene);
+
+ +
+ +

Excercise - How to Load Assets

+
+ +

+ +As an exercise, let's try different ways of loading a scene. You will learn how to load the scene directly, or from a zip file. +

+
    +
  1. sample scene.
    +
  2. +
  3. (Optional:) Unzip the town.zip to see the structure of the contained Ogre dotScene: You'll get a directory named town. It contains XML and texture files, and file called main.scene. (This is just for your information, you do not need to do anything with it.)
    +
  4. +
  5. Place the town.zip file in the top level directory of your JME3 project, like so:
    jMonkeyProjects/MyGameProject/assets/
    +jMonkeyProjects/MyGameProject/build.xml
    +jMonkeyProjects/MyGameProject/src/
    +jMonkeyProjects/MyGameProject/town.zip
    +...
    +
    +
  6. +
+ +

+ +Use the following method to load models from a zip file: + +

+
    +
  1. Verify town.zip is in the project directory.
    +
  2. +
  3. Register a zip file locator to the project directory: Add the following code under simpleInitApp() {
        assetManager.registerLocator("town.zip", ZipLocator.class);
    +    Spatial gameLevel = assetManager.loadModel("main.scene");
    +    gameLevel.setLocalTranslation(0, -5.2f, 0);
    +    gameLevel.setLocalScale(2);
    +    rootNode.attachChild(gameLevel);
    + +

    +The loadModel() method now searches this zip directly for the files to load.
    +(This means, do not write loadModel(town.zip/main.scene) or similar!) +

    +
    +
  4. +
  5. Clean, build and run the project.
    +You should now see the Ninja+wall+teapot standing in a town.
    +
  6. +
+ +

+ +Tip: If you register new locators, make sure you do not get any file name conflicts: Don't name all scenes main.scene but give each scene a unique name. +

+ +

+Earlier in this tutorial, you loaded scenes and models from the asset directory. This is the most common way you will be loading scenes and models. Here is the typical procedure: + +

+
    +
  1. Remove the code that you added for the previous exercise.
    +
  2. +
  3. Move the unzipped town/ directory into the assets/Scenes/ directory of your project.
    +
  4. +
  5. Add the following code under simpleInitApp() {
        Spatial gameLevel = assetManager.loadModel("Scenes/town/main.scene");
    +    gameLevel.setLocalTranslation(0, -5.2f, 0);
    +    gameLevel.setLocalScale(2);
    +    rootNode.attachChild(gameLevel);
    + +

    + Note that the path is relative to the assets/??? directory. +

    +
    +
  6. +
  7. Clean, build and run the project. Again, you should see the Ninja+wall+teapot standing in a town.
    +
  8. +
+ +

+ +Here is a third method you must know, loading a scene/model from a .j3o file: + +

+
    +
  1. Remove the code from the previous exercise.
    +
  2. +
  3. If you haven't already, open the SDK and open the project that contains the HelloAsset class.
    +
  4. +
  5. In the projects window, browse to the assets/Scenes/town directory.
    +
  6. +
  7. Right-click the main.scene and convert the scene to binary: The jMoneyPlatform generates a main.j3o file.
    +
  8. +
  9. Add the following code under simpleInitApp() {
        Spatial gameLevel = assetManager.loadModel("Scenes/town/main.j3o");
    +    gameLevel.setLocalTranslation(0, -5.2f, 0);
    +    gameLevel.setLocalScale(2);
    +    rootNode.attachChild(gameLevel);
    + +

    + Again, note that the path is relative to the assets/??? directory. +

    +
    +
  10. +
  11. Clean, Build and run the project.
    +Again, you should see the Ninja+wall+teapot standing in a town.
    +
  12. +
+ +
+ +

Conclusion

+
+ +

+ +Now you know how to populate the scenegraph with static shapes and models, and how to build scenes. You have learned how to load assets using the assetManager and you have seen that the paths start relative to your project directory. Another important thing you have learned is to convert models to .j3o format for the executable JARs etc. +

+ +

+Let's add some action to the scene and continue with the Update Loop! + +

+
+ +

+See also: +

+ +
+ beginner, + intro, + documentation, + lightnode, + material, + model, + node, + gui, + hud, + texture +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html new file mode 100644 index 000000000..eeefda9e0 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html @@ -0,0 +1,395 @@ + +

jMonkeyEngine 3 Tutorial (11) - Hello Audio

+
+ +

+Previous: Hello Terrain, Next: Hello Effects +

+ +

+This tutorial explains how to add 3D sound to a game, and how make sounds play together with events, such as clicking. You learn how to use an Audio Listener and Audio Nodes. You also make use of an Action Listener and a MouseButtonTrigger from the previous Hello Input tutorial to make a mouse click trigger a gun shot sound. +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+ +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+ 
+/** Sample 11 - playing 3D audio. */
+public class HelloAudio extends SimpleApplication {
+ 
+  private AudioNode audio_gun;
+  private AudioNode audio_nature;
+  private Geometry player;
+ 
+  public static void main(String[] args) {
+    HelloAudio app = new HelloAudio();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    flyCam.setMoveSpeed(40);
+ 
+    /** just a blue box floating in space */
+    Box box1 = new Box(Vector3f.ZERO, 1, 1, 1);
+    player = new Geometry("Player", box1);
+    Material mat1 = new Material(assetManager, 
+            "Common/MatDefs/Misc/Unshaded.j3md");
+    mat1.setColor("Color", ColorRGBA.Blue);
+    player.setMaterial(mat1);
+    rootNode.attachChild(player);
+ 
+    /** custom init methods, see below */
+    initKeys();
+    initAudio();
+  }
+ 
+  /** We create two audio nodes. */
+  private void initAudio() {
+    /* gun shot sound is to be triggered by a mouse click. */
+    audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
+    audio_gun.setLooping(false);
+    audio_gun.setVolume(2);
+    rootNode.attachChild(audio_gun);
+ 
+    /* nature sound - keeps playing in a loop. */
+    audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", false);
+    audio_nature.setLooping(true);  // activate continuous playing
+    audio_nature.setPositional(true);
+    audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
+    audio_nature.setVolume(3);
+    rootNode.attachChild(audio_nature);
+    audio_nature.play(); // play continuously!
+  }
+ 
+  /** Declaring "Shoot" action, mapping it to a trigger (mouse click). */
+  private void initKeys() {
+    inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
+    inputManager.addListener(actionListener, "Shoot");
+  }
+ 
+  /** Defining the "Shoot" action: Play a gun sound. */
+  private ActionListener actionListener = new ActionListener() {
+    @Override
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Shoot") && !keyPressed) {
+        audio_gun.playInstance(); // play each instance once!
+      }
+    }
+  };
+ 
+  /** Move the listener with the a camera - for 3D audio. */
+  @Override
+  public void simpleUpdate(float tpf) {
+    listener.setLocation(cam.getLocation());
+    listener.setRotation(cam.getRotation());
+  }
+ 
+}
+ +

+When you run the sample, you should see a blue cube. You should hear a nature-like ambient sound. When you click, you hear a loud shot. +

+ +
+ +

Understanding the Code Sample

+
+ +

+ +In the initSimpleApp() method, you create a simple blue cube geometry called player and attach it to the scene ??? this just arbitrary sample content, so you see something when running the audio sample. +

+ +

+Let's have a closer look at initAudio() to learn how to use AudioNodes. +

+ +
+ +

AudioNodes

+
+ +

+ +Adding sound to your game is quite simple: Save your audio files into your assets/Sound directory. JME3 supports both Ogg Vorbis (.ogg) and Wave (.wav) file formats. +

+ +

+For each sound, you create an AudioNode. You can use an AudioNode like any node in the JME scene graph, e.g. attach it to other Nodes. You create one node for a gunshot sound, and one node for a nature sound. +

+
  private AudioNode audio_gun;
+  private AudioNode audio_nature;
+ +

+Look at the custom initAudio() method: Here you initialize the sound objects and set their parameters. +

+
audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
+    ...
+audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", false);
+ +

+These two lines create new sound nodes from the given audio files in the AssetManager. The false flag means that you want to buffer these sounds before playing. (If you set this flag to true, the sound will be streamed, which makes sense for really long sounds.) +

+ +

+You want the gunshot sound to play once (you don't want it to loop). You also specify its volume as gain factor (at 0, sound is muted, at 2, it is twice as loud, etc.). +

+
    audio_gun.setLooping(false);
+    audio_gun.setVolume(2);
+    rootNode.attachChild(audio_gun);
+ +

+The nature sound is different: You want it to loop continuously as background sound. This is why you set looping to true, and immediately call the play() method on the node. You also choose to set its volume to 3. +

+
    audio_nature.setLooping(true); // activate continuous playing
+    ...
+    audio_nature.setVolume(3);
+    rootNode.attachChild(audio_nature);
+    audio_nature.play(); // play continuously!
+  }
+ +

+Here you make audio_nature a positional sound that comes from a certain place. For that you give the node an explicit translation, in this example, you choose Vector3f.ZERO (which stands for the coordinates 0.0f,0.0f,0.0f, the center of the scene.) Since jME supports 3D audio, you are now able to hear this sound coming from this particular location. Making the sound positional is optional. If you don't use these lines, the ambient sound comes from every direction. +

+
    ...
+    audio_nature.setPositional(true);
+    audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
+    ...
+ +

+Tip: Attach AudioNodes into the scene graph like all nodes, to make certain moving nodes stay up-to-date. If you don't attach them, they are still audible and you don't get an error message but 3D sound will not work as expected. AudioNodes can be attached directly to the root node or they can be attached inside a node that is moving through the scene and both the AudioNode and the 3d position of the sound it is generating will move accordingly. +

+ +

+Tip: playInstance always plays the sound from the position of the AudioNode so multiple gunshots from one gun (for example) can be generated this way, however if multiple guns are firing at once then an AudioNode is needed for each one. +

+ +
+ +

Triggering Sound

+
+ +

+ +Let's have a closer look at initKeys(): As you learned in previous tutorials, you use the inputManager to respond to user input. Here you add a mapping for a left mouse button click, and name this new action Shoot. +

+
  /** Declaring "Shoot" action, mapping it to a trigger (mouse click). */
+  private void initKeys() {
+    inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
+    inputManager.addListener(actionListener, "Shoot");
+  }
+ +

+Setting up the ActionListener should also be familiar from previous tutorials. You declare that, when the trigger (the mouse button) is pressed and released, you want to play a gun sound. +

+
  /** Defining the "Shoot" action: Play a gun sound. */
+  private ActionListener actionListener = new ActionListener() {
+    @Override
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Shoot") && !keyPressed) {
+        audio_gun.playInstance(); // play each instance once!
+      }
+    }
+  };
+ +

+Since you want to be able to shoot fast repeatedly, so you do not want to wait for the previous gunshot sound to end before the next one can start. This is why you play this sound using the playInstance() method. This means that every click starts a new instance of the sound, so two instances can overlap. You set this sound not to loop, so each instance only plays once. As you would expect it of a gunshot. +

+ +
+ +

Ambient or Situational?

+
+ +

+ +The two sounds are two different use cases: +

+ + +

+ +Now every sound knows whether it should loop or not. +

+ +

+Apart from the looping boolean, another difference is where play().playInstance() is called on those nodes: +

+ + +
+ +

Buffered or Streaming?

+
+ +

+ +The Boolean in the AudioNode constructor defines whether the audio is buffered (false) or streamed (true). For example: + +

+
audio_nature = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false); // buffered
+...
+audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true); // streamed 
+ +

+Typically, you stream long sounds, and buffer short sounds. +

+ +

+Note that streamed sounds can not loop (i.e. setLooping will not work as you expect). Check the getStatus on the node and if it has stopped recreate the node. + +

+ +
+ +

Play() or PlayInstance()?

+
+
+ + + + + + + + + + + + +
audio.play()audio.playInstance()
Plays buffered sounds.Plays buffered sounds.
Plays streamed sounds.Cannot play streamed sounds.
The same sound cannot play twice at the same time.The same sounds can play multiple times and overlap.
+ +
+ +

Your Ear in the Scene

+
+ +

+ +To create a 3D audio effect, JME3 needs to know the position of the sound source, and the position of the ears of the player. The ears are represented by an 3D Audio Listener object. The listener object is a default object in a SimpleApplication. +

+ +

+In order to make the most of the 3D audio effect, you must use the simpleUpdate() method to move and rotate the listener (the player's ears) together with the camera (the player's eyes). +

+
  public void simpleUpdate(float tpf) {
+    listener.setLocation(cam.getLocation());
+    listener.setRotation(cam.getRotation());
+  }
+ +

+If you don't do that, the results of 3D audio will be quite random. +

+ +
+ +

Global, Directional, Positional?

+
+ +

+ +In this example, you defined the nature sound as coming from a certain position, but not the gunshot sound. This means your gunshot is global and can be heard everywhere with the same volume. JME3 also supports directional sounds which you can only hear from a certain direction. +

+ +

+It makes equally sense to make the gunshot positional, and let the ambient sound come from every direction. How do you decide which type of 3D sound to use from case to case? +

+ + +

+ +In short, you must choose in every situation whether it makes sense for a sound to be global, directional, or positional. +

+ +
+ +

Conclusion

+
+ +

+ +You now know how to add the two most common types of sound to your game: Global sounds and positional sounds. You can play sounds in two ways: Either continuously in a loop, or situationally just once. You know the difference between buffering short sounds and streaming long sounds. You know the difference between playing overlapping sound instances, and playing unique sounds that cannot overlap with themselves. You also learned to use sound files that are in either .ogg or .wav format. +

+ +

+Tip: JME's Audio implementation also supports more advanced effects such as reverberation and Doppler effect. Use these "pro" features to make audio sound different depending on whether it's in the hallway, in a cave, outdoors, or in a carpeted room. Find out more about environmental effects from the sample code included in the jme3test directory and from the advanced Audio docs. +

+ +

+Want some fire and explosions to go with your sounds? Read on to learn more about effects. + +

+
+ +

+ +See also: +

+ +
+ sound, + documentation, + beginner, + intro +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html new file mode 100644 index 000000000..456e65b73 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html @@ -0,0 +1,539 @@ + +

jMonkeyEngine 3 Tutorial (9) - Hello Collision

+
+ +

+Previous: Hello Picking, +Next: Hello Terrain +

+ +

+This tutorial demonstrates how you load a scene model and give it solid walls and floors for a character to walk around. +You use a RigidBodyControl for the static collidable scene, and a CharacterControl for the mobile first-person character. You also learn how to set up the default first-person camera to work with physics-controlled navigation. +You can use the solution shown here for first-person shooters, mazes, and similar games. +

+ +

+ +

+ +
+ +

Sample Code

+
+ +

+ +If you don't have it yet, sample scene. +

+
jMonkeyProjects$ ls -1 BasicGame
+assets/
+build.xml
+town.zip
+src/
+ +

+Place town.zip in the root directory of your JME3 project. Here is the code: +

+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.ZipLocator;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.bullet.util.CollisionShapeFactory;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+ 
+/**
+ * Example 9 - How to make walls and floors solid.
+ * This collision code uses Physics and a custom Action Listener.
+ * @author normen, with edits by Zathras
+ */
+public class HelloCollision extends SimpleApplication
+        implements ActionListener {
+ 
+  private Spatial sceneModel;
+  private BulletAppState bulletAppState;
+  private RigidBodyControl landscape;
+  private CharacterControl player;
+  private Vector3f walkDirection = new Vector3f();
+  private boolean left = false, right = false, up = false, down = false;
+ 
+  public static void main(String[] args) {
+    HelloCollision app = new HelloCollision();
+    app.start();
+  }
+ 
+  public void simpleInitApp() {
+    /** Set up Physics */
+    bulletAppState = new BulletAppState();
+    stateManager.attach(bulletAppState);
+    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ 
+    // We re-use the flyby camera for rotation, while positioning is handled by physics
+    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
+    flyCam.setMoveSpeed(100);
+    setUpKeys();
+    setUpLight();
+ 
+    // We load the scene from the zip file and adjust its size.
+    assetManager.registerLocator("town.zip", ZipLocator.class);
+    sceneModel = assetManager.loadModel("main.scene");
+    sceneModel.setLocalScale(2f);
+ 
+    // We set up collision detection for the scene by creating a
+    // compound collision shape and a static RigidBodyControl with mass zero.
+    CollisionShape sceneShape =
+            CollisionShapeFactory.createMeshShape((Node) sceneModel);
+    landscape = new RigidBodyControl(sceneShape, 0);
+    sceneModel.addControl(landscape);
+ 
+    // 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(20);
+    player.setFallSpeed(30);
+    player.setGravity(30);
+    player.setPhysicsLocation(new Vector3f(0, 10, 0));
+ 
+    // We attach the scene and the player to the rootNode and the physics space,
+    // to make them appear in the game world.
+    rootNode.attachChild(sceneModel);
+    bulletAppState.getPhysicsSpace().add(landscape);
+    bulletAppState.getPhysicsSpace().add(player);
+  }
+ 
+  private void setUpLight() {
+    // We add light so we see the scene
+    AmbientLight al = new AmbientLight();
+    al.setColor(ColorRGBA.White.mult(1.3f));
+    rootNode.addLight(al);
+ 
+    DirectionalLight dl = new DirectionalLight();
+    dl.setColor(ColorRGBA.White);
+    dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
+    rootNode.addLight(dl);
+  }
+ 
+  /** 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")) {
+      left = value;
+    } else if (binding.equals("Right")) {
+      right = value;
+    } else if (binding.equals("Up")) {
+      up = value;
+    } else if (binding.equals("Down")) {
+      down = value;
+    } 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());
+  }
+}
+ +

+Run the sample. You should see a town square with houses and a monument. Use the WASD keys and the mouse to navigate around with a first-person perspective. Run forward and jump by pressing W and Space. Note how you step over the sidewalk, and up the steps to the monument. You can walk in the alleys between the houses, but the walls are solid. Don't walk over the edge of the world! :-) +

+ +
+ +

Understanding the Code

+
+ +

+ +Let's start with the class declaration: +

+
public class HelloCollision extends SimpleApplication
+        implements ActionListener { ... }
+ +

+You already know that SimpleApplication is the base class for all jME3 games. You make this class implement the ActionListener interface because you want to customize the navigational inputs later. +

+
  private Spatial sceneModel;
+  private BulletAppState bulletAppState;
+  private RigidBodyControl landscape;
+  private CharacterControl player;
+  private Vector3f walkDirection = new Vector3f();
+  private boolean left = false, right = false, up = false, down = false;
+ +

+You initialize a few private fields: +

+ + +

+ +Let's have a look at all the details: +

+ +
+ +

Initializing the Game

+
+ +

+ +As usual, you initialize the game in the simpleInitApp() method. +

+
    viewPort.setBackgroundColor(new ColorRGBA(0.7f,0.8f,1f,1f));
+    flyCam.setMoveSpeed(100);
+    setUpKeys();
+    setUpLight();
+
    +
  1. You set the background color to light blue, since this is a scene with a sky.
    +
  2. +
  3. You repurpose the default camera control "flyCam" as first-person camera and set its speed.
    +
  4. +
  5. The auxiliary method setUpLights() adds your light sources.
    +
  6. +
  7. The auxiliary method setUpKeys() configures input mappings???we will look at it later.
    +
  8. +
+ +
+ +

The Physics-Controlled Scene

+
+ +

+ +The first thing you do in every physics game is create a BulletAppState object. It gives you access to jME3's jBullet integration which handles physical forces and collisions. +

+
    bulletAppState = new BulletAppState();
+    stateManager.attach(bulletAppState);
+ +

+For the scene, you load the sceneModel from a zip file, and adjust the size. +

+
    assetManager.registerLocator("town.zip", ZipLocator.class);
+    sceneModel = assetManager.loadModel("main.scene");
+    sceneModel.setLocalScale(2f);
+ +

+The file town.zip is included as a sample model in the JME3 sources ??? you can . (Optionally, use any OgreXML scene of your own.) For this sample, place the zip file in the application's top level directory (that is, next to src/, assets/, build.xml). +

+
    CollisionShape sceneShape =
+      CollisionShapeFactory.createMeshShape((Node) sceneModel);
+    landscape = new RigidBodyControl(sceneShape, 0);
+    sceneModel.addControl(landscape);
+    rootNode.attachChild(sceneModel);
+ +

+To use collision detection, you add a RigidBodyControl to the sceneModel Spatial. The RigidBodyControl for a complex model takes two arguments: A Collision Shape, and the object's mass. +

+ + +

+ +Tip: Remember to add a light source so you can see the scene. +

+ +
+ +

The Physics-Controlled Player

+
+ +

+ +A first-person player is typically invisible. When you use the default flyCam as first-person cam, it does not even test for collisons and runs through walls. This is because the flyCam control does not have any physical shape assigned. In this code sample, you represent the first-person player as an (invisible) physical shape. You use the WASD keys to steer this physical shape around, while the physics engine manages for you how it walks along solid walls and on solid floors and jumps over solid obstacles. Then you simply make the camera follow the walking shape's location ??? and you get the illusion of being a physical body in a solid environment seeing through the camera. +

+ +

+So let's set up collision detection for the first-person player. +

+
    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
+ +

+Again, you create a CollisionShape: This time you choose a CapsuleCollisionShape, a cylinder with a rounded top and bottom. This shape is optimal for a person: It's tall and the roundness helps to get stuck less often on obstacles. +

+ +
    player = new CharacterControl(capsuleShape, 0.05f);
+ +

+

"Does that CollisionShape make me look fat?" If you ever get confusing physics behaviour, remember to have a look at the collision shapes. Add the following line after the bulletAppState initialization to make the shapes visible: +

+
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ +

+ +

+

+ +

+Now you use the CollisionShape to create a CharacterControl that represents the first-person player. The last argument of the CharacterControl constructor (here .05f) is the size of a step that the character should be able to surmount. +

+
    player.setJumpSpeed(20);
+    player.setFallSpeed(30);
+    player.setGravity(30);
+ +

+Apart from step height and character size, the CharacterControl lets you configure jumping, falling, and gravity speeds. Adjust the values to fit your game situation. +

+
    player.setPhysicsLocation(new Vector3f(0, 10, 0));
+ +

+Finally we put the player in its starting position and update its state ??? remember to use setPhysicsLocation() instead of setLocalTranslation() now, since you are dealing with a physical object. +

+ +
+ +

PhysicsSpace

+
+ +

+ +Remember, in physical games, you must register all solid objects (usually the characters and the scene) to the PhysicsSpace! +

+
    bulletAppState.getPhysicsSpace().add(landscape);
+    bulletAppState.getPhysicsSpace().add(player);
+ +

+The invisible body of the character just sits there on the physical floor. It cannot walk yet ??? you will deal with that next. +

+ +
+ +

Navigation

+
+ +

+ +The default camera controller cam is a third-person camera. JME3 also offers a first-person controller, flyCam, which we use here to handle camera rotation. The flyCam control moves the camera using setLocation(). +

+ +

+However, you must redefine how walking (camera movement) is handled for physics-controlled objects: When you navigate a non-physical node (e.g. the default flyCam), you simply specify the target location. There are no tests that prevent the flyCam from getting stuck in a wall! When you move a PhysicsControl, you want to specify a walk direction instead. Then the PhysicsSpace can calculate for you how far the character can actually move in the desired direction ??? or whether an obstacle prevents it from going any further. +

+ +

+In short, you must re-define the flyCam's navigational key mappings to use setWalkDirection() instead of setLocalTranslation(). Here are the steps: +

+ +
+ +

1. inputManager

+
+ +

+ +In the simpleInitApp() method, you re-configure the familiar WASD inputs for walking, and Space for 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");
+}
+ +

+You can move this block of code into an auxiliary method setupKeys() and call this method from simpleInitApp()??? to keep the code more readable. +

+ +
+ +

2. onAction()

+
+ +

+ +Remember that this class implements the an ActionListener interface, so you can customize the flyCam inputs. The ActionListener interface requires you to implement the onAction() method: You re-define the actions triggered by navigation key presses to work with physics. +

+
  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();
+    }
+  }
+ +

+The only movement that you do not have to implement yourself is the jumping action. The call player.jump() is a special method that handles a correct jumping motion for your PhysicsCharacterNode. +

+ +

+For all other directions: Every time the user presses one of the WASD keys, you keep track of the direction the user wants to go, by storing this info in four directional Booleans. No actual walking happens here yet. The update loop is what acts out the directional info stored in the booleans, and makes the player move, as shown in the next code snippet: +

+ +
+ +

3. setWalkDirection()

+
+ +

+ +Previously in the onAction() method, you have collected the info in which direction the user wants to go in terms of "forward" or "left". In the update loop, you repatedly poll the current rotation of the camera. You calculate the actual vectors to which "forward" or "left" corresponds in the coordinate system. +

+ +

+This last and most important code snippet goes into the simpleUpdate() method. +

+
  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());
+  }
+ +

+This is how the walking is triggered: +

+
    +
  1. Initialize the vector walkDirection to zero. This is where you want to store the calculated walk direction.
    +
  2. +
  3. Add to walkDirection the recent motion vectors that you polled from the camera. This way it is posible for a character to move forward and to the left simultaneously, for example!
    +
  4. +
  5. This one last line does the "walking magic":
    player.setWalkDirection(walkDirection);
    + +

    + Always use setWalkDirection() to make a physics-controlled object move continuously, and the physics engine handles collision detection for you. +

    +
    +
  6. +
  7. Make the first-person camera object follow along with the physics-controlled player:
    cam.setLocation(player.getPhysicsLocation());
    +
    +
  8. +
+ +

+ +Important: Again, do not use setLocalTranslation() to walk the player around. You will get it stuck by overlapping with another physical object. You can put the player in a start position with setPhysicalLocation() if you make sure to place it a bit above the floor and away from obstacles. +

+ +
+ +

Conclusion

+
+ +

+ +You have learned how to load a "solid" physical scene model and walk around in it with a first-person perspective. +You learned to speed up the physics calculations by using the CollisionShapeFactory to create efficient CollisionShapes for complex Geometries. You know how to add PhysicsControls to your collidable geometries and you register them to the PhysicsSpace. You also learned to use player.setWalkDirection(walkDirection) to move collision-aware characters around, and not setLocalTranslation(). +

+ +

+Terrains are another type of scene in which you will want to walk around. Let's proceed with learning how to generate terrains now. + +

+
+ +

+ +Related info: +

+ +
+ beginner, + collision, + control, + intro, + documentation, + model, + physics +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_effects.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_effects.html new file mode 100644 index 000000000..9b7f75999 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_effects.html @@ -0,0 +1,383 @@ + +

jMonkeyEngine 3 Tutorial (12) - Hello Effects

+
+ +

+ +Previous: Hello Audio, +Next: Hello Physics +

+ +

+ +

+ +

+When you see one of the following in a game, then a particle system is likely behind it: +

+ + +

+ +These scene elements cannot be modeled by meshes. In very simple terms: +

+ + +

+ +Particle effects can be animated (e.g. sparks, drops) and static (strands of grass, hair). Non-particle effects include bloom/glow, and motion blur/afterimage. In this tutorial you learn how to make animated particles (com.jme3.effect). +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.effect.ParticleEmitter;
+import com.jme3.effect.ParticleMesh;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+ 
+/** Sample 11 - how to create fire, water, and explosion effects. */
+public class HelloEffects extends SimpleApplication {
+ 
+  public static void main(String[] args) {
+    HelloEffects app = new HelloEffects();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+ 
+    ParticleEmitter fire = 
+            new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+    Material mat_red = new Material(assetManager, 
+            "Common/MatDefs/Misc/Particle.j3md");
+    mat_red.setTexture("Texture", assetManager.loadTexture(
+            "Effects/Explosion/flame.png"));
+    fire.setMaterial(mat_red);
+    fire.setImagesX(2); 
+    fire.setImagesY(2); // 2x2 texture animation
+    fire.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
+    fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+    fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 2, 0));
+    fire.setStartSize(1.5f);
+    fire.setEndSize(0.1f);
+    fire.setGravity(0, 0, 0);
+    fire.setLowLife(1f);
+    fire.setHighLife(3f);
+    fire.getParticleInfluencer().setVelocityVariation(0.3f);
+    rootNode.attachChild(fire);
+ 
+    ParticleEmitter debris = 
+            new ParticleEmitter("Debris", ParticleMesh.Type.Triangle, 10);
+    Material debris_mat = new Material(assetManager, 
+            "Common/MatDefs/Misc/Particle.j3md");
+    debris_mat.setTexture("Texture", assetManager.loadTexture(
+            "Effects/Explosion/Debris.png"));
+    debris.setMaterial(debris_mat);
+    debris.setImagesX(3); 
+    debris.setImagesY(3); // 3x3 texture animation
+    debris.setRotateSpeed(4);
+    debris.setSelectRandomImage(true);
+    debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 4, 0));
+    debris.setStartColor(ColorRGBA.White);
+    debris.setGravity(0, 6, 0);
+    debris.getParticleInfluencer().setVelocityVariation(.60f);
+    rootNode.attachChild(debris);
+    debris.emitAllParticles();
+  }
+}
+ +

+You should see an explosion that sends debris flying, and a fire. + +

+ +
+ +

Texture Animation and Variation

+
+ +

+ + +

+ +

+Start by choosing a material texture for your effect. If you provide the emitter with a set of textures (see image), it can use them either for variation (random order), or as animation steps (fixed order). +

+ +

+Setting emitter textures works just as you have already learned in previous chapters. This time you base the material on the Particle.j3md material definition. Let's have a closer look at the material for the Debris effect. +

+
    ParticleEmitter debris = 
+            new ParticleEmitter("Debris", ParticleMesh.Type.Triangle, 10);
+    Material debris_mat = new Material(assetManager, 
+            "Common/MatDefs/Misc/Particle.j3md");
+    debris_mat.setTexture("Texture", assetManager.loadTexture(
+            "Effects/Explosion/Debris.png"));
+    debris.setMaterial(debris_mat);
+    debris.setImagesX(3); 
+    debris.setImagesY(3); // 3x3 texture animation
+    debris.setSelectRandomImage(true);
+        ...
+
    +
  1. Create a material and load the texture.
    +
  2. +
  3. Tell the Emitter into how many animation steps (x*y) the texture is divided.
    +The debris texture has 3x3 frames.
    +
  4. +
  5. Optionally, tell the Emitter whether the animation steps are to be at random, or in order.
    +For the debris, the frames play at random.
    +
  6. +
+ +

+ +As you see in the debris example, texture animations improve effects because each "flame" or "piece of debris" now looks different. Also think of electric or magic effects, where you can create very interesting animations by using an ordered morphing series of lightning bolts; or flying leaves or snow flakes, for instance. +

+ +

+The fire material is created the same way, just using "Effects/Explosion/flame.png" texture, which has with 2x2 ordered animation steps. +

+ +
+ +

Default Particle Textures

+
+ +

+ +The following particle textures included in test-data.jar. You can copy and use them in your own effects. + +

+
+ + + + + + + + + + + + + + + + + + +
Texture Path Dimension Preview
Effects/Explosion/Debris.png 3*3
Effects/Explosion/flame.png 2*2
Effects/Explosion/shockwave.png 1*1
Effects/Explosion/smoketrail.png 1*3
Effects/Smoke/Smoke.png 1*15
+ +

+ +Copy them into you assets/Effects directory to use them. +

+ +
+ +

Creating Custom Textures

+
+ +

+ +For your game, you will likely create custom particle textures. Look at the fire example again. +

+
    ParticleEmitter fire = 
+            new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
+    Material mat_red = new Material(assetManager, 
+            "Common/MatDefs/Misc/Particle.j3md");
+    mat_red.setTexture("Texture", assetManager.loadTexture(
+            "Effects/Explosion/flame.png"));
+    fire.setMaterial(mat_red);
+    fire.setImagesX(2); 
+    fire.setImagesY(2); // 2x2 texture animation
+    fire.setEndColor(  new ColorRGBA(1f, 0f, 0f, 1f));   // red
+    fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow
+ 
+ +

+ +

+ +

+Compare the texture with the resulting effect. +

+ + +

+ +Create a grayscale texture in a graphic editor, and save it to your assets/Effects directory. If you split up one image file into x*y animation steps, make sure each square is of equal size???just as you see in the examples here. +

+ +
+ +

Emitter Parameters

+
+ +

+ +A particle system is always centered around an emitter. +

+ +

+Use the setShape() method to change the EmitterShape: +

+ + +

+Example: +

+
emitter.setShape(new EmitterPointShape(Vector3f.ZERO));
+ +

+You create different effects by changing the emitter parameters: + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameter Method Default Description
number setNumParticles() N/A The maximum number of particles visible at the same time. Value is specified by user in constructor. This influences the density and length of the "trail".
velocity getParticleInfluencer(). setInitialVelocity() Vector3f.ZERO Specify a vector how fast particles move and in which start direction.
direction getParticleInfluencer(). setVelocityVariation()
+setFacingVelocity()
+setRandomAngle()
+setFaceNormal()
+setRotateSpeed()
0.2f
+false
+false
+Vector3f.NAN
+0.0f
Optional accessors that control in which direction particles face while flying.
lifetime setLowLife()
+setHighLife()
3f
+7f
Minimum and maximum time period before particles fade.
emission rate setParticlesPerSec() 20 How many new particles are emitted per second.
color setStartColor()
+setEndColor()
gray Set to the same colors, or to two different colors for a gradient effect.
size setStartSize()
+setEndSize()
0.2f
+2f
Set to two different values for shrink/grow effect, or to same size for constant effect.
gravity setGravity() 0,1,0 Whether particles fall down (positive) or fly up (negative). Set to 0f for a zero-g effect where particles keep flying.
+ +

+ +You can find details about effect parameters here. +Add and modify one parameter at a time, and try different values until you get the effect you want. +

+ +

+

Tip: Use the SceneComposer in the jMonkeyEngine SDK to create effects more easily. Create an empty scene and add an emitter object to it. Change the emitter properties and watch the outcome live. You can save created effects as .j3o file and load them like scenes or models. +

+

+ +
+ +

Exercise

+
+ +

+ +Can you "invert" the fire effect into a small waterfall? Here some tips: +

+ + +
+ +

Conclusion

+
+ +

+ +You have learned that many different effects can be created by changing the parameters and textures of one general emitter object. +

+ +

+Now you move on to another exciting chapter ??? the simulation of . Let's shoot some cannon balls at a brick wall! + +

+
+
+ beginner, + documentation, + intro, + transparency, + effect +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html new file mode 100644 index 000000000..eb4828086 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html @@ -0,0 +1,422 @@ + +

jMonkeyEngine 3 Tutorial (5) - Hello Input System

+
+ +

+ +Previous: Hello Update Loop, +Next: Hello Material +

+ +

+By default, SimpleApplication sets up a camera control that allows you to steer the camera with the WASD keys, the arrow keys, and the mouse. You can use it as a flying first-person camera right away. But what if you need a third-person camera, or you want keys to trigger special game actions? +

+ +

+Every game has its custom keybindings, and this tutorial explains how you define them. We first define the key presses and mouse events, and then we define the actions they should trigger. +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.math.ColorRGBA;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
+ 
+/** Sample 5 - how to map keys and mousebuttons to actions */
+public class HelloInput extends SimpleApplication {
+ 
+  public static void main(String[] args) {
+    HelloInput app = new HelloInput();
+    app.start();
+  }
+  protected Geometry player;
+  Boolean isRunning=true;
+ 
+  @Override
+  public void simpleInitApp() {
+    Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+    player = new Geometry("Player", b);
+    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.Blue);
+    player.setMaterial(mat);
+    rootNode.attachChild(player);
+    initKeys(); // load my custom keybinding
+  }
+ 
+  /** Custom Keybinding: Map named actions to inputs. */
+  private void initKeys() {
+    // You can map one or several inputs to one named action
+    inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
+    inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
+    inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
+    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE),
+                                      new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+    // Add the names to the action listener.
+    inputManager.addListener(actionListener, new String[]{"Pause"});
+    inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate"});
+ 
+  }
+ 
+  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Pause") && !keyPressed) {
+        isRunning = !isRunning;
+      }
+    }
+  };
+ 
+  private AnalogListener analogListener = new AnalogListener() {
+    public void onAnalog(String name, float value, float tpf) {
+      if (isRunning) {
+        if (name.equals("Rotate")) {
+          player.rotate(0, value*speed, 0);
+        }
+        if (name.equals("Right")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x + value*speed, v.y, v.z);
+        }
+        if (name.equals("Left")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x - value*speed, v.y, v.z);
+        }
+      } else {
+        System.out.println("Press P to unpause.");
+      }
+    }
+  };
+}
+ +

+Build and run the example. +

+ + +
+ +

Defining Mappings and Triggers

+
+ +

+ +First you register each mapping name with its trigger(s). Remember the following: +

+ + +

+ +Have a look at the code: +

+
    +
  1. You register the mapping named "Rotate" to the Spacebar key trigger.
    +new KeyTrigger(KeyInput.KEY_SPACE)).
    +
  2. +
  3. In the same line, you also register "Rotate" to an alternative mouse click trigger.
    +new MouseButtonTrigger(MouseInput.BUTTON_LEFT)
    +
  4. +
  5. You map the Pause, Left, Right mappings to the P, J, K keys, respectively.
    +
  6. +
+
    // You can map one or several inputs to one named action
+    inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
+    inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
+    inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
+    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE),
+                                      new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ +

+ +Now you need to register your trigger mappings. +

+
    +
  1. You register the pause action to the ActionListener, because it is an "on/off" action.
    +
  2. +
  3. You register the movement actions to the AnalogListener, because they are gradual actions.
    +
  4. +
+
    // Add the names to the action listener.
+    inputManager.addListener(actionListener, new String[]{"Pause"});
+    inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate"});
+ +

+This code goes into the simpleInitApp() method. But since we will likely add many keybindings, we extract these lines and wrap them in an auxiliary method, initKeys(). The initKeys() method is not part of the Input Controls interface ??? you can name it whatever you like. Just don't forget to call your method from the initSimpleApp() method. +

+ +
+ +

Implementing the Actions

+
+ +

+ +You have mapped action names to input triggers. Now you specify the actions themselves. +

+ +

+The two important methods here are the ActionListener with its onAction() method, and the AnalogListener with its onAnalog() method. In these two methods, you test for each named mapping, and call the game action you want to trigger. +

+ +

+In this example, we trigger the following actions: + +

+
    +
  1. The Rotate mapping triggers the action player.rotate(0, value, 0).
    +
  2. +
  3. The Left and Right mappings increase and decrease the player's x coordinate.
    +
  4. +
  5. The Pause mapping flips a boolean isRunning.
    +
  6. +
  7. We also want to check the boolean isRunning before any action (other than unpausing) is executed.
    +
  8. +
+
  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Pause") && !keyPressed) {
+        isRunning = !isRunning;
+      }
+    }
+  };
+ 
+  private AnalogListener analogListener = new AnalogListener() {
+    public void onAnalog(String name, float value, float tpf) {
+      if (isRunning) {
+        if (name.equals("Rotate")) {
+          player.rotate(0, value*speed, 0);
+        }
+        if (name.equals("Right")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x + value*speed, v.y, v.z);
+        }
+        if (name.equals("Left")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x - value*speed, v.y, v.z);
+        }
+      } else {
+        System.out.println("Press P to unpause.");
+      }
+    }
+  };
+ +

+You can also combine both listeners into one, the engine will send the appropriate events to each method (onAction or onAnalog). For example: + +

+
  private MyCombinedListener combinedListener = new MyCombinedListener();
+ 
+  private static class MyCombinedListener implements AnalogListener, ActionListener {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Pause") && !keyPressed) {
+        isRunning = !isRunning;
+      }
+    }
+ 
+    public void onAnalog(String name, float value, float tpf) {
+      if (isRunning) {
+        if (name.equals("Rotate")) {
+          player.rotate(0, value*speed, 0);
+        }
+        if (name.equals("Right")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x + value*speed, v.y, v.z);
+        }
+        if (name.equals("Left")) {
+          Vector3f v = player.getLocalTranslation();
+          player.setLocalTranslation(v.x - value*speed, v.y, v.z);
+        }
+      } else {
+        System.out.println("Press P to unpause.");
+      }
+    }
+  }
+// ...
+inputManager.addListener(combinedListener, new String[]{"Pause", "Left", "Right", "Rotate"});
+ 
+ +

+It's okay to use only one of the two Listeners, and not implement the other one, if you are not using this type of interaction. In the following, we have a closer look how to decide which of the two listeners is best suited for which situation. + +

+ +
+ +

Analog, Pressed, or Released?

+
+ +

+ +Technically, every input can be either an "analog" or a "digital" action. Here is how you find out which listener is the right one for which type of input. +

+ +

+Mappings registered to the AnalogListener are triggered repeatedly and gradually. +

+ + +

+ +In order to see the total time that a key has been pressed for then the incoming value can be accumulated. The analogue listener may also need to be combined with an action listener so that you are notified when they key is released. +

+ + +

+Mappings registered to the ActionListener are digital either-or actions ??? "Pressed or released? On or off?" +

+ + +

+ +Tip: It's very common that you want an action to be only triggered once, in the moment when the key is released. For instance when opening a door, flipping a boolean state, or picking up an item. To achieve that, you use an ActionListener and test for ??? && !keyPressed. For an example, look at the Pause button code: +

+
      if (name.equals("Pause") && !keyPressed) {
+        isRunning = !isRunning;
+      }
+ +
+ +

Table of Triggers

+
+ +

+ +You can find the list of input constants in the files src/core/com/jme3/input/KeyInput.java, JoyInput.java, and MouseInput.java. Here is an overview of the most common triggers constants: + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Trigger Code
Mouse button: Left Click MouseButtonTrigger(MouseInput.BUTTON_LEFT)
Mouse button: Right Click MouseButtonTrigger(MouseInput.BUTTON_RIGHT)
Keyboard: Characters and Numbers KeyTrigger(KeyInput.KEY_X)
Keyboard: Spacebar KeyTrigger(KeyInput.KEY_SPACE)
Keyboard: Return, Enter KeyTrigger(KeyInput.KEY_RETURN), KeyTrigger(KeyInput.KEY_NUMPADENTER)
Keyboard: Escape KeyTrigger(KeyInput.KEY_ESCAPE)
Keyboard: Arrows KeyTrigger(KeyInput.KEY_UP), KeyTrigger(KeyInput.KEY_DOWN)
+KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT)
+ +

+ +Tip: If you don't recall an input constant during development, you benefit from an IDE's code completion functionality: Place the caret after e.g. KeyInput.| and trigger code completion to select possible input identifiers. +

+ +
+ +

Exercises

+
+
    +
  1. Add mappings for moving the player (box) up and down with the H and L keys!
    +
  2. +
  3. Modify the mappings so that you can also trigger the up an down motion with the mouse scroll wheel!
    +
      +
    • Tip: Use new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)
      +
    • +
    +
  4. +
  5. In which situation would it be better to use variables instead of literals for the MouseInput/KeyInput definitions?
    int usersPauseKey = KeyInput.KEY_P; 
    +...
    +inputManager.addMapping("Pause",  new KeyTrigger(usersPauseKey));
    +
    +
  6. +
+ +

+ +

Link to user-proposed solutions: +Be sure to try to solve them for yourself first! +

+ +

+ +
+ +

Conclusion

+
+ +

+ +You are now able to add custom interactions to your game: You know that you first have to define the key mappings, and then the actions for each mapping. You have learned to respond to mouse events and to the keyboard. You understand the difference between "analog" (gradually repeated) and "digital" (on/off) inputs. +

+ +

+Now you can already write a little interactive game! But wouldn't it be cooler if these old boxes were a bit more fancy? Let's continue with learning about materials. +

+
+ input, + intro, + beginner, + documentation, + keyinput, + click +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html new file mode 100644 index 000000000..f552e1591 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html @@ -0,0 +1,225 @@ + +

jMonkeyEngine 3 Tutorial (4) - Hello Update Loop

+
+ +

+ +Previous: Hello Assets, +Next: Hello Input System +

+ +

+Now that you know how to load assets, such as 3D models, you want to implement some gameplay that uses these assets. In this tutorial we look at the update loop. The update loop of your game is where the action happens. +

+ +
+ +

Code Sample

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+ 
+/** Sample 4 - how to trigger repeating actions from the main update loop.
+ * In this example, we make the player character rotate. */
+public class HelloLoop extends SimpleApplication {
+ 
+    public static void main(String[] args){
+        HelloLoop app = new HelloLoop();
+        app.start();
+    }
+ 
+    protected Geometry player;
+ 
+    @Override
+    public void simpleInitApp() {
+ 
+        Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+        player = new Geometry("blue cube", b);
+        Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setColor("Color", ColorRGBA.Blue);
+        player.setMaterial(mat);
+        rootNode.attachChild(player);
+    }
+ 
+    /* This is the update loop */
+    @Override
+    public void simpleUpdate(float tpf) {
+        // make the player rotate
+        player.rotate(0, 2*tpf, 0); 
+    }
+}
+ +

+Build and run the file: You see a constantly rotating cube. +

+ +
+ +

Understanding the Code

+
+ +

+ +Compared to our previous code samples you note that the player Geometry is now a class field. This is because we want the update loop to be able to access and transform this Geometry. As usual, we initialize the player object in the simpleInitApp() method. +

+ +

+Now have a closer look at the simpleUpdate() method ??? this is the update loop. +

+ + +
+ +

Using the Update Loop

+
+ +

+ +A rotating object is just a simple example. In the update loop, you typically have many tests and trigger various game actions. This is were you update score and health points, check for collisions, make enemies calculate their next move, roll the dice whether a trap has been set off, play random ambient sounds, and much more. + +

+ + +
+ +

Init - Update - Render

+
+ +

+ +Note the contrast: +

+ + +

+ +Since rendering is automatic, initialization and updating are the two most important concepts in a SimpleApplication for you right now. These methods are where you load and create game data (once), and (repeatedly) change their properties to update the game state: +

+ + +

+ +

Everything in a game happens either during initialization or during the update loop. This means that these two methods grow very long over time. There are two strategies how experienced developers spread out their init and update code over several Java classes: +

+ + +

+Keep this in mind for later when your application grows. + +

+

+ +
+ +

Exercises

+
+ +

+ +Here are some fun things to try: +

+
    +
  1. What happens if you give the rotate() method negative numbers?
    +
  2. +
  3. Can you create two Geometries next to each other, and make one rotate twice as fast as the other? (use the tpf variable)
    +
  4. +
  5. Can you make a cube that pulsates? (grows and shrinks)
    +
  6. +
  7. Can you make a cube that changes color? (change and set the Material)
    +
  8. +
  9. Can you make a rolling cube? (rotate around the x axis, and translate along the z axis)
    +
  10. +
+ +

+ +Look back at the Hello Node tutorial if you do not remember the transformation methods for scaling, translating, and rotating. +

+ +

+

Link to user-proposed solutions: +Be sure to try to solve them for yourself first! +

+ +

+ +
+ +

Conclusion

+
+ +

+ +Now you are listening to the update loop, "the heart beat" of the game, and you can add all kinds of action to it. +

+ +

+The next thing the game needs is some interaction! Continue learning how to respond to user input. +

+
+ +

+See also: +

+ +
+ documentation, + state, + states, + intro, + beginner, + control, + loop +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html new file mode 100644 index 000000000..e685fbabe --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html @@ -0,0 +1,454 @@ + +

jMonkeyEngine 3 Tutorial (6) - Hello Materials

+
+ +

+ +Previous: Hello Input System, +Next: Hello Animation +

+ +

+The term Material includes everything that influences what the surface of a 3D model looks like: The color, texture, shininess, and opacity/transparency. Plain coloring is covered in Hello Node. Loading models that come with materials is covered in Hello Asset. In this tutorial you learn to create and use custom JME3 Material Definitions. + +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+ +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.texture.Texture;
+import com.jme3.util.TangentBinormalGenerator;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+ 
+/** Sample 6 - how to give an object's surface a material and texture.
+ * How to make objects transparent, or let colors "leak" through partially
+ * transparent textures. How to make bumpy and shiny surfaces.  */
+ 
+public class HelloMaterial extends SimpleApplication {
+  public static void main(String[] args) {
+    HelloMaterial app = new HelloMaterial();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    /** A simple textured cube -- in good MIP map quality. */
+    Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);
+    Geometry cube = new Geometry("My Textured Box", boxshape1);
+    Material mat_stl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
+    mat_stl.setTexture("ColorMap", tex_ml);
+    cube.setMaterial(mat_stl);
+    rootNode.attachChild(cube);
+ 
+    /** A translucent/transparent texture, similar to a window frame. */
+    Box boxshape3 = new Box(new Vector3f(0f,0f,0f), 1f,1f,0.01f);
+    Geometry window_frame = new Geometry("window frame", boxshape3);
+    Material mat_tt = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat_tt.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+    mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+    window_frame.setMaterial(mat_tt);
+ 
+    /** Objects with transparency need to be in the render bucket for transparent objects: */
+    window_frame.setQueueBucket(Bucket.Transparent);
+    rootNode.attachChild(window_frame);
+ 
+    /** A cube with base color "leaking" through a partially transparent texture */
+    Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);
+    Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4);
+    Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+    mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple
+    cube_leak.setMaterial(mat_tl);
+    rootNode.attachChild(cube_leak);
+ 
+    /** A bumpy rock with a shiny light effect */
+    Sphere rock = new Sphere(32,32, 2f);
+    Geometry shiny_rock = new Geometry("Shiny rock", rock);
+    rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
+    TangentBinormalGenerator.generate(rock);           // for lighting effect
+    Material mat_lit = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+    mat_lit.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
+    mat_lit.setTexture("NormalMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
+    mat_lit.setBoolean("UseMaterialColors",true);    
+    mat_lit.setColor("Specular",ColorRGBA.White);
+    mat_lit.setColor("Diffuse",ColorRGBA.White);
+    mat_lit.setFloat("Shininess", 5f); // [1,128]    
+    shiny_rock.setMaterial(mat_lit);
+    shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
+    shiny_rock.rotate(1.6f, 0, 0);          // Rotate it a bit
+    rootNode.attachChild(shiny_rock);
+ 
+    /** Must add a light to make the lit object visible! */
+    DirectionalLight sun = new DirectionalLight();
+    sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
+    sun.setColor(ColorRGBA.White);
+    rootNode.addLight(sun);
+  }
+}
+ +

+You should see +

+ + +

+Move around with the WASD keys to have a closer look at the translucency, and the rock's bumpiness. +

+ +
+ +

Simple Unshaded Texture

+
+ +

+ +Typically you want to give objects in your scene textures: It can be rock, grass, brick, wood, water, metal, paper??? A texture is a normal image file in JPG or PNG format. In this example, you create a box with a simple unshaded Monkey texture as material. +

+
    /** A simple textured cube. */
+    Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);
+    Geometry cube = new Geometry("My Textured Box", boxshape1);
+    Material mat_stl = new Material(assetManager, 
+        "Common/MatDefs/Misc/Unshaded.j3md");
+    Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
+    mat_stl.setTexture("ColorMap", tex_ml);
+    cube.setMaterial(mat_stl);
+    rootNode.attachChild(cube);
+ +

+Here is what we did: +

+
    +
  1. Create a Geometry from a Box mesh. Let's call it cube.
    +
  2. +
  3. Create a Material based on jME3's default Unshaded.j3md material definition.
    +
  4. +
  5. Create a texture from the Monkey.jpg file and load it into the material.
    +The ColorMap is the typical material layer where textures go.
    +
  6. +
  7. Apply the material to the cube, and attach the cube to the rootnode.
    +
  8. +
+ +
+ +

Transparent Unshaded Texture

+
+ +

+ +Monkey.png is the same texture as Monkey.jpg, but with an added alpha channel. The alpha channel allows you to specify which areas of the texture you want to be opaque or transparent: Black areas remain opaque, gray areas become translucent, and white areas become transparent. +

+ +

+For a partially translucent/transparent texture, you need: +

+ +
    /** A translucent/transparent texture. */
+    Box boxshape3 = new Box(new Vector3f(0f,0f,0f), 1f,1f,0.01f);
+    Geometry seethrough = new Geometry("see-through box", boxshape3);
+    Material mat_tt = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat_tt.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
+    mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); // activate transparency
+    seethrough.setMaterial(mat_tt);
+    seethrough.setQueueBucket(Bucket.Transparent);
+    rootNode.attachChild(seethrough);
+ +

+What you did is the same as before, with only one added step for the transparency. +

+
    +
  1. Create a Geometry from a mesh. This Geometry is flat upright box.
    +
  2. +
  3. Create a Material based on jME3's default Unshaded.j3md material definition.
    +
  4. +
  5. Create a texture from the Monkey.png file and load it into the material.
    +The ColorMap is the material layer where textures go. This PNG file must have an alpha layer.
    +
  6. +
  7. Activate transparency in the material by setting the blend mode to Alpha!
    +
  8. +
  9. Apply the material to the Geometry.
    +
  10. +
  11. Set the QueueBucket of the Geometry to Bucket.Transparent.
    +
  12. +
  13. Attach the cube to the rootnode.
    +
  14. +
+ +

+ +Tip: Learn more about creating PNG images with an alpha layer in the help system of your graphic editor. +

+ +
+ +

Shininess and Bumpiness

+
+ +

+ +But textures are not all. Have a close look at the shiny sphere ??? you cannot get such a nice bumpy material with just a texture. JME3 also supports so-called Phong-illuminated materials: +

+ +

+In a lit material, the standard texture layer is refered to as Diffuse Map, any material can use this layer. A lit material can additionally have lighting effects such as Shininess used together with the Specular Map layer, and even a realistically bumpy or cracked surface with help of the Normal Map layer. +

+ +

+Let's have a look at the part of the code example where you create the shiny bumpy rock. +

+
    +
  1. Create a Geometry from a Sphere shape. Note that this shape is a normal smooth sphere mesh.
        Sphere rock = new Sphere(32,32, 2f);
    +    Geometry shiny_rock = new Geometry("Shiny rock", rock);
    +
    +
      +
    1. (Only for Spheres) Change the sphere's TextureMode to make the square texture project better onto the sphere.
          rock.setTextureMode(Sphere.TextureMode.Projected); 
      +
      +
    2. +
    3. You generate TangentBinormals for the sphere mesh so you can use the NormalMap layer of the texture.
          TangentBinormalGenerator.generate(rock);
      +
      +
    4. +
    +
  2. +
  3. Create a material based on the Lighting.j3md default material.
        Material mat_lit = new Material(assetManager, 
    +    "Common/MatDefs/Light/Lighting.j3md");
    +
    +
      +
    1. Set a standard rocky texture in the DiffuseMap layer.
      +
          mat_lit.setTexture("DiffuseMap", assetManager.loadTexture(
      +    "Textures/Terrain/Pond/Pond.jpg"));
      +
      +
    2. +
    3. Set the NormalMap layer that contains the bumpiness. The NormalMap was generated for this particular DiffuseMap with a special tool (e.g. Blender).
          mat_lit.setTexture("NormalMap", assetManager.loadTexture(
      +    "Textures/Terrain/Pond/Pond_normal.png"));
      +
      +
    4. +
    5. Set the Material's Shininess to a value between 1 and 128. For a rock, a low fuzzy shininess is appropriate.
          mat_lit.setFloat("Shininess", 5f); // [1,128]
      +
      +
    6. +
    +
  4. +
  5. Assign your newly created material to the Geometry.
        shiny_rock.setMaterial(mat_lit);
    +
    +
  6. +
  7. Let's move and rotate the geometry a bit to position it better.
        shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
    +    shiny_rock.rotate(1.6f, 0, 0);          // Rotate it a bit
    +    rootNode.attachChild(shiny_rock);
    +
    +
  8. +
+ +

+ +Remember that any Lighting.j3md-based material requires a light source, as shown in the full code sample above. +

+ +

+Tip: To deactivate Shininess, do not set Shininess to 0, but instead set the Specular color to ColorRGBA.Black. +

+ +
+ +

Default Material Definitions

+
+ +

+ +As you have seen, you can find the following default materials in jme/core-data/Common/???. + +

+
+ + + + + + + + + +
Default Definition Usage Parameters
Common/MatDefs/Misc/Unshaded.j3md Colored: Use with mat.setColor() and ColorRGBA.
+Textured: Use with mat.setTexture() and Texture.
Color : Color
+ColorMap : Texture2D
Common/MatDefs/Light/Lighting.j3md Use with shiny Textures, Bump- and NormalMaps textures.
+Requires a light source.
Ambient, Diffuse, Specular : Color
+DiffuseMap, NormalMap, SpecularMap : Texture2D
+Shininess : Float
+ +

+ +For a game, you create custom Materials based on these existing MaterialDefintions ??? as you have just seen in the example with the shiny rock's material. +

+ +
+ +

Exercises

+
+ +
+ +

Exercise 1: Custom .j3m Material

+
+ +

+ +Look at the purple leak-through sample above again. It takes four lines to create and set the Material. +

+ + +

+If you want to use one custom material for several models, you can store it in a .j3m file, and save a few lines of code every time. +You create a j3m file as follows: +

+
    +
  1. Create a file assets/Materials/LeakThrough.j3m in your project directory, with the following content:
    Material Leak Through : Common/MatDefs/Misc/Unshaded.j3md {
    +     MaterialParameters {
    +         Color : 1 0 1 1
    +         ColorMap : Flip Textures/ColoredTex/Monkey.png
    +     }
    +}
    +
    +
      +
    • Note that Material is a fixed keyword.
      +
    • +
    • Note that Leak Through is a String that you can choose to name the material.
      +
    • +
    • Note how the code sets the same three properties, Color, ColorMap, and Unshaded.j3md.
      +
    • +
    +
  2. +
  3. In the code sample, comment out the three lines with mat_tl in them.
    +
  4. +
  5. Below them, add the following line:
    cube_leak.setMaterial((Material) assetManager.loadMaterial( "Materials/LeakThrough.j3m"));
    +
    +
  6. +
  7. Run the app. The result is the same.
    +
  8. +
+ +

+ +Using this new custom material LeakThrough.j3m only takes one line. You have replaced the three lines of an on-the-fly material definition with one line that loads a custom material from a file. This method is very handy if you use the same material often. +

+ +
+ +

Exercise 2: Bumpiness and Shininess

+
+ +

+ +Go back to the bumpy rock sample above: +

+
    +
  1. Comment out the DiffuseMap line, and run the app. (Uncomment it again.)
    +
      +
    • Which property of the rock is lost?
      +
    • +
    +
  2. +
  3. Comment out the NormalMap line, and run the app. (Uncomment it again.)
    +
      +
    • Which property of the rock is lost?
      +
    • +
    +
  4. +
  5. Change the value of Shininess to values like 0, 63, 127.
    +
      +
    • What aspect of the Shininess changes?
      +
    • +
    +
  6. +
+ +
+ +

Conclusion

+
+ +

+ +You have learned how to create a Material, specify its properties, and use it on a Geometry. You know how to load an image file (.png, .jpg) as texture into a material. You know to save texture files in a subfolder of your project's assets/Textures/ directory. +

+ +

+You have also learned that a material can be stored in a .j3m file. The file references a built-in MaterialDefinition and specifies values for properties of that MaterialDefinition. You know to save your custom .j3m files in your project's assets/Materials/ directory. +

+ +

+Now that you know how to load models and how to assign good-looking materials to them, let's have a look at how to animate models in the next chapter, Hello Animation. + +

+
+ +

+See also +

+ +
+ documentation, + beginner, + intro, + model, + material, + color, + texture, + transparency +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html new file mode 100644 index 000000000..34adcb913 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html @@ -0,0 +1,449 @@ + +

jMonkeyEngine 3 Tutorial (2) - Hello Node

+
+ +

+ +Previous: Hello SimpleApplication, +Next: Hello Assets. +

+ +

+In this tutorial we will have a look at the creation of a 3D scene. +

+ + +

+ +When creating a 3D game +

+
    +
  1. You create some scene objects like players, buildings, etc.
    +
  2. +
  3. You add the objects to the scene.
    +
  4. +
  5. You move, resize, rotate, color, and animate them.
    +
  6. +
+ +

+ +You will learn that the scene graph represents the 3D world, and why the rootNode is important. You will learn how to create simple objects, how to let them carry custom data (such as health points), and how to "transform" them by moving, scaling, and rotating. You will understand the difference between the two types of "Spatials" in the scene graph: Nodes and Geometries. +

+ +
+ +

Code Sample

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Node;
+ 
+/** Sample 2 - How to use nodes as handles to manipulate objects in the scene.
+ * You can rotate, translate, and scale objects by manipulating their parent nodes.
+ * The Root Node is special: Only what is attached to the Root Node appears in the scene. */
+ 
+public class HelloNode extends SimpleApplication {
+ 
+    public static void main(String[] args){
+        HelloNode app = new HelloNode();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+ 
+        /** create a blue box at coordinates (1,-1,1) */
+        Box box1 = new Box( Vector3f.ZERO, 1,1,1);
+        Geometry blue = new Geometry("Box", box1);
+        Material mat1 = new Material(assetManager, 
+                "Common/MatDefs/Misc/Unshaded.j3md");
+        mat1.setColor("Color", ColorRGBA.Blue);
+        blue.setMaterial(mat1);
+        blue.move(1,-1,1);
+ 
+        /** create a red box straight above the blue one at (1,3,1) */
+        Box box2 = new Box( Vector3f.ZERO, 1,1,1);
+        Geometry red = new Geometry("Box", box2);
+        Material mat2 = new Material(assetManager, 
+                "Common/MatDefs/Misc/Unshaded.j3md");
+        mat2.setColor("Color", ColorRGBA.Red);
+        red.setMaterial(mat2);
+        red.move(1,3,1);
+ 
+        /** Create a pivot node at (0,0,0) and attach it to the root node */
+        Node pivot = new Node("pivot");
+        rootNode.attachChild(pivot); // put this node in the scene
+ 
+        /** Attach the two boxes to the *pivot* node. */
+        pivot.attachChild(blue);
+        pivot.attachChild(red);
+        /** Rotate the pivot node: Note that both boxes have rotated! */
+        pivot.rotate(.4f,.4f,0f);
+    }
+}
+ +

+Build and run the code sample. You should see two colored boxes tilted at the same angle. +

+ +
+ +

Understanding the Terminology

+
+ +

+ +In this tutorial, you learn some new terms: + +

+
+ + + + + + + + + + + + + + + + + + +
What you want to doHow you say it in JME3 terminology
Lay out the 3D scenePopulate the scene graph
Create scene objectsCreate Spatials (e.g. create Geometries)
Make an object appear in the sceneAttach a Spatial to the rootNode
Make an object disappear from the sceneDetach the Spatial from the rootNode
Position/move, turn, or resize an objectTranslate, or rotate, or scale an object = transform an object.
+ +

+ +Every JME3 application has a rootNode: Your game automatically inherits the rootNode object from SimpleApplication. Everything attached to the rootNode is part of the scene graph. The elements of the scene graph are Spatials. +

+ +
+ + + + + + + + + + + + +
Geometry Node
Visibility: A Geometry is a visible scene object. A Node is an invisible "handle" for scene objects.
Purpose: A Geometry stores an object's looks. A Node groups Geometries and other Nodes together.
Examples: A box, a sphere, a player, a building, a piece of terrain, a vehicle, missiles, NPCs, etc??? The rootNode, a floor node grouping several terrains, a custom vehicle-with-passengers node, a player-with-weapon node, an audio node, etc???
+ +
+ +

Understanding the Code

+
+ +

+ +What happens in the code snippet? You use the simpleInitApp() method that was introduced in the first tutorial to initialize the scene. + +

+
    +
  1. You create the first box Geometry.
    +
      +
    • Create a Box shape with extents of (1,1,1), that makes the box 2x2x2 world units big.
      +
    • +
    • Position the box at (1,-1,1) using the move() method. (Don't change the Vector3f.ZERO unless you want to change the center of rotation)
      +
    • +
    • Wrap the Box shape into a Geometry.
      +
    • +
    • Create a blue material.
      +
    • +
    • Apply the blue material to the Box Geometry.
          Box box1 = new Box( Vector3f.ZERO, 1,1,1);
      +    Geometry blue = new Geometry("Box", box1);
      +    Material mat1 = new Material(assetManager,
      +      "Common/MatDefs/Misc/Unshaded.j3md");
      +    mat1.setColor("Color", ColorRGBA.Blue);
      +    blue.setMaterial(mat1);
      +    blue.move(1,-1,1);
      +
      +
    • +
    +
  2. +
  3. You create a second box Geometry.
    +
      +
    • Create a second Box shape with the same size.
      +
    • +
    • Position the second box at (1,3,1). This is straight above the first box, with a gap of 2 world units inbetween.
      +
    • +
    • Wrap the Box shape into a Geometry.
      +
    • +
    • Create a red material.
      +
    • +
    • Apply the red material to the Box Geometry.
          Box box2 = new Box( Vector3f.ZERO, 1,1,1);
      +    Geometry red = new Geometry("Box", box2);
      +    Material mat2 = new Material(assetManager,
      +      "Common/MatDefs/Misc/Unshaded.j3md");
      +    mat2.setColor("Color", ColorRGBA.Red);
      +    red.setMaterial(mat2);
      +    red.move(1,3,1);
      +
      +
    • +
    +
  4. +
  5. You create a pivot Node.
    +
      +
    • Name the Node "pivot".
      +
    • +
    • By default the Node is positioned at (0,0,0).
      +
    • +
    • Attach the Node to the rootNode.
      +
    • +
    • The Node has no visible appearance in the scene.
          Node pivot = new Node("pivot");
      +    rootNode.attachChild(pivot);
      + +

      +If you run the application with only the code up to here, the scene appears empty. This is because a Node is invisible, and you have not yet attached any visible Geometries to the rootNode. +

      +
      +
    • +
    +
  6. +
  7. Attach the two boxes to the pivot node.
            pivot.attachChild(blue);
    +        pivot.attachChild(red);
    + +

    +If you run the app with only the code up to here, you see two cubes: A red cube straight above a blue cube. +

    +
    +
  8. +
  9. Rotate the pivot node.
            pivot.rotate( 0.4f , 0.4f , 0.0f );
    + +

    + If you run the app now, you see two boxes on top of each other ??? both tilted at the same angle. +

    +
    +
  10. +
+ +
+ +

What is a Pivot Node?

+
+ +

+ +You can transform (e.g. rotate) Geometries around their own center, or around a user defined center point. A user defined center point for one or more Geometries is called pivot. + +

+ + +
+ +

How do I Populate the Scenegraph?

+
+
+ + + + + + + + + + + + + + + + + + +
Task???? Solution!
Create a Spatial Create a Mesh shape, wrap it into a Geometry, and give it a Material. For example:
Box mesh = new Box(Vector3f.ZERO, 1, 1, 1); // a cuboid default mesh
+Geometry thing = new Geometry("thing", mesh); 
+Material mat = new Material(assetManager,
+   "Common/MatDefs/Misc/ShowNormals.j3md");
+thing.setMaterial(mat);
+
Make an object appear in the scene Attach the Spatial to the rootNode, or to any node that is attached to the rootNode.
rootNode.attachChild(thing);
+
Remove objects from the scene Detach the Spatial from the rootNode, and from any node that is attached to the rootNode.
rootNode.detachChild(thing);
+
rootNode.detachAllChildren();
+
Find a Spatial in the scene by the object's name, or ID, or by its position in the parent-child hierarchy. Look at the node's children or parent:
Spatial thing = rootNode.getChild("thing");
+
Spatial twentyThird = rootNode.getChild(22);
+
Spatial parent = myNode.getParent();
+
Specify what should be loaded at the start Everything you initialize and attach to the rootNode in the simpleInitApp() method is part of the scene at the start of the game.
+ +
+ +

How do I Transform Spatials?

+
+ +

+ +There are three types of 3D transformation: Translation, Scaling, and Rotation. + +

+
+ + + + + + +
Translation moves Spatials X-axis Y-axis Z-axis
Specify the new location in three dimensions: How far away is it from the origin going right-up-forward?
+To move a Spatial to specific coordinates, such as (0,40.2f,-2), use:
thing.setLocalTranslation( new Vector3f( 0.0f, 40.2f, -2.0f ) );
+ +

+ To move a Spatial by a certain amount, e.g. higher up (y=40.2f) and further back (z=-2.0f): +

+
thing.move( 0.0f, 40.2f, -2.0f );
+
+right -left+up -down+forward -backward
+
+ + + + + + +
Scaling resizes Spatials X-axis Y-axis Z-axis
Specify the scaling factor in each dimension: length, height, width.
+A value between 0.0f and 1.0f shrinks the Spatial; bigger than 1.0f stretches it; 1.0f keeps it the same.
+Using the same value for each dimension scales proportionally, different values stretch it.
+To scale a Spatial 10 times longer, one tenth the height, and keep the same width:
thing.scale( 10.0f, 0.1f, 1.0f );
+
lengthheightwidth
+
+ + + + + + +
Rotation turns Spatials X-axis Y-axis Z-axis
3-D rotation is a bit tricky (learn details here). In short: You can rotate around three axes: Pitch, yaw, and roll. You can specify angles in degrees by multiplying the degrees value with FastMath.DEG_TO_RAD.
+To roll an object 180?? around the z axis:
thing.rotate( 0f , 0f , 180*FastMath.DEG_TO_RAD );
+ +

+ Tip: If your game idea calls for a serious amount of rotations, it is worth looking into quaternions, a data structure that can combine and store rotations efficiently. +

+
thing.setLocalRotation( 
+  new Quaternion().fromAngleAxis(180*FastMath.DEG_TO_RAD, new Vector3f(1,0,0)));
+
pitch = nodding your headyaw = shaking your headroll = cocking your head
+ +
+ +

How do I Troubleshoot Spatials?

+
+ +

+ +If you get unexpected results, check whether you made the following common mistakes: + +

+
+ + + + + + + + + + + + +
Problem? Solution!
A created Geometry does not appear in the scene. Have you attached it to (a node that is attached to) the rootNode?
+Does it have a Material?
+What is its translation (position)? Is it behind the camera or covered up by another Geometry?
+Is it to tiny or too gigantic to see?
+Is it too far from the camera? (Try (111111f); to see further)
A Spatial rotates in unexpected ways. Did you use radian values, and not degrees? (If you used degrees, multiply them with FastMath.DEG_TO_RAD to convert them to radians)
+Did you create the Spatial at the origin (Vector.ZERO) before moving it?
+Did you rotate around the intended pivot node or around something else?
+Did you rotate around the right axis?
A Geometry has an unexpected Color or Material. Did you reuse a Material from another Geometry and have inadvertently changed its properties? (If so, consider cloning it: mat2 = mat.clone(); )
+ +
+ +

How do I Add Custom Data to Spatials?

+
+ +

+ +Many Spatials represent game characters or other entities that the player can interact with. The above code that rotates the two boxes around a common center (pivot) could be used for a spacecraft docked to a orbiting space station, for example. +

+ +

+Depending on your game, game entities do not only change their position, rotation, or scale (the transformations that you just learned about). Game entities also have custom properties, such as health, inventory carried, equipment worn for a character, or hull strength and fuel left for a spacecraft. In Java, you represent entity data as class variables, e.g. floats, Strings, or Arrays. +

+ +

+You can add custom data directly to any Node or Geometry. You do not need to extend the Node class to include variables! +For example, to add a custom id number to a node, you would use: + +

+
pivot.setUserData( "pivot id", 42 );
+ +

+ +To read this Node's id number elsewhere, you would use: + +

+
int id = pivot.getUserData( "pivot id" ); 
+ +

+ +By using different Strings keys (here the key is pivot id), you can get and set several values for whatever data the Spatial needs to carry. When you start writing your game, you might add a fuel value to a car node, speed value to an airplane node, or number of gold coins to a player node, and much more. However, one should note that only custom objects that implements Savable can be passed. +

+ +
+ +

Conclusion

+
+ +

+ +You have learned that your 3D scene is a scene graph made up of Spatials: Visible Geometries and invisible Nodes. You can transform Spatials, or attach them to nodes and transform the nodes. You know the easiest way how to add custom entity properties (such as player health or vehicle speed) to Spatials. +

+ +

+Since standard shapes like spheres and boxes get old fast, continue with the next chapter where you learn to load assets such as 3-D models. +

+
+ beginner, + rootNode, + node, + intro, + documentation, + color, + spatial, + geometry, + scenegraph, + mesh +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html new file mode 100644 index 000000000..4e1ba5bd5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html @@ -0,0 +1,653 @@ + +

jMonkeyEngine 3 Tutorial (13) - Hello Physics

+
+ +

+Previous: Hello Effects, +Next: JME 3 documentation +

+ +

+Do you remember the Hello Collision tutorial where you made the model of a town solid and walked through it in a first-person perspective? Then you may remember that, for the simulation of physical forces, jME3 integrates the library. +

+ +

+Apart from making models "solid", the most common use cases for physics in 3D games are: +

+ + +

+ +All these physical properties can be simulated in JME3. Let's have a look at a simulation of physical forces in this example where you shoot cannon balls at a brick wall. +

+ +

+ +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+

+ +
+ +

Sample Code

+
+ +

+Thanks to double1984 for contributing this fun sample! + +

+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
+import com.jme3.bullet.BulletAppState;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+ 
+/**
+ * Example 12 - how to give objects physical properties so they bounce and fall.
+ * @author base code by double1984, updated by zathras
+ */
+public class HelloPhysics extends SimpleApplication {
+ 
+  public static void main(String args[]) {
+    HelloPhysics app = new HelloPhysics();
+    app.start();
+  }
+ 
+  /** Prepare the Physics Application State (jBullet) */
+  private BulletAppState bulletAppState;
+ 
+  /** Prepare Materials */
+  Material wall_mat;
+  Material stone_mat;
+  Material floor_mat;
+ 
+  /** Prepare geometries and physical nodes for bricks and cannon balls. */
+  private RigidBodyControl    brick_phy;
+  private static final Box    box;
+  private RigidBodyControl    ball_phy;
+  private static final Sphere sphere;
+  private RigidBodyControl    floor_phy;
+  private static final Box    floor;
+ 
+  /** dimensions used for bricks and wall */
+  private static final float brickLength = 0.48f;
+  private static final float brickWidth  = 0.24f;
+  private static final float brickHeight = 0.12f;
+ 
+  static {
+    /** Initialize the cannon ball geometry */
+    sphere = new Sphere(32, 32, 0.4f, true, false);
+    sphere.setTextureMode(TextureMode.Projected);
+    /** Initialize the brick geometry */
+    box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
+    box.scaleTextureCoordinates(new Vector2f(1f, .5f));
+    /** Initialize the floor geometry */
+    floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+    floor.scaleTextureCoordinates(new Vector2f(3, 6));
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    /** Set up Physics Game */
+    bulletAppState = new BulletAppState();
+    stateManager.attach(bulletAppState);
+    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ 
+    /** Configure cam to look at scene */
+    cam.setLocation(new Vector3f(0, 4f, 6f));
+    cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);
+    /** Add InputManager action: Left click triggers shooting. */
+    inputManager.addMapping("shoot", 
+            new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+    inputManager.addListener(actionListener, "shoot");
+    /** Initialize the scene, materials, and physics space */
+    initMaterials();
+    initWall();
+    initFloor();
+    initCrossHairs();
+  }
+ 
+  /**
+   * Every time the shoot action is triggered, a new cannon ball is produced.
+   * The ball is set up to fly from the camera position in the camera direction.
+   */
+  private ActionListener actionListener = new ActionListener() {
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("shoot") && !keyPressed) {
+        makeCannonBall();
+      }
+    }
+  };
+ 
+  /** Initialize the materials used in this scene. */
+  public void initMaterials() {
+    wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
+    key.setGenerateMips(true);
+    Texture tex = assetManager.loadTexture(key);
+    wall_mat.setTexture("ColorMap", tex);
+ 
+    stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+    key2.setGenerateMips(true);
+    Texture tex2 = assetManager.loadTexture(key2);
+    stone_mat.setTexture("ColorMap", tex2);
+ 
+    floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
+    key3.setGenerateMips(true);
+    Texture tex3 = assetManager.loadTexture(key3);
+    tex3.setWrap(WrapMode.Repeat);
+    floor_mat.setTexture("ColorMap", tex3);
+  }
+ 
+  /** Make a solid floor and add it to the scene. */
+  public void initFloor() {
+    Geometry floor_geo = new Geometry("Floor", floor);
+    floor_geo.setMaterial(floor_mat);
+    floor_geo.setLocalTranslation(0, -0.1f, 0);
+    this.rootNode.attachChild(floor_geo);
+    /* Make the floor physical with mass 0.0f! */
+    floor_phy = new RigidBodyControl(0.0f);
+    floor_geo.addControl(floor_phy);
+    bulletAppState.getPhysicsSpace().add(floor_phy);
+  }
+ 
+  /** This loop builds a wall out of individual bricks. */
+  public void initWall() {
+    float startpt = brickLength / 4;
+    float height = 0;
+    for (int j = 0; j < 15; j++) {
+      for (int i = 0; i < 6; i++) {
+        Vector3f vt =
+         new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 0);
+        makeBrick(vt);
+      }
+      startpt = -startpt;
+      height += 2 * brickHeight;
+    }
+  }
+ 
+  /** This method creates one individual physical brick. */
+  public void makeBrick(Vector3f loc) {
+    /** Create a brick geometry and attach to scene graph. */
+    Geometry brick_geo = new Geometry("brick", box);
+    brick_geo.setMaterial(wall_mat);
+    rootNode.attachChild(brick_geo);
+    /** Position the brick geometry  */
+    brick_geo.setLocalTranslation(loc);
+    /** Make brick physical with a mass > 0.0f. */
+    brick_phy = new RigidBodyControl(2f);
+    /** Add physical brick to physics space. */
+    brick_geo.addControl(brick_phy);
+    bulletAppState.getPhysicsSpace().add(brick_phy);
+  }
+ 
+  /** This method creates one individual physical cannon ball.
+   * By defaul, the ball is accelerated and flies
+   * from the camera position in the camera direction.*/
+   public void makeCannonBall() {
+    /** Create a cannon ball geometry and attach to scene graph. */
+    Geometry ball_geo = new Geometry("cannon ball", sphere);
+    ball_geo.setMaterial(stone_mat);
+    rootNode.attachChild(ball_geo);
+    /** Position the cannon ball  */
+    ball_geo.setLocalTranslation(cam.getLocation());
+    /** Make the ball physcial with a mass > 0.0f */
+    ball_phy = new RigidBodyControl(1f);
+    /** Add physical ball to physics space. */
+    ball_geo.addControl(ball_phy);
+    bulletAppState.getPhysicsSpace().add(ball_phy);
+    /** Accelerate the physcial ball to shoot it. */
+    ball_phy.setLinearVelocity(cam.getDirection().mult(25));
+  }
+ 
+  /** A plus sign used as crosshairs to help the player with aiming.*/
+  protected void initCrossHairs() {
+    guiNode.detachAllChildren();
+    guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+    BitmapText ch = new BitmapText(guiFont, false);
+    ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+    ch.setText("+");        // fake crosshairs :)
+    ch.setLocalTranslation( // center
+      settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+      settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+    guiNode.attachChild(ch);
+  }
+}
+ +

+You should see a brick wall. Click to shoot cannon balls. Watch the bricks fall and bounce off one another! +

+ +
+ +

A Basic Physics Application

+
+ +

+ +In the previous tutorials, you used static Geometries (boxes, spheres, and models) that you placed in the scene. Depending on their translation, Geometries can "float in mid-air" and even overlap ??? they are not affected by "gravity" and have no physical mass. This tutorial shows how to add physical properties to Geometries. +

+ +

+As always, start with a standard com.jme3.app.SimpleApplication. To activate physics, create a com.jme3.bullet.BulletAppState, and and attach it to the SimpleApplication's AppState manager. +

+
public class HelloPhysics extends SimpleApplication {
+  private BulletAppState bulletAppState;
+ 
+  public void simpleInitApp() {
+    bulletAppState = new BulletAppState();
+    stateManager.attach(bulletAppState);
+    ...
+  }
+  ...
+}
+ +

+The BulletAppState gives the game access to a PhysicsSpace. The PhysicsSpace lets you use com.jme3.bullet.control.PhysicsControls that add physical properties to Nodes. +

+ +
+ +

Creating Bricks and Cannon Balls

+
+ +
+ +

Geometries

+
+ +

+ +In this "shoot at the wall" example, you use Geometries such as cannon balls and bricks. Geometries contain meshes, such as Shapes. Let's create and initialize some Shapes: Boxes and Spheres. +

+
  /** Prepare geometries and physical nodes for bricks and cannon balls. */
+  private static final Box    box;
+  private static final Sphere sphere;
+  private static final Box    floor;
+  /** dimensions used for bricks and wall */
+  private static final float brickLength = 0.48f;
+  private static final float brickWidth  = 0.24f;
+  private static final float brickHeight = 0.12f;
+  static {
+    /** Initialize the cannon ball geometry */
+    sphere = new Sphere(32, 32, 0.4f, true, false);
+    sphere.setTextureMode(TextureMode.Projected);
+    /** Initialize the brick geometry */
+    box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
+    box.scaleTextureCoordinates(new Vector2f(1f, .5f));
+    /** Initialize the floor geometry */
+    floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
+    floor.scaleTextureCoordinates(new Vector2f(3, 6));
+  }
+ +
+ +

RigidBodyControl: Brick

+
+ +

+ +We want to create brick Geometries from those boxes. For each Geometry with physcial properties, you create a RigidBodyControl. +

+
  private RigidBodyControl brick_phy;
+ +

+The custom makeBrick(loc) methods creates individual bricks at the location loc. A brick has the following properties: +

+ +
  public void makeBrick(Vector3f loc) {
+    /** Create a brick geometry and attach to scene graph. */
+    Geometry brick_geo = new Geometry("brick", box);
+    brick_geo.setMaterial(wall_mat);
+    rootNode.attachChild(brick_geo);
+    /** Position the brick geometry  */
+    brick_geo.setLocalTranslation(loc);
+    /** Make brick physical with a mass > 0.0f. */
+    brick_phy = new RigidBodyControl(2f);
+    /** Add physical brick to physics space. */
+    brick_geo.addControl(brick_phy);
+    bulletAppState.getPhysicsSpace().add(brick_phy);
+  }
+ +

+This code sample does the following: +

+
    +
  1. You create a brick Geometry brick_geo. A Geometry describes the shape and look of an object.
    +
      +
    • brick_geo has a box shape
      +
    • +
    • brick_geo has a brick-colored material.
      +
    • +
    +
  2. +
  3. You attach brick_geo to the rootNode
    +
  4. +
  5. You position brick_geo at loc.
    +
  6. +
  7. You create a RigidBodyControl brick_phy for brick_geo.
    +
      +
    • brick_phy has a mass of 2f.
      +
    • +
    • You add brick_phy to brick_geo.
      +
    • +
    • You register brick_phy to the PhysicsSpace.
      +
    • +
    +
  8. +
+ +
+ +

RigidBodyControl: Cannonball

+
+ +

+ +You notice that the cannon ball is created in the same way, using the custom makeCannonBall() method. The cannon ball has the following properties: +

+ +
    /** Create a cannon ball geometry and attach to scene graph. */
+    Geometry ball_geo = new Geometry("cannon ball", sphere);
+    ball_geo.setMaterial(stone_mat);
+    rootNode.attachChild(ball_geo);
+    /** Position the cannon ball  */
+    ball_geo.setLocalTranslation(cam.getLocation());
+    /** Make the ball physcial with a mass > 0.0f */
+    ball_phy = new RigidBodyControl(1f);
+    /** Add physical ball to physics space. */
+    ball_geo.addControl(ball_phy);
+    bulletAppState.getPhysicsSpace().add(ball_phy);
+    /** Accelerate the physcial ball to shoot it. */
+    ball_phy.setLinearVelocity(cam.getDirection().mult(25));
+ 
+ +

+This code sample does the following: +

+
    +
  1. You create a ball Geometry ball_geo. A Geometry describes the shape and look of an object.
    +
      +
    • ball_geo has a sphere shape
      +
    • +
    • ball_geo has a stone-colored material.
      +
    • +
    +
  2. +
  3. You attach ball_geo to the rootNode
    +
  4. +
  5. You position ball_geo at the camera location.
    +
  6. +
  7. You create a RigidBodyControl ball_phy for ball_geo.
    +
      +
    • ball_phy has a mass of 1f.
      +
    • +
    • You add ball_phy to ball_geo.
      +
    • +
    • You register ball_phy to the PhysicsSpace.
      +
    • +
    +
  8. +
+ +

+ +Since you are shooting cannon balls, the last line accelerates the ball in the direction the camera is looking, with a speed of 25f. +

+ +
+ +

RigidBodyControl: Floor

+
+ +

+ +The (static) floor has one important difference compared to the (dynamic) bricks and cannonballs: Static objects have a mass of zero. +As before, you write a custom initFloor() method that creates a flat box with a rock texture that you use as floor. The floor has the following properties: +

+ +
  public void initFloor() {
+    Geometry floor_geo = new Geometry("Floor", floor);
+    floor_geo.setMaterial(floor_mat);
+    floor_geo.setLocalTranslation(0, -0.1f, 0);
+    this.rootNode.attachChild(floor_geo);
+    /* Make the floor physical with mass 0.0f! */
+    floor_phy = new RigidBodyControl(0.0f);
+    floor_geo.addControl(floor_phy);
+    bulletAppState.getPhysicsSpace().add(floor_phy);
+  }
+ +

+This code sample does the following: +

+
    +
  1. You create a floor Geometry floor_geo. A Geometry describes the shape and look of an object.
    +
      +
    • floor_geo has a box shape
      +
    • +
    • floor_geo has a pebble-colored material.
      +
    • +
    +
  2. +
  3. You attach floor_geo to the rootNode
    +
  4. +
  5. You position floor_geo a bit below y=0 (to prevent overlap with other PhysicControl'ed Spatials).
    +
  6. +
  7. You create a RigidBodyControl floor_phy for floor_geo.
    +
      +
    • floor_phy has a mass of 0f :!:
      +
    • +
    • You add floor_phy to floor_geo.
      +
    • +
    • You register floor_phy to the PhysicsSpace.
      +
    • +
    +
  8. +
+ +
+ +

Creating the Scene

+
+ +

+ +Let's have a quick look at the custom helper methods: +

+ + +

+ +These methods are each called once from the simpleInitApp() method at the start of the game. As you see, you can write any number of custom methods to set up your game's scene. +

+ +
+ +

The Cannon Ball Shooting Action

+
+ +

+ +In the initInputs() method, you add an input mapping that triggers a shoot action when the left mouse button is pressed. +

+
  private void initInputs() {
+    inputManager.addMapping("shoot", 
+            new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+    inputManager.addListener(actionListener, "shoot");
+  }
+ +

+You define the actual action of shooting a new cannon ball as follows: +

+
    private ActionListener actionListener = new ActionListener() {
+        public void onAction(String name, boolean keyPressed, float tpf) {
+            if (name.equals("shoot") && !keyPressed) {
+                makeCannonBall();
+            }
+        }
+    };
+ +

+In the moment the cannonball appears in the scene, it flies off with the velocity (and in the direction) that you specified using setLinearVelocity() inside makeCannonBall(). The newly created cannon ball flies off, hits the wall, and exerts a physical force that impacts individual bricks. +

+ +
+ +

Moving a Physical Spatial

+
+ +

+ +The location of the dynamic Spatial is controlled by its RigidBodyControl. Move the RigidBodyControl to move the Spatial. If it's a dynamic PhysicsControl, you can use setLinearVelocity() and apply forces and torques to it. Other RigidBodyControl'led objects can push the dynamic Spatial around (like pool billard balls). +

+ +

+You can make Spatials that are not dynamic: Switch the RigidBodyControl to setKinematic(true) to have it move along with its Spatial. +

+ + +

+ +Learn more about static versus kinematic versus dynamic in the advanced physics doc. +

+ +
+ +

Excercises

+
+ +
+ +

Exercise 1: Debug Shapes

+
+ +

+ +Add the following line after the bulletAppState initialization. + +

+
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ +

+ +Now you see the collisionShapes of the bricks and spheres, and the floor highlighted. +

+ +
+ +

Exercise 2: No Mo' Static

+
+ +

+ +What happens if you give a static node, such as the floor, a mass of more than 0.0f? +

+ +
+ +

Exercise 3: Behind the Curtain

+
+ +

+ +Fill your scene with walls, bricks, and cannon balls. When do you begin to see a performance impact? +

+ +

+Popular AAA games use a clever mix of physics, animation and prerendered graphics to give you the illusion of a real, "physical" world. Think of your favorite video games and try to spot where and how the game designers trick you into believing that the whole scene is physical. For example, think of a building "breaking" into 4-8 parts after an explosion. The pieces most likely fly on predefined (so called kinematic) paths and are only replaced by dynamic Spatials after they touch the ground??? Now that you start to implement game physics yourself, look behind the curtain! +

+ +

+Using physics everywhere in a game sounds like a cool idea, but it is easily overused. Although the physics nodes are put to "sleep" when they are not moving, creating a world solely out of dynamic physics nodes will quickly bring you to the limits of your computer's capabilities. +

+ +
+ +

Conclusion

+
+ +

+ +You have learned how to activate the jBullet PhysicsSpace in an application by adding a BulletAppState. You have created PhysicsControls for simple Shape-based Geometries (for more complex shapes, read up on CollisionShapes). You have learned that physical objects are not only attached to the rootNode, but also registered to the PhysicsSpace. You know that it makes a difference whether a physical object has a mass (dynamic) or not (static). You are aware that overusing physics has a huge performance impact. +

+ +

+

Congratulations! ??? You have completed the last beginner tutorial. Now you are ready to start combining what you have learned, to create a cool 3D game of your own. Show us what you can do, and feel free to share your demos, game videos, and screenshots on the ! +

+

+
+ beginner, + intro, + physics, + documentation, + input, + model, + control +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html new file mode 100644 index 000000000..186660d47 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html @@ -0,0 +1,537 @@ + +

jMonkeyEngine 3 Tutorial (8) - Hello Picking

+
+ +

+ +Previous: Hello Animation, +Next: Hello Collision +

+ +

+Typical interactions in games include shooting, picking up objects, and opening doors. From an implementation point of view, these apparently different interactions are surprisingly similar: The user first aims and selects a target in the 3D scene, and then triggers an action on it. We call this process picking. +

+ +

+You can pick something by either pressing a key on the keyboard, or by clicking with the mouse. In either case, you identify the target by aiming a ray ???a straight line??? into the scene. This method to implement picking is called ray casting (which is not the same as ray tracing). +

+ +

+This tutorial relies on what you have learned in the Hello Input tutorial. You find more related code samples under Mouse Picking and Collision and Intersection. +

+ +

+ +

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
+import com.jme3.font.BitmapText;
+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.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+ 
+/** Sample 8 - how to let the user pick (select) objects in the scene 
+ * using the mouse or key presses. Can be used for shooting, opening doors, etc. */
+public class HelloPicking extends SimpleApplication {
+ 
+  public static void main(String[] args) {
+    HelloPicking app = new HelloPicking();
+    app.start();
+  }
+  Node shootables;
+  Geometry mark;
+ 
+  @Override
+  public void simpleInitApp() {
+    initCrossHairs(); // a "+" in the middle of the screen to help aiming
+    initKeys();       // load custom key mappings
+    initMark();       // a red sphere to mark the hit
+ 
+    /** create four colored boxes and a floor to shoot at: */
+    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));
+    shootables.attachChild(makeFloor());
+  }
+ 
+  /** Declaring the "Shoot" action and mapping to its triggers. */
+  private void initKeys() {
+    inputManager.addMapping("Shoot",
+      new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
+      new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // trigger 2: left-button click
+    inputManager.addListener(actionListener, "Shoot");
+  }
+  /** Defining the "Shoot" action: Determine what was hit and how to respond. */
+  private ActionListener actionListener = new ActionListener() {
+ 
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Shoot") && !keyPressed) {
+        // 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.
+        shootables.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);
+        }
+      }
+    }
+  };
+ 
+  /** A cube object for target practice */
+  protected Geometry makeCube(String name, float x, float y, float z) {
+    Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);
+    Geometry cube = new Geometry(name, box);
+    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat1.setColor("Color", ColorRGBA.randomColor());
+    cube.setMaterial(mat1);
+    return cube;
+  }
+ 
+  /** A floor to show that the "shot" can go through several objects. */
+  protected Geometry makeFloor() {
+    Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15);
+    Geometry floor = new Geometry("the Floor", box);
+    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat1.setColor("Color", ColorRGBA.Gray);
+    floor.setMaterial(mat1);
+    return floor;
+  }
+ 
+  /** A red ball that marks the last spot that was "hit" by the "shot". */
+  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);
+  }
+ 
+  /** A centred plus sign to help the player aim. */
+  protected void initCrossHairs() {
+    guiNode.detachAllChildren();
+    guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+    BitmapText ch = new BitmapText(guiFont, false);
+    ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+    ch.setText("+"); // crosshairs
+    ch.setLocalTranslation( // center
+      settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+      settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+    guiNode.attachChild(ch);
+  }
+}
+ +

+You should see four colored cubes floating over a gray floor, and cross-hairs. Aim the cross-hairs and click, or press the spacebar to shoot. The hit spot is marked with a red dot. +

+ +

+Keep an eye on the application's output stream, it will give you more details: The name of the mesh that was hit, the coordinates of the hit, and the distance. +

+ +
+ +

Understanding the Helper Methods

+
+ +

+ +The methods makeCube(), makeFloor(), initMark(), and initCrossHairs, are custom helper methods. We call them from simpleInitApp() to initialize the scenegraph with sample content. + +

+
    +
  1. makeCube() creates simple colored boxes for "target practice".
    +
  2. +
  3. makeFloor() creates a gray floor node for "target practice".
    +
  4. +
  5. initMark() creates a red sphere ("mark"). We will use it later to mark the spot that was hit.
    +
      +
    • Note that the mark is not attached and therefor not visible at the start!
      +
    • +
    +
  6. +
  7. initCrossHairs() creates simple cross-hairs by printing a "+" sign in the middle of the screen.
    +
      +
    • Note that the cross-hairs are attached to the guiNode, not to the rootNode.
      +
    • +
    +
  8. +
+ +

+ +In this example, we attached all "shootable" objects to one custom node, Shootables. This is an optimization so the engine only has to calculate intersections with objects we are actually interested in. The Shootables node is attached to the rootNode as usual. +

+ +
+ +

Understanding Ray Casting for Hit Testing

+
+ +

+ +Our goal is to determine which box the user "shot" (picked). In general, we want to determine which mesh the user has selected by aiming the cross-hairs at it. Mathematically, we draw a line from the camera and see whether it intersects with objects in the 3D scene. This line is called a ray. +

+ +

+Here is our simple ray casting algorithm for picking objects: +

+
    +
  1. Reset the results list.
    +
  2. +
  3. Cast a ray from cam location into the cam direction.
    +
  4. +
  5. Collect all intersections between the ray and Shootable nodes in the results list.
    +
  6. +
  7. Use the results list to determine what was hit:
    +
      +
    1. For each hit, JME reports its distance from the camera, impact point, and the name of the mesh.
      +
    2. +
    3. Sort the results by distance.
      +
    4. +
    5. Take the closest result, it is the mesh that was hit.
      +
    6. +
    +
  8. +
+ +
+ +

Implementing Hit Testing

+
+ +
+ +

Loading the scene

+
+ +

+ +First initialize some shootable nodes and attach them to the scene. You will use the mark object later. + +

+
  Node shootables;
+  Geometry mark;
+ 
+  @Override
+  public void simpleInitApp() {
+    initCrossHairs();
+    initKeys();
+    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, -4));
+    shootables.attachChild(makeFloor());
+  }
+ +
+ +

Setting Up the Input Listener

+
+ +

+ +Next you declare the shooting action. It can be triggered either by clicking, or by pressing the space bar. The initKeys() method is called from simpleInitApp() to set up these input mappings. + +

+
  /** Declaring the "Shoot" action and its triggers. */
+  private void initKeys() {
+    inputManager.addMapping("Shoot",      // Declare...
+      new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar, or
+      new MouseButtonTrigger(0));         // trigger 2: left-button click
+    inputManager.addListener(actionListener, "Shoot"); // ... and add.
+  }
+ +
+ +

Picking Action Using Crosshairs

+
+ +

+ +Next we implement the ActionListener that responds to the Shoot trigger with an action. The action follows the ray casting algorithm described above: +

+
    +
  1. For every click or press of the spacebar, the Shoot action is triggered.
    +
  2. +
  3. The action casts a ray forward and determines intersections with shootable objects (= ray casting).
    +
  4. +
  5. For any target that has been hit, it prints name, distance, and coordinates of the hit.
    +
  6. +
  7. Finally it attaches a red mark to the closest result, to highlight the spot that was actually hit.
    +
  8. +
  9. When nothing was hit, the results list is empty, and the red mark is removed.
    +
  10. +
+ +

+ +Note how it prints a lot of output to show you which hits were registered. +

+
  /** Defining the "Shoot" action: Determine what was hit and how to respond. */
+  private ActionListener actionListener = new ActionListener() {
+    @Override
+    public void onAction(String name, boolean keyPressed, float tpf) {
+      if (name.equals("Shoot") && !keyPressed) {
+        // 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.
+        shootables.collideWith(ray, results);
+        // 4. Print 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();
+          mark.setLocalTranslation(closest.getContactPoint());
+          // Let's interact - we mark the hit with a red dot.
+          rootNode.attachChild(mark);
+        } else {
+        // No hits? Then remove the red mark.
+          rootNode.detachChild(mark);
+        }
+      }
+    }
+  };
+ +

+Tip: Notice how you use the provided method results.getClosestCollision().getContactPoint() to determine the closest hit's location. If your game includes a "weapon" or "spell" that can hit multiple targets, you could also loop over the list of results, and interact with each of them. +

+ +
+ +

Picking Action Using Mouse Pointer

+
+ +

+ +The above example assumes that the player is aiming crosshairs (attached to the center of the screen) at the target. But you can change the picking code to allow you to freely click at objects in the scene with a visible mouse pointer. In order to do this, and you have to convert the 2d screen coordinates of the click to 3D world coordinates to get the start point of the picking ray. + +

+
    +
  1. Reset result list.
    +
  2. +
  3. Get 2D click coordinates.
    +
  4. +
  5. Convert 2D screen coordinates to their 3D equivalent.
    +
  6. +
  7. Aim the ray from the clicked 3D location forwards into the scene.
    +
  8. +
  9. Collect intersections between ray and all nodes into a results list.
    +
  10. +
+
...
+CollisionResults results = new CollisionResults();
+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).normalizeLocal();
+Ray ray = new Ray(click3d, dir);
+shootables.collideWith(ray, results);
+...
+ +

+Use this together with inputManager.setCursorVisible(true) to make certain the cursor is visible. +

+ +

+Note that since you now use the mouse for picking, you can no longer use it to rotate the camera. If you want to have a visible mouse pointer for picking in your game, you have to redefine the camera rotation mappings. +

+ +
+ +

Exercises

+
+ +

+ +After a hit was registered, the closest object is identified as target, and marked with a red dot. +Modify the code sample to solve these exercises: +

+ +
+ +

Exercise 1: Magic Spell

+
+ +

+ +Change the color of the closest clicked target!
+Here are some tips: +

+
    +
  1. Go to the line where the closest target is indentified, and add your changes after that.
    +
  2. +
  3. To change an object's color, you must first know its Geometry. Identify the node by identifying the target's name.
    +
      +
    • Use Geometry g = closest.getGeometry();
      +
    • +
    +
  4. +
  5. Create a new color material and set the node's Material to this color.
    +
      +
    • Look inside the makeCube() method for an example of how to set random colors.
      +
    • +
    +
  6. +
+ +
+ +

Exercise 2: Shoot a Character

+
+ +

+ +Shooting boxes isn't very exciting ??? can you add code that loads and positions a model in the scene, and shoot at it? +

+ + +
+ +

Exercise 3: Pick up into Inventory

+
+ +

+ +Change the code as follows to simulate the player picking up objects into the inventory: When you click once, the closest target is identified and detached from the scene. When you click a second time, the target is reattached at the location that you have clicked. Here are some tips: +

+
    +
  1. Create an inventory node to store the detached nodes temporarily.
    +
  2. +
  3. The inventory node is not attached to the rootNode.
    +
  4. +
  5. You can make the inventory visible by attaching the inventory node to the guiNode (which attaches it to the HUD). Note the following caveats:
    +
      +
    • If your nodes use a lit Material (not "Unshaded.j3md"), also add a light to the guiNode.
      +
    • +
    • Size units are pixels in the HUD, therefor a 2-wu cube is displayed only 2 pixels wide in the HUD. ??? Scale it bigger!
      +
    • +
    • Position the nodes: The bottom left corner of the HUD is (0f,0f), and the top right corner is at (settings.getWidth(),settings.getHeight()).
      +
    • +
    +
  6. +
+ +

+ +

Link to user-proposed solutions: +Be sure to try to solve them for yourself first! +

+ +

+ +
+ +

Conclusion

+
+ +

+ +You have learned how to use ray casting to solve the task of determining what object a user selected on the screen. You learned that this can be used for a variety of interactions, such as shooting, opening, picking up and dropping items, pressing a button or lever, etc. +

+ +

+Use your imagination from here: +

+ + +

+ +Now, wouldn't it be nice if those targets and the floor were solid objects and you could walk around between them? Let's continue to learn about Collision Detection. + +

+
+ +

+See also: + +

+ +
+ beginner, + documentation, + intro, + node, + ray, + click, + collision, + keyinput, + input +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html new file mode 100644 index 000000000..8420d279b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html @@ -0,0 +1,353 @@ + +

jMonkeyEngine 3 Tutorial (1) - Hello SimpleApplication

+
+ +

+ +Previous: Installing JME3, +Next: Hello Node +

+ +

+Prerequisites: This tutorial assumes that you have . +

+ +

+Nesta s??rie de tutoriais, n??s assumimos que voc?? usa o SDK da jMonkeyEngine. Como um desenvolvedor Java intermedi??rio ou avan??ado, voc?? rapidamente ver?? que, em geral, voc?? pode desenvolver c??digo da jMonkeyEngine em qualquer ambiente de desenvolvimento integrado (NetBeans IDE, Eclipse, IntelliJ) ou mesmo da linha de comando. +

+ +

+OK, Vamos nos aprontar para criar nossa primeira aplica????o jMonkeyEngine3. +

+ +
+ +

Crie um Projeto

+
+ +

+ +Na SDK da jMonkeyEngine: +

+
    +
  1. Escolha Arquivo (File)???Novo Projeto (New Project)??? do menu principal.
    +
  2. +
  3. No assistente de Novo Projeto, selecione o modelo JME3???Jogo B??sico (Basic Game). Clique em prosseguir (Next).
    +
      +
    1. Especifique um nome de projeto, e.g. "HelloWorldTutorial"
      +
    2. +
    3. Especifique um caminho para armazenar seu novo projeto, e.g. um diret??rio projetosjMonkey no seu diret??rio de usu??rio.
      +
    4. +
    +
  4. +
  5. Clique em terminar (Finish).
    +
  6. +
+ +

+ +Se voc?? tem perguntas, leia mais sobre Cria????o de Projeto aqui. +

+ +

+

N??s recomendamos atravessar os passos voc?? mesmo, como descrito nos tutoriais. Alternativamente, voc?? pode criar um projeto baseado no modelo JmeTests no SDK da jMonkeyEngine. Isto criar?? um projeto que j?? cont??m as amostras jme3test.helloworld (e muitas outras). Por exemplo, voc?? pode usar o projeto JmeTests para verificar se voc?? tem a solu????o certa. +

+

+ +
+ +

Escreva uma aplica????o de amostra

+
+ +

+ +Para este tutorial, voc?? deseja criar um pacote jme3test.helloworld no seu projeto, e criar um arquivo HelloJME3.java nele. +

+ +

+No SDK da jMonkeyEngine: +

+
    +
  1. D?? um clique com o bot??o direito no n?? pacotes de c??digo-fonte (Source Packages) de seu projeto.
    +
  2. +
  3. Escolha Novo (New)??? ???Classe Java (Java Class) para criar um novo arquivo.
    +
  4. +
  5. Digite o nome da classe: HelloJME3
    +
  6. +
  7. Digite o nome do pacote: jme3test.helloworld.
    +
  8. +
  9. Clique em Finalizar (Finish).
    +
  10. +
+ +

+O SDK cria o arquivo HelloJME3.java para voc??. +

+ +
+ +

C??digo de Amostra

+
+ +

+ +Substitua os conte??dos do arquivo HelloJME3.java com o seguinte c??digo: +

+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.math.ColorRGBA;
+ 
+/** Sample 1 - how to get started with the most simple JME 3 application.
+ * Display a blue 3D cube and view from all sides by
+ * moving the mouse and pressing the WASD keys. */
+public class HelloJME3 extends SimpleApplication {
+ 
+    public static void main(String[] args){
+        HelloJME3 app = new HelloJME3();
+        app.start(); // start the game
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+        Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create cube shape at the origin
+        Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
+        Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
+        mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
+        geom.setMaterial(mat);                   // set the cube's material
+        rootNode.attachChild(geom);              // make the cube appear in the scene
+    }
+}
+ +

+D?? um clique com o bot??o direito na classe HelloJME3 class e escolha Executar (Run). Se um di??logo de configura????es da jME3 aparecer, confirme as configura????es padr??o. +

+
    +
  1. Voc?? deveria ver uma janela simples exibindo um cubo 3D.
    +
  2. +
  3. Pressione as teclas WASD keys e mova para navegar ao redor.
    +
  4. +
  5. Olhe no texto do FPS e na informa????o de contagem de objeto na esquerda-fundo. Voc?? usar?? esta informa????o durante o desenvolvimento, e voc?? remover?? ela para a libera????o. (Para ler os n??meros corretamente, considere que as 14 linhas de texto contam como 14 objetos com 914 v??rtices.)
    +
  6. +
  7. Pressione Escape (Esc) para fechar a aplica????o.
    +
  8. +
+ +

+Parab??ns! Agora camos decobrir como isso funciona! +

+ +
+ +

Compreendendo o c??digo

+
+ +

+ +O c??digo acima tem inicializado a cena, e iniciado a aplica????o. +

+ +
+ +

Inicie a SimpleApplication

+
+ +

+ +Olhe na primeira linha. A classe HelloJME3.java estende com.jme3.app.SimpleApplication. +

+
public class HelloJME3 extends SimpleApplication {
+  // your code...
+}
+ +

+Todo jogo JME3 ?? uma inst??ncia de com.jme3.app.SimpleApplication. A classe SimpleApplication gerencia seu grafo de cena 3D e automaticamente desenha ele para a tela ??? isto ??, em breve, o que uma engine de jogo faz para voc??! +

+ +

+You start every JME3 game from the main() method, as every standard Java application: +

+
    +
  1. Instantiate your SimpleApplication-based class
    +
  2. +
  3. Call the application's start() method to start the game engine.
    +
  4. +
+
    public static void main(String[] args){
+        HelloJME3 app = new HelloJME3(); // instantiate the game
+        app.start();                     // start the game!
+    }
+ +

+This code opens your application window. Let's learn how you put something into the window next. +

+ +
+ +

Understanding the Terminology

+
+
+ + + + + + + + + + + + + + + + + + +
What you want to doHow you say that in JME3 terminology
You want to create a cube.I create a Geometry with a 1x1x1 Box shape.
You want to use a blue color.I create a Material with a blue Color property.
You want to colorize the cube blue.I set the Material of the Box Geometry.
You want to add the cube to the scene.I attach the Box Geometry to the rootNode.
You want the cube to appear in the center.I create the Box at the origin = at Vector3f.ZERO.
+ +

+ +If you are unfamiliar with the vocabulary, read more about the Scene Graph here. +

+ +
+ +

Initialize the Scene

+
+ +

+ +Look at rest of the code sample. The simpleInitApp() method is automatically called once at the beginning when the application starts. Every JME3 game must have this method. In the simpleInitApp() method, you load game objects before the game starts. +

+
    public void simpleInitApp() {
+       // your initialization code...
+    }
+ +

+The initialization code of a blue cube looks as follows: +

+
    public void simpleInitApp() {
+        Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create a 2x2x2 box shape at the origin
+        Geometry geom = new Geometry("Box", b);  // create a cube geometry from the box shape
+        Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
+        mat.setColor("Color", ColorRGBA.Blue);   // set color of material to blue
+        geom.setMaterial(mat);                   // set the cube geometry 's material
+        rootNode.attachChild(geom);              // make the cube geometry appear in the scene
+    }
+ +

+A typical JME3 game has the following initialization process: +

+
    +
  1. You initialize game objects:
    +
      +
    • You create or load objects and position them.
      +
    • +
    • You make objects appear in the scene by attaching them to the rootNode.
      +
    • +
    • Examples: Load player, terrain, sky, enemies, obstacles, ???, and place them in their start positions.
      +
    • +
    +
  2. +
  3. You initialize variables
    +
      +
    • You create variables to track the game state.
      +
    • +
    • You set variables to their start values.
      +
    • +
    • Examples: Set the score to 0, set health to 100%, ???
      +
    • +
    +
  4. +
  5. You initialize keys and mouse actions.
    +
      +
    • The following input bindings are pre-configured:
      +
        +
      • W,A,S,D keys ??? Move around in the scene
        +
      • +
      • Mouse movement and arrow keys ??? Turn the camera
        +
      • +
      • Escape key ??? Quit the game
        +
      • +
      +
    • +
    • Define your own additional keys and mouse click actions.
      +
    • +
    • Examples: Click to shoot, press Space to jump, ???
      +
    • +
    +
  6. +
+ +
+ +

Conclusion

+
+ +

+ +You have learned that a SimpleApplication is a good starting point because it provides you with: +

+ + +

+ +When developing a game application, you want to: +

+
    +
  1. Initialize the game scene
    +
  2. +
  3. Trigger game actions
    +
  4. +
  5. Respond to user input.
    +
  6. +
+ +

+The now following tutorials teach how you accomplish these tasks with the jMonkeyEngine 3. +

+ +

+Continue with the Hello Node tutorial, where you learn more details about how to initialize the game world, also known as the scene graph. + +

+
+ +

+See also: +

+ +
+ beginner, + intro, + documentation, + init, + simpleapplication, + basegame +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_terrain.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_terrain.html new file mode 100644 index 000000000..ea536d372 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_terrain.html @@ -0,0 +1,576 @@ + +

jMonkeyEngine 3 Tutorial (10) - Hello Terrain

+
+ +

+Previous: Hello Collision, +Next: Hello Audio +

+ +

+One way to create a 3D landscape is to sculpt a huge terrain model. This gives you a lot of artistic freedom ??? but rendering such a huge model can be quite slow. This tutorial explains how to create fast-rendering terrains from heightmaps, and how to use texture splatting to make the terrain look good. +

+ +

+ +

+ +

+Note: If you get an error when trying to create your ImageBasedHeightMap object, you may need to update the SDK, click on "Help" / "Check for updates" +

+ +

+

To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library. +

+

+ +
+ +

Sample Code

+
+
package jme3test.helloworld;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.renderer.Camera;
+import com.jme3.terrain.geomipmap.TerrainLodControl;
+import com.jme3.terrain.heightmap.AbstractHeightMap;
+import com.jme3.terrain.geomipmap.TerrainQuad;
+import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
+import com.jme3.terrain.heightmap.HillHeightMap; // for exercise 2
+import com.jme3.terrain.heightmap.ImageBasedHeightMap;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.util.ArrayList;
+import java.util.List;
+ 
+/** Sample 10 - How to create fast-rendering terrains from heightmaps,
+and how to use texture splatting to make the terrain look good.  */
+public class HelloTerrain extends SimpleApplication {
+ 
+  private TerrainQuad terrain;
+  Material mat_terrain;
+ 
+  public static void main(String[] args) {
+    HelloTerrain app = new HelloTerrain();
+    app.start();
+  }
+ 
+  @Override
+  public void simpleInitApp() {
+    flyCam.setMoveSpeed(50);
+ 
+    /** 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/splat/alphamap.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/Terrain/splat/dirt.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/road.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/splat/mountains512.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.
+     */
+    int patchSize = 65;
+    terrain = new TerrainQuad("my terrain", patchSize, 513, heightmap.getHeightMap());
+ 
+    /** 4. We give the terrain its material, position & scale it, and attach it. */
+    terrain.setMaterial(mat_terrain);
+    terrain.setLocalTranslation(0, -100, 0);
+    terrain.setLocalScale(2f, 1f, 2f);
+    rootNode.attachChild(terrain);
+ 
+    /** 5. The LOD (level of detail) depends on were the camera is: */
+    TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+    terrain.addControl(control);
+  }
+}
+ +

+When you run this sample you should see a landscape with dirt mountains, grass plains, plus some winding roads in between. +

+ +
+ +

What is a Heightmap?

+
+ +

+ +Heightmaps are an efficient way of representing the shape of a hilly landscape. Not every pixel of the landscape is stored, instead, a grid of sample values is used to outline the terrain height at certain points. The heights between the samples is interpolated. +

+ +

+In Java, a heightmap is a float array containing height values between 0f and 255f. Here is a very simple example of a terrain generated from a heightmap with 5x5=25 height values. +

+ +

+ +

+ +

+Important things to note: +

+ + +

+ +When looking at Java data types to hold an array of floats between 0 and 255, the Image class comes to mind. Storing a terrain's height values as a grayscale image has one big advantage: The outcome is a very userfriendly, like a topographical map: +

+ + +

+ +Look at the next screenshot: In the top left you see a 128x128 grayscale image (heightmap) that was used as a base to generate the depicted terrain. To make the hilly shape better visible, the mountain tops are colored white, valleys brown, and the areas inbetween green: +

+ +

+} +

+ +

+In a real game, you will want to use more complex and smoother terrains than the simple heightmaps shown here. Heightmaps typically have square sizes of 512x512 or 1024x1024, and contain hundred thousands to 1 million height values. No matter which size, the concept is the same as described here. +

+ +
+ +

Looking at the Heightmap Code

+
+ +

+ + +

+ +

+The first step of terrain creation is the heightmap. You can create one yourself in any standard graphic application. Make sure it has the following properties: +

+ + +

+ +The file mountains512.png that you see here is a typical example of an image heightmap. +

+ +

+Here is how you create the heightmap object in your jME code: +

+
    +
  1. Create a Texture object.
    +
  2. +
  3. Load your prepared heightmap image into the texture object.
    +
  4. +
  5. Create an AbstractHeightmap object from an ImageBasedHeightMap.
    +It requires an image from a JME Texture.
    +
  6. +
  7. Load the heightmap.
    +
  8. +
+
AbstractHeightMap heightmap = null;
+    Texture heightMapImage = assetManager.loadTexture(
+            "Textures/Terrain/splat/mountains512.png");
+    heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+    heightmap.load();
+ +
+ +

What is Texture Splatting?

+
+ +

+ +Previously you learned how to create a material for a simple shape such as a cube. All sides of the cube have the same color. You can apply the same material to a terrain, but then you have one big meadow, one big rock desert, etc. This is not always what you want. +

+ +

+Texture splatting allows you create a custom material, and "paint" textures on it like with a "paint brush". This is very useful for terrains: As you see in the example here, you can paint a grass texture into the valleys, a dirt texture onto the mountains, and free-form roads inbetween. +

+ +

+

The jMonkeyEngine SDK comes with a TerrainEditor plugin. Using the TerrainEditor plugin, you can sculpt the terrain with the mouse, and save the result as heightmap. You can paint textures on the terrain and the plugin saves the resulting splat textures as alphamap(s). The following paragraphs describe the manual process for you. You can choose to create the terrain by hand, or using the TerrainEditor plugin. +

+

+ +

+Splat textures are based on the Terrain.j3md material defintion. If you open the Terrain.j3md file, and look in the Material Parameters section, you see that you have several texture layers to paint on: Tex1, Tex2, Tex3, etc. +

+ +

+Before you can start painting, you have to make a few decisions: +

+
    +
  1. Choose three textures. For example grass.jpg, dirt.jpg, and road.jpg.
    +
  2. +
  3. You "paint" three texture layers by using three colors: Red, blue and, green. You arbitrarily decide that???
    +
      +
    1. Red is grass ??? red is layer Tex1, so put the grass texture into Tex1.
      +
    2. +
    3. Green is dirt ??? green is layer Tex2, so put the dirt texture into Tex2.
      +
    4. +
    5. Blue is roads ??? blue is layer Tex3, so put the roads texture into Tex3.
      +
    6. +
    +
  4. +
+ +

+ +Now you start painting the texture: +

+
    +
  1. Make a copy of your terrains heightmap, mountains512.png. You want it as a reference for the shape of the landscape.
    +
  2. +
  3. Name the copy alphamap.png.
    +
  4. +
  5. Open alphamap.png in a graphic editor and switch the image mode to color image.
    +
      +
    1. Paint the black valleys red ??? this will be the grass.
      +
    2. +
    3. Paint the white hills green ??? this will be the dirt of the mountains.
      +
    4. +
    5. Paint blue lines where you want roads to criss-cross the landscape.
      +
    6. +
    +
  6. +
  7. The end result should look similar to this:
    +
  8. +
+ +

+ ??? +

+ +
+ +

Looking at the Texturing Code

+
+ +

+ +As usual, you create a Material object. Base it on the Material Definition Terrain.j3md that is included in the jME3 framework. +

+
Material mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
+ +

+Load four textures into this material. The first one, Alpha, is the alphamap that you just created. +

+
mat_terrain.setTexture("Alpha",
+    assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
+ +

+The three other textures are the layers that you have previously decided to paint: grass, dirt, and road. You create texture objects and load the three textures as usual. Note how you assign them to their respective texture layers (Tex1, Tex2, and Tex3) inside the Material! +

+
    /** 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/Terrain/splat/dirt.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/road.jpg");
+    rock.setWrap(WrapMode.Repeat);
+    mat_terrain.setTexture("Tex3", rock);
+    mat_terrain.setFloat("Tex3Scale", 128f);
+ +

+The individual texture scales (e.g. mat_terrain.setFloat("Tex3Scale", 128f);) depend on the size of the textures you use. +

+ + +

+ +Use setWrap(WrapMode.Repeat) to make the small texture fill the wide area. If the repetition is too visible, try adjusting the respective Tex*Scale value. +

+ +
+ +

What is a Terrain?

+
+ +

+ +Internally, the generated terrain mesh is broken down into tiles and blocks. This is an optimization to make culling easier. You do not need to worry about "tiles and blocks" too much, just use recommended values for now ??? 64 is a good start. +

+ +

+Let's assume you want to generate a 512x512 terrain. You already have created the heightmap object. Here are the steps that you perform everytime you create a new terrain. +

+ +

+Create a TerrainQuad with the following arguments: +

+
    +
  1. Specify a name: E.g. my terrain.
    +
  2. +
  3. Specify tile size: You want to terrain tiles of size 64x64, so you supply 64+1 = 65.
    +
      +
    • In general, 64 is a good starting value for terrain tiles.
      +
    • +
    +
  4. +
  5. Specify block size: Since you prepared a heightmap of size 512x512, you supply 512+1 = 513.
    +
      +
    • If you supply a block size of 2x the heightmap size (1024+1=1025), you get a stretched out, wider, flatter terrain.
      +
    • +
    • If you supply a block size 1/2 the heightmap size (256+1=257), you get a smaller, more detailed terrain.
      +
    • +
    +
  6. +
  7. Supply the 512x512 heightmap object that you created.
    +
  8. +
+ +
+ +

Looking at the Terrain Code

+
+ +

+ +Here's the code: + +

+
terrain = new TerrainQuad(
+  "my terrain",               // name
+  65,                         // tile size
+  513,                        // block size
+  heightmap.getHeightMap());  // heightmap
+ +

+You have created the terrain object. +

+
    +
  1. Remember to apply the created material:
    terrain.setMaterial(mat_terrain);
    +
    +
  2. +
  3. Remember to attach the terrain to the rootNode.
    rootNode.attachChild(terrain);
    +
    +
  4. +
  5. If needed, scale and translate the terrain object, just like any other Spatial.
    +
  6. +
+ +

+ +Tip: Terrain.j3md is an unshaded material definition, so you do not need a light source. You can also use TerrainLighting.j3md plus a light, if you want a shaded terrain. +

+ +
+ +

What is LOD (Level of Detail)?

+
+ +

+ +JME3 includes an optimization that adjusts the level of detail (LOD) of the rendered terrain depending on how close or far the camera is. +

+
    TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
+    terrain.addControl(control);
+ +

+Close parts of the terrain are rendered in full detail. Terrain parts that are further away are not clearly visible anyway, and JME3 improves performance by rendering them less detailed. This way you can afford to load huge terrains with no penalty caused by invisible details. +

+ +
+ +

Exercises

+
+ +
+ +

Exercise 1: Texture Layers

+
+ +

+ +What happens when you swap two layers, for example Tex1 and Tex2? + +

+
...
+mat_terrain.setTexture("Tex2", grass);
+...
+mat_terrain.setTexture("Tex1", dirt);
+ +

+You see it's easier to swap layers in the code, than to change the colors in the alphamap. +

+ +
+ +

Exercise 2: Randomized Terrains

+
+ +

+ +The following two lines generate the heightmap object based on your user-defined image: +

+
Texture heightMapImage = assetManager.loadTexture(
+        "Textures/Terrain/splat/mountains512.png");
+    heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
+ +

+Instead, you can also let JME3 generate a random landscape for you: +

+
    +
  1. What result do you get when you replace the above two heightmap lines by the following lines and run the sample?
    HillHeightMap heightmap = null;
    +HillHeightMap.NORMALIZE_RANGE = 100; // optional
    +try {
    +    heightmap = new HillHeightMap(513, 1000, 50, 100, (byte) 3); // byte 3 is a random seed
    +} catch (Exception ex) {
    +    ex.printStackTrace();
    +}
    +
    +
  2. +
  3. Change one parameter at a time, and the run the sample again. Note the differences. Can you find out which of the values has which effect on the generated terrain (look at the javadoc also)?
    +
      +
    • Which value controls the size?
      +
        +
      • What happens if the size is not a square number +1 ?
        +
      • +
      +
    • +
    • Which value controls the number of hills generated?
      +
    • +
    • Which values control the size and steepness of the hills?
      +
        +
      • What happens if the min is bigger than or equal to max?
        +
      • +
      • What happens if both min and max are small values (e.g. 10/20)?
        +
      • +
      • What happens if both min and max are large values (e.g. 1000/1500)?
        +
      • +
      • What happens if min and max are very close(e.g. 1000/1001, 20/21)? Very far apart (e.g. 10/1000)?
        +
      • +
      +
    • +
    +
  4. +
+ +

+You see the variety of hilly landscapes that can be generated using this method. +

+ +

+

For this exercise, you can keep using the splat Material from the sample code above. Just don't be surprised that the Material does not match the shape of the newly randomized landscape. If you want to generate real matching splat textures for randomized heightmaps, you need to write a custom method that, for example, creates an alphamap from the heightmap by replacing certain grayscales with certain RGB values. +

+

+ +
+ +

Exercise 3: Solid Terrains

+
+ +

+ +Can you combine what you learned here and in Hello Collision, and make the terrain solid? +

+ +
+ +

Conclusion

+
+ +

+ +You have learned how to create terrains that are more efficient than loading one giant model. You know how to create generate random or handmade heightmaps. You can add a LOD control to render large terrains faster. You are aware that you can combine what you learned about collison detection to make the terrain solid to a physical player. You are also able to texture a terrain "like a boss" using layered Materials and texture splatting. You are aware that the jMonkeyEngine SDK provides a TerrainEditor that helps with most of these manual tasks. +

+ +

+Do you want to hear your players say "ouch!" when they bump into a wall or fall off a hill? Continue with learning how to add sound to your game. + +

+
+ +

+See also: +

+ +
+ beginner, + heightmap, + documentation, + terrain, + texture +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_from_sources.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_from_sources.html new file mode 100644 index 000000000..78c88b0bc --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_from_sources.html @@ -0,0 +1,66 @@ + +

Building jMonkeyEngine 3 from the Sources

+
+ +

+ +We recommend downloading the - but of course you can also build the jMonkeyEngine yourself from the sources. In this case, you need the file version system installed (svn). + +

+
    +
  1. Checkout: Checkout the Subversion repository.
    svn checkout http://jmonkeyengine.googlecode.com/svn/trunk/engine jme3
    +
    +
      +
    • You can leave login and password empty
      +
    • +
    +
  2. +
  3. Build: Execute ant jar
    +
      +
    • This compiles the JAR files in dist/libs/*
      +
    • +
    +
  4. +
  5. Javadoc: Execute ant javadoc
    +
      +
    • This generates javadocs in the dist/javadoc directory.
      +
    • +
    +
  6. +
  7. Run: Execute ant run
    +
      +
    • This runs the TestChooser where you can browse examples.
      +
    • +
    +
  8. +
  9. Use: Create a Java SE project and place all JARs from the dist/lib directory on the classpath.
    +
      +
    • You can now extend your first game from com.jme3.app.SimpleApplication.
      +
    • +
    +
  10. +
+ +

+ +For a detailed description of the created jar files see this list. + +

+
+ +

+Learn more about: +

+ +
+ documentation, + install +
+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html new file mode 100644 index 000000000..fa8326467 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html @@ -0,0 +1,163 @@ + +

Setting up JME3 in Netbeans 6+

+
+ +

+ +You are welcome to try out the new jME3, and contribute patches and features! This document shows how to download, set up, build, and run the latest development version from the sources. (As of Spring 2010, we are in alpha.) These instructions work in NetBeans IDE 6 or better. +

+ +

+Note: In the following, always replace "~" with the path to your home directory. +

+ +
+ +

Downloading the Sources

+
+ +

+ +Check out the sources from the repository. (The following NetBeans instructions are equivalent to executing cd ~/NetBeansProjects; svn checkout jme3 on the commandline.) + +

+
    +
  1. In NetBeans go to Team > Subversion > Checkout
    +
      +
    1. Repository URL:
      +
    2. +
    3. You can leave user/pw blank for anonymous access.
      +
    4. +
    +
  2. +
  3. Click Next
    +
      +
    1. Repository Folders: trunk/engine
      +
    2. +
    3. Enable the checkbox to Skip "engine" and only checkout its contents.
      +
    4. +
    5. Local Folder: ~/NetBeansProjects/jme3
      +
    6. +
    +
  4. +
  5. Click Finish and wait.
    +
  6. +
+ +

+ +The jme3 project opens in the Project window. It already includes a working ANT build script for building and running. +

+ +

+Look into the Libraries node and confirm that the project depends on the following libraries in the classpath: +

+
jME3-natives-joal.jar	lwjgl.jar       gluegen-rt.jar
+jME3-lwjgl-natives.jar	jinput.jar	swing-layout-1.0.4.jar
+j-ogg-oggd.jar	        vecmath.jar     stack-alloc.jar
+j-ogg-vorbisd.jar       asm-all-3.1.jar jbullet.jar	         
+jheora-jst-debug-0.6.0.jar              xmlpull.xpp3-1.1.4c.jar
+nifty*.jar                              eventbus-1.4.jar
+ +

+For a detailed description of the separate jar files see this list. +

+ +
+ +

Build and Run

+
+ +

+ +That's it! +

+
    +
  1. Right-click the jme3 project node and "Clean and Build" the project.
    +
  2. +
  3. In the Projects window, browse to the src/test/jme3test folder.
    +
  4. +
  5. Right-click e.g. the file src/test/jme3test/model/TestHoverTank.java and choose "Run" to run a sample.
    +
      +
    1. In the sample application, use the mouse and the AWSD keys to move around the test object.
      +
    2. +
    3. Press escape to quit the sample application.
      +
    4. +
    +
  6. +
+ +

+ +Sample code for cool features is in the src/test/jme3test folder. A sample game can be found in src/games/jme3game/cubefield/CubeField.java. +

+ +

+Tips: +

+ + +
+ +

Optional: Javadoc Popups and Source Navigation in NetBeans

+
+ +

+ +If you are working on the jme3 sources: +

+
    +
  1. In the Projects window, right-click the jme3 project and choose Generate Javadoc. Wait.
    +
  2. +
  3. Confirm in the Files window that the javadoc has been created in ~/NetBeansProjects/jme3/dist/javadoc
    +
  4. +
  5. In the editor, place the caret in a jme class and press ctrl-space to view javadoc.
    +
  6. +
+ +

+ +If you are working on a game project that depends on jme3: +

+
    +
  1. First follow the previous tip. (In the future, we may offer jme javadoc as download instead.)
    +
  2. +
  3. In your game project, right-click the Libraries node and choose "Properties".
    +
  4. +
  5. In the Library properties, select jme3.jar and click the Edit button.
    +
      +
    1. For the Javadoc field, browse to ~/NetBeansProjects/jme3/dist/javadoc. Check "as relative path" and click select.
      +
    2. +
    3. For the Sources field, browse to ~/NetBeansProjects/jme3/src. Check "as relative path" and click select.
      +
    4. +
    5. Click OK.
      +
    6. +
    +
  6. +
  7. In the editor, place the caret in a jme class and press ctrl-space to view javadoc. Ctrl-click any jme3 method to jump to its definition in the sources.
    +
  8. +
+ +

+ +This tip works for any third-party JAR library that you use. (You may have to download the javadoc/sources from their home page separately). + +

+
+ +

+Sources used: , +

+
+ documentation, + install +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/dolphin-mesh.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/dolphin-mesh.png new file mode 100644 index 000000000..532af5bdf Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/dolphin-mesh.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.gif new file mode 100644 index 000000000..e2f97aab0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.png new file mode 100644 index 000000000..8462c8d08 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/2.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/2.gif new file mode 100644 index 000000000..96d619d50 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/2.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.1.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.1.gif new file mode 100644 index 000000000..d91744836 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.1.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.gif new file mode 100644 index 000000000..cffb99759 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-0.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-0.png new file mode 100644 index 000000000..d8d81cfa0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-0.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-1.png new file mode 100644 index 000000000..a0469023d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-10.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-10.png new file mode 100644 index 000000000..db952ffcc Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-10.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-11.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-11.png new file mode 100644 index 000000000..7393cd2da Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-11.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-12.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-12.png new file mode 100644 index 000000000..0b12f9feb Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-12.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-2.png new file mode 100644 index 000000000..9e224edb1 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-3.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-3.png new file mode 100644 index 000000000..5edecb993 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-3.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-4.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-4.png new file mode 100644 index 000000000..1d5f5b772 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-4.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-5.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-5.png new file mode 100644 index 000000000..61eefd4a9 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-5.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-6.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-6.png new file mode 100644 index 000000000..f8515db0f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-6.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-7.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-7.png new file mode 100644 index 000000000..0cf9e310f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-7.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-8.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-8.png new file mode 100644 index 000000000..d5bd508da Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-8.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-9.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-9.png new file mode 100644 index 000000000..32d0400a7 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax-9.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax.html new file mode 100644 index 000000000..b619fe0af --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax.html @@ -0,0 +1,616 @@ + +

3ds Max Bone Animation to JME3 using OgreMax plugin

+
+ +
+ +

Asset Management

+
+ +

+For the managing of assets in general, be sure to read the Asset Pipeline Documentation. It contains vital information on how to manage your asset files. +

+ +
+ +

Creating models in 3dsMax

+
+ +

+For this tutorial I used 3D Studio Max 2012 and OgreMax 2.4.3 free edition +

+ +
+ +

Create Model and Bones

+
+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +
+ +

Create the animation

+
+ + +

+ +

+ + +

+ +

+ + +
+ +

OgreMax settings

+
+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ +
+ +

Export and Import

+
+ +
import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Skeleton;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.FileLocator;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.LodControl;
+import com.jme3.scene.debug.SkeletonDebugger;
+ 
+/**
+ * This is a test class for loading a Ogre XML scene exported by OgreMax.
+ * 
+ * @author Stephan Dreyer
+ * 
+ */
+public class TestOgreMaxImport extends SimpleApplication {
+ 
+  @Override
+  public void simpleInitApp() {
+    assetManager.registerLocator("Assets/model/ogre/test/", FileLocator.class);
+ 
+    // create the geometry and attach it
+    final Node model = (Node) assetManager.loadModel("worm.scene");
+    // resize it, because of the large 3dsmax scales
+    model.setLocalScale(.001f);
+ 
+    // attach to root node
+    rootNode.attachChild(model);
+    addLodControl(model);
+ 
+    final AnimControl ac = findAnimControl(model);
+ 
+    try {
+      // add a skeleton debugger to make bones visible
+      final Skeleton skel = ac.getSkeleton();
+      final SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton",
+          skel);
+      final Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");
+      mat.setColor("Color", ColorRGBA.Green);
+      mat.getAdditionalRenderState().setDepthTest(false);
+      skeletonDebug.setMaterial(mat);
+      model.attachChild(skeletonDebug);
+ 
+      // create a channel and start the wobble animation
+      final AnimChannel channel = ac.createChannel();
+      channel.setAnim("wobble");
+    } catch (final Exception e) {
+      e.printStackTrace();
+    }
+ 
+    // add some lights
+    rootNode.addLight(new AmbientLight());
+    rootNode.addLight(new PointLight());
+  }
+ 
+  public void addLodControl(final Spatial parent) {
+    if (parent instanceof Node) {
+      for (final Spatial s : ((Node) parent).getChildren()) {
+        addLodControl(s);
+      }
+    } else if (parent instanceof Geometry) {
+      final LodControl lc = new LodControl();
+      lc.setDistTolerance(1f);
+      parent.addControl(lc);
+    }
+  }
+ 
+  /**
+   * Method to find the animation control, because it is not on the models root
+   * node.
+   * 
+   * @param parent
+   *          The spatial to search.
+   * @return The {@link AnimControl} or null if it does not exist.
+   */
+  public AnimControl findAnimControl(final Spatial parent) {
+    final AnimControl animControl = parent.getControl(AnimControl.class);
+    if (animControl != null) {
+      return animControl;
+    }
+ 
+    if (parent instanceof Node) {
+      for (final Spatial s : ((Node) parent).getChildren()) {
+        final AnimControl animControl2 = findAnimControl(s);
+        if (animControl2 != null) {
+          return animControl2;
+        }
+      }
+    }
+ 
+    return null;
+  }
+ 
+  public static void main(final String[] args) {
+    new TestOgreMaxImport().start();
+  }
+}
+ +

+You will see your worms strange movements. Have fun! +

+ +

+ +

+ +
+ +

3ds Max Biped Animation to JME3

+
+ +

+ +You can also use the biped operator to animate models, but you have to consider a lot of things. +

+ +
+ +

Creating a character in 3dsMax

+
+ +

+ +I will not tell you in detail how to model a character. There I many good tutorials on the web, I used . + +

+ + +

+ + + +

+ + +

+ + + +

+ + +
+ +

Creating a simple walk animation

+
+ + +

+ + + +

+ + +
+ +

Preparing the export and setting up OgreMax

+
+ + +

+ + + +

+ + +

+ + + +

+ + +
+ +

Fixing the location

+
+ + +

+ + + +

+ + +
+ +

Export and Import

+
+ + +

+ +For that, I extended the first class: +

+
import com.jme3.animation.AnimChannel;
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Skeleton;
+import com.jme3.app.SimpleApplication;
+import com.jme3.asset.plugins.FileLocator;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.LodControl;
+import com.jme3.scene.debug.SkeletonDebugger;
+import com.jme3.scene.shape.Box;
+ 
+/**
+ * This is a test class for loading a Ogre XML scene exported by OgreMax.
+ * 
+ * @author Stephan Dreyer
+ * 
+ */
+public class TestOgreMaxImport extends SimpleApplication {
+ 
+  @Override
+  public void simpleInitApp() {
+    assetManager.registerLocator("Assets/model/ogre/test/", FileLocator.class);
+ 
+    // create the geometry and attach it
+    final Node model = (Node) assetManager.loadModel("guy.scene");
+    // resize it, because of the large 3dsmax scales
+    model.setLocalScale(.02f);
+ 
+    // attach to root node
+    rootNode.attachChild(model);
+    addLodControl(model);
+ 
+    final AnimControl ac = findAnimControl(model);
+ 
+    try {
+      // add a skeleton debugger to make bones visible
+      final Skeleton skel = ac.getSkeleton();
+      final SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton",
+          skel);
+      final Material mat = new Material(assetManager,
+          "Common/MatDefs/Misc/Unshaded.j3md");
+      mat.setColor("Color", ColorRGBA.Green);
+      mat.getAdditionalRenderState().setDepthTest(false);
+      skeletonDebug.setMaterial(mat);
+      // model.attachChild(skeletonDebug);
+ 
+      // create a channel and start the walk animation
+      final AnimChannel channel = ac.createChannel();
+      channel.setAnim("walk");
+    } catch (final Exception e) {
+      e.printStackTrace();
+    }
+ 
+    flyCam.setMoveSpeed(40f);
+    cam.setLocation(new Vector3f(15, 10, 15));
+    cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
+    cam.setFrustumNear(1f);
+ 
+    // add some lights
+    rootNode.addLight(new AmbientLight());
+ 
+    final PointLight pl = new PointLight();
+    pl.setPosition(new Vector3f(-3f, 3f, 1f));
+    rootNode.addLight(pl);
+ 
+    // add a box as floor
+    final Box b = new Box(100f, 0.1f, 100f);
+    final Geometry geo = new Geometry("floor", b);
+ 
+    final Material mat = new Material(assetManager,
+        "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.LightGray);
+    geo.setMaterial(mat);
+ 
+    rootNode.attachChild(geo);
+  }
+ 
+  /**
+   * Method to traverse through the scene graph and add a {@link LodControl} to
+   * the mesh.
+   * 
+   * @param parent
+   *          The Node to add the control to.
+   */
+  public void addLodControl(final Spatial parent) {
+    if (parent instanceof Node) {
+      for (final Spatial s : ((Node) parent).getChildren()) {
+        addLodControl(s);
+      }
+    } else if (parent instanceof Geometry) {
+      final LodControl lc = new LodControl();
+ 
+      // the distance for LOD changes is set here, you may adjust this
+      lc.setDistTolerance(1f);
+      parent.addControl(lc);
+    }
+  }
+ 
+  /**
+   * Method to find the animation control, because it is not on the models root
+   * node.
+   * 
+   * @param parent
+   *          The spatial to search.
+   * @return The {@link AnimControl} or null if it does not exist.
+   */
+  public AnimControl findAnimControl(final Spatial parent) {
+    final AnimControl animControl = parent.getControl(AnimControl.class);
+    if (animControl != null) {
+      return animControl;
+    }
+ 
+    if (parent instanceof Node) {
+      for (final Spatial s : ((Node) parent).getChildren()) {
+        final AnimControl animControl2 = findAnimControl(s);
+        if (animControl2 != null) {
+          return animControl2;
+        }
+      }
+    }
+ 
+    return null;
+  }
+ 
+  public static void main(final String[] args) {
+    new TestOgreMaxImport().start();
+  }
+}
+ +

+After starting the class, you can see a nice smooth walk animation (if it's not smooth, you need to adjust your track frames): +

+ +

+ +

+ +

+As you can see, the LOD is working: +

+ +

+ + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_2.png new file mode 100644 index 000000000..4e7718ac5 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_3_1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_3_1.png new file mode 100644 index 000000000..81953ea32 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_3_1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_4.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_4.png new file mode 100644 index 000000000..3f9c31842 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_4.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_5.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_5.png new file mode 100644 index 000000000..f0b9524ac Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_5.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_6.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_6.png new file mode 100644 index 000000000..ef5c62f93 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_6.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_7.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_7.png new file mode 100644 index 000000000..0b23612b5 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_7.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_8.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_8.png new file mode 100644 index 000000000..0f104550c Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/3dsmax_biped_8.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/4.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/4.gif new file mode 100644 index 000000000..224207012 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/4.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/5.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/5.gif new file mode 100644 index 000000000..ebf57f48f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/5.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/6.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/6.gif new file mode 100644 index 000000000..8c3f029b2 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/6.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/7.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/7.gif new file mode 100644 index 000000000..d4d352ebc Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/7.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/8.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/8.gif new file mode 100644 index 000000000..fb0f71db7 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/8.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-action-editor.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-action-editor.png new file mode 100644 index 000000000..1d5c5a7f7 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-action-editor.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-bone.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-bone.png new file mode 100644 index 000000000..296c0d37f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-bone.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-keyframes.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-keyframes.png new file mode 100644 index 000000000..eda14011e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-add-keyframes.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-make-armature.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-make-armature.png new file mode 100644 index 000000000..4d90847ae Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-make-armature.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-1.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-1.png new file mode 100644 index 000000000..0a000cc7d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-1.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-2.png new file mode 100644 index 000000000..e89a9693b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-3.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-3.png new file mode 100644 index 000000000..d357b5a45 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-3.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-4.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-4.png new file mode 100644 index 000000000..e30d88cd8 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender-material-4.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html new file mode 100644 index 000000000..a96d258d7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html @@ -0,0 +1,547 @@ + +

Creating assets in Blender3D

+
+ +

+This section discusses how to create and import models from Blender3D (2.62+, see bottom of page for Blender 2.49 and before) to jME3. Furthermore it explains how you can create various typical game-related assets like normal maps of high-poly models and baked lighting maps. +

+ +
+ +

Asset Management

+
+ +

+For the managing of assets in general, be sure to read the Asset Pipeline Documentation. It contains vital information on how to manage your asset files. +

+ +
+ +

Creating Models

+
+ +

+Game-compatible models are models that basically only consist of a mesh and UV-mapped textures, in some cases animations. All other material parameters or effects (like particles etc.) can not be expected to be transferred properly and probably would not translate to live rendering very well anyway. +

+ +

+Note that BMeshes are not yet supported by the importers and exporters so please use the "legacy mesh format" for now in blender 2.63+ +

+ +
+ +

UV Mapped Textures

+
+ +

+To successfully import a texture, the texture has to be UV-mapped to the model. Heres how to assign diffuse, normal and specular maps: +

+ +

+ + + + +

+ +

+Its important to note that each used texture will create one separate geometry. So its best to either combine the UV maps or use a premade atlas with different texture types from the start and then map the uv coords of the models to the atlas instead of painting on the texture. This works best for large models like cities and space ships. + +

+ +
+ +

Animations

+
+ +

+To create an animation from scratch do the following: +

+ + +
+ +

Model Checklist

+
+ +

+Sometimes you do not create the model yourself and often times models from the web are not really made for OpenGL live rendering or not completely compatible with the bone system in jME. +

+ +

+To export an animated model in Blender make sure the following conditions are met: + +

+
    +
  1. The animation has to be a bone animation
    +
  2. +
  3. Apply Location, Rotation and Scale to the mesh on Blender: On 3D View editor on Blender, select the mesh in Object Mode and go to the 3D View Editor???s header ??? Object Menu ??? Apply ??? Location / Rotation / Scale.
    +
  4. +
  5. Apply Location, Rotation and Scale to the armature on Blender: On 3D View editor on Blender, select the armature in Object Mode and go to the 3D View Editor???s header ??? Object Menu ??? Apply ??? Location / Rotation / Scale.
    +
  6. +
  7. Set the mesh???s origin point in the bottom of the mesh (see the images below).
    +
  8. +
  9. Set the armature???s origin point in the bottom of the armature (see the images below).
    +
  10. +
  11. Armature???s origin point and mesh???s origin point must be in the same location(see the images below).
    +
  12. +
  13. Use a root bone located in the armature???s origin. This root bone must be in vertical position (see the images below) and it is the root bone of the armature. If you rotate the root bone, the the entire armature might be rotate when you import the model into jMonkey (I???m just mentioning the result, I don???t know where is the problem (jMonkey importer or blender???s ogre exporter plugin)).
    +
  14. +
  15. Uncheck ???Bone Envelopes??? checkbox on the Armature modifier for the mesh (see the images below).
    +
  16. +
  17. Uncheck ???Envelopes??? checkbox on the armature (see the images below).
    +
  18. +
+ +

+ + + + +

+ +

+You can use SkeletonDebugger to show the skeleton on your game in order to check if the mesh and the skeleton are loaded correctly: + +

+
    final Material soldier2Mat = assetManager.loadMaterial("Materials/soldier2/soldier2.j3m");
+    final Spatial soldier2 = assetManager.loadModel("Models/soldier2/soldier2.j3o");
+    TangentBinormalGenerator.generate(soldier2);
+    soldier2.setMaterial(soldier2Mat);
+ 
+    final Node soldier2Node = new Node("Soldier2 Node");
+ 
+    soldier2Node.attachChild(soldier2);
+    rootNode.attachChild(soldier2Node);
+ 
+    final AnimControl control = soldier2.getControl(AnimControl.class);
+    control.addListener(this);
+    final AnimChanel channel = control.createChannel();
+ 
+    final SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
+    final Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+    mat.setColor("Color", ColorRGBA.Green);
+    mat.getAdditionalRenderState().setDepthTest(false);
+    skeletonDebug.setMaterial(mat);
+    soldier2Node.attachChild(skeletonDebug);
+ +

+ + +

+ +

+Also check out these videos and resources: + +

+ + +
+ +

NormalMap baking

+
+ +

+ +Models for live rendering should have a low polygon count. To increase the perceived detail of a model normal maps are commonly used in games. This tutorial will show how to create a normalmap from a highpoly version of your model that you can apply to a lowpoly version of the model in your game. +

+ +
+ +

Blender modeling lowPoly & highPoly

+
+ +

+ +If you use the multiresolution modifier you only need one object. Lets look at this example: +

+ +

+ +

+ +

+Add a multiresolution modifier: +

+ +

+ +

+ +

+There are two types of modifiers: Catmull-Clark and Simple. +- Simple is better for things like walls or floors. +- Catmull-Clark is better for objects like spheres. +

+ +

+When using Catmull-Clark with a higher "subdivide" value (more than 3) its good to have the "preview" value above 0 and less than the subdivide level. This is because Catmull-Clark smoothes the vertices, so the normalMap is not so precise. +

+ +

+Here is an example of Prewiew 1, it's more smooth than the original mesh: +

+ +

+ +

+ +

+Enable "Sculpt Mode" in blender and design the highPoly version of your model like here: +

+ +

+ +

+ +

+Now go into Render Tab, and bake a normalMap using same configuration as here: +

+ +

+ +

Remember! The actual preview affects the baking output and mesh export! +

+

+ +
+ +

Fixing the normal colors in Blender

+
+ +

+ +Blender has its own normal colors standard. We need to fix the colors to prepare the normalmap for using it with the JME Lighting Material. +

+ +

+To do this, go to the Blender Node Window +

+ +

+Here is Blender Node example. It fixes the normal colors: +

+ +

+ +

+ +

+Here is the colors configuration: +

+ +

+ + + +

+ +

+

Sometimes it will be needed to change R and G scale and add some blur for better effect. Do it like on image below +

+

+ +

+ +

+ +

+After rendering, save the file to a destination you want and use it with the JME Lighting Material and the lowpoly version of the model. +

+ +

+ +

+ +
+ +

LightMap baking

+
+ +

+ +The goal of this tutorial is to explain briefly how to bake light map in blender with a separate set of texture coordinates and then export a model using this map in jME3. +

+ +
+ +

Blender modeling + texturing

+
+ +

+create a mesh in blender and unwrap it to create uvs +

+ +

+ +

+ +

+In the mesh tab you can see the sets of Uvs, it will create the first one.
+ +You can assign w/e texture on it, i used the built in checker of blender for the example.
+ +Then in this list, create a new one and click on the camera icon so that baking is made with this set. Name it LightUvMap.
+ +Then in the 3D view in edit mode select all your mesh vertice and hit 'U'/LightMap pack then ok it will unfold the mesh for light map.
+ +Then create a new image, go to the render tab an all at the end check the "Bake" section and select shadows. Then click bake.
+ +If all went ok it will create a light map like this.
+ + +

+ +

+Then go to the material tab, create a new one for your model and go to the Texture Tab.
+ +Create 2 textures one for the color map, and one for the light map.
+ +In the Mapping section be sure to select coordinates : UV and select the good set of coordinates.
+ + +

+ +

+ +then the light map
+ + +

+ +
+ +

Importing the model in the SDK and creating the appropriate material

+
+ +

+Once this is done, export your model with the ogre exporter, and turn it into J3o with the SDK.
+ +Create material for it using the lighting definition.
+ +Add the colorMap in the diffuse map slot and the lightMap in the light map slot.
+ +Make sure you check "SeparateTexCoords"
+ + +

+ +

+It should roughly result in something like that :
+ + +

+ +

+The blend file, the ogre xml files and the textures can be found in the download section of the google code repo
+ + +

+ +
+ +

SkyBox baking

+
+ +

+There are several ways to create static images to use for a sky in your game. This will describe the concepts used in blender and create an ugly sky :-) Check the links below for other ways and prettier skies. +

+ +

+A sky box is a texture mapped cube, it can also, loosely, be called en EnvMap or a CubeMap. The camera is inside the cube and the clever thing that jME does is to draw the sky so it is always behind whatever else is in your scene. Imagine the monkey is the camera in the picture. +

+ +

+ +

+ +

+But a real sky is not a box around our heads, it is more like a sphere. So if we put any old image in the sky it will look strange and might even look like a box. This is not what we want. The trick is to distort the image so that it will look like a sphere even if it in fact is a picture pasted on a box. Luckily blender can do that tricky distortion for us. +

+ +

+The screenshots are from Blender 2.63 but the equivalent operations have been in blender for years so with minor tweaks should work for almost any version. +

+ +

+So let's get started, fire up blender and you'll see something like this. +

+ +

+ +

+ +

+The cube in the start scene is perfect for us. What we'll do is have Blender render the scene onto that cube. The resulting image is what we'll use for our sky box. So our jME sky will look like we stood inside the blender box and looked out on the scene in blender. +

+ +

+Start by selecting the box and set its material to shadeless. +

+ +

+ +

+ +

+Now we will create a texture for the box. Make sure the texture is an Environment Map, that the Viewpoint Object is set to the cube. The resolution is how large the resulting image will be. More pixels makes the sky look better but comes at the cost of texture memory. You'll have to trim the resolution to what works in your application. +

+ +

+ +

+ +

+Next up is the fun part, create the sky scene in blender. You can do whatever fits your application, include models for a city landscape, set up a texture mapped sphere in blender with a nice photographed sky, whatever you can think will make a good sky. +I am not so creative so I created this scene: +

+ +

+ +

+ +

+Now render the scene (press F12). It doesn't actually matter where the camera is in blender but you might see something similar to this: +

+ +

+ +

+ +

+You can see that Blender has actually drawn the scene onto the cube. This is exactly what we want. Now to save the image. +

+ +

+Select the texture of the cube and select save environment map. +

+ +

+ +

+ +

+That is it for Blender. Open the saved image in some image editor (I use the Gimp from here). +

The SDK also contains an image editor, right-click the image and select "edit image" to open it. +

+You will notice that Blender has taken the 6 sides of the cube and pasted together into one image (3x2). So now we need to cut it up again into 6 separate images. In gimp I usually set the guides to where I want to cut and then go into Filters???Web???Slice and let gimp cut it up for me. +

+ +

+ +

+ +

+Next up is to move the image files into your assets directory and create the sky in jME. You can do that in the Scene Composer by right clicking the scene node, select Add Spatial and then select Skybox. +If you want to do it from code, here is an example: +

+
public void simpleInitApp() {
+ 
+    Texture westTex = assetManager.loadTexture("Textures/west.png");
+    Texture eastTex = assetManager.loadTexture("Textures/east.png");
+    Texture northTex = assetManager.loadTexture("Textures/north.png");
+    Texture southTex = assetManager.loadTexture("Textures/south.png");
+    Texture upTex = assetManager.loadTexture("Textures/top.png");
+    Texture downTex = assetManager.loadTexture("Textures/bottom.png");
+ 
+    final Vector3f normalScale = new Vector3f(-1, 1, 1);
+    Spatial skySpatial = SkyFactory.createSky(
+                        assetManager,
+                        westTex,
+                        eastTex,
+                        northTex,
+                        southTex,
+                        upTex,
+                        downTex,
+                        normalScale);
+    rootNode.attachChild(skySpatial);
+}
+ +

+

This example uses a strange normalScale, this is to flip the image on the X-axis and might not be needed in your case. Hint: the texture is applied on the outside of the cube but we are inside so what do we see? +

+ +

+ +
+ +

Further reading

+
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_bones.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_bones.png new file mode 100644 index 000000000..8040bc887 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_bones.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_mesh.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_mesh.png new file mode 100644 index 000000000..285d40c30 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_apply_mesh.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_envelopes.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_envelopes.png new file mode 100644 index 000000000..4f8ecfa50 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_envelopes.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_finished.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_finished.png new file mode 100644 index 000000000..95da95bf5 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_finished.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_rootbone.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_rootbone.png new file mode 100644 index 000000000..5fa522745 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender_rootbone.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/exception2.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/exception2.gif new file mode 100644 index 000000000..33ae2834b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/exception2.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/post-slice.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/post-slice.png new file mode 100644 index 000000000..8b531700b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/post-slice.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/ready_normal.gif b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/ready_normal.gif new file mode 100644 index 000000000..bacec1605 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/ready_normal.gif differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/render.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/render.png new file mode 100644 index 000000000..9430652a2 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/render.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/saveenvmap.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/saveenvmap.png new file mode 100644 index 000000000..a7436867a Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/saveenvmap.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/scene.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/scene.png new file mode 100644 index 000000000..03fac4a57 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/scene.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/shadeless.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/shadeless.png new file mode 100644 index 000000000..a282df212 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/shadeless.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/skybox-concept.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/skybox-concept.png new file mode 100644 index 000000000..c51e5e4f5 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/skybox-concept.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/start-screen2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/start-screen2.png new file mode 100644 index 000000000..eeadda404 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/start-screen2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/texture.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/texture.png new file mode 100644 index 000000000..dce99a568 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/texture.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html new file mode 100644 index 000000000..a8d80c66c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html @@ -0,0 +1,904 @@ + +

Frequently Asked Questions

+
+ +
+ +

I want to create and configure a jME3 Application

+
+ +
+ +

How do I start writing a preconfigured jME game?

+
+ +

+Write a Java class that extends . +
+Learn more: Hello SimpleApplication, . + +

+ +
+ +

How do I change the background color?

+
+
viewPort.setBackgroundColor(ColorRGBA.Blue);
+ +
+ +

Can I customize the SimpleApplication class?

+
+ +

+Yes! Actually, you MUST customize it! For your own games, you always create a custom base class that extends class. From now on it's no longer a "simple application" ??? it's now your game. Configure your application settings, implement methods, and customize away! +
+Learn more: SimpleApplication, AppSettings. + +

+ +
+ +

How can I switch between screens or states?

+
+ +

+You should break app your application logic into components by spreading it out over individual AppStates. AppStates can be attached to and detached from the game. AppStates have access to all objects (rootNode, PhysicsSpace, inputManager, etc) and methods in your main application. So each AppState can bring its own subset of input handlers, GUI nodes, spatial nodes, and even its own subset of game mechanics in the update() loop. +
+Learn more: Application States. + +

+ +
+ +

How do I pause/unpause a game?

+
+ +

+You split up your application into several AppStates and implement the setEnabled() methods for each state. Then you create, for example, a GameRunningAppState and a GamePausedAppState. GamePausedAppState's job is to attach all your AppStates that contain the logic and GUI of the pause screen, and to detach all the AppStates that contain logic and GUI of the running game. GameRunningAppState does the opposite. By attaching one or the other to the game, you switch between the paused and unpaused states. +
+Learn more: Application States. + +

+ +
+ +

How do I disable logger output to the console?

+
+ +

+During development, you can switch the severity level of the default logger to no longer print FINE warnings, but only WARNINGs. + +

+
java.util.logging.Logger.getLogger("").setLevel(Level.WARNING);
+ +

+ +For the release, switch the severity level of the default logger to print only SEVERE errors. + +

+
java.util.logging.Logger.getLogger("").setLevel(Level.SEVERE);
+ +

+ +Learn more: Logging. + +

+ +
+ +

Why does the executable crash with "Cannot locate resource"?

+
+ +

+Make sure to only load() models converted to .j3o binary format, not the original Ogre or Wavefront formats. If you load assets from zip files, make sure to ammend the build script to copy them ito the build. +
+Learn more: Asset Manager + +

+ +
+ +

What is java.lang.LinkageError: Version mismatch?

+
+ +

+This rare exception shows a message similar to the following: Exception in thread "LWJGL Renderer Thread" java.lang.LinkageError: Version mismatch: jar version is (number), native library version is (another number). jME3 needs native libraries (.dll, .jnilib, lib*.so files) to run LWJGL and jBullet. The correct versions of these libraries are included when you install the SDK or download the binaries. However there are circumstances where jME3 cannot determine which copy of the native library it should use:
+If you install another application that needs a different version of a native library, and this app globally installs its version over jME3's; or if an old copy of a native library is in your project directory, your home directory, or Java library path, or in the classpath; or if you permanently linked an old copy in your IDE's settings; then Java assumes you prefer these native libraries over the bundled ones, and your jME3 application ends up running with the wrong version.
+To fix this, search for .dll (Windows), .jnilib (Mac), and .so (Linux) files for jBullet and LWJGL on your harddrive and in your path and IDE settings, and verify they don't interfere. (If you have other jME versions installed and linked somehow, the outdated natives may also be in a lwjgl.jar or jbullet.jar file!) + +

+ +
+ +

I want to load my scene

+
+ +
+ +

How do I make objects appear / disappear in the 3D scene?

+
+ +

+To make a spatial appear in the scene, you attach it to the rootNode (or to a node that is attached to the rootnode). To remove a spatial, you detach it from its parent node. + +

+
rootNode.attachChild(spatial); // appear in scene
+
rootNode.detachChild(spatial); // remove from scene
+ +

+ +Learn more: The Scene Graph, Hello Node, Hello Asset, Spatial, and . +

+ +
+ +

Why do I get AssetNotFoundException when loading X ?

+
+ +

+First check whether the file path of the asset is correct. By default it is relative to your project's assets directory: + +

+
// To load .../jMonkeyProjects/MyGame/assets/Models/Ninja/Ninja.j3o
+Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");
+ +

+ +If you are not using the default assets directory, verify that you have registered a locator to the AssetManager. are available. + +

+
this.assetManager.registerLocator("assets/", FileLocator.class); // default
+this.assetManager.registerLocator("c:/jme3User/JMEisSoCool/myAwesomeFolder/", FileLocator.class);
+this.assetManager.registerLocator("town.zip", ZipLocator.class);
+ +

+ +Learn more: Asset Manager +

+ +
+ +

How do I Create 3-D models, textures, sounds?

+
+ +

+Follow our best practices for the multi-media asset pipeline.
+ +You create 3-D models in a 3-D mesh editor, for example Blender, and export it in Ogre Mesh XML (animated objects, scenes) or Wavefront OBJ format (static objects, scenes). +You create textures in a graphic editor, for example Gimp, and export them as PNG or JPG. +You create sounds in an audio editor, for example, Audacity, and export them as WAVE or OGG. +
+Learn more: 3D Models, Multi-Media Asset Pipeline, JME3's blend-to-j3o importer;
+, , , . +

+ +
+ +

How do I load a 3-D model into the scene?

+
+ +

+Use the jMonkeyEngine SDK to convert models from Ogre XML or Wavefront OBJ formats to .j3o binary format. Load the .j3o file using the AssetManager. + +

+
// To load .../jMonkeyProjects/MyGame/assets/Models/Ninja/Ninja.j3o
+Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o");
+ +

+ +Learn more: Hello Asset, Asset Manager, , , jMonkeyEngine SDK j3o converter, +
+Code sample: , . + +

+ +
+ +

How do initialize the scene?

+
+ +

+Use the simpleInitApp() method in SimpleApplication (or initApp() in Application). +
+Learn more: Hello SimpleApplication, . +

+ +
+ +

I want to transform objects in the scene

+
+ +
+ +

How do I move or turn or resize a spatial?

+
+ +

+To move or turn or resize a spatial you use transformations. You can concatenate transformations (e.g. perform rotations around several axes in one step using a Quaternion with slerp() or a com.jme3.math.Transform with interpolateTransforms(). + +

+
spatial.setLocalTranslation(1,-3,2.5f); spatial.rotate(0,3.14f,0); spatial.scale(2,2,2);
+ +

+ +Learn more: Hello Node, Spatial, rotate, rotate_about_a_point, quaternion, math_for_dummies. +

+ +
+ +

How do I make a spatial move by itself?

+
+ +

+Change the geometry's translation (position) live in the update loop using setLocalTranslation() for non-physical and applyForce() or setWalkDirection() for physical objects. You can also define and remote-control a spatial's motion using Cinematics, e.g. to record cutscenes, or to implement mobile platforms, elevators, airships, etc. +
+Learn more: Hello Loop, Update Loop, Custom Controls, Cinematics +
+Code sample: , +

+ +
+ +

How do I access a named sub-mesh in Model?

+
+
Geometry result = spatial.getName().startsWith(name);
+ +

+ +Learn more: Spatial +

+ +
+ +

How do I make procedural or custom shapes?

+
+ +

+You can programmatically create com.jme3.scene.Mesh'es. +
+Learn more: Custom Meshes +

+ +
+ +

I want to change the surface of objects in the scene

+
+ +
+ +

Why is my UV wrapping / texture appearance all wrong?

+
+ +

+The most likely reason is the flipping of textures. You may be using the following default method: + +

+
  material.setTexture("ColorMap", assetManager.loadTexture("myTexture.jpg"));
+ +

+ +You can set the boolean value in the constructor of TextureKey to flipped or not flipped. Toggle the boolean to see if it fixes your UV wrapping/texture problem: + +

+
  material.setTexture("ColorMap", this.assetManager.loadTexture(new TextureKey("myTexture.jpg", false)));
+ +
+ +

How do I scale, mirror, or wrap a texture?

+
+ +

+You cannot scale a texture, but you scale the texture coordinates of the mesh the texture is applied to: + +

+
mesh.scaleTextureCoordinates(new Vector2f(2,2));
+ +

+ +You can choose among various com.jme3.texture.Texture.WrapModes for individual texture maps of a material: BorderClamp, EdgeClamp, Clamp; MirrorBorderClamp, MirrorEdgeClamp, MirrorClamp; Repeat, MirroredRepeat. + +

+
material.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
+ +
+ +

How do I change color or shininess of an material?

+
+ +

+Use the AssetManager to load Materials, and change material settings. +
+Learn more: Hello Material, How To Use Materials, Materials Overview, Asset Manager. +
+Code sample: , . +

+ +
+ +

How do I make a surface wood, stone, metal, etc?

+
+ +

+Create Textures as image files. Use the AssetManager to load a Material and use texture mapping for improved looks. +
+Learn more: Hello Material, How To Use Materials, Materials Overview, Asset Manager, , +
+Code sample: +

+ +
+ +

Why are materials too bright, too dark, or flickering?

+
+ +

+If you use a lit material (based on Lighting.j3md) then you must attach a light source to the rootNode, otherwise you see nothing. If you use lit material colors, make sure you have specified an Ambient color (can be the same as the Diffuse color) if you use an AmbientLight. If you see objects, but they are gray or too dark, set the light color to white, or make it brighter (you can multiply the color value with a scalar), or add a global white light source (AmbientLight). Similarly, if everything is too white, tune down the lights. If materials flicker under a directional light, change the light direction vector. Change the background color (which is independent of light sources) to get a better contrast while debugging a light problem. +

+ +
+ +

How do I make geometries cast a shadow?

+
+ +

+Use com.jme3.shadow.BasicShadowRenderer together with com.jme3.light.DirectionalLight, and setShadowMode(). +
+Learn more: Light and Shadow +
+Code sample: , +

+ +
+ +

How do I make materials transparent?

+
+ +

+Assign a texture with an alpha channel to a Material and set the Material's blend mode to alpha. Use this to create transparent or translucent materials such as glass, window panes, water, tree leaves, etc. + +

+
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ +

+ +Learn more: Hello Material, How To Use Materials, +

+ +
+ +

How do I force or disable culling?

+
+ +

+While debugging custom meshes, you can switch the com.jme3.material.RenderState.FaceCullMode off to see the inside and outside of the mesh. + +

+
someMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
+ +

+ +You can also deactivate the com.jme3.scene.Spatial.CullHint of a whole spatial to force jme to calculate it even if it is behind the camera and outside of view. + +

+
someNode.setCullHint(CullHint.Never);
+ +

+ +Learn more: Spatial + +

+ +
+ +

Can I draw only an outline of the scene?

+
+ +

+Add a renders state to the material's and activate Wireframe. + +

+
material.getAdditionalRenderState().setWireframe(true);
+ +

+ +Learn more: Debugging. +

+ +
+ +

I want to control the camera

+
+ +
+ +

How do I switch between third-person and first-person view ?

+
+ +

+The default camera is the cam object. Learn more: +

+ + +
+ +

How do I increase camera speed?

+
+
flyCam.setMoveSpeed(50f);
+ +
+ +

Actions, Interactions, Physics

+
+ +
+ +

How do I implement game logic / game mechanics?

+
+ +

+Use Controls to define the behaviour of types of Spatials. Use Application States to implement global behaviour, to group subsets of input handlers or GUI screens, etc. Use the simpleUpdate() and update() loops for tests and interactions. Use Cinematics to remote-control objects in scenes. +
+Learn more: Hello Loop, Update Loop, Custom Controls, Application States, Cinematics +

+ +
+ +

How do I let players interact via keyboard?

+
+ +

+Use com.jme3.input.KeyInput and a Input Listener. +
+Learn more: Hello Input, Input Handling +

+ +
+ +

How do I let players interact by clicking?

+
+ +

+Players typically click the mouse to pick up objects, to open doors, to shoot a weapon, etc. Use an Input Listener to respond to mouse clicks, then cast a ray from the player; if it intersects with the bounding volume of a spatial, this is the selected target. The links below contain code samples for both "fixed crosshair" picking and "free mouse pointer" picking. +
+Learn more: Hello Picking, Mouse Picking, Collision and Intersection, Input Handling, com.jme3.bounding.*, com.jme3.math.Ray, com.jme3.collision.CollisionResults. +
+Code sample: +

+ +
+ +

How do I animate characters?

+
+ +

+Create an animated OgreMesh model with bones in a 3-D mesh editor (e.g. Blender). +
+Learn more: com.jme3.animation.*, Hello Animation, Animation, +
+Code sample: +

+ +
+ +

How do I keep players from falling through walls and floors?

+
+ +

+Use collision detection. The most common solution is to use jme's physics integration, jBullet. +
+Learn more: Hello Collision, Physics, com.jme3.bullet.*, CapsuleCollisionShape versus CompoundCollisionShape, CharacterControl versus RigidBodyControl. +

+ +
+ +

How do I make balls/wheels/etc bounce and roll?

+
+ +

+Add physics controls to Spatials and give them spherical or cylindrical bounding volumes. +
+Learn more: Hello Physics, Physics, com.jme3.bounding.*, com.jme3.bullet.collisions, com.jme3.bullet.controls.RigidBodyControl, +
+Code sample: , +

+ +
+ +

How do I debug weird Physics behaviour?

+
+ +

+Maybe your collision shapes overlap ??? or they are not where you think they are. Make the collision shapes visible by adding the following line after the bulletAppState initialization: +

+
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+ +
+ +

How do I make a walking character?

+
+ +

+You can use jBullet's CharacterControl that locks a physical object upright, so it does not tip over when moving/walking (as tall physical objects are wont to do). +
+Learn more: CharacterControl +
+Code samples: (first-person), (third-person) +

+ +
+ +

How do I steer vehicles?

+
+ +

+Use a VehicleControl that supports suspension behavior. +
+Learn more: Vehicles, com.jme3.bullet.*, VehicleControl +
+Code samples: , (Press HUJK keys to steer, spacebar to jump.) +

+ +
+ +

Can objects swing like a pendulums, chains, ropebridges?

+
+ +

+Use a PhysicsControl's hinges and joints. +
+Learn more: Hinges and Joints, com.jme3.bullet.joints.PhysicsHingeJoint, + (Press HK keys to turn, spacebar to swing.) +

+ +
+ +

Default GUI Display

+
+ +
+ +

What are these FPS/Objects/Vertices/Triangles statistics?

+
+ +

+At the bottom left of every default SimpleGame, you see the StatsView and the FPS (frames per seconds) view. These views provide you with extra information during the development phase. For example, if you notice the object count is increasing and the FPS is decreasing, then you know that your code attaches too many objects and does not detach enough of them again (maybe a loop gone wild?). +
+Learn more: StatsView + +

+ +
+ +

How do I get rid of the FPS/Objects statistics?

+
+ +

+In the application's simpleInitApp() method, call: + +

+
setDisplayFps(false); // to hide the FPS
+setDisplayStatView(false); // to hide the statistics 
+ +

+ +
+Learn more: StatsView + +

+ +
+ +

How do I display score, health, mini-maps, status icons?

+
+ +

+Attach text and pictures to the orthogonal guiNode to create a heads-up display (). +
+Learn more: HUD, com.jme3.font.*, com.jme3.ui.Picture, guiNode.attachChild() +
+Code sample: , | +

+ +
+ +

How do I display buttons and UI controls?

+
+ +

+You may want to display buttons to let the player switch between the game, settings screen, and score screens. For buttons and other more advanced UI controls, jME supports the Nifty GUI library. +
+Learn more: Nifty GUI +
+Sample Code: +

+ +
+ +

How do i display a loading screen?

+
+ +

+Instead of having a frozen frame while your games loads, you can have a loading screen while it loads. +
+Learn more: Loading screen +

+ +
+ +

Nifty GUI

+
+ +
+ +

I get NoSuchElementException when adding controls (buttons etc)!

+
+ +

+Verify that you include a controls definition file link in your XML: This is the default: + +

+
<useControls filename="nifty-default-controls.xml"/>
+ +
+ +

Where can I find example code of Nifty GUI's XML and Java classes?

+
+ +

+ +

+ +
+ +

Is there Java Doc for Nifty GUI?

+
+ +

+Nifty GUI 1.3 Java docs +

+ +
+ +

I want to create an environment with sounds, effects, and landscapes

+
+ +
+ +

How do I play sounds and noises?

+
+ +

+Use AudioRenderer, Listener, and AudioNode from com.jme3.audio.*. +
+Learn more: Hello Audio, Audio +
+Code sample: +

+ +
+ +

How do I make fire, smoke, explosions, swarms, magic spells?

+
+ +

+For swarm like effects you use particle emitters. +
+Learn more: Hello Effects, Particle Emitters, Bloom and Glow, Effects Overview, com.jme3.effect.EmitterSphereShape, com.jme3.effect.ParticleEmitter +
+Code sample: , +

+ +
+ +

How do I make water, waves, reflections?

+
+ +

+Use a special post-processor renderer from com.jme3.water.*. +
+Learn more: Water, Post-Processor Water +
+Code sample: , , , +

+ +
+ +

How do I make fog, bloom, blur, light scattering?

+
+ +

+Use special post-processor renderers from com.jme3.post.*. +
+Learn more: effects_overview +

+ +
+ +

How do I generate a terrain?

+
+ +

+Use com.jme3.terrain.*. The JMonkeyEngine also provides you with a Terrain Editor plugin. +
+Learn more: Hello Terrain, Terrain, Terrain Editor +
+Code sample: +

+ +
+ +

How do I make a sky?

+
+ +

+Code sample: + +

+
rootNode.attachChild(SkyFactory.createSky( assetManager,
+       "Textures/Sky/Bright/BrightSky.dds", false));
+skyGeo.setQueueBucket(Bucket.Sky) 
+ +

+ +Learn more: Sky +

+ +
+ +

I want to access to back-end properties

+
+ +
+ +

How do I read out graphic card capabilities?

+
+ +

+If your game is heavily using features that older cards do not support, you can Read Graphic Card Capabilites in the beginning before starting the app, and then decide how to proceed. + +

+
Collection<com.jme3.renderer.Caps> caps = renderer.getCaps();
+Logger.getLogger(HelloJME3.class.getName()).log(Level.INFO, "Capabilities: {0}", caps.toString());
+ +
+ +

How do I Run jMonkeyEngine 3 with OpenGL1?

+
+ +

+In you game, add +

+
settings.setRenderer(AppSettings.LWJGL_OPENGL1)
+ +

+ to the AppSettings (see details there).
+ +For the jMonkeyEngine SDK itself, choose Options > OpenGL, and check OpenGL1. + +

+ +
+ +

How do I optimize the heck out of the Scene Graph?

+
+ +

+You can batch all Geometries in a scene (or a subnode) that remains static. + +

+
jme3tools.optimize.GeometryBatchFactory.optimize(rootNode);
+ +

+ +Batching means that all Geometries with the same Material are combined into one mesh. This optimization only has an effect if you use only few (roughly up to 32) Materials total. The pay-off is that batching takes extra time when the game is initialized. + +

+ +
+ +

How do I prevent users from unzipping my JAR?

+
+ +

+Add an . The SDK comes with a basic obfuscation script that you can enable in the project settings. + +

+ +
+ +

I want to do maths

+
+ +
+ +

What does addLocal() / multLocal() etc mean?

+
+ +

+Many maths functions (mult(), add(), subtract(), etc) come as local and a non-local variant (multLocal(), addLocal(), subtractLocal(), etc). +

+
    +
  1. Non-local means a new independent object is created (similar to clone()) as a return value. Use non-local methods if you want to keep using the old value of the object calling the method.
    +
      +
    • Example 1: Quaternion q1 = q2.mult(q3);
      +
        +
      • Returns the result as a new Quaternion q1.
        +
      • +
      • The involved objects q2 and q3 stay as they are and can be reused.
        +
      • +
      +
    • +
    • Example 2: v.mult(b).add(b); :!:
      +
        +
      • Watch out: This calculates the expected result, but unless you actually use the return value, it is discarded!
        +
      • +
      +
    • +
    +
  2. +
  3. Local means that no new objects are created, instead, the calling object is modified. Use this if you are sure you no longer need the old value of the calling object.
    +
      +
    • Example 1: q2.multLocal(q3)
      +
        +
      • Calculates q2*q3 without creating temp objects.
        +
      • +
      • The result is stored in the calling object q2. The old value of q2 is gone.
        +
      • +
      • Object q3 stays as it was.
        +
      • +
      +
    • +
    • Example 2:v.multLocal(a).addLocal(b);
      +
        +
      • Calculates the expected result without creating temp objects.
        +
      • +
      • The result is stored in the calling object v. The old value of v is gone.
        +
      • +
      • The objects a and b stay as they were.
        +
      • +
      +
    • +
    +
  4. +
+ +
+ +

What is the difference between World and Local coordinates?

+
+ +

+World coordinates of a Spatial are its absolute coordinates in the 3D scene (this is like giving GPS coordinates). Local coordinates are relative to the Spatial's parent Spatial (this is like saying, "I'm ten meters left of the entrance"). + +

+ +
+ +

How do I convert Degrees to Radians?

+
+ +

+ +Multiply degree value by FastMath.DEG_TO_RAD to convert it to radians. +

+
+ documentation, + faq +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html new file mode 100644 index 000000000..eb5451272 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html @@ -0,0 +1,211 @@ + +

jME3 Application Display Settings

+
+ +

+ +Every class that extends jme3.app.SimpleApplication has properties that can be configured by customizing a com.jme3.system.AppSettings object. +

+ +

+

Configure application settings in main(), before you call app.start() on the application object. If you change display settings during runtime, for eyample in simpleInitApp(), you must call app.restart() to make them take effect. +

+

+ +

+Note: Other runtime settings are covered in SimpleApplication. +

+ +
+ +

Code Samples

+
+ +

+ +Specify settings for a game (here called MyGame, or whatever you called your SimpleApplication instance) in the main() method before the game starts: +

+
public static void main(String[] args) {
+  AppSettings settings = new AppSettings(true);
+  settings.setResolution(640,480);
+  // ... other properties, see below
+  MyGame app = new MyGame(); 
+  app.setSettings(settings);
+  app.start();
+}
+ +

+Set the boolean in the AppSettings contructor to true if you want to keep the default settings for values that you do not specify. Set this parameter to false if you want the application to load user settings from previous launches. In either case you can still customize individual settings. +

+ +

+This example toggles the settings to fullscreen while the game is already running. Then it restarts the game context (not the whole game) which applies the changed settings. +

+
public void toggleToFullscreen() {
+  GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
+  DisplayMode[] modes = device.getDisplayModes();
+  int i=0; // note: there are usually several, let's pick the first
+  settings.setResolution(modes[i].getWidth(),modes[i].getHeight());
+  settings.setFrequency(modes[i].getRefreshRate());
+  settings.setDepthBits(modes[i].getBitDepth());
+  settings.setFullscreen(device.isFullScreenSupported());
+  app.setSettings(settings);
+  app.restart(); // restart the context to apply changes
+}
+ +
+ +

Properties

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Settings Property (Video)DescriptionDefault
setRenderer(AppSettings.LWJGL_OPENGL1)
+setRenderer(AppSettings.LWJGL_OPENGL2)
+setRenderer(AppSettings.LWJGL_OPENGL3)
Switch Video Renderer to OpenGL 1.1, OpenGL 2, or OpenGL 3.3. If your graphic card does not support all OpenGL2 features (UnsupportedOperationException: GLSL and OpenGL2 is required for the LWJGL renderer), then you can force your SimpleApplication to use OpenGL1 compatibility. (Then you still can't use special OpenGL2 features, but at least the error goes away and you can continue with the rest.) OpenGL 2
setBitsPerPixel(32)Set the color depth.
+1 bpp = black and white, 2 bpp = gray,
+4 bpp = 16 colors, 8 bpp = 256 colors, 24 or 32 bpp = "truecolor".
24
setFramerate(60)How often per second the engine should try to refresh the frame. For the release, usually 60 fps. Can be lower (30) if you need to free up the CPU for other applications. No use setting it to a higher value than the screen frequency! If the framerate goes below 30 fps, viewers start to notice choppiness or flickering.-1 (unlimited)
setFullscreen(true)Set this to true to make the game window fill the whole screen; you need to provide a key that calls app.stop() to exit the fullscreen view gracefully (default: escape).
+Set this to false to play the game in a normal window of its own.
False (windowed)
setHeight(480), setWidth(640)
+setResolution(640,480)
Two equivalent ways of setting the display resolution.640x480 pixels
setSamples(4)Set multisampling to 0 to switch antialiasing off (harder edges, faster.)
+Set multisampling to 2 or 4 to activate antialising (softer edges, may be slower.)
+Depending on your graphic card, you may be able to set multisampling to higher values such as 8, 16, or 32 samples.
0
setVSync(true)
+setFrequency(60)
Set vertical syncing to true to time the frame buffer to coincide with the refresh frequency of the screen. VSync prevents ugly page tearing artefacts, but is a bit slower; recommened for release build.
+Set VSync to false to deactivate vertical syncing (faster, but possible page tearing artifacts); can remain deactivated during development or for slower PCs.
false
+60 fps
+
+ + + + + + + + + + + + + + + +
Settings Property (Input)DescriptionDefault
setUseInput(false)Respond to user input by mouse and keyboard. Can be deactivated for use cases where you only display a 3D scene on the canvas without any interaction.true
setUseJoysticks(true)Activate optional joystick supportfalse
setEmulateMouse(true)Enable or disable mouse emulation for touchscreen-based devices. Setting this to true converts taps on the touchscreen to clicks, and finger swiping gestures over the touchscreen into mouse axis events.false
setEmulateMouseFlipAxis(true,true)Flips the X or Y (or both) axes for the emulated mouse. Set the first parameter to true to flip the x axis, and the second to flip the y axis.false,false
+
+ + + + + + + + + +
Settings Property (Audio)DescriptionDefault
setAudioRenderer(AppSettings.LWJGL_OPENAL)Switch Audio Renderer. Currently there is only one option. OpenAL
setStereo3D(true)Enable 3D stereo. This feature requires hardware support from the GPU driver. See . Currently, your everday user's hardware does not support this, so you can ignore it for now.false
+
+ + + + + + + + + + + + +
Settings Property (Branding)DescriptionDefault
setTitle("My Game")This string will be visible in the titlebar, unless the window is fullscreen."jMonkey Engine 3.0"
setIcons(new BufferedImage[]{
+ImageIO.read(new File("")), ???});
This specifies the little application icon in the titlebar of the application (unused in MacOS?). You should specify the icon in various sizes (256,128,32,16) to look good on various operating systems. Note: This is not the application icon on the desktop.null
setSettingsDialogImage("Interface/mysplashscreen.png")A custom splashscreen image in the assets/Interface directory which is displayed when the settings dialog is shown."/com/jme3/app/Monkey.png"
+ +

+ +

You can use app.setShowSettings(true); and setSettingsDialogImage("Interface/mysplashscreen.png") to present the user with jme3's default display settings dialog when starting the game. Use app.setShowSettings(false); to hide the default settings screen. Set this boolean before calling app.start() on the SimpleApplication. +

+

+ +
+ +

Toggling and Activating Settings

+
+
+ + + + + + + + + + + + + + + +
SimpleApplication methodDescription
app.setShowSettings(boolean)Activate or deactivate the default settings screen before start()ing the game. If you let users use this screen, you do not need to modify the settings object. Note: Most developers implement their own custom settings screen, but the default one is useful during the alpha stages.
app.setSettings(settings)After you have modified the properties on the settings object, you apply it to your application. Note that the settings are not automatically reloaded while the game is running.
app.start()Every game calls start() in the beginning to initialize the game and apply the settings. Modify and set your settings before calling start().
app.restart()Restart()ing a running game restarts the game context and applies the updated settings object. (This does not restart or reinitialize the whole game.)
+ +
+ +

Saving and Loading Settings

+
+ +

+ +An AppSettings object also supports the following methods to save your settings under a unique key (in this example "com.foo.MyCoolGame3"): +

+ + +

+ +Usage: +

+ +

+Provide the unique name of your jME3 application as the String argument. For example com.foo.MyCoolGame3. +

+
    try { settings.save("com.foo.MyCoolGame3"); } 
+    catch (BackingStoreException ex) { /** could not save settings */ }
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html new file mode 100644 index 000000000..9b7ec0575 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html @@ -0,0 +1,564 @@ + +

Best Practices For jME3 Developers

+
+ +

+ +Every milestone of a game development project is made up of phases: Planning, development, testing, and release. Every milestone involves updates to multi-media assets and to code. +

+ +

+This "best practices" page is a collection of recommendations and expert tips. Feel free to add your own! +

+ +
+ +

Requirements and Planning

+
+ +

+ +If you are a beginner, you should first game development. We cannot cover all general tips here. +

+ +
+ +

Requirements Gathering

+
+ +

+ +As a quick overview, answer yourself the following questions: +

+ + +
+ +

Planning Development Milestones

+
+ +

+ +Use an to outline what features you want and what components are needed. +

+
    +
  1. Pre-Alpha Development
    +
      +
    • Artwork: Test asset loading and saving with mock-ups and stock art.
      +
    • +
    • Lay out the overall application flow, i.e. switching between intro / options / game screen, etc.
      +
    • +
    • Get one typical level working before you can announce the Alpha Release.
      +E.g. if the game is a "Jump'n'Run", jumping and running must work.
      +
    • +
    +
  2. +
  3. Alpha Release
    +
  4. +
  5. Pre-Beta Development
    +
      +
    • Artwork: Replace all mock-ups with first drafts of real media and level maps.
      +
    • +
    • Have your team members review and "alpha test" it on various systems, track bugs, debug, optimize.
      +
    • +
    • Declare before you announce the Beta Release to prevent a bottomless pit of new bugs.
      +
    • +
    +
  6. +
  7. Beta Release
    +
  8. +
  9. Post-Beta Development
    +
      +
    • Artwork: Fill in the final media and level maps.
      +
    • +
    • Have external people review and "beta test" it, make it easy to report bugs.
      +
    • +
    • Fix high-priority bugs, even out the kinks in code and gameplay, don't add new features for now!
      +
    • +
    +
  10. +
  11. Gamma Release, Delta Release??? = Release Candidates
    +
      +
    • Think you're done? Make test runs incl. packaging and distribution. (Order form? download?)
      +
    • +
    • Test the heck out of it. Last chance to find and fix a horrible bug.
      +
    • +
    +
  12. +
  13. Omega = Final Release
    +
  14. +
+ +

+ +How you name or number these stages is fully up to your team. Development teams use numbered milestones (m1, m2, m3), Greek letters (e.g. alpha, beta, gamma, delta), numbering (e.g. "2.7.23-1328"), or combinations thereof. +

+ +
+ +

Use File Version Control

+
+ +

+ +Whether you work in a team or alone, keeping a version controlled repository of your code will help you roll-back buggy changes, or recover old code that someone deleted and that is now needed again. +

+ + +
+ +

Multi-Media Asset Pipeline

+
+
+ + + + + + + + + + + + + + + + + + + + + +
DODON'T
Save original models+textures into assets/Textures. Don't reference textures or models outside your JME project.
Save sounds into assets/Sounds. Don't reference audio files outside your JME project.
Create simple, low-polygon models. Don't create high-polygon models, they render too slow to be useful in games.
Only use Diffuse Map, Normal Map, Glow Map, Specular Map. Don't use unsupported material properties that are not listed in the Materials Overview.
Use UV texture / texture atlases / baking for each texture map. Don't create models based on multiple separate textures, it will break the model into separate meshes.
Convert Models to j3o format. Move j3o files into assets/Models. Don't reference Blender/Ogre/OBJ files in your load() code, because these unoptimized files are not packaged into the JAR.
+ +

+ +Learn details about the Multi-Media Asset Pipeline here. +

+ +
+ +

Development Phase

+
+ +

+ +

Many game developers dream of creating their very own "MMORPG with full-physics, AI, post-rendering effects, multi-player networking, procedurally generated maps, and customizable characters". So why aren't there tons of MMORPGs out there?
+Even for large experienced game producers, the creation of such a complex game is time-intensive and failure-prone. How familiar are you with multi-threading, persistence, optimization, client-server synchonization, ???? Unless your answer is "very!", then start with a single-player desktop game, and work your way up ??? just as the pros did when they started. +

+

+ +
+ +

Extend SimpleApplication

+
+ +

+ +Every jME3 game is centered around one main class that (directly or indirectly) extends com.jme3.app.SimpleApplication. +

+ +

+

Note that although the "SimpleApplication" name might be misleading, all jME3 applications, including very large projects, are based on this class. The name only implies that this class itself is a simple application already. You make it "non-simple" by extending it! +

+

+ +

+For your future game releases, you will want to rely on your own framework (based on jME): Your custom framework extends jME's SimpleApplication, and includes your custom methods for loading, saving, and arranging your scenes, your custom navigation methods, your inputs for pausing and switching your custom screens, your custom user interface (options screen, HUD, etc), your custom NPC factory, your custom physics properties, your custom networking synchronization, etc. +

+ +

+

Writing and reusing (extending) your own base framework saves you time. When you update your generic base classes, all your games that extend them benefit from improvements to the base (just as all jME-based games benefit of improvements to the jME framework).
+Also, your own framework gives all your games a common look and feel. +

+

+ +
+ +

Where to Start?

+
+ +

+ +You have a list of features that you want in game, but which one do you implement first? You will keep adding features to a project that grows more and more complex, how can you minimize the amount of rewriting required? +

+
    +
  1. Make sure the game's high-level frame (screen switching, network sync, loading/saving) is sound and solid.
    +
  2. +
  3. Start with implementing the most complex game feature first ??? the one that imposes most constraints on the structure of your project (for example: multi-player networking, or physics.)
    +
  4. +
  5. Add only one larger feature at a time. If there are complex interactions (such as "networking + physics"), start with a small test case ("one shared cube") and work your way up. Starting with a whole scene introduces too many extra sources of error.
    +
  6. +
  7. Implement low-complexity decorations (audio and visual effects) last.
    +
  8. +
  9. Test for side-effects on existing code after you add a new feature (regression test).
    +
  10. +
+ +

+ +

Acknowledge whether you want a feature because it is necessary for gameplay, or simply because "everyone else has it". Your goal should be to bring out the essence of your game idea. Don't water down gameplay by attempting to make it "do everything, but better". Successful high-performance games are the ones where someone made smart decisions what to keep and what to drop. +

+

+ +
+ +

The Smart Way to Add Custom Methods and Fields

+
+ +

+ +

Avoid the Anti-Pattern: Don't design complex role-based classes using Java inheritance, it will result in an unmaintainable mess.
+Example: You start extending Node ???> MyMobileNode ???> MyNPC. Then you extend MyFighterNPC (defends, attacks) and MyShopKeeperNPC (trades) from MyNPC. What if you need an NPC that trades and defends itself, but doesn't attack? Do you extend MyShopKeeperNPC and copy and paste the defensive methods from MyFighterNPC? Or do you extend MyFighterNPC and override the attacking methods of its parent? Neither is a clean solution.
+Wouldn't it be better if behaviours were a separate "system", and attributes were separate "components" that you add to the "entity" that needs them? +

+

+ +

+You write Java classes named Controls to implement your Game Entities, and define an Entity's visuals, attributes, and behaviours. In jME, Spatials (Nodes or Geometrys) are the visual representation of the game entity in the scene graph. +

+ + +

+ +

Follow the Best Practice: In general, use composition over inheritance and keep "what an entity does" (behaviour system) separate from "what this entity is" (attributes). +

+ + +

+ +

+

+ +

+If your game is even more complex, you may want to learn about "real" Entity Systems, which form a quite different programming paradigm from object oriented coding but are scalable to very large proportions. Note however that this topic is very unintuitive to handle for an OOP programmer and you should really decide on a case basis if you really need this or not and gather some experiences before diving head first into a MMO project :-) +

+ + +
+ +

The Smart Way to Access Game Features

+
+ +

+ +SimpleApplication gives you access to game features such as a the rootNode, assetManager, guiNode, inputManager, audioManager, physicsSpace, viewPort, and the camera. But what if you need this access also from another class? Don't extend SimpleApplication a second time, and don't pass around tons of object references in constructors! Needing access to application level objects is a sign that this class should be designed as an AppState (read details there). +

+ +

+An AppState has access to all game features in the SimpleApplication via the this.app and this.stateManager objects. Examples: + +

+
Spatial sky = SkyFactory.createSky(this.app.getAssetManager(), "sky.dds", false);
+...
+this.app.getRootNode().attachChild( sky );
+ +
+ +

The Smart Way to Implement Game Logic

+
+ +

+ +As your SimpleApplication-based game grows more advanced, you find yourself putting more and more interactions in the simpleUpdate() loop, and your simpleInitApp() methods grows longer and longer. It's a best practice to move blocks of game mechanics into reusable component classes of their own. In jME3, these resuable classes are called Controls and AppStates. + +

+ + + +

+ +

A game contains algorithms that do not directly affect spatials (for example, AI pathfinding code that calculates and chooses paths, but does not actually move spatials). You do not need to put such non-spatial code in controls, you can run thse things in a new thread. Only the tranformation code that actually modifies the spatial must be called from a control, or must be enqueue()ed. +

+

+ +

+Controls and AppStates often work together: An AppState can reach up to the application and get all Spatials from the rootNode that carry a specific Control, and perform a global action on them. Example: In BulletPhysics, all physical Spatials that carry RigidBodyControls are steered by the overall BulletAppState. +

+ +

+

AppStates and Controls are extensions to a SimpleApplication. They are your modular building blocks to build a more complex game. In the ideal case, you move all init/update code into Controls and AppStates, and your simpleInitApp() and simpleUpdate() could end up virtually empty. This powerful and modular approach cleans up your code considerably. +

+

+ +

+Read all about Custom Controls and Application States here. +

+ +
+ +

Optimize Application Performance

+
+ + +
+ +

Don't Mess With Geometric State

+
+ +

+ +These tips are especially important for users who already know jME2. Automatic handling of the Geometric State has improved in jME3, and it is now a best practice to not mess with it. +

+ + +
+ +

Maintain Internal Documentation

+
+ +

+ +It's unlikely you will fully document every class you write, we hear you. However, you should at least write meaningful javadoc to provide context for your most crucial methods/parameters. +

+ + +

+ +Treat javadoc as messages to your future self. "genNextVal() generates the next value" and "@param float factor A factor influencing the result" do not count as documentation. +

+ +
+ +

Debugging and Test Phase

+
+ +

+ +A Java Debugger is included in the jMonkeyEngine SDK. It allows you to set a break point in your code near the line of code where an exception happens. Then you step through the execution line by line and watch object and variable states live, to detect where the bug starts. +

+ +

+Use the Logger to print status messages during the development and debugging phase, instead of System.out.println(). The logger can be switched off with one line of code, whereas commenting out all your println()s takes a while. +

+ +

+Unit Testing () has a different status in 3D graphics development than in other types of software. You cannot write assertions that automatically test whether the rendered image looks correct, or whether interactions are intuitive. Still you should create simple test cases for individual game features such as loaders, content generators, effects. Run the test cases now and then to see whether they still work as intended ??? or whether they are suffering from regressions or side-effects. Keep the test classes in the test directory of your project, don't include them in the distribution. +

+ +

+Quality Assurance (QA) means repeatedly checking a certain set of features that must work, but that might be unexpectedly broken as a side-effect. Every game has some crazy bugs somewhere ??? but basic tasks must work, no excuse. This includes installing and de-installing; saving and loading; changing options; starting, pausing, quitting; basic actions such as walking, fighting, etc. After every milestone, you go through your QA list again and systematically look for regressions or newly introduced bugs. Check the application on every supported operating system and hardware (!) because not all graphic cards support the same features. If you don't find the obvious bugs, your users will, and carelessness will put them off. +

+ +

+Alpha and Beta Testing means that you ask someone to try to install and run your game. It should be a real user situation, where they are left to figure out the installation and gameplay by themselves???you only can include the usual read-me and help docs. Provide the testers with an easy method to report back what problems they encountered, what they liked best, or why they gave up. Evaluate whether reported problems are one-off glitches, or whether they must be fixed for the game to be playable for everyone. +

+ +
+ +

Release Phase

+
+ +
+ +

Pre-Release To-Do List

+
+ + +
+ +

Distributing the Executables

+
+ +

+ +The jMonkeyEngine SDK helps you with deployment: You specify your branding and deployment options in the Project Properties dialog, and then choose Clean and Build from the context menu. If you use another IDE, consult this IDE's documentation. +

+ +

+Decide whether you want to release your game as WebStart, desktop JAR, mobile APK, or browser Applet ??? Each has its pros and cons. + +

+
+ + + + + + + + + + + + + + + + + + +
DistributionProsCons
Desktop Launcher
+(.EXE, .app, .jar+.sh)
This is the standard way of distributing desktop applications. The jMonkeyEngine SDK can be configured to automatically create zipped launchers for each operating system. You need to offer three separate, platform-dependent downloads.
Desktop Application
+(.JAR)
Platform independent desktop application. User must have Java configured to run JARs when they are opened; or user must know how to run JARs from command line; or you must provide a custom JAR wrapper.
Web Start
+(.JNLP)
The user accesses a URL, saves the game as one executable file. Easy process, no installer required. You can allow the game to be played offline.Users need network connection to install the game. Downloading bigger games takes a while as opposed to running them from a CD.
Browser Applet
+(.HTML+.JAR)
Easy to access and play game via most web browsers. Userfriendly solution for quick small games.Game only runs in the browser. Game or settings cannot be saved to disk. Some restrictions in default camera navigation (jME cannot capture mouse.)
Android
+(.APK)
Game runs on Android devices.Android devices do not support post-procesor effects.
+ +

+ +Which ever method you choose, a Java-Application works on the main operating systems: Windows, Mac OS, Linux, Android. +

+ +

+The distribution appears in a newly generated dist directory inside your project directory. These are the files that you upload or burn to CD to distribute to your customers. + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/coordinate-system.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/coordinate-system.png new file mode 100644 index 000000000..c4042392d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/coordinate-system.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/file_types.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/file_types.html new file mode 100644 index 000000000..19f9457ca --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/file_types.html @@ -0,0 +1,79 @@ + +

jMonkeyEngine3 Supported File Types

+
+ +
+ +

jMonkeyEngine3 File Formats

+
+
+ + + + + + + + + + + + + + + +
SuffixUsageLearn more
.j3oBinary 3D model or scene. At the latest from the Beta release of your game on, you should convert all models to .j3o format.
+During alpha and earlier development phases (when models still change a lot) you can alternatively load OgreXML/OBJ models directly.
Model Loader and Viewer
.j3mA custom Material. You can create a .j3m file to store a Material configuration for a Geometry (e.g. 3D model).Materials Overview
+Material Editing
.j3mdA Material definition. These are pre-defined templates for shader-based Materials.
+Each custom .j3m Material is based on a material definition. Advanced users can create their own material definitions.
Materials Overview
.j3fA custom post-processor filter configuration. You can create a .j3f file to store a FilterPostProcessor with a set of preconfigured filters. Filters
+Effects Overview
+ +
+ +

Supported External File Types

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
File SuffixTypeDescription
.mesh.xml, .meshxml3D modelOgre Mesh XML
.scene3D sceneOgre DotScene
.OBJ, .MTL3D modelWavefront
.blend3D modelBlender version 2.49 or 2.5x
.JPG, .PNG, .GIFimageTextures, icons
.DDSimageDirect Draw Surface texture
.HDRimageHigh Dynamic Range texture
.TGAimageTarga Image File texture
.PFMimagePortable Float Map texture
.fntbitmap fontAngelCode font for GUI and HUD
.WAVaudioWave music and sounds
.OGGaudioOGG Vorbis music and sounds
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html new file mode 100644 index 000000000..3d3161cef --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html @@ -0,0 +1,370 @@ + +

How to Use Materials

+
+ +

+ +A Geometry (mesh) is just the shape of the object. jMonkeyEngine cannot render a shape without knowing anything about its surface properties. You need to apply a color or texture to the surface of your Geometries to make them visible. In jMonkeyEngine, colors and textures are represented as Material objects. (An alternative would also be to load a model that includes materials generated by a mesh editor, such as Blender.) + +

+ + +

+ +You want to make the most of your 3D models by specifying good looking material parameters. The developers must be in contact with the graphic designers regarding which of the Material properties they intend to use in their 3D models. You must have an understanding what texture maps are, to be able to use texture-mapped materials. +

+ +

+

Don't forget to add a Light Source to the scene! All Materials (except "Unshaded" ones) are invisible without a light source. +

+

+ +

+If you want more advanced background info: You can learn more about Material Definitions in general here. You can find the full list of Material Parameters in the Material Definitions Properties overview. The following sections introduce you to the most commonly used cases. You typically initialize Material objects in the simpleInitApp() method, and configure them using the setters described here. Then load the Materials using myGeometry.setMaterial(mat). +

+ +
+ +

Code Sample

+
+ +

+ +The following samples assume that you loaded a Geometry called myGeometry, and want to assign a material to it. +

+ +

+This example creates a simple unshaded blue material: Use it for abstract objects that need no illumination/shading, e.g. sky, GUI and billboard nodes, tiles/cards, or toons. +

+
Spatial myGeometry = assetManager.loadModel("Models/Teapot/Teapot.j3o");
+Material mat = new Material(assetManager,  // Create new material and...
+    "Common/MatDefs/Misc/Unshaded.j3md");  // ... specify .j3md file to use (unshaded).
+mat.setColor("Color", ColorRGBA.Blue);     // Set some parameters, e.g. blue.
+myGeometry.setMaterial(mat);               // Use new material on this Geometry.
+ +

+This example creates a -illuminated blue material. Use it for illuminated, naturalistic objects, such as characters, buildings, terrains, vehicles. Needs a light source, otherwise it will be invisible. +

+
Spatial myGeometry = assetManager.loadModel("Models/Teapot/Teapot.j3o");
+Material mat = new Material(assetManager,  // Create new material and...
+    "Common/MatDefs/Light/Lighting.j3md"); // ... specify .j3md file to use (illuminated).
+mat.setBoolean("UseMaterialColors",true);  // Set some parameters, e.g. blue.
+mat.setColor("Ambient", ColorRGBA.Blue);   // ... color of this object
+mat.setColor("Diffuse", ColorRGBA.Blue);   // ... color of light being reflected
+myGeometry.setMaterial(mat);               // Use new material on this Geometry.
+ +

+

Do you reuse Materials? You can store Material properties in a .j3m file and load all properties in one line using +

+
myGeometry.setMaterial( assetManager.loadMaterial("Materials/myMaterial.j3m"));
+ +

+ +

+

+ +
+ +

Colored or Textured

+
+ +

+ +Every Material must have at least Material Colors or Textures. Some optional material features also require a combination of both. +

+ +
+ +

Colored

+
+ +

+ +To give an unshaded material a color: +

+
    +
  1. Specify the color property
    mat.setColor("Color", ColorRGBA.Blue); // with Unshaded.j3md
    +
    +
  2. +
+ +

+ +To give an Phong-illuminated material a color: +

+
    +
  1. Activate material colors:
    mat.setBoolean("UseMaterialColors",true); // with Lighting.j3md
    +
    +
  2. +
  3. Specify at least Diffuse and Ambient colors. Set both to the same color in the standard case.
    mat.setColor("Diffuse", ColorRGBA.Blue ); // with Lighting.j3md
    +mat.setColor("Ambient", ColorRGBA.Blue ); // with Lighting.j3md
    +
    +
  4. +
+ +
+ +

Textured

+
+ +

+ +To give an unshaded material a texture: +

+ + +

+ +To give a Phong-illuminated material a texture: +

+ + +

+ +

It can happen that you load textures at different scales, for example, your blades of grass may look as big as twigs, or your spaceship's heat tiles may look like small bathroom tiles. Then you need to adjust the texture scale, either bigger (< 1.0f) or smaller (< 1.0f). +

+
geometry.scaleTextureCoordinates(new Vector2f(0.5f, 0.5f));
+ +

+ +

+

+ +

+All other Texture Maps or Material settings are optional. If used skillfully, they make your model look really spiffy. +

+ +
+ +

(Optional) Bumpy

+
+ +

+ +A NormalMap (also called BumpMap) is an extra colored texture that describes the fine bumpy details of the Material surface. E.g. fine cracks, pores, creases, notches. Using a BumpMap is more efficient than trying to shape the mesh to be bumpy. +

+ +

+To add a BumpMap (this only makes sense for illuminated Materials): +

+
    +
  1. Generate normal vectors information for the Mesh (not for the Geometry!) using com.jme3.util.TangentBinormalGenerator.
    TangentBinormalGenerator.generate(mesh);
    +
    +
  2. +
  3. Specify the NormalMap texture for the Material.
    mat.setTexture("NormalMap", assetManager.loadTexture("Textures/wood_normal.png")); // with Lighting.j3md
    +
    +
  4. +
+ +

+ + +

+ +
+ +

(Optional) Shiny

+
+ +

+ +To activate Shininess (this only makes sense for illuminated Materials): +

+
    +
  1. Specify the Shininess intensity the Material.
    +Shininess is a float value between 1 (rough surface with blurry shininess) and 128 (very smooth surface with focused shininess)
    mat.setFloat("Shininess", 5f);
    +
    +
  2. +
  3. Activate material colors:
    mat.setBoolean("UseMaterialColors",true);
    +
    +
  4. +
  5. Specify the Specular and Diffuse colors of the shiny spot.
    +Typically you set Specular to the ColorRGBA value of the light source, often RGBA.White.
    mat.setColor("Specular",ColorRGBA.White);
    +mat.setColor("Diffuse",ColorRGBA.White);
    +
    +
  6. +
  7. (Optional) Specify a SpecularMap texture.
    +You optionally hand-draw this grayscale texture to outline in detail where the surface should be more shiny (whiter grays) and where less (blacker grays). If you don't supply a SpecularMap, the whole material is shiny everywhere.
    mat.setTexture("SpecularMap", assetManager.loadTexture("Textures/metal_spec.png")); // with Lighting.j3md
    +
    +
  8. +
+ +

+ +To deactivate shininess +

+ + +
+ +

(Optional) Glow

+
+ +

+ +To activate glow: +

+
    +
  1. Add one BloomFilter PostProcessor in your simpleInitApp() method (only once, it is used by all glowing objects).
    FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
    +BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
    +fpp.addFilter(bloom);
    +viewPort.addProcessor(fpp);
    +
    +
  2. +
  3. Specify a Glow color.
    +A ColorRGBA value of your choice, e.g. choose a warm or cold color for different effects, or white for a neutral glow.
    mat.setColor("GlowColor",ColorRGBA.White);
    +
    +
  4. +
  5. (Optional) Specify a GlowMap texture.
    +This texture outlines in detail where the DiffuseMap texture glows. If you don't supply a GlowMap, the whole material glows everwhere.
    mat.setTexture("GlowMap", assetManager.loadTexture("Textures/alien_glow.png"));
    +
    +
  6. +
+ +

+ +To deactivate glow: +

+ + +

+ +Learn more about Bloom and Glow. +

+ +
+ +

(Optional) Transparent

+
+ +

+ +Most Material Definitions support an alpha channel to make a model opaque, translucent, or transparent. +

+ + +

+ +To make a Geometry transparent or translucent: +

+
    +
  1. Specify which areas you want to be transparent or translucent by specifying the alpha channel:
    +
      +
    • (For colored Materials) In any RGBA color, the first three are Red-Green-Blue, and the last float is the Alpha channel. For example, to replace ColorRGBA.Red with a translucent red:
      mat.setColor("Color", new ColorRGBA(1,0,0,0.5f));
      +
      +
    • +
    • (For textured Materials) Supply an AlphaMap that outlines which areas are transparent.
      setTexture("AlphaMap", assetManager.loadTexture("Textures/window_alpha.png"));
      +
      +
    • +
    • (For textured Materials) If the DiffuseMap has an alpha channel, use:
      mat.setBoolean("UseAlpha",true);
      +
      +
    • +
    +
  2. +
  3. Specify BlendMode Alpha for the Material.
    mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
    +
    +
  4. +
  5. Put the Geometry (not the Material!) in the appropriate render queue bucket.
    +Objects in the translucent bucket (e.g. particles) are not affected by SceneProcessors (e.g. shadows). Obejcts in the transparent bucket (e.g. foliage) are affected by SceneProcessors (e.g. shadows).
    +
      +
    • geo.setQueueBucket(Bucket.Translucent); 
      +
      +
    • +
    • geo.setQueueBucket(Bucket.Transparent); 
      +
      +
    • +
    +
  6. +
  7. (Optional) Specify other material settings.
    +
  8. +
+
+ + + + + + + + + + + + + + + +
Standard Material TransparencyDescriptionExample
getAdditionalRenderState().setBlendMode(BlendMode.Off);This is the default, no transparency.Use for all opaque objects like walls, floors, people???
getAdditionalRenderState().setBlendMode(BlendMode.Alpha);Interpolates the background pixel with the current pixel by using the current pixel's alpha.This is the most commonly used BlendMode for transparency and translucency: Frosted window panes, ice, glass, alpha-blended vegetation textures???
getAdditionalRenderState().setDepthWrite(false);Disables writing of the pixel's depth value to the depth buffer.Deactivate this on Materials if you expect two or more transparent/translucent objects to be obscuring one another, but you want to see through both.
getAdditionalRenderState().setAlphaTest(true)
+getAdditionalRenderState().setAlphaFallOff(0.5f);
Enables Alpha Testing and uses an AlphaDiscardThreshold as alpha fall-off value. This means that gradients in the AlphaMap are no longer interpreted as soft translucency, but parts of the texture become either fully opaque or fully transparent. Only pixels above the alpha threshold (e.g. 0.5f) are rendered. Activate Alpha Testing for (partially) transparent objects such as foliage, hair, etc.
+Deactivate Alpha Testing for gradually translucent objects, such as colored glass, smoked glass, ghosts.
+ +

+ +

It is possible to load a DiffuseMap texture that has an Alpha channel, and combine it with an underlying Material Color. +

+
mat.setBoolean("UseAlpha",true);
+ +

+ The Material Color bleeds through the transparent areas of the top-layer DiffuseMap texture. In this case you do not need BlendMode Alpha ??? because it's not the whole Material that is transparent, but only one of the texture layers. You use this bleed-through effect, for example, to generate differently colored uniforms, animals, or plants, where each Material uses the same "template" DiffuseMap texture but combines it with a different color. +

+

+ +
+ +

(Optional) Wireframe

+
+ +

+ +Additionally to the above settings, you can switch off and on a wireframe rendering of the mesh. Since a wireframe has no faces, this temporarily disables the other Texture Maps. + +

+
+ + + + + + +
Material PropertyDescriptionExample
getAdditionalRenderState().setWireframe(true);Switch to showing the (textured) Material in wireframe mode. The wireframe optionally uses the Material's Color value.Use wireframes to debug meshes, or for a "matrix" or "holodeck" effect.
+
+ material, + texture, + effect, + wireframe, + light, + documentation +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html new file mode 100644 index 000000000..91afc1e1e --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html @@ -0,0 +1,126 @@ + +

Math Cheat Sheet

+
+ +
+ +

Formulas

+
+ +

+Note: Typically you have to string these formulas together. Look in the table for what you want, and what you have. If the two are not the same line, than you need conversion steps inbetween. E.g. if you have an angle in degrees, but the formula expects radians: 1) convert degrees to radians, 2) use the radians formula on the result. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
I have???I want???Formula
normalized direction and length
+n1,d1
a vector that goes that far in that direction
+new direction vector v0
v0 = n1.mult(d1)
point and direction vector
+p1,v1
to move the point
+new point p0
p0 = p1.add(v1)
direction, position and distance
+v1,p1,dist
Position at distance
+p2
v1.normalzeLocal()
+scaledDir = v1.mult(dist)
+p2 = p1.add(scaledDir)
two direction vectors or normals
+v1,v2
to combine both directions
+new direction vector v0
v0 = v1.add(v2)
two points
+p1, p2
distance between two points
+new scalar d0
d0 = p1.subtract(p2).length()
+d0 = p1.distance(p2)
two points
+p1, p2
the direction from p2 to p1
+new direction vector v0
v0 = p1.substract(p2)
two points, a fraction
+p1, p2, h=0.5f
the point "halfway" (h=0.5f) between the two points
+new interpolated point p0
p0 = FastMath.interpolateLinear(h,p1,p2)
a direction vector, an up vector
+v, up
A rotation around this up axis towards this direction
+new Quaternion q
Quaternion q = new Quaternion();
+q.lookAt(v,up)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
I have???I want???Formula
angle in degrees
+a
to convert angle a from degrees to radians
+new float angle phi
phi = a / 180 * FastMath.PI;
+OR
+phi=a.mult(FastMath.DEG_TO_RAD);
angle in radians
+phi
to convert angle phi from radians to degrees
+new float angle a
a = phi * 180 / FastMath.PI
radian angle and x axis
+phi, x
to rotate around x axis
+new quaternion q0
q0.fromAngleAxis( phi, Vector3f.UNIT_X )
radian angle and y axis
+phi, y
to rotate around y axis
+new quaternion q0
q0.fromAngleAxis( phi, Vector3f.UNIT_Y )
radian angle and z axis
+phi, z
to rotate around z axis
+new quaternion q0
q0.fromAngleAxis( phi, Vector3f.UNIT_Z )
several quaternions
+q1, q2, q3
to combine rotations, in that order
+new quaternion q0
q0 = q1.mult(q2).mult(q3)
point and quaternion
+p1, q1
to rotate the point around origin
+new point p0
p0 = q1.mult(p1)
angle in radians and radius
+phi,r
to arrange or move objects horizontally in a circle (with y=0)
+x and z coordinates
float x = FastMath.cos(phi)*r;
+float z = FastMath.sin(phi)*r;
+ +
+ +

Local vs Non-local methods?

+
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html new file mode 100644 index 000000000..c03c50b16 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html @@ -0,0 +1,259 @@ + +

Multi-Media Asset Pipeline

+
+ +

+ +Assets are files that are not code. Your multi-media assets includes, for example, your textures (image files), models (mesh files), and sounds (audio files). + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
DODON'T
Save original models plus textures into assets/Textures. Don't leave textures or models in a folder outside your JME project: The game cannot load or reference them from there.
Save sounds into assets/Sounds. Don't leave audio files in a folder outside your JME project: The game cannot load or reference them from there.
Create low-polygon models. Don't create high-polygon models, they render too slow to be useful in games.
Only use Diffuse Map, Normal Map, Glow Map, Specular Map in your models' materials. Don't use unsupported material properties that are not listed in the Materials Overview.
Use UV texture / texture atlases / baking for each texture map. Don't create models based on multiple separate textures, it will break the model into separate meshes.
Convert original models to JME3's .j3o format. Move .j3o files into assets/Models. Don't reference original Blender/Ogre/OBJ files in your load() code, because these unoptimized files are not automatically packaged into the final JAR.
+ +

+ +Read on for details. +

+ +
+ +

Use The Assets Folder

+
+ +

+ +Store your assets in subfolders of your project's assets directory. The assets directory is the default path where a JME game's Asset Manager looks for files to load. +

+
jMonkeyProjects/MyGame/assets/Interface/ # .font, .jpg, .png, .xml
+jMonkeyProjects/MyGame/assets/MatDefs/   # .j3md
+jMonkeyProjects/MyGame/assets/Materials/ # .j3m
+jMonkeyProjects/MyGame/assets/Models/    # .j3o
+jMonkeyProjects/MyGame/assets/Scenes/    # .j3o
+jMonkeyProjects/MyGame/assets/Shaders/   # .j3f, .vert, .frag
+jMonkeyProjects/MyGame/assets/Sounds/    # .ogg, .wav
+jMonkeyProjects/MyGame/assets/Textures/  # .jpg, .png; also .mesh.xml+.material, .mtl+.obj, 
+ +

+Prepare the asset folder structure for your individual project: + +

+
    +
  1. Agree on a directory structure with the graphic designers.
    +
  2. +
  3. Create subfolders of assets in any way that suits your project (see example above). Stick with one system.
    +
      +
    • If different assets belong together, create a parallel subdirectory structure for them.
      +Example: For car models, create Textures/vehicles/car1/, Materials/vehicles/car1/, Models/vehicles/car1/, , Sounds/vehicles/car1/ (etc) directories now.
      +
    • +
    +
  4. +
  5. Agree on a file naming and numbering scheme with the graphic designers.
    +
      +
    • Are some assets used interchangeably? Systematic naming and numbering lets developers easily swap out assets by swapping out parts of the path String.
      +
    • +
    • Decide on naming standards for naming interactive parts (arms/legs) of animated models.
      +
    • +
    +
  6. +
+ +

+ + +

+ +

+See also: +

+ + +
+ +

Create Textures and Materials

+
+ +

+ +Install a graphic editor such as Gimp or Photoshop. Consult the graphic editor's documentation for specific details how to do the following tasks. + +

+
    +
  1. Create textures in a graphic editor.
    +
      +
    • Save all textures to your prepared subfolders in the assets/Textures directory.
      +
    • +
    +
  2. +
  3. (Optional) If you plan to use JME materials that you set programmatically from the code, create .j3m materials in the SDK.
    +
      +
    • Save these .j3m files into the assets/Materials directory.
      +
    • +
    +
  4. +
+ +

+ +Storing the textures inside your project directory is necessary for the paths in JME's binary model files (.j3o) to work. Treat the paths of your assets like class names of java classes, they define a specific asset. When you later generate .j3o files, compile class files, and distribute the project, paths and files need to be available in their final absolute form. It is imperative to keep the same directory structure from beginning to end. +

+ +

+If you ever change the assets directory structure, you will have to do manual refactoring (just as for Java package name changes): You will need to re-export all affected models, regenerate all affected .j3o files, and manually update all affected paths in your code. +

+ +
+ +

Create 3D Models

+
+ +

+ +Install a mesh editor such as Blender or 3D Studio MAX. Reuse textures and materials as much as possible. Consult the mesh editor's documentation for specific details how to do the following tasks. +

+ +

+

Note that UV coords are part of the mesh and not part of the material, so if you import your mesh successfully you can later apply the texture again and it will map correctly. +

+ +

+
    +
  1. Create 3D models in a mesh editor.
    +
      +
    1. Create simple low-polygon models. High-polygon models may look pretty in static 3D art contests, but they unnecessarily slow down dynamic games.
      +
    2. +
    3. Create materials for your models either in the 3D editor, or in the jME3 SDK. Only use Diffuse Map (minimum), Normal Map, Glow Map, and Specular Map.
      +Every material feature not listed in the Materials Overview is unsupported and ignored by JME3.
      +
    4. +
    5. Unwrap the model in the 3D editor and generate a UV texture (i.e. one texture file that contains all the pieces of one model from different angles).
      +Don't use multiple separate texture files with one model, it will break the model into several meshes.
      +
    6. +
    +
  2. +
  3. Export the model mesh in one of the following formats: .blend, Wavefront .OBJ/.MTL, Ogre .mesh/.material/.scene.
    +
      +
    1. Bake each texture into one file when exporting. (Create a Texture Atlas.)
      +
    2. +
    3. Save exported models to subfolders of the assets/Textures directory, together with their Textures. (for now)
      +
    4. +
    +
  4. +
+ +

+ +See also: +

+ +

+

When I load the model in JME, why does it look different than in the 3D editor?
+3D models will never look identical in a game engine and in a mesh editor. Mesh editors are optimized for high-quality offline rendering, and many of the material and texture options simply do not work in a live rendering context. Also, the shaders that render the materials in JME are different than in your mesh editor's renderer. Remind your graphic designers to only focus on features that game engines support. +

+ +

+ +
+ +

Convert 3D Models to .j3o Format

+
+ +

+ +Convert all models and scenes to jME3's binary .j3o format to load() them. You use the jMonkeyEngine SDK to do the conversion. + +

+
    +
  1. Confirm that you exported the model into the assets/Textures directory (or subdirectories) together with all its textures.
    +
  2. +
  3. In the SDK, right-click the model and choose "Convert to j3o Binary".
    +The paths in the j3o now reference files with an absolute assets/Textures/??? path.
    +
  4. +
  5. Now, move the .j3o into the corresponding assets/Models/ or assets/Scenes/ directory.
    +
  6. +
  7. Use the AssetManager to load() the .j3o files.
    +
  8. +
+ +

+ +This process ensures that the texture paths are correct, and it also keeps your assets/Models folder free from textures. You can reuse your set of textures for many models. +

+ +

+Must I convert to .j3o? ??? Yes! +

+ + +

+ +

Important: Unoptimized external model files (.mesh.xml, .material, .obj, .mat, .blend, etc) are not bundled by the default build script into the final executables! If you try to run executables containing code that loads non-.j3o models, you get a Runtime Error (resource not found). The final application code should only reference .j3o files. (Note that you will not get this runtime error when running development builds straight from the SDK!) +

+

+ +
+ +

See Also

+
+ +
+ spatial, + node, + mesh, + geometry, + scenegraph, + sdk +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html new file mode 100644 index 000000000..43461ed27 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html @@ -0,0 +1,96 @@ + +

Optimization reference

+
+ +

+This page is intended as a reference collection of optimization tricks that can be used to speed up JME3 applications. + +

+ +
+ +

Maintain low Geometry count

+
+ +

+The more Geometry objects are added to the scene, the harder it gets to handle them in a speedy fashion. +The reason for this is that a render command must be done for every object, potentially creating a bottleneck between the CPU and the graphics card. +

+ +

+Possible optimization techniques +

+ + +

+Side-effects +

+ + +
+ +

Avoid creating new objects

+
+ +

+ +When you use math operations like vectorA.mult(vectorB);, they create new objects for the result. These objects have to be garbage collected when you don't use them anymore. +

+ +

+Check your math operations for opportunities to use the local version of the math operations, e.g. vectorA.multLocal(vectorB). Local methods store the result in vectorA and do not create a new object. Use local methods if you do not need to keep the previous vectorA. +

+ +
+ +

Avoid large objects in physics

+
+ +

+ +To offload much computation to the less CPU intense physics broadphase collision check, avoid having large meshes that cover e.g. the whole map of your level. Instead, separate the collision shapes into multiple smaller chunks. Obviously, don't exaggerate the chunking, because having excessive amounts of physics objects similarly cause performance problems. +

+ +
+ +

Check the Statistics

+
+ +

+ +SimpleApplication displays a HUD with statistics. Use app.setDisplayStatView(true); to activate it, and false to deactivate it. +The StatsView counts Objects,Uniforms,Triangles,Vertices are in the scene, and it counts how many FrameBuffers, Textures, or Shaders: +

+ + +

+ +For example, Textures (M) tells you how many textures are currently in OpenGL memory. +

+ +

+Generally jME3 is well optimized and optimizes these things correctly. Read statsview to learn the details about how to interpret the statistics, how to tell whether your values are off, or whether they point out a problem. +

+
+ performance +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/scene-graph.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/scene-graph.png new file mode 100644 index 000000000..2fa05d56f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/scene-graph.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/simpleapplication.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/simpleapplication.html new file mode 100644 index 000000000..db385d0bc --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/simpleapplication.html @@ -0,0 +1,354 @@ + +

SimpleApplication

+
+ +

+ +The base class of the jMonkeyEngine3 is com.jme3.app.SimpleApplication. Your first game's Main class extends SimpleApplication directly. When you feel confident you understand the features, you will typically extend SimpleApplication to create a custom base class for the type of games that you want to develop. +

+ +

+SimpleApplication gives you access to standard game features, such as a scene graph (rootNode), an asset manager, a user interface (guiNode), input manager, audio manager, a physics simulation, and a fly-by camera. You call app.start() and app.stop() on your game instance to start or quit the application. +

+ +

+

For each game, you (directly or indirectly) extend SimpleApplication exactly once as the central class. If you need access to any SimpleApplication features from another game class, make the other class extend AbstractAppState (don't extend SimpleApplication once more). +

+

+ +

+The following code sample shows the typical base structure of a jME3 game: +

+
import com.jme3.app.SimpleApplication;
+ 
+public class MyBaseGame extends SimpleApplication {
+ 
+    public static void main(String[] args){
+        MyBaseGame app = new MyBaseGame();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+       /* Initialize the game scene here */
+    }
+ 
+    @Override
+    public void simpleUpdate(float tpf) {
+       /* Interact with game events in the main loop */
+    }
+ 
+    @Override
+    public void simpleRender(RenderManager rm) {
+       /* (optional) Make advanced modifications to frameBuffer and scene graph. */
+    }
+}
+ +

+Let's have a look at the API of the base class. +

+ +
+ +

Application Class

+
+ +

+ +Internally, com.jme3.app.SimpleApplication extends com.jme3.app.Application. The Application class represents a generic real-time 3D rendering jME3 application (i.e., not necessarily a game). Typically, you do not extend com.jme3.app.Application directly to create a game. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Application class fieldsPurpose
viewPort
+getViewPort()
The view object for the default camera. You can register advanced post-processor filters here.
settings
+setSettings()
Use this AppSettings object to specify the display width and height (by default 640x480), color bit depth, z-buffer bits, anti-aliasing samples, and update frequency, video and audio renderer, asset manager. See: AppSettings.
cam
+getCamera()
The default camera provides perspective projection, 45?? field of view, near plane = 1 wu, far plane = 1000 wu.
assetManager
+getAssetManager()
An object that manages paths for loading models, textures, materials, sounds, etc. By default the Asset Manager paths are relative to your project's root directory.
audioRenderer
+getAudioRenderer()
This object gives you access to the jME3 audio system.
listener
+getListener()
This object represents the user's ear for the jME3 audio system.
inputManager
+getInputManager()
Use the inputManager to configure your custom inputs (mouse movement, clicks, key presses, etc) and set mouse pointer visibility.
stateManager
+getStateManager()
You use the Application's state manager to activate AppStates, such as Physics.
+
+ + + + + + + + + + + + + + + + + + +
Application methodsPurpose
setPauseOnLostFocus(true)Set this boolean whether the game loop should stop running when ever the window loses focus (typical for single-player game). Set this to false for real-time and multi-player games that keep running.
start()Call this method to start a jME3 game. By default this opens a new jME3 window, initializes the scene, and starts the event loop.
restart()Loads modified AppSettings into the current application context.
stop()Stops the running jME3 game and closes the jME3 window.
start(Type.Headless) etcSwitch Context com.???jme3.???system.???JmeContext.Type when starting the application:
+Type.Display ??? jME application runs in a window of its own. (This is the default.)
+Type.Canvas ??? jME application is embedded in a Swing Canvas.
+Type.Headless ??? jME application runs its event loop without calculating any view and without opening any window. Can be used for a Headless Server application.
+Type.OffscreenSurface ??? jME application view is not shown and no window opens, but everything calculated and cached as bitmap (back buffer) for use by other applications.
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Internal class field/methodPurpose
context
+getContext()
The application context contains the renderer, AppSettings, timer, etc. Typically, you do not directly access the context object.
inputEnabledthis internal boolean is true if you want the system to listen for user inputs, and false if you just want to play a non-interactive scene. You change the boolean using AppSettings.
keyInput, mouseInput
+joyInput, touchInput
Default input contexts for keyboard, mouse, and joystick. Internally used to enable handling of joysticks or touch devices. The base classes contain key and mouse button enums.
renderManager
+getRenderManager()
+renderer
+getRenderer();
Low-level and high-level rendering interface. Mostly used internally.
guiViewPort
+getGuiViewPort()
The view object for the orthogonal GUI view. Only used internally for HUDs.
timerAn internally used update loop timer. You already have access to the float tpf in the simpleUpdate() loop to time actions according to the time per frame.
pausedBoolean is used only internally during runtime to pause/unpause a game. (You need to implement your own isRunning boolean or so.)
+ +
+ +

SimpleApplication Class

+
+ +

+ +The com.jme3.app.SimpleApplication class extends the generic com.jme3.app.Application class. SimpleApplication makes it easy to start writing a game because it adds typical functionality: +

+ + +

+ +Additional to the functionality that Application brings, SimpleApplication offers the following methods and fields that can be used, for example, inside the simpleInitApp() method: + +

+
+ + + + + + + + + + + + +
SimpleApplication Class FieldPurpose
rootNode
+getRootNode()
The root node of the scene graph. Attach a Spatial to the rootNode and it appears in the 3D scene.
guiNode
+getGuiNode()
Attach flat GUI elements (such as HUD images and text) to this orthogonal GUI node to make them appear on the screen.
flyCam
+getFlyByCamera()
The default first-person fly-by camera control. This default camera control lets you navigate the 3D scene using the preconfigured WASD and arrow keys and the mouse.
+
+ + + + + + + + + + + + + + + +
SimpleApplication MethodPurpose
loadStatsView();Call this method to print live statistic information to the screen, such as current frames-per-second and triangles/vertices counts. You use this info typically only during development or debugging.
loadFPSText();Call this method to print the current framerate (frames per second) to the screen.
setDisplayFps(false);A default SimpleApplication displays the framerate (frames per second) on the screen. You can choose to deactivate the FPS display using this command.
setDisplayStatView(false);A default SimpleApplication displays mesh statistics on the screen using the com.jme3.app.StatsView class. The information is valuable during the development and debugging phase, but for the release, you should hide the statistics HUD.
+
+ + + + + + + + + + + + +
SimpleApplication InterfacePurpose
public void simpleInitApp()Override this method to initialize the game scene. Here you load and create objects, attach Spatials to the rootNode, and bring everything in its starts position. See also Application States for best practices.
public void simpleUpdate(float tpf)Override this method to have access to the update loop. Use this loop to poll the current game state and respond to changes, or to let the game mechanics generate encounters and initiate state changes. Use tpf (time per frame) as a factor to time events. For more info on how to hook into the update loop, see Application States and Custom Controls.
public void simpleRender(RenderManager rm)Optional: Override this method to implement advanced modifications of the frameBuffer and scene graph.
+ +

+ +

Use app.setShowSettings(true); to present the user with a splashscreen and the built-in display settings dialog when starting the game; or use app.setShowSettings(false); to hide the buil-in screen (in this case, you may want to provide a custom splashscreen and settings panel). Set this boolean before calling app.start() in the main() method of the SimpleApplication. See also AppSettings. +

+

+ +
+ +

Default Input Mappings

+
+ +

+ +The following default navigational input actions are mapped by the default flyCam control in a SimpleApplication: You can use these mappings for debugging and testing until you implement custom input handling. + +

+
+ + + + + + + + + + + + + + + +
KeyAction
KEY_ESCAPEQuits the game by calling app.stop()
KEY_CDebug key: Prints camera position, rotation, and direction to the out stream.
KEY_MDebug key: Prints memory usage stats the out stream.
F5Hides or shows the statistics the bottom left.
+ +

+ +As long as the flyCam is enabled, the following so-called "WASD" inputs, including MouseLook, are available: + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Camera MotionKey or Mouse Input
Move ForwardKEY_W
Move Left (Strafe)KEY_A
Move BackwardKEY_S
Move Right (Strafe)KEY_D
Move Vertical UpwardKEY_Q
Move Vertical DownwardKEY_Z
Rotate LeftKEY_LEFT, or move mouse horizontally left (-x)
Rotate RightKEY_RIGHT, or move mouse horizontally right (+x)
Rotate UpKEY_UP, or move mouse vertically forward (+y)
Rotate DownKEY_DOWN, or move mouse vertically backward (-y)
RotateBUTTON_LEFT, or hold left mouse button and drag to rotate
Zoom InAXIS_WHEEL, or scroll mouse wheel backward
Zoom OutAXIS_WHEEL, or scroll mouse wheel forward
+ +
+ +

Defaults and Customization

+
+ +

+ +By default, a SimpleApplication displays Statistics (new StatsAppState()), has debug output keys configured (new DebugKeysAppState()), and enables the flyCam (new FlyCamAppState()). You can customize which you want to reuse in your SimpleApplication. +

+ +

+The following example shows how you can remove one of the default AppStates, in this case, the FlyCamAppState: + +

+ +
+ display, + basegame, + documentation, + intro, + intermediate, + init, + input, + game, + loop, + rootnode, + application, + simpleapplication +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios-deployment.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios-deployment.png new file mode 100644 index 000000000..68e6fe883 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios-deployment.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html new file mode 100644 index 000000000..73c77bfcd --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html @@ -0,0 +1,101 @@ + +

iOS Deployment

+
+ +

+To use iOS deployment you need a computer running MacOSX and a version of Xcode 4.0+ installed. To deploy to a device or the Apple App Store, you need an Apple developer account. +

+ +

+iOS deployment works via cross-compilation to native iOS ARM code, there is no virtual machine running on the device. The Avian JVM supports this feature while maintaining general compatibility to OpenJDK and JNI for native access. The minimum compatible iOS deployment target is 4.3. +

+ +

+

Note that at the moment this option is in pre-alpha state and the system runs on a null renderer. This means there is no visual or audio output. You can however use the current system to explore the options and test cross-compiling your applications. +

+

+ +
+ +

Enabling iOS deployment

+
+ +

+To enable iOS deployment, go to the project settings and under "Application???iOS" select the "Enable iOS deployment" checkbox, adapt the application ID and then press OK. +

+ +

+ +

+ +

+After enabling deployment, a new ios directory is created in the project root that contains a project and a src folder. The ios/project folder contains an Xcode project that you will use to build and run the final iOS application for both iPhone and iOS. The ios/src folder contains java and native source files for bridging iOS and native code, you can add .java and .m files with your own iOS code here. +

+ +

+

When you enable iOS deployment for the first time or any time that the Avian library and OpenJDK is updated, they will be extracted to your SDK settings folder, wait until it has been extracted before building an iOS-enabled project. +

+ +

+ +
+ +

Building the iOS binaries

+
+ +

+The iOS binaries are automatically built when you have iOS deployment enabled and build your project. +

+ +

+When the iOS binaries are built, all needed classes, including a complete copy of the OpenJDK7 classes are run through a proguard process that strips out the unnecessary classes for the project and optimizes the code for the platform. This happens without changing the naming structure so that reflection etc. still works. If necessary, adapt the proguard options in the ios properties file. +

+ +

+After the iOS classpath has been created the avian compiler is used to create a native .o file from the classpath for both arm (device) and i386 (simulator). Furthermore the other needed avian .o files are extracted and a library list is compiled which is referenced in the Xcode project. +

+ +
+ +

Running and deploying the application

+
+ +

+To run the application, open the Xcode project under ios/project in Xcode and press the run button. You can make changes to the UI and native invocation classes in the Xcode project as well. From here you can also deploy the application to your devices or the App Store. +

Note that you should also adapt the project settings like application name and registration package in Xcode before deploying the final application. +

+ +

+ +
+ +

Creating native and java code for iOS

+
+ +

+To bridge between native and java code, JNI is used like in a normal java application. The ios/src folder is for Java and C/Obj-C source files that are specific to your iOS application. In these java files you have access to the full project classpath as well as the iOS-specific jME3 classes. +

+ +

+The JmeAppHarness.java class is initialized and called from native code through the default project and you can extend it to perform other native operations. It has a simple native popup method. The JmeAppHarness.m file contains the native method needed for that popup. +

+ +

+Effectively native code can reside in both the Xcode project and in the ios/src folder. To keep the dependencies clean and make code reusable you should try to put generic native code that does not depend on the Xcode project in the ios/src folder. You can also mix and match ARC and non-ARC code through this by converting the main project to use ARC and putting code with manual memory management in the ios/src folder. +

+ +

+Java code for iOS should be in the ios/src folder as well for clean separation, its also the only place where they will be compiled with a reference to the iOS specific jME classes. For information on how to connect your application code and device specific code, see the notes in the android deployment documentation. +

+
+ documentation, + iOS, + Mac, + MacOS, + deployment, + platform +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html new file mode 100644 index 000000000..653234adf --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html @@ -0,0 +1,1083 @@ + +

Introduction to Mathematical Functionality

+
+ +

+ +It's a fact of life, math is hard. Unfortunately, 3D graphics require a fair bit of knowledge about the subject. Fortunately, jME is able to hide the majority of the details away from the user. Vectors are the fundamental type in the 3D environment, and it is used extensively. Matrices are also a basic necessity of 3D for representing linear systems. Quaternions are perhaps the most powerful and complicated of the basic types and are used for rotation in jME. +

+ +

+I'll discuss how these are used in the system for the core functionality. Including Transforming, Visibility Determination, Collision Detection, and the Coordinate System. Note, that these are low level details. Further chapters will discuss how to use these various systems from a high level perspective. +

+ +

+To get a visual introduction to math in jME3 for the absolute beginner, check out our Math for Dummies introduction class. +

+ +
+ +

Coordinate System

+
+ +
+ +

Definition

+
+ +

+ +A coordinate system consists of an origin (single point in space) and three coordinate axes that are each unit length and mutually perpendicular. The axes can be written as the column of a Matrix, R = [U1|U2|U3]. In fact, this is exactly how CameraNode works. The coordinate system defined by Camera is stored in a Matrix. +

+ +

+jME uses a Right-Handed coordinate system (as OpenGL does). +

+ +

+The definition of a coordinate system is defined in jME by the properties sent to Camera. There are no error checks to insure that: 1) the coordinate system is right-handed and 2) The axes are mutually perpendicular. Therefore, if the user sets the axes incorrectly, they are going to experience very odd rendering artifacts (random culling, etc). +

+ +

+ +

+ +
+ +

Transformations

+
+ +

+ +Transformations define an operation that converts points from one coordinate system to another. This includes translation, rotation and scaling. In jME, local transforms are used to represent the positioning of objects relative to a parent coordinate system. While, world transforms are used to represent the positioning of objects in a global coordinate system. +

+ +
+ +

Visibility Determination

+
+ +

+ +Visibility Determination concerns itself with minimizing the amount of data that is sent to the graphics card for rendering. Specifically, we do not want to send data that will not be seen. Data not sent to the graphics card is said to be culled. The primary focus of this section is Frustum Culling based on the Camera's view frustum. In essence, this frustum creates six standard view planes. The BoundingVolume of an object is tested against the frustum planes to determine if it is contained in the frustum. If at any point the object's bounding is outside of the plane, it is tossed out and no longer processed for rendering. This also includes any children that it managed, allowing fast culling of large sections of the scene. +

+ +
+ +

Fundamental Types

+
+ +
+ +

ColorRGBA

+
+ +
+ +

Definition

+
+ +

+ +ColorRGBA defines a color value in the jME library. The color value is made of three components, red, green and blue. A fourth component defines the alpha value (transparent) of the color. Every value is set between [0, 1]. Anything less than 0 will be clamped to 0 and anything greater than 1 will be clamped to 1. +

+ +

+Note: If you would like to "convert" an ordinary RGB value (0-255) to the format used here (0-1), simply multiply it with: 1/255. +

+ +
+ +

jME Class

+
+ +

+ +ColorRGBA defines a few static color values for ease of use. That is, rather than: +

+
ColorRGBA red = new ColorRGBA(1,0,0,1);
+object.setSomeColor(red);
+ +

+you can simply say: + +

+
object.setSomeColor(ColorRGBA.red)
+ +

+ColorRGBA will also handle interpolation between two colors. Given a second color and a value between 0 and 1, a the owning ColorRGBA object will have its color values altered to this new interpolated color. +

+ +
+ +

Matrix

+
+ +

+ +See
+ +and +

+ +
+ +

Definition

+
+ +

+ +A Matrix is typically used as a linear transformation to map vectors to vectors. That is: Y = MX where X is a Vector and M is a Matrix applying any or all transformations (scale, rotate, translate). +

+ +

+There are a few special matrices: +

+ +

+zero matrix is the Matrix with all zero entries. +

+
+ + + + + + + + + +
000
000
000
+ +

+ +The Identity Matrix is the matrix with 1 on the diagonal entries and 0 for all other entries. +

+
+ + + + + + + + + +
100
010
001
+ +

+ +A Matrix is invertible if there is a matrix M-1 where MM-1 = M-1M = I. +

+ +

+ +The transpose of a matrix M = [mij] is MT = [mji]. This causes the rows of M to become the columns of MT. +

+
+ + + + + + + + + +
111 123
222 ??? 123
333 123
+ +

+ +A Matrix is symmetric if M = MT. +

+
+ + + + + + + + + +
XAB
AXC
BCX
+ +

+Where X, A, B, and C equal numbers +

+ +

+jME includes two types of Matrix classes: Matrix3f and Matrix4f. Matrix3f is a 3x3 matrix and is the most commonly used (able to handle scaling and rotating), while Matrix4f is a 4x4 matrix that can also handle translation. +

+ +
+ +

Transformations

+
+ +

+ +Multiplying a vector with a Matrix allows the vector to be transformed. Either rotating, scaling or translating that vector. +

+ +
+ +

Scaling

+
+ +

+ +If a diagonal Matrix, defined by D = [dij] and dij = 0 for i != j, has all positive entries it is a scaling matrix. If di is greater than 1 then the resulting vector will grow, while if di is less than 1 it will shrink. +

+ +
+ +

Rotation

+
+ +

+ +A rotation matrix requires that the transpose and inverse are the same matrix (R-1 = RT). The rotation matrix R can then be calculated as: R = I + (sin(angle)) S + (1 - cos(angle)S2 where S is: + +

+
+ + + + + + + + + +
0u2-u1
-u20u0
u1-u00
+ +
+ +

Translation

+
+ +

+ +Translation requires a 4x4 matrix, where the vector (x,y,z) is mapped to (x,y,z,1) for multiplication. The Translation Matrix is then defined as: + +

+
+ + + + + + +
MT
ST1
+ +

+ +where M is the 3x3 matrix (containing any rotation/scale information), T is the translation vector and ST is the transpose Vector of T. 1 is just a constant. +

+ +
+ +

jME Class

+
+ +

+ +Both Matrix3f and Matrix4f store their values as floats and are publicly available as (m00, m01, m02, ???, mNN) where N is either 2 or 3. +

+ +

+Most methods are straight forward, and I will leave documentation to the Javadoc. +

+ +
+ +

Vector

+
+ +

+ +See
+ +and +

+ +
+ +

Definition

+
+ +

+ +Vectors are used to represent a multitude of things in jME, points in space, vertices in a triangle mesh, normals, etc. These classes (Vector3f in particular) are probably the most used class in jME. +

+ +

+A Vector is defined by an n-tuple of real numbers. V = <V1, V2,???, Vn>. +

+ +

+We have two Vectors (2f and 3f) meaning we have tuples of 2 float values or 3 float values. +

+ +
+ +

Operations

+
+ +
+ +

Multiplication by Scalar

+
+ +

+ +A Vector can be multiplied by a scalar value to produce a second Vector with the same proportions as the first. aV = Va = <aV1, aV2,???,aVn> +

+ +
+ +

Addition and Subtraction

+
+ +

+ +Adding or subtracting two Vectors occurs component-wise. That is the first component is added (subtracted) with the first component of the second Vector and so on. +

+ +

+P + Q = <P1+Q1, P2+Q2, ???, Pn+Qn> +

+ +
+ +

Magnitude

+
+ +

+ +The magnitude defines the length of a Vector. A Vector of magnitude 1 is unit length. +

+ +

+For example, if V = (x, y, z), the magnitude is the square root of (x2 + y2 + z2). +

+ +

+A Vector can be normalized or made unit length by multiplying the Vector by (1/magnitude). +

+ +
+ +

Dot Products

+
+ +

+ +The dot product of two vectors is defined as: +P dot Q = PxQx + PyQy + PzQz +

+ +

+Using the dot product allows us to determine how closely two Vectors are pointing to the same point. If the dot product is negative they are facing in relatively opposite directions, while postive tells us they are pointing in the relative same direction. +

+ +

+If the dot product is 0 then the two Vectors are orthogonal or 90 degrees off. +

+ +
+ +

Cross Product

+
+ +

+ +The Cross Product of two Vectors returns a third Vector that is prependicular to the two Vectors. This is very useful for calculating surface normals. +

+ +

+P X Q = <PyQz - PzQy, PzQx - PxQz, PxQy - PyQx> +

+ +
+ +

jME Class

+
+ +

+ +Vector3f and Vector2f store their values (x, y, z) and (x, y) respectively as floats. Most methods are straight forward, and I will leave documentation to the Javadoc. +

+ +
+ +

Quaternion

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +Quaternions define a subset of a hypercomplex number system. Quaternions are defined by (i2 = j2 = k2 = ijk = -1). jME makes use of Quaternions because they allow for compact representations of rotations, or correspondingly, orientations, in 3D space. With only four float values, we can represent an object's orientation, where a rotation matrix would require nine. They also require fewer arithmetic operations for concatenation. +

+ +

+Additional benefits of the Quaternion is reducing the chance of and allowing for easily interpolation between two rotations (spherical linear interpolation or slerp). +

+ +

+While Quaternions are quite difficult to fully understand, there are an exceeding number of convenience methods to allow you to use them without having to understand the math behind it. Basically, these methods involve nothing more than setting the Quaternion's x,y,z,w values using other means of representing rotations. The Quaternion is then contained in the Spatial as its local rotation component. +

+ +

+Quaternion q has the form +

+ +

+q = <w,x,y,z> = w + xi + yj + zk +

+ +

+or alternatively, it can be written as: +

+ +

+q = s + v, where s represents the scalar part corresponding to the w-component of q, and v represents the vector part of the (x, y, z) components of q. +

+ +

+Multiplication of Quaternions uses the distributive law and adheres to the following rules with multiplying the imaginary components (i, j, k): +

+ +

+i2 = j2 = k2 = -1
+ +ij = -ji = k
+ +jk = -kj = i
+ +ki = -ik = j +

+ +

+However, Quaternion multiplication is not commutative, so we have to pay attention to order. +

+ +

+q1q2 = s1s2 - v1 dot v2 + s1v2 + s2v1 + v1 X v2 +

+ +

+Quaternions also have conjugates where the conjugate of q is (s - v) +

+ +

+These basic operations allow us to convert various rotation representations to Quaternions. +

+ +
+ +

Angle Axis

+
+ +

+ +You might wish to represent your rotations as Angle Axis pairs. That is, you define a axis of rotation and the angle with which to rotate about this axis. Quaternion defines a method fromAngleAxis (and fromAngleNormalAxis) to create a Quaternion from this pair. This is acutally used quite a bit in jME demos to continually rotate objects. You can also obtain a Angle Axis rotation from an existing Quaternion using toAngleAxis. +

+ +
+ +

Example - Rotate a Spatial Using fromAngleAxis

+
+
//rotate about the Y-Axis by approximately 1 pi
+Vector3f axis = Vector3f.UNIT_Y; 
+// UNIT_Y equals (0,1,0) and does not require to create a new object
+float angle = 3.14f;
+s.getLocalRotation().fromAngleAxis(angle, axis);
+ +
+ +

Three Angles

+
+ +

+ +You can also represent a rotation by defining three angles. The angles represent the rotation about the individual axes. Passing in a three-element array of floats defines the angles where the first element is X, second Y and third is Z. The method provided by Quaternion is fromAngles and can also fill an array using toAngles +

+ +
+ +

Example - Rotate a Spatial Using fromAngles

+
+
//rotate 1 radian on the x, 3 on the y and 0 on z
+float[] angles = {1, 3, 0};
+s.getLocalRotation().fromAngles(angles);
+ +
+ +

Three Axes

+
+ +

+ +If you have three axes that define your rotation, where the axes define the left axis, up axis and directional axis respectively) you can make use of fromAxes to generate the Quaternion. It should be noted that this will generate a new Matrix object that is then garbage collected, thus, this method should not be used if it will be called many times. Again, toAxes will populate a Vector3f array. +

+ +
+ +

Example - Rotate a Spatial Using fromAxes

+
+
//rotate a spatial to face up ~45 degrees
+Vector3f[] axes = new Vector3f[3];
+axes[0] = new Vector3f(-1, 0, 0); //left
+axes[1] = new Vector3f(0, 0.5f, 0.5f); //up
+axes[2] = new Vector3f(0, 0.5f, 0.5f); //dir
+ 
+s.getLocalRotation().fromAxes(axes);
+ +
+ +

Rotation Matrix

+
+ +

+ +Commonly you might find yourself with a Matrix defining a rotation. In fact, it's very common to contain a rotation in a Matrix create a Quaternion, rotate the Quaternion, and then get the Matrix back. Quaternion contains a fromRotationMatrix method that will create the appropriate Quaternion based on the give Matrix. The toRotationMatrix will populate a given Matrix. +

+ +
+ +

Example - Rotate a Spatial Using a Rotation Matrix

+
+
Matrix3f mat = new Matrix3f();
+mat.setColumn(0, new Vector3f(1,0,0));
+mat.setColumn(1, new Vector3f(0,-1,0));
+mat.setColumn(2, new Vector3f(0,0,1));
+ 
+s.getLocalRotation().fromRotationMatrix(mat);
+ +

+As you can see there are many ways to build a Quaternion. This allows you to work with rotations in a way that is conceptually easier to picture, but still build Quaternions for internal representation. +

+ +
+ +

Slerp

+
+ +

+ +One of the biggest advantages to using Quaternions is allowing interpolation between two rotations. That is, if you have an initial Quaternion representing the original orientation of an object, and you have a final Quaternion representing the orientation you want the object to face, you can do this very smoothly with slerp. Simply supply the time, where time is [0, 1] and 0 is the initial rotation and 1 is the final rotation. +

+ +
+ +

Example - Use Slerp to Rotate Between two Quaternions

+
+
Quaternion q1;
+Quaternion q2;
+ 
+//the rotation half-way between these two
+Quaternion q3 = q1.slerp(q2, 0.5f);
+ +
+ +

Multiplication

+
+ +

+ +You can concatenate (add) rotations: This means you turn the object first around one axis, then around the other, in one step. +

+
Quaternion myRotation = pitch90.mult(roll45); /* pitch and roll */
+ +

+To rotate a Vector3f around its origin by the Quaternion amount, use the multLocal method of the Quaternion: +

+
Quaternion myRotation = pitch90;
+Vector3f myVector = new Vector3f(0,0,-1);
+myRotation.multLocal(myVector);
+ +
+ +

Utility Classes

+
+ +

+ +Along with the base Math classes, jME provides a number of Math classes to make development easier (and, hopefully, faster). Most of these classes find uses throughout the jME system internally. They can also prove beneficial to users as well. +

+ +
+ +

Fast Math

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +FastMath provides a number of convience methods, and where possible faster versions (although this can be at the sake of accuracy). +

+ +
+ +

Usage

+
+ +

+ +FastMath provides a number of constants that can help with general math equations. One important attribute is USE_FAST_TRIG if you set this to true, a look-up table will be used for trig functions rather than Java's standard Math library. This provides significant speed increases, but might suffer from accuracy so care should be taken. +

+ +

+There are five major categories of functions that FastMath provides. +

+ +
+ +

Trig Functions

+
+ + +
+ +

Numerical Methods

+
+ + +
+ +

Linear Algebra

+
+ + +
+ +

Geometric Functions

+
+ + +
+ +

Misc.

+
+ + +
+ +

Line

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +A line is a straight one-dimensional figure having no thickness and extending infinitely in both directions. A line is defined by two points A and B with the line passing through both. +

+ +
+ +

Usage

+
+ +

+ +jME defines a Line class that is defined by an origin and direction. In reality, this Line class is typically used as a line segment. Where the line is finite and contained between these two points. +

+ +

+random provides a means of generate a random point that falls on the line between the origin and direction points. +

+ +
+ +

Example 1 - Find a Random Point on a Line

+
+
Line l = new Line(new Vector3f(0,1,0), new Vector3f(3,2,1));
+Vector3f randomPoint = l.random();
+ +
+ +

Plane

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +A plane is defined by the equation N . (X - X0) = 0 where N = (a, b, c) and passes through the point X0 = (x0, y0, z0). X defines another point on this plane (x, y, z). +

+ +

+N . (X - X0) = 0 can be described as (N . X) + (N . -X0) = 0 +

+ +

+or +

+ +

+(ax + by + cz) + (-ax0-by0-cz0) = 0 +

+ +

+where (-ax0-by0-cz0) = d +

+ +

+Where d is the negative value of a point in the plane times the unit vector describing the orientation of the plane. +

+ +

+This gives us the general equation: (ax + by + cz + d = 0) +

+ +
+ +

Usage in jME

+
+ +

+ +jME defines the Plane as ax + by + cz = -d. Therefore, during creation of the plane, the normal of the plane (a,b,c) and the constant d is supplied. +

+ +

+The most common usage of Plane is Camera frustum planes. Therefore, the primary purpose of Plane is to determine if a point is on the positive side, negative side, or intersecting a plane. +

+ +

+Plane defines the constants: + +

+ + +

+ +These values are returned on a call to whichSide. +

+ +
+ +

Example 1 - Determining if a Point is On the Positive Side of a Plane

+
+
Vector3f normal = new Vector3f(0,1,0);
+float constant = new Vector3f(1,1,1).dot(normal);
+Plane testPlane = new Plane(normal, constant);
+ 
+int side = testPlane.whichSide(new Vector3f(2,1,0);
+ 
+if(side == Plane.NO_SIDE) {
+   System.out.println("This point lies on the plane");
+}
+ +
+ +

Example 2 - For the Layperson

+
+ +

+ +Using the standard constructor Plane(Vector3f normal, float constant), here is what you need to do to create a plane, and then use it to check which side of the plane a point is on. +

+
package test;
+ 
+import java.util.logging.Logger;
+ 
+import com.jme.math.*;
+ 
+/**
+ *@author Nick Wiggill
+ */
+ 
+public class TestPlanes
+{
+  public static final Logger logger = Logger.getLogger(LevelGraphBuilder.class.getName());
+ 
+  public static void main(String[] args) throws Exception
+  {
+    //***Outline.
+    //This example shows how to construct a plane representation using
+    //com.jme.math.Plane.
+    //We will create a very simple, easily-imagined 3D plane. It will
+    //be perpendicular to the x axis (it's facing). It's "centre" (if
+    //such a thing exists in an infinite plane) will be positioned 1
+    //unit along the positive x axis.
+ 
+    //***Step 1.
+    //The vector that represents the normal to the plane, in 3D space.
+    //Imagine a vector coming out of the origin in this direction.
+    //There is no displacement yet (see Step 2, below).
+    Vector3f normal = new Vector3f(5f,0,0);
+ 
+    //***Step 2.
+    //This is our displacement vector. The plane remains facing in the
+    //direction we've specified using the normal above, but now we are
+    //are actually giving it a position other than the origin.
+    //We will use this displacement to define the variable "constant"
+    //needed to construct the plane. (see step 3)
+    Vector3f displacement = Vector3f.UNIT_X;
+    //or
+    //Vector3f displacement = new Vector3f(1f, 0, 0);
+ 
+    //***Step 3.
+    //Here we generate the constant needed to define any plane. This
+    //is semi-arcane, don't let it worry you. All you need to
+    //do is use this same formula every time.
+    float constant = displacement.dot(normal);
+ 
+    //***Step 4.
+    //Finally, construct the plane using the data you have assembled.
+    Plane plane = new Plane(normal, constant);
+ 
+    //***Some tests.
+    logger.info("Plane info: "+plane.toString()); //trace our plane's information
+ 
+    Vector3f p1  = new Vector3f(1.1f,0,0); //beyond the plane (further from origin than plane)
+    Vector3f p2  = new Vector3f(0.9f,0,0); //before the plane (closer to origin than plane)
+    Vector3f p3  = new Vector3f(1f,0,0); //on the plane
+ 
+    logger.info("p1 position relative to plane is "+plane.whichSide(p1)); //outputs NEGATIVE
+    logger.info("p2 position relative to plane is "+plane.whichSide(p2)); //outputs POSITIVE
+    logger.info("p3 position relative to plane is "+plane.whichSide(p3)); //outputs NONE
+  }
+}
+ +
+ +

Ray

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +Ray defines a line that starts at a point A and continues in a direction through B into infinity. +

+ +

+This Ray is used extensively in jME for Picking. A Ray is cast from a point in screen space into the scene. Intersections are found and returned. To create a ray supply the object with two points, where the first point is the origin. +

+ +
+ +

Example 1 - Create a Ray That Represents Where the Camera is Looking

+
+
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
+ +
+ +

Rectangle

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +Rectangle defines a finite plane within three dimensional space that is specified via three points (A, B, C). These three points define a triangle with the forth point defining the rectangle ( (B + C) - A ). +

+ +
+ +

jME Usage

+
+ +

+ +Rectangle is a straight forward data class that simply maintains values that defines a Rectangle in 3D space. One interesting use is the random method that will create a random point on the Rectangle. The Particle System makes use of this to define an area that generates Particles. +

+ +
+ +

Example 1 : Define a Rectangle and Get a Point From It

+
+
Vector3f v1 = new Vector3f(1,0,0);
+Vector3f v2 = new Vector3f(1,1,0);
+Vector3f v3 = new Vector3f(0,1,0);
+Rectangle r = new Rectangle(v1, v2, v3);
+Vector3f point = r.random();
+ +
+ +

Triangle

+
+ +

+ +See +

+ +
+ +

Definition

+
+ +

+ +A triangle is a 3-sided polygon. Every triangle has three sides and three angles, some of which may be the same. If the triangle is a right triangle (one angle being 90 degrees), the side opposite the 90 degree angle is the hypotenuse, while the other two sides are the legs. All triangles are and . +

+ +
+ +

Usage

+
+ +

+ +jME's Triangle class is a simple data class. It contains three Vector3f objects that represent the three points of the triangle. These can be retrieved via the get method. The get method, obtains the point based on the index provided. Similarly, the values can be set via the set method. +

+ +
+ +

Example 1 - Creating a Triangle

+
+
//the three points that make up the triangle
+Vector3f p1 = new Vector3f(0,1,0);
+Vector3f p2 = new Vector3f(1,1,0);
+Vector3f p3 = new Vector3f(0,1,1);
+Triangle t = new Triangle(p1, p2, p3);
+ +
+ +

Tips and Tricks

+
+ +
+ +

How do I get height/width of a spatial?

+
+ +

+ +Cast the spatial to com.jme3.bounding.BoundingBox to be able to use getExtent(). + +

+
Vector3f extent = ((BoundingBox) spatial.getWorldBound()).getExtent(new Vector3f());
+float x = ( (BoundingBox)spatial.getWorldBound()).getXExtent();
+float y = ( (BoundingBox)spatial.getWorldBound()).getYExtent();
+float z = ( (BoundingBox)spatial.getWorldBound()).getZExtent();
+ +
+ +

How do I position the center of a Geomtry?

+
+
geo.center().move(pos);
+ +
+ +

See Also

+
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/nvyyd.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/nvyyd.png new file mode 100644 index 000000000..d2d5bc35f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/nvyyd.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/simpleapplication_from_the_commandline.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/simpleapplication_from_the_commandline.html new file mode 100644 index 000000000..d722568b3 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/simpleapplication_from_the_commandline.html @@ -0,0 +1,198 @@ + +

Starting a JME3 application from the Commandline

+
+ +

+ +Although we recommend the jMonkeyEngine SDK for developing JME3 games, you can use any IDE (integrated development environment) such as NetBeans or Eclipse, and even work freely from the commandline. Here is a generic IDE-independent "getting started" tutorial. +

+ +

+This example shows how to set up and run a simple application (HelloJME3) that depends on the jMonkeyEngine3 libraries. +

+ +

+The directory structure will look as follows: + +

+
jme3/
+jme3/lib
+jme3/src
+...
+HelloJME3/
+HelloJME3/lib
+HelloJME3/assets
+HelloJME3/src
+...
+ +
+ +

Installing the JME3 Framework

+
+ +

+ +To install the development version of jme3, , unzip the folder into a directory named jme3. The filenames here are just an example, but they will always be something like jME3_xx-xx-2011. +

+
mkdir jme3
+cd jme3
+unzip jME3_01-18-2011.zip
+ +

+Alternatively, you can build JME3 from the sources. (Recommended for JME3 developers.) +

+
svn checkout https://jmonkeyengine.googlecode.com/svn/trunk/engine jme3
+cd jme3
+ant run
+cd ..
+ +

+If you see a Test Chooser open now, the build was successful. Tip: Use ant to build the libraries without running the demos. +

+ +
+ +

Sample Project Directory Structure

+
+ +

+ +First we set up the directory and source package structure for your game project. Note that the game project directory HelloJME3 is on the same level as your jme3 checkout. In this example, we create a Java package that we call hello in the source directory. +

+
mkdir HelloJME3
+mkdir HelloJME3/src
+mkdir HelloJME3/src/hello
+ +
+ +

Libraries

+
+ +

+ +Next you copy the necessary JAR libraries from the download to your project. You only have to do this set of steps once every time you download a new JME3 build. For a detailed description of the separate jar files see this list. +

+
mkdir HelloJME3/build 
+mkdir HelloJME3/lib
+cp jme3/lib/*.* HelloJME3/lib
+ +

+If you have built JME3 from the sources, then the copy paths are different: +

+
mkdir HelloJME3/build 
+mkdir HelloJME3/lib
+cp jme3/dist/*.* HelloJME3/lib
+ +
+ +

Sample Code

+
+ +

+ +To test your setup, create the file HelloJME3/src/hello/HelloJME3.java with any text editor, paste the following sample code, and save. +

+
package hello;
+ 
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.math.ColorRGBA;
+ 
+public class HelloJME3 extends SimpleApplication {
+ 
+    public static void main(String[] args){
+        HelloJME3 app = new HelloJME3();
+        app.start();
+    }
+ 
+    @Override
+    public void simpleInitApp() {
+        Box b = new Box(Vector3f.ZERO, 1, 1, 1);
+        Geometry geom = new Geometry("Box", b);
+        Material mat = new Material(assetManager, 
+          "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setColor("Color", ColorRGBA.Blue);
+        geom.setMaterial(mat);
+        rootNode.attachChild(geom);
+    }
+}
+ +
+ +

Build and Run

+
+ +

+ +We build the sample application into the build directory??? +

+
cd HelloJME3
+javac -d build -cp "lib/eventbus-1.4.jar:lib/j-ogg-oggd.jar:lib/j-ogg-vorbisd.jar:lib/jME3-lwjgl-natives.jar:lib/jbullet.jar:lib/jinput.jar:lib/lwjgl.jar:lib/stack-alloc.jar:lib/vecmath.jar:lib/xmlpull-xpp3-1.1.4c.jar:lib/jME3-blender.jar:lib/jME3-core.jar:lib/jME3-desktop.jar:lib/jME3-jogg.jar:lib/jME3-plugins.jar:lib/jME3-terrain.jar:lib/jME3-testdata.jar:lib/jME3-niftygui.jar:lib/nifty-default-controls.jar:lib/nifty-examples.jar:lib/nifty-style-black.jar:lib/nifty.jar:." src/hello/HelloJME3.java 
+ +

+??? and run it. +

+
cd build
+java -cp "../lib/eventbus-1.4.jar:../lib/j-ogg-oggd.jar:../lib/j-ogg-vorbisd.jar:../lib/jME3-lwjgl-natives.jar:../lib/jbullet.jar:../lib/jinput.jar:../lib/lwjgl.jar:../lib/stack-alloc.jar:../lib/vecmath.jar:../lib/xmlpull-xpp3-1.1.4c.jar:../lib/jME3-blender.jar:../lib/jME3-core.jar:../lib/jME3-desktop.jar:../lib/jME3-jogg.jar:../lib/jME3-plugins.jar:../lib/jME3-terrain.jar:../lib/jME3-testdata.jar:../lib/jME3-niftygui.jar:../lib/nifty-default-controls.jar:../lib/nifty-examples.jar:../lib/nifty-style-black.jar:../lib/nifty.jar:." hello/HelloJME3
+ +

+Note: If you use Windows, the classpath separator is ";" instead of ":". +

+ +

+If a settings dialog pops up, confirm the default settings. You should now see a simple window with a 3-D cube. Use the mouse and the WASD keys to move. It works! +

+ +
+ +

Recommended Asset Directory Structure

+
+ +

+ +For media files and other assets, we recommend creating the following project structure: +

+
cd HelloJME3
+mkdir assets
+mkdir assets/Interface
+mkdir assets/Materials
+mkdir assets/MatDefs
+mkdir assets/Models
+mkdir assets/Scenes
+mkdir assets/Shaders
+mkdir assets/Sounds
+mkdir assets/Textures
+ +

+This will allow the default assetManager to load media files stored in the assets directory, like in this example: +

+
import com.jme3.scene.Spatial;
+...
+Spatial elephant = assetManager.loadModel("Models/Elephant/Elephant.meshxml");
+rootNode.attachChild(elephant);
+...
+ +

+You will learn more about the asset manager and how to customize it later. For now feel free to structure your assets (images, textures, models) into further sub-directories, like in this example the assets/models/Elephant directory that contains the elephant.meshxml model and its materials. +

+ +
+ +

Next Steps

+
+ +

+ +Now follow the tutorials and write your first jMonkeyEngine game. +

+
+ documentation, + install +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html new file mode 100644 index 000000000..6465b1d35 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html @@ -0,0 +1,699 @@ + +

3D Game Development Terminology

+
+ +

+ +Before you start, make certain you are familiar with the following concepts and terminology. +

+ +
+ +

3D Graphics and Audio

+
+ +

+ +OpenGL is the Open Graphics Library, a platform-independent specification for rendering 2D/3D computer graphics. For Java, there are two implementations of OpenGL-based renderers: +

+
    +
  1. Lightweight Java Game Library (LWJGL) ??? jme3 uses lwjgl.
    +
  2. +
  3. Java OpenGL (JOGL)
    +
  4. +
+ +

+OpenAL is the Open Audio Library, a platform-independent 3D audio API. +

+ +
+ +

Context, Display, Renderer

+
+ +

+ +The jME Context makes settings, renderer, timer, input and event listeners, display system, accessible to a JME game. +

+ + +
+ +

Geometry

+
+ +
+ +

Polygon, Mesh, Vertex

+
+ +

+ + +

+ +

+Most visible objects in a 3D scene are made up of polygon meshes ??? characters, terrains, buildings, etc. A mesh is a grid-like structure that represents a complex shape. The advantage of a mesh is that it is mathematically simple enough to render in real time, and detailed enough to be recognizable. +

+ +

+Every shape is reduced to a number of connected polygons, usually triangles; even round surfaces such as spheres are reduced to a grid of triangles. The polygons' corner points are called vertices. Every vertex is positioned at a coordinate, all vertices together describe the outline of the shape. +

+ +

+You create 3D meshes in tools called mesh editors, e.g in Blender. The jMonkeyEngine can load finished meshes (=models) and arrange them to scenes, but it cannot edit the mesh itself. +

+ +
+ +

Materials: Color, Lighting/Shading

+
+ +

+ +What we call "color" is merely part of an object's light reflection. The onlooker's brain uses shading and reflecting properties to infer an object's shape and material. Factors like these make all the difference between chalk vs milk, skin vs paper, water vs plastic, etc! () +

+ +
+ +

Color

+
+ +
+ +

Ambient color

+
+ + +
+ +

Diffuse color

+
+ + +
+ +

Light Sources

+
+ +
+ +

Emissive color

+
+ + +
+ +

Reflections

+
+ +
+ +

Shininess

+
+ + +
+ +

Specular Color

+
+ + +

+ +

+ +
+ +

Materials: Textures

+
+ +

+ +Textures are part of Materials. In the simplest case, an object could have just one texture, the Color Map, loaded from one image file. When you think back of old computer games you'll remember this looks quite plain. +

+ +

+The more information you (the game designer) provide additionally to the Color Map, the higher the degree of detail and realism. Whether you want photo-realistic rendering or "toon" rendering (Cel Shading), everything depends on the quality of your materials and textures. Modern 3D graphics use several layers of information to describe one material, each mapped layer is a texture. +

+ +

+

Got no textures? . Remember to keep the copyright notice together with the textures! +

+

+ +
+ +

Texture Mapping

+
+ +
+ +

Color Map / Diffuse Map

+
+ +

+ + +

+ + +
+ +

Bump Map

+
+ +

+ +Bump maps are used to describe detailed shapes that would be too hard or simply too inefficient to sculpt in a mesh editor. There are two types: +

+ + +

+ +

+ +
+ +

Height Map

+
+ + +
+ +

Normal Map

+
+ +

+ + +

+ + +
+ +

Specular Map

+
+ +

+ + +

+ + +
+ +

Seamless Tiled Textures

+
+ +

+ + +Tiles are a very simple, commonly used type of texture. When texturing a wide area (e.g. walls, floors), you don't create one huge texture ??? instead you tile a small texture repeatedly to fill the area. +

+ +

+A seamless texture is an image file that has been designed or modified so that it can be used as tiles: The right edge matches the left edge, and the top edge matches the bottom edge. The onlooker cannot easily tell where one starts and the next one ends, thus creating an illusion of a huge texture. The downside is that the tiling becomes painfully obvious when the area is viewed from a distance. Also you cannot use it on more complex models such as characters. +

+ +

+See also this tutorial on . +

+ +
+ +

UV Maps / Texture Atlas

+
+ +

+ + +

+ +

+Creating a texture for a cube is easy ??? but what about a character with a face and extremities? For more complex objects, you design the texture in the same ways as a flat sewing pattern: One image file contains the outline of the front, back, and side of the object, next to one another. Specific areas of the flat texture (UV coordinates) map onto certain areas of your 3D model (XYZ coordinates), hence the name UV map. Using UV Maps (also known as Texture Atlas), one model can have different textures on each side. You create one corresponding UV map for each texture. +

+ +

+Getting the seams and mappings right is crucial: You must use a graphic tool like Blender to create UV Maps (Texture Atlas) and store the coordinates correctly. It's worth the while to learn this, UV mapped models look a lot more professional. +

+ +
+ +

Environment Mapping

+
+ +

+ + +

+ +

+Environment Mapping or Reflection Mapping is used to create the impression of reflections and refractions in real time. It's faster (but less accurate) than the raytracing methods used in offline rendering applications. +

+ +

+You create a Cube Map to represent your environment; Sphere Maps are also possible, but often look distorted. Basically you give the environment map a set of images showing a "360?? view" of the background scene ??? very similar to a skybox. The renderer maps the environment on the texture of the reflective surface, which results in an acceptable "glass/mirror/water" effect. Just like a skybox, the reflection map is static, so dynamic things (such as the player walking) are not part of the reflection. (!) +

+ +

+See also: Water. +

+ +
+ +

MIP Map Texture

+
+ +

+ +MIP Map means that you provide one texture in two or three resolutions in one file (MIP = "multum in parvo" = "many in one"). Depending on how close (or far) the camera is, the engine automatically renders a more (or less) detailed texture for the object. Thus objects look smooth from close up, but don't waste resources with unspottable details when far away. Good for everything, but requires more time to create and more space to store textures. If you don't provide custom ones, the jMonkeyEngine creates basic MIP maps automatically as an optimization. +

+ +
+ +

Procedural Textures

+
+ +

+ +A procedural texture is generated from repeating one small image, plus some pseudo-random, gradient variations (called Perlin noise). Procedural textures look more natural than static rectangular textures, and they look less distorted on spheres. On big meshes, their repetitiveness is much less noticable than with tiled seamless textures. Procedural textures are ideal for irregular large-area textures like grass, soil, rock, rust, and walls. Use the to create them. +

+ +

+ +

+ +

+See also: , +

+ +
+ +

Animation

+
+ +

+ +In 3D games, Skeletal Animation is used for animated characters, but in principle the skeleton approach can be extended to any 3D mesh (for example, an opening crate's hinge can be considered a primitive joint). +

+ +

+Unless you animate a 3D cartoon, realism of animated characters is generally a problem: Movement can look alien-like mechanical or broken, the character appears hollow, or as if floating. Professional game designers invest a lot of effort to make characters animate in a natural way, including . +

+ +
+ +

Rigging and Skinning

+
+ +

+ + +

+ +

+An animated character has an armature: An internal skeleton (Bones) and an external surface (Skin). The Skin is the visible outside of the character and it includes clothing. The Bones are not visible and are used to interpolate (calculate) the morphing steps of the skin. +

+ +

+JME3, the game engine, only loads and plays your recorded animations. You must use a tool (such as Blender) to set up (rig, skin, and animate) a character. +

+
    +
  1. Rigging: The Construction of a character's skeleton.
    +
      +
    • Create as few Bones as possible to decrease complexity.
      +
    • +
    • Bones are connected in a parent-child hierarchy: Moving one bone can pull another bone with it (e.g. arm pulls hand).
      +
    • +
    • Bones follow a certain naming scheme so the 3D engines know what's what.
      +
    • +
    +
  2. +
  3. Skinning: The association of individual bones with the corresponding skin sections.
    +
      +
    • Each Bone is connected to a part of the Skin. Animating the (invisible) Bone pulls the (visible) Skin with it.
      +E.g. the thigh Bone is connected to the upper leg Skin.
      +
    • +
    • One part of the Skin can be affected by more than one bone (e.g. knee, elbow).
      +
    • +
    • The connection between bones and skin sections is gradual: You assign weights how much each skin polygon is affected by any bone's motion.
      +E.g. when the thigh bone moves, the leg is fully affected, the hips joints less so, and the head not at all.
      +
    • +
    +
  4. +
  5. Keyframe Animation: A keyframe is one recorded snapshot of a motion sequence.
    +
      +
    • A series of keyframes makes up one animation.
      +
    • +
    • Each model can have several animations. Each animation has a name to identify it (e.g. "walk", "attack", "jump").
      +
    • +
    • You specify in your game code which keyframe animation to load, and when to play it.
      +
    • +
    +
  6. +
+ +

+ +

What is the difference between animation (rigging, skinning, keyframes) and transformation (rotation, scaling, moving, "slerp")? +

+ + +

+ +

+

+ +
+ +

Kinematics

+
+ + +
+ +

Controller and Channel

+
+ +

+In the JME3 application, you register animated models to the Animation Controller. The controller object gives you access to the available animation sequences. The controller has several channels, each channels can run one animation sequence at a time. To run several sequences, you create several channels, and run them in parallel. +

+ +
+ +

Artificial Intelligence (AI)

+
+ +

+Non-player (computer-controlled) characters (NPCs) are only fun in a game if they do not stupidly bump into walls, or blindly run into the line of fire. You want to make NPCs "aware" of their surroundings and let them make decisions based on the game state ??? otherwise the player can just ignore them. The most common use case is that you want to make enemies interact in a way so they offer a more interesting challenge for the player. +

+ +

+"Smart" game elements are called artificially intelligent agents (AI agents). An AI agent can be used to implement enemy NPCs as well as trained pets; you also use them to create automatic alarm systems that lock doors and "call the guards" after the player triggers an intruder alert. +

+ +

+The domain of artificial intelligence deals, among other things, with: +

+ + +

+ +More advanced AIs can also learn, for example using neural networks. +

+ +

+There are lots of resources explaining interesting AI algorithms: +

+ + +
+ +

Math

+
+ +

+ +

+ +
+ +

Coordinates

+
+ +

+ +Coordinates represent a location in a coordinate system. Coordinates are relative to the origin at (0,0,0). In 3D space, you need to specify three coordinate values to locate a point: X (right), Y (up), Z (towards you). Similarly, -X (left), -Y (down), -Z (away from you). +In contrast to a vector (which looks similar), a coordinate is a location, not a direction. +

+ +
+ +

The Origin

+
+ +

+ +The origin is the central point in the 3D world, where the three axes meet. It's always at the coordinates (0,0,0). +

+ +

+Example: Vector3f origin = new Vector3f( Vector3f.ZERO ); +

+ +
+ +

Vectors

+
+ +

+ +A vector has a length and a direction, like an arrow in 3D space. A vector starts at a coordinate (x1,y1,z1) or at the origin, and ends at the target coordinate (x2,y2,z2). Backwards directions are expressed with negative values. +

+ +

+Example: +

+
Vector3f v = new Vector3f( 17f , -4f , 0f ); // starts at (0/0/0)
+Vector3f v = new Vector3f( 8f , 0f , 33f ).add(new Vector3f( 0f , -2f , -2f )); // starts at (8/0/33)
+ +
+ +

Unit Vectors

+
+ +

+ +A unit vector is a basic vector with a length of 1 world unit. Since its length is fixed (and it thus can only point at one location anyway), the only interesting thing about this vector is its direction. +

+ + +

+Negate the vegator to change its direction, e.g. (-1, 0, 0) = left. +

+ +
+ +

Normalized Vectors

+
+ +

+ +A normalized vector is a custom unit vector. A normalized vector is not the same as a (surface) normal vector. +When you normalize a vector, it still has the same direction, but you lose the information where the vector originally pointed. +

+ +

+Example: You normalize vectors before calculating angles. +

+ +
+ +

Surface Normal Vectors

+
+ +

+ + +A surface normal is a vector that is perpendicular (orthogonal) to a plane. +You calculate the Surface Normal by calculating the cross product. +

+ +
+ +

Cross Product

+
+ +

+ +The cross product is a calculation that you use to find a perpendicular vector (an orthogonal, a "right angle" at 90??). +In 3D space, speaking of an orthogonal only makes sense with respect to a plane. You need two vectors to uniquely define a plane. The cross product of the two vectors, v1 ?? v2, is a new vector that is perpendicular to this plane. A vector perpendicular to a plane is a called Surface Normal. +

+ +

+Example: The x unit vector and the y unit vector together define the x/y plane. The vector perpendicular to them is the z axis. JME can calculate that this equation is true:
+ +( Vector3f.UNIT_X.cross( Vector3f.UNIT_Y ) ).equals( Vector3f.UNIT_Z ) == true +

+ +
+ +

Transformation

+
+ +

+ +Transformation means rotating (turning), scaling (resizing), or translating (moving) objects in 3D scenes. 3D engines offer simple methods so you can write code that transforms nodes. +

+ +

+Examples: Falling and rotating bricks in 3D Tetris. +

+ +
+ +

Slerp

+
+ +

+ +Slerp is how we pronounce spherical linear interpolation when we are in a hurry. A slerp is an interpolated transformation that is used as a simple "animation" in 3D engines. You define a start and end state, and the slerp interpolates a constant-speed transition from one state to the other. You can play the motion, pause it at various percentages (values between 0.0 and 1.0), and play it backwards and forwards. +

+ +

+Example: A burning meteorite Geometry slerps from "position p1, rotation r1, scale s1" in the sky down to "p2, r2, s2" into a crater. +

+ +

+Learn more about 3D maths here. +

+ +
+ +

Game Developer Jargon

+
+ + +
+ +

3D graphics Terminology Wiki book

+
+ + +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/the_scene_graph.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/the_scene_graph.html new file mode 100644 index 000000000..41e8329b6 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/the_scene_graph.html @@ -0,0 +1,151 @@ + +

The Scene Graph and Other jME3 Terminology

+
+ +

+ +Before you start making games, make sure you understand general 3D Gaming terminology. +

+ +

+Second, if you are a beginner, we recommend our Scene Graph for Dummies presentation for a visual introduction to the concept of a scene graph. +

+ +

+Then continue learning about jME3 concepts here. +

+ +
+ +

Coordinate System

+
+ +

+ + +

+ +

+The jMonkeyEngine uses a right-handed coordinate system, just as OpenGL does. +

+ +

+The coordinate system consists of: + +

+ + +

+ +Every point in 3D space is defined by its (x,y,z) coordinates. The data type for vectors is com.jme3.math.Vector3f. +

+ +

+For your orientation, the default camera's location is (0.0f,0.0f,10.0f), and it is looking in the direction described by the unit vector (0.0f, 0.0f, -1.0f). This means your point of view is on the positive side of the Z axis, looking towards the origin, down the Z axis. +

+ +

+The unit of meassurement is world unit (wu). Typically, 1 wu is considered to be one meter. All scales, vectors and points are relative to this coordinate system. +

+ +
+ +

Scene Graph and RootNode

+
+ +

+ +The scene graph represents your 3D world. Objects in the jME3 scene graph are called Spatials. Everything attached to the rootNode is part of the scene graph. Attaching a Spatial to the rootNode (or other nodes) adds the Spatial to the scene; detaching removes it. +

+ +

+ +

+ +
+ +

Spatials: Node vs Geometry

+
+ +

+ +A Spatial can be transformed, loaded and saved. There are two types of Spatials, Nodes and Geometries. + +

+
+ + + + + + + + + + + + + + + + + + + + + +
Spatial
Purpose: A Spatial is an abstract data structure that stores transformations (translation, rotation, scale).
Geometry Node
Visibility: A visible 3-D object. An invisible "handle" for a group of objects.
Purpose: Represents the "look" of an object: Shape, color, texture, opacity/transparency. Groups Geometries and other Nodes together: You transform a Node to affect all attached Nodes.
Content: Transformations, mesh, material. Transformations. No mesh, no material.
Examples: A box, a sphere, player, a building, a piece of terrain, a vehicle, missiles, NPCs, etc??? The rootNode, the guiNode, an audio node, a custom grouping node, etc.
+ +
+ +

How to Use This Knowledge?

+
+ +

+ +Before you start creating your game, you should plan your scene graph: Which Nodes and Geometries will you need? Complete the Hello World tutorial series to learn how to load and create Spatials, how to lay out a scene by attaching, detaching, and transforming Spatials, and how to add interaction and effects to a game. +

+ +

+The intermediate and advanced documentation gives you more details on how to put all the parts together to create an awesome 3D game in Java! +

+ +
+ +

See also

+
+ +
+ spatial, + node, + mesh, + geometry, + scenegraph, + rootnode +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/webstart.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/webstart.html new file mode 100644 index 000000000..8d62c6b8a --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/webstart.html @@ -0,0 +1,129 @@ + +

WebStart (JNLP) Deployment

+
+ +

+ +When you use the jMonkeyEngine SDK to deploy your application, you can configure the project to build files required for WebStart automatically. If you use another IDE, or work on the command line, use the following tips to set up WebStart correctly: +

+ +
+ +

Problem Statement

+
+ +

+ +Problem: +

+ +

+When running under WebStart, jMonkeyEngine may not have permission to extract the native libraries to the current directory. +

+ +

+Solution: +

+ +

+You can instruct WebStart to load the native libraries itself using the JNLP file, and then instruct jME3 not to try to do so itself. +

+ +
+ +

Simple way

+
+ +

+ +You can import the LWJGL JNLP extension directly into your extension, however be aware that your application will break whenever they update their jars. Simply add this line to your JNLP: +

+
<extension name="lwjgl" href="http://lwjgl.org/webstart/2.7.1/extension.jnlp" />
+ +
+ +

Reliable way

+
+ +
+ +

Native jars

+
+ +

+ +You can download the LWJGL native jars from their site, or to ensure you're using the exact same version as bundled with your jME3 release, make your own: +

+
mkdir tmp
+cd tmp
+jar xfv ../jME3-lwjgl-natives.jar
+cd native
+for i in *; do
+  cd $i
+  jar cfv ../../native_$i.jar .
+  cd ..
+done
+ +

+For Windows: +

+
@echo off
+md tmp
+cd tmp
+"%JDK_HOME%\bin\jar" -xfv ..\jME3-lwjgl-natives.jar
+cd native
+for /D %%i in ("*") do (
+  cd %%i
+  "%JDK_HOME%\bin\jar" -cfv ..\..\native_%%i%.jar .
+  cd ..
+)
+cd ..
+ +

+Remember to sign all the jar files and move them into the right place from the tmp directory. +

+ +
+ +

JNLP file

+
+ +

+ +Add the following to your JNLP file: +

+
  <resources os="Windows">
+    <j2se version="1.4+"/>
+    <nativelib href="native_windows.jar"/>
+  </resources>
+  <resources os="Linux">
+    <j2se version="1.4+"/>
+    <nativelib href="native_linux.jar"/>
+  </resources>
+  <resources os="Mac OS X">
+    <j2se version="1.4+"/>
+    <nativelib href="native_macosx.jar"/>
+  </resources>
+  <resources os="SunOS" arch="x86">
+    <j2se version="1.4+"/>
+    <nativelib href="native_solaris.jar"/>
+  </resources>
+ +
+ +

Set low-permissions mode

+
+ +

+ +In your main() method, if running under WebStart, tell jME3 it is running in a low-permission environment so that it doesn't try to load the natives itself: +

+
  public static void main(String[] args)
+  {
+      if (System.getProperty("javawebstart.version") != null) {
+        JmeSystem.setLowPermissions(true);
+      }
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/package-info.java b/sdk/jme3-documentation/src/com/jme3/gde/docs/package-info.java new file mode 100644 index 000000000..03b857cd1 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/package-info.java @@ -0,0 +1,8 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +@HelpSetRegistration(helpSet = "docs-hs.xml", position = 3662) +package com.jme3.gde.docs; + +import org.netbeans.api.javahelp.HelpSetRegistration; diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/3ds_to_blender_to_jmp.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/3ds_to_blender_to_jmp.html new file mode 100644 index 000000000..2cfbc4498 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/3ds_to_blender_to_jmp.html @@ -0,0 +1,70 @@ + +

Using Blender as a Intermediator Between 3dMax and the jMonkeyEngine SDK

+
+ +

+ +The jMonkeyEngine SDK supports .blend files and can convert them to jMonkeyEngine's .j3o format. This means you can use Blender to convert, for example, a 3dMax file to .j3o format. +

+ +
+ +

Importing the .3ds file to Blender

+
+ +

+ +I'm using the blender 2.59 at this tutorial, but if you blender 2.49b, no problem ;). +After you saved your .3ds file in 3dmax, open the blender, delete the default cube, +and import your .3ds file via File??????Import??????>3D Studio. +

+ +

+ +

+ +
+ +

Saving the .blend file

+
+ +

+ +Now save your .blend file so you can load it into the jMonkeyEngine SDK. +

+ +

+ +

+ +
+ +

Importing the .blend file to the SDK by using the ModelImporter and BlenderSupport plugins

+
+ +

+ +Click on Import Model button and then click on Open Model button to open the .blend file. Click next, select the checkbox to import a copy from .blend file, and click finish. +

+ +

+ +

+ +
+ +

Edit your model in SceneComposer and "VOILA"

+
+ +

+ +As you can see, the .blend model was automatically converted to .j3o binary format. Now, you are able to edit it in SceneComposer ;D. +

+ +

+ +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html new file mode 100644 index 000000000..d92a0bca3 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html @@ -0,0 +1,346 @@ + +

jMonkeyEngine SDK: Application Deployment

+
+ +

+ +After you have written and tested your game, you want to brand it and distribute it to your users. If you use the build script provided by the jMonkeyEngine SDK's BaseGame, you have the following deployment options: +

+ + +
+ +

Requirements

+
+ +

+ +Since JAR files are platform independent, your customers can play your jMonkeyEngine application on Windows, Mac OS, or Linux. The only requirement is that the user has the free Java 5 or 6 Runtime (or browser plugin) installed. For more information see . +

+ +
+ +

Branding

+
+ +

+ + +Make your game unique and recognizable: +

+
    +
  1. Open your game project in the SDK's Projects window.
    +
  2. +
  3. Right-click the project and open the Properties
    +
  4. +
  5. Open the Properties ??? Application section. Here you configure your branding:
    +
      +
    1. Title: Enter the game's name
      +
    2. +
    3. Vendor: Enter your name (the development team)
      +
    4. +
    5. Description: Write one line why your game is the coolest ever ;-)
      +
    6. +
    7. Homepage: Enter your web URL, so your fans can find you
      +
    8. +
    9. Splashscreen: Browse to a cool screenshot that will be displayed while the game loads.
      +
    10. +
    +
  6. +
  7. Click OK.
    +
  8. +
  9. Clean and Build.
    +
  10. +
+ +

+ +Your executables are now branded. +

+ +

+TODO: where does this info actually show up? +

+ +
+ +

Creating the Distributable

+
+ +

+ +When you run the build script provided by the jMonkeyEngine SDK, it automatically compiles your classes, libraries, and assets. It creates a dist directory in your project directory which contains the executable JAR and a directory with libraries. +

+ +

+In the simplest case, you zip up the dist directory and distribute it to your customers. Companies often have additional tools to create executables and installers. +

+ +

+Here are your deployment options in detail: +

+ +
+ +

Desktop Application (JAR)

+
+ +

+ +The JAR file is the most common deployment method for Java desktop applications. The user downloads the executable JAR file to his machine and runs it to start the game. +

+
    +
  1. Right-click your project and open the Project Properties.
    +
  2. +
  3. In the Application>Web Start category, make sure the box "Enable Web Start" is not checked. Click OK.
    +
  4. +
  5. Right-click your project and Clean and Build.
    +
  6. +
  7. If the build succeeds you see a line similar to
    +Building jar: /home/joe/jMonkeyPlatform/MySuperTestGame/dist/MySuperTestGame.jar
    +this means the executable JAR has been generated successfully in your project directory.
    +
  8. +
  9. Zip up the dist directory and distribute it to your users. Make sure to keep the lib directory in it!
    +
  10. +
+ +

+Most operating systems execute a JAR when users double-click on it, but you can also create a launcher. +

+ +
+ +

Desktop Executables (.EXE, .APP, .JAR)

+
+ +

+ +jMonkeyEngine SDK allows you to create launchers for different desktop platforms, like an .exe file for Windows systems, an Application for MaxOSX and a launcher for Linux systems. +

+
    +
  1. Right-click your project and open the Project Properties.
    +
  2. +
  3. In the Application>Desktop category, select the checkboxes for the platforms you want to distribute to.
    +
  4. +
  5. Click OK.
    +
  6. +
+ +

+A resources folder in your project folder will be created that contains the template icons and settings files for each selected platform. If you change one of them, de-selecting the deployment for that platform will not delete this resource file anymore and it will not be overwritten when you re-enable deployment for that platform. +

+ +

+When you build your project, zip files for each selected platform will be created in the dist folder that contain all that is needed to run your application on that platform. +

+ +
+ +

Web Start (.JNLP)

+
+ +

+ +Web Start allows your users to start your application by simply clicking a link that you provide, for example in a button on your web page. The browser downloads the JAR file and then automatically runs your game in an extra window. The only requirement is that the user's browser has the Java plugin installed. This is a very user-friendly way for your customers to play your game without any extra steps to install it. Optionally, you can set it up so the file is saved to their desktop and can be restarted later, so they do not need to be online to play. +

+
    +
  1. Right-click your project and open the Project Properties.
    +
      +
    1. In the Application>Web Start category, check the box to Enable Web Start.
      +
    2. +
    3. Check the box to make the application self-signed. :!:
      +
    4. +
    5. Optionally, check the box to allow offline use.
      +
    6. +
    7. Make sure Application Descriptor is activated. Click OK.
      +
    8. +
    +
  2. +
  3. Right-click your project and Clean and Build. The dist directory is generated.
    +
  4. +
  5. Upload the contents of the dist directory to a public http server
    +
  6. +
  7. Either edit the sample launch.html file, or simply add a standard link (A HREF) pointing to your .jnlp file to one of your web pages.
    +
  8. +
  9. Tell your users to open your page in a webbrowser, and click the link to webstart the application.
    +
  10. +
+ +

+Look at the sample launch.html, you can have any custom content around the link. Keep a copy of your launcher file because the jMonkeyEngine SDK will always regenerate its default launch.html. +Also, see this on creating WebStarts. +

+ +
+ +

Browser Applet

+
+ +

+ +A browser Applet is a Java application that runs in the web browser while the user is visiting your web page. The only requirement is that the user's browser has the Java plugin installed. There is no installation step, the user can play right away in the browser. The user will not be able to save the game to his harddrive, nor can he play offline. +

+ +

+These instructions assume that you have already written a game that you want to turn into an Applet. As opposed to other jME3 games, Applets cannot capture the mouse for navigation, so the camera will be switched to dragToRotate mode. The jMonkeyEngine SDK and the included build script already contain what you need. + +

+ +
+ +

To Turn a Project Into an Applet

+
+
    +
  1. Right-click your project and open the Project Properties.
    +
      +
    1. In the Application>Applet category, check the box to enable Applet creation.
      +
    2. +
    3. Change the applet width and height as you want it.
      +
    4. +
    5. Click OK.
      +
    6. +
    +
  2. +
  3. Right-click your project and Clean and Build.
    +
  4. +
+ +

+The dist/Applet directory now contains all the files necessary for the game to run as Applet. To test the Applet-based game, run the project in the jMonkeyEngine SDK. + +

+ +
+ +

To Deploy the Game as Applet

+
+
    +
  1. Edit the dist/Applet/run-applet.html file in anyway you like. Just keep the Applet code.
    +
  2. +
  3. Upload the contents of the dist/Applet directory to a public http server.
    +
  4. +
  5. Access the run-applet.html file using a webbrowser
    +
  6. +
  7. Click the link to web-start your application.
    +
  8. +
+ +
+ +

To Troubleshoot Applets

+
+ + +
+ +

Android Mobile Device

+
+ +

+ +You can set the jMonkeyEngine SDK to build an executable for Android mobile platforms. +

+ +

+Learn more about Android Support here. +

+ +
+ +

iOS Device

+
+ +

+ +You can set the jMonkeyEngine SDK to build an executable for iOS platforms. Mac support is work in progress. +

+ +

+Learn more about iOS Support here. +

+ +
+ +

Tip: Switching Build Configurations

+
+ +

+The jMonkeyEngine SDK has a Run Configuration menu in the toolbar. Use it to save your various sets of Project Property configuations, and switch between them. +

+
    +
  1. Click the Set Project Configuration popup in the toolbar and choose Customize.
    +
  2. +
  3. The Project Properties Run section opens. Under Configuration, click New.
    +
  4. +
  5. Name the saved configuration, for instance "my webstart" vs "my desktop app", or "development" vs "deployment". Click OK.
    +
  6. +
  7. Make sure the new config is selected in the Set Project Configuration popup above the editor.
    +
  8. +
  9. Make changes to the Project Properties as described above.
    +
  10. +
+ +

+Now you can use the Set Project Configuration popup menu to switch between your run/build configurations. +

+ +
+ +

Tip: Reduce Distribution File Size

+
+ +

+ +There may be several parts of the full jMonkeyEngine library that you do not even use in your application. You should leave out the corresponding libraries from your distribution. +

+ +

+To remove unused libraries: +

+
    +
  1. Right-click your project and select "Properties"
    +
  2. +
  3. Select "Libraries" on the left
    +
  4. +
  5. Select the "jme3-libraries" entry and press "remove".
    +This library package contains all libraries for jME3 and is quite large.
    +
  6. +
  7. Press the "Add Library" button
    +
  8. +
  9. Select the "jme3-libraries-lwjgl-minimum" library
    +
  10. +
  11. Add other jME3 libraries in the same way depending which features you use:
    +jme3-libraries-gui, jme3-libraries-physics, jme3-libraries-video, etc.
    +
  12. +
  13. Click OK.
    +
  14. +
  15. Clean, Build and Run the project and make sure you have not missed anything.
    +
  16. +
+
+ documentation, + sdk, + deployment, + android, + applet, + webstart, + desktop +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/applymaterial.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/applymaterial.jpg new file mode 100644 index 000000000..5298937b1 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/applymaterial.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/asset_packs.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/asset_packs.html new file mode 100644 index 000000000..56de8a93c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/asset_packs.html @@ -0,0 +1,204 @@ + +

jMonkeyEngine SDK: AssetPacks and AssetPack Browser

+
+ +

+ +AssetPacks are a way to package jME3 compatible assets (like models, textures, sounds and whole scenes!) into a package that contains publisher info, license info, descriptions etc. for all of the assets. An AssetPack basically consists of an assetpack.xml file that describes the content and an assets folder that contains the content. The integrated browser in the jMonkeyEngine SDK allows you to add the assets from installed AssetPacks to any jme3 project scene you are working on. +

+ +
+ +

The AssetPack Browser

+
+ +

+ +

+ +
+ +

Browsing Assets

+
+ +

+ +The AssetPack browser in jMonkeyEngine SDK makes browsing the installed AssetPacks easy. Browse categories, search for tags and find the right asset for your project. When you have found it, you can add it with one click to your current scene. The AssetPack manager will automagically copy all needed textures, sounds etc. to your projects assets folder. +

+ +

+

You can also browse a selection of online assetpacks that are available on jMonkeyEngine.org for download and install them to your jMonkeyEngine SDK's AssetPack browser. +

+

+ +

+The AssetPack browser uses a predefined directory to store the AssetPacks which is also used for new AssetPack projects. You can see and change the folder path in the AssetPack preferences (jMonkeyEngine SDK???Settings). +

+ +
+ +

Adding Assets to Your Scene

+
+ +

+ +To preview a model from the browser, right-click it and select "Preview Asset" +

+ +

+To add a model from the AssetPack browser to a scene do the following: +

+
    +
  1. Create a new scene (j3o) file or use an existing one
    +
  2. +
  3. Open it in the SceneComposer by right-clicking it and selecting "Edit in SceneComposer"
    +
  4. +
  5. Select the root node of the scene (or another node you want to add the model to)
    +
  6. +
  7. Right-click a model in the AssetBrowser and select "Add to SceneComposer"
    +
  8. +
+ +

+The model will be added to your scene and all needed texture files will be copied to your projects assets folder. +

+ +
+ +

Create Your Own AssetPack Project

+
+ +

+ +AssetPack projects are a way to create your own AssetPacks, either for personal use or for publishing. With this project type in jMonkeyEngine SDK you can create a library of assets on your computer that you can use in any of your projects via the AssetPack browser. +Editing of asset and project info, adding of new assets and setting their properties, everything you need to create and publish your AssetPacks is there. + +

+
    +
  1. Choose "File ??? New Project" from the menu
    +
  2. +
  3. Choose "AssetPack Project"
    +
  4. +
  5. Fill in the project info and press "finish"
    +
  6. +
+ +

+ +You can access and change the project properties by right-clicking the project and selecting "Properties". +

+ +
+ +

Add Your Own Assets

+
+ +

+ +

+ +

+To add new assets to your AssetPack do the following: +

+
    +
  1. Right-Click the "Assets" node of the AssetPack project
    +
  2. +
  3. Select "Add Asset.."
    +
  4. +
  5. Specify the info about your asset and press Next
    +
  6. +
  7. Press the "add files" button and select all files belonging to your asset
    +
  8. +
  9. Select the "main" checkbox for the main model file if your asset is a model file
    +
  10. +
  11. Change the asset paths and types if needed and press "finish"
    +
  12. +
+ +

+ +The global asset type can be "model", "scene", "texture", "sound", "shader" or "other" +

+ +

+With the "model" or "scene" types, the AssetPack browser will try to load and add a model file from the selected assets when the user selects "Add to SceneComposer". Specify the "load this model with material" flag for the model file that should be loaded via the AssetManager, you can also specify multiple mesh or scene files to be loaded. All texture and other needed files will be copied to the users project folder. +

+ +

+On the "Add Files" page you define the path of the files in the AssetPack. The importer tries to generate a proper path from the info entered on the first page. Note that for j3o binary models, the texture paths have to be exactly like they were during conversion. The given paths will also be used when copying the data to the users "assets" folder. +

+ +

+With the "add files" button you can open a file browser to select files from your harddisk that will be copied into the assets/ folder of the AssetPack project. With the "add existing" button you can add a file thats already in your AssetPack assets folder to a new asset item. This way you can reuse e.g. textures for asset items or make items for an existing collection of asset files that you copied to the projects assets folder. +

+ +

+ +

+ +

+You can specify a specific material to be used for the single mesh/scene files, just select it in the dropdown below the mesh or scene file. If you don't select a material file here, the first found material file is used or none if none is found. +

+ +

+If the material file is an Ogre material file (.material) it will be used for loading an Ogre scene or mesh file. If it is a jMonkeyEngine3 material file (.j3m), it will be applied to the mesh regardless of model type. Note that for j3o models you don't need material files as the material is stored inside the j3o file. +

+ +

+

In your AssetPack Project, right-click each asset and select "preview asset" to see your model. Verify hat it looks correctly, because then it should work for other users as well. +

+

+ +

+You can change the single assets properties in the properties window after you have added them. Just select an asset and open the properties window (Windows???Properties). +

+ +

+Supported formats for models (main files) are: +

+
    +
  1. OgreXML .mesh.xml / .scene
    +
  2. +
  3. Wavefront .obj
    +
  4. +
  5. jMonkeyEngine3 .j3o
    +
  6. +
  7. Blender .blend (unpack textures)
    +
  8. +
+ +
+ +

AssetPack Publishing

+
+ +

+ +

+ +

+You can publish your AssetPacks either as a zip file or directly to jmonkeyengine.org, using your website user name and login. This means other jMonkeyEngine SDK users can download your AssetPacks and install them to their local database right off the AssetPack online packages browser. +

+ +

+

To make sure you can upload, you have to be registered on jmonkeyengine.org, and have to enter your login info in the AssetPack preferences: jMonkeyEngine SDK???Options???Asset Packs first. +

+ +

+
    +
  1. Right-Click your AssetPack project in the SDK and select "Publish AssetPack???"
    +
  2. +
  3. Check the description etc. settings, and press "Next".
    +
  4. +
  5. Select the checkbox for online and/or local publishing and press "finish".
    +
  6. +
+
+ documentation, + sdk, + asset +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackbrowser-300x166.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackbrowser-300x166.jpg new file mode 100644 index 000000000..7b4eb1292 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackbrowser-300x166.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackdownload-263x300.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackdownload-263x300.jpg new file mode 100644 index 000000000..1e5cc2648 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackdownload-263x300.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport-300x222.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport-300x222.jpg new file mode 100644 index 000000000..e2411f2aa Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport-300x222.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport2-300x179.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport2-300x179.jpg new file mode 100644 index 000000000..275f75f78 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/assetpackimport2-300x179.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html new file mode 100644 index 000000000..156f6b74c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html @@ -0,0 +1,353 @@ + +

Blender importer for jMonkeyEngine 3

+
+ +
+ +

Introduction

+
+ +

+ +Importing models to any game engine is as important as using them. The quality of the models depends on the abilities of the people who create it and on the tools they use. +Blender is one of the best free tools for creating 3D enviroments. Its high amount of features attract many model designers. +So far jMonkeyEngine used Ogre mesh files to import 3D data. These files were created by the python script that exported data from blender. +It was important to have always the lates version of the script that is compatible with the version of blender and to use it before importing data to jme. +Now we have an opportunity to simplify the import process by loading data directly from blender binary files: *.blend. +

+ +

+

Before you try to import models, make sure you created them properly. +

+

+ +
+ +

Usage

+
+ +

+To use it in your game or the SDK you should follow the standard asset loading instructions. +By default a BlenderModelLoader is registered with your assetManager to load blend files. This means you can load and convert .blend model files to .j3o format, just like any other supported model format. +

+ +
+ +

Currently supported features

+
+
    +
  1. Loading scene (only the current scene is loaded and imported as a node)
    +
  2. +
  3. Loading mesh objects.
    +
      +
    • Meshes are split into several geometries when they have several materials applied.
      +
    • +
    • All faces are stored as triangles (even if blender uses quads).
      +
    • +
    • The mesh is 'Smooth' aware.
      +
    • +
    • User defined UV coordinates are read.
      +
    • +
    • Loading BMesh is supported.
      +
    • +
    +
  4. +
  5. Loading textures.
    +
      +
    • Both image and generated textures are imported.
      +
    • +
    • Textures influence is supported ('Influence' tab in blender 2.5+ and 'Map to' in 2.49).
      +
    • +
    • Map input is not yet fully supported (currently working on it ;) ) so please use UV-mapping for all kinds of textures.
      +
    • +
    +
  6. +
  7. Image textures.
    +
      +
    • Textures can be loaded from: png, jpg, bmp, dds and tga.
      +
    • +
    • Both textures stored in the blender file and the outside are loaded (the outside textures need a valid path).
      +
    • +
    • Image textures are stored as Texture2D.
      +
    • +
    +
  8. +
  9. Generated textures.
    +
      +
    • All generated textures can be loaded except: VoxelData, EnviromentMap and PointDensity.
      +
    • +
    • Feel free to use colorbands.
      +
    • +
    • Generated textures are 'baked' into 2D textures and merged to create one flat texture. They can be freely merged with image textures.
      +
    • +
    • Generated textures can be used as normal maps (but this looks poor when large amount of small triangles is used; incleasing generated texture ppu in blender key might help a little)
      +
    • +
    +
  10. +
  11. Loading materials.
    +
      +
    • Materials are loaded and attached to geometries.
      +
    • +
    • Because jMonkeyEngine supports only one material for each Mesh, if you apply several materials to one object ??? it will be split into several meshes (but still in one node).
      +
    • +
    • Several kinds of input mapping is supported: UV maps, Orco and Nor; all projection types for 2D textures, XYZ coordinates mapping.
      +
    • +
    +
  12. +
  13. Loading animations.
    +
      +
    • Bone animations and object animations are supported.
      +
    • +
    • Armatures are imported as Skeleton. Constraint loading is not fully supported so use it carefully.
      +
    • +
    • Only assigning vertices to bones is at the moment supported so do not use bones' envelopes.
      +
    • +
    +
  14. +
  15. Loading modifiers.
    +
      +
    1. Array modifier
      +
    2. +
    3. Mirror modifier
      +
    4. +
    5. Armature modifier (see loading animations)
      +
    6. +
    7. Particles modifier (see loading particles)
      +
    8. +
    +
      +
    • More will come with time.
      +
    • +
    +
  16. +
  17. Constraints loading
    +
      +
    • Constraints are basicly supported but they do not work the way I'd like it. So feel free to experiment with it. I will create another post when I get it to work properly.
      +
    • +
    +
  18. +
  19. Particles loading.
    +
      +
    • Some features of particles loading is supported. You can use only particle emitters at the moment.
      +
    • +
    • You can choose to emit particles from vertices, faces or the geometry's convex hull (instead of volume).
      +
    • +
    • Currently Newtonian Physics is only supported.
      +
    • +
    • It was mostly tested for blender 2.49 (so I'm not 100% sure about its use in blender 2.5+).
      +
    • +
    +
  20. +
  21. Using sculpting.
    +
      +
    • This should work quite well for now :).
      +
    • +
    +
  22. +
  23. Importing curves.
    +
      +
    • Both bezier and NURBS curves are supproted.
      +
    • +
    • Feel free to use bevel and taper objects as well ;)
      +
    • +
    +
  24. +
  25. Importing surfaces
    +
      +
    • NURBS surface and sphere can be imported.
      +
    • +
    +
  26. +
+ +
+ +

Planned features.

+
+
    +
  1. Loading sky.
    +
  2. +
  3. Full support for scale and offset in texture input mapping.
    +
  4. +
  5. Full support for bone and object constraints.
    +
  6. +
  7. More modifiers loaded.
    +
  8. +
  9. Loading texts.
    +
  10. +
  11. Loading meta objects (if jme will support it ;) ).
    +
  12. +
+ +
+ +

Known bugs/problems.

+
+
    +
  1. RGB10 and RGB9E5 texture types are not supported in texture merging operations (which means that you can use this as a single texture on the model, but you should not combine it with other images or generated textures).
    +
  2. +
  3. If an armature is attached to a mesh that has more than one material the vertices of the mesh might be strongly displaced. Hope to fix that soon.
    +
  4. +
+ +
+ +

Using BlenderLoader instead of BlenderModelLoader

+
+ +

+You have two loaders available. + +

+ +
public static class LoadingResults extends Spatial {
+        /** Bitwise mask of features that are to be loaded. */
+        private final int featuresToLoad;
+        /** The scenes from the file. */
+        private List<Node> scenes;
+        /** Objects from all scenes. */
+        private List<Node> objects;
+        /** Materials from all objects. */
+        private List<Material> materials;
+        /** Textures from all objects. */
+        private List<Texture> textures;
+        /** Animations of all objects. */
+        private List<AnimData> animations;
+        /** All cameras from the file. */
+        private List<Camera> cameras;
+        /** All lights from the file. */
+        private List<Light> lights;
+	/** Access Methods goes here. */
+}
+ + +

+To register the model do the following: + +

+
assetManager.registerLoader(BlenderLoader.class, "blend");
+ +

+ +or + +

+
assetManager.registerLoader(BlenderModelLoader.class, "blend");
+ + +

+You can use com.jme3.asset.BlenderKey for that. +The simplest use is to create the key with the asset's name. +It has many differens settings descibing the blender file more precisely, but all of them have default values so you do not need to worry about it at the beggining. +You can use ModelKey as well. This will give the same result as using default BlenderKey. +

+ +
+ +

How does it work?

+
+ +

+BlenderLoader (as well as BlenderModelLoader) is looking for all kinds of known assets to load. +It's primary use is of course to load the models withon the files. +Each blender object is imported as scene Node. The node should have applied textures and materials as well. +If you define animations in your BlenderKey the animations will as well be imported and attached to the model. +

+ +

+Here is the list of how blender features are mapped into jme. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BlenderjMonkeyEngine3Note
Scene Node
Object Node
Mesh List<Geometry> One mesh can have several materials so that is why a list is needed here.
Lamp Light
Camera Camera
Material Material
Texture Texture
Curve Node Node with Curve as its mesh
Surface Node The surface is transformed to the proper mesh
+ +

+ +Using BlenderLoader can allow you to use blend file as your local assets repository. +You can store your textures, materials or meshes there and simply import it when needed. +Currently blender 2.49 and 2.5+ are supported (only the stable versions). +Probably versions before 2.49 will work pretty well too, but I never checked that :) +

+ +
+ +

Notes

+
+ +

+I know that the current version of loader is not yet fully functional, but belive me ??? Blender is a very large issue ;) +Hope I will meet your expectations. +

+ +

+Be mindful of the result model vertices amount. The best results are achieved when the model is smooth and has no texture. Then the vertex amount is equal to the vertex amount in blender. If the model is not smooth or has a generated texture applied then the amount of vertices is 3 times larger than mesh's triangles amount. If a 2d texture is applied with UV mapping then the vertex count will vary depending on how much the UV map is fragmented. +

+ +

+Cheers, +Marcin Roguski (Kaelthas) +

+ +

+P.S. +This text might be edited in a meantime if I forgot about something ;) + +

+
+ +

+See also: + +

+ +
+ documentation, + sdk, + tool, + asset +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/build-impl.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/build-impl.png new file mode 100644 index 000000000..4bb7d2872 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/build-impl.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/code_editor.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/code_editor.html new file mode 100644 index 000000000..23f46bf53 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/code_editor.html @@ -0,0 +1,261 @@ + +

jMonkeyEngine SDK: Code Editor and Palette

+
+ +

+ +The Source Code Editor is the central part of the jMonkeyEngine SDK. This documentation shows you how to make the most of the jMonkeyEngine SDK's assistive features. +

+ +

+Note: Since the jMonkeyEngine SDK is based on the NetBeans Platform framework, you can learn about certain jMonkeyEngine SDK features by reading the corresponding NetBeans IDE tutorials (in the "see also links"). +

+ +
+ +

Code Completion and Code Generation

+
+ +

+ +While typing Java code in the source code editor, you will see popups that help you to write more quickly by completing keywords, and generating code snippets. Additionally, they will let you see the javadoc for the classes you are working with. +

+ +

+ +

+ +

+Code Completion +

+ + +

+ +Code Generation +

+ + +
+ +

Semantic and Syntactic Coloring

+
+ +

+ + +

+ +

+The text color in the editor gives you important hints how the compiler will interpret what you typed, even before you compiled it. +

+ +

+Examples: +

+ + +

+ +To customize Colors and indentation: +

+ + +
+ +

Editor Hints and Quick Fixes (a.k.a. Lightbulbs)

+
+ +

+ +Editor hints and quick fixes show as lightbulbs along the left edge of the editor. They point out warnings and errors, and often propose useful solutions! + +

+ + +
+ +

Javadoc

+
+ + +

+ +To display a javadoc popup in the editor, place the caret in a line and press Ctrl-Space (Alternatively use Ctrl-\). + +

+ + +
+ +

Navigating the jME3 Source

+
+ +

+ +When the JavaDoc does not deliver enough information, you can have a look at the source of every method or object of jME3 that you use. Just right-click the variable or method, select "Navigate > Go to source.." and an editor will open showing you the source file of jME3. +

+ +
+ +

Palette

+
+ +

+ + +

+ +

+Choose Windows > Palette to open the context-sensitive Palette. The jMonkeyEngine SDK provides you with jme3 code snippets here that you can drag and drop into your source files. +

+ + +

+ +Tip: Choose Tools > Add to Palette??? from the menu to add your own code snippets to the Palette. (not available yet in beta build) +

+ +
+ +

Keyboard Shortcuts

+
+ +

+ +Keyboard Shortcuts save you time when when you need to repeat common actions such as Build&Run or navigation to files. +

+ + +

+ +By default, jMonkeyEngine uses the same as the NetBeans IDE, but you can also switch to an Eclipse Keymap, or create your own set. +

+ + +
+ +

Tips and Tricks

+
+ +
+ +

+See also + +

+ +
+ documentation, + sdk, + editor +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/debugging_profiling_testing.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/debugging_profiling_testing.html new file mode 100644 index 000000000..331b8cb4b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/debugging_profiling_testing.html @@ -0,0 +1,237 @@ + +

jMonkeyEngine SDK: Debugging, Profiling, Testing

+
+ +

+ +Debugging, testing and profiling are important parts of the development cycle. This documentation shows you how to make the most of the jMonkeyEngine SDK's assistive features. +

+ +

+

Since the jMonkeyEngine SDK is based on the NetBeans IDE and the NetBeans Platform, you can learn about certain jMonkeyEngine SDK features by reading the corresponding NetBeans IDE tutorials (in the "see also links"). +

+

+ +
+ +

Testing

+
+ +

+ +The jMonkeyEngine SDK supports the JUnit testing framework. It is a good practice to write tests (assertions) for each of your classes. Each test makes certain this "unit" (e.g. method) meets its design and behaves as intended. Run your tests after each major change and you immediately see if you broke something. +

+ +
+ +

Creating Tests

+
+
    +
  1. Right-click a Java file in the Projects window and choose Tools > Create JUnit Tests.
    +
  2. +
  3. Click OK. The jMonkeyEngine SDK creates a JUnit test skeleton in the Test Package directory.
    +
  4. +
  5. The body of each generated test method is provided solely as a guide. In their place, you need to write your actual test cases!
    +
  6. +
  7. You can use tests such as assertTrue(), assertFalse(), assertEquals(), or assert().
    +
      +
    • The following example assertions test an addition method: assert( add(1, 1) == 2); assertTrue( add(7,-5) == add(-5,7) )???
      +
    • +
    +
  8. +
  9. "Ideally", you write a test case for every method (100% coverage).
    +
  10. +
+ +

+ +Tip: Use the Navigate menu to jump from a test to its tested class, and back! +

+ +
+ +

Running Tests

+
+
    +
  1. Run one or all tests:
    +
      +
    • Right-click the class in the Projects window and Choose Test File, or
      +
    • +
    • Right-click the project and select Test to run all tests.
      +
    • +
    +
  2. +
  3. Check the Test window to see successful tests (green) and failures (red).
    +
  4. +
  5. If a test fails that has succeeded before, you know that your latest changes broke something!
    +
  6. +
+ +

+ +Using unit tests regularly allows you to detect side-effects on classes that you thought were unaffected by a code change. +

+ +

+See also: +

+ + +
+ +

Debugging

+
+ +

+ +In the jMonkeyEngine SDK, you have access to a debugger to examine your application for errors such as deadlocks and NullPointerExeptions. You can set breakpoints, watch variables, and execute your code line-by-line to identify the source of a problem. + +

+
    +
  1. First, you set breakpoints and/or watches before the problematic lines of code where you suspect the bug.
    +
      +
    • If you want to watch a variable's value: Right-click on a variable and select New Watch from the context menu.
      +
    • +
    • If you want to step through the execution line by line: Right-click on a line and choose Toggle Line Breakpoint; a pink box appears as a mark.
      +
    • +
    +
  2. +
  3. Choose "Debug > Debug Main Project" to start a debugger session for the whole project. Or, right-click a file and select Debug File to debug only one file.
    +
  4. +
  5. The application starts running normally. If you have set a breakpoint, the execution stops in this line. Debugger windows open and print debugger output.
    +
  6. +
  7. You can do many things now to track down a bug:
    +
      +
    • Inspect the values of local variables.
      +
    • +
    • Use the Step buttons in the top to step into, out of, and over expressions while you watch the execution.
      +
    • +
    • Navigate through your application's call stack. Right-click on threads to suspend or resume them.
      +
    • +
    • Choose Debug > Evaluate Expression from the menu to evaluate an expression.
      +
    • +
    • Move the mouse pointer over a variable to inspect its value in a tooltip.
      +
    • +
    • Inspect the classes loaded on the heap and the percentage and number of object instances. Right-click a class in the Loaded Classes window and choose Show in Instances view (JDK 6 only).
      +
    • +
    • And more???
      +
    • +
    +
  8. +
  9. To stop debugging, choose Debug > End Debugger Session from the menu.
    +
  10. +
+ +
+ +

Profiling

+
+ +

+ +The profiler tool is used to monitor thread states, CPU performance, and memory usage of your jme3 application. It helps you detect memory leaks and bottlenecks in your game while it's running. +

+ +
+ +

Installing the Profiler

+
+ +

+ +If you do not see a Profiler menu in the jMonkeyEngine SDK, you need to download the Profiler plugin first. +

+
    +
  1. Open the Tools > Plugins menu, and got to the "Available plugins" tab
    +
  2. +
  3. Find the "Java Profiler" plugin ("Java SE" category) and check the Install box.
    +
  4. +
  5. Click the install button and follow the instructions.
    +
  6. +
  7. When you start the profiler for the first time, you are prompted to run a calibration once. Click OK in the "Profiler integration" dialog to complete the installation process.
    +
  8. +
+ +
+ +

Monitoring and Analyzing

+
+
    +
  1. Choose Profile Project from the Profile menu.
    +
  2. +
  3. Select one of three tasks:
    +
      +
    • Monitor Application ??? Collect high-level information about properties of the target JVM, including thread activity and memory allocations.
      +
    • +
    • Analyze CPU Performance ??? Collect detailed data on application performance, including the time to execute methods and the number of times the method is invoked.
      +
    • +
    • Analyze Memory Usage ??? Collect detailed data on object allocation and garbage collection.
      +
    • +
    +
  4. +
  5. Click Run. Your application starts and runs normally.
    +
  6. +
  7. Use the Profiling window to track and collect live profiling results while you application is running.
    +
  8. +
+ +
+ +

Comparing Snapshots

+
+ +

+ +Click the Take Snapshot button to capture the profiling data for later! +

+ + +
+ +

Using Profiling Points

+
+ +

+ +Profiling points are similar to debugger breakpoints: You place them directly in the source code and they can trigger profiling behaviour when hit. +

+ + +

+ +See also: +

+ +
+ documentation, + sdk, + tool +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/default_build_script.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/default_build_script.html new file mode 100644 index 000000000..8056427c3 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/default_build_script.html @@ -0,0 +1,123 @@ + +

Default Build Script

+
+ +

+ +If you use jMonkeyEngine libraries together with the jMonkeyEngine SDK (recommended) then you benefit from the provided build script. Every new project comes with a default Ant script. The toolbar buttons and clean/build/run actions in the jMonkeyEngine SDK are already pre-configured. +

+ +
+ +

Default Targets

+
+ +

+ +The build script includes targets for the following tasks: + +

+ + +

+ +You can call these targets on the command line using default Ant commands: + +

+
ant clean
+ant jar
+ant run
+ +

+

We recommended to use the user-friendly menu items, F-keys, or toolbar buttons in the jMonkeyEngine SDK to trigger clean, build, and run actions. +

+

+ +
+ +

Browsing the Build Script

+
+ +

+ +To see the build script and the predefined tasks +

+
    +
  1. Open your project in the jMonkeyEngine SDK if it isn't already open (File > Open Project???)
    +
  2. +
  3. Open the Files window (Window > Files)
    +
  4. +
  5. Open the project node. You see build.xml listed.
    +
      +
    1. Double-click build.xml to see how the jme3-specify build targets were defined. You typically do not need to edit the existing ones, but you can.
      +
    2. +
    3. Click the triangle next to build.xml to see all targets.
      +
        +
      1. Double-click a target in the Files window, or the Navigator, to see how the target was defined.
        +You will notice that the file nbproject/build-impl.xml opens. It contains very generic targets that you typically will never need to edit. Note that build.xml includes build-impl.xml!
        +
      2. +
      +
    4. +
    +
  6. +
+ +
+ +

Adding Custom Targets

+
+ +

+ +The build script is a non-proprietary Apache Ant script. It will work out-of-the-box, but if necessary, you can extend and customize it. +

+ +

+Read the comments in build.xml, they explain how to override targets, or extend them, to customize the build process without breaking the existing functionality. +

+ +

+Additionally, you can manually override the targets in the *-impl.xml files that are created when you change the deployment settings: +

+ + +

+Simply copy&paste a target from these files into the main build.xml and that will be run instead with all modifications. +

+ +

+Don't edit the base *-impl.xml files directly, if you deactivate and reactivate a deployment setting, the SDK resets these files, so you have to copy the whole target and its dependencies, else your build script will become invalid when you disable the deployment option. +

+
+ documentation, + sdk, + builds, + project, + deployment +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/deploy_android.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/deploy_android.png new file mode 100644 index 000000000..f1f7f93e0 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/deploy_android.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html new file mode 100644 index 000000000..5d69658be --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html @@ -0,0 +1,119 @@ + +

Developing for jMonkeyEngine SDK

+
+ +

+Note that all info is subject to change while jMonkeyEngine SDK is still in beta! +

+ +

+In general, developing plugins for jMonkeyEngine SDK is not much different than creating plugins for the NetBeans Platform which in turn is not much different than creating Swing applications. You can use jMonkeyEngine SDK to develop plugins, be it for personal use or to contribute to the community. +

+ +

+If you feel like you want to make an addition to jMonkeyEngine SDK, don't hesitate to contact the jme team regardless of your knowledge in NetBeans platform development. For new plugins, the basic project creation and layout of the plugin can always be handled by a core developer and you can go on from there fleshing out the plugin. By using the Platform functions, your plugin feels more like a Platform application (global save button, file type support etc.). +

+ +
+ +

Creating plugins and components

+
+ + +
+ +

Extending jMonkeyEngine SDK

+
+ + +
+ +

Recipes

+
+ + +
+ +

General Notes

+
+ + +
+ +

Handy things in jMonkeyEngine SDK Core

+
+ + +

+ +Learn more about NetBeans Plugin Development at +

+ +

+Also check out this Essential NetBeans Platform Refcard: +

+
+ documentation, + sdk, + contribute +
+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/extension_library.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/extension_library.html new file mode 100644 index 000000000..e2a0cecc0 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/extension_library.html @@ -0,0 +1,80 @@ + +

Creating an extension library plugin

+
+ +

+This page describes how you can wrap any jar library into a plugin that a jMonkeyEngine SDK user can download, install and then use the contained library in his own game projects. +

+ +

+Make sure you have your SDK set up for plugin development as described here. +

+ +

+Creating the plugin project (in jMonkeyEngine SDK): +

+ + +

+ +Adding the library: +

+ + +

+ +You will notice a new file "MyLibrary.xml" is created in the plugins base package and linked to in the layer.xml file. Additionally the jar file and sources /javadoc are copied into a "release" folder in the project root. This is basically it, you can configure a version number, license file (should be placed in Module root folder) and more via the Module Properties. +

+ +

+Note that the files in the release folder are not automatically updated when the library changes, you have to pack and replace the jar and zip files manually. See the build script extension in the link below on how you can make your module build script do that automatically. +

+ +

+After you are done, you can contribute the plugin in the jMonkeyEngine SDK contribution update center. +

+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/general.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/general.html new file mode 100644 index 000000000..341573250 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/general.html @@ -0,0 +1,22 @@ + +

Creating plugin components

+
+ +

+For the most common extensions like Windows, File Types, Libraries etc. there exist templates that you can add to your plugin. + +

+
    +
  1. Right-click your Module Project and select New???Other
    +
  2. +
  3. Select "Module Development" to the left
    +
  4. +
+ +

+ +You will see a list of components you can add to your project. A wizard will guide you through the creation of the component. +

+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/model_loader.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/model_loader.html new file mode 100644 index 000000000..1c032e8a7 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/model_loader.html @@ -0,0 +1,42 @@ + +

jMonkeyEngine SDK: Creating a model importer

+
+ +

+ +You can create custom model importers for the jMonkeyEngine SDK. The SDK supports NBM plugins. + +

+
    +
  1. +
  2. +
  3. Add importer jar file (wrap jar file)
    +
  4. +
  5. Add filetype (Template)
    +
  6. +
  7. Change DataObject to extend SpatialAssetDataObject
    +
  8. +
  9. Implement loadAsset method in DataObject (if necessary, most model formats should load normally via the loader)
    +
  10. +
  11. Create AssetManagerConfigurator
    +
  12. +
+ +

+ +See also: +

+ +
+ documentation, + sdk, + tool +
+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/projects_assets.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/projects_assets.html new file mode 100644 index 000000000..9ec8ba80a --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/projects_assets.html @@ -0,0 +1,76 @@ + +

Projects and Assets

+
+ +

+The SDK heavily uses the systems provided by the base platform for the handling of assets and projects and extends the system with jME3 specific features. + +

+ +
+ +

ProjectAssetManager

+
+ +

+All AssetDataObjects and SceneExplorerNodes allow access to the ProjectAssetManager of the project they were loaded from. + +

+
ProjectAssetManager pm = node.getLookup().lookup(ProjectAssetManager.class)
+ +

+ +The ProjectAssetManager is basically a normal DesktopAssetManager for each project with some added functionality: +

+ + +
+ +

AssetDataObject

+
+ +

+Most "files" that you encounter in the SDK come in the form of AssetDataObjects. All Nodes that you encounter contain the AssetDataObject they were loaded from. It provides not just access to the FileObject of the specific file but also an AssetData object that allows access to jME specific properties and data. The AssetData object also allows loading the object via the jME3 assetManager. It is accessible via the lookup of the Node or AssetDataObject: + +

+
assetDataObject.getLookup().lookup(AssetData.class)
+ +
+ +

New Asset File Types

+
+ +

+When you add a new file type for a model format or other asset file that can be loaded in jME3 you can start by using new file type template (New File???Module Development???File Type). Change the DataObject to extend AssetDataObject (general), SpatialAssetDataObject (some type of model) or BinaryModelDataObject (basically a j3o savable file). And possibly override the loadAsset and saveAsset methods which are used by the AssetData object to return the correct AssetKey type (needed for import properties to work). + +

+
public class BlenderDataObject extends SpatialAssetDataObject {
+    public BlenderDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+        super(pf, loader);
+    }
+}
+ +

+ +An AssetManagerConfigurator class can be created to configure the assetManager of the projects and model importer to use the new asset type: + +

+
@org.openide.util.lookup.ServiceProvider(service = AssetManagerConfigurator.class)
+public class BlenderAssetManagerConfigurator implements AssetManagerConfigurator {
+    public void prepareManager(AssetManager manager) {
+        manager.registerLoader(com.jme3.scene.plugins.blender.BlenderModelLoader.class, "blend");
+    }
+}
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/scene.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/scene.html new file mode 100644 index 000000000..5b1868d88 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/scene.html @@ -0,0 +1,166 @@ + +

jMonkeyEngine SDK -- The Scene

+
+ +

+ +To reduce system overhead the jMonkeyEngine SDK Core supplies one scene/jme3 application that is shared between plugins. Furthermore there's the "SceneExplorer" that shows a visual representation of the scenegraph and its objects properties across plugins. +

+ +
+ +

How to access the Scene

+
+ +

+ +There are several ways for your plugin to interact with the Scene: +

+ + +
+ +

Listening for Node selection

+
+ +

+ +In the jMonkeyEngine SDK, all objects are wrapped into NetBeans "Nodes" (different thing than jme Nodes!). Such nodes can have properties and icons and can be displayed and selected in the jMonkeyEngine SDK UI. The SceneExplorer shows a tree of Nodes that wrap the Spatials of the current scene and allows manipulating their properties on selection. A jME "Spatial" is wrapped by a "JmeSpatial" node, for example. One advantage of these Nodes is that one can manipulate properties of Spatials directly from the AWT thread. +

+ +

+To listen to the current selection, implement org.openide.util.LookupListener and register like this: +

+
private final Result<JmeSpatial> result;
+ 
+//method to register the listener;
+private void registerListener(){
+    result = Utilities.actionsGlobalContext().lookupResult(JmeSpatial.class);
+    result.addLookupListener(this);
+}
+ 
+//implements org.openide.util.LookupListener (called from AWT thread)
+public void resultChanged(LookupEvent ev) {
+    Collection<JmeSpatial> items = (Collection<JmeSpatial>) result.allInstances();
+    for (JmeSpatial jmeSpatial : items) {
+        //Using the JmeSpatials properties you can modify the spatial directly from the AWT thread:
+        spatial.getPropertySets()[0].setValue("Local Translation", Vector3f.ZERO);
+        return;
+    }
+}
+ +

+You can also access the "real" spatial but since its part of the scenegraph you will have to modify it on that thread: +

+
//retrieve the "real" spatial class from the JmeNode
+for (JmeSpatial jmeSpatial : items) {
+    //the spatial is stored inside the JmeSpatials "Lookup", a general container for Objects
+    final Spatial realSpatial = jmeSpatial.getLookup().lookup(Spatial.class);
+    //use a Callable to execute on the render thread:
+    SceneApplication.getApplication().enqueue(new Callable() {
+        public Object call() throws Exception {
+            realSpatial.setLocalTranslation(Vector3f.ZERO);
+            return null;
+        }
+    });
+    return;
+}
+ +
+ +

Requesting the Scene

+
+ +

+ +If your plugin wants to use the scene by itself, it first has to implement SceneListener and register at the scene and then send a SceneRequest to the SceneApplication. When the SceneRequest has been approved and the current Scene has been closed, the SceneListener (your class) is called with its own SceneRequest as a parameter. When another plugin sends a SceneRequest it is also reported to you and its a hint that your RootNode has been removed from the Scene and you are no longer in control of it. You could also hook into the SceneRequests of other plugins to see if/when they are activated to display add-on plugins for that plugin. +

+ +

+
+ +The SceneRequest object has to contain several things. A thing that you must supply is a jme "Node" wrapped into a "JmeNode" object. This is your rootNode that you use to display and build your scene. As soon as you control the scene, you will have to control the camera etc. yourself. +

+
com.jme3.scene.Node rootNode = new com.jme3.scene.Node("MyRootNode");
+ 
+private void registerSceneListener(){
+    SceneApplication.getApplication().addSceneListener(this);
+}
+ 
+private void requestScene(){
+    //create a jmeNode from the rootNode using the NodeUtility
+    JmeNode jmeNode = NodeUtility.createNode(rootNode);
+    //create the scene request
+    SceneRequest request=new SceneRequest(this, jmeNode, assetManager);
+    //request the scene
+    SceneApplication.getApplication().openScene(request);
+}
+ 
+//implements SceneListener (called from AWT thread)
+public void sceneOpened(SceneRequest request){
+    //check if its our request
+    if (request.getRequester() == this) {
+        //we now own the scene, any operations on the scene have to be done via Callables
+    }
+}
+ 
+public void sceneClosed(SceneRequest request) {
+    if (request.getRequester() == this) {
+        //we have to close the scene,  any operations on the scene have to be done via Callables
+    }
+}
+ +
+ +

Undo/Redo support

+
+ +

+The jMonkeyEngine SDK has a global undo/redo queue that activates the undo/redo buttons. To use it in your TopComponent, add the following method: + +

+
@Override 
+public UndoRedo getUndoRedo() { 
+return Lookup.getDefault().lookup(SceneUndoRedoManager.class); 
+} 
+ +

+To add a undo/redo event that modifies objects on the Scenegraph, theres a special version of AbstractUndoableEdit which executes the undo/redo calls on the scene thread. Simply implement that class and add it to the queue like this: + +

+
Lookup.getDefault().lookup(SceneUndoRedoManager.class).addEdit(this, new AbstractUndoableSceneEdit() { 
+ 
+@Override 
+public void sceneUndo() { 
+    //undo stuff in scene here
+} 
+ 
+@Override 
+public void sceneRedo() { 
+    //redo stuff in scene here
+} 
+ 
+@Override 
+public void awtUndo() { 
+    //undo stuff on awt thread here (updating of visual nodes etc, called post scene edit)
+} 
+ 
+@Override 
+public void awtRedo() { 
+    //redo stuff on awt thread here
+} 
+});
+ +

+Note: Its important that you use the method addEdit(Object source, UndoableEdit edit); + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/sceneexplorer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/sceneexplorer.html new file mode 100644 index 000000000..ebe7d6526 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/sceneexplorer.html @@ -0,0 +1,405 @@ + +

The SceneExplorer

+
+ +
+ +

Adding Node types to SceneExplorer

+
+ +

+ +If your plugin brings in its own SceneGraph objects you can still have them work like any other SceneExplorer item, including its special properties. +

+ +

+If you want to support special properties of your objects that are not exposed by the SDK automatically, you will have to create your own class that extends org.openide.nodes.Node and implement the interface com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode. Then you register that class by adding +

+
@org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
+ +

+ above the body of your class. Thats all, your Spatial type will automatically be used and displayed in the SceneExplorer. Make sure you register a jar with the used classes in the plugin preferences under "wrapped libraries", otherwise the IDE cannot access those classes. +

+ +

+AbstractSceneExplorerNode brings some other useful features you might want to include like automatic creation of properly threaded properties etc. JmeSpatial for example bases on it. A simple SceneExplorerNode example for an object extending Spatial would be JmeGeometry (see below). Editors for special variable types can be added using the SceneExplorerPropertyEditor interface, which can be registered as a ServiceProvider as well. +

+ +

+The SceneExplorerNode can be used for Spatial and Control type objects. + +

+ + +
+ +

Spatial Example

+
+
@org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
+public class JmeGeometry extends JmeSpatial {
+ 
+    private static Image smallImage =
+            ImageUtilities.loadImage("com/jme3/gde/core/sceneexplorer/nodes/icons/geometry.gif");
+    private Geometry geom;
+ 
+    public JmeGeometry() {
+    }
+ 
+    public JmeGeometry(Geometry spatial, SceneExplorerChildren children) {
+        super(spatial, children);
+        getLookupContents().add(spatial);
+        this.geom = spatial;
+        setName(spatial.getName());
+    }
+ 
+    @Override
+    public Image getIcon(int type) {
+        return smallImage;
+    }
+ 
+    @Override
+    public Image getOpenedIcon(int type) {
+        return smallImage;
+    }
+ 
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        Sheet.Set set = Sheet.createPropertiesSet();
+        set.setDisplayName("Geometry");
+        set.setName(Geometry.class.getName());
+        Geometry obj = geom;//getLookup().lookup(Geometry.class);
+        if (obj == null) {
+            return sheet;
+        }
+ 
+        set.put(makeProperty(obj, int.class, "getLodLevel", "setLodLevel", "Lod Level"));
+        set.put(makeProperty(obj, Material.class, "getMaterial", "setMaterial", "Material"));
+        set.put(makeProperty(obj, Mesh.class, "getMesh", "Mesh"));
+ 
+        sheet.put(set);
+        return sheet;
+ 
+    }
+ 
+    public Class getExplorerObjectClass() {
+        return Geometry.class;
+    }
+ 
+    public Class getExplorerNodeClass() {
+        return JmeGeometry.class;
+    }
+ 
+    public org.openide.nodes.Node[] createNodes(Object key, Object key2, boolean readOnly) {
+        SceneExplorerChildren children=new SceneExplorerChildren((com.jme3.scene.Spatial)key);
+        children.setReadOnly(readOnly);
+        return new org.openide.nodes.Node[]{new JmeGeometry((Geometry) key, children).setReadOnly(readOnly)};
+    }
+}
+ +
+ +

Control Example

+
+
@org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
+public class JmeGhostControl extends AbstractSceneExplorerNode {
+ 
+    private static Image smallImage =
+            ImageUtilities.loadImage("com/jme3/gde/core/sceneexplorer/nodes/icons/ghostcontrol.gif");
+    private GhostControl control;
+ 
+    public JmeGhostControl() {
+    }
+ 
+    public JmeGhostControl(GhostControl control, DataObject dataObject) {
+        super(dataObject);
+        getLookupContents().add(this);
+        getLookupContents().add(control);
+        this.control = control;
+        setName("GhostControl");
+    }
+ 
+    @Override
+    public Image getIcon(int type) {
+        return smallImage;
+    }
+ 
+    @Override
+    public Image getOpenedIcon(int type) {
+        return smallImage;
+    }
+ 
+    protected SystemAction[] createActions() {
+        return new SystemAction[]{
+                    //                    SystemAction.get(CopyAction.class),
+                    //                    SystemAction.get(CutAction.class),
+                    //                    SystemAction.get(PasteAction.class),
+                    SystemAction.get(DeleteAction.class)
+                };
+    }
+ 
+    @Override
+    public boolean canDestroy() {
+        return !readOnly;
+    }
+ 
+    @Override
+    public void destroy() throws IOException {
+        super.destroy();
+        final Spatial spat=getParentNode().getLookup().lookup(Spatial.class);
+        try {
+            SceneApplication.getApplication().enqueue(new Callable<Void>() {
+ 
+                public Void call() throws Exception {
+                    spat.removeControl(control);
+                    return null;
+                }
+            }).get();
+            ((AbstractSceneExplorerNode)getParentNode()).refresh(true);
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (ExecutionException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+ 
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        Sheet.Set set = Sheet.createPropertiesSet();
+        set.setDisplayName("GhostControl");
+        set.setName(GhostControl.class.getName());
+        GhostControl obj = control;//getLookup().lookup(Spatial.class);
+        if (obj == null) {
+            return sheet;
+        }
+ 
+        set.put(makeProperty(obj, Vector3f.class, "getPhysicsLocation", "setPhysicsLocation", "Physics Location"));
+        set.put(makeProperty(obj, Quaternion.class, "getPhysicsRotation", "setPhysicsRotation", "Physics Rotation"));
+ 
+        set.put(makeProperty(obj, CollisionShape.class, "getCollisionShape", "setCollisionShape", "Collision Shape"));
+        set.put(makeProperty(obj, int.class, "getCollisionGroup", "setCollisionGroup", "Collision Group"));
+        set.put(makeProperty(obj, int.class, "getCollideWithGroups", "setCollideWithGroups", "Collide With Groups"));
+ 
+        sheet.put(set);
+        return sheet;
+ 
+    }
+ 
+    public Class getExplorerObjectClass() {
+        return GhostControl.class;
+    }
+ 
+    public Class getExplorerNodeClass() {
+        return JmeGhostControl.class;
+    }
+ 
+    public org.openide.nodes.Node[] createNodes(Object key, DataObject key2, boolean cookie) {
+        return new org.openide.nodes.Node[]{new JmeGhostControl((GhostControl) key, key2).setReadOnly(cookie)};
+    }
+}
+ +
+ +

Adding items to the add and tools menus

+
+ +

+For adding Spatials, Contols and for general tools theres premade abstract classes that you can use to extend the options. Undo/Redo is handled by the abstract class. AbstractNewSpatialWizardAction allows you to show an AWT wizard before creating the Spatial. You can also just implement the base ServiceProvider class and return any kind of action (such as a wizard), in this case you have to handle the threading yourself! +

+ +

+

Note that the classes you create are singletons which are used across multiple nodes and you should not store any data in local variables! +

+

+ +

+To add a new Tool, create a new AbstractToolAction: +

+
@org.openide.util.lookup.ServiceProvider(service = ToolAction.class)
+public class GenerateTangentsTool extends AbstractToolAction {
+ 
+    public GenerateTangentsTool() {
+        name = "Generate Tangents";
+    }
+ 
+    @Override
+    protected Object doApplyTool(AbstractSceneExplorerNode rootNode) {
+        Geometry geom = rootNode.getLookup().lookup(Geometry.class);
+        Mesh mesh = geom.getMesh();
+        if (mesh != null) {
+            TangentBinormalGenerator.generate(mesh);
+        }
+        return geom;
+    }
+ 
+    @Override
+    protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) {
+        Geometry geom = rootNode.getLookup().lookup(Geometry.class);
+        Mesh mesh = geom.getMesh();
+        if (mesh != null) {
+            mesh.clearBuffer(Type.Tangent);
+        }
+    }
+ 
+    public Class<?> getNodeClass() {
+        return JmeGeometry.class;
+    }
+ 
+}
+ +

+For a new Spatial or Control, use AbstractNewSpatialAction +

+
@org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)
+public class NewSpecialSpatialAction extends AbstractNewSpatialAction {
+ 
+    public NewSpecialSpatialAction() {
+        name = "Spatial";
+    }
+ 
+    @Override
+    protected Spatial doCreateSpatial(Node parent) {
+        Spatial spatial=new Node();
+        return spatial;
+    }
+}
+ +

+or AbstractNewControlAction: +

+
@org.openide.util.lookup.ServiceProvider(service = NewControlAction.class)
+public class NewRigidBodyAction extends AbstractNewControlAction {
+ 
+    public NewRigidBodyAction() {
+        name = "Static RigidBody";
+    }
+ 
+    @Override
+    protected Control doCreateControl(Spatial spatial) {
+        RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
+        if (control != null) {
+            spatial.removeControl(control);
+        }
+        Node parent = spatial.getParent();
+        spatial.removeFromParent();
+        control = new RigidBodyControl(0);
+        if (parent != null) {
+            parent.attachChild(spatial);
+        }
+        return control;
+    }
+}
+ +
+ +

Adding using a Wizard

+
+ +

+You can create a new wizard using the wizard template in the SDK (New File???Module Development???Wizard). The Action that the template creates can easily be changed to one for adding a Control or Spatial or for applying a Tool. Note that we extend AbstractNewSpatialWizardAction here. +

+ +

+A good example is the "Add SkyBox" Wizard: + +

+
@org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)
+public class AddSkyboxAction extends AbstractNewSpatialWizardAction {
+ 
+    private WizardDescriptor.Panel[] panels;
+ 
+    public AddSkyboxAction() {
+        name = "Skybox..";
+    }
+ 
+    @Override
+    protected Object showWizard(org.openide.nodes.Node node) {
+        WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
+        wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
+        wizardDescriptor.setTitle("Skybox Wizard");
+        Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
+        dialog.setVisible(true);
+        dialog.toFront();
+        boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;
+        if (!cancelled) {
+            return wizardDescriptor;
+        }
+        return null;
+    }
+ 
+    @Override
+    protected Spatial doCreateSpatial(Node parent, Object properties) {
+        if (properties != null) {
+            return generateSkybox((WizardDescriptor) properties);
+        }
+        return null;
+    }
+ 
+    private Spatial generateSkybox(WizardDescriptor wiz) {
+        if ((Boolean) wiz.getProperty("multipleTextures")) {
+            Texture south = (Texture) wiz.getProperty("textureSouth");
+            Texture north = (Texture) wiz.getProperty("textureNorth");
+            Texture east = (Texture) wiz.getProperty("textureEast");
+            Texture west = (Texture) wiz.getProperty("textureWest");
+            Texture top = (Texture) wiz.getProperty("textureTop");
+            Texture bottom = (Texture) wiz.getProperty("textureBottom");
+            Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
+            return SkyFactory.createSky(pm, west, east, north, south, top, bottom, normalScale);
+        } else {
+            Texture textureSingle = (Texture) wiz.getProperty("textureSingle");
+            Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
+            boolean useSpheremap = (Boolean) wiz.getProperty("useSpheremap");
+            return SkyFactory.createSky(pm, textureSingle, normalScale, useSpheremap);
+        }
+    }
+ 
+    /**
+     * Initialize panels representing individual wizard's steps and sets
+     * various properties for them influencing wizard appearance.
+     */
+    private WizardDescriptor.Panel[] getPanels() {
+        if (panels == null) {
+            panels = new WizardDescriptor.Panel[]{
+                        new SkyboxWizardPanel1(),
+                        new SkyboxWizardPanel2()
+                    };
+            String[] steps = new String[panels.length];
+            for (int i = 0; i < panels.length; i++) {
+                Component c = panels[i].getComponent();
+                // Default step name to component name of panel. Mainly useful
+                // for getting the name of the target chooser to appear in the
+                // list of steps.
+                steps[i] = c.getName();
+                if (c instanceof JComponent) { // assume Swing components
+                    JComponent jc = (JComponent) c;
+                    // Sets step number of a component
+                    // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_*:
+                    jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
+                    // Sets steps names for a panel
+                    jc.putClientProperty("WizardPanel_contentData", steps);
+                    // Turn on subtitle creation on each step
+                    jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
+                    // Show steps on the left side with the image on the background
+                    jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
+                    // Turn on numbering of all steps
+                    jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
+                }
+            }
+        }
+        return panels;
+    }
+}
+ +

+ +

The abstract spatial and control actions implement undo/redo automatically, for the ToolActions you have to implement it yourself. +

+

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/setup.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/setup.html new file mode 100644 index 000000000..3ceb3cfd2 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development/setup.html @@ -0,0 +1,155 @@ + +

Creating a jMonkeyEngine SDK plugin

+
+ +

+Note that the creation of a Module Suite is only necessary if you want to upload your plugin to the contribution update center. + +

+ +
+ +

Using jMonkeyEngine SDK for development

+
+ + +
+ +

jMonkeyEngine SDK Contributions Update Center

+
+ +

+If you want your plugin to appear in the "jMonkeyEngine SDK Contributions Update Center" so users can download, install and update it easily via the plugin manager, you can host your plugin in the contributions update center svn repository. The contributions update center is based on a googlecode project for contributed plugins which will be automatically compiled, version-labeled and added to the contributions update center like the core jMonkeyEngine SDK plugins. +

+ +

+Effectively its one large module suite with multiple modules which each represent one plugin, extension library. +

+ +
+ +

Adding your plugin to the repository

+
+ +

+To add your plugin to the repository, do the following: +

+ + +

+ +And thats it, from now on each time you commit changes to your module it will be built and added to the contributions center automatically and the version number will be extended by the svn revision number (e.g. 0.8.1.1234) +

+ +
+ +

Building library jar files on the server

+
+ +

+You can just build your library locally and update and commit the jar file and javadoc/sources zip files to the contrib repo, the users plugins will automatically be updated with the new jar files. You can however also build the whole library project on the server. +

+ +

+As only the module project is being built on the server, any projects that create the actual jar files for library plugins ("normal" projects from the SDK/NetBeans) have to be built from the module build file. To do that simply add the following ant targets to the module build file: + +

+
<target name="init" depends="basic-init,files-init,build-init,-javac-init,-build-subproject"/>
+<target name="-build-subproject">
+    <ant dir="./MySubProject" inheritall="false" target="jar"/>
+    <copy file="./MySubProject/dist/MySubProject.jar" todir="release/libs"/>
+</target>
+ +

+ +Note that for the module version number to increase automatically on a commit to the library project, the library project has to be a subfolder of the main module project. + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filterexplorer.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filterexplorer.png new file mode 100644 index 000000000..a03bb052a Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filterexplorer.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filters.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filters.html new file mode 100644 index 000000000..6050a73b9 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/filters.html @@ -0,0 +1,98 @@ + +

jMonkeyEngine SDK: Post-Processor Filters

+
+ +

+ +Filters are used for scene-wide effects such as glow, fog, blur. The SDK lets you create a file storing combinations of filters. You can preview the filter settings on a loaded scene in the SDK. You can load them into your application (add them to the viewPort) to activate your preconfigured set of several filters in one step. +

+ +
+ +

Creating Filters

+
+ +

+ +To create a new filter: +

+ + +
+ +

Editing Filters

+
+ +

+ +To add filters or modify existing filters +

+
    +
  1. Double-click a j3f file to open it in the FilterExplorer window.
    +
  2. +
  3. Right-click the j3f file's root node to add a filter.
    +Added filters are listed under the filter's root node.
    +
  4. +
  5. Open the Properties window and select a filter in the FilterExplorer. Configure filter options like intensity etc.
    +
  6. +
+ +

+ +View the filter in the SceneComposer to see what you are doing: +

+ +
+ +

Viewing Filters

+
+ +

+ + +

+ +

+To see a loaded filter +

+
    +
  1. Open a model or scene in the SceneComposer.
    +
  2. +
  3. Double-click a j3f file to open it in the FilterExplorer window.
    +
  4. +
  5. Press the "show filter" button in the OpenGL window.
    +
  6. +
+ +
+ +

Loading filters in a game

+
+ +

+ +To load a filter in a game (that is, to add it to your game's viewport), add the following lines to your game's simpleInit() method (or some other place): +

+
FilterPostProcessor processor = (FilterPostProcessor) assetManager.loadAsset("Filters/MyFilter.j3f");
+viewPort.addProcessor(processor);
+
+ documentation, + sdk, + effect, + file +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkey-branding.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkey-branding.png new file mode 100644 index 000000000..1ab57be01 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkey-branding.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-2.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-2.png new file mode 100644 index 000000000..1810c655d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-2.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-3.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-3.png new file mode 100644 index 000000000..769400ead Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-3.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-4.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-4.png new file mode 100644 index 000000000..d582c3eb8 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-4.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-5.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-5.png new file mode 100644 index 000000000..c2ef24b0a Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-docu-5.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-sceneexplorer-add.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-sceneexplorer-add.jpg new file mode 100644 index 000000000..1f3688a20 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/jmonkeyplatform-sceneexplorer-add.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/log_files.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/log_files.html new file mode 100644 index 000000000..af3e4012e --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/log_files.html @@ -0,0 +1,49 @@ + +

jMonkeyEngine SDK: Log Files

+
+ +

+ +You find the jMonkeyEngine SDK log file in /dev/var/log/messages.log in the jMonkeyEngine SDK preferences folder. You can learn the location of the preferences folder in the ???About??? screen of the jMonkeyEngine SDK under the label Userdir. + +

+ + +

+ +The message log contains all paths and capabilities used in your development system, and also warnings, e.g. if a plugin crashed. +

+ +
+ +

Example Log

+
+
>Log Session: Saturday, September 24, 2011 10:45:30 AM CEST
+>System Info: 
+  Product Version         = jMonkeyPlatform Alpha-4
+  Operating System        = Mac OS X version 10.6.8 running on i386
+  Java; VM; Vendor        = 1.6.0_26; Java HotSpot(TM) Client VM 20.1-b02-384; Apple Inc.
+  Runtime                 = Java(TM) SE Runtime Environment 1.6.0_26-b03-384-10M3425
+  Java Home               = /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
+  System Locale; Encoding = de_DE (jmonkeyplatform); MacRoman
+  Home Directory          = /Users/joemonkey
+  Current Directory       = /
+  User Directory          = /Users/joemonkey/Library/Application Support/jmonkeyplatform/dev
+  Installation            = /Applications/jmonkeyplatform.app/Contents/Resources/jmonkeyplatform/jmonkeyplatform
+  ...
+
+ documentation, + sdk, + file +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material-editor.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material-editor.png new file mode 100644 index 000000000..7adc7c6a3 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material-editor.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html new file mode 100644 index 000000000..52a51c0cb --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html @@ -0,0 +1,146 @@ + +

jMonkeyEngine SDK: Material Editor

+
+ +

+ +If you are looking for background information, read about Material Definitions and j3M Material Files. +You can write .j3m files in a text editor, or use the jMonkeyEngine SDK to generate them for you as described in this article. +

+ +
+ +

Materials

+
+ +

+ +The jMonkeyEngine uses a special Material format, which comes in .j3m files. You use .j3m files to store sets of material properties that you use repeatedly. This enables you write one short line of code that simply loads the presets from a custom .j3m file. Without a .j3m file you need to write several lines of material property setters every time when you want to use a non-default material. +

+ +
+ +

Creating .j3m Materials

+
+ +

+ + +

+ +

+To create new .j3m files in the jMonkeyEngine SDK, +

+
    +
  1. Right-click the assets/Materials directory and choose New??? > Other.
    +
  2. +
  3. In the New File Wizard, choose Material > Empty Material File, and click Next.
    +
  4. +
  5. Give the file a name, for example mat_wall for a wall material.
    +
  6. +
  7. A new file mat_wall.j3m is created in the Materials directory and opens in the Material Editor.
    +
  8. +
+ +

+ +You can edit the source of the material, or use the user-friendly visual editor to set the properties of the material. Set the properties to the same values as you would otherwise specify with setters on a Material object in Java code: + +

+
Material mat_wall = new Material(
+    assetManager, "Common/MatDefs/Light/Lighting.j3md");
+mat_wall.setTexture("DiffuseMap", 
+    assetManager.loadTexture("Textures/wall_diffuse.png"));
+mat_wall.setTexture("NormalMap", 
+    assetManager.loadTexture("Textures/wall_normals.png"));
+mat_wall.setFloat("Shininess", 5f);
+ +

+ +This Java code corresponds to the following .j3m file: + +

+
Material my brick wall : Common/MatDefs/Light/Lighting.j3md {
+  MaterialParameters {
+    DiffuseMap: Repeat Textures/wall_diffuse.png
+    NormalMap:  Repeat Textures/wall_normals.png
+    Shininess: 5.0
+  }
+}
+ +

+You can modify the source code of the j3m file in the "source" tab of the Material Editor. +

+ +
+ +

Using .j3m Materials

+
+ +

+ + +

+ +

+When the material is ready and saved into your projects assets directory, you can assign the .j3m to a Geometry. +

+ +

+In the jMonkeyEngine SDK +

+
    +
  1. Right-click the j3o file and select "Edit in SceneComposer"
    +
  2. +
  3. Open the SceneExplorer window
    +
  4. +
  5. In the SceneExplorer, click the geometry to which you want to assign the material.
    +
  6. +
  7. Open the Properties window
    +
  8. +
  9. Assign the .j3m material to the .j3o in the Properties>Geometry>Material section
    +
  10. +
  11. Save the j3o and load it into you game.
    +
  12. +
+ +

+ +Or in your Java code +

+ +
mywall.setMaterial(assetManager.loadAsset( "Materials/mat_wall.j3m"));
+
+ +

+See also: + +

+ +
+ documentation, + sdk, + material, + file, + texture +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/model_loader_and_viewer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/model_loader_and_viewer.html new file mode 100644 index 000000000..f38b76395 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/model_loader_and_viewer.html @@ -0,0 +1,189 @@ + +

jMonkeyEngine SDK: Importing and Viewing Models

+
+ +

+ +The jMonkeyEngine SDK imports models from your project and stores them in the assets folder. The imported models are converted to a jME3 compatible binary format called .j3o. Double-click .j3o files in the jMonkeyEngine SDK to display them in the SceneViewer, or load them in-game using the AssetManager. +

+ +

+Presently, is the preferred modelling tool for jME3 as it is also Open-Source Software and an exporter for OgreXML files exists. There is a direct .blend file importer available in the SDK and you can directly import or store blend files in your project to convert them. If for some reason your version of blender is not compatible, you can use the default OgreXML format. Note that the OgreXML exporter is not compatible with Blender 2.49 or before! +

+ +

+Also, see this on importing models. +

+ +
+ +

Installing the OgreXML Exporter in Blender

+
+ +

+ +The jMonkeyEngine SDK includes a tool to install the correct exporter tools in Blender to export OgreXML files. To install the exporter, do the following: + +

+
    +
  1. Select "Tools"???"OgreXML"???"Install Blender OgreXML" in the jMonkeyEngine SDK Menu
    +
  2. +
  3. If you are presented a filechooser, select the folder where your blender scripts reside
    +
  4. +
  5. Press "Install" in the window that opens
    +
  6. +
+ +

+ +Also check out how to create compatible models in blender and how to organize your assets. + +

+ +
+ +

Importing and Viewing a Model

+
+ +
+ +

Using the Model Importer Tool

+
+ +

+jMonkeyEngine SDK includes a model importer tool that allows you to preview and import supported models into your jMonkeyEngine3 project. +

+ +

+ + + +

+
    +
  1. Select the project you want to import your model to
    +
  2. +
  3. Click the "Import Model" button in the toolbar
    +
  4. +
  5. Click "open model.." and select the main model file
    +
  6. +
  7. Check the preview and file list and press "Next"
    +
  8. +
  9. Check and change the import path if necessary
    +
  10. +
  11. If you want to copy the original model files as well, check the checkbox
    +
  12. +
  13. Press "finish"
    +
  14. +
+ +

+ +The model is converted to j3o and all necessary files are copied to the project assets folder. If you have specified the corresponding option, all original model files will also be copied to your assets folder. +

+ +
+ +

Using the model files directly

+
+ +

+ + +

+
    +
  1. Create a separate folder for each model in the assets folder of your project.
    +
  2. +
  3. Save the model created in Blender (.blend) in the asset folder of your project,
    +
  4. +
  5. Make sure that no textures are packed in the .blend file.
    +
  6. +
  7. Make sure all textures used in the .blend file are in the assets folder of the project.
    +
  8. +
  9. In the Projects Explorer Assets node, select the model that you want to import.
    +
  10. +
  11. Double-click the model or right-click and select "Convert to JME binary" from the context-menu.
    +
  12. +
  13. In the Projects Explorer Assets node you should see your model j3o file.
    +
  14. +
  15. Double-click it to view it in the SceneViewer.
    +
  16. +
  17. Click on the lightbulb to turn on the light if you cannot see your model.
    +
  18. +
+ +

+ +Note: It is important that you copy the model file and its textures to the correct assets folder before creating the j3o file because the paths for textures (and possibly other things) will be stored as absolute (to the assets folder root) when you convert that model. This means the texture location should not change after the import. +

+ +

+Note: If the SceneViewer doesn't work refer to Troubleshooting jMonkeyEngine3 SDK. + +

+ +
+ +

Working With a Model

+
+ + +
+ +

Notes About Model Assets

+
+ +

+ +The original OgreXML .mesh.xml, .scene, .material, .skeleton.xml and .blend model files will not be included in the distribution assets.jar file of your distributed game, they are only available in the assets folder so you are able to recreate the .j3o file from the original if you ever come to change it in blender and have to export it again. +

+ +
+ +

About the SceneViewer and SceneExplorer window

+
+ +

+ +The SceneViewer and SceneExplorer windows are shared among plugins to save system resources. This means that you will have to keep an eye on what plugin is using the scene right now and what you are actually modifying in these windows. +

+ +

+Most plugins will deliver their own UI elements to modify the scene so the SceneExplorer is more of a global tool. The simple SceneComposer however heavily relies on its functions as other plugins might too in the future. +

+ +
+ +

About the projects AssetManager

+
+ +

+Each jMonkeyEngine SDK project has its own internal AssetManager that has the projects assets folder registered as a FileLocator. When the project is run, the assets folder is compressed into a jar file and added to the projects main jar classpath. This way the editors in jMonkeyEngine SDK and the running game have the same asset folder structure. +

+ +

+You might wonder why jMonkeyEngine SDK requires you to copy the model that is to be converted to j3o into the assets folder before. The Model Import Tool also copies the model and associated files to the project directory before converting. To load the model it needs to be in a folder (or jar etc..) that belongs to the projects AssetManager root. To load a model from some other folder of the filesystem, that folder would have to be added to the AssetManager root. If every folder that contains a model was in the root of the AssetManager, all textures named "hull.jpg" for example would be the same for the AssetManager and it would only deliver the texture of the first model folder that was added. +

+ +

+To have a valid jME3 object, the paths to textures and other assets belonging to the model have to be read with the correct, final path that can then be stored in the j3o object. The j3o object will use those paths when it is loaded with the AssetManager and it requires the AssetManager to deliver the assets on those paths, this is why the folder structure while converting has to be the same as when loading. +

+
+ documentation, + sdk, + tool, + asset, + scene +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/netbeans_code_completion.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/netbeans_code_completion.png new file mode 100644 index 000000000..f6bd6b60e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/netbeans_code_completion.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/p3wuv.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/p3wuv.png new file mode 100644 index 000000000..a728f5972 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/p3wuv.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/project_creation.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/project_creation.html new file mode 100644 index 000000000..fdc6ef15b --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/project_creation.html @@ -0,0 +1,280 @@ + +

jMonkeyEngine SDK: Creating Projects

+
+ +

+ +The jMonkeyEngine SDK makes it easy to get started with developing 3-D games based on the jMonkeyEngine. +

+ +
+ +

Creating a New jMonkeyEngine Project

+
+
    +
  1. Choose File > New Project from the main menu.
    +
  2. +
  3. In the New Project Wizard, select the template JME3 > Basic Game
    +
  4. +
  5. Click next to specify a project name, and the path where to store your new project.
    +
  6. +
  7. Click Finish. A skeleton application is created and opens in the Project Explorer.
    +
      +
    • This basic jme3 application is based on the SimpleApplication class to allow an easy start with jme3.
      +
    • +
    • You can click the run button to run it: You will see a jMonkey cube.
      +
    • +
    +
  8. +
+ +
+ +

Project Structure

+
+ +

+ + +

+ +

+Let's have a look at the abstract project structure in the Project Explorer (ctrl-1). + +

+ + +
+ +

Directory Structure

+
+ +

+ +Now let's have a look at the project's file structure in the File Explorer (ctrl-2). This explorer shows the physical directory structure on your hard drive. + +

+ + +

+ +Project Configuration +

+ +

+Right-Click the project to open the Project properties. +

+ +

+In the Run section, specify the main class of your project. (Pressing F6 runs this main class.) +In the Run section, you can optionally configure JVM options and command line parameters (in most cases set the-Xms VMOption [NUMBER] m for the memory usage. for example (-Xms500m). see ). +In the Application section, specify the game title (by default the game will be named BasicGame). +In the Application section, specify the vendor name (your name), a short description, your project's homepage, and a splash screen. + +

+ +
+ +

Project Configuration

+
+ +

+ +Right-Click the project to open the Project properties. +

+ + +
+ +

Clean, Build and Run Cycle

+
+ +

+ + +

+ +

+

Pressing F6 builds & runs the main class of the main project. If there are several classes, or several projects, you have to specify which one you want F6 to run. Right-click a project and choose Set As Main Project, then right-click the project again and choose Properties > Run and choose a Main Class.
+To build and run the main() of any file that is open in the editor, press Shift-F6 ! +

+ +

+ + +

+ +More than one project open? The toolbar buttons and the F-keys are bound to the main project, which is shown in bold in the Project Explorer. Right-click a project and select Set As Main Project to make it respond to the toolbar buttons and F-keys. +

+ +

+Worried About Proprietary Lock-in? You are never locked into the jMonkeyEngine SDK: At any time, you can change into your project directory on the command line, and clean, build, and run your project, using non-proprietary Apache Ant commands: +

+
ant clean; ant jar; ant run;
+ +
+ +

Development Process

+
+ + +
+ +

Adding external jar libraries

+
+ +

+ +You may want to use external Java libraries in your jME project, for example content generators or artificial intelligence implementations. +

+ +

+Add the library to the global library list: +

+ + +

+ +Add the library to a project: +

+ + +

+ +That's it, your project can now use the external library. If you also linked the javadoc and sources, the SDK will assist you with javadoc popups, code completion (ctrl-space) and source navigation (ctrl-click). +

+ +
+ +

Application Deployment

+
+ + +
+ +

Running Sample Projects

+
+ +

+ +The SDK contains Sample Code (read more). +

+ +

+Open the Source Packages node of the JmeTests project. +

+ +
+ documentation, + project, + deployment, + sdk +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_composer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_composer.html new file mode 100644 index 000000000..1eebc9c16 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_composer.html @@ -0,0 +1,208 @@ + +

jMonkeyEngine SDK: Scene Composer

+
+ +

+ +SceneComposer allows you to edit scenes stored in j3o files and add content or modify existing content. Please note that the Scene Composer and Explorer are a work in progress and will provide more powerful functions in the future. Also other plugins will allow creation of more specific game type scenes in jMonkeyEngine SDK. +

+ +

+Most buttons in the SceneComposer have tooltips that appear when you hover the mouse over the button for a short while. +

+ +
+ +

Mouse/Cursor Controls

+
+ + +

+ +In the SceneComposer toolbar are buttons to snap the camera to the cursor, snap the cursor to the selection and etc. +

+ +
+ +

Creating a scene file

+
+ +

+ +The jMonkeyEngine SDK stores the scene in a j3o file, this binary file contains the whole scenegraph including all settings for spatials, materials, physics, effects etc. Textures are not stored in the j3o file but as absolute locators to the textures. +

+ +

+To create a blank scene file do the following: +

+
    +
  1. Right click the "Scenes" folder in your Project Assets and select "New???Other"
    +
  2. +
  3. Select "Scene" to the left then select "Empty jME3 Scene" and press "Next"
    +
  4. +
  5. Enter a file name for your scene like "MyScene" and press "OK"
    +
  6. +
+ +
+ +

Loading the scene

+
+ +

+ + +

+ +

+To open a scene +

+
    +
  1. In the Project Explorer, right-click the *.j3o file of the scene
    +
  2. +
  3. Choose "Open in SceneComposer"
    +
  4. +
+ +

+ +Now the SceneComposer window opens at the bottom and displays the scene in the SceneViewer. The SceneExplorer displays the contained scene graph as a tree and when selecting a node, you can edit the properties of the corresponding scene graph object in the Properties window. +

+ +

+For now, you only see the cursor in the SceneViewer and a single node (the root node of the scene) in the SceneExplorer. +

+ +
+ +

Adding light to the scene

+
+
    +
  1. Select the root node in the SceneExplorer
    +
  2. +
  3. Select "Directional Light" in the SceneComposer window
    +
  4. +
  5. Press the "+" button in the SceneComposer window
    +
  6. +
+ +

+ +A directional light has been added to your scene, you can see it in the SceneExplorer. +

+ +
+ +

Adding effects etc. to the scene

+
+ +

+ +You can add a variety of special objects with the SceneComposer, including lights, effects, audio etc. + +

+
    +
  1. Select root Node in the SceneExplorer
    +
  2. +
  3. Select the object type in the list displayed in the SceneComposer window
    +
  4. +
  5. Press the "+ cursor" button in the SceneComposer window
    +
  6. +
+ +
+ +

Adding Models to the scene

+
+ +

+ + +

+ +

+You can directly import 3d models to your scene so that they will be part of your scene file. To be able to import for example an OgreXML file, first export it from your 3D editor to a separate folder in the assets folder of your project (e.g. assets/Models/MyModel/). + +

+
    +
  1. Place the SceneComposer cursor where you want the model to be
    +
  2. +
  3. Select the parent Node for the model in the SceneExplorer
    +
  4. +
  5. In the Project Explorer right-click the model file you want to import
    +
  6. +
  7. Choose "Add to SceneComposer"
    +
  8. +
+ +

+ +Note that when importing a model the texture paths are stored absolute, so the folder you import the model from will later only be a textures folder because the original model file is not included in the release. +

+ +

+Also note that when adding models this way, changes in the original model file will not be reflected in the scene file as its a complete copy of the original file. If you change the original model, delete the models node from the scene and import it again. +

+ +
+ +

Linking Models to the scene

+
+ +

+ +You can also link models/objects into your scene, this way they are reloaded dynamically from the other/original file. + +

+
    +
  1. Place the SceneComposer cursor where you want the model to be
    +
  2. +
  3. Select the parent Node for the model in the SceneExplorer
    +
  4. +
  5. In the Project Explorer right-click the model file you want to link
    +
  6. +
  7. Choose "Link in SceneComposer"
    +
  8. +
+ +

+ +Note that when linking objects this way, you cannot edit them as part of the scene. To change the model you have to change the original j3o file. +

+ +

+Also note that although it its possible to directly link external model files (OgreXML, OBJ etc.), this is not recommended. Convert the original file to a j3o file by right-clicking it and selecting "Convert to jME Binary" before linking it. This is required because the original model files are not included in the release version of the application. +

+ +
+ +

Saving the Scene

+
+
    +
  1. When a scene has been changed, press the "save" button in the main toolbar or press [Ctrl-S] / [Apple-S] to save it.
    +
  2. +
+
+ documentation, + sdk, + scene, + node, + asset, + light, + effect +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html new file mode 100644 index 000000000..8a8e8c3a5 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html @@ -0,0 +1,79 @@ + +

jMonkeyEngine SDK: Scene Explorer

+
+ +

+ +

+ +
+ +

About the SceneExplorer window

+
+ +

+The SceneExplorer gives you a structural overview of the currently edited scene and is active among all plugins +

+ +

+Most plugins will deliver their own UI elements to modify the scene so the SceneExplorer is more of a global tool. The simple SceneComposer however heavily relies on its functions as other plugins might too in the future. +

+ +
+ +

Using the SceneExplorer

+
+ +

+ +The SceneExplorer displays Nodes in a tree that represents the tree of Spatials in your scene. Spatial controls, lights and geometry meshes are also displayed in the tree. +

+ +

+You open the SceneExplorer by viewing a model (j3o file or other) in the jMonkeyEngine SDK. +

+ +
+ +

Editing Objects in the scene

+
+
    +
  1. Select a node in the SceneExplorer window (Open via Window???SceneExplorer if not open)
    +
  2. +
  3. Edit the node in the Properties window (Open via Window???Properties if not open)
    +
  4. +
  5. You can rename a Spatial by right clicking it or by slowly double-clicking the node
    +
  6. +
+ +
+ +

Reorganizing Objects in the scene

+
+
    +
  1. You can cut, copy and paste Nodes in the SceneExplorer with the normal keyboard commands or the right-click menu of the Nodes
    +
  2. +
  3. You can move single object within the SceneExplorer tree by dragging&dropping them
    +
  4. +
+ +
+ +

Adding Objects to the scene

+
+ +

+ +Right-click a Spatial or Node in the SceneExplorer to add other Spatials like ParticleEmitters or Lights, you can also add UserData to a Spatial that can be read during runtime. +

+
+ documentation, + sdk, + tool, + scene, + node +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addnormaltexture.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addnormaltexture.png new file mode 100644 index 000000000..bfae2898e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addnormaltexture.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addtexture.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addtexture.png new file mode 100644 index 000000000..eba08c480 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-addtexture.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-editdiffuse.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-editdiffuse.png new file mode 100644 index 000000000..14e10659f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-editdiffuse.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-raise-lower.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-raise-lower.png new file mode 100644 index 000000000..8393ba3f8 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-raise-lower.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-texturescale.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-texturescale.png new file mode 100644 index 000000000..5a7cf936f Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-texturescale.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-addterrain.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-addterrain.png new file mode 100644 index 000000000..4294a7110 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-addterrain.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-selectnode.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-selectnode.png new file mode 100644 index 000000000..dc38fba8b Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/sdk-terrain-tut-selectnode.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/terrain_editor.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/terrain_editor.html new file mode 100644 index 000000000..6dce226be --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/terrain_editor.html @@ -0,0 +1,230 @@ + +

jMonkeyEngine SDK: Terrain Editor

+
+ +

+The terrain editor lets you create, modify, and paint terrain. + +

+ +
+ +

Controls

+
+ +

+Terrain controls are the same as the Scene Composer, you rotate the camera with the left mouse button and pan the camera with the right mouse button. Until you select one of the terrain tools in the toolbar, then the controls change for that tool. Then left mouse button will use that tool: painting, raising/lowering terrain, etc. The right mouse button might do something, depending on the tool. +

+ +
+ +

Creating Terrain

+
+ +

+To create terrain, first select a node (probably your root node) in your scene.
+ +
+ +Then click the add terrain button.
+ +
+ +This will pop up the Create Terrain wizard that will walk you through the steps for creating terrain. Make sure you decide now how large you want your terrain to be and how detailed you want the textures to be as you cannot change it later on! +

+ +

+In order to see the terrain, you will need to add light to your scene. To do this, right-click the root node in the SceneExplorer window and select "Add Light???Directional Light" +

+ +
+ +

Step 1: Terrain Size

+
+ +

+Here you determine the size of your terrain, the total size and the patch size. You should probably leave the patch size alone. But the total size should be defined; this is how big your terrain is. +

+ +
+ +

Step 2: Heightmap

+
+ +

+Here you can select a heightmap generation technique to give you a default/random look of the terrain. You can also select a heightmap image to pre-define how your terrain looks. +By default, it will give you a flat terrain. +

+ +
+ +

Step 3: Alpha Image Detail

+
+ +

+This step determines how large the alpha blend images are for your terrain. The smaller the alpha image, the less detailed you can paint the terrain. Play around with this to see how big you need it to be. Remember that terrain does not have to be that detailed, and is often covered by vegetation, rocks, and other items. So having a really detailed texture is not always necessary. +

+ +
+ +

Modifying Terrain

+
+ +

+Right now there are two terrain modification tools: raise and lower terrain.
+ +
+ +There are two sliders that affect how these tools operate: +

+ + +

+Once a tool is selected, you will see the tool marker (now an ugly wire sphere) on the map where your mouse is. Click and drag on the terrain to see the tool change the height of the terrain. +

+ +
+ +

Painting Terrain

+
+ +

+ +Your terrain comes with one diffuse default texture, and you can have up to 12 total diffuse textures. It also allows you to add normal maps to each texture layer. There can be a maximum of 13 textures, including Diffuse and Normal (3 textures are taken up by alpha maps). +

+ +

+All of the textures can be controlled in the Texture Layer table by clicking on the textures. +There are two sliders that affect how the paint tool operates: +

+ + +
+ +

Adding a new texture layer

+
+ +

+ +Adds a new texture layer to the terrain. The will be drawn over top of the other texture layers listed above it in the list. +

+ +

+ +

+ +
+ +

Changing the diffuse texture

+
+ +

+ +Click on the diffuse texture image in the texture table. This will pop up a window with the available textures in your assets directory. +

+ +

+ +

+ +
+ +

Adding a normal map to the texture layer

+
+ +

+When you add a texture layer, by default it does not add a normal map for you. To add one, click on the button next to the diffuse texture for that texture layer. This will pop up the texture browser. Select a texture and hit Ok, and you are done.
+ + +

+ +
+ +

Removing a normal map from the texture layer

+
+ +

+To remove a normal map from the texture layer, hit the normal map button for that texture layer again, and deselect the texture. Then hit Ok and the texture should be removed. +

+ +
+ +

Changing the texture scale

+
+ +

+The field in the table to the right of the diffuse and normal textures for your texture layer is the scale. Changing that value changes the scale of the texture.
+ +
+ +You will notice that the scale changes when you switch between Tri-Planar and normal texture mapping. Tri-planar mapping does not use the texture coordinates of the geometry, but real world coordinates. And because of this, in order for the texture to look the same when you switch between the two texture mapping methods, the terrain editor will automatically convert the scales for you. +Essentially if your scale in normal texture coordinates is 16, then for tri-planar gets converted like this: 1/terrainSize/16 +

+ +
+ +

Tri-planar texture mapping

+
+ +

+ +Tri-planar texture mapping is recommended if you have lots of near-vertical terrain. With normal texture mapping the textures can look stretched because it is rendered on the one plane: X-Z. Tri-planar mapping renders the textures on three planes: X-Z, X-Y, Z-Y; and blends them together based on what plane the normal of the triangle is facing most on. +This makes the terrain look much better, but it does have a performance hit! +Here is an article on tri-planar mapping: +

+ +
+ +

Total texture count

+
+ +

+Terrain will support a maximum of 12 diffuse texture. And a combined total of 13 diffuse and normal maps. +Most video cards are limited to 16 texture units (textures), and 3 are used behind the scenes of the terrain material for alpha blending of the textures, so you are left with a maximum of 13 textures. +

+ +
+ +

Generating Terrain Entropies for LOD

+
+ +

+If you are using the recommended PerspectiveLodCalculator for calculating LOD levels of the terrain, then you will want to pre-generate the entropy levels for the terrain. This is a slow process. If they are not pre-generated, the LOD control will generate them for you, but this will lag the user when they load the scene, and the terrain will flicker. +Use the 'Generate Entropies' button to pre-generate the entropies for the terrain, they will be saved with it. +Note that whenever you modify the height of the terrain, you should re-generate the entropies. Of course, don't do this every time, but maybe just before you are ready to send the map out for testing. +

+ +
+ +

Loading Terrain Into Your Game

+
+ +

+When you want to load a scene into your game that has terrain, you have to set the camera on the LOD control in order for it to work correctly: + +

+
TerrainLodControl lodControl = ((Node)terrain).getControl(TerrainLodControl.class);
+            if (lodControl != null)
+                lodControl.setCamera(getCamera());
+
+ documentation, + sdk, + tool, + terrain, + asset, + texture +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html new file mode 100644 index 000000000..6577d7378 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html @@ -0,0 +1,136 @@ + +

Troubleshooting jMonkeyEngine3 SDK

+
+ +
+ +

Specifying the JDK location

+
+ +

+ +You should install the JDK (the one from Oracle, not OpenJDK) first, and then the jMonkey SDK. If jMonkeyEngine SDK cannot find a valid JDK although you have it installed, then you have to specify the location manually. + +

+
    +
  1. Go to your jMonkeyEngine SDK installation directory.
    +Mac users right-click jMonkeyApplication.app (which actually is a directory) in the Finder and select "Show package contents".
    +
  2. +
  3. Navigate to the etc directory.
    +Mac users navigate to Contents/Resources/jmonkeyplatform/etc/.
    +
  4. +
  5. Open the file jmonkeyplatform.conf in a text editor.
    +
  6. +
  7. Uncomment the following line and enter the path to the JDK:
    jdkhome="/path/to/jdk"
    +
    +
  8. +
+ +
+ +

Graphics Card Driver

+
+ +

+On Windows and Linux make sure you have the latest driver installed. Make sure its the one supplied by the card manufacturer and not just the OS-default one. +

+ +
+ +

Stability / Graphics issues

+
+ +

+On some Linux and Windows systems, the SDK might perform unstable and quit with native VM crashes or "x errors". There are a few things one can try to remedy those issues. +

+ +
+ +

Heavyweight Canvas

+
+ +

+First of all theres the new ???OpenGL??? settings page in the SDK global settings where you can enable the "heavyweight" canvas, which solved some issues for some people. The settings panel can be found under Tools???Options on Windows and Linux and in the main menu (or by pressing Apple-Comma) for MacOSX. +

+ +

+If you cannot start the SDK, edit the file config/Preferences/com/jme3/gde/core.properties in the SDK settings folder (see above). If it doesn't exist, create the file including all folders. Add the line use_lwjgl_canvas=true. To try OpenGL1 compatibility mode (works for both canvas settings) add use_opengl_1=true. +

+ +
+ +

Look and Feel

+
+ +

+The OS-built-in look and feel might cause issues, you can change the LAF by using the appropriate command line switch (or add it to the [app folder]/etc/jmonkeyplatform.conf file, without the "- -" prefix). + +

+
--laf javax.swing.plaf.metal.MetalLookAndFeel
+ +
+ +

Compiz

+
+ +

+Compiz on Linux might cause issues, if you set its rendering quality to "medium" these should go away. +* Appearance???Set Special effects to ??? ???Medium??? +

+ +
+ +

Updating problems

+
+ +

+If you have problems updating the SDK, try deleting all files from jmonkeyplatform/update/download and/or [settings folder]/update/download depending on your system (see below for the settings folder location). +

+ +
+ +

Preferences and Settings

+
+ +

+To completely remove and/or reinstall the SDK it is vital that the settings folder is deleted too. The location can be seen through the "about" menu and is as following for the different OS's: +

+ + +
+ +

Getting error messages and reporting issues

+
+ +

+When an exception happens in the SDK, a small warning sign appears in the lower right corner of the main window. Double-click it to open a window that allows you to see the exception stack trace. When posting about issues in the forum, always post the stack trace along with a description of what happens and how it can be reproduced. +

+ +
+ +

Known Issues

+
+ +

+For a list of known issues and possible workarounds see the following link: + +

+
+ documentation, + tool, + sdk, + faq +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/use_own_jme.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/use_own_jme.html new file mode 100644 index 000000000..edde1e138 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/use_own_jme.html @@ -0,0 +1,44 @@ + +

How to integrate your own jME3 compile in jMonkeyEngine SDK projects

+
+
    +
  1. Download jme3 project from svn
    +
  2. +
  3. Make your changes
    +
  4. +
  5. Compile jme3 project
    +
  6. +
  7. Go to Tools ??? Libraries
    +
  8. +
  9. Press "New Library"
    +
  10. +
  11. Name it "jme3-modified"
    +
  12. +
  13. Press "Add Jar/Folder"
    +
  14. +
  15. Select all JAR files from the dist dir of the compiled jme3 version
    +
  16. +
  17. Add the src folder of the jme3 project in the "sources" tab
    +
  18. +
  19. Optionally javadoc in the "javadoc" tab
    +
  20. +
  21. Press "OK"
    +
  22. +
  23. Right-click your project and select "Properties"
    +
  24. +
  25. Select "Libraries" to the left
    +
  26. +
  27. Remove the "jme3" library
    +
  28. +
  29. Press "Add Library" and select the "jme3-modified" library
    +
  30. +
+
+ documentation, + sdk, + project, + builds +
+ +
+

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/vehicle_creator.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/vehicle_creator.html new file mode 100644 index 000000000..a72bf0d72 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/vehicle_creator.html @@ -0,0 +1,75 @@ + +

jMonkeyEngine SDK: Vehicle Creator

+
+ +

+ +Best results when car is facing in z direction (towards you). +

+ +
+ +

Usage

+
+
    +
  1. Select a j3o that contains a vehicle model and press the vehicle button (or right-click ??? edit vehicle)
    +
  2. +
  3. The VehicleCreator automatically adds a PhysicsVehicleControl to the rootNode of the model if there is none
    +
  4. +
  5. Select the Geometry or Node that contains the chassis in the SceneExplorer and press "create hull shape from selected"
    +
  6. +
  7. Select a Geometry that contains a wheel and press "make selected spatial wheel", select the "front wheel" checkboxes for front wheels
    +
  8. +
  9. Do so for all wheels
    +
  10. +
+ +

+ +New wheels will get the current suspension settings, you can edit single wheels via the SceneExplorer (update VehicleControl if wheels dont show up) or apply settings created with the settings generator to wheel groups. +

+ +

+Press the "test vehicle" button to drive the vehicle, use WASD to control, Enter to reset. +

+ +
+ +

Known Issues

+
+ +

+Don't save while testing the vehicle, you will save the location and acceleration info in the j3o. +

+ +
+ +

Code Sample

+
+ +

+ +Code Example to load vehicle: + +

+
//load vehicle and access VehicleControl
+Spatial car=assetManager.loadModel("Models/MyCar.j3o");
+VehicleControl control=car.getControl(VehicleControl.class);
+rootNode.attachChild(car);
+physicsSpace.add(control);
+??
+//then use the control to control the vehicle:
+control.setPhysicsLocation(new Vector3f(10,2,10));
+control.accelerate(100);
+
+ documentation, + sdk, + tool, + asset, + editor, + physics +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html new file mode 100644 index 000000000..5212944da --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html @@ -0,0 +1,225 @@ + +

jMonkeyEngine SDK: Version Control

+
+ +

+ +Whether you work in a development team or alone: File versioning is a handy method to keep your code consistent, compare files line-by-line, and even roll back unwanted changes. This documentation shows you how to make the most of the SDK's integrated version control features for Subversion, Mercurial, and CVS. +

+ +

+For every team member, the file versioning workflow is as follows: +

+
    +
  1. Once: Download a working copy of the project from the repository ("checkout").
    +
  2. +
  3. Regularly: Upload your own changes to the repository ("commit").
    +
  4. +
  5. Regularly: Download updates by others from the repository ("update").
    +
  6. +
+ +

+ +Note: Since the jMonkeyEngine SDK is based on the NetBeans Platform framework, you can learn about certain jMonkeyEngine SDK features by reading the corresponding NetBeans IDE tutorials (in the "see also links"). +

+ +
+ +

Version Control Systems

+
+ +

+ +The jMonkeyEngine SDK supports various Version Control Systems such as Subversion, Mercurial, and CVS. No matter which of them you use, they all share a common user interface. +

+ +

+You can use file versioning alone or in a team. The advantages are that you can keep track of changes (who did it, when, and why), you can compare versions line-by-line, you can revert changes to files and lines, merge multiple changes to one file, and you can even undelete files. +

+ +
+ +

Creating a Repository (Upload)

+
+ +

+ +Requirements: +

+ + +

+ +Now you create a repository to store your project's files. + +

+
    +
  1. In the jMonkeyEngine SDK, right-click the project in the Projects window and choose Versioning > Import Into CVS/Subversion Repository (or initialize Mercurial Project, respectively).
    +
      +
    • Tip: If you haven't evaluated yet which system to choose, start with Subversion for now.
      +
    • +
    +
  2. +
  3. Go through the wizard and fill in the fields to set up the repository.
    +
  4. +
+ +
+ +

Checking Out a Repository (Download)

+
+ +

+ +You and your team mates check out (download) the repository to their individual workstations. + +

+
    +
  1. Go to the Team menu and choose Subversion > Checkout (or CVS/Mercurial>Checkout respectively)
    +
  2. +
  3. Fill in your repo data into the wizard and click Finish.
    +
      +
    • A typical repository URL looks like this example:
      +
    • +
    • If you want to be able to submit changes, you must have a username and password to this repository. Otherwise leave these fields blank.
      +
    • +
    +
  4. +
  5. The repository is downloaded and stored in the location you chose.
    +
  6. +
  7. Use the File > Open Project menu to open the checkout as project and start working.
    +
      +
    • If the checkout is not recognized you need to choose File > New Project from Existing Sources
      +
    • +
    +
  8. +
+ +

+ +Of course you can also check out existing repositories and access code from other open-source projects (e.g. SourceForge, GoogleCode, dev.java.net). +

+ +
+ +

Updating and Committing Changes (Send and Receive)

+
+ +

+ +Receiving the latest changes from the team's repository is referred to as updating. Sending your changes to the team's repository is refered to as commiting. + +

+
    +
  1. Before making changes, right-click the project and select Subversion > Update to make sure you have the latest revision.
    +
      +
    • Get in the habit of updating regularly, always before you edit a version controlled file. It will spare you much grief.
      +
    • +
    +
  2. +
  3. After making changes to the project, make certain your change did not break anything.
    +
      +
    1. Update, build, run, test.
      +
    2. +
    3. Look at the red/green/blue marks in the editor to review what you have deleted/added/changed. Click the marks to review all differences in a file.
      +
    4. +
    5. Choose Subversion > Show Changes to see all files that were recently changed ??? by you and other team members.
      +
    6. +
    7. Update again in case your team mates made changes while you were reviewing.
      +
    8. +
    +
  4. +
  5. If there are no conflicts and you want to commit your changes, choose Subversion > Commit from the menu.
    +
  6. +
  7. Write a commit message describing what you changed.
    +
      +
    • Remember, you are writing "a message to your future self". Never write redundant stuff like "I changed something".
      +
    • +
    +
  8. +
+ +
+ +

Comparing and Reverting Changes

+
+ + +
+ +

No Version Control? Local History!

+
+ +

+ +If you do not use any version control, you can still track changes in projects to a certain degree. + +

+ + +

+See also: + +

+ +
+ documentation, + sdk, + editor, + tool +
+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/3_0rc3.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/3_0rc3.html new file mode 100644 index 000000000..d8de53b4c --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/3_0rc3.html @@ -0,0 +1,56 @@ + +

Welcome to the jMonkeyEngine SDK

+
+ +

+You are running the latest version of the SDK Application (jMonkeyEngine SDK 3.0RC3), see below how you can check for incremental updates. +

+ +

+This place will be updated with the latest news about your SDK. + +

+ +
+ +

Getting Started

+
+ +

+Press the "New Project" button to create a new Project. Press the "New File" button to create new java files, materials, scenes, fonts and other files. +

+ +

+ +

+ +
+ +

Tutorials / Manual

+
+ +

+By pressing "F1" you can open the manual which contains up to date tutorials, documentation and more to help you get started. You can search the manual contents via the search field up right. +

+ +

+ +

+ +
+ +

Updates

+
+ +

+You can check for incremental updates to the application via the Help menu: +

+ +

+ + +

+ +
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/help_update.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/help_update.png new file mode 100644 index 000000000..f6699f23e Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/help_update.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/new_project.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/new_project.png new file mode 100644 index 000000000..a5b3201f7 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/new_project.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/search_field.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/search_field.png new file mode 100644 index 000000000..95a3e692d Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/welcome/search_field.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-download-index.properties b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-download-index.properties new file mode 100644 index 000000000..127456246 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-download-index.properties @@ -0,0 +1,133 @@ +javahelp_path = com/jme3/gde/docs +wiki_url = http://jmonkeyengine.org/wiki/doku.php/ +wiki_pages = sdk:project_creation,\ +sdk:3ds_to_blender_to_jmp,\ +sdk:application_deployment,\ +sdk:asset_packs,\ +sdk:blender,\ +sdk:code_editor,\ +sdk:debugging_profiling_testing,\ +sdk:default_build_script,\ +sdk:filters,\ +sdk:log_files,\ +sdk:material_editing,\ +sdk:model_loader_and_viewer,\ +sdk:project_creation,\ +sdk:scene_composer,\ +sdk:scene_explorer,\ +sdk:terrain_editor,\ +sdk:troubleshooting,\ +sdk:use_own_jme,\ +sdk:vehicle_creator,\ +sdk:version_control,\ +sdk:development,\ +sdk:development:extension_library,\ +sdk:development:general,\ +sdk:development:model_loader,\ +sdk:development:projects_assets,\ +sdk:development:scene,\ +sdk:development:sceneexplorer,\ +sdk:development:setup,\ +sdk:welcome:3_0rc3,\ +jme3:android,\ +jme3:ios,\ +jme3:build_jme3_sources_with_netbeans,\ +jme3:build_from_sources,\ +jme3:simpleapplication_from_the_commandline,\ +jme3:faq,\ +jme3:math,\ +jme3:terminology,\ +jme3:the_scene_graph,\ +jme3:webstart,\ +jme3:external:blender,\ +jme3:external:3dsmax,\ +jme3:intermediate:appsettings,\ +jme3:intermediate:best_practices,\ +jme3:intermediate:file_types,\ +jme3:intermediate:how_to_use_materials,\ +jme3:intermediate:math,\ +jme3:intermediate:multi-media_asset_pipeline,\ +jme3:intermediate:optimization,\ +jme3:intermediate:simpleapplication,\ +jme3:advanced:3d_models,\ +jme3:advanced:animation,\ +jme3:advanced:application_states,\ +jme3:advanced:appstatesdemo,\ +jme3:advanced:asset_manager,\ +jme3:advanced:audio,\ +jme3:advanced:audio_environment_presets,\ +jme3:advanced:bloom_and_glow,\ +jme3:advanced:bullet_multithreading,\ +jme3:advanced:camera,\ +jme3:advanced:capture_audio_video_to_a_file,\ +jme3:advanced:cinematics,\ +jme3:advanced:collision_and_intersection,\ +jme3:advanced:combo_moves,\ +jme3:advanced:custom_controls,\ +jme3:advanced:custom_meshes,\ +jme3:advanced:debugging,\ +jme3:advanced:effects_overview,\ +jme3:advanced:endless_terraingrid,\ +jme3:advanced:fade,\ +jme3:advanced:headless_server,\ +jme3:advanced:hinges_and_joints,\ +jme3:advanced:hud,\ +jme3:advanced:input_handling,\ +jme3:advanced:j3m_material_files,\ +jme3:advanced:jme3_shaders,\ +jme3:advanced:light_and_shadow,\ +jme3:advanced:loading_screen,\ +jme3:advanced:localization,\ +jme3:advanced:logging,\ +jme3:advanced:making_the_camera_follow_a_character,\ +jme3:advanced:material_definitions,\ +jme3:advanced:materials_overview,\ +jme3:advanced:mesh,\ +jme3:advanced:monkey_zone,\ +jme3:advanced:motionpath,\ +jme3:advanced:mouse_picking,\ +jme3:advanced:multiple_camera_views,\ +jme3:advanced:multithreading,\ +jme3:advanced:networking,\ +jme3:advanced:nifty_gui,\ +jme3:advanced:nifty_gui_java_interaction,\ +jme3:advanced:nifty_gui_java_layout,\ +jme3:advanced:nifty_gui_overlay,\ +jme3:advanced:nifty_gui_popup_menu,\ +jme3:advanced:nifty_gui_projection,\ +jme3:advanced:nifty_gui_scenarios,\ +jme3:advanced:nifty_gui_xml_layout,\ +jme3:advanced:ogrecompatibility,\ +jme3:advanced:open_game_finder,\ +jme3:advanced:particle_emitters,\ +jme3:advanced:physics,\ +jme3:advanced:physics_listeners,\ +jme3:advanced:post-processor_water,\ +jme3:advanced:ragdoll,\ +jme3:advanced:read_graphic_card_capabilites,\ +jme3:advanced:remote-controlling_the_camera,\ +jme3:advanced:save_and_load,\ +jme3:advanced:screenshots,\ +jme3:advanced:shape,\ +jme3:advanced:sky,\ +jme3:advanced:spatial,\ +jme3:advanced:swing_canvas,\ +jme3:advanced:terrain,\ +jme3:advanced:terrain_collision,\ +jme3:advanced:update_loop,\ +jme3:advanced:vehicles,\ +jme3:advanced:walking_character,\ +jme3:advanced:water,\ +jme3:beginner:hello_simpleapplication,\ +jme3:beginner:hello_node,\ +jme3:beginner:hello_asset,\ +jme3:beginner:hello_main_event_loop,\ +jme3:beginner:hello_input_system,\ +jme3:beginner:hello_material,\ +jme3:beginner:hello_animation,\ +jme3:beginner:hello_picking,\ +jme3:beginner:hello_collision,\ +jme3:beginner:hello_terrain,\ +jme3:beginner:hello_audio,\ +jme3:beginner:hello_effects,\ +jme3:beginner:hello_physics \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml new file mode 100644 index 000000000..f4fb29835 --- /dev/null +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform002-300x218.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform002-300x218.jpg new file mode 100644 index 000000000..5fa59cf08 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform002-300x218.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform003-300x117.jpg b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform003-300x117.jpg new file mode 100644 index 000000000..827cd4909 Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2010/11/jmonkeyplatform003-300x117.jpg differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2011/07/terrain-blogpost-july.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2011/07/terrain-blogpost-july.png new file mode 100644 index 000000000..d4064531c Binary files /dev/null and b/sdk/jme3-documentation/src/com/jme3/gde/docs/wp-uploads/2011/07/terrain-blogpost-july.png differ