From 752ef87720a928dbfde603b3850efec2d7c45537 Mon Sep 17 00:00:00 2001 From: "nor..67" Date: Mon, 16 Jan 2012 01:16:17 +0000 Subject: [PATCH] SDK: - update help sets - add missing help pages to wiki_help.properties git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9075 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../core/docs/jme3/advanced/3d_models.html | 85 ++- .../core/docs/jme3/advanced/animation.html | 354 +++++++++- .../docs/jme3/advanced/appstatesdemo.html | 381 ++++++++++ .../gde/core/docs/jme3/advanced/audio.html | 84 ++- .../advanced/audio_environment_presets.html | 147 +++- .../docs/jme3/advanced/bloom_and_glow.html | 250 ++++++- .../jme3/advanced/bullet_multithreading.html | 47 +- .../docs/jme3/advanced/bump-mapped-sphere.png | Bin 0 -> 51941 bytes .../gde/core/docs/jme3/advanced/camera.html | 12 +- .../capture_audio_video_to_a_file.html | 652 ++++++++++++++++++ .../core/docs/jme3/advanced/cinematics.html | 384 ++++++++++- .../advanced/collision_and_intersection.html | 2 +- .../core/docs/jme3/advanced/combo_moves.html | 40 +- .../core/docs/jme3/advanced/drop-shadows.png | Bin 0 -> 106544 bytes .../docs/jme3/advanced/effects_overview.html | 201 +++++- .../jme3/advanced/elephant-pointlights.png | Bin 0 -> 53826 bytes .../gde/core/docs/jme3/advanced/fade.html | 49 ++ .../docs/jme3/advanced/hinges_and_joints.html | 96 ++- .../jme3/advanced/house-directionallight.png | Bin 0 -> 98522 bytes .../jme3/advanced/j3m_material_files.html | 36 +- .../core/docs/jme3/advanced/jme3_shaders.html | 26 +- .../core/docs/jme3/advanced/light-sources.png | Bin 15087 -> 31348 bytes .../docs/jme3/advanced/light_and_shadow.html | 289 ++++++-- .../docs/jme3/advanced/loading_screen.html | 75 +- .../core/docs/jme3/advanced/localization.html | 134 +++- .../gde/core/docs/jme3/advanced/logging.html | 65 +- .../making_the_camera_follow_a_character.html | 77 ++- .../jme3/advanced/materials_overview.html | 353 +++++++++- .../core/docs/jme3/advanced/motionpath.html | 113 ++- .../docs/jme3/advanced/mouse_picking.html | 43 +- .../jme3/advanced/multiple_camera_views.html | 175 ++++- .../docs/jme3/advanced/multithreading.html | 146 +++- .../core/docs/jme3/advanced/networking.html | 107 ++- .../core/docs/jme3/advanced/nifty_gui.html | 33 +- .../advanced/nifty_gui_java_interaction.html | 74 +- .../docs/jme3/advanced/nifty_gui_overlay.html | 48 +- .../jme3/advanced/nifty_gui_popup_menu.html | 92 +++ .../jme3/advanced/nifty_gui_projection.html | 84 ++- .../jme3/advanced/nifty_gui_scenarios.html | 345 +++++++++ .../jme3/advanced/nifty_gui_xml_layout.html | 279 +++++++- .../docs/jme3/advanced/open_game_finder.html | 195 +++++- .../docs/jme3/advanced/particle_emitters.html | 82 ++- .../gde/core/docs/jme3/advanced/physics.html | 163 ++++- .../docs/jme3/advanced/physics_listeners.html | 73 +- .../remote-controlling_the_camera.html | 46 +- .../docs/jme3/advanced/save_and_load.html | 45 +- .../core/docs/jme3/advanced/shading-ani.gif | Bin 0 -> 368820 bytes .../jme3/advanced/shading-textured-ani.gif | Bin 0 -> 241568 bytes .../gde/core/docs/jme3/advanced/shadow.png | Bin 30521 -> 103050 bytes .../jme3/gde/core/docs/jme3/advanced/sky.html | 94 ++- .../gde/core/docs/jme3/advanced/spatial.html | 37 +- .../gde/core/docs/jme3/advanced/spotlight.png | Bin 0 -> 79498 bytes .../advanced/teapot-phong-illuminated.png | Bin 0 -> 24998 bytes .../advanced/terrain-lod-high-medium-low.png | Bin 0 -> 99459 bytes .../gde/core/docs/jme3/advanced/terrain.html | 139 +++- .../docs/jme3/advanced/terrain_collision.html | 15 +- .../gde/core/docs/jme3/advanced/vehicles.html | 111 ++- .../docs/jme3/advanced/walking_character.html | 248 ++++++- .../gde/core/docs/jme3/advanced/water.html | 35 +- .../docs/jme3/beginner/hello_animation.html | 161 ++++- .../core/docs/jme3/beginner/hello_asset.html | 77 ++- .../core/docs/jme3/beginner/hello_audio.html | 18 +- .../docs/jme3/beginner/hello_collision.html | 99 ++- .../docs/jme3/beginner/hello_effects.html | 153 +++- .../jme3/beginner/hello_input_system.html | 89 ++- .../jme3/beginner/hello_main_event_loop.html | 20 +- .../docs/jme3/beginner/hello_material.html | 20 +- .../core/docs/jme3/beginner/hello_node.html | 90 ++- .../core/docs/jme3/build_from_sources.html | 56 +- .../com/jme3/gde/core/docs/jme3/faq.html | 457 +++++++++++- .../docs/jme3/intermediate/appsettings.html | 100 ++- .../jme3/intermediate/best_practices.html | 46 +- .../docs/jme3/intermediate/file_types.html | 78 ++- .../jme3/intermediate/headlessserver.html | 367 +++++++++- .../docs/jme3/intermediate/optimization.html | 45 +- .../jme3/intermediate/simpleapplication.html | 42 +- .../com/jme3/gde/core/docs/jme3/math.html | 147 +++- ...impleapplication_from_the_commandline.html | 89 ++- .../jme3/gde/core/docs/jme3/terminology.html | 116 +++- .../gde/core/docs/jme3/the_scene_graph.html | 98 ++- .../com/jme3/gde/core/docs/jme3/webstart.html | 99 ++- .../core/docs/sdk/3ds_to_blender_to_jmp.html | 69 ++ .../core/docs/sdk/application_deployment.html | 46 +- .../jme3/gde/core/docs/sdk/asset_packs.html | 25 +- .../com/jme3/gde/core/docs/sdk/blender.html | 42 +- .../jme3/gde/core/docs/sdk/code_editor.html | 92 ++- .../docs/sdk/debugging_profiling_testing.html | 38 +- .../jme3/gde/core/docs/sdk/development.html | 60 +- .../docs/sdk/development/model_loader.html | 30 +- .../docs/sdk/development/projects_assets.html | 43 +- .../gde/core/docs/sdk/development/scene.html | 96 ++- .../docs/sdk/development/sceneexplorer.html | 85 ++- .../com/jme3/gde/core/docs/sdk/log_files.html | 43 ++ .../gde/core/docs/sdk/material_editing.html | 35 +- .../gde/core/docs/sdk/project_creation.html | 47 +- .../gde/core/docs/sdk/terrain_editor.html | 34 +- .../gde/core/docs/sdk/troubleshooting.html | 31 +- .../gde/core/docs/sdk/version_control.html | 126 +++- .../com/jme3/gde/core/docs/wiki-map.xml | 2 +- sdk/jme3-core/javahelp/wiki_help.properties | 7 + 100 files changed, 9733 insertions(+), 346 deletions(-) create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/appstatesdemo.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bump-mapped-sphere.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/capture_audio_video_to_a_file.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/drop-shadows.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/elephant-pointlights.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/fade.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/house-directionallight.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/nifty_gui_popup_menu.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/nifty_gui_scenarios.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/shading-ani.gif create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/shading-textured-ani.gif create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/spotlight.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/teapot-phong-illuminated.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/terrain-lod-high-medium-low.png create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/sdk/3ds_to_blender_to_jmp.html create mode 100644 sdk/jme3-core/javahelp/com/jme3/gde/core/docs/sdk/log_files.html diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/3d_models.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/3d_models.html index 141a96512..7057315e0 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/3d_models.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/3d_models.html @@ -1,3 +1,84 @@ -

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. Save the files into a subdirectory of your jME3 project's assets directory.
  3. 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" );
  4. (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.

Creating Models and Scenes

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

Tip: Consider creating for more complex models, it looks more professional.

3D mesh 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. 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.
  3. In the Export Meshes field: Select a target subdirectory of your assets/Models/ directory. E.g. assets/Models/something/.
  4. Activate the following exporter settings:
    • Copy Textures: YES
    • Rendering Materials: YES
    • Flip Axis: YES
    • Require Materials: YES
    • Skeleton name follows mesh: YES
  5. Click export.

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.

+ +

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: Consider creating for more complex models, it looks more professional. +

+ +

+3D mesh 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-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/animation.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/animation.html index 53498fc03..1928b05cb 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/animation.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/animation.html @@ -1,9 +1,286 @@ -

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. Animated models must be created in an external mesh editor (for example, Blender).

What is required for the model?

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

More information: Animation

What is required in your java class?

Code Samples

Controlling Animations

The Controller

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 Controllers. The control object gives you access to the available animation sequences in the model.

  AnimControl playerControl; // you need one controller per model
+
+

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. Animated models must be created in an external mesh editor (for example, Blender). +

+ +

+What is required for the model? +

+
    +
  1. For each model, you have to define a skeleton (bones rigging).
    +
  2. +
  3. For each motion, you have to specify how it distorts 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. +
+ +

+ +More information: Animation +

+ +

+What is required in your java class? +

+
    +
  • One animation controller per animated Model
    +
  • +
  • As many channels per controller as you need to play several animations in parallel. In simple cases one channel is enough, sometimes you need two or more per model.
    +
  • +
+ +
+ +

Code Samples

+
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+ +
+ +

Controlling Animations

+
+ +
+ +

The Controller

+
+ +

+ +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 Controllers. The control object gives you access to the available animation sequences in the model. +

+
  AnimControl playerControl; // you need one controller 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

Channels

A controller 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();
+  playerControl.addListener(this); // add listener
+ +
+ +

Channels

+
+ +

+ +A controller 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 controller, 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() {
+  ...
+ +

+To reset a controller, 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");
@@ -19,18 +296,81 @@
         }
       }
     }
-  };

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:

  • The controller to which the listener is assigned.
  • The animation channel being played.
  • The name of the animation that has just finished playing.
  public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
+  };
+ +
+ +

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: +

+
    +
  • The controller to which the listener is assigned.
    +
  • +
  • The animation channel being played.
    +
  • +
  • The name of the animation that has just finished playing.
    +
  • +
+
  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

  • The controller to which the listener is assigned.
  • The animation channel being played.
  • The name of the animation that will start playing.
  public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
+  }
+ +
+ +

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 +

+
    +
  • The controller to which the listener is assigned.
    +
  • +
  • The animation channel being played.
    +
  • +
  • The name of the animation that will start playing.
    +
  • +
+
  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-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/appstatesdemo.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/appstatesdemo.html new file mode 100644 index 000000000..1f1d6e146 --- /dev/null +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/appstatesdemo.html @@ -0,0 +1,381 @@ + +

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() {
+    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(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(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(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-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio.html index 92ce88f82..d66889707 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio.html @@ -1,4 +1,86 @@ -

Audio in jME3

There are two ways to handle audio data: Short audio files are to be stored entirely in memory, while long audio files (music) is streamed from the hard drive as it is played.

Place audio files in the assets/Sound/ directory of your project. jME3 supports Ogg Vorbis (.ogg) and Wave (.wav) formats.

Creating Audio Nodes: Streamed or Buffered

The main class to look at is com.jme3.audio.AudioNode.

Getting AudioNode Properties

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

There are other obvious getters to poll the status of corresponding setters below.

Setting AudioNode Properties

AudioNode MethodUsage
setVolume(1)Sets the volume gain. 1 is the default volume, 2 is twice as loud, etc. 0 is silent/mute.
setPitch(1)Makes the sound play in a higher or lower pitch. Default is 1. 2 is twice as high, .5f is half as low.
+ +

Audio in jME3

+
+ +

+ +There are two ways to handle audio data: Short audio files are to be stored entirely in memory, while long audio files (music) is streamed from the hard drive as it is played. +

+ +

+Place audio files in the assets/Sound/ directory of your project. jME3 supports Ogg Vorbis (.ogg) and Wave (.wav) formats. +

+ +
+ +

Creating Audio Nodes: Streamed or Buffered

+
+ +

+ +The main class to look at is com.jme3.audio.AudioNode. + +

+
    +
  • Buffered: By default, a new audio node is buffered. This means jME3 loads the whole file into memory before playing. You create a buffered sound by setting the boolean to false, or using no boolean at all:
    AudioNode boom = new AudioNode(assetManager, "Sound/boom.wav");
    +
    +
  • +
  • Streamed: If it is a long file, you stream the audio, that means, you load and play in parallel until the sound is done. You create a streamed sound by setting the boolean to true:
    AudioNode music = new AudioNode(assetManager, "Sound/music.wav", true);
    +
    +
  • +
+ +
+ +

Getting AudioNode Properties

+
+
AudioNode MethodUsage
setLooping(false)Configures the sound so that, if it is played, it plays once and stops. This is the default.
setLooping(true)Configures the sound so that, if it is played, it plays repeats from the beginning, until stop() or pause() are called. Good for ambient background noises.
Does not work for streamed sounds!
+ + + + + + + + + + + + +
AudioNode MethodUsage
getStatus()Returns either Status.Playing, Status.Stopped, or Status.Paused.
getVolume()Returns the volume.
getPitch()Returns the pitch.
+ +

+ +There are other obvious getters to poll the status of corresponding setters below. +

+ +
+ +

Setting AudioNode Properties

+
+
+ + + + + + + + + +
AudioNode MethodUsage
setVolume(1)Sets the volume gain. 1 is the default volume, 2 is twice as loud, etc. 0 is silent/mute.
setPitch(1)Makes the sound play in a higher or lower pitch. Default is 1. 2 is twice as high, .5f is half as low.
+
+ + + + + + + + + + diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio_environment_presets.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio_environment_presets.html index 5a1f208e7..9dece2711 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio_environment_presets.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/audio_environment_presets.html @@ -1,8 +1,27 @@ -

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(
+
+

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(
     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} ) );
+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} ) );
@@ -10,7 +29,13 @@ CastleLargeroom    = new Environment ( new float[]{ 26, 8.3f, 0
 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, 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} ) );
+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} ) );
@@ -18,7 +43,13 @@ FactoryLargeroom    = new Environment ( new float[]{ 26, 1.9f,
 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, 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} ) );
+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} ) );
@@ -26,14 +57,26 @@ IcepalaceLargeroom    = new Environment ( new float[]{ 26, 2.9f
 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, 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} ) );
+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, 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} ) );
+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} ) );
@@ -41,53 +84,113 @@ WoodenLargeroom        = new Environment ( new float[]{ 26, 7.5
 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.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} ) );
+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[]{ 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} ) );
+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, 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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[]{ 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} ) );
+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} ) );
@@ -96,6 +199,14 @@ Valley        = new Environment ( new float[]{ 26, 80.3f, 0.280
 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[]{ 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} ) );
+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-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bloom_and_glow.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bloom_and_glow.html index d9fea212a..927b1897b 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bloom_and_glow.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bloom_and_glow.html @@ -1,7 +1,96 @@ -

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. Create a BloomFilter
  3. Add the filter to the processor
  4. Add the processor to the viewPort
 FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
+
+

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 :

AudioNode MethodUsage
setLooping(false)Configures the sound so that, if it is played, it plays once and stops. This is the default.
setLooping(true)Configures the sound so that, if it is played, it plays repeats from the beginning, until stop() or pause() are called. Good for ambient background noises.
+Does not work for streamed sounds!
setPositional(false)
setDirectional(false)
All 3D effects switched off. This sound is global and comes from everywhere. Good for environmental ambient sounds and background music.
ParameterMethodDefaultDescription
blur scalesetBlurScale(float)1.5fthe scale of the bloom effect, but be careful, high values does artifacts
exposure PowersetExposurePower(float)5.0fthe glowing channel color is raised to the value power
exposure cut-offsetExposureCutOff(float)0.0fthe threshold of color to bloom during extraction
bloom intensitysetBloomIntensity(float)2.0fthe 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 works with Lighting.j3md, Particles.j3md and SolidColor.j3md material definitions. The tank material looks like that :

Material My Material : Common/MatDefs/Light/Lighting.j3md {
+ 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 works with Lighting.j3md, Particles.j3md and SolidColor.j3md material definitions. +The tank material looks like that : + +

+
Material My Material : Common/MatDefs/Light/Lighting.j3md {
      MaterialParameters {
         SpecularMap : Models/HoverTank/tank_specular.png
         Shininess : 8
@@ -13,17 +102,132 @@
         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. Create a BloomFilter with the GlowMode.Objects parameter
  3. Add the filter to the processor
  4. Add the processor to the viewPort
  FilterPostProcessor fpp=new FilterPostProcessor(assetManager);
+}
+ +

+ +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. Create a FilterPostProcessor
  3. Create a BloomFilter with the GlowMode.Objects parameter
  4. Add the filter to the processor
  5. Add the processor to the viewPort
    Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/SolidColor.j3md");
+  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 {
+    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 {
         
         ....
 
@@ -31,7 +235,13 @@
         Texture2D GlowMap
         // The glow color of the object
         Color GlowColor
-    }

Then add the following technique :

    Technique Glow {
+    }
+ +

+Then add the following technique : + +

+
    Technique Glow {
 
         LightMode SinglePass
 
@@ -46,5 +256,29 @@
             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);
+ } + +

+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);
+ +

view online version

\ No newline at end of file diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bullet_multithreading.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bullet_multithreading.html index dd6f4c52f..816718000 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bullet_multithreading.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bullet_multithreading.html @@ -1,4 +1,47 @@ -

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.

In the simpleInitApp() method:

bulletAppState = new BulletAppState();
+
+

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. +

+ +

+In the simpleInitApp() method: + +

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

The physics update happens in parallel to rendering, after the users changes have been made in the update() call. 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. More physics spaces can simply be added by using multiple bulletAppStates.

documentation, physics, threading
+stateManager.attach(bulletAppState);
+ +

+The physics update happens in parallel to rendering, after the users changes have been made in the update() call. 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. More physics spaces can simply be added by using multiple bulletAppStates. +

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

view online version

\ No newline at end of file diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bump-mapped-sphere.png b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/bump-mapped-sphere.png new file mode 100644 index 0000000000000000000000000000000000000000..22629d27ed4ce6e44293ea1adb7e021e874c2bb3 GIT binary patch literal 51941 zcmc$Fg;N{f7jLjap@rZn?(XhR@dClExO*w??%Lp5v;-^eZpE!YaS9ZN;4UxU-<$V0 zyvbx|XS2!f-h1vj=Oc+$`y}@sod_KO0K8X_mjMF+2rTgZzi4mZ+b@1m6#xLbjjgn_ znytAx0N}Npn&zqYZHZ`T1NJzJ!Agk;mV(LC2VD`hef^1zgBA>wmIu^+uWJdhmy|_z z_!ScsVvk_K8ile#_2)5!g=w@+R}C-q5tXpR^R&~=IzN;9GTVArWUm8ej0K4A|E4(j z16`s?hAkHJ!jhhjwmv!$fPe=I3KmVwXTFY(K@WW`$MOUq09tG(;OCy*7Bk6@{X?Qq z2P855`e%(qF$@6m88dxDDrg4uFc`C?A{CeedLXoPv`8jA026fj%=dut-wGbcfKnE| zWh?;38;Th(kxXC^5eDE`q)QD`A{8m9N`@i@pmqf4`Ig^{3s8Fn04k1;Wf1{c=zxrp z7o~)=4K9r`Zf}vlJgSq3002#(dbQ@b!4v=D`D-exwsL1L{8Y_T;`aa=fQ&TF-q0u4 z2r2=m7z}PR+)~{XB5c9IC&TU?#z}oVpUs{I8difc7}Lhg%AUWfQ+Nu=k?{KKlt$&{(W>C^`|I>vV&$3v zB?Zoc*j1U6kN|hADQs!cVIRIUC2BH#syB0^^&@6(>a5BYrGXDCz0O?%3L1c-l6j*u z2NSRe2FTp4ZGS+_@p{v9cecjuhas_o<^qkZN&2VaBLD!1s7ZXtD0tguf@Wp%{$}&t z>E?R`AYw8QL*XajE(u|YBGgY8o%uWRs0p=0IMW{#UsG|C5b9}k`oAoOVHMLz&3`Gc zdMP?WuARzSV+H_j@5!I5i16>etJ}5v`1f?_Wz+L zK^X3-bzKnfG~{5z93K9+nYO*~7wYop%Gz?-lHC%pigrqzm#{cwu(ft(=tuQAYHB!r zllkoNZ1|k;toc0j4htDESeApf1sL*HAxyasr%$L4Q;Pe0=(G}uA`T~7Mx~H;CKRNw zgZ*y+XUk(ta_hs^`z;FBI2qc5K((UGefo56edgdRZX3U7CR z?Ad45EBsY_HRW{Z<>jFDMteDvKiQNP4fr)YV^3|@C`BXk%JSr$W7j`)raQPdVHu29 zTjX0O?%N-s9x-3t?t3035@!$%e5mBI;q!8k=iC965<77o^N?`Pa-;L>+x@n=>d9~z z=bYqNw_3G)ni=>_(`ym0P%(m`@m{0a_PcG-0X279X1ZR2Ueyw-TZrJW$e3TGcOr`2 zUyEc@bw}4VoW`$BczUNlj~n|8TJ;qw?W#`o=PLaxFDr?w9vyZ0O!+Dt^c_D>ab=~7 z*w0l~>uosXIt(_!@Ac&OvDC5lQsYWzWAo3w6`hrhF9pKLLX|EyZeH6q2N}C9bLV-u z{fw)&@fu}wI@W~NnQq@MlVX#VVwB>?j4;394(biJ?!A4;0H{uo|6fqB(2pJuT7)dT9CnOEo9eF!c zI+Pz}Ta+q0?oi1g3RAZ(MjpX#CvniqN*v6lCmaWWG@I z%ZVj$#g9fmxMP}r`kiwqI8}I3nEugoki(2%h$QMBctqSzd1IQRmqS&}{aG;Jv!C?S zF!F2IMOfE>Ui27F4V^7*bjor{^vL;xBwJ%)!kju_g=?)KwxU-feO|-VYex zBVFoU82XuusLXYD=%F~BBkv|xQ|~#Mxhll$rE(~oCh088Eb^0nDAdvH5gxEH>HcPJ zgrvzoep&s}v>W$J%JT6`^6Gu7ji-YBsmt+-N#e1@DRKP*eG0~wraxxq z%Y#!GNEm^iLk#+iXInJ`4@OI#$NbVx^b%N9jn-THTx8E$P8+Mm@Cd zp|=&d+RcPVc!c;^Prxf_^Js8%<<2S5EpL0kz%#=`t#4KT@`-(SD!O6~WGPJx;;%Ps z?D>zrJ)bEsOE8szy0cNs^_Ot- z!D>f{V~nGJs%fg@N_>;^>Prbz(V<79v%6co$Ha10qA;?s)A7~%QiGDU&QkSMyKFn` z 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 IbxsLQ0Cgd8jsO4v literal 0 HcmV?d00001 diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/camera.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/camera.html index e7f53a25e..86b01456c 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/camera.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/camera.html @@ -64,10 +64,10 @@ The camera object is created with the following defaults: 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 frustrum is defined by the near/far plane, left/rught plane, top/bottom plane (all distances as float values) + cam.setFrustum( near, far, left, right, top, bottom )The frustum is defined by the near/far plane, left/rught plane, top/bottom plane (all distances as float values) - cam.setFrustumPerspective( fovY, aspect ratio, near, far)The frustrum is defined by view angle along the Y axis (in degrees), aspect ratio, and the near/far plane. + 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. @@ -82,9 +82,9 @@ The camera object is created with the following defaults: cam.getScreenCoordinates()? - +

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

@@ -114,7 +114,7 @@ The flyby camera is an extension of the default camera in com.jme3.app.SimpleApp flyCam.setDragToRotate(true)Must keep mouse button pressed to rotate camera. Used e.g. for Applets. if false, all mouse movement will be captured and interpreted as rotations. - +

Chase Camera

@@ -164,7 +164,7 @@ ChaseCamera chaseCam = new ChaseCamera(cam, target, inputManager); chaseCam.setDefaultHorizontalRotation(-FastMath.PI/2);The default horizontal rotation angle of the camera around the target at the start of the application. -
+ diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/capture_audio_video_to_a_file.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/capture_audio_video_to_a_file.html new file mode 100644 index 000000000..27f169c51 --- /dev/null +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/capture_audio_video_to_a_file.html @@ -0,0 +1,652 @@ + +

Capture Audio/Video to a File

+
+ +

+ +

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 +

+

+ +

+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 then jMonkeyEngine3'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

+
+ +

+ +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(IOException{
+	File.createTempFile("JME-water-video", ".avi");
+	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-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/cinematics.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/cinematics.html index dc5d35081..02252cbff 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/cinematics.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/cinematics.html @@ -1,12 +1,386 @@ -

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. 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.
  3. Create a Cinematic object for this movie scene. The Cinematic will contain and manage the movie script.
  4. For each line in your script (for each frame in your timeline), add a CinematicEvent to the Cinematic.

Sample Code

How to Use a Cinematic

A Cinematic is like a movie script for a node.

Cinematic cinematic = new Cinematic(sceneNode, duration);
+
+

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. 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.
  3. Attach the Cinematic to the SimpleApplication's stateManager.
  4. Play, stop and pause the Cinematic from your code.
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:

  • thingNode is the Spatial to be moved.
  • path is a complex MotionPath.

To create a MotionTrack, do the following:

  1. Create a MotionTrack based on the MotionPath.
  2. Configure your MotionTrack (see below).
  3. Add the MotionTrack to a Cinematic.
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:

  • thingNode is the Spatial to be moved.
  • endPosition is the target location as Vector3f.
  • duration is the time that it should take from start to end point.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

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:

  • thingNode is the Spatial to be rotated.
  • endRotation is the target rotation in Quaternion format.
  • duration is the time that it should take from start to target rotation.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

ScaleTrack

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

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

Details of the constructor:

  • thingNode is the Spatial to be resized.
  • endScale is the target Scale in Vector3f format.
  • duration is the time that it should take from start to target scale.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

SoundTrack

A SoundTrack plays a sound as part of the cinematic.

SoundTrack( audioPath, isStream, duration, loopMode )

Details of the constructor:

  • audioPath is the path to an audio file as String, e.g. "Sounds/mySound.wav".
  • isStream toggles between streaming and buffering. Set to true to stream long audio file, set to false to play short buffered sounds.
  • duration is the time that it should take to play.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

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:

  • screen is the name of the Nifty GUI screen to load, as String.
  • duration is the time that it should take to play.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

AnimationTrack

An AnimationTrack triggers an animation as part of a cinematic.

AnimationTrack( thingNode, animationName, duration, loopMode )

Details of the constructor:

  • thingNode is the Spatial whose animation you want to play.
  • animationName the name of the animation stored in the animated model that you want to trigger, as a String.
  • duration is the time that it should take to play.
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.

Customizations

You can extend individual CinematicEvents. The shows how to extend a GuiTrack to script subtitles. See how the subtitles are used in the . +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: +

+
    +
  • thingNode is the Spatial to be moved.
    +
  • +
  • path is a complex MotionPath.
    +
  • +
+ +

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

+
    +
  1. +
  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: +

+
    +
  • thingNode is the Spatial to be moved.
    +
  • +
  • endPosition is the target location as Vector3f.
    +
  • +
  • duration is the time that it should take from start to end point.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +

+ +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: +

+
    +
  • thingNode is the Spatial to be rotated.
    +
  • +
  • endRotation is the target rotation in Quaternion format.
    +
  • +
  • duration is the time that it should take from start to target rotation.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +
+ +

ScaleTrack

+
+ +

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

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

+Details of the constructor: +

+
    +
  • thingNode is the Spatial to be resized.
    +
  • +
  • endScale is the target Scale in Vector3f format.
    +
  • +
  • duration is the time that it should take from start to target scale.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +
+ +

SoundTrack

+
+ +

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

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

+ +Details of the constructor: +

+
    +
  • audioPath is the path to an audio file as String, e.g. "Sounds/mySound.wav".
    +
  • +
  • isStream toggles between streaming and buffering. Set to true to stream long audio file, set to false to play short buffered sounds.
    +
  • +
  • duration is the time that it should take to play.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +
+ +

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: +

+
    +
  • screen is the name of the Nifty GUI screen to load, as String.
    +
  • +
  • duration is the time that it should take to play.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +
+ +

AnimationTrack

+
+ +

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

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

+ +Details of the constructor: +

+
    +
  • thingNode is the Spatial whose animation you want to play.
    +
  • +
  • animationName the name of the animation stored in the animated model that you want to trigger, as a String.
    +
  • +
  • duration is the time that it should take to play.
    +
  • +
  • loopMode can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.
    +
  • +
+ +
+ +

Customizations

+
+ +

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

diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/collision_and_intersection.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/collision_and_intersection.html index 900144f9e..7c8ff5989 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/collision_and_intersection.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/collision_and_intersection.html @@ -177,7 +177,7 @@ For example you can use Bounding Volumes on custom meshes, or complex non-physic

mesh.setBound(new BoundingSphere());
-q.updateBound();
+mesh.updateBound();
diff --git a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/combo_moves.html b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/combo_moves.html index a094be6f6..068a581d1 100644 --- a/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/combo_moves.html +++ b/sdk/jme3-core/javahelp/com/jme3/gde/core/docs/jme3/advanced/combo_moves.html @@ -1,4 +1,42 @@ -

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. Define combos
  3. Detect combos in ActionListener
  4. Execute combos in update loop

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

Example Code