diff --git a/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java b/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java index 0376f0031..def72fe3a 100644 --- a/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java +++ b/engine/src/bullet-common/com/jme3/bullet/control/KinematicRagdollControl.java @@ -355,6 +355,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P //I remove the skeletonControl and readd it to the spatial to make sure it's after the ragdollControl in the stack //Find a proper way to order the controls. SkeletonControl sc = model.getControl(SkeletonControl.class); + if(sc == null){ + throw new IllegalArgumentException("The root node of the model should have a SkeletonControl. Make sure the control is there and that it's not on a sub node."); + } model.removeControl(sc); model.addControl(sc); diff --git a/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java b/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java index 38551ea3f..de28aba02 100644 --- a/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java +++ b/engine/src/bullet-common/com/jme3/bullet/util/CollisionShapeFactory.java @@ -210,7 +210,7 @@ public class CollisionShapeFactory { private static MeshCollisionShape createSingleMeshShape(Geometry geom, Spatial parent) { Mesh mesh = geom.getMesh(); Transform trans = getTransform(geom, parent); - if (mesh != null) { + if (mesh != null && mesh.getMode() == Mesh.Mode.Triangles) { MeshCollisionShape mColl = new MeshCollisionShape(mesh); mColl.setScale(trans.getScale()); return mColl; diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag index eac42df22..e4773c8cf 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag @@ -211,11 +211,14 @@ void main(){ // *********************** #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) vec4 normalHeight = texture2D(m_NormalMap, newTexCoord); - vec3 normal = normalize((normalHeight.xyz * vec3(2.0) - vec3(1.0))); + //Note the -2.0 and -1.0. We invert the green channel of the normal map, + //as it's complient with normal maps generated with blender. + //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898 + //for more explanation. + vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0))); #ifdef LATC normal.z = sqrt(1.0 - (normal.x * normal.x) - (normal.y * normal.y)); - #endif - //normal.y = -normal.y; + #endif #elif !defined(VERTEX_LIGHTING) vec3 normal = vNormal; #if !defined(LOW_QUALITY) && !defined(V_TANGENT) diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert index 72323cbae..c7cb3e5db 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert @@ -171,7 +171,7 @@ void main(){ vec3 wvTangent = normalize(g_NormalMatrix * modelSpaceTan); vec3 wvBinormal = cross(wvNormal, wvTangent); - mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal); + mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal); //vPosition = wvPosition * tbnMat; //vViewDir = viewDir * tbnMat; diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag index c2666fcc8..f22a93af2 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag @@ -35,6 +35,10 @@ vec3 getPosition(in float depth, in vec2 uv){ } void main(){ + #if !defined( RENDER_SHADOWS ) + gl_FragColor = texture2D(m_Texture,texCoord); + return; + #endif float depth = texture2D(m_DepthTexture,texCoord).r; vec4 color = texture2D(m_Texture,texCoord); diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md index b3aa35ac9..a9362b7db 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -57,6 +57,9 @@ MaterialDef Post Shadow { FADE : FadeInfo PSSM : Splits POINTLIGHT : LightViewProjectionMatrix5 + //if no shadow map don't render shadows + RENDER_SHADOWS : ShadowMap0 + } } diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag index a8280ea1c..c4579fdf8 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag @@ -87,6 +87,11 @@ vec4 main_multiSample(in int numSample){ void main(){ + #if !defined( RENDER_SHADOWS ) + outFragColor = fetchTextureSample(m_Texture,texCoord,0); + return; + #endif + #ifdef RESOLVE_MS vec4 color = vec4(0.0); for (int i = 0; i < m_NumSamples; i++){ diff --git a/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib index 0e5355cea..d0723a233 100644 --- a/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib +++ b/engine/src/core-data/Common/ShaderLib/MultiSample.glsllib @@ -46,17 +46,17 @@ vec4 getDepth(in sampler2DMS tex,in vec2 texC){ #endif vec4 fetchTextureSample(in sampler2D tex,in vec2 texC,in int sample){ - return texture2D(tex,texC); + return texture(tex,texC); } vec4 getColor(in sampler2D tex, in vec2 texC){ - return texture2D(tex,texC); + return texture(tex,texC); } vec4 getColorSingle(in sampler2D tex, in vec2 texC){ - return texture2D(tex, texC); + return texture(tex, texC); } vec4 getDepth(in sampler2D tex,in vec2 texC){ - return texture2D(tex,texC); + return texture(tex,texC); } \ No newline at end of file diff --git a/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java b/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java index adde49dd5..2207f30c0 100644 --- a/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java +++ b/engine/src/core-effects/com/jme3/post/filters/BloomFilter.java @@ -1,314 +1,329 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.post.filters; - -import com.jme3.asset.AssetManager; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; -import com.jme3.post.Filter; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.Image.Format; -import java.io.IOException; -import java.util.ArrayList; - -/** - * BloomFilter is used to make objects in the scene have a glow effect.
- * There are 2 mode : Scene and Objects.
- * Scene mode extracts the bright parts of the scene to make them glow
- * Object mode make objects glow according to their material's glowMap or their GlowColor
- * @see advanced:bloom_and_glow for more details - * - * @author Rémy Bouquet aka Nehon - */ -public class BloomFilter extends Filter { - - /** - * GlowMode specifies if the glow will be applied to the whole scene,or to objects that have aglow color or a glow map - */ - public enum GlowMode { - - /** - * Apply bloom filter to bright areas in the scene. - */ - Scene, - /** - * Apply bloom only to objects that have a glow map or a glow color. - */ - Objects, - /** - * Apply bloom to both bright parts of the scene and objects with glow map. - */ - SceneAndObjects; - } - - private GlowMode glowMode = GlowMode.Scene; - //Bloom parameters - private float blurScale = 1.5f; - private float exposurePower = 5.0f; - private float exposureCutOff = 0.0f; - private float bloomIntensity = 2.0f; - private float downSamplingFactor = 1; - private Pass preGlowPass; - private Pass extractPass; - private Pass horizontalBlur = new Pass(); - private Pass verticalalBlur = new Pass(); - private Material extractMat; - private Material vBlurMat; - private Material hBlurMat; - private int screenWidth; - private int screenHeight; - private RenderManager renderManager; - private ViewPort viewPort; - - /** - * Creates a Bloom filter - */ - public BloomFilter() { - super("BloomFilter"); - } - - /** - * Creates the bloom filter with the specific glow mode - * @param glowMode - */ - public BloomFilter(GlowMode glowMode) { - this(); - this.glowMode = glowMode; - } - - @Override - protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { - this.renderManager = renderManager; - this.viewPort = vp; - screenWidth = (int) Math.max(1, (w / downSamplingFactor)); - screenHeight = (int) Math.max(1, (h / downSamplingFactor)); - // System.out.println(screenWidth + " " + screenHeight); - if (glowMode != GlowMode.Scene) { - preGlowPass = new Pass(); - preGlowPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth); - } - - postRenderPasses = new ArrayList(); - //configuring extractPass - extractMat = new Material(manager, "Common/MatDefs/Post/BloomExtract.j3md"); - extractPass = new Pass() { - - @Override - public boolean requiresSceneAsTexture() { - return true; - } - - @Override - public void beforeRender() { - extractMat.setFloat("ExposurePow", exposurePower); - extractMat.setFloat("ExposureCutoff", exposureCutOff); - if (glowMode != GlowMode.Scene) { - extractMat.setTexture("GlowMap", preGlowPass.getRenderedTexture()); - } - extractMat.setBoolean("Extract", glowMode != GlowMode.Objects); - } - }; - - extractPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, extractMat); - postRenderPasses.add(extractPass); - - //configuring horizontal blur pass - hBlurMat = new Material(manager, "Common/MatDefs/Blur/HGaussianBlur.j3md"); - horizontalBlur = new Pass() { - - @Override - public void beforeRender() { - hBlurMat.setTexture("Texture", extractPass.getRenderedTexture()); - hBlurMat.setFloat("Size", screenWidth); - hBlurMat.setFloat("Scale", blurScale); - } - }; - - horizontalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, hBlurMat); - postRenderPasses.add(horizontalBlur); - - //configuring vertical blur pass - vBlurMat = new Material(manager, "Common/MatDefs/Blur/VGaussianBlur.j3md"); - verticalalBlur = new Pass() { - - @Override - public void beforeRender() { - vBlurMat.setTexture("Texture", horizontalBlur.getRenderedTexture()); - vBlurMat.setFloat("Size", screenHeight); - vBlurMat.setFloat("Scale", blurScale); - } - }; - - verticalalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, vBlurMat); - postRenderPasses.add(verticalalBlur); - - - //final material - material = new Material(manager, "Common/MatDefs/Post/BloomFinal.j3md"); - material.setTexture("BloomTex", verticalalBlur.getRenderedTexture()); - } - - - @Override - protected Material getMaterial() { - material.setFloat("BloomIntensity", bloomIntensity); - return material; - } - - @Override - protected void postQueue(RenderQueue queue) { - if (glowMode != GlowMode.Scene) { - renderManager.getRenderer().setBackgroundColor(ColorRGBA.BlackNoAlpha); - renderManager.getRenderer().setFrameBuffer(preGlowPass.getRenderFrameBuffer()); - renderManager.getRenderer().clearBuffers(true, true, true); - renderManager.setForcedTechnique("Glow"); - renderManager.renderViewPortQueues(viewPort, false); - renderManager.setForcedTechnique(null); - renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); - } - } - - /** - * returns the bloom intensity - * @return - */ - public float getBloomIntensity() { - return bloomIntensity; - } - - /** - * intensity of the bloom effect default is 2.0 - * @param bloomIntensity - */ - public void setBloomIntensity(float bloomIntensity) { - this.bloomIntensity = bloomIntensity; - } - - /** - * returns the blur scale - * @return - */ - public float getBlurScale() { - return blurScale; - } - - /** - * sets The spread of the bloom default is 1.5f - * @param blurScale - */ - public void setBlurScale(float blurScale) { - this.blurScale = blurScale; - } - - /** - * returns the exposure cutoff
- * for more details see {@link #setExposureCutOff(float exposureCutOff)} - * @return - */ - public float getExposureCutOff() { - return exposureCutOff; - } - - /** - * Define the color threshold on which the bloom will be applied (0.0 to 1.0) - * @param exposureCutOff - */ - public void setExposureCutOff(float exposureCutOff) { - this.exposureCutOff = exposureCutOff; - } - - /** - * returns the exposure power
- * form more details see {@link #setExposurePower(float exposurePower)} - * @return - */ - public float getExposurePower() { - return exposurePower; - } - - /** - * defines how many time the bloom extracted color will be multiplied by itself. default id 5.0
- * a high value will reduce rough edges in the bloom and somhow the range of the bloom area * - * @param exposurePower - */ - public void setExposurePower(float exposurePower) { - this.exposurePower = exposurePower; - } - - /** - * returns the downSampling factor
- * form more details see {@link #setDownSamplingFactor(float downSamplingFactor)} - * @return - */ - public float getDownSamplingFactor() { - return downSamplingFactor; - } - - /** - * Sets the downSampling factor : the size of the computed texture will be divided by this factor. default is 1 for no downsampling - * A 2 value is a good way of widening the blur - * @param downSamplingFactor - */ - public void setDownSamplingFactor(float downSamplingFactor) { - this.downSamplingFactor = downSamplingFactor; - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(glowMode, "glowMode", GlowMode.Scene); - oc.write(blurScale, "blurScale", 1.5f); - oc.write(exposurePower, "exposurePower", 5.0f); - oc.write(exposureCutOff, "exposureCutOff", 0.0f); - oc.write(bloomIntensity, "bloomIntensity", 2.0f); - oc.write(downSamplingFactor, "downSamplingFactor", 1); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - glowMode = ic.readEnum("glowMode", GlowMode.class, GlowMode.Scene); - blurScale = ic.readFloat("blurScale", 1.5f); - exposurePower = ic.readFloat("exposurePower", 5.0f); - exposureCutOff = ic.readFloat("exposureCutOff", 0.0f); - bloomIntensity = ic.readFloat("bloomIntensity", 2.0f); - downSamplingFactor = ic.readFloat("downSamplingFactor", 1); - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.post.filters; + +import com.jme3.asset.AssetManager; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.post.Filter; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.texture.Image.Format; +import java.io.IOException; +import java.util.ArrayList; + +/** + * BloomFilter is used to make objects in the scene have a glow effect.
+ * There are 2 mode : Scene and Objects.
+ * Scene mode extracts the bright parts of the scene to make them glow
+ * Object mode make objects glow according to their material's glowMap or their GlowColor
+ * @see advanced:bloom_and_glow for more details + * + * @author Rémy Bouquet aka Nehon + */ +public class BloomFilter extends Filter { + + /** + * GlowMode specifies if the glow will be applied to the whole scene,or to objects that have aglow color or a glow map + */ + public enum GlowMode { + + /** + * Apply bloom filter to bright areas in the scene. + */ + Scene, + /** + * Apply bloom only to objects that have a glow map or a glow color. + */ + Objects, + /** + * Apply bloom to both bright parts of the scene and objects with glow map. + */ + SceneAndObjects; + } + + private GlowMode glowMode = GlowMode.Scene; + //Bloom parameters + private float blurScale = 1.5f; + private float exposurePower = 5.0f; + private float exposureCutOff = 0.0f; + private float bloomIntensity = 2.0f; + private float downSamplingFactor = 1; + private Pass preGlowPass; + private Pass extractPass; + private Pass horizontalBlur = new Pass(); + private Pass verticalalBlur = new Pass(); + private Material extractMat; + private Material vBlurMat; + private Material hBlurMat; + private int screenWidth; + private int screenHeight; + private RenderManager renderManager; + private ViewPort viewPort; + + private AssetManager assetManager; + private int initalWidth; + private int initalHeight; + + /** + * Creates a Bloom filter + */ + public BloomFilter() { + super("BloomFilter"); + } + + /** + * Creates the bloom filter with the specific glow mode + * @param glowMode + */ + public BloomFilter(GlowMode glowMode) { + this(); + this.glowMode = glowMode; + } + + @Override + protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + this.renderManager = renderManager; + this.viewPort = vp; + + this.assetManager = manager; + this.initalWidth = w; + this.initalHeight = h; + + screenWidth = (int) Math.max(1, (w / downSamplingFactor)); + screenHeight = (int) Math.max(1, (h / downSamplingFactor)); + // System.out.println(screenWidth + " " + screenHeight); + if (glowMode != GlowMode.Scene) { + preGlowPass = new Pass(); + preGlowPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth); + } + + postRenderPasses = new ArrayList(); + //configuring extractPass + extractMat = new Material(manager, "Common/MatDefs/Post/BloomExtract.j3md"); + extractPass = new Pass() { + + @Override + public boolean requiresSceneAsTexture() { + return true; + } + + @Override + public void beforeRender() { + extractMat.setFloat("ExposurePow", exposurePower); + extractMat.setFloat("ExposureCutoff", exposureCutOff); + if (glowMode != GlowMode.Scene) { + extractMat.setTexture("GlowMap", preGlowPass.getRenderedTexture()); + } + extractMat.setBoolean("Extract", glowMode != GlowMode.Objects); + } + }; + + extractPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, extractMat); + postRenderPasses.add(extractPass); + + //configuring horizontal blur pass + hBlurMat = new Material(manager, "Common/MatDefs/Blur/HGaussianBlur.j3md"); + horizontalBlur = new Pass() { + + @Override + public void beforeRender() { + hBlurMat.setTexture("Texture", extractPass.getRenderedTexture()); + hBlurMat.setFloat("Size", screenWidth); + hBlurMat.setFloat("Scale", blurScale); + } + }; + + horizontalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, hBlurMat); + postRenderPasses.add(horizontalBlur); + + //configuring vertical blur pass + vBlurMat = new Material(manager, "Common/MatDefs/Blur/VGaussianBlur.j3md"); + verticalalBlur = new Pass() { + + @Override + public void beforeRender() { + vBlurMat.setTexture("Texture", horizontalBlur.getRenderedTexture()); + vBlurMat.setFloat("Size", screenHeight); + vBlurMat.setFloat("Scale", blurScale); + } + }; + + verticalalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, vBlurMat); + postRenderPasses.add(verticalalBlur); + + + //final material + material = new Material(manager, "Common/MatDefs/Post/BloomFinal.j3md"); + material.setTexture("BloomTex", verticalalBlur.getRenderedTexture()); + } + + + protected void reInitFilter() { + initFilter(assetManager, renderManager, viewPort, initalWidth, initalHeight); + } + + @Override + protected Material getMaterial() { + material.setFloat("BloomIntensity", bloomIntensity); + return material; + } + + @Override + protected void postQueue(RenderQueue queue) { + if (glowMode != GlowMode.Scene) { + renderManager.getRenderer().setBackgroundColor(ColorRGBA.BlackNoAlpha); + renderManager.getRenderer().setFrameBuffer(preGlowPass.getRenderFrameBuffer()); + renderManager.getRenderer().clearBuffers(true, true, true); + renderManager.setForcedTechnique("Glow"); + renderManager.renderViewPortQueues(viewPort, false); + renderManager.setForcedTechnique(null); + renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); + } + } + + /** + * returns the bloom intensity + * @return + */ + public float getBloomIntensity() { + return bloomIntensity; + } + + /** + * intensity of the bloom effect default is 2.0 + * @param bloomIntensity + */ + public void setBloomIntensity(float bloomIntensity) { + this.bloomIntensity = bloomIntensity; + } + + /** + * returns the blur scale + * @return + */ + public float getBlurScale() { + return blurScale; + } + + /** + * sets The spread of the bloom default is 1.5f + * @param blurScale + */ + public void setBlurScale(float blurScale) { + this.blurScale = blurScale; + } + + /** + * returns the exposure cutoff
+ * for more details see {@link #setExposureCutOff(float exposureCutOff)} + * @return + */ + public float getExposureCutOff() { + return exposureCutOff; + } + + /** + * Define the color threshold on which the bloom will be applied (0.0 to 1.0) + * @param exposureCutOff + */ + public void setExposureCutOff(float exposureCutOff) { + this.exposureCutOff = exposureCutOff; + } + + /** + * returns the exposure power
+ * form more details see {@link #setExposurePower(float exposurePower)} + * @return + */ + public float getExposurePower() { + return exposurePower; + } + + /** + * defines how many time the bloom extracted color will be multiplied by itself. default id 5.0
+ * a high value will reduce rough edges in the bloom and somhow the range of the bloom area * + * @param exposurePower + */ + public void setExposurePower(float exposurePower) { + this.exposurePower = exposurePower; + } + + /** + * returns the downSampling factor
+ * form more details see {@link #setDownSamplingFactor(float downSamplingFactor)} + * @return + */ + public float getDownSamplingFactor() { + return downSamplingFactor; + } + + /** + * Sets the downSampling factor : the size of the computed texture will be divided by this factor. default is 1 for no downsampling + * A 2 value is a good way of widening the blur + * @param downSamplingFactor + */ + public void setDownSamplingFactor(float downSamplingFactor) { + this.downSamplingFactor = downSamplingFactor; + if (assetManager != null) // dirty isInitialised check + reInitFilter(); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(glowMode, "glowMode", GlowMode.Scene); + oc.write(blurScale, "blurScale", 1.5f); + oc.write(exposurePower, "exposurePower", 5.0f); + oc.write(exposureCutOff, "exposureCutOff", 0.0f); + oc.write(bloomIntensity, "bloomIntensity", 2.0f); + oc.write(downSamplingFactor, "downSamplingFactor", 1); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + glowMode = ic.readEnum("glowMode", GlowMode.class, GlowMode.Scene); + blurScale = ic.readFloat("blurScale", 1.5f); + exposurePower = ic.readFloat("exposurePower", 5.0f); + exposureCutOff = ic.readFloat("exposureCutOff", 0.0f); + bloomIntensity = ic.readFloat("bloomIntensity", 2.0f); + downSamplingFactor = ic.readFloat("downSamplingFactor", 1); + } +} diff --git a/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java b/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java index 20e5e3397..271358454 100644 --- a/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java +++ b/engine/src/core-effects/com/jme3/post/filters/LightScatteringFilter.java @@ -37,6 +37,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.material.Material; +import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.post.Filter; import com.jme3.renderer.Camera; @@ -102,7 +103,9 @@ public class LightScatteringFilter extends Filter { getClipCoordinates(lightPosition, screenLightPos, viewPort.getCamera()); viewPort.getCamera().getViewMatrix().mult(lightPosition, viewLightPos); if (adaptative) { - innerLightDensity = Math.max(lightDensity - Math.max(screenLightPos.x, screenLightPos.y), 0.0f); + float densityX = 1f - FastMath.clamp(FastMath.abs(screenLightPos.x - 0.5f), 0, 1); + float densityY = 1f - FastMath.clamp(FastMath.abs(screenLightPos.y - 0.5f), 0, 1); + innerLightDensity = lightDensity * densityX * densityY; } else { innerLightDensity = lightDensity; } diff --git a/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java b/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java index 1177fc266..51bec6a8b 100644 --- a/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java +++ b/engine/src/core-effects/com/jme3/water/SimpleWaterProcessor.java @@ -48,6 +48,7 @@ import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; +import com.jme3.util.TempVars; /** * @@ -119,15 +120,12 @@ public class SimpleWaterProcessor implements SceneProcessor { private Plane reflectionClipPlane; private Plane refractionClipPlane; private float refractionClippingOffset = 0.3f; - private float reflectionClippingOffset = -5f; - private Vector3f vect1 = new Vector3f(); - private Vector3f vect2 = new Vector3f(); - private Vector3f vect3 = new Vector3f(); + private float reflectionClippingOffset = -5f; private float distortionScale = 0.2f; private float distortionMix = 0.5f; private float texScale = 1f; - + /** * Creates a SimpleWaterProcessor * @param manager the asset manager @@ -190,10 +188,6 @@ public class SimpleWaterProcessor implements SceneProcessor { public void postQueue(RenderQueue rq) { Camera sceneCam = rm.getCurrentCamera(); - //update ray - ray.setOrigin(sceneCam.getLocation()); - ray.setDirection(sceneCam.getDirection()); - //update refraction cam refractionCam.setLocation(sceneCam.getLocation()); refractionCam.setRotation(sceneCam.getRotation()); @@ -203,40 +197,11 @@ public class SimpleWaterProcessor implements SceneProcessor { sceneCam.getFrustumRight(), sceneCam.getFrustumTop(), sceneCam.getFrustumBottom()); - refractionCam.setParallelProjection(false); + refractionCam.setParallelProjection(sceneCam.isParallelProjection()); //update reflection cam - boolean inv = false; - if (!ray.intersectsWherePlane(plane, targetLocation)) { - ray.setDirection(ray.getDirection().negateLocal()); - ray.intersectsWherePlane(plane, targetLocation); - inv = true; - } - Vector3f loc = plane.reflect(sceneCam.getLocation(), new Vector3f()); - reflectionCam.setLocation(loc); - reflectionCam.setFrustum(sceneCam.getFrustumNear(), - sceneCam.getFrustumFar(), - sceneCam.getFrustumLeft(), - sceneCam.getFrustumRight(), - sceneCam.getFrustumTop(), - sceneCam.getFrustumBottom()); - reflectionCam.setParallelProjection(false); - // tempVec and calcVect are just temporary vector3f objects - vect1.set(sceneCam.getLocation()).addLocal(sceneCam.getUp()); - float planeDistance = plane.pseudoDistance(vect1); - vect2.set(plane.getNormal()).multLocal(planeDistance * 2.0f); - vect3.set(vect1.subtractLocal(vect2)).subtractLocal(loc).normalizeLocal().negateLocal(); - // now set the up vector - reflectionCam.lookAt(targetLocation, vect3); - if (inv) { - reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal()); - } - - //we are rendering a sub part of the scene so the camera planeState may never be reseted to 0. -// reflectionCam.setPlaneState(0); -// refractionCam.setPlaneState(0); - - + WaterUtils.updateReflectionCam(reflectionCam, plane, sceneCam); + //Rendering reflection and refraction rm.renderViewPort(reflectionView, savedTpf); rm.renderViewPort(refractionView, savedTpf); diff --git a/engine/src/core-effects/com/jme3/water/WaterFilter.java b/engine/src/core-effects/com/jme3/water/WaterFilter.java index 10749cc99..e62e1fbf4 100644 --- a/engine/src/core-effects/com/jme3/water/WaterFilter.java +++ b/engine/src/core-effects/com/jme3/water/WaterFilter.java @@ -155,40 +155,11 @@ public class WaterFilter extends Filter { material.setFloat("WaterHeight", waterHeight); - //update reflection cam - ray.setOrigin(sceneCam.getLocation()); - ray.setDirection(sceneCam.getDirection()); + //update reflection cam plane = new Plane(Vector3f.UNIT_Y, new Vector3f(0, waterHeight, 0).dot(Vector3f.UNIT_Y)); - reflectionProcessor.setReflectionClipPlane(plane); - boolean inv = false; - if (!ray.intersectsWherePlane(plane, targetLocation)) { - ray.setDirection(ray.getDirection().negateLocal()); - ray.intersectsWherePlane(plane, targetLocation); - inv = true; - } - Vector3f loc = plane.reflect(sceneCam.getLocation(), new Vector3f()); - reflectionCam.setLocation(loc); - reflectionCam.setFrustum(sceneCam.getFrustumNear(), - sceneCam.getFrustumFar(), - sceneCam.getFrustumLeft(), - sceneCam.getFrustumRight(), - sceneCam.getFrustumTop(), - sceneCam.getFrustumBottom()); - reflectionCam.setParallelProjection(false); - TempVars vars = TempVars.get(); - - - vars.vect1.set(sceneCam.getLocation()).addLocal(sceneCam.getUp()); - float planeDistance = plane.pseudoDistance(vars.vect1); - vars.vect2.set(plane.getNormal()).multLocal(planeDistance * 2.0f); - vars.vect3.set(vars.vect1.subtractLocal(vars.vect2)).subtractLocal(loc).normalizeLocal().negateLocal(); - - reflectionCam.lookAt(targetLocation, vars.vect3); - vars.release(); - - if (inv) { - reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal()); - } + reflectionProcessor.setReflectionClipPlane(plane); + WaterUtils.updateReflectionCam(reflectionCam, plane, sceneCam); + //if we're under water no need to compute reflection if (sceneCam.getLocation().y >= waterHeight) { @@ -1026,7 +997,7 @@ public class WaterFilter extends Filter { public void setReflectionDisplace(float reflectionDisplace) { this.reflectionDisplace = reflectionDisplace; if (material != null) { - material.setFloat("m_ReflectionDisplace", reflectionDisplace); + material.setFloat("ReflectionDisplace", reflectionDisplace); } } diff --git a/engine/src/core-effects/com/jme3/water/WaterUtils.java b/engine/src/core-effects/com/jme3/water/WaterUtils.java new file mode 100644 index 000000000..f232a6512 --- /dev/null +++ b/engine/src/core-effects/com/jme3/water/WaterUtils.java @@ -0,0 +1,53 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.water; + +import com.jme3.math.Plane; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.util.TempVars; + +/** + * + * @author Nehon + */ +public class WaterUtils { + + public static void updateReflectionCam(Camera reflectionCam, Plane plane, Camera sceneCam){ + + TempVars vars = TempVars.get(); + //Temp vects for reflection cam orientation calculation + Vector3f sceneTarget = vars.vect1; + Vector3f reflectDirection = vars.vect2; + Vector3f reflectUp = vars.vect3; + Vector3f reflectLeft = vars.vect4; + Vector3f camLoc = vars.vect5; + camLoc = plane.reflect(sceneCam.getLocation(), camLoc); + reflectionCam.setLocation(camLoc); + reflectionCam.setFrustum(sceneCam.getFrustumNear(), + sceneCam.getFrustumFar(), + sceneCam.getFrustumLeft(), + sceneCam.getFrustumRight(), + sceneCam.getFrustumTop(), + sceneCam.getFrustumBottom()); + reflectionCam.setParallelProjection(sceneCam.isParallelProjection()); + + sceneTarget.set(sceneCam.getLocation()).addLocal(sceneCam.getDirection()); + reflectDirection = plane.reflect(sceneTarget, reflectDirection); + reflectDirection.subtractLocal(camLoc); + + sceneTarget.set(sceneCam.getLocation()).subtractLocal(sceneCam.getUp()); + reflectUp = plane.reflect(sceneTarget, reflectUp); + reflectUp.subtractLocal(camLoc); + + sceneTarget.set(sceneCam.getLocation()).addLocal(sceneCam.getLeft()); + reflectLeft = plane.reflect(sceneTarget, reflectLeft); + reflectLeft.subtractLocal(camLoc); + + reflectionCam.setAxes(reflectLeft, reflectUp, reflectDirection); + + vars.release(); + } +} diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java index 215b09e1a..c02f7088a 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryExporter.java @@ -327,14 +327,18 @@ public class BinaryExporter implements JmeExporter { public boolean save(Savable object, File f) throws IOException { File parentDirectory = f.getParentFile(); - if(parentDirectory != null && !parentDirectory.exists()) { + if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } FileOutputStream fos = new FileOutputStream(f); - boolean rVal = save(object, fos); - fos.close(); - return rVal; + try { + return save(object, fos); + } finally { + if (fos != null) { + fos.close(); + } + } } public BinaryOutputCapsule getCapsule(Savable object) { diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java index 47ea18e6b..933bee721 100644 --- a/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryImporter.java @@ -267,9 +267,13 @@ public final class BinaryImporter implements JmeImporter { public Savable load(File f, ReadListener listener) throws IOException { FileInputStream fis = new FileInputStream(f); - Savable rVal = load(fis, listener); - fis.close(); - return rVal; + try { + return load(fis, listener); + } finally { + if (fis != null) { + fis.close(); + } + } } public Savable load(byte[] data) throws IOException { diff --git a/engine/src/core/com/jme3/animation/AnimChannel.java b/engine/src/core/com/jme3/animation/AnimChannel.java index 7c79bae22..6a74b1864 100644 --- a/engine/src/core/com/jme3/animation/AnimChannel.java +++ b/engine/src/core/com/jme3/animation/AnimChannel.java @@ -59,6 +59,7 @@ public final class AnimChannel { private float time; private float speed; private float timeBlendFrom; + private float blendTime; private float speedBlendFrom; private boolean notified=false; @@ -192,6 +193,11 @@ public final class AnimChannel { */ public void setSpeed(float speed) { this.speed = speed; + if(blendTime>0){ + this.speedBlendFrom = speed; + blendTime = Math.min(blendTime, animation.getLength() / speed); + blendRate = 1/ blendTime; + } } /** @@ -248,7 +254,9 @@ public final class AnimChannel { control.notifyAnimChange(this, name); if (animation != null && blendTime > 0f){ + this.blendTime = blendTime; // activate blending + blendTime = Math.min(blendTime, anim.getLength() / speed); blendFrom = animation; timeBlendFrom = time; speedBlendFrom = speed; @@ -393,8 +401,7 @@ public final class AnimChannel { } animation.setTime(time, blendAmount, control, this, vars); - time += tpf * speed; - + if (animation.getLength() > 0){ if (!notified && (time >= animation.getLength() || time < 0)) { if (loopMode == LoopMode.DontLoop) { @@ -406,7 +413,7 @@ public final class AnimChannel { control.notifyAnimCycleDone(this, animation.getName()); } } - + time += tpf * speed; time = clampWrapTime(time, animation.getLength(), loopMode); if (time < 0){ // Negative time indicates that speed should be inverted diff --git a/engine/src/core/com/jme3/app/FlyCamAppState.java b/engine/src/core/com/jme3/app/FlyCamAppState.java index 5a7b11e95..11d3e3fa2 100644 --- a/engine/src/core/com/jme3/app/FlyCamAppState.java +++ b/engine/src/core/com/jme3/app/FlyCamAppState.java @@ -87,7 +87,9 @@ public class FlyCamAppState extends AbstractAppState { public void cleanup() { super.cleanup(); - flyCam.unregisterInput(); + if (app.getInputManager() != null) { + flyCam.unregisterInput(); + } } diff --git a/engine/src/core/com/jme3/app/StatsAppState.java b/engine/src/core/com/jme3/app/StatsAppState.java index 3225c5798..346733257 100644 --- a/engine/src/core/com/jme3/app/StatsAppState.java +++ b/engine/src/core/com/jme3/app/StatsAppState.java @@ -81,7 +81,7 @@ public class StatsAppState extends AbstractAppState { * is because several applications expect to directly access * fpsText... unfortunately. */ - void setFont( BitmapFont guiFont ) { + public void setFont( BitmapFont guiFont ) { this.guiFont = guiFont; this.fpsText = new BitmapText(guiFont, false); } diff --git a/engine/src/core/com/jme3/audio/AudioNode.java b/engine/src/core/com/jme3/audio/AudioNode.java index 5ee74dbd5..0e75db9fe 100644 --- a/engine/src/core/com/jme3/audio/AudioNode.java +++ b/engine/src/core/com/jme3/audio/AudioNode.java @@ -45,15 +45,18 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * An AudioNode is used in jME3 for playing audio files. - *
- * First, an {@link AudioNode} is loaded from file, and then assigned - * to an audio node for playback. Once the audio node is attached to the - * scene, its location will influence the position it is playing from relative - * to the {@link Listener}. - *
- * An audio node can also play in "headspace", meaning its location - * or velocity does not influence how it is played. + * An AudioNode is a scene Node which can play audio assets. + * + * An AudioNode is either positional or ambient, with positional being the + * default. Once a positional node is attached to the scene, its location and + * velocity relative to the {@link Listener} affect how it sounds when played. + * Positional nodes can only play monoaural (single-channel) assets, not stereo + * ones. + * + * An ambient AudioNode plays in "headspace", meaning that the node's location + * and velocity do not affect how it sounds when played. Ambient audio nodes can + * play stereo assets. + * * The "positional" property of an AudioNode can be set via * {@link AudioNode#setPositional(boolean) }. * diff --git a/engine/src/core/com/jme3/bounding/BoundingBox.java b/engine/src/core/com/jme3/bounding/BoundingBox.java index 5cf7d83a6..4768b3db8 100644 --- a/engine/src/core/com/jme3/bounding/BoundingBox.java +++ b/engine/src/core/com/jme3/bounding/BoundingBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2013 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,31 +47,40 @@ import java.nio.FloatBuffer; //import com.jme.scene.TriMesh; /** - * BoundingBox defines an axis-aligned cube that defines a - * container for a group of vertices of a particular piece of geometry. This box - * defines a center and extents from that center along the x, y and z axis.
+ * BoundingBox describes a bounding volume as an axis-aligned box. *
- * A typical usage is to allow the class define the center and radius by calling - * either containAABB or averagePoints. A call to - * computeFramePoint in turn calls containAABB. - * + * Instances may be initialized by invoking the containAABB method. + * * @author Joshua Slack * @version $Id: BoundingBox.java,v 1.50 2007/09/22 16:46:35 irrisor Exp $ */ public class BoundingBox extends BoundingVolume { - - float xExtent, yExtent, zExtent; - /** - * Default constructor instantiates a new BoundingBox - * object. + * the X-extent of the box (>=0, may be +Infinity) + */ + float xExtent; + /** + * the Y-extent of the box (>=0, may be +Infinity) + */ + float yExtent; + /** + * the Z-extent of the box (>=0, may be +Infinity) + */ + float zExtent; + + /** + * Instantiate a BoundingBox without initializing it. */ public BoundingBox() { } /** - * Contstructor instantiates a new BoundingBox object with - * given specs. + * Instantiate a BoundingBox with given center and extents. + * + * @param c the coordinates of the center of the box (not null, not altered) + * @param x the X-extent of the box (>=0, may be +Infinity) + * @param y the Y-extent of the box (>=0, may be +Infinity) + * @param z the Z-extent of the box (>=0, may be +Infinity) */ public BoundingBox(Vector3f c, float x, float y, float z) { this.center.set(c); @@ -80,6 +89,11 @@ public class BoundingBox extends BoundingVolume { this.zExtent = z; } + /** + * Instantiate a BoundingBox equivalent to an existing box. + * + * @param source the existing box (not null, not altered) + */ public BoundingBox(BoundingBox source) { this.center.set(source.center); this.xExtent = source.xExtent; @@ -368,52 +382,28 @@ public class BoundingBox extends BoundingVolume { } /** - * merge combines this bounding box with a second bounding box. - * This new box contains both bounding box and is returned. - * - * @param volume - * the bounding box to combine with this bounding box. - * @return the new bounding box + * merge combines this bounding box locally with a second + * bounding volume. The result contains both the original box and the second + * volume. + * + * @param volume the bounding volume to combine with this box (or null) (not + * altered) + * @return this box (with its components modified) or null if the second + * volume is of some type other than AABB or Sphere */ public BoundingVolume merge(BoundingVolume volume) { - if (volume == null) { - return this; - } - - switch (volume.getType()) { - case AABB: { - BoundingBox vBox = (BoundingBox) volume; - return merge(vBox.center, vBox.xExtent, vBox.yExtent, - vBox.zExtent, new BoundingBox(new Vector3f(0, 0, 0), 0, - 0, 0)); - } - - case Sphere: { - BoundingSphere vSphere = (BoundingSphere) volume; - return merge(vSphere.center, vSphere.radius, vSphere.radius, - vSphere.radius, new BoundingBox(new Vector3f(0, 0, 0), - 0, 0, 0)); - } - -// case OBB: { -// OrientedBoundingBox box = (OrientedBoundingBox) volume; -// BoundingBox rVal = (BoundingBox) this.clone(null); -// return rVal.mergeOBB(box); -// } - - default: - return null; - } + return mergeLocal(volume); } /** - * mergeLocal combines this sphere with a second bounding - * sphere locally. Altering this sphere to contain both the original and the - * additional sphere volumes; - * - * @param volume - * the sphere to combine with this sphere. - * @return this + * mergeLocal combines this bounding box locally with a second + * bounding volume. The result contains both the original box and the second + * volume. + * + * @param volume the bounding volume to combine with this box (or null) (not + * altered) + * @return this box (with its components modified) or null if the second + * volume is of some type other than AABB or Sphere */ public BoundingVolume mergeLocal(BoundingVolume volume) { if (volume == null) { @@ -421,17 +411,15 @@ public class BoundingBox extends BoundingVolume { } switch (volume.getType()) { - case AABB: { + case AABB: BoundingBox vBox = (BoundingBox) volume; - return merge(vBox.center, vBox.xExtent, vBox.yExtent, - vBox.zExtent, this); - } + return mergeLocal(vBox.center, vBox.xExtent, vBox.yExtent, + vBox.zExtent); - case Sphere: { + case Sphere: BoundingSphere vSphere = (BoundingSphere) volume; - return merge(vSphere.center, vSphere.radius, vSphere.radius, - vSphere.radius, this); - } + return mergeLocal(vSphere.center, vSphere.radius, + vSphere.radius, vSphere.radius); // case OBB: { // return mergeOBB((OrientedBoundingBox) volume); @@ -486,61 +474,68 @@ public class BoundingBox extends BoundingVolume { // return this; // } /** - * merge combines this bounding box with another box which is - * defined by the center, x, y, z extents. - * - * @param boxCenter - * the center of the box to merge with - * @param boxX - * the x extent of the box to merge with. - * @param boxY - * the y extent of the box to merge with. - * @param boxZ - * the z extent of the box to merge with. - * @param rVal - * the resulting merged box. + * mergeLocal combines this bounding box locally with a second + * bounding box described by its center and extents. + * + * @param c the center of the second box (not null, not altered) + * @param x the X-extent of the second box + * @param y the Y-extent of the second box + * @param z the Z-extent of the second box * @return the resulting merged box. */ - private BoundingBox merge(Vector3f boxCenter, float boxX, float boxY, - float boxZ, BoundingBox rVal) { - - TempVars vars = TempVars.get(); - - vars.vect1.x = center.x - xExtent; - if (vars.vect1.x > boxCenter.x - boxX) { - vars.vect1.x = boxCenter.x - boxX; - } - vars.vect1.y = center.y - yExtent; - if (vars.vect1.y > boxCenter.y - boxY) { - vars.vect1.y = boxCenter.y - boxY; - } - vars.vect1.z = center.z - zExtent; - if (vars.vect1.z > boxCenter.z - boxZ) { - vars.vect1.z = boxCenter.z - boxZ; + private BoundingBox mergeLocal(Vector3f c, float x, float y, float z) { + if (xExtent == Float.POSITIVE_INFINITY + || x == Float.POSITIVE_INFINITY) { + center.x = 0; + xExtent = Float.POSITIVE_INFINITY; + } else { + float low = center.x - xExtent; + if (low > c.x - x) { + low = c.x - x; + } + float high = center.x + xExtent; + if (high < c.x + x) { + high = c.x + x; + } + center.x = (low + high) / 2; + xExtent = high - center.x; } - vars.vect2.x = center.x + xExtent; - if (vars.vect2.x < boxCenter.x + boxX) { - vars.vect2.x = boxCenter.x + boxX; - } - vars.vect2.y = center.y + yExtent; - if (vars.vect2.y < boxCenter.y + boxY) { - vars.vect2.y = boxCenter.y + boxY; - } - vars.vect2.z = center.z + zExtent; - if (vars.vect2.z < boxCenter.z + boxZ) { - vars.vect2.z = boxCenter.z + boxZ; + if (yExtent == Float.POSITIVE_INFINITY + || y == Float.POSITIVE_INFINITY) { + center.y = 0; + yExtent = Float.POSITIVE_INFINITY; + } else { + float low = center.y - yExtent; + if (low > c.y - y) { + low = c.y - y; + } + float high = center.y + yExtent; + if (high < c.y + y) { + high = c.y + y; + } + center.y = (low + high) / 2; + yExtent = high - center.y; } - center.set(vars.vect2).addLocal(vars.vect1).multLocal(0.5f); - - xExtent = vars.vect2.x - center.x; - yExtent = vars.vect2.y - center.y; - zExtent = vars.vect2.z - center.z; - - vars.release(); + if (zExtent == Float.POSITIVE_INFINITY + || z == Float.POSITIVE_INFINITY) { + center.z = 0; + zExtent = Float.POSITIVE_INFINITY; + } else { + float low = center.z - zExtent; + if (low > c.z - z) { + low = c.z - z; + } + float high = center.z + zExtent; + if (high < c.z + z) { + high = c.z + z; + } + center.z = (low + high) / 2; + zExtent = high - center.z; + } - return rVal; + return this; } /** @@ -570,8 +565,9 @@ public class BoundingBox extends BoundingVolume { /** * toString returns the string representation of this object. - * The form is: "Radius: RRR.SSSS Center: ". - * + * The form is: "[Center: xExtent: X.XX yExtent: Y.YY zExtent: + * Z.ZZ]". + * * @return the string representation of this. */ @Override @@ -985,4 +981,4 @@ public class BoundingBox extends BoundingVolume { public float getVolume() { return (8 * xExtent * yExtent * zExtent); } -} +} \ No newline at end of file diff --git a/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java index a68b84e4a..8acd12708 100644 --- a/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java +++ b/engine/src/core/com/jme3/cinematic/events/AbstractCinematicEvent.java @@ -44,34 +44,35 @@ import java.util.ArrayList; import java.util.List; /** - * This calls contains basic behavior of a cinematic event - * every cinematic event must extend this class + * This call contains the basic behaviour of a cinematic event. + * Every cinematic event must extend this class. * - * A cinematic event must be given an inital duration in seconds (duration of the event at speed = 1) (default is 10) + * A cinematic event must be given an inital duration in seconds + * (duration of the event at speed = 1). Default is 10 sec. * @author Nehon */ public abstract class AbstractCinematicEvent implements CinematicEvent { protected PlayState playState = PlayState.Stopped; - protected float speed = 1; - protected float initialDuration = 10; protected LoopMode loopMode = LoopMode.DontLoop; + protected float initialDuration = 10; + protected float speed = 1; protected float time = 0; protected boolean resuming = false; /** - * the list of listeners + * The list of listeners. */ protected List listeners; /** - * contruct a cinematic event + * Contruct a cinematic event (empty constructor). */ public AbstractCinematicEvent() { } /** - * contruct a cinematic event wwith the given initial duration + * Contruct a cinematic event with the given initial duration. * @param initialDuration */ public AbstractCinematicEvent(float initialDuration) { @@ -79,7 +80,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * contruct a cinematic event with the given loopMode + * Contruct a cinematic event with the given loopMode. * @param loopMode */ public AbstractCinematicEvent(LoopMode loopMode) { @@ -87,9 +88,9 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * contruct a cinematic event with the given loopMode and the given initialDuration - * @param initialDuration the duration of the event at speed = 1 - * @param loopMode the loop mode of the event + * Contruct a cinematic event with the given loopMode and the given initialDuration. + * @param initialDuration the duration of the event at speed = 1. + * @param loopMode the loop mode of the event. */ public AbstractCinematicEvent(float initialDuration, LoopMode loopMode) { this.initialDuration = initialDuration; @@ -97,17 +98,17 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * this method can be implemented if the event needs different handling when - * stopped naturally (when the event reach its end) - * or when it was forced stopped during playback - * otherwise it just call regular stop() + * Implement this method if the event needs different handling when + * stopped naturally (when the event reach its end), + * or when it was force-stopped during playback. + * By default, this method just calls regular stop(). */ public void forceStop(){ stop(); } /** - * Play this event + * Play this event. */ public void play() { onPlay(); @@ -121,13 +122,13 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Place here the code you want to execute when the event is started + * Implement this method with code that you want to execute when the event is started. */ protected abstract void onPlay(); /** - * should be used internally only - * @param tpf time per frame + * Used internally only. + * @param tpf time per frame. */ public void internalUpdate(float tpf) { if (playState == PlayState.Playing) { @@ -135,7 +136,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { onUpdate(tpf); if (time >= initialDuration && loopMode == LoopMode.DontLoop) { stop(); - }else if(time >= initialDuration && loopMode == LoopMode.Loop){ + } else if(time >= initialDuration && loopMode == LoopMode.Loop){ setTime(0); } } @@ -143,13 +144,15 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Place here the code you want to execute on update (only called when the event is playing) + * Implement this method with the code that you want to execute on update + * (only called when the event is playing). * @param tpf time per frame */ protected abstract void onUpdate(float tpf); /** - * stops the animation, next time play() is called the animation will start from the begining. + * Stops the animation. + * Next time when play() is called, the animation starts from the beginning. */ public void stop() { onStop(); @@ -164,12 +167,13 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Place here the code you want to execute when the event is stoped. + * Implement this method with code that you want to execute when the event is stopped. */ protected abstract void onStop(); /** - * pause this event + * Pause this event. + * Next time when play() is called, the animation restarts from here. */ public void pause() { onPause(); @@ -183,12 +187,12 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * place here the code you want to execute when the event is paused + * Implement this method with code that you want to execute when the event is paused. */ public abstract void onPause(); /** - * returns the actual duration of the animtion (initialDuration/speed) + * Returns the actual duration of the animtion (initialDuration/speed) * @return */ public float getDuration() { @@ -198,7 +202,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { /** * Sets the speed of the animation. * At speed = 1, the animation will last initialDuration seconds, - * At speed = 2 the animation will last initialDuraiton/2... + * At speed = 2, the animation will last initialDuration/2... * @param speed */ public void setSpeed(float speed) { @@ -206,7 +210,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * returns the speed of the animation. + * Returns the speed of the animation. * @return */ public float getSpeed() { @@ -214,7 +218,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Returns the current playstate of the animation + * Returns the current playstate of the animation (playing or paused or stopped). * @return */ public PlayState getPlayState() { @@ -222,7 +226,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * returns the initial duration of the animation at speed = 1 in seconds. + * Returns the initial duration of the animation at speed = 1 in seconds. * @return */ public float getInitialDuration() { @@ -230,7 +234,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Sets the duration of the antionamtion at speed = 1 in seconds + * Sets the duration of the animation at speed = 1 in seconds. * @param initialDuration */ public void setInitialDuration(float initialDuration) { @@ -238,7 +242,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * retursthe loopMode of the animation + * Returns the loopMode of the animation. * @see LoopMode * @return */ @@ -247,7 +251,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Sets the loopMode of the animation + * Sets the loopMode of the animation. * @see LoopMode * @param loopMode */ @@ -256,7 +260,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * for serialization only + * Used for serialization only. * @param ex exporter * @throws IOException */ @@ -269,7 +273,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * for serialization only + * Used for serialization only. * @param im importer * @throws IOException */ @@ -282,7 +286,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * initialize this event (should be called internally only) + * Initialize this event (called internally only). * @param app * @param cinematic */ @@ -290,7 +294,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * return a list of CinematicEventListener added on this event + * Returns the list of CinematicEventListeners added to this event. * @return */ private List getListeners() { @@ -301,7 +305,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * Add a CinematicEventListener to this event + * Add a CinematicEventListener to this event. * @param listener CinematicEventListener */ public void addListener(CinematicEventListener listener) { @@ -309,7 +313,7 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * remove a CinematicEventListener from this event + * Remove a CinematicEventListener from this event. * @param listener CinematicEventListener */ public void removeListener(CinematicEventListener listener) { @@ -317,13 +321,16 @@ public abstract class AbstractCinematicEvent implements CinematicEvent { } /** - * When this method is invoked, the event should fast forward to the given time according tim 0 is the start of the event. - * @param time the time to fast forward to + * Fast-forward the event to the given timestamp. Time=0 is the start of the event. + * @param time the time to fast forward to. */ public void setTime(float time) { this.time = time ; } + /** + * Return the current timestamp of the event. Time=0 is the start of the event. + */ public float getTime() { return time; } diff --git a/engine/src/core/com/jme3/input/FlyByCamera.java b/engine/src/core/com/jme3/input/FlyByCamera.java index 04b323f95..b759833d9 100644 --- a/engine/src/core/com/jme3/input/FlyByCamera.java +++ b/engine/src/core/com/jme3/input/FlyByCamera.java @@ -341,7 +341,11 @@ public class FlyByCamera implements AnalogListener, ActionListener { float fovY = FastMath.atan(h / near) / (FastMath.DEG_TO_RAD * .5f); - fovY += value * 0.1f * zoomSpeed; + float newFovY = fovY + value * 0.1f * zoomSpeed; + if (newFovY > 0f) { + // Don't let the FOV go zero or negative. + fovY = newFovY; + } h = FastMath.tan( fovY * FastMath.DEG_TO_RAD * .5f) * near; w = h * aspect; diff --git a/engine/src/core/com/jme3/input/KeyNames.java b/engine/src/core/com/jme3/input/KeyNames.java index cca929d83..a1e1a41a8 100644 --- a/engine/src/core/com/jme3/input/KeyNames.java +++ b/engine/src/core/com/jme3/input/KeyNames.java @@ -149,7 +149,7 @@ public class KeyNames { KEY_NAMES[KEY_BACK] = "Backspace"; KEY_NAMES[KEY_TAB] = "Tab"; - KEY_NAMES[KEY_SYSRQ] = "SysEq"; + KEY_NAMES[KEY_SYSRQ] = "SysRq"; KEY_NAMES[KEY_PAUSE] = "Pause"; KEY_NAMES[KEY_HOME] = "Home"; diff --git a/engine/src/core/com/jme3/light/DirectionalLight.java b/engine/src/core/com/jme3/light/DirectionalLight.java index 1830f44e1..cb65258e7 100644 --- a/engine/src/core/com/jme3/light/DirectionalLight.java +++ b/engine/src/core/com/jme3/light/DirectionalLight.java @@ -69,8 +69,8 @@ public class DirectionalLight extends Light { /** * Sets the direction of the light. *

- * Represents the vector direction the light is coming from. - * (1, 0, 0) would represent a directional light coming from the X axis. + * Represents the direction the light is shining. + * (1, 0, 0) would represent light shining in the +X direction. * * @param dir the direction of the light. */ diff --git a/engine/src/core/com/jme3/light/SpotLight.java b/engine/src/core/com/jme3/light/SpotLight.java index 04e6ca7d4..f566f0dc7 100644 --- a/engine/src/core/com/jme3/light/SpotLight.java +++ b/engine/src/core/com/jme3/light/SpotLight.java @@ -76,8 +76,7 @@ public class SpotLight extends Light implements Savable { if(((int)packedAngleCos)== ((int)(outerCos*1000)) ){ outerCos -= 0.001f; } - packedAngleCos+=outerCos; - System.out.println("anfle"+ packedAngleCos); + packedAngleCos+=outerCos; } @Override diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java index 8c9b81ba0..06521780e 100644 --- a/engine/src/core/com/jme3/post/FilterPostProcessor.java +++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java @@ -312,11 +312,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { } } else { - if (renderFrameBufferMS != null) { - viewPort.setOutputFrameBuffer(renderFrameBufferMS); - } else { - viewPort.setOutputFrameBuffer(renderFrameBuffer); - } + setupViewPortFrameBuffer(); //init of the camera if it wasn't already if (!cameraInit) { viewPort.getCamera().resize(width, height, true); @@ -353,11 +349,20 @@ public class FilterPostProcessor implements SceneProcessor, Savable { for (int i = filters.size() - 1; i >= 0 && lastFilterIndex == -1; i--) { if (filters.get(i).isEnabled()) { lastFilterIndex = i; + //the Fpp is initialized, but the viwport framebuffer is the + //original out framebuffer so we must recover from a situation + //where no filter was enabled. So we set th correc framebuffer + //on the viewport + if(isInitialized() && viewPort.getOutputFrameBuffer()==outputBuffer){ + setupViewPortFrameBuffer(); + } return; } } if (lastFilterIndex == -1) { - cleanup(); + //There is no enabled filter, we restore the original framebuffer + //to the viewport to bypass the fpp. + viewPort.setOutputFrameBuffer(outputBuffer); } } @@ -441,12 +446,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { Filter filter = it.next(); initFilter(filter, vp); } - - if (renderFrameBufferMS != null) { - viewPort.setOutputFrameBuffer(renderFrameBufferMS); - } else { - viewPort.setOutputFrameBuffer(renderFrameBuffer); - } + setupViewPortFrameBuffer(); } /** @@ -543,4 +543,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable { public List getFilterList(){ return Collections.unmodifiableList(filters); } + + private void setupViewPortFrameBuffer() { + if (renderFrameBufferMS != null) { + viewPort.setOutputFrameBuffer(renderFrameBufferMS); + } else { + viewPort.setOutputFrameBuffer(renderFrameBuffer); + } + } } diff --git a/engine/src/core/com/jme3/renderer/Camera.java b/engine/src/core/com/jme3/renderer/Camera.java index a714d7a6c..74da04671 100644 --- a/engine/src/core/com/jme3/renderer/Camera.java +++ b/engine/src/core/com/jme3/renderer/Camera.java @@ -320,33 +320,33 @@ public class Camera implements Savable, Cloneable { this.width = cam.width; this.height = cam.height; - this.planeState = cam.planeState; - this.viewportChanged = cam.viewportChanged; + this.planeState = 0; + this.viewportChanged = true; for (int i = 0; i < MAX_WORLD_PLANES; ++i) { worldPlane[i].setNormal(cam.worldPlane[i].getNormal()); worldPlane[i].setConstant(cam.worldPlane[i].getConstant()); } this.parallelProjection = cam.parallelProjection; - if(cam.projectionMatrixOverride != null) { - if(this.projectionMatrixOverride == null) { - this.projectionMatrixOverride = cam.projectionMatrixOverride.clone(); - } else { - this.projectionMatrixOverride.set(cam.projectionMatrixOverride); - } + if (cam.projectionMatrixOverride != null) { + if (this.projectionMatrixOverride == null) { + this.projectionMatrixOverride = cam.projectionMatrixOverride.clone(); + } else { + this.projectionMatrixOverride.set(cam.projectionMatrixOverride); + } } else { - this.projectionMatrixOverride = null; + this.projectionMatrixOverride = null; } this.viewMatrix.set(cam.viewMatrix); this.projectionMatrix.set(cam.projectionMatrix); this.viewProjectionMatrix.set(cam.viewProjectionMatrix); - + this.guiBounding.setXExtent(cam.guiBounding.getXExtent()); this.guiBounding.setYExtent(cam.guiBounding.getYExtent()); this.guiBounding.setZExtent(cam.guiBounding.getZExtent()); this.guiBounding.setCenter(cam.guiBounding.getCenter()); this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane()); - + this.name = cam.name; } diff --git a/engine/src/core/com/jme3/renderer/RenderContext.java b/engine/src/core/com/jme3/renderer/RenderContext.java index 67023e4f8..0aac09fe5 100644 --- a/engine/src/core/com/jme3/renderer/RenderContext.java +++ b/engine/src/core/com/jme3/renderer/RenderContext.java @@ -55,10 +55,15 @@ public class RenderContext { public boolean depthTestEnabled = false; /** - * @see RenderState#setAlphaTest(boolean) + * @see RenderState#setAlphaFallOff(float) */ public float alphaTestFallOff = 0f; + /** + * @see RenderState#setAlphaTest(boolean) + */ + public boolean alphaTestEnabled = false; + /** * @see RenderState#setDepthWrite(boolean) */ diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index 656c8f8c2..aec62c01e 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java @@ -754,4 +754,24 @@ public class BatchNode extends Node implements Savable { public int getOffsetIndex(Geometry batchedGeometry) { return batchedGeometry.startIndex; } + + @Override + public Node clone(boolean cloneMaterials) { + BatchNode clone = (BatchNode)super.clone(cloneMaterials); + if ( batches.size() > 0) { + for ( Batch b : batches ) { + for ( int i =0; i < clone.children.size(); i++ ) { + if ( clone.children.get(i).getName().equals(b.geometry.getName())) { + clone.children.remove(i); + break; + } + } + } + clone.needsFullRebatch = true; + clone.batches.clear(); + clone.batchesByGeom.clear(); + clone.batch(); + } + return clone; + } } diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java index 1d567cdde..afb223e7e 100644 --- a/engine/src/core/com/jme3/scene/Mesh.java +++ b/engine/src/core/com/jme3/scene/Mesh.java @@ -240,8 +240,8 @@ public class Mesh implements Savable, Cloneable { } clone.vertexArrayID = -1; - clone.vertCount = -1; - clone.elementCount = -1; + clone.vertCount = vertCount; + clone.elementCount = elementCount; // although this could change // if the bone weight/index buffers are modified diff --git a/engine/src/core/com/jme3/scene/Node.java b/engine/src/core/com/jme3/scene/Node.java index 37477733c..b5bd45b10 100644 --- a/engine/src/core/com/jme3/scene/Node.java +++ b/engine/src/core/com/jme3/scene/Node.java @@ -229,7 +229,7 @@ public class Node extends Spatial implements Savable { * @param child * the child to attach to this node. * @return the number of children maintained by this node. - * @throws NullPointerException If child is null. + * @throws IllegalArgumentException if child is null. */ public int attachChild(Spatial child) { if (child == null) diff --git a/engine/src/core/com/jme3/scene/Spatial.java b/engine/src/core/com/jme3/scene/Spatial.java index 75490b04f..7562a9140 100644 --- a/engine/src/core/com/jme3/scene/Spatial.java +++ b/engine/src/core/com/jme3/scene/Spatial.java @@ -1,1512 +1,1515 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.scene; - -import com.jme3.asset.AssetKey; -import com.jme3.asset.CloneableSmartAsset; -import com.jme3.bounding.BoundingVolume; -import com.jme3.collision.Collidable; -import com.jme3.export.*; -import com.jme3.light.Light; -import com.jme3.light.LightList; -import com.jme3.material.Material; -import com.jme3.math.*; -import com.jme3.renderer.Camera; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; -import com.jme3.scene.control.Control; -import com.jme3.util.SafeArrayList; -import com.jme3.util.TempVars; -import java.io.IOException; -import java.util.*; -import java.util.logging.Logger; - -/** - * Spatial defines the base class for scene graph nodes. It - * maintains a link to a parent, it's local transforms and the world's - * transforms. All other scene graph elements, such as {@link Node} and - * {@link Geometry} are subclasses of Spatial. - * - * @author Mark Powell - * @author Joshua Slack - * @version $Revision: 4075 $, $Data$ - */ -public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset { - - private static final Logger logger = Logger.getLogger(Spatial.class.getName()); - - /** - * Specifies how frustum culling should be handled by - * this spatial. - */ - public enum CullHint { - - /** - * Do whatever our parent does. If no parent, default to {@link #Dynamic}. - */ - Inherit, - /** - * Do not draw if we are not at least partially within the view frustum - * of the camera. This is determined via the defined - * Camera planes whether or not this Spatial should be culled. - */ - Dynamic, - /** - * Always cull this from the view, throwing away this object - * and any children from rendering commands. - */ - Always, - /** - * Never cull this from view, always draw it. - * Note that we will still get culled if our parent is culled. - */ - Never; - } - - /** - * Specifies if this spatial should be batched - */ - public enum BatchHint { - - /** - * Do whatever our parent does. If no parent, default to {@link #Always}. - */ - Inherit, - /** - * This spatial will always be batched when attached to a BatchNode. - */ - Always, - /** - * This spatial will never be batched when attached to a BatchNode. - */ - Never; - } - /** - * Refresh flag types - */ - protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms - RF_BOUND = 0x02, - RF_LIGHTLIST = 0x04; // changes in light lists - - protected CullHint cullHint = CullHint.Inherit; - protected BatchHint batchHint = BatchHint.Inherit; - /** - * Spatial's bounding volume relative to the world. - */ - protected BoundingVolume worldBound; - /** - * LightList - */ - protected LightList localLights; - protected transient LightList worldLights; - /** - * This spatial's name. - */ - protected String name; - // scale values - protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects; - protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit; - protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit; - public transient float queueDistance = Float.NEGATIVE_INFINITY; - protected Transform localTransform; - protected Transform worldTransform; - protected SafeArrayList controls = new SafeArrayList(Control.class); - protected HashMap userData = null; - /** - * Used for smart asset caching - * - * @see AssetKey#useSmartCache() - */ - protected AssetKey key; - /** - * Spatial's parent, or null if it has none. - */ - protected transient Node parent; - /** - * Refresh flags. Indicate what data of the spatial need to be - * updated to reflect the correct state. - */ - protected transient int refreshFlags = 0; - - /** - * Serialization only. Do not use. - */ - public Spatial() { - localTransform = new Transform(); - worldTransform = new Transform(); - - localLights = new LightList(this); - worldLights = new LightList(this); - - refreshFlags |= RF_BOUND; - } - - /** - * Constructor instantiates a new Spatial object setting the - * rotation, translation and scale value to defaults. - * - * @param name - * the name of the scene element. This is required for - * identification and comparison purposes. - */ - public Spatial(String name) { - this(); - this.name = name; - } - - public void setKey(AssetKey key) { - this.key = key; - } - - public AssetKey getKey() { - return key; - } - - /** - * Indicate that the transform of this spatial has changed and that - * a refresh is required. - */ - protected void setTransformRefresh() { - refreshFlags |= RF_TRANSFORM; - setBoundRefresh(); - } - - protected void setLightListRefresh() { - refreshFlags |= RF_LIGHTLIST; - } - - /** - * Indicate that the bounding of this spatial has changed and that - * a refresh is required. - */ - protected void setBoundRefresh() { - refreshFlags |= RF_BOUND; - - Spatial p = parent; - while (p != null) { - if ((p.refreshFlags & RF_BOUND) != 0) { - return; - } - - p.refreshFlags |= RF_BOUND; - p = p.parent; - } - } - - /** - * (Internal use only) Forces a refresh of the given types of data. - * - * @param transforms Refresh world transform based on parents' - * @param bounds Refresh bounding volume data based on child nodes - * @param lights Refresh light list based on parents' - */ - public void forceRefresh(boolean transforms, boolean bounds, boolean lights) { - if (transforms) { - setTransformRefresh(); - } - if (bounds) { - setBoundRefresh(); - } - if (lights) { - setLightListRefresh(); - } - } - - /** - * checkCulling checks the spatial with the camera to see if it - * should be culled. - *

- * This method is called by the renderer. Usually it should not be called - * directly. - * - * @param cam The camera to check against. - * @return true if inside or intersecting camera frustum - * (should be rendered), false if outside. - */ - public boolean checkCulling(Camera cam) { - if (refreshFlags != 0) { - throw new IllegalStateException("Scene graph is not properly updated for rendering.\n" - + "State was changed after rootNode.updateGeometricState() call. \n" - + "Make sure you do not modify the scene from another thread!\n" - + "Problem spatial name: " + getName()); - } - - CullHint cm = getCullHint(); - assert cm != CullHint.Inherit; - if (cm == Spatial.CullHint.Always) { - setLastFrustumIntersection(Camera.FrustumIntersect.Outside); - return false; - } else if (cm == Spatial.CullHint.Never) { - setLastFrustumIntersection(Camera.FrustumIntersect.Intersects); - return true; - } - - // check to see if we can cull this node - frustrumIntersects = (parent != null ? parent.frustrumIntersects - : Camera.FrustumIntersect.Intersects); - - if (frustrumIntersects == Camera.FrustumIntersect.Intersects) { - if (getQueueBucket() == Bucket.Gui) { - return cam.containsGui(getWorldBound()); - } else { - frustrumIntersects = cam.contains(getWorldBound()); - } - } - - return frustrumIntersects != Camera.FrustumIntersect.Outside; - } - - /** - * Sets the name of this spatial. - * - * @param name - * The spatial's new name. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Returns the name of this spatial. - * - * @return This spatial's name. - */ - public String getName() { - return name; - } - - /** - * Returns the local {@link LightList}, which are the lights - * that were directly attached to this Spatial through the - * {@link #addLight(com.jme3.light.Light) } and - * {@link #removeLight(com.jme3.light.Light) } methods. - * - * @return The local light list - */ - public LightList getLocalLightList() { - return localLights; - } - - /** - * Returns the world {@link LightList}, containing the lights - * combined from all this Spatial's parents up to and including - * this Spatial's lights. - * - * @return The combined world light list - */ - public LightList getWorldLightList() { - return worldLights; - } - - /** - * getWorldRotation retrieves the absolute rotation of the - * Spatial. - * - * @return the Spatial's world rotation quaternion. - */ - public Quaternion getWorldRotation() { - checkDoTransformUpdate(); - return worldTransform.getRotation(); - } - - /** - * getWorldTranslation retrieves the absolute translation of - * the spatial. - * - * @return the Spatial's world tranlsation vector. - */ - public Vector3f getWorldTranslation() { - checkDoTransformUpdate(); - return worldTransform.getTranslation(); - } - - /** - * getWorldScale retrieves the absolute scale factor of the - * spatial. - * - * @return the Spatial's world scale factor. - */ - public Vector3f getWorldScale() { - checkDoTransformUpdate(); - return worldTransform.getScale(); - } - - /** - * getWorldTransform retrieves the world transformation - * of the spatial. - * - * @return the world transform. - */ - public Transform getWorldTransform() { - checkDoTransformUpdate(); - return worldTransform; - } - - /** - * rotateUpTo is a utility function that alters the - * local rotation to point the Y axis in the direction given by newUp. - * - * @param newUp - * the up vector to use - assumed to be a unit vector. - */ - public void rotateUpTo(Vector3f newUp) { - TempVars vars = TempVars.get(); - - Vector3f compVecA = vars.vect1; - Quaternion q = vars.quat1; - - // First figure out the current up vector. - Vector3f upY = compVecA.set(Vector3f.UNIT_Y); - Quaternion rot = localTransform.getRotation(); - rot.multLocal(upY); - - // get angle between vectors - float angle = upY.angleBetween(newUp); - - // figure out rotation axis by taking cross product - Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal(); - - // Build a rotation quat and apply current local rotation. - q.fromAngleNormalAxis(angle, rotAxis); - q.mult(rot, rot); - - vars.release(); - - setTransformRefresh(); - } - - /** - * lookAt is a convenience method for auto-setting the local - * rotation based on a position in world space and an up vector. It computes the rotation - * to transform the z-axis to point onto 'position' and the y-axis to 'up'. - * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) } - * this method takes a world position to look at and not a relative direction. - * - * Note : 28/01/2013 this method has been fixed as it was not taking into account the parent rotation. - * This was resulting in improper rotation when the spatial had rotated parent nodes. - * This method is intended to work in world space, so no matter what parent graph the - * spatial has, it will look at the given position in world space. - * - * @param position - * where to look at in terms of world coordinates - * @param upVector - * a vector indicating the (local) up direction. (typically {0, - * 1, 0} in jME.) - */ - public void lookAt(Vector3f position, Vector3f upVector) { - Vector3f worldTranslation = getWorldTranslation(); - - TempVars vars = TempVars.get(); - - Vector3f compVecA = vars.vect4; - - compVecA.set(position).subtractLocal(worldTranslation); - getLocalRotation().lookAt(compVecA, upVector); - - if ( getParent() != null ) { - Quaternion rot=vars.quat1; - rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation()); - rot.normalizeLocal(); - setLocalRotation(rot); - } - vars.release(); - setTransformRefresh(); - } - - /** - * Should be overridden by Node and Geometry. - */ - protected void updateWorldBound() { - // the world bound of a leaf is the same as it's model bound - // for a node, the world bound is a combination of all it's children - // bounds - // -> handled by subclass - refreshFlags &= ~RF_BOUND; - } - - protected void updateWorldLightList() { - if (parent == null) { - worldLights.update(localLights, null); - refreshFlags &= ~RF_LIGHTLIST; - } else { - if ((parent.refreshFlags & RF_LIGHTLIST) == 0) { - worldLights.update(localLights, parent.worldLights); - refreshFlags &= ~RF_LIGHTLIST; - } else { - assert false; - } - } - } - - /** - * Should only be called from updateGeometricState(). - * In most cases should not be subclassed. - */ - protected void updateWorldTransforms() { - if (parent == null) { - worldTransform.set(localTransform); - refreshFlags &= ~RF_TRANSFORM; - } else { - // check if transform for parent is updated - assert ((parent.refreshFlags & RF_TRANSFORM) == 0); - worldTransform.set(localTransform); - worldTransform.combineWithParent(parent.worldTransform); - refreshFlags &= ~RF_TRANSFORM; - } - } - - /** - * Computes the world transform of this Spatial in the most - * efficient manner possible. - */ - void checkDoTransformUpdate() { - if ((refreshFlags & RF_TRANSFORM) == 0) { - return; - } - - if (parent == null) { - worldTransform.set(localTransform); - refreshFlags &= ~RF_TRANSFORM; - } else { - TempVars vars = TempVars.get(); - - Spatial[] stack = vars.spatialStack; - Spatial rootNode = this; - int i = 0; - while (true) { - Spatial hisParent = rootNode.parent; - if (hisParent == null) { - rootNode.worldTransform.set(rootNode.localTransform); - rootNode.refreshFlags &= ~RF_TRANSFORM; - i--; - break; - } - - stack[i] = rootNode; - - if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) { - break; - } - - rootNode = hisParent; - i++; - } - - vars.release(); - - for (int j = i; j >= 0; j--) { - rootNode = stack[j]; - //rootNode.worldTransform.set(rootNode.localTransform); - //rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform); - //rootNode.refreshFlags &= ~RF_TRANSFORM; - rootNode.updateWorldTransforms(); - } - } - } - - /** - * Computes this Spatial's world bounding volume in the most efficient - * manner possible. - */ - void checkDoBoundUpdate() { - if ((refreshFlags & RF_BOUND) == 0) { - return; - } - - checkDoTransformUpdate(); - - // Go to children recursively and update their bound - if (this instanceof Node) { - Node node = (Node) this; - int len = node.getQuantity(); - for (int i = 0; i < len; i++) { - Spatial child = node.getChild(i); - child.checkDoBoundUpdate(); - } - } - - // All children's bounds have been updated. Update my own now. - updateWorldBound(); - } - - private void runControlUpdate(float tpf) { - if (controls.isEmpty()) { - return; - } - - for (Control c : controls.getArray()) { - c.update(tpf); - } - } - - /** - * Called when the Spatial is about to be rendered, to notify - * controls attached to this Spatial using the Control.render() method. - * - * @param rm The RenderManager rendering the Spatial. - * @param vp The ViewPort to which the Spatial is being rendered to. - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - * @see Spatial#getControl(java.lang.Class) - */ - public void runControlRender(RenderManager rm, ViewPort vp) { - if (controls.isEmpty()) { - return; - } - - for (Control c : controls.getArray()) { - c.render(rm, vp); - } - } - - /** - * Add a control to the list of controls. - * @param control The control to add. - * - * @see Spatial#removeControl(java.lang.Class) - */ - public void addControl(Control control) { - controls.add(control); - control.setSpatial(this); - } - - /** - * Removes the first control that is an instance of the given class. - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - */ - public void removeControl(Class controlType) { - for (int i = 0; i < controls.size(); i++) { - if (controlType.isAssignableFrom(controls.get(i).getClass())) { - Control control = controls.remove(i); - control.setSpatial(null); - } - } - } - - /** - * Removes the given control from this spatial's controls. - * - * @param control The control to remove - * @return True if the control was successfuly removed. False if - * the control is not assigned to this spatial. - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - */ - public boolean removeControl(Control control) { - boolean result = controls.remove(control); - if (result) { - control.setSpatial(null); - } - - return result; - } - - /** - * Returns the first control that is an instance of the given class, - * or null if no such control exists. - * - * @param controlType The superclass of the control to look for. - * @return The first instance in the list of the controlType class, or null. - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - */ - public T getControl(Class controlType) { - for (Control c : controls.getArray()) { - if (controlType.isAssignableFrom(c.getClass())) { - return (T) c; - } - } - return null; - } - - /** - * Returns the control at the given index in the list. - * - * @param index The index of the control in the list to find. - * @return The control at the given index. - * - * @throws IndexOutOfBoundsException - * If the index is outside the range [0, getNumControls()-1] - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - */ - public Control getControl(int index) { - return controls.get(index); - } - - /** - * @return The number of controls attached to this Spatial. - * @see Spatial#addControl(com.jme3.scene.control.Control) - * @see Spatial#removeControl(java.lang.Class) - */ - public int getNumControls() { - return controls.size(); - } - - /** - * updateLogicalState calls the update() method - * for all controls attached to this Spatial. - * - * @param tpf Time per frame. - * - * @see Spatial#addControl(com.jme3.scene.control.Control) - */ - public void updateLogicalState(float tpf) { - runControlUpdate(tpf); - } - - /** - * updateGeometricState updates the lightlist, - * computes the world transforms, and computes the world bounds - * for this Spatial. - * Calling this when the Spatial is attached to a node - * will cause undefined results. User code should only call this - * method on Spatials having no parent. - * - * @see Spatial#getWorldLightList() - * @see Spatial#getWorldTransform() - * @see Spatial#getWorldBound() - */ - public void updateGeometricState() { - // assume that this Spatial is a leaf, a proper implementation - // for this method should be provided by Node. - - // NOTE: Update world transforms first because - // bound transform depends on them. - if ((refreshFlags & RF_LIGHTLIST) != 0) { - updateWorldLightList(); - } - if ((refreshFlags & RF_TRANSFORM) != 0) { - updateWorldTransforms(); - } - if ((refreshFlags & RF_BOUND) != 0) { - updateWorldBound(); - } - - assert refreshFlags == 0; - } - - /** - * Convert a vector (in) from this spatials' local coordinate space to world - * coordinate space. - * - * @param in - * vector to read from - * @param store - * where to write the result (null to create a new vector, may be - * same as in) - * @return the result (store) - */ - public Vector3f localToWorld(final Vector3f in, Vector3f store) { - checkDoTransformUpdate(); - return worldTransform.transformVector(in, store); - } - - /** - * Convert a vector (in) from world coordinate space to this spatials' local - * coordinate space. - * - * @param in - * vector to read from - * @param store - * where to write the result - * @return the result (store) - */ - public Vector3f worldToLocal(final Vector3f in, final Vector3f store) { - checkDoTransformUpdate(); - return worldTransform.transformInverseVector(in, store); - } - - /** - * getParent retrieves this node's parent. If the parent is - * null this is the root node. - * - * @return the parent of this node. - */ - public Node getParent() { - return parent; - } - - /** - * Called by {@link Node#attachChild(Spatial)} and - * {@link Node#detachChild(Spatial)} - don't call directly. - * setParent sets the parent of this node. - * - * @param parent - * the parent of this node. - */ - protected void setParent(Node parent) { - this.parent = parent; - } - - /** - * removeFromParent removes this Spatial from it's parent. - * - * @return true if it has a parent and performed the remove. - */ - public boolean removeFromParent() { - if (parent != null) { - parent.detachChild(this); - return true; - } - return false; - } - - /** - * determines if the provided Node is the parent, or parent's parent, etc. of this Spatial. - * - * @param ancestor - * the ancestor object to look for. - * @return true if the ancestor is found, false otherwise. - */ - public boolean hasAncestor(Node ancestor) { - if (parent == null) { - return false; - } else if (parent.equals(ancestor)) { - return true; - } else { - return parent.hasAncestor(ancestor); - } - } - - /** - * getLocalRotation retrieves the local rotation of this - * node. - * - * @return the local rotation of this node. - */ - public Quaternion getLocalRotation() { - return localTransform.getRotation(); - } - - /** - * setLocalRotation sets the local rotation of this node - * by using a {@link Matrix3f}. - * - * @param rotation - * the new local rotation. - */ - public void setLocalRotation(Matrix3f rotation) { - localTransform.getRotation().fromRotationMatrix(rotation); - setTransformRefresh(); - } - - /** - * setLocalRotation sets the local rotation of this node. - * - * @param quaternion - * the new local rotation. - */ - public void setLocalRotation(Quaternion quaternion) { - localTransform.setRotation(quaternion); - setTransformRefresh(); - } - - /** - * getLocalScale retrieves the local scale of this node. - * - * @return the local scale of this node. - */ - public Vector3f getLocalScale() { - return localTransform.getScale(); - } - - /** - * setLocalScale sets the local scale of this node. - * - * @param localScale - * the new local scale, applied to x, y and z - */ - public void setLocalScale(float localScale) { - localTransform.setScale(localScale); - setTransformRefresh(); - } - - /** - * setLocalScale sets the local scale of this node. - */ - public void setLocalScale(float x, float y, float z) { - localTransform.setScale(x, y, z); - setTransformRefresh(); - } - - /** - * setLocalScale sets the local scale of this node. - * - * @param localScale - * the new local scale. - */ - public void setLocalScale(Vector3f localScale) { - localTransform.setScale(localScale); - setTransformRefresh(); - } - - /** - * getLocalTranslation retrieves the local translation of - * this node. - * - * @return the local translation of this node. - */ - public Vector3f getLocalTranslation() { - return localTransform.getTranslation(); - } - - /** - * setLocalTranslation sets the local translation of this - * spatial. - * - * @param localTranslation - * the local translation of this spatial. - */ - public void setLocalTranslation(Vector3f localTranslation) { - this.localTransform.setTranslation(localTranslation); - setTransformRefresh(); - } - - /** - * setLocalTranslation sets the local translation of this - * spatial. - */ - public void setLocalTranslation(float x, float y, float z) { - this.localTransform.setTranslation(x, y, z); - setTransformRefresh(); - } - - /** - * setLocalTransform sets the local transform of this - * spatial. - */ - public void setLocalTransform(Transform t) { - this.localTransform.set(t); - setTransformRefresh(); - } - - /** - * getLocalTransform retrieves the local transform of - * this spatial. - * - * @return the local transform of this spatial. - */ - public Transform getLocalTransform() { - return localTransform; - } - - /** - * Applies the given material to the Spatial, this will propagate the - * material down to the geometries in the scene graph. - * - * @param material The material to set. - */ - public void setMaterial(Material material) { - } - - /** - * addLight adds the given light to the Spatial; causing - * all child Spatials to be effected by it. - * - * @param light The light to add. - */ - public void addLight(Light light) { - localLights.add(light); - setLightListRefresh(); - } - - /** - * removeLight removes the given light from the Spatial. - * - * @param light The light to remove. - * @see Spatial#addLight(com.jme3.light.Light) - */ - public void removeLight(Light light) { - localLights.remove(light); - setLightListRefresh(); - } - - /** - * Translates the spatial by the given translation vector. - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial move(float x, float y, float z) { - this.localTransform.getTranslation().addLocal(x, y, z); - setTransformRefresh(); - - return this; - } - - /** - * Translates the spatial by the given translation vector. - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial move(Vector3f offset) { - this.localTransform.getTranslation().addLocal(offset); - setTransformRefresh(); - - return this; - } - - /** - * Scales the spatial by the given value - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial scale(float s) { - return scale(s, s, s); - } - - /** - * Scales the spatial by the given scale vector. - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial scale(float x, float y, float z) { - this.localTransform.getScale().multLocal(x, y, z); - setTransformRefresh(); - - return this; - } - - /** - * Rotates the spatial by the given rotation. - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial rotate(Quaternion rot) { - this.localTransform.getRotation().multLocal(rot); - setTransformRefresh(); - - return this; - } - - /** - * Rotates the spatial by the xAngle, yAngle and zAngle angles (in radians), - * (aka pitch, yaw, roll) in the local coordinate space. - * - * @return The spatial on which this method is called, e.g this. - */ - public Spatial rotate(float xAngle, float yAngle, float zAngle) { - TempVars vars = TempVars.get(); - Quaternion q = vars.quat1; - q.fromAngles(xAngle, yAngle, zAngle); - rotate(q); - vars.release(); - - return this; - } - - /** - * Centers the spatial in the origin of the world bound. - * @return The spatial on which this method is called, e.g this. - */ - public Spatial center() { - Vector3f worldTrans = getWorldTranslation(); - Vector3f worldCenter = getWorldBound().getCenter(); - - Vector3f absTrans = worldTrans.subtract(worldCenter); - setLocalTranslation(absTrans); - - return this; - } - - /** - * @see #setCullHint(CullHint) - * @return the cull mode of this spatial, or if set to CullHint.Inherit, - * the cullmode of it's parent. - */ - public CullHint getCullHint() { - if (cullHint != CullHint.Inherit) { - return cullHint; - } else if (parent != null) { - return parent.getCullHint(); - } else { - return CullHint.Dynamic; - } - } - - public BatchHint getBatchHint() { - if (batchHint != BatchHint.Inherit) { - return batchHint; - } else if (parent != null) { - return parent.getBatchHint(); - } else { - return BatchHint.Always; - } - } - - /** - * Returns this spatial's renderqueue bucket. If the mode is set to inherit, - * then the spatial gets its renderqueue bucket from its parent. - * - * @return The spatial's current renderqueue mode. - */ - public RenderQueue.Bucket getQueueBucket() { - if (queueBucket != RenderQueue.Bucket.Inherit) { - return queueBucket; - } else if (parent != null) { - return parent.getQueueBucket(); - } else { - return RenderQueue.Bucket.Opaque; - } - } - - /** - * @return The shadow mode of this spatial, if the local shadow - * mode is set to inherit, then the parent's shadow mode is returned. - * - * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) - * @see ShadowMode - */ - public RenderQueue.ShadowMode getShadowMode() { - if (shadowMode != RenderQueue.ShadowMode.Inherit) { - return shadowMode; - } else if (parent != null) { - return parent.getShadowMode(); - } else { - return ShadowMode.Off; - } - } - - /** - * Sets the level of detail to use when rendering this Spatial, - * this call propagates to all geometries under this Spatial. - * - * @param lod The lod level to set. - */ - public void setLodLevel(int lod) { - } - - /** - * updateModelBound recalculates the bounding object for this - * Spatial. - */ - public abstract void updateModelBound(); - - /** - * setModelBound sets the bounding object for this Spatial. - * - * @param modelBound - * the bounding object for this spatial. - */ - public abstract void setModelBound(BoundingVolume modelBound); - - /** - * @return The sum of all verticies under this Spatial. - */ - public abstract int getVertexCount(); - - /** - * @return The sum of all triangles under this Spatial. - */ - public abstract int getTriangleCount(); - - /** - * @return A clone of this Spatial, the scene graph in its entirety - * is cloned and can be altered independently of the original scene graph. - * - * Note that meshes of geometries are not cloned explicitly, they - * are shared if static, or specially cloned if animated. - * - * All controls will be cloned using the Control.cloneForSpatial method - * on the clone. - * - * @see Mesh#cloneForAnim() - */ - public Spatial clone(boolean cloneMaterial) { - try { - Spatial clone = (Spatial) super.clone(); - if (worldBound != null) { - clone.worldBound = worldBound.clone(); - } - clone.worldLights = worldLights.clone(); - clone.localLights = localLights.clone(); - - // Set the new owner of the light lists - clone.localLights.setOwner(clone); - clone.worldLights.setOwner(clone); - - // No need to force cloned to update. - // This node already has the refresh flags - // set below so it will have to update anyway. - clone.worldTransform = worldTransform.clone(); - clone.localTransform = localTransform.clone(); - - if (clone instanceof Node) { - Node node = (Node) this; - Node nodeClone = (Node) clone; - nodeClone.children = new SafeArrayList(Spatial.class); - for (Spatial child : node.children) { - Spatial childClone = child.clone(cloneMaterial); - childClone.parent = nodeClone; - nodeClone.children.add(childClone); - } - } - - clone.parent = null; - clone.setBoundRefresh(); - clone.setTransformRefresh(); - clone.setLightListRefresh(); - - clone.controls = new SafeArrayList(Control.class); - for (int i = 0; i < controls.size(); i++) { - Control newControl = controls.get(i).cloneForSpatial(clone); - newControl.setSpatial(clone); - clone.controls.add(newControl); - } - - if (userData != null) { - clone.userData = (HashMap) userData.clone(); - } - - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - /** - * @return A clone of this Spatial, the scene graph in its entirety - * is cloned and can be altered independently of the original scene graph. - * - * Note that meshes of geometries are not cloned explicitly, they - * are shared if static, or specially cloned if animated. - * - * All controls will be cloned using the Control.cloneForSpatial method - * on the clone. - * - * @see Mesh#cloneForAnim() - */ - @Override - public Spatial clone() { - return clone(true); - } - - /** - * @return Similar to Spatial.clone() except will create a deep clone - * of all geometry's meshes, normally this method shouldn't be used - * instead use Spatial.clone() - * - * @see Spatial#clone() - */ - public abstract Spatial deepClone(); - - public void setUserData(String key, Object data) { - if (userData == null) { - userData = new HashMap(); - } - - if(data == null){ - userData.remove(key); - }else if (data instanceof Savable) { - userData.put(key, (Savable) data); - } else { - userData.put(key, new UserData(UserData.getObjectType(data), data)); - } - } - - @SuppressWarnings("unchecked") - public T getUserData(String key) { - if (userData == null) { - return null; - } - - Savable s = userData.get(key); - if (s instanceof UserData) { - return (T) ((UserData) s).getValue(); - } else { - return (T) s; - } - } - - public Collection getUserDataKeys() { - if (userData != null) { - return userData.keySet(); - } - - return Collections.EMPTY_SET; - } - - /** - * Note that we are matching the pattern, therefore the pattern - * must match the entire pattern (i.e. it behaves as if it is sandwiched - * between "^" and "$"). - * You can set regex modes, like case insensitivity, by using the (?X) - * or (?X:Y) constructs. - * - * @param spatialSubclass Subclass which this must implement. - * Null causes all Spatials to qualify. - * @param nameRegex Regular expression to match this name against. - * Null causes all Names to qualify. - * @return true if this implements the specified class and this's name - * matches the specified pattern. - * - * @see java.util.regex.Pattern - */ - public boolean matches(Class spatialSubclass, - String nameRegex) { - if (spatialSubclass != null && !spatialSubclass.isInstance(this)) { - return false; - } - - if (nameRegex != null && (name == null || !name.matches(nameRegex))) { - return false; - } - - return true; - } - - public void write(JmeExporter ex) throws IOException { - OutputCapsule capsule = ex.getCapsule(this); - capsule.write(name, "name", null); - capsule.write(worldBound, "world_bound", null); - capsule.write(cullHint, "cull_mode", CullHint.Inherit); - capsule.write(batchHint, "batch_hint", BatchHint.Inherit); - capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit); - capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit); - capsule.write(localTransform, "transform", Transform.IDENTITY); - capsule.write(localLights, "lights", null); - - // Shallow clone the controls array to convert its type. - capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null); - capsule.writeStringSavableMap(userData, "user_data", null); - } - - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - - name = ic.readString("name", null); - worldBound = (BoundingVolume) ic.readSavable("world_bound", null); - cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit); - batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit); - queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class, - RenderQueue.Bucket.Inherit); - shadowMode = ic.readEnum("shadow_mode", ShadowMode.class, - ShadowMode.Inherit); - - localTransform = (Transform) ic.readSavable("transform", Transform.IDENTITY); - - localLights = (LightList) ic.readSavable("lights", null); - localLights.setOwner(this); - - //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split - //the AnimControl creates the SkeletonControl for old files and add it to the spatial. - //The SkeletonControl must be the last in the stack so we add the list of all other control before it. - //When backward compatibility won't be needed anymore this can be replaced by : - //controls = ic.readSavableArrayList("controlsList", null)); - controls.addAll(0, ic.readSavableArrayList("controlsList", null)); - - userData = (HashMap) ic.readStringSavableMap("user_data", null); - } - - /** - * getWorldBound retrieves the world bound at this node - * level. - * - * @return the world bound at this level. - */ - public BoundingVolume getWorldBound() { - checkDoBoundUpdate(); - return worldBound; - } - - /** - * setCullHint sets how scene culling should work on this - * spatial during drawing. NOTE: You must set this AFTER attaching to a - * parent or it will be reset with the parent's cullMode value. - * - * @param hint - * one of CullHint.Dynamic, CullHint.Always, CullHint.Inherit or - * CullHint.Never - */ - public void setCullHint(CullHint hint) { - cullHint = hint; - } - - /** - * setBatchHint sets how batching should work on this - * spatial. NOTE: You must set this AFTER attaching to a - * parent or it will be reset with the parent's cullMode value. - * - * @param hint - * one of BatchHint.Never, BatchHint.Always, BatchHint.Inherit - */ - public void setBatchHint(BatchHint hint) { - batchHint = hint; - } - - /** - * @return the cullmode set on this Spatial - */ - public CullHint getLocalCullHint() { - return cullHint; - } - - /** - * @return the batchHint set on this Spatial - */ - public BatchHint getLocalBatchHint() { - return batchHint; - } - - /** - * setQueueBucket determines at what phase of the - * rendering process this Spatial will rendered. See the - * {@link Bucket} enum for an explanation of the various - * render queue buckets. - * - * @param queueBucket - * The bucket to use for this Spatial. - */ - public void setQueueBucket(RenderQueue.Bucket queueBucket) { - this.queueBucket = queueBucket; - } - - /** - * Sets the shadow mode of the spatial - * The shadow mode determines how the spatial should be shadowed, - * when a shadowing technique is used. See the - * documentation for the class {@link ShadowMode} for more information. - * - * @see ShadowMode - * - * @param shadowMode The local shadow mode to set. - */ - public void setShadowMode(RenderQueue.ShadowMode shadowMode) { - this.shadowMode = shadowMode; - } - - /** - * @return The locally set queue bucket mode - * - * @see Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) - */ - public RenderQueue.Bucket getLocalQueueBucket() { - return queueBucket; - } - - /** - * @return The locally set shadow mode - * - * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) - */ - public RenderQueue.ShadowMode getLocalShadowMode() { - return shadowMode; - } - - /** - * Returns this spatial's last frustum intersection result. This int is set - * when a check is made to determine if the bounds of the object fall inside - * a camera's frustum. If a parent is found to fall outside the frustum, the - * value for this spatial will not be updated. - * - * @return The spatial's last frustum intersection result. - */ - public Camera.FrustumIntersect getLastFrustumIntersection() { - return frustrumIntersects; - } - - /** - * Overrides the last intersection result. This is useful for operations - * that want to start rendering at the middle of a scene tree and don't want - * the parent of that node to influence culling. - * - * @param intersects - * the new value - */ - public void setLastFrustumIntersection(Camera.FrustumIntersect intersects) { - frustrumIntersects = intersects; - } - - /** - * Returns the Spatial's name followed by the class of the spatial
- * Example: "MyNode (com.jme3.scene.Spatial) - * - * @return Spatial's name followed by the class of the Spatial - */ - @Override - public String toString() { - return name + " (" + this.getClass().getSimpleName() + ')'; - } - - /** - * Creates a transform matrix that will convert from this spatials' - * local coordinate space to the world coordinate space - * based on the world transform. - * - * @param store Matrix where to store the result, if null, a new one - * will be created and returned. - * - * @return store if not null, otherwise, a new matrix containing the result. - * - * @see Spatial#getWorldTransform() - */ - public Matrix4f getLocalToWorldMatrix(Matrix4f store) { - if (store == null) { - store = new Matrix4f(); - } else { - store.loadIdentity(); - } - // multiply with scale first, then rotate, finally translate (cf. - // Eberly) - store.scale(getWorldScale()); - store.multLocal(getWorldRotation()); - store.setTranslation(getWorldTranslation()); - return store; - } - - /** - * Visit each scene graph element ordered by DFS - * @param visitor - */ - public abstract void depthFirstTraversal(SceneGraphVisitor visitor); - - /** - * Visit each scene graph element ordered by BFS - * @param visitor - */ - public void breadthFirstTraversal(SceneGraphVisitor visitor) { - Queue queue = new LinkedList(); - queue.add(this); - - while (!queue.isEmpty()) { - Spatial s = queue.poll(); - visitor.visit(s); - s.breadthFirstTraversal(visitor, queue); - } - } - - protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue); -} +/* + * Copyright (c) 2009-2013 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.scene; + +import com.jme3.asset.AssetKey; +import com.jme3.asset.CloneableSmartAsset; +import com.jme3.bounding.BoundingVolume; +import com.jme3.collision.Collidable; +import com.jme3.export.*; +import com.jme3.light.Light; +import com.jme3.light.LightList; +import com.jme3.material.Material; +import com.jme3.math.*; +import com.jme3.renderer.Camera; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.control.Control; +import com.jme3.util.SafeArrayList; +import com.jme3.util.TempVars; +import java.io.IOException; +import java.util.*; +import java.util.logging.Logger; + +/** + * Spatial defines the base class for scene graph nodes. It + * maintains a link to a parent, it's local transforms and the world's + * transforms. All other scene graph elements, such as {@link Node} and + * {@link Geometry} are subclasses of Spatial. + * + * @author Mark Powell + * @author Joshua Slack + * @version $Revision: 4075 $, $Data$ + */ +public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset { + + private static final Logger logger = Logger.getLogger(Spatial.class.getName()); + + /** + * Specifies how frustum culling should be handled by + * this spatial. + */ + public enum CullHint { + + /** + * Do whatever our parent does. If no parent, default to {@link #Dynamic}. + */ + Inherit, + /** + * Do not draw if we are not at least partially within the view frustum + * of the camera. This is determined via the defined + * Camera planes whether or not this Spatial should be culled. + */ + Dynamic, + /** + * Always cull this from the view, throwing away this object + * and any children from rendering commands. + */ + Always, + /** + * Never cull this from view, always draw it. + * Note that we will still get culled if our parent is culled. + */ + Never; + } + + /** + * Specifies if this spatial should be batched + */ + public enum BatchHint { + + /** + * Do whatever our parent does. If no parent, default to {@link #Always}. + */ + Inherit, + /** + * This spatial will always be batched when attached to a BatchNode. + */ + Always, + /** + * This spatial will never be batched when attached to a BatchNode. + */ + Never; + } + /** + * Refresh flag types + */ + protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms + RF_BOUND = 0x02, + RF_LIGHTLIST = 0x04; // changes in light lists + + protected CullHint cullHint = CullHint.Inherit; + protected BatchHint batchHint = BatchHint.Inherit; + /** + * Spatial's bounding volume relative to the world. + */ + protected BoundingVolume worldBound; + /** + * LightList + */ + protected LightList localLights; + protected transient LightList worldLights; + /** + * This spatial's name. + */ + protected String name; + // scale values + protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects; + protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit; + protected ShadowMode shadowMode = RenderQueue.ShadowMode.Inherit; + public transient float queueDistance = Float.NEGATIVE_INFINITY; + protected Transform localTransform; + protected Transform worldTransform; + protected SafeArrayList controls = new SafeArrayList(Control.class); + protected HashMap userData = null; + /** + * Used for smart asset caching + * + * @see AssetKey#useSmartCache() + */ + protected AssetKey key; + /** + * Spatial's parent, or null if it has none. + */ + protected transient Node parent; + /** + * Refresh flags. Indicate what data of the spatial need to be + * updated to reflect the correct state. + */ + protected transient int refreshFlags = 0; + + /** + * Serialization only. Do not use. + */ + public Spatial() { + localTransform = new Transform(); + worldTransform = new Transform(); + + localLights = new LightList(this); + worldLights = new LightList(this); + + refreshFlags |= RF_BOUND; + } + + /** + * Constructor instantiates a new Spatial object setting the + * rotation, translation and scale value to defaults. + * + * @param name + * the name of the scene element. This is required for + * identification and comparison purposes. + */ + public Spatial(String name) { + this(); + this.name = name; + } + + public void setKey(AssetKey key) { + this.key = key; + } + + public AssetKey getKey() { + return key; + } + + /** + * Indicate that the transform of this spatial has changed and that + * a refresh is required. + */ + protected void setTransformRefresh() { + refreshFlags |= RF_TRANSFORM; + setBoundRefresh(); + } + + protected void setLightListRefresh() { + refreshFlags |= RF_LIGHTLIST; + } + + /** + * Indicate that the bounding of this spatial has changed and that + * a refresh is required. + */ + protected void setBoundRefresh() { + refreshFlags |= RF_BOUND; + + Spatial p = parent; + while (p != null) { + if ((p.refreshFlags & RF_BOUND) != 0) { + return; + } + + p.refreshFlags |= RF_BOUND; + p = p.parent; + } + } + + /** + * (Internal use only) Forces a refresh of the given types of data. + * + * @param transforms Refresh world transform based on parents' + * @param bounds Refresh bounding volume data based on child nodes + * @param lights Refresh light list based on parents' + */ + public void forceRefresh(boolean transforms, boolean bounds, boolean lights) { + if (transforms) { + setTransformRefresh(); + } + if (bounds) { + setBoundRefresh(); + } + if (lights) { + setLightListRefresh(); + } + } + + /** + * checkCulling checks the spatial with the camera to see if it + * should be culled. + *

+ * This method is called by the renderer. Usually it should not be called + * directly. + * + * @param cam The camera to check against. + * @return true if inside or intersecting camera frustum + * (should be rendered), false if outside. + */ + public boolean checkCulling(Camera cam) { + if (refreshFlags != 0) { + throw new IllegalStateException("Scene graph is not properly updated for rendering.\n" + + "State was changed after rootNode.updateGeometricState() call. \n" + + "Make sure you do not modify the scene from another thread!\n" + + "Problem spatial name: " + getName()); + } + + CullHint cm = getCullHint(); + assert cm != CullHint.Inherit; + if (cm == Spatial.CullHint.Always) { + setLastFrustumIntersection(Camera.FrustumIntersect.Outside); + return false; + } else if (cm == Spatial.CullHint.Never) { + setLastFrustumIntersection(Camera.FrustumIntersect.Intersects); + return true; + } + + // check to see if we can cull this node + frustrumIntersects = (parent != null ? parent.frustrumIntersects + : Camera.FrustumIntersect.Intersects); + + if (frustrumIntersects == Camera.FrustumIntersect.Intersects) { + if (getQueueBucket() == Bucket.Gui) { + return cam.containsGui(getWorldBound()); + } else { + frustrumIntersects = cam.contains(getWorldBound()); + } + } + + return frustrumIntersects != Camera.FrustumIntersect.Outside; + } + + /** + * Sets the name of this spatial. + * + * @param name + * The spatial's new name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the name of this spatial. + * + * @return This spatial's name. + */ + public String getName() { + return name; + } + + /** + * Returns the local {@link LightList}, which are the lights + * that were directly attached to this Spatial through the + * {@link #addLight(com.jme3.light.Light) } and + * {@link #removeLight(com.jme3.light.Light) } methods. + * + * @return The local light list + */ + public LightList getLocalLightList() { + return localLights; + } + + /** + * Returns the world {@link LightList}, containing the lights + * combined from all this Spatial's parents up to and including + * this Spatial's lights. + * + * @return The combined world light list + */ + public LightList getWorldLightList() { + return worldLights; + } + + /** + * getWorldRotation retrieves the absolute rotation of the + * Spatial. + * + * @return the Spatial's world rotation quaternion. + */ + public Quaternion getWorldRotation() { + checkDoTransformUpdate(); + return worldTransform.getRotation(); + } + + /** + * getWorldTranslation retrieves the absolute translation of + * the spatial. + * + * @return the Spatial's world tranlsation vector. + */ + public Vector3f getWorldTranslation() { + checkDoTransformUpdate(); + return worldTransform.getTranslation(); + } + + /** + * getWorldScale retrieves the absolute scale factor of the + * spatial. + * + * @return the Spatial's world scale factor. + */ + public Vector3f getWorldScale() { + checkDoTransformUpdate(); + return worldTransform.getScale(); + } + + /** + * getWorldTransform retrieves the world transformation + * of the spatial. + * + * @return the world transform. + */ + public Transform getWorldTransform() { + checkDoTransformUpdate(); + return worldTransform; + } + + /** + * rotateUpTo is a utility function that alters the + * local rotation to point the Y axis in the direction given by newUp. + * + * @param newUp + * the up vector to use - assumed to be a unit vector. + */ + public void rotateUpTo(Vector3f newUp) { + TempVars vars = TempVars.get(); + + Vector3f compVecA = vars.vect1; + Quaternion q = vars.quat1; + + // First figure out the current up vector. + Vector3f upY = compVecA.set(Vector3f.UNIT_Y); + Quaternion rot = localTransform.getRotation(); + rot.multLocal(upY); + + // get angle between vectors + float angle = upY.angleBetween(newUp); + + // figure out rotation axis by taking cross product + Vector3f rotAxis = upY.crossLocal(newUp).normalizeLocal(); + + // Build a rotation quat and apply current local rotation. + q.fromAngleNormalAxis(angle, rotAxis); + q.mult(rot, rot); + + vars.release(); + + setTransformRefresh(); + } + + /** + * lookAt is a convenience method for auto-setting the local + * rotation based on a position in world space and an up vector. It computes the rotation + * to transform the z-axis to point onto 'position' and the y-axis to 'up'. + * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) } + * this method takes a world position to look at and not a relative direction. + * + * Note : 28/01/2013 this method has been fixed as it was not taking into account the parent rotation. + * This was resulting in improper rotation when the spatial had rotated parent nodes. + * This method is intended to work in world space, so no matter what parent graph the + * spatial has, it will look at the given position in world space. + * + * @param position + * where to look at in terms of world coordinates + * @param upVector + * a vector indicating the (local) up direction. (typically {0, + * 1, 0} in jME.) + */ + public void lookAt(Vector3f position, Vector3f upVector) { + Vector3f worldTranslation = getWorldTranslation(); + + TempVars vars = TempVars.get(); + + Vector3f compVecA = vars.vect4; + + compVecA.set(position).subtractLocal(worldTranslation); + getLocalRotation().lookAt(compVecA, upVector); + + if ( getParent() != null ) { + Quaternion rot=vars.quat1; + rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation()); + rot.normalizeLocal(); + setLocalRotation(rot); + } + vars.release(); + setTransformRefresh(); + } + + /** + * Should be overridden by Node and Geometry. + */ + protected void updateWorldBound() { + // the world bound of a leaf is the same as it's model bound + // for a node, the world bound is a combination of all it's children + // bounds + // -> handled by subclass + refreshFlags &= ~RF_BOUND; + } + + protected void updateWorldLightList() { + if (parent == null) { + worldLights.update(localLights, null); + refreshFlags &= ~RF_LIGHTLIST; + } else { + if ((parent.refreshFlags & RF_LIGHTLIST) == 0) { + worldLights.update(localLights, parent.worldLights); + refreshFlags &= ~RF_LIGHTLIST; + } else { + assert false; + } + } + } + + /** + * Should only be called from updateGeometricState(). + * In most cases should not be subclassed. + */ + protected void updateWorldTransforms() { + if (parent == null) { + worldTransform.set(localTransform); + refreshFlags &= ~RF_TRANSFORM; + } else { + // check if transform for parent is updated + assert ((parent.refreshFlags & RF_TRANSFORM) == 0); + worldTransform.set(localTransform); + worldTransform.combineWithParent(parent.worldTransform); + refreshFlags &= ~RF_TRANSFORM; + } + } + + /** + * Computes the world transform of this Spatial in the most + * efficient manner possible. + */ + void checkDoTransformUpdate() { + if ((refreshFlags & RF_TRANSFORM) == 0) { + return; + } + + if (parent == null) { + worldTransform.set(localTransform); + refreshFlags &= ~RF_TRANSFORM; + } else { + TempVars vars = TempVars.get(); + + Spatial[] stack = vars.spatialStack; + Spatial rootNode = this; + int i = 0; + while (true) { + Spatial hisParent = rootNode.parent; + if (hisParent == null) { + rootNode.worldTransform.set(rootNode.localTransform); + rootNode.refreshFlags &= ~RF_TRANSFORM; + i--; + break; + } + + stack[i] = rootNode; + + if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) { + break; + } + + rootNode = hisParent; + i++; + } + + vars.release(); + + for (int j = i; j >= 0; j--) { + rootNode = stack[j]; + //rootNode.worldTransform.set(rootNode.localTransform); + //rootNode.worldTransform.combineWithParent(rootNode.parent.worldTransform); + //rootNode.refreshFlags &= ~RF_TRANSFORM; + rootNode.updateWorldTransforms(); + } + } + } + + /** + * Computes this Spatial's world bounding volume in the most efficient + * manner possible. + */ + void checkDoBoundUpdate() { + if ((refreshFlags & RF_BOUND) == 0) { + return; + } + + checkDoTransformUpdate(); + + // Go to children recursively and update their bound + if (this instanceof Node) { + Node node = (Node) this; + int len = node.getQuantity(); + for (int i = 0; i < len; i++) { + Spatial child = node.getChild(i); + child.checkDoBoundUpdate(); + } + } + + // All children's bounds have been updated. Update my own now. + updateWorldBound(); + } + + private void runControlUpdate(float tpf) { + if (controls.isEmpty()) { + return; + } + + for (Control c : controls.getArray()) { + c.update(tpf); + } + } + + /** + * Called when the Spatial is about to be rendered, to notify + * controls attached to this Spatial using the Control.render() method. + * + * @param rm The RenderManager rendering the Spatial. + * @param vp The ViewPort to which the Spatial is being rendered to. + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + * @see Spatial#getControl(java.lang.Class) + */ + public void runControlRender(RenderManager rm, ViewPort vp) { + if (controls.isEmpty()) { + return; + } + + for (Control c : controls.getArray()) { + c.render(rm, vp); + } + } + + /** + * Add a control to the list of controls. + * @param control The control to add. + * + * @see Spatial#removeControl(java.lang.Class) + */ + public void addControl(Control control) { + controls.add(control); + control.setSpatial(this); + } + + /** + * Removes the first control that is an instance of the given class. + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + */ + public void removeControl(Class controlType) { + for (int i = 0; i < controls.size(); i++) { + if (controlType.isAssignableFrom(controls.get(i).getClass())) { + Control control = controls.remove(i); + control.setSpatial(null); + } + } + } + + /** + * Removes the given control from this spatial's controls. + * + * @param control The control to remove + * @return True if the control was successfuly removed. False if + * the control is not assigned to this spatial. + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + */ + public boolean removeControl(Control control) { + boolean result = controls.remove(control); + if (result) { + control.setSpatial(null); + } + + return result; + } + + /** + * Returns the first control that is an instance of the given class, + * or null if no such control exists. + * + * @param controlType The superclass of the control to look for. + * @return The first instance in the list of the controlType class, or null. + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + */ + public T getControl(Class controlType) { + for (Control c : controls.getArray()) { + if (controlType.isAssignableFrom(c.getClass())) { + return (T) c; + } + } + return null; + } + + /** + * Returns the control at the given index in the list. + * + * @param index The index of the control in the list to find. + * @return The control at the given index. + * + * @throws IndexOutOfBoundsException + * If the index is outside the range [0, getNumControls()-1] + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + */ + public Control getControl(int index) { + return controls.get(index); + } + + /** + * @return The number of controls attached to this Spatial. + * @see Spatial#addControl(com.jme3.scene.control.Control) + * @see Spatial#removeControl(java.lang.Class) + */ + public int getNumControls() { + return controls.size(); + } + + /** + * updateLogicalState calls the update() method + * for all controls attached to this Spatial. + * + * @param tpf Time per frame. + * + * @see Spatial#addControl(com.jme3.scene.control.Control) + */ + public void updateLogicalState(float tpf) { + runControlUpdate(tpf); + } + + /** + * updateGeometricState updates the lightlist, + * computes the world transforms, and computes the world bounds + * for this Spatial. + * Calling this when the Spatial is attached to a node + * will cause undefined results. User code should only call this + * method on Spatials having no parent. + * + * @see Spatial#getWorldLightList() + * @see Spatial#getWorldTransform() + * @see Spatial#getWorldBound() + */ + public void updateGeometricState() { + // assume that this Spatial is a leaf, a proper implementation + // for this method should be provided by Node. + + // NOTE: Update world transforms first because + // bound transform depends on them. + if ((refreshFlags & RF_LIGHTLIST) != 0) { + updateWorldLightList(); + } + if ((refreshFlags & RF_TRANSFORM) != 0) { + updateWorldTransforms(); + } + if ((refreshFlags & RF_BOUND) != 0) { + updateWorldBound(); + } + + assert refreshFlags == 0; + } + + /** + * Convert a vector (in) from this spatials' local coordinate space to world + * coordinate space. + * + * @param in + * vector to read from + * @param store + * where to write the result (null to create a new vector, may be + * same as in) + * @return the result (store) + */ + public Vector3f localToWorld(final Vector3f in, Vector3f store) { + checkDoTransformUpdate(); + return worldTransform.transformVector(in, store); + } + + /** + * Convert a vector (in) from world coordinate space to this spatials' local + * coordinate space. + * + * @param in + * vector to read from + * @param store + * where to write the result + * @return the result (store) + */ + public Vector3f worldToLocal(final Vector3f in, final Vector3f store) { + checkDoTransformUpdate(); + return worldTransform.transformInverseVector(in, store); + } + + /** + * getParent retrieves this node's parent. If the parent is + * null this is the root node. + * + * @return the parent of this node. + */ + public Node getParent() { + return parent; + } + + /** + * Called by {@link Node#attachChild(Spatial)} and + * {@link Node#detachChild(Spatial)} - don't call directly. + * setParent sets the parent of this node. + * + * @param parent + * the parent of this node. + */ + protected void setParent(Node parent) { + this.parent = parent; + } + + /** + * removeFromParent removes this Spatial from it's parent. + * + * @return true if it has a parent and performed the remove. + */ + public boolean removeFromParent() { + if (parent != null) { + parent.detachChild(this); + return true; + } + return false; + } + + /** + * determines if the provided Node is the parent, or parent's parent, etc. of this Spatial. + * + * @param ancestor + * the ancestor object to look for. + * @return true if the ancestor is found, false otherwise. + */ + public boolean hasAncestor(Node ancestor) { + if (parent == null) { + return false; + } else if (parent.equals(ancestor)) { + return true; + } else { + return parent.hasAncestor(ancestor); + } + } + + /** + * getLocalRotation retrieves the local rotation of this + * node. + * + * @return the local rotation of this node. + */ + public Quaternion getLocalRotation() { + return localTransform.getRotation(); + } + + /** + * setLocalRotation sets the local rotation of this node + * by using a {@link Matrix3f}. + * + * @param rotation + * the new local rotation. + */ + public void setLocalRotation(Matrix3f rotation) { + localTransform.getRotation().fromRotationMatrix(rotation); + setTransformRefresh(); + } + + /** + * setLocalRotation sets the local rotation of this node. + * + * @param quaternion + * the new local rotation. + */ + public void setLocalRotation(Quaternion quaternion) { + localTransform.setRotation(quaternion); + setTransformRefresh(); + } + + /** + * getLocalScale retrieves the local scale of this node. + * + * @return the local scale of this node. + */ + public Vector3f getLocalScale() { + return localTransform.getScale(); + } + + /** + * setLocalScale sets the local scale of this node. + * + * @param localScale + * the new local scale, applied to x, y and z + */ + public void setLocalScale(float localScale) { + localTransform.setScale(localScale); + setTransformRefresh(); + } + + /** + * setLocalScale sets the local scale of this node. + */ + public void setLocalScale(float x, float y, float z) { + localTransform.setScale(x, y, z); + setTransformRefresh(); + } + + /** + * setLocalScale sets the local scale of this node. + * + * @param localScale + * the new local scale. + */ + public void setLocalScale(Vector3f localScale) { + localTransform.setScale(localScale); + setTransformRefresh(); + } + + /** + * getLocalTranslation retrieves the local translation of + * this node. + * + * @return the local translation of this node. + */ + public Vector3f getLocalTranslation() { + return localTransform.getTranslation(); + } + + /** + * setLocalTranslation sets the local translation of this + * spatial. + * + * @param localTranslation + * the local translation of this spatial. + */ + public void setLocalTranslation(Vector3f localTranslation) { + this.localTransform.setTranslation(localTranslation); + setTransformRefresh(); + } + + /** + * setLocalTranslation sets the local translation of this + * spatial. + */ + public void setLocalTranslation(float x, float y, float z) { + this.localTransform.setTranslation(x, y, z); + setTransformRefresh(); + } + + /** + * setLocalTransform sets the local transform of this + * spatial. + */ + public void setLocalTransform(Transform t) { + this.localTransform.set(t); + setTransformRefresh(); + } + + /** + * getLocalTransform retrieves the local transform of + * this spatial. + * + * @return the local transform of this spatial. + */ + public Transform getLocalTransform() { + return localTransform; + } + + /** + * Applies the given material to the Spatial, this will propagate the + * material down to the geometries in the scene graph. + * + * @param material The material to set. + */ + public void setMaterial(Material material) { + } + + /** + * addLight adds the given light to the Spatial; causing + * all child Spatials to be effected by it. + * + * @param light The light to add. + */ + public void addLight(Light light) { + localLights.add(light); + setLightListRefresh(); + } + + /** + * removeLight removes the given light from the Spatial. + * + * @param light The light to remove. + * @see Spatial#addLight(com.jme3.light.Light) + */ + public void removeLight(Light light) { + localLights.remove(light); + setLightListRefresh(); + } + + /** + * Translates the spatial by the given translation vector. + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial move(float x, float y, float z) { + this.localTransform.getTranslation().addLocal(x, y, z); + setTransformRefresh(); + + return this; + } + + /** + * Translates the spatial by the given translation vector. + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial move(Vector3f offset) { + this.localTransform.getTranslation().addLocal(offset); + setTransformRefresh(); + + return this; + } + + /** + * Scales the spatial by the given value + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial scale(float s) { + return scale(s, s, s); + } + + /** + * Scales the spatial by the given scale vector. + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial scale(float x, float y, float z) { + this.localTransform.getScale().multLocal(x, y, z); + setTransformRefresh(); + + return this; + } + + /** + * Rotates the spatial by the given rotation. + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial rotate(Quaternion rot) { + this.localTransform.getRotation().multLocal(rot); + setTransformRefresh(); + + return this; + } + + /** + * Rotates the spatial by the xAngle, yAngle and zAngle angles (in radians), + * (aka pitch, yaw, roll) in the local coordinate space. + * + * @return The spatial on which this method is called, e.g this. + */ + public Spatial rotate(float xAngle, float yAngle, float zAngle) { + TempVars vars = TempVars.get(); + Quaternion q = vars.quat1; + q.fromAngles(xAngle, yAngle, zAngle); + rotate(q); + vars.release(); + + return this; + } + + /** + * Centers the spatial in the origin of the world bound. + * @return The spatial on which this method is called, e.g this. + */ + public Spatial center() { + Vector3f worldTrans = getWorldTranslation(); + Vector3f worldCenter = getWorldBound().getCenter(); + + Vector3f absTrans = worldTrans.subtract(worldCenter); + setLocalTranslation(absTrans); + + return this; + } + + /** + * @see #setCullHint(CullHint) + * @return the cull mode of this spatial, or if set to CullHint.Inherit, + * the cullmode of it's parent. + */ + public CullHint getCullHint() { + if (cullHint != CullHint.Inherit) { + return cullHint; + } else if (parent != null) { + return parent.getCullHint(); + } else { + return CullHint.Dynamic; + } + } + + public BatchHint getBatchHint() { + if (batchHint != BatchHint.Inherit) { + return batchHint; + } else if (parent != null) { + return parent.getBatchHint(); + } else { + return BatchHint.Always; + } + } + + /** + * Returns this spatial's renderqueue bucket. If the mode is set to inherit, + * then the spatial gets its renderqueue bucket from its parent. + * + * @return The spatial's current renderqueue mode. + */ + public RenderQueue.Bucket getQueueBucket() { + if (queueBucket != RenderQueue.Bucket.Inherit) { + return queueBucket; + } else if (parent != null) { + return parent.getQueueBucket(); + } else { + return RenderQueue.Bucket.Opaque; + } + } + + /** + * @return The shadow mode of this spatial, if the local shadow + * mode is set to inherit, then the parent's shadow mode is returned. + * + * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) + * @see ShadowMode + */ + public RenderQueue.ShadowMode getShadowMode() { + if (shadowMode != RenderQueue.ShadowMode.Inherit) { + return shadowMode; + } else if (parent != null) { + return parent.getShadowMode(); + } else { + return ShadowMode.Off; + } + } + + /** + * Sets the level of detail to use when rendering this Spatial, + * this call propagates to all geometries under this Spatial. + * + * @param lod The lod level to set. + */ + public void setLodLevel(int lod) { + } + + /** + * updateModelBound recalculates the bounding object for this + * Spatial. + */ + public abstract void updateModelBound(); + + /** + * setModelBound sets the bounding object for this Spatial. + * + * @param modelBound + * the bounding object for this spatial. + */ + public abstract void setModelBound(BoundingVolume modelBound); + + /** + * @return The sum of all verticies under this Spatial. + */ + public abstract int getVertexCount(); + + /** + * @return The sum of all triangles under this Spatial. + */ + public abstract int getTriangleCount(); + + /** + * @return A clone of this Spatial, the scene graph in its entirety + * is cloned and can be altered independently of the original scene graph. + * + * Note that meshes of geometries are not cloned explicitly, they + * are shared if static, or specially cloned if animated. + * + * All controls will be cloned using the Control.cloneForSpatial method + * on the clone. + * + * @see Mesh#cloneForAnim() + */ + public Spatial clone(boolean cloneMaterial) { + try { + Spatial clone = (Spatial) super.clone(); + if (worldBound != null) { + clone.worldBound = worldBound.clone(); + } + clone.worldLights = worldLights.clone(); + clone.localLights = localLights.clone(); + + // Set the new owner of the light lists + clone.localLights.setOwner(clone); + clone.worldLights.setOwner(clone); + + // No need to force cloned to update. + // This node already has the refresh flags + // set below so it will have to update anyway. + clone.worldTransform = worldTransform.clone(); + clone.localTransform = localTransform.clone(); + + if (clone instanceof Node) { + Node node = (Node) this; + Node nodeClone = (Node) clone; + nodeClone.children = new SafeArrayList(Spatial.class); + for (Spatial child : node.children) { + Spatial childClone = child.clone(cloneMaterial); + childClone.parent = nodeClone; + nodeClone.children.add(childClone); + } + } + + clone.parent = null; + clone.setBoundRefresh(); + clone.setTransformRefresh(); + clone.setLightListRefresh(); + + clone.controls = new SafeArrayList(Control.class); + for (int i = 0; i < controls.size(); i++) { + Control newControl = controls.get(i).cloneForSpatial(clone); + newControl.setSpatial(clone); + clone.controls.add(newControl); + } + + if (userData != null) { + clone.userData = (HashMap) userData.clone(); + } + + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + /** + * @return A clone of this Spatial, the scene graph in its entirety + * is cloned and can be altered independently of the original scene graph. + * + * Note that meshes of geometries are not cloned explicitly, they + * are shared if static, or specially cloned if animated. + * + * All controls will be cloned using the Control.cloneForSpatial method + * on the clone. + * + * @see Mesh#cloneForAnim() + */ + @Override + public Spatial clone() { + return clone(true); + } + + /** + * @return Similar to Spatial.clone() except will create a deep clone + * of all geometry's meshes, normally this method shouldn't be used + * instead use Spatial.clone() + * + * @see Spatial#clone() + */ + public abstract Spatial deepClone(); + + public void setUserData(String key, Object data) { + if (userData == null) { + userData = new HashMap(); + } + + if(data == null){ + userData.remove(key); + }else if (data instanceof Savable) { + userData.put(key, (Savable) data); + } else { + userData.put(key, new UserData(UserData.getObjectType(data), data)); + } + } + + @SuppressWarnings("unchecked") + public T getUserData(String key) { + if (userData == null) { + return null; + } + + Savable s = userData.get(key); + if (s instanceof UserData) { + return (T) ((UserData) s).getValue(); + } else { + return (T) s; + } + } + + public Collection getUserDataKeys() { + if (userData != null) { + return userData.keySet(); + } + + return Collections.EMPTY_SET; + } + + /** + * Note that we are matching the pattern, therefore the pattern + * must match the entire pattern (i.e. it behaves as if it is sandwiched + * between "^" and "$"). + * You can set regex modes, like case insensitivity, by using the (?X) + * or (?X:Y) constructs. + * + * @param spatialSubclass Subclass which this must implement. + * Null causes all Spatials to qualify. + * @param nameRegex Regular expression to match this name against. + * Null causes all Names to qualify. + * @return true if this implements the specified class and this's name + * matches the specified pattern. + * + * @see java.util.regex.Pattern + */ + public boolean matches(Class spatialSubclass, + String nameRegex) { + if (spatialSubclass != null && !spatialSubclass.isInstance(this)) { + return false; + } + + if (nameRegex != null && (name == null || !name.matches(nameRegex))) { + return false; + } + + return true; + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(name, "name", null); + capsule.write(worldBound, "world_bound", null); + capsule.write(cullHint, "cull_mode", CullHint.Inherit); + capsule.write(batchHint, "batch_hint", BatchHint.Inherit); + capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit); + capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit); + capsule.write(localTransform, "transform", Transform.IDENTITY); + capsule.write(localLights, "lights", null); + + // Shallow clone the controls array to convert its type. + capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null); + capsule.writeStringSavableMap(userData, "user_data", null); + } + + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + + name = ic.readString("name", null); + worldBound = (BoundingVolume) ic.readSavable("world_bound", null); + cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit); + batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit); + queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class, + RenderQueue.Bucket.Inherit); + shadowMode = ic.readEnum("shadow_mode", ShadowMode.class, + ShadowMode.Inherit); + + localTransform = (Transform) ic.readSavable("transform", Transform.IDENTITY); + + localLights = (LightList) ic.readSavable("lights", null); + localLights.setOwner(this); + + //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split + //the AnimControl creates the SkeletonControl for old files and add it to the spatial. + //The SkeletonControl must be the last in the stack so we add the list of all other control before it. + //When backward compatibility won't be needed anymore this can be replaced by : + //controls = ic.readSavableArrayList("controlsList", null)); + controls.addAll(0, ic.readSavableArrayList("controlsList", null)); + + userData = (HashMap) ic.readStringSavableMap("user_data", null); + } + + /** + * getWorldBound retrieves the world bound at this node + * level. + * + * @return the world bound at this level. + */ + public BoundingVolume getWorldBound() { + checkDoBoundUpdate(); + return worldBound; + } + + /** + * setCullHint alters how view frustum culling will treat this + * spatial. + * + * @param hint one of: CullHint.Dynamic, + * CullHint.Always, CullHint.Inherit, or + * CullHint.Never + *

+ * The effect of the default value (CullHint.Inherit) may change if the + * spatial gets re-parented. + */ + public void setCullHint(CullHint hint) { + cullHint = hint; + } + + /** + * setBatchHint alters how batching will treat this spatial. + * + * @param hint one of: BatchHint.Never, + * BatchHint.Always, or BatchHint.Inherit + *

+ * The effect of the default value (BatchHint.Inherit) may change if the + * spatial gets re-parented. + */ + public void setBatchHint(BatchHint hint) { + batchHint = hint; + } + + /** + * @return the cullmode set on this Spatial + */ + public CullHint getLocalCullHint() { + return cullHint; + } + + /** + * @return the batchHint set on this Spatial + */ + public BatchHint getLocalBatchHint() { + return batchHint; + } + + /** + * setQueueBucket determines at what phase of the + * rendering process this Spatial will rendered. See the + * {@link Bucket} enum for an explanation of the various + * render queue buckets. + * + * @param queueBucket + * The bucket to use for this Spatial. + */ + public void setQueueBucket(RenderQueue.Bucket queueBucket) { + this.queueBucket = queueBucket; + } + + /** + * Sets the shadow mode of the spatial + * The shadow mode determines how the spatial should be shadowed, + * when a shadowing technique is used. See the + * documentation for the class {@link ShadowMode} for more information. + * + * @see ShadowMode + * + * @param shadowMode The local shadow mode to set. + */ + public void setShadowMode(RenderQueue.ShadowMode shadowMode) { + this.shadowMode = shadowMode; + } + + /** + * @return The locally set queue bucket mode + * + * @see Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) + */ + public RenderQueue.Bucket getLocalQueueBucket() { + return queueBucket; + } + + /** + * @return The locally set shadow mode + * + * @see Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) + */ + public RenderQueue.ShadowMode getLocalShadowMode() { + return shadowMode; + } + + /** + * Returns this spatial's last frustum intersection result. This int is set + * when a check is made to determine if the bounds of the object fall inside + * a camera's frustum. If a parent is found to fall outside the frustum, the + * value for this spatial will not be updated. + * + * @return The spatial's last frustum intersection result. + */ + public Camera.FrustumIntersect getLastFrustumIntersection() { + return frustrumIntersects; + } + + /** + * Overrides the last intersection result. This is useful for operations + * that want to start rendering at the middle of a scene tree and don't want + * the parent of that node to influence culling. + * + * @param intersects + * the new value + */ + public void setLastFrustumIntersection(Camera.FrustumIntersect intersects) { + frustrumIntersects = intersects; + } + + /** + * Returns the Spatial's name followed by the class of the spatial
+ * Example: "MyNode (com.jme3.scene.Spatial) + * + * @return Spatial's name followed by the class of the Spatial + */ + @Override + public String toString() { + return name + " (" + this.getClass().getSimpleName() + ')'; + } + + /** + * Creates a transform matrix that will convert from this spatials' + * local coordinate space to the world coordinate space + * based on the world transform. + * + * @param store Matrix where to store the result, if null, a new one + * will be created and returned. + * + * @return store if not null, otherwise, a new matrix containing the result. + * + * @see Spatial#getWorldTransform() + */ + public Matrix4f getLocalToWorldMatrix(Matrix4f store) { + if (store == null) { + store = new Matrix4f(); + } else { + store.loadIdentity(); + } + // multiply with scale first, then rotate, finally translate (cf. + // Eberly) + store.scale(getWorldScale()); + store.multLocal(getWorldRotation()); + store.setTranslation(getWorldTranslation()); + return store; + } + + /** + * Visit each scene graph element ordered by DFS + * @param visitor + */ + public abstract void depthFirstTraversal(SceneGraphVisitor visitor); + + /** + * Visit each scene graph element ordered by BFS + * @param visitor + */ + public void breadthFirstTraversal(SceneGraphVisitor visitor) { + Queue queue = new LinkedList(); + queue.add(this); + + while (!queue.isEmpty()) { + Spatial s = queue.poll(); + visitor.visit(s); + s.breadthFirstTraversal(visitor, queue); + } + } + + protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue); +} diff --git a/engine/src/core/com/jme3/scene/shape/Dome.java b/engine/src/core/com/jme3/scene/shape/Dome.java index 4494cce30..fd61ce2d3 100644 --- a/engine/src/core/com/jme3/scene/shape/Dome.java +++ b/engine/src/core/com/jme3/scene/shape/Dome.java @@ -250,7 +250,7 @@ public class Dome extends Mesh { BufferUtils.populateFromBuffer(tempVa, vb, i); kNormal = tempVa.subtractLocal(center); kNormal.normalizeLocal(); - if (insideView) { + if (!insideView) { nb.put(kNormal.x).put(kNormal.y).put(kNormal.z); } else { nb.put(-kNormal.x).put(-kNormal.y).put(-kNormal.z); @@ -267,7 +267,7 @@ public class Dome extends Mesh { // pole vb.put(center.x).put(center.y + radius).put(center.z); - nb.put(0).put(insideView ? 1 : -1).put(0); + nb.put(0).put(insideView ? -1 : 1).put(0); tb.put(0.5f).put(1.0f); // allocate connectivity diff --git a/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java b/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java index a10775458..ce7e35bc5 100644 --- a/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java +++ b/engine/src/core/com/jme3/shadow/AbstractShadowFilter.java @@ -71,7 +71,7 @@ public abstract class AbstractShadowFilter ext @SuppressWarnings("all") protected AbstractShadowFilter(AssetManager manager, int shadowMapSize, T shadowRenderer) { super("Post Shadow"); - material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); + material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); this.shadowRenderer = shadowRenderer; this.shadowRenderer.setPostShadowMaterial(material); } @@ -86,7 +86,7 @@ public abstract class AbstractShadowFilter ext return true; } - public Material getShadowMaterial() { + public Material getShadowMaterial() { return material; } Vector4f tmpv = new Vector4f(); @@ -103,11 +103,17 @@ public abstract class AbstractShadowFilter ext @Override protected void postQueue(RenderQueue queue) { shadowRenderer.postQueue(queue); + if(shadowRenderer.skipPostPass){ + //removing the shadow map so that the post pass is skipped + material.setTexture("ShadowMap0", null); + } } @Override protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { - shadowRenderer.setPostShadowParams(); + if(!shadowRenderer.skipPostPass){ + shadowRenderer.setPostShadowParams(); + } } @Override diff --git a/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java b/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java index d3fb58034..c81fab22a 100644 --- a/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java +++ b/engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java @@ -92,18 +92,27 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable protected CompareMode shadowCompareMode = CompareMode.Hardware; protected Picture[] dispPic; protected boolean flushQueues = true; - // define if the fallback material should be used. + /** + * true if the fallback material should be used, otherwise false + */ protected boolean needsfallBackMaterial = false; - //Name of the post material technique + /** + * name of the post material technique + */ protected String postTechniqueName = "PostShadow"; - //flags to know when to change params in the materials - //a list of material of the post shadow queue geometries. + /** + * list of materials for post shadow queue geometries + */ protected List matCache = new ArrayList(); protected GeometryList sceneReceivers; protected GeometryList lightReceivers = new GeometryList(new OpaqueComparator()); protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator()); private String[] shadowMapStringCache; private String[] lightViewStringCache; + /** + * true to skip the post pass when there are no shadow casters + */ + protected boolean skipPostPass; /** @@ -113,14 +122,13 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * Create an abstract shadow renderer, this is to be called in extending - * classes + * Create an abstract shadow renderer. Subclasses invoke this constructor. * * @param assetManager the application asset manager - * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, + * @param shadowMapSize the size of the rendered shadow maps (512,1024,2048, * etc...) * @param nbShadowMaps the number of shadow maps rendered (the more shadow - * maps the more quality, the less fps). + * maps the more quality, the fewer fps). */ protected AbstractShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbShadowMaps) { @@ -187,10 +195,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * Sets the filtering mode for shadow edges see {@link EdgeFilteringMode} - * for more info + * Sets the filtering mode for shadow edges. See {@link EdgeFilteringMode} + * for more info. * - * @param EdgeFilteringMode + * @param filterMode the desired filter mode (not null) */ final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) { if (filterMode == null) { @@ -214,7 +222,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * returns the the edge filtering mode + * returns the edge filtering mode * * @see EdgeFilteringMode * @return @@ -224,9 +232,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * sets the shadow compare mode see {@link CompareMode} for more info + * Sets the shadow compare mode. See {@link CompareMode} for more info. * - * @param compareMode + * @param compareMode the desired compare mode (not null) */ final public void setShadowCompareMode(CompareMode compareMode) { if (compareMode == null) { @@ -263,7 +271,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable return shadowCompareMode; } - //debug function that create a displayable frustrum + /** + * debug function to create a visible frustum + */ protected Geometry createFrustum(Vector3f[] pts, int i) { WireFrustum frustum = new WireFrustum(pts); Geometry frustumMdl = new Geometry("f", frustum); @@ -294,6 +304,12 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable return frustumMdl; } + /** + * Initialize this shadow renderer prior to its first update. + * + * @param rm the render manager + * @param vp the viewport + */ public void initialize(RenderManager rm, ViewPort vp) { renderManager = rm; viewPort = vp; @@ -305,25 +321,31 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } } + /** + * Test whether this shadow renderer has been initialized. + * + * @return true if initialized, otherwise false + */ public boolean isInitialized() { return viewPort != null; } /** - * This mehtod is called once per frame. it is responsible for updating the - * shadow cams according to the light view. - * + * Invoked once per frame to update the shadow cams according to the light + * view. + * * @param viewCam the scene cam */ protected abstract void updateShadowCams(Camera viewCam); /** - * this method must return the geomtryList that contains the oclluders to be + * Returns a subclass-specific geometryList containing the occluders to be * rendered in the shadow map * * @param shadowMapIndex the index of the shadow map being rendered * @param sceneOccluders the occluders of the whole scene - * @param sceneReceivers the recievers of the whole scene + * @param sceneReceivers the receivers of the whole scene + * @param shadowMapOcculders * @return */ protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders); @@ -350,7 +372,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable public void postQueue(RenderQueue rq) { GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); sceneReceivers = rq.getShadowQueueContent(ShadowMode.Receive); + skipPostPass = false; if (sceneReceivers.size() == 0 || occluders.size() == 0) { + skipPostPass = true; return; } @@ -401,7 +425,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable debugfrustums = true; } - //debug only : displays depth shadow maps + /** + * For debugging purposes, display depth shadow maps. + */ protected void displayShadowMap(Renderer r) { Camera cam = viewPort.getCamera(); renderManager.setCamera(cam, true); @@ -417,8 +443,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * For dubuging purpose Allow to "snapshot" the current frustrum to the - * scene + * For debugging purposes, "snapshot" the current frustum to the scene. */ public void displayDebug() { debug = true; @@ -427,7 +452,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable abstract GeometryList getReceivers(GeometryList sceneReceivers, GeometryList lightReceivers); public void postFrame(FrameBuffer out) { - + if (skipPostPass) { + return; + } if (debug) { displayShadowMap(renderManager.getRenderer()); } @@ -463,10 +490,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * This method is called once per frame and is responsible of setting the - * material parameters than sub class may need to set on the post material + * This method is called once per frame and is responsible for setting any + * material parameters than subclass may need to set on the post material. * - * @param material the materail to use for the post shadow pass + * @param material the material to use for the post shadow pass */ protected abstract void setMaterialParameters(Material material); @@ -537,7 +564,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * returns the shdaow intensity + * Returns the shadow intensity. * * @see #setShadowIntensity(float shadowIntensity) * @return shadowIntensity @@ -547,9 +574,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * Set the shadowIntensity, the value should be between 0 and 1, a 0 value - * gives a bright and invisilble shadow, a 1 value gives a pitch black - * shadow, default is 0.7 + * Set the shadowIntensity. The value should be between 0 and 1. A 0 value + * gives a bright and invisible shadow, a 1 value gives a pitch black + * shadow. The default is 0.7 * * @param shadowIntensity the darkness of the shadow */ @@ -581,7 +608,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * returns true if the PssmRenderer flushed the shadow queues + * Returns true if this shadow renderer flushes the shadow queues. * * @return flushQueues */ @@ -590,9 +617,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } /** - * Set this to false if you want to use several PssmRederers to have - * multiple shadows cast by multiple light sources. Make sure the last - * PssmRenderer in the stack DO flush the queues, but not the others + * Set flushQueues to false if you have multiple shadow renderers, in order + * for multiple light sources to cast shadows. Make sure the last shadow + * renderer in the stack DOES flush the queues, but not the others. * * @param flushQueues */ @@ -600,11 +627,16 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable this.flushQueues = flushQueues; } + /** + * De-serialize this instance, for example when loading from a J3O file. + * + * @param im importer (not null) + */ public void read(JmeImporter im) throws IOException { InputCapsule ic = (InputCapsule) im.getCapsule(this); assetManager = im.getAssetManager(); nbShadowMaps = ic.readInt("nbShadowMaps", 1); - shadowMapSize = ic.readInt("shadowMapSize", 0); + shadowMapSize = ic.readFloat("shadowMapSize", 0f); shadowIntensity = ic.readFloat("shadowIntensity", 0.7f); edgeFilteringMode = ic.readEnum("edgeFilteringMode", EdgeFilteringMode.class, EdgeFilteringMode.Bilinear); shadowCompareMode = ic.readEnum("shadowCompareMode", CompareMode.class, CompareMode.Hardware); @@ -615,6 +647,11 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable } + /** + * Serialize this instance, for example when saving to a J3O file. + * + * @param ex exporter (not null) + */ public void write(JmeExporter ex) throws IOException { OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); oc.write(nbShadowMaps, "nbShadowMaps", 1); @@ -625,4 +662,4 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable oc.write(flushQueues, "flushQueues", false); oc.write(edgesThickness, "edgesThickness", 1.0f); } -} +} \ No newline at end of file diff --git a/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java b/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java index 161e7684c..6c9f3d922 100644 --- a/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java +++ b/engine/src/core/com/jme3/shadow/EdgeFilteringMode.java @@ -57,8 +57,13 @@ public enum EdgeFilteringMode { */ PCF4(3), /** - * 8x8 percentage-closer filtering is used. Shadows will be smoother at the - * cost of performance + * 12 samples percentage-closer filtering with a POISON disc distribution + * is used. + * http://devmag.org.za/2009/05/03/poisson-disk-sampling/ + * The principle is to eliminate the regular blurring pattern that can be + * seen with pcf4x4 by randomizing the samble position with a poisson disc. + * Shadows will look smoother than 4x4 PCF but with slightly better or + * similar performance. */ PCFPOISSON(4), /** diff --git a/engine/src/core/com/jme3/system/JmeVersion.java b/engine/src/core/com/jme3/system/JmeVersion.java index 615f697b2..da32da90c 100644 --- a/engine/src/core/com/jme3/system/JmeVersion.java +++ b/engine/src/core/com/jme3/system/JmeVersion.java @@ -32,5 +32,5 @@ package com.jme3.system; public class JmeVersion { - public static final String FULL_NAME = "jMonkeyEngine 3.0.5"; + public static final String FULL_NAME = "jMonkeyEngine 3.0.6"; } diff --git a/engine/src/core/com/jme3/util/SkyFactory.java b/engine/src/core/com/jme3/util/SkyFactory.java index ee597f017..9d31ebba0 100644 --- a/engine/src/core/com/jme3/util/SkyFactory.java +++ b/engine/src/core/com/jme3/util/SkyFactory.java @@ -1,245 +1,298 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.util; - -import com.jme3.asset.AssetManager; -import com.jme3.asset.TextureKey; -import com.jme3.bounding.BoundingSphere; -import com.jme3.material.Material; -import com.jme3.math.Vector3f; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.Spatial; -import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.TextureCubeMap; -import java.nio.ByteBuffer; -import java.util.ArrayList; - -/** - * SkyFactory is used to create jME {@link Spatial}s that can - * be attached to the scene to display a sky image in the background. - * - * @author Kirill Vainer - */ -public class SkyFactory { - - /** - * Creates a sky using the given texture (cubemap or spheremap). - * - * @param assetManager The asset manager to use to load materials - * @param texture Texture to use for the sky - * @param normalScale The normal scale is multiplied by the 3D normal - * to get a texture coordinate. Use Vector3f.UNIT_XYZ to not apply - * and transformation to the normal. - * @param sphereMap The way the texture is used - * depends on this value:
- *

    - *
  • true: Its a Texture2D with the pixels arranged for - * sphere mapping.
  • - *
  • false: Its either a TextureCubeMap or Texture2D. If its a Texture2D - * then the image is taken from it and is inserted into a TextureCubeMap
  • - *
- * @return A spatial representing the sky - */ - public static Spatial createSky(AssetManager assetManager, Texture texture, Vector3f normalScale, boolean sphereMap) { - return createSky(assetManager, texture, normalScale, sphereMap, 10); - } - - /** - * Creates a sky using the given texture (cubemap or spheremap). - * - * @param assetManager The asset manager to use to load materials - * @param texture Texture to use for the sky - * @param normalScale The normal scale is multiplied by the 3D normal - * to get a texture coordinate. Use Vector3f.UNIT_XYZ to not apply - * and transformation to the normal. - * @param sphereMap The way the texture is used - * depends on this value:
- *
    - *
  • true: Its a Texture2D with the pixels arranged for - * sphere mapping.
  • - *
  • false: Its either a TextureCubeMap or Texture2D. If its a Texture2D - * then the image is taken from it and is inserted into a TextureCubeMap
  • - *
- * @param sphereRadius If specified, this will be the sky sphere's radius. - * This should be the camera's near plane for optimal quality. - * @return A spatial representing the sky - */ - public static Spatial createSky(AssetManager assetManager, Texture texture, Vector3f normalScale, boolean sphereMap, int sphereRadius) { - if (texture == null) { - throw new IllegalArgumentException("texture cannot be null"); - } - final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true); - - Geometry sky = new Geometry("Sky", sphereMesh); - sky.setQueueBucket(Bucket.Sky); - sky.setCullHint(Spatial.CullHint.Never); - sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO)); - - Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); - - skyMat.setVector3("NormalScale", normalScale); - if (sphereMap) { - skyMat.setBoolean("SphereMap", sphereMap); - } else if (!(texture instanceof TextureCubeMap)) { - // make sure its a cubemap - Image img = texture.getImage(); - texture = new TextureCubeMap(); - texture.setImage(img); - } - skyMat.setTexture("Texture", texture); - sky.setMaterial(skyMat); - - return sky; - } - - private static void checkImage(Image image) { -// if (image.getDepth() != 1) -// throw new IllegalArgumentException("3D/Array images not allowed"); - - if (image.getWidth() != image.getHeight()) { - throw new IllegalArgumentException("Image width and height must be the same"); - } - - if (image.getMultiSamples() != 1) { - throw new IllegalArgumentException("Multisample textures not allowed"); - } - } - - private static void checkImagesForCubeMap(Image... images) { - if (images.length == 1) { - return; - } - - Format fmt = images[0].getFormat(); - int width = images[0].getWidth(); - int height = images[0].getHeight(); - - ByteBuffer data = images[0].getData(0); - int size = data != null ? data.capacity() : 0; - - checkImage(images[0]); - - for (int i = 1; i < images.length; i++) { - Image image = images[i]; - checkImage(images[i]); - if (image.getFormat() != fmt) { - throw new IllegalArgumentException("Images must have same format"); - } - if (image.getWidth() != width || image.getHeight() != height) { - throw new IllegalArgumentException("Images must have same resolution"); - } - ByteBuffer data2 = image.getData(0); - if (data2 != null){ - if (data2.capacity() != size) { - throw new IllegalArgumentException("Images must have same size"); - } - } - } - } - - public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down, Vector3f normalScale) { - return createSky(assetManager, west, east, north, south, up, down, normalScale, 10); - } - - public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down, Vector3f normalScale, int sphereRadius) { - final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true); - Geometry sky = new Geometry("Sky", sphereMesh); - sky.setQueueBucket(Bucket.Sky); - sky.setCullHint(Spatial.CullHint.Never); - sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO)); - - Image westImg = west.getImage(); - Image eastImg = east.getImage(); - Image northImg = north.getImage(); - Image southImg = south.getImage(); - Image upImg = up.getImage(); - Image downImg = down.getImage(); - - checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); - - Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null); - - cubeImage.addData(westImg.getData(0)); - cubeImage.addData(eastImg.getData(0)); - - cubeImage.addData(downImg.getData(0)); - cubeImage.addData(upImg.getData(0)); - - cubeImage.addData(southImg.getData(0)); - cubeImage.addData(northImg.getData(0)); - - if (westImg.getEfficentData() != null){ - // also consilidate efficient data - ArrayList efficientData = new ArrayList(6); - efficientData.add(westImg.getEfficentData()); - efficientData.add(eastImg.getEfficentData()); - efficientData.add(downImg.getEfficentData()); - efficientData.add(upImg.getEfficentData()); - efficientData.add(southImg.getEfficentData()); - efficientData.add(northImg.getEfficentData()); - cubeImage.setEfficentData(efficientData); - } - - TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); - cubeMap.setAnisotropicFilter(0); - cubeMap.setMagFilter(Texture.MagFilter.Bilinear); - cubeMap.setMinFilter(Texture.MinFilter.NearestNoMipMaps); - cubeMap.setWrap(Texture.WrapMode.EdgeClamp); - - Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); - skyMat.setTexture("Texture", cubeMap); - skyMat.setVector3("NormalScale", normalScale); - sky.setMaterial(skyMat); - - return sky; - } - - public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down) { - return createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); - } - - public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) { - return createSky(assetManager, texture, Vector3f.UNIT_XYZ, sphereMap); - } - - public static Spatial createSky(AssetManager assetManager, String textureName, boolean sphereMap) { - TextureKey key = new TextureKey(textureName, true); - key.setGenerateMips(true); - key.setAsCube(!sphereMap); - Texture tex = assetManager.loadTexture(key); - return createSky(assetManager, tex, sphereMap); - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.util; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; +import com.jme3.bounding.BoundingSphere; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Sphere; +import com.jme3.texture.Image; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; +import com.jme3.texture.TextureCubeMap; +import java.nio.ByteBuffer; +import java.util.ArrayList; + +/** + * SkyFactory is used to create jME {@link Spatial}s that can + * be attached to the scene to display a sky image in the background. + * + * @author Kirill Vainer + */ +public class SkyFactory { + + /** + * Create a sky with radius=10 using the given cubemap or spheremap texture. + * + * For the sky to be visible, its radius must fall between the near and far + * planes of the camera's frustrum. + * + * @param assetManager from which to load materials + * @param texture to use + * @param normalScale The normal scale is multiplied by the 3D normal to get + * a texture coordinate. Use Vector3f.UNIT_XYZ to not apply and + * transformation to the normal. + * @param sphereMap determines how the texture is used:
+ *
    + *
  • true: The texture is a Texture2D with the pixels arranged for + * sphere + * mapping.
  • + *
  • false: The texture is either a TextureCubeMap or Texture2D. If it is + * a Texture2D then the image is taken from it and is inserted into a + * TextureCubeMap
  • + *
+ * @return a new spatial representing the sky, ready to be attached to the + * scene graph + */ + public static Spatial createSky(AssetManager assetManager, Texture texture, + Vector3f normalScale, boolean sphereMap) { + return createSky(assetManager, texture, normalScale, sphereMap, 10); + } + + /** + * Create a sky using the given cubemap or spheremap texture. + * + * @param assetManager from which to load materials + * @param texture to use + * @param normalScale The normal scale is multiplied by the 3D normal to get + * a texture coordinate. Use Vector3f.UNIT_XYZ to not apply and + * transformation to the normal. + * @param sphereMap determines how the texture is used:
+ *
    + *
  • true: The texture is a Texture2D with the pixels arranged for + * sphere + * mapping.
  • + *
  • false: The texture is either a TextureCubeMap or Texture2D. If it is + * a Texture2D then the image is taken from it and is inserted into a + * TextureCubeMap
  • + *
+ * @param sphereRadius the sky sphere's radius: for the sky to be visible, + * its radius must fall between the near and far planes of the camera's + * frustrum + * @return a new spatial representing the sky, ready to be attached to the + * scene graph + */ + public static Spatial createSky(AssetManager assetManager, Texture texture, + Vector3f normalScale, boolean sphereMap, int sphereRadius) { + if (texture == null) { + throw new IllegalArgumentException("texture cannot be null"); + } + final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true); + + Geometry sky = new Geometry("Sky", sphereMesh); + sky.setQueueBucket(Bucket.Sky); + sky.setCullHint(Spatial.CullHint.Never); + sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO)); + + Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); + + skyMat.setVector3("NormalScale", normalScale); + if (sphereMap) { + skyMat.setBoolean("SphereMap", sphereMap); + } else if (!(texture instanceof TextureCubeMap)) { + // make sure its a cubemap + Image img = texture.getImage(); + texture = new TextureCubeMap(); + texture.setImage(img); + } + skyMat.setTexture("Texture", texture); + sky.setMaterial(skyMat); + + return sky; + } + + private static void checkImage(Image image) { +// if (image.getDepth() != 1) +// throw new IllegalArgumentException("3D/Array images not allowed"); + + if (image.getWidth() != image.getHeight()) { + throw new IllegalArgumentException("Image width and height must be the same"); + } + + if (image.getMultiSamples() != 1) { + throw new IllegalArgumentException("Multisample textures not allowed"); + } + } + + private static void checkImagesForCubeMap(Image... images) { + if (images.length == 1) { + return; + } + + Format fmt = images[0].getFormat(); + int width = images[0].getWidth(); + int height = images[0].getHeight(); + + ByteBuffer data = images[0].getData(0); + int size = data != null ? data.capacity() : 0; + + checkImage(images[0]); + + for (int i = 1; i < images.length; i++) { + Image image = images[i]; + checkImage(images[i]); + if (image.getFormat() != fmt) { + throw new IllegalArgumentException("Images must have same format"); + } + if (image.getWidth() != width || image.getHeight() != height) { + throw new IllegalArgumentException("Images must have same resolution"); + } + ByteBuffer data2 = image.getData(0); + if (data2 != null){ + if (data2.capacity() != size) { + throw new IllegalArgumentException("Images must have same size"); + } + } + } + } + + /** + * Create a cube-mapped sky with radius=10 using six textures. + * + * For the sky to be visible, its radius must fall between the near and far + * planes of the camera's frustrum. + * + * @param assetManager from which to load materials + * @param west texture for the western face of the cube + * @param east texture for the eastern face of the cube + * @param north texture for the northern face of the cube + * @param south texture for the southern face of the cube + * @param up texture for the top face of the cube + * @param down texture for the bottom face of the cube + * @param normalScale The normal scale is multiplied by the 3D normal to get + * a texture coordinate. Use Vector3f.UNIT_XYZ to not apply and + * transformation to the normal. + * @return a new spatial representing the sky, ready to be attached to the + * scene graph + */ + public static Spatial createSky(AssetManager assetManager, Texture west, + Texture east, Texture north, Texture south, Texture up, + Texture down, Vector3f normalScale) { + return createSky(assetManager, west, east, north, south, up, down, + normalScale, 10); + } + + /** + * Create a cube-mapped sky using six textures. + * + * @param assetManager from which to load materials + * @param west texture for the western face of the cube + * @param east texture for the eastern face of the cube + * @param north texture for the northern face of the cube + * @param south texture for the southern face of the cube + * @param up texture for the top face of the cube + * @param down texture for the bottom face of the cube + * @param normalScale The normal scale is multiplied by the 3D normal to get + * a texture coordinate. Use Vector3f.UNIT_XYZ to not apply and + * transformation to the normal. + * @param sphereRadius the sky sphere's radius: for the sky to be visible, + * its radius must fall between the near and far planes of the camera's + * frustrum + * @return a new spatial representing the sky, ready to be attached to the + * scene graph + */ + public static Spatial createSky(AssetManager assetManager, Texture west, + Texture east, Texture north, Texture south, Texture up, + Texture down, Vector3f normalScale, float sphereRadius) { + final Sphere sphereMesh = new Sphere(10, 10, sphereRadius, false, true); + Geometry sky = new Geometry("Sky", sphereMesh); + sky.setQueueBucket(Bucket.Sky); + sky.setCullHint(Spatial.CullHint.Never); + sky.setModelBound(new BoundingSphere(Float.POSITIVE_INFINITY, Vector3f.ZERO)); + + Image westImg = west.getImage(); + Image eastImg = east.getImage(); + Image northImg = north.getImage(); + Image southImg = south.getImage(); + Image upImg = up.getImage(); + Image downImg = down.getImage(); + + checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); + + Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), null); + + cubeImage.addData(westImg.getData(0)); + cubeImage.addData(eastImg.getData(0)); + + cubeImage.addData(downImg.getData(0)); + cubeImage.addData(upImg.getData(0)); + + cubeImage.addData(southImg.getData(0)); + cubeImage.addData(northImg.getData(0)); + + if (westImg.getEfficentData() != null){ + // also consilidate efficient data + ArrayList efficientData = new ArrayList(6); + efficientData.add(westImg.getEfficentData()); + efficientData.add(eastImg.getEfficentData()); + efficientData.add(downImg.getEfficentData()); + efficientData.add(upImg.getEfficentData()); + efficientData.add(southImg.getEfficentData()); + efficientData.add(northImg.getEfficentData()); + cubeImage.setEfficentData(efficientData); + } + + TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); + cubeMap.setAnisotropicFilter(0); + cubeMap.setMagFilter(Texture.MagFilter.Bilinear); + cubeMap.setMinFilter(Texture.MinFilter.NearestNoMipMaps); + cubeMap.setWrap(Texture.WrapMode.EdgeClamp); + + Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); + skyMat.setTexture("Texture", cubeMap); + skyMat.setVector3("NormalScale", normalScale); + sky.setMaterial(skyMat); + + return sky; + } + + public static Spatial createSky(AssetManager assetManager, Texture west, Texture east, Texture north, Texture south, Texture up, Texture down) { + return createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); + } + + public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) { + return createSky(assetManager, texture, Vector3f.UNIT_XYZ, sphereMap); + } + + public static Spatial createSky(AssetManager assetManager, String textureName, boolean sphereMap) { + TextureKey key = new TextureKey(textureName, true); + key.setGenerateMips(true); + key.setAsCube(!sphereMap); + Texture tex = assetManager.loadTexture(key); + return createSky(assetManager, tex, sphereMap); + } +} diff --git a/engine/src/desktop/com/jme3/cursors/plugins/CursorLoader.java b/engine/src/desktop/com/jme3/cursors/plugins/CursorLoader.java index c49c31834..d3e71fcad 100644 --- a/engine/src/desktop/com/jme3/cursors/plugins/CursorLoader.java +++ b/engine/src/desktop/com/jme3/cursors/plugins/CursorLoader.java @@ -41,6 +41,7 @@ import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; @@ -210,13 +211,13 @@ public class CursorLoader implements AssetLoader { } } else if (isIco) { DataInputStream in = new DataInputStream(inStream); - int bytesToRead; - while ((bytesToRead = in.available()) != 0) { - byte[] icoimage2 = new byte[icoimages.length + bytesToRead]; - System.arraycopy(icoimages, 0, icoimage2, 0, icoimages.length); - in.read(icoimage2, icoimages.length, bytesToRead); - icoimages = icoimage2; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[16384]; + int bytesRead; + while ((bytesRead = in.read(buffer)) >= 0) { + out.write(buffer, 0, bytesRead); } + icoimages = out.toByteArray(); } BufferedImage bi[] = parseICOImage(icoimages); diff --git a/engine/src/desktop/com/jme3/system/Natives.java b/engine/src/desktop/com/jme3/system/Natives.java index e528aadcc..ce3aad430 100644 --- a/engine/src/desktop/com/jme3/system/Natives.java +++ b/engine/src/desktop/com/jme3/system/Natives.java @@ -85,6 +85,7 @@ public final class Natives { } private static int computeNativesHash() { + URLConnection conn = null; try { String classpath = System.getProperty("java.class.path"); URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/Natives.class"); @@ -101,11 +102,18 @@ public final class Natives { throw new UnsupportedOperationException(ex); } - URLConnection conn = url.openConnection(); + conn = url.openConnection(); int hash = classpath.hashCode() ^ (int) conn.getLastModified(); return hash; } catch (IOException ex) { throw new UnsupportedOperationException(ex); + } finally { + if (conn != null) { + try { + conn.getInputStream().close(); + conn.getOutputStream().close(); + } catch (IOException ex) { } + } } } @@ -233,7 +241,7 @@ public final class Natives { String libraryPath = getExtractionDir().toString(); if (needLWJGL) { - logger.log(Level.FINE, "Extraction Directory: {0}", getExtractionDir().toString()); + logger.log(Level.INFO, "Extraction Directory: {0}", getExtractionDir().toString()); // LWJGL supports this feature where // it can load libraries from this path. diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java index 0b2cd1507..f4a748320 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -490,14 +490,31 @@ public class LwjglRenderer implements Renderer { context.depthTestEnabled = false; } - if (state.isAlphaTest() && context.alphaTestFallOff == 0) { + if (state.isAlphaTest() && !context.alphaTestEnabled) { glEnable(GL_ALPHA_TEST); +<<<<<<< .working glAlphaFunc(GL_GREATER, state.getAlphaFallOff()); context.alphaTestFallOff = state.getAlphaFallOff(); } else if (!state.isAlphaTest() && context.alphaTestFallOff != 0) { +======= + glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); + context.alphaTestEnabled = true; + } else if (!state.isAlphaTest() && context.alphaTestEnabled) { +>>>>>>> .merge-right.r10938 glDisable(GL_ALPHA_TEST); - context.alphaTestFallOff = 0; + context.alphaTestEnabled = false; } +<<<<<<< .working +======= + if (state.getAlphaFallOff() != context.alphaTestFallOff) { + glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); + context.alphaTestFallOff = state.getAlphaFallOff(); + } + if (state.getAlphaFunc() != context.alphaFunc) { + glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff); + context.alphaFunc = state.getAlphaFunc(); + } +>>>>>>> .merge-right.r10938 if (state.isDepthWrite() && !context.depthWriteEnabled) { glDepthMask(true); @@ -1762,6 +1779,7 @@ public class LwjglRenderer implements Renderer { case ThreeDimensional: case CubeMap: // cubemaps use 3D coords glTexParameteri(target, GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); + //There is no break statement on purpose here case TwoDimensional: case TwoDimensionalArray: glTexParameteri(target, GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); diff --git a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java index 1074d6f3f..6c0e52566 100644 --- a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java +++ b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java @@ -130,7 +130,42 @@ public class InputSystemJme implements InputSystem, RawInputListener { //System.out.format("niftyMouse(%d, %d, %d, true) = %b\n", x, y, button, consumed); } else { // Forward the event if nifty owns it or if the cursor is visible. - if (niftyOwnsDragging[button] || inputManager.isCursorVisible()){ + // + // 2013-10-06: void256 was here and changed stuff ;-) Explanation: + // + // Currently Nifty remembers any mouse down event internally as "mouse button now down" regardless of it + // hitting a Nifty element. As long as it does not receive a mouse up event, Nifty will think of the mouse + // button as being pressed. + // + // The original code: + // -> if (niftyOwnsDragging[button] || inputManager.isCursorVisible()){ + // + // forwarded mouse up events to Nifty when: + // a) Nifty owns dragging, e.g. there was a mouse down event that actually hit a Nifty element before OR + // b) when the jme mouse cursor is visible. + // + // That's ok but the "Nifty remembers the mouse down event" thing had the following consequences in one + // special case: + // 1) You click on the jme scene (not Nifty) and Nifty will correctly return false (event not consumed) but + // internally it remembers: "mouse button is now down". Note that the jme mouse cursor is now hidden. + // 2) You release the mouse button but the mouse down event will not be forwarded to Nifty because it did + // owned the mouse and the jme mouse cursor is not visible. + // + // Nifty now still thinks that the mouse button is down although it's not. The result is that the next click + // on any Nifty element will not be recognized as an initial click by Nifty. So you need an additional click + // on the Nifty element to activate it correctly. In case of drag and drop this additional click was quite + // irritating. + // + // To fix that we'll now forward the mouse button up event ALWAYS to Nifty regardless of it owning the mouse + // or the jme mouse cursor visibility. + // + // Please note: Compared to the original version a side effect is that jme will now always send mouse move + // events to Nifty even when the mouse cursor is hidden. So in theory it could happen that input events are + // handled by both: jme and Nifty when f.i. you move around your scene with the mouse cursor hidden and that + // invisible cursor is moved over some Nifty element. I've not been able to reproduce that case though, + // which is good ;-) If that ever happens to someone there is an easy fix possible: + // nifty.setIgnoreMouseEvents() to completely stop Nifty from processing events. + boolean consumed = nic.processMouseEvent(x, y, 0, button, false); // Only consume event if it ORIGINATED in nifty! @@ -138,7 +173,7 @@ public class InputSystemJme implements InputSystem, RawInputListener { evt.setConsumed(); processSoftKeyboard(); } - } + niftyOwnsDragging[button] = false; //System.out.format("niftyMouse(%d, %d, %d, false) = %b\n", x, y, button, consumed); } diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java index 63692c61b..21c504528 100644 --- a/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java +++ b/engine/src/terrain/com/jme3/terrain/heightmap/AbstractHeightMap.java @@ -253,9 +253,12 @@ public abstract class AbstractHeightMap implements HeightMap { throw new Exception("Filename must not be null"); } //open the streams and send the height data to the file. + FileOutputStream fos = null; + DataOutputStream dos = null; try { - FileOutputStream fos = new FileOutputStream(filename); - DataOutputStream dos = new DataOutputStream(fos); + fos = new FileOutputStream(filename); + dos = new DataOutputStream(fos); + for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { dos.write((int) heightData[j + (i * size)]); @@ -270,6 +273,13 @@ public abstract class AbstractHeightMap implements HeightMap { } catch (IOException e) { logger.log(Level.WARNING, "Error writing to file {0}", filename); return false; + } finally { + if (fos != null) { + fos.close(); + } + if (dos != null) { + dos.close(); + } } logger.log(Level.FINE, "Saved terrain to {0}", filename); diff --git a/engine/src/test/jme3test/audio/TestMusicStreaming.java b/engine/src/test/jme3test/audio/TestMusicStreaming.java index 37ba7f8ad..6d7ceb913 100644 --- a/engine/src/test/jme3test/audio/TestMusicStreaming.java +++ b/engine/src/test/jme3test/audio/TestMusicStreaming.java @@ -47,6 +47,8 @@ public class TestMusicStreaming extends SimpleApplication { public void simpleInitApp(){ assetManager.registerLocator("http://www.vorbis.com/music/", UrlLocator.class); AudioNode audioSource = new AudioNode(assetManager, "Lumme-Badloop.ogg", true); + audioSource.setPositional(false); + audioSource.setReverbEnabled(false); audioSource.play(); } diff --git a/sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderFbxDataObject.java b/sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderFbxDataObject.java index 31d29ea22..05359f9ea 100644 --- a/sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderFbxDataObject.java +++ b/sdk/jme3-blender/src/com/jme3/gde/blender/filetypes/BlenderFbxDataObject.java @@ -83,7 +83,7 @@ public class BlenderFbxDataObject extends AbstractBlenderImportDataObject { public BlenderFbxDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { super(pf, loader); - SUFFIX = "3ds"; + SUFFIX = "fbx"; // registerEditor("application/fbx", false); } // @Override diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html index d4c5c8df5..150ba0bc7 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/3d_models.html @@ -83,7 +83,7 @@ To export your models as Ogre XML -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. +You can now use the jMonkeyEngine SDK to load and view models. You can create scenes from them and write code that loads them into your application.

diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html index 3f693ff1e..37c1b9cf9 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/application_states.html @@ -4,16 +4,16 @@

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

- +

Overview

- +

Use Case Examples

@@ -56,7 +56,7 @@ You can! This is what AppStates are there for. An AppState class is subset of (o
- +

Supported Features

@@ -86,7 +86,7 @@ Each AppState has its own update loop, which hooks into the main simpleUpdate()

- +

Usage

@@ -109,7 +109,7 @@ To implement game logic:
  • Enable and disable (unpause and pause) the AppStates that you need during the game.
  • -
  • Detach the AppState from the AppStateManager (stateManager.detach(myAppState);) and clean it up it.
    +
  • Detach the AppState from the AppStateManager (stateManager.detach(myAppState);) and clean it up.
  • @@ -119,7 +119,7 @@ When you add several AppStates to one Application and activate them, their initi

    - +

    Code Samples

    @@ -139,7 +139,7 @@ JME3 comes with a BulletAppState that implements Physical behaviour (using the j
    - +

    AppState

    @@ -184,9 +184,9 @@ stateDetached(asm)The AppState knows when it is attached to, or detache postRender()Called after all rendering commands are flushed, including your optional customizations. (Typically not used.)
    - + - +

    AbstractAppState

    @@ -249,7 +249,7 @@ Definition: }
    - +

    Pausing and Unpausing

    @@ -259,7 +259,7 @@ You define what an AppState does when Paused or Unpaused, in the setEnable

    - +

    AppStateManager

    @@ -279,7 +279,7 @@ The com.jme3.app.state.AppStateManager holds the list of AppStates for an applic getState(MyAppState.class)Returns the first attached state that is an instance of a subclass of MyAppState.class.
    - +

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

    Best Practices

    - +

    Communication Among AppStates

    @@ -320,7 +320,7 @@ You can use custom accessors to get data from AppStates, to set data in AppState
    this.app.getStateManager().getState(MyAppState.class).doSomeCustomStuffInThisState();
    - +

    Initialize Familiar Class Fields

    @@ -351,5 +351,5 @@ public class MyAppState extends AbstractAppState { }
    - +

    view online version

    \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html index cff5fee85..2cab347f4 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/audio.html @@ -50,7 +50,7 @@ AudioNode boom = new AudioNode(assetManager, "Sound/boom.wav", fal AudioNode MethodUsage - getStatus()Returns either Status.Playing, Status.Stopped, or Status.Paused. + getStatus()Returns either AudioSource.Status.Playing, AudioSource.Status.Stopped, or AudioSource.Status.Paused. getVolume()Returns the volume. @@ -59,14 +59,13 @@ AudioNode boom = new AudioNode(assetManager, "Sound/boom.wav", fal getPitch()Returns the pitch. - +

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

    - +

    Setting AudioNode Properties

    @@ -92,9 +91,9 @@ Note: There are other obvious getters to poll the status of all corresponding se
    setLooping(false)Configures the sound so that, if it is played, it plays once and stops. No looping is the default.
    - +
    - +

    Looping & Ambient Sounds

    @@ -110,9 +109,9 @@ setDirectional(false)
    All 3D effects switched off. This sound is global Looping does not work on streamed sounds.
    - +
    - +

    Positional 3D Sounds

    @@ -127,7 +126,7 @@ setLocalTranslation(???)
    Activates 3D audio: The sound appears to come f setReverbEnabled(true)Reverb is a 3D echo effect that only makes sense with positional AudioNodes. Use Audio Environments to make scenes sound as if they were "outdoors", or "indoors" in a large or small room, etc. The reverb effect is defined by the com.jme3.audio.Environment that the audioRenderer is in. See "Setting Audio Environment Properties" below.
    - +

    Positional 3D sounds require an AudioListener object in the scene (representing the player's ears). @@ -135,7 +134,7 @@ setLocalTranslation(???)Activates 3D audio: The sound appears to come f

    - +

    Directional 3D Sounds

    @@ -151,7 +150,7 @@ setDirection(???)
    Activates 3D audio: This sound can only be heard from setOuterAngle()Set the angle in degrees for the directional audio. The angle is relative to the direction. Note: By default, both angles are 360?? and the sound can be heard from all directions!
    - +

    Directional 3D sounds require an AudioListener object in the scene (representing the player's ears). @@ -159,7 +158,7 @@ setOuterAngle()Set the angle in degrees for the directional audio. The

    - +

    Play, Pause, Stop

    @@ -181,7 +180,7 @@ You can also start playing instances of an AudioNode. Use the playInstance
    myAudioNode.playInstance();
    - +

    The Audio Listener

    @@ -197,7 +196,7 @@ The default AudioListener object listener in SimpleApplicatio }
    - +

    Setting Audio Environment Properties

    @@ -226,7 +225,7 @@ Optionally, You can choose from the following environmental presets from c Closet 1.00f1.0f1.0f1.00f0.15f1.0f0.600f0.0025f0.500f0.0006f
    -
      +
      1. Activate a Environment preset
        • Either use a default, e.g. make you scene sounds like a dungeon environment:
          audioRenderer.setEnvironment(new Environment(Environment.Dungeon));
          @@ -266,5 +265,5 @@ Advanced users find more info about OpenAL and its features here: - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html index 321422f5b..3b6e7638c 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/capture_audio_video_to_a_file.html @@ -571,7 +571,7 @@ public class Advanced extends SimpleApplication {   public void simpleUpdate(float tpf) { motionTimer.update(); - if (music.getStatus() != AudioNode.Status.Playing){ + if (music.getStatus() != AudioSource.Status.Playing){ music.play(); } Vector3f loc = cam.getLocation(); @@ -600,7 +600,7 @@ The - +

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

          @@ -621,7 +621,7 @@ The - +

          More Information

          @@ -653,5 +653,5 @@ listeners can be found here.

          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html index 343f4c792..c709dd228 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/cinematics.html @@ -4,7 +4,11 @@

          -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. You can use cinematics to create and movies/trailers for your game. Internally, Cinematics are implemented as AppStates. +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. You can use cinematics to create and movies/trailers for your game. Another good use case is efficient "destruction physics": Playing back prerecorded flying pieces of debris for demolitions is much faster than calculating them with live physics. +

          + +

          +Internally, Cinematics are implemented as AppStates.

          @@ -24,7 +28,7 @@ This Node can be the rootNode, or a Node that is attached to the rootNode. - +

          Sample Code

            @@ -33,7 +37,7 @@ This Node can be the rootNode, or a Node that is attached to the rootNode.
          - +

          How to Use a Cinematic

          @@ -85,9 +89,9 @@ stateManager.attach(cinematic); cinematic.pause()Pauses the cinematic.
          - + - +

          Events(CinematicEvents)

          @@ -117,7 +121,7 @@ Here is the list of available CinematicEvents that you use as events. Each track AnimationEventUse this to start playing a model animation at a given time (a character walking animation for example)
          - +

          Of course one can make is own event implementation, by extending the AbstractCinematicEvent. @@ -125,7 +129,7 @@ Of course one can make is own event implementation, by extending the AbstractCin

          - +

          MotionEvent

          @@ -189,14 +193,14 @@ To create a MotionEvent, do the following: event.setRotation(quaternion)Sets the rotation. Use together with MotionEvent.Direction.Rotation or MotionEvent.Direction.PathAndRotation.
          - +

          Tip: Most likely you remote-control more than one object in your scene. Give the events and paths useful names such as dragonEvent, dragonPath, heroEvent, heroPath, etc.

          - +

          SoundEvent

          @@ -223,7 +227,7 @@ Details of the constructor:
          - +

          GuiEvent

          @@ -254,7 +258,7 @@ Details of the constructor:
          - +

          AnimationEvent

          @@ -281,7 +285,7 @@ Details of the constructor:
          - +

          Camera Management

          @@ -314,7 +318,7 @@ Then i just have to schedule its activation in the cinematic. I want it to get a
           cinematic.activateCamera(3,???topView???);
          - +

          Customizations

          @@ -328,12 +332,12 @@ You can also create new CinematicEvent by extending +

          Interacting with Cinematics

          - +

          CinematicEventListener

          CinematicEventListener cel = new CinematicEventListener() {
          @@ -355,7 +359,7 @@ You can also create new CinematicEvent by extending 
          +
           

          Physics Interaction

          @@ -365,5 +369,5 @@ Upcoming.

          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html index 4f959e01a..ee83db106 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/custom_controls.html @@ -3,7 +3,7 @@

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

          @@ -43,7 +43,7 @@ To implement game logic for a type of spatial, you will either extend AbstractCo

          - +

          Usage

          @@ -91,7 +91,7 @@ The possibilities are endless. +

          Example Code

          @@ -115,7 +115,7 @@ Existing examples in the code base include:
          - +

          AbstractControl Class

          @@ -155,12 +155,19 @@ Usage: Your custom subclass implements the three methods controlUpdate()/** This is your init method. Optionally, you can modify - * the spatial from here (transform it, initialize userdata, etc). */ + /** This method is called when the control is added to the spatial, + * and when the control is removed from the spatial (setting a null value). + * It can be used for both initialization and cleanup. */ @Override public void setSpatial(Spatial spatial) { super.setSpatial(spatial); - // spatial.setUserData("index", i); // example + /* Example: + if (spatial != null){ + // initialize + }else{ + // cleanup + } + */ }     @@ -214,13 +221,13 @@ See also:
          - +

          The Control Interface

          -

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

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

          @@ -321,7 +328,7 @@ Usage example: }
          - +

          Best Practices

          @@ -387,5 +394,5 @@ vehicleSpatial.addControl(new ManualVehicleControl()); c.steerX(steerX);
          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html index 69f16ed68..b7857aa62 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/debugging.html @@ -132,8 +132,24 @@ You can display a wireframe of the (usually invisible) collision shape around al

          physicsSpace.enableDebug(assetManager);
          +

          +With debugging enabled, colors are used to indicate various types of physical objects: +

          +
            +
          • A magenta wire mesh indicates an active rigid body.
            +
          • +
          • A blue wire mesh indicates a rigid body which is either new or inactive.
            +
          • +
          • A yellow wire mesh indicates a ghost.
            +
          • +
          • Two green arrows indicate a joint.
            +
          • +
          • A pink wire mesh indicates a character.
            +
          • +
          +
          - +

          Wireframe for Animations

          @@ -150,7 +166,7 @@ Making the skeleton visible inside animated models can be handy for debugging an player.attachChild(skeletonDebug);
          - +

          Example: Toggle Wireframe on Model

          @@ -208,7 +224,7 @@ TIP :: To set the line width of wireframe display, use mesh.setLineWidth(lineWid

          - +

          Example: Toggle Wireframe on the scene

          @@ -262,7 +278,7 @@ Then attach the scene processor to the getViewPort().addProcessor(new WireProcessor());
          - +

          See also

            @@ -271,5 +287,5 @@ Then attach the scene processor to the
          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html index 74789eb8c..e7f102619 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/effects_overview.html @@ -298,6 +298,57 @@ Thanks for your awesome contributions! Keep them coming!
          +
          + +

          Creating your own Filters

          +
          + +

          + +Here is an extract taken from @nehon in the forum thread () +

          + +

          +The methods are called in this order (pretty much the same flow as processors): +- initFilter() is called once when the FilterPostPorcessor is initialized or when the filter is added to the processor and this one as already been initialized. +

          + +

          +for each frame the methods are called in that sequence : +- preFrame() occurs before anything happens +- postQueue() occcurs once the queues have been populated (there is one queue per bucket and 2 additional queues for the shadows, casters and recievers). Note that geometries in the queues are the one in the view frustum. +- postFrame occurs once the main frame has been rendered (the back buffer) +

          + +

          +Those methods are optional in a filter, they are only there if you want to hook in the rendering process. +

          + +

          +The material variable is here for convenience. You have a getMaterial method that returns the material that???s gonna be used to render the full screen quad. It just happened that in every implementation I had a material attribute in all my sub-classes, so I just put it back in the abstract class. Most of the time getMaterial returns this attribute. +

          + +

          +Forced-technique can be any technique really, they are more related with the material system than to the filters but anyway. When you use a forced technique the renderer tries to select it on the material of each geometry, if the technique does not exists for the material the geometry is not rendered. +You assume well about the SSAO filer, the normal of the scene are rendered to a texture in a pre pass. +

          + +

          +Passes : these are filters in filters in a way. First they are a convenient way to initialize a FrameBuffer and the associated textures it needs, then you can use them for what ever you want. +For example, a Pass can be (as in the SSAO filter) an extra render of the scene with a forced technique, and you have to handle the render yourself in the postQueue method. +It can be a post pass to do after the main filter has been rendered to screen (for example an additional blur pass used in SSAO again). You have a list of passes called postRenderPass in the Filter abstract class. If you add a pass to this list, it???ll be automatically rendered by the FilterPostProcessor during the filter chain. +

          + +

          +The bloom Filter does an intensive use of passes. +

          + +

          +Filters in a nutshell. + +

          +
          +

          See also:

          @@ -317,5 +368,5 @@ See also:
          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html index 3414c7f1b..88a25a394 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/input_handling.html @@ -158,15 +158,15 @@ In your IDE, use code completion to quickly look up Trigger literals. In the jMo INPUT_MAPPING_CAMERA_POSKEY_CPrints debug output about the camera. - INPUT_MAPPING_MEMORYKEY_MPrints debug output for memtory usage. + INPUT_MAPPING_MEMORYKEY_MPrints debug output for memory usage. INPUT_MAPPING_EXITKEY_ESCAPECloses the application by calling stop();. Typically you do not remove this, unless you replace it by another way of quitting gracefully. - + - +

          3. Add Custom Trigger Mapping

          @@ -192,7 +192,7 @@ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D ...
          - +

          4. Create Listeners

          @@ -206,7 +206,7 @@ The jME3 input manager supports two types of event listeners for inputs: AnalogL

          - +

          ActionListener

          @@ -241,7 +241,7 @@ The jME3 input manager supports two types of event listeners for inputs: AnalogL };
          - +

          AnalogListener

          @@ -274,7 +274,7 @@ The jME3 input manager supports two types of event listeners for inputs: AnalogL };
          - +

          4. Register Mappings to Listeners

          @@ -303,7 +303,7 @@ As you see, you can add several listeners in one String array. You can call the

          - +

          5. Implement Actions in Listeners

          @@ -323,7 +323,7 @@ Make use of the distinction between if and else if in
          - +

          ActionListener

          @@ -344,7 +344,7 @@ In the most common case, you want an action to be triggered once, in the moment };
          - +

          AnalogListener

          @@ -365,7 +365,7 @@ The following example shows how you define actions with an AnalogListener. Thies };
          - +

          Let Users Remap Keys

          @@ -384,5 +384,5 @@ The abstraction of separating triggers and mappings has the advantage that you c
          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html index 024d3220d..02d29c160 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/light_and_shadow.html @@ -75,7 +75,7 @@ You can get a list of all lights added to a Spatial by calling getWorldLig

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

          @@ -88,7 +88,7 @@ lamp_light.setPosition(new Vector3f(lamp_geo.getLocalTranslation( rootNode.addLight(lamp_light); - +

          DirectionalLight

          @@ -110,7 +110,7 @@ sun.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal( rootNode.addLight(sun);
          - +

          SpotLight

          @@ -140,7 +140,7 @@ If you want the spotlight to follow the flycam, repeat the setDirection(???) and

          - +

          AmbientLight

          @@ -163,7 +163,7 @@ Example: mylight.setColor(ColorRGBA.White.mult(1.3f));

          - +

          Light Follows Spatial

          @@ -181,117 +181,84 @@ Obviously, this does not apply to AmbientLights, which have no position.

          - -

          Simple Lighting

          + +

          BasicShadowRenderer (deprecated)

          -Full sample code: +Full code sample

            -
          • -
          • -
          • +
          -

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

          - -

          - -

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

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

          - -

          - -

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

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

          -
          - -

          BasicShadowRenderer

          + +

          Casting Shadows

          -Full code sample +For each type of non-ambient light source, JME3 implements two ways to simulate geometries casting shadows on other geometries:

            -
          • +
          • a shadow renderer (which you apply to a viewport) and
            +
          • +
          • a shadow filter (which you can add to a viewport's filter post-processor).
          - +
          + + + + + + + + + + + + + + + +
          light source class shadow renderer class shadow filter class
          DirectionalLight DirectionalLightShadowRenderer DirectionalLightShadowFilter
          PointLight PointLightShadowRenderer PointLightShadowFilter
          SpotLight SpotLightShadowRenderer SpotLightShadowFilter
          AmbientLight (not applicable) (not applicable)
          +

          -Use the Shadow Renderer to make Geometries with Lighting.j3md-based Materials cast and receive basic drop shadows. This fast and simple implementation of a shadow effect is good for scenes with flat floors, but looks less realistic on uneven terrains. To use it, you add a jME SceneProcessor named com.jme3.shadow.BasicShadowRenderer to the viewPort. +You only need one shadow simulation per light source: if you use shadow rendering, you won't need a shadow filter and vice versa. Which way is more efficient depends partly on the complexity of your scene. All six shadow simulation classes have similar interfaces, so once you know how to use one, you can easily figure out the rest.

          - +Shadow calculations (cast and receive) have a performance impact, so use them sparingly. With shadow renderers, you can turn off shadow casting and/or shadow receiving for individual geometries, for portions of the scene graph, or for the entire scene:

          -
          BasicShadowRenderer bsr;
          -...
          -public void simpleInitApp() {
          -    ...
          -    bsr = new BasicShadowRenderer(assetManager, 256);
          -    bsr.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
          -    viewPort.addProcessor(bsr);
          -    ...
          +
          spatial.setShadowMode(ShadowMode.Inherit);     // This is the default setting for new spatials.
          +rootNode.setShadowMode(ShadowMode.Off);        // Disable shadows for the whole scene, except where overridden. 
          +wall.setShadowMode(ShadowMode.CastAndReceive); // The wall can cast shadows and also receive them.
          +floor.setShadowMode(ShadowMode.Receive);       // Any shadows cast by the floor would be hidden by it.
          +airplane.setShadowMode(ShadowMode.Cast);       // There's nothing above the airplane to cast shadows on it.
          +ghost.setShadowMode(ShadowMode.Off);           // The ghost is translucent: it neither casts nor receives shadows.

          -Shadow calculations (cast and receive) have a performance impact, therefor we recommend to use them smartly. Switch off the default shadow mode for the whole scene graph, and then specify the shadow behaviour individually for every scene node that needs shadows: You specifiy whether it casts shadows, receives shadows, both (slower), or neither (faster). +Both shadow renderers and shadow filters use shadow modes to determine which objects can cast shadows. However, only the shadow renderers pay attention to shadow modes when determining which objects receive shadows. With a shadow filter, shadow modes have no effect on which objects receive shadows.

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

          DirectionalLightShadowRenderer

          -

          - -Full sample code +Here's a sample application which demonstrates both DirectionalLightShadowRenderer and DirectionalLightShadowFilter:

          + +

          + +Here is the key code fragment: + +

                  DirectionalLight sun = new DirectionalLight();
                   sun.setColor(ColorRGBA.White);
                   sun.setDirection(cam.getDirection());
          @@ -310,8 +277,24 @@ Full sample code
                   fpp.addFilter(dlsf);
                   viewPort.addProcessor(fpp);
          +

          +Constructor arguments: + * your AssetManager object + * size of the rendered shadow maps, in pixels per side (512, 1024, 2048, etc???) + * the number of shadow maps rendered (more shadow maps = better quality, but slower) +

          + +

          +Properties you can set: + * setDirection(Vector3f) ??? the direction of the light + * setLambda(0.65f) ??? to reduce the split size + * setShadowIntensity(0.7f) ??? shadow darkness (1=black, 0=invisible) + * setShadowZextend(float) ??? distance from camera to which shadows will be computed + +

          +
          - +

          Parallel-Split Shadow Map (deprecated)

          @@ -326,10 +309,6 @@ Full sample code

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

          - -

          private PssmShadowRenderer pssmRenderer;
          @@ -340,42 +319,8 @@ public void simpleInitApp() {
               pssmRenderer.setDirection(new Vector3f(-.5f,-.5f,-.5f).normalizeLocal()); // light direction
               viewPort.addProcessor(pssmRenderer);
          -

          -The constructor expects the following values: -

          -
            -
          • Your assetManager object
            -
          • -
          • The size of the rendered shadowmaps (512, 1024, 2048, etc???)
            -
          • -
          • The number of shadow maps rendered (the more shadow maps, the more quality, the less FPS).
            -
          • -
          - -

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

          -
            -
          • setDirection(Vector3f) ??? the direction of the light
            -
          • -
          • setLambda(0.65f) ??? Factor to use to reduce the split size
            -
          • -
          • setShadowIntensity(0.7f) ??? shadow darkness (1 black, 0 invisible)
            -
          • -
          • setShadowZextend() ??? distance how far away from camera shadows will still be computed
            -
          • -
          - -

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

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

          Screen Space Ambient Occlusion

          @@ -384,17 +329,21 @@ terrain.setShadowMode(ShadowMode.Receive); Full sample code

            -
          • , ??? Screen-Space Ambient Occlusion shadows
            +
          • ??? Screen-Space Ambient Occlusion shadows
          • ??? Screen-Space Ambient Occlusion shadows plus transparancy
          • -
          • +

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

          + +

          +In JME3, SSAO is implemented by adding an instance of com.jme3.post.SSAOFilter to a viewport which already simulates shadows using another method such as DirectionalLightShadowRenderer.

          FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
           SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.92f, 0.33f, 0.61f);
          @@ -407,5 +356,5 @@ viewPort.addProcessor(fpp);

          - +

          view online version

          \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html index 77760caf3..1f6274912 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/mouse_picking.html @@ -22,7 +22,7 @@ See Input Handlin

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

            @@ -72,7 +72,7 @@ The following example rotates Spatials named "Red Box" or "Blue B }; - +

            Pick a Target Using the Mouse Pointer

            @@ -146,5 +146,5 @@ The following example rotates Spatials named "Red Box" or "Blue B
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html index efb29180f..b0a93d3a5 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/multithreading.html @@ -4,7 +4,7 @@

            -jME3 is similar to Swing in that for speed and efficiency all changes to the world must be made in a single update thread. This is happening automatically if using Controls and AppSates update metod or simpleUpdate however whenever you pass work to another thread you need to hand the results back to the main jME3 thread before making any changes to the scene graph. +jME3 is similar to Swing in that, for speed and efficiency, all changes to the scene graph must be made in a single update thread. If you make changes only in Control.update(), AppState.update(), or SimpleApplication.simpleUpdate(), this will happen automatically. However, if you pass work to another thread, you may need to pass results back to the main jME3 thread so that scene graph changes can take place there.

            public void rotateGeometry(final Geometry geo, final Quaternion rot) {
                 mainApp.enqueue(new Callable<Spatial>() {
            @@ -24,7 +24,7 @@ If the processing thread needs to wait or needs the return value then get(
             

            - +

            Multithreading Optimization

            @@ -46,7 +46,7 @@ Effectively, each for-loop in the main update loop might be a chance for multith

            - +

            Java Multithreading

            @@ -65,7 +65,7 @@ The java.util.concurrent package provides a good foundation for multithreading a
            - +

            Multithreading in jME3

            @@ -83,7 +83,7 @@ To avoid slowdown, we decide to keep the pathfinding operations in the NPC Contr

            - +

            Executor

            @@ -110,7 +110,7 @@ In your simple application you can override the destroy method and shutdown the }
            - +

            Control Class Fields

            @@ -130,7 +130,7 @@ Here we also created the Future variable to track the state of this task.

            - +

            Control Update() Method

            @@ -143,7 +143,7 @@ Next let's look at the update() call of the Control where the time-intensiv //If we have no waylist and not started a callable yet, do so! if(wayList == null && future == null){ //set the desired location vector, after that we should not modify it anymore - //because its being accessed on the other thread! + //because it's being accessed on the other thread! desiredLocation.set(getGoodNextLocation()); //start the callable on the executor future = executor.submit(findWay); // Thread starts! @@ -178,7 +178,7 @@ Remember not to mess with the class fields after starting the thread, because th

            - +

            The Callable

            @@ -230,7 +230,17 @@ private Callable<MyWayList> findWay = new Callable<MyWayList>(&# };
            - + +

            Useful Links

            +
            + +

            + +High level description which describes how to manage the game state and the rendering in different threads - +

            + +
            +

            Conclusion

            @@ -249,5 +259,5 @@ The cool thing about this approach is that every entity creates one self-contain
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html index ea71f7074..78b2a1974 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/networking.html @@ -53,15 +53,17 @@ You can register several types of listeners to be notified of changes.
          1. ConnectionListeners inform the Server about HostedConnection arrivals and removals, e.g. if a client joins or quits.
          2. +
          3. ErrorListeners inform the Client about network exceptions that have happened, e.g. if the server crashes, the client throws a ConnectorException, this can be picked up so that the application can do something about it.
            +
          4. - +

            Client and Server

            - +

            Creating a Server

            @@ -96,7 +98,7 @@ When you run this app on a host, the server is ready to accept clients. Let'

            - +

            Creating a Client

            @@ -135,7 +137,7 @@ When you run this client, it connects to the server.

            - +

            Getting Info About a Client

            @@ -158,7 +160,7 @@ The server refers to a connected client as com.jme3.network.HostedConnection obj myServer.getConnection(0)Server gets the first (0), second (1), etc, connected HostedConnection object (one client).
            - +

            Your game can define its own game data based on whatever criteria you want, typically these include player ID and state. If the server needs to look up player/client-specific information, you can store this information directly on the HostedConnection object. The following examples read and write a custom Java object MyState in the HostedConnection object conn: @@ -175,14 +177,14 @@ Your game can define its own game data based on whatever criteria you want, typi MyState state = conn.getAttribute("MyState") Server can read an attribute of the HostedConnection. - + - +

            Messaging

            - +

            Creating Message Types

            @@ -203,7 +205,7 @@ You must register each message type to the com.jme3.network.serializing.Serializ
            Serializer.registerClass(HelloMessage.class);
            - +

            Responding to Messages

            @@ -260,7 +262,7 @@ For each message type, register a server listener to the server:
            myServer.addMessageListener(new ServerListener(), HelloMessage.class);
            - +

            Creating and Sending Messages

            @@ -297,7 +299,7 @@ The last two broadcasting methods use com.jme3.network.Filters to select a subse

            - +

            Identification and Rejection

            @@ -308,21 +310,22 @@ The ID of the Client and HostedConnection are the same at both ends of a connect
            ... myClient.getId() ...

            -A server has a game version and game name property. Each client expects to communicate with a server with a certain game name and version. Test first whether the game name matches, and then whether game version matches, before sending any messages! If they do not match, you should refuse to connect, because unmatched clients and servers will likely miscommunicate. +A server has a game version and game name property. Each client expects to communicate with a server with a certain game name and version. Test first whether the game name matches, and then whether game version matches, before sending any messages! If they do not match, SpiderMoney will reject it for you, you have no choice in the mater. This is so the client and server can avoid miscommunication.

            Typically, your networked game defines its own attributes (such as player ID) based on whatever criteria you want. If you want to look up player/client-specific information beyond the game version, you can set this information directly on the Client/HostedConnection object (see Getting Info About a Client).

            +

            - +

            Closing Clients and Server Cleanly

            - +

            Closing a Client

            @@ -338,7 +341,7 @@ You must override the client's destroy() method to close the connection cle }
            - +

            Closing a Server

            @@ -354,7 +357,7 @@ You must override the server's destroy() method to close the connection whe }
            - +

            Kicking a Client

            @@ -365,7 +368,7 @@ The server can kick a HostedConnection to make it disconnect. You should provide
            conn.close("We kick cheaters.");
            - +

            Listening to Connection Notification

            @@ -375,13 +378,18 @@ The server and clients are notified about connection changes.

            - +

            ClientStateListener

            The com.jme3.network.ClientStateListener notifies the Client when the Client has fully connected to the server (including any internal handshaking), and when the Client is kicked (disconnected) from the server. +

            + +

            +

            The ClientStateListener when it receives a network exception applies the default close action. This just stops the client and you'll have to build around it so your application knows what to do. If you need more control when a network exception happens and the client closes, you may want to investigate in a ErrorListener. +

            @@ -395,7 +403,7 @@ The com.jme3.network.ClientStateListener notifies the Client when the Client has
            public void clientDisconnected(Client c, DisconnectInfo info){} Implement here what happens after the server kicks this client. For example, display the DisconnectInfo to the user.
            - +

            First implement the ClientStateListener interface in the Client class. Then register it to myClient in MyGameClient's simpleInitApp() method: @@ -403,7 +411,7 @@ First implement the ClientStateListener interface in the Client class. Then regi

            myClient.addClientStateListener(this);
            - +

            ConnectionListener

            @@ -423,7 +431,7 @@ The com.jme3.network.ConnectionListener notifies the Server whenever new HostedC public void connectionRemoved(Server s, HostedConnection c){} Implement here what happens after a HostedConnection has left. E.g. a player has quit the game and the server removes his character.
            - +

            First implement the ConnectionListener interface in the Server class. Then register it to myServer in MyGameServer's simpleInitApp() method. @@ -432,7 +440,55 @@ First implement the ConnectionListener interface in the Server class. Then regis

            myServer.addConnectionListener(this);
            - + +

            ErrorListener

            +
            + +

            + +The com.jme3.network.ErrorListener is a listener for when network exception happens. This listener is built so that you can override the default actions when a network exception happens. +

            + +

            +

            If you intend on using the default network mechanics, don't use this! +If you do override this, make sure you add a mechanic that can close the client otherwise your client will get stuck open and cause errors. +

            + +

            +
            + + + + + + +
            ErrorListener interface method Purpose
            public void handleError(Client c, Throwable t){} Implemenent here what happens after a exception affects the network .
            + +

            + +

            This interface was built for the client and server, but the code has never been put on the server to handle this listener. +

            +

            + +

            +First implement the ErrorListener interface in the client class. Then you need to register it to myClient in MyGameClients's simpleInitApp() method. +

            +
            myClient.addErrorListener(this);
            + +

            +In the class that implements the ErrorListener, a method would of been added call handleError(Client s, Throwable t). Inside this method to get you started, you going to want to listen for an error. To do this you're going to want a bit of code like this. +

            +
            if(t instanceof exception) {
            +     //Add your own code here
            +}
            + +

            +Replace exception part in the if statement for the type of exception that you would like it to handle. + +

            + +
            +

            UDP versus TCP

            @@ -450,7 +506,7 @@ message2.setReliable(false); // UDP
            - +

            Important: Use Multi-Threading

            @@ -474,7 +530,7 @@ For general advice, see the articles +

            Troubleshooting

            @@ -489,5 +545,5 @@ If you have set up a server in your home network, and the game clients cannot re
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html index 4a9fdd813..52a536314 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui.html @@ -154,10 +154,16 @@ Learn more from the NiftyGUI page:
          5. +
          6. +
          7. +
          8. +
          9. +
          10. +
          11. - +

            Next Steps

            @@ -170,10 +176,12 @@ Now that you understand the concepts and know where to find more information, le
          12. +
          13. +
          14. - +

            Nifty Logging (Nifty 1.3.1)

            @@ -191,5 +199,5 @@ Logger.getLogger("NiftyInputEventHandlingLog").setLevel(Level.SEVERE);
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html index 65166b537..d2ad0dba8 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_java_interaction.html @@ -26,7 +26,7 @@ In the previous parts of the tutorial, you created a two-screen user interface.

            -To let a Nifty screen communicate with the Java application, you register a ScreenController to every NiftyGUI screen. You create a ScreenController by creating a Java class that implements the de.lessvoid.nifty.screen.ScreenController interface and its abtract methods. +To let a Nifty screen communicate with the Java application, you register a ScreenController to every NiftyGUI screen. You create a ScreenController by creating a Java class that implements the de.lessvoid.nifty.screen.ScreenController interface and its abstract methods.

            @@ -100,7 +100,7 @@ Now the Java class MyStartScreen and this +

            Make GUI and Java Interact

            @@ -114,7 +114,7 @@ Use any combination of the three following approaches to make Java classes inter

            - +

            GUI Calls a Void Java Method

            @@ -214,7 +214,7 @@ The quitGame() example shows that you have access to the application app
            - +

            GUI Gets Return Value from Java Method

            @@ -253,7 +253,7 @@ You can use this for Strings and numeric values (e.g. when you read settings fro

            - +

            Java Modifies Nifty Elements and Events

            @@ -293,7 +293,7 @@ For this to work, there already needs to be a (possibly inactive) <inte
            <interact onClick="doNothing()"/>
            - +

            Next Steps

            @@ -317,5 +317,5 @@ You're done with the basic Nifty
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html index ca47d84bf..d6ec99bd6 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_scenarios.html @@ -104,8 +104,22 @@ public StartScreenState(SimpleApplication app){   } +

            + +

            It is not sufficient to just inherit from AbstractAppState. You need to instantiate your controller class, register it with app's stateManager and then pass it to nifty. See code sample below. +

            +

            +
            public class TestNiftyGui extends SimpleApplication {
            +  public void simpleInitApp() {
            +     StartScreenState startScreenState = new StartScreenState(this);
            +     stateManager.attach(startScreenState);
            +     // [...] boilerplate init nifty omitted
            +     nifty.fromXml("Interface/myGui.xml", "start", startScreenState); //one of the XML screen elements needs to reference StartScreenState controller class
            +  }
            +}
            + - +

            Know Your Variables

            @@ -125,14 +139,14 @@ public StartScreenState(SimpleApplication app){
            ${PROP.key} looks up key in the Nifty properties. Use Nifty.setGlobalproperties(properties) and Nifty.getGlobalproperties("key"). Or SystemGetProperties(key);
            - +

            See also:

            - +

            Use ScreenControllers for Mutally Exclusive Functionality

            @@ -152,7 +166,7 @@ For example, create a MyHudScreen.java for the hud scr
            - +

            Create a "Loading..." Screen

            @@ -162,7 +176,7 @@ Get the full Load

            - +

            Create a Popup Menu

            @@ -172,7 +186,7 @@ Get the full +

            Add Visual Effects

            @@ -206,7 +220,7 @@ Learn more from the NiftyGUI page:
            - +

            Add Sound Effects

            @@ -223,7 +237,7 @@ Playing sounds using Nifty is also possible with a playSound effect </label>
            - +

            Pass ClickLoc From Nifty to Java

            @@ -253,7 +267,7 @@ You can name the method (here clicked) what ever you like, as long

            - +

            Load Several XML Files

            @@ -274,7 +288,7 @@ stateManager.attach(optionsControl); guiViewPort.addProcessor(niftyDisplay);
            - +

            Register additional explicit screen controllers

            @@ -293,7 +307,7 @@ nifty.registerScreenController(new OptionsScreenController(randomConstru nifty.addXml("Interface/Screens/OptionsScreen.xml");
            - +

            Design Your Own Styles

            @@ -345,5 +359,5 @@ Learn more from the NiftyGUI page:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html index 5450391f5..81fa57af9 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/nifty_gui_xml_layout.html @@ -100,7 +100,7 @@ The following minimal XML
            <?xml version="1.0" encoding="UTF-8"?>
             <nifty xmlns="http://nifty-gui.sourceforge.net/nifty-1.3.xsd"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            -  xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd">
            +  xsi:schemaLocation="http://nifty-gui.sourceforge.net/nifty-1.3.xsd">
               <screen id="start">
                 <!-- ... -->
               </screen>
            @@ -118,7 +118,7 @@ Every Nifty GUI must have a
             

            - +

            Make Layers

            @@ -150,7 +150,7 @@ In a layer, you can now add panels and arrange them. Panels are containers that

            - +

            Make Panels

            @@ -203,7 +203,7 @@ The result should look as follows:

            - +

            Adding Content to Panels

            @@ -213,7 +213,7 @@ See also <

            - +

            Add Images

            @@ -258,7 +258,7 @@ This image is scaled to use 50% of the height and 30% of the width of its contai

            - +

            Add Static Text

            @@ -285,7 +285,7 @@ The font used is jME3's default font "Interface/Fonts/Default.fnt"

            - +

            Add Controls

            @@ -351,7 +351,7 @@ Nifty additionally offers many customizable controls such as check boxes, text f

            - +

            Intermediate Result

            @@ -369,7 +369,7 @@ Compare this result with the layout draft above.

            - +

            Next Steps

            @@ -391,5 +391,5 @@ Integrate the GUI into the g
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html index 4da7e2e78..d2af4ff00 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/physics.html @@ -360,12 +360,12 @@ Click the links for details on the special PhysicsControls. This article is abou -

            PhysicsControls Code Samples

            +

            Physics Control Code Samples

            -The PhysicsControl constructors expect a Collision Shape and a mass (a float in kilogram). The most commonly used PhysicsControl is the RigidBodyControl: +The most commonly used physics control is RigidBodyControl. The RigidBodyControl constructor takes up to two parameters: a collision shape and a mass (a float in kilograms). The mass parameter also determines whether the object is dynamic (movable) or static (fixed). For a static object such as a floor or wall, specify zero mass.

            RigidBodyControl myThing_phys = 
                 new RigidBodyControl( myThing_shape , 123.0f ); // dynamic
            @@ -373,7 +373,7 @@ The PhysicsControl constructors expect a Collision Shape and a mass (a float in new RigidBodyControl( myDungeon_shape , 0.0f ); // static

            -

            When you create the PhysicsControl, the mass value makes an important distinction: Set the mass to a non-zero value to create a dynamic object that can fall or roll, etc. Set the mass value to zero to create a static object, such as floor, wall, etc. If you give your floor a mass, it will fall out of the scene! +

            If you give your floor a non-zero mass, it will fall out of the scene!

            @@ -395,10 +395,11 @@ gameLevel.addControl(new RigidBodyControl(0.0f)); // explicit ze

            Spheres and Boxes automatically fall back on the correct default CollisionShape if you do not specify a CollisionShape in the RigidBodyControl constructor. Complex static objects can fall back on MeshCollisionShapes, unless it is a Node, in which case it will become a CompoundCollisionShape containing a MeshCollisionShape

            +

            - +

            Add PhysicsControl to Spatial

            @@ -415,7 +416,7 @@ For each physical Spatial in the scene:
            - +

            Add PhysicsControl to PhysicsSpace

            @@ -442,7 +443,7 @@ myThing_geo.removeFromParent();

            - +

            Changing the Scale of a PhysicsControl

            @@ -481,7 +482,7 @@ With the corresponding output below:

            - +

            PhysicsSpace Code Samples

            @@ -511,9 +512,9 @@ setWorldMin(new Vector3f(-10000f, -10000f, -10000f));Specifies the size setCcdMotionThreshold()The amount of motion in 1 physics tick to trigger the continuous motion detection in moving objects that push one another. Rarely used, but necessary if your moving objects get stuck or roll through one another.
            - +
            - +

            Specify Physical Properties

            @@ -553,7 +554,7 @@ This setting has an impact on performance, so use it sparingly. Brick: Rubber ball: 1.0f
            - +

            On a RigidBodyControl, you can apply the following physical forces: @@ -574,9 +575,9 @@ On a RigidBodyControl, you can apply the following physical forces: (See detailed explanation below.) - + - +

            Kinematic vs Dynamic vs Static

            @@ -626,7 +627,7 @@ setKinematic(true);setMass(1f);
            setKinematic(false);
            - +

            When Do I Use Kinematic Objects?

            @@ -649,7 +650,7 @@ setKinematic(false);

            - +

            Forces: Moving Dynamic Objects

            @@ -687,7 +688,7 @@ Use the following methods to move dynamic physical objects. clearForces()Cancels out all forces (force, torque) etc and stops the motion.
            - +

            It is technically possible to position PhysicsControls using setLocalTranslation(), e.g. to place them in their start position in the scene. However you must be very careful not to cause an "impossible state" where one physical object overlaps with another! Within the game, you typically use the setters shown here exclusively. @@ -727,7 +728,7 @@ removeCollideWithGroup(COLLISION_GROUP_01)Collision Groups are integer setCcdSweptSphereRadius(.5f)Bullet does not use the full collision shape for continuous collision detection, instead it uses a "swept sphere" shape to approximate a motion, which can be imprecise and cause strange behaviors such as objects passing through one another or getting stuck. Only relevant for fast moving dynamic bodies.
            - +

            You can setApplyPhysicsLocal(true) for an object to make it move relatively to its local physics space. You would do that if you need a physics space that moves with a node (e.g. a spaceship with artificial gravity surrounded by zero-g space). By default, it's set to false, and all movement is relative to the world. @@ -735,7 +736,7 @@ removeCollideWithGroup(COLLISION_GROUP_01)Collision Groups are integer

            - +

            Best Practices

              @@ -771,7 +772,7 @@ removeCollideWithGroup(COLLISION_GROUP_01)Collision Groups are integer
            -
            +
            1) Inertia is calculated for kinematic objects, and you need mass to do that.
            diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html index b106f4fc1..f19599224 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/post-processor_water.html @@ -188,11 +188,11 @@ manager.loadTexture("Textures/wavenormals.png") )This normal water.setWaterColor(ColorRGBA.Brown.mult(2.0f));Sets the main water color.greenish blue
            -Vector3f(0.0f,0.5f,0.5f,1.0f) +ColorRGBA(0.0f,0.5f,0.5f,1.0f) water.setDeepWaterColor(ColorRGBA.Brown);Sets the deep water color.dark blue
            -Vector3f(0.0f, 0.0f,0.2f,1.0f) +ColorRGBA(0.0f, 0.0f,0.2f,1.0f) water.setWaterTransparency(0.2f);Sets how fast colors fade out. use this to control how clear (e.g. 0.05f) or muddy (0.2f) water is. 0.1f @@ -201,7 +201,7 @@ Vector3f(0.0f, 0.0f,0.2f,1.0f) water.setColorExtinction(new Vector3f(10f,20f,30f));Sets At what depth the refraction color extincts. The three values are RGB (red, green, blue) in this order. Play with these parameters to "muddy" the water.Vector3f(5f,20f,30f)
            -
            +
            @@ -216,7 +216,7 @@ water.setRadius(260);
            Water method example Effects: ShoreDefault
            Limit the water filter to a semisphere with the gi water.setUseHQShoreline(false);Renders shoreline with better quality ?true
            -
            +
            @@ -234,7 +234,7 @@ water.setRadius(260);
            Water method example Effects: FoamDefault
            Limit the water filter to a semisphere with the gi manager.loadTexture("Textures/foam.png") )This foam texture will be used with WrapMode.Repeat"Common/MatDefs/Water/Textures/foam.jpg"
            -
            +
            @@ -260,9 +260,9 @@ manager.loadTexture("Textures/foam.png") )
            Water method example Effects: LightDefault
            This foam texture w water.setReflectionMapSize(256)Sets the size of the reflection map. The higher, the better the quality, but the slower the effect.512
            - + - +

            Sound Effects

            @@ -291,5 +291,5 @@ See also:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html index c092e2096..28a753092 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/terrain.html @@ -7,14 +7,14 @@ The goal of TerraMonkey is to provide a base implementation that will be usable for 80% of people's goals, while providing tools and a good foundation for the other 20% to build off of. Check out the videos in the following announcements:

              -
            • +
            • -
            • +
            - +

            Overview

            @@ -38,7 +38,7 @@ TerraMonkey is a GeoMipMapping quad tree of terrain tiles that supports real tim
            - +

            Current Features:

              @@ -55,7 +55,7 @@ TerraMonkey is a GeoMipMapping quad tree of terrain tiles that supports real tim
            - +

            Planned Features:

              @@ -66,7 +66,7 @@ TerraMonkey is a GeoMipMapping quad tree of terrain tiles that supports real tim
            - +

            Sample Code

              @@ -83,7 +83,7 @@ TerraMonkey is a GeoMipMapping quad tree of terrain tiles that supports real tim
            - +

            Geo Mip Mapping

            @@ -105,7 +105,7 @@ GeoMipMapping in TerraMonkey has been split into several parts: the terrain quad

            - +

            Terrain Quad Tree

            @@ -115,7 +115,7 @@ TerraMonkey is a quad tree. Each node is a TerrainQuad, and each leaf is a Terra

            - +

            Texture Splatting

            @@ -185,7 +185,7 @@ Here are the names of TerrainLighting.j3md's material properties:

            -Video cards support a maximum of 16 Splat textures total. This means you can only use a subset of material properties at the same time! +OpenGL supports a maximum of 16 samplers in any given shader. This means you can only use a subset of material properties at the same time if you use the terrain's default lighting shader (TerrainLighting.j3md)!

            @@ -237,7 +237,7 @@ You can hand-paint Alpha, Diffuse, Glow, and Specular maps in a drawing program,

            - +

            Code Sample: Terrain.j3md

            @@ -304,5 +304,5 @@ PS: As an alternative to an image-based height map, you can also generate a Hill
            heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html index 80bebd27d..362551789 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/advanced/walking_character.html @@ -419,6 +419,15 @@ This method resets the walk animation. }   public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { } + + + +

            See also

            +
            +
              +
            • +
            • +
            documentation, physics, @@ -430,5 +439,5 @@ public void onAnimChange(AnimControl control, AnimChannel channel, String an
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png index fb9a2ce59..b98e4f987 100644 Binary files a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png and b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/beginner-materials.png differ diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html index 07f98cf87..db71a3421 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_asset.html @@ -55,7 +55,7 @@ public class HelloAssets extends SimpleApplication { rootNode.attachChild(teapot);   // Create a wall with a simple texture from test_data - Box box = new Box(Vector3f.ZERO, 2.5f,2.5f,1.0f); + Box box = new Box(2.5f,2.5f,1.0f); Spatial wall = new Geometry("Box", box ); Material mat_brick = new Material( assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); @@ -93,7 +93,7 @@ Build and run the code sample. You should see a green Ninja with a colorful teap

            - +

            The Asset Manager

            @@ -115,16 +115,17 @@ The AssetManager can load files from: The following is the recommended directory structure for storing assets in your project directoy:

            -
            MyGame/assets/Interface/
            +
            MyGame/assets/               
            +MyGame/assets/Interface/
             MyGame/assets/MatDefs/
             MyGame/assets/Materials/
            -MyGame/assets/Models/
            +MyGame/assets/Models/       <-- your .j3o models go here
             MyGame/assets/Scenes/
             MyGame/assets/Shaders/
            -MyGame/assets/Sounds/
            -MyGame/assets/Textures/
            -MyGame/build.xml            <-- Ant build script
            -MyGame/src/...              <-- Java sources go here
            +MyGame/assets/Sounds/       <-- your audio files go here
            +MyGame/assets/Textures/     <-- your textures go here
            +MyGame/build.xml            <-- Default Ant build script
            +MyGame/src/...              <-- your Java sources go here
             MyGame/...

            @@ -132,7 +133,7 @@ This is just a suggested best practice, and it's what you get by default wh

            - +

            Loading Textures

            @@ -141,7 +142,7 @@ This is just a suggested best practice, and it's what you get by default wh Place your textures in a subdirectory of assets/Textures/. Load the texture into the material before you set the Material. The following code sample is from the simpleInitApp() method and loads a simple wall model:

            // Create a wall with a simple texture from test_data
            -Box box = new Box(Vector3f.ZERO, 2.5f,2.5f,1.0f);
            +Box box = new Box(2.5f,2.5f,1.0f);
             Spatial wall = new Geometry("Box", box );
             Material mat_brick = new Material( 
                 assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            @@ -156,7 +157,7 @@ In this case, you 
             
             
            - +

            Loading Text and Fonts

            @@ -179,7 +180,7 @@ guiNode.attachChild(helloText);

            - +

            Loading a Model

            @@ -203,7 +204,7 @@ Note that you do not need to create a Material if you exported the model with a

            - +

            Loading Assets From Custom Paths

            @@ -233,18 +234,19 @@ JME3 offers ClasspathLocator, ZipLocator, FileLocator, HttpZipLocator, and UrlLo

            - +

            Creating Models and Scenes

            -To create 3D models and scenes, you need a 3D Mesh Editor with an OgreXML Exporter plugin. For example, you can . -You use the SDK to load models, convert models and create scenes from them. +To create 3D models and scenes, you need a 3D Mesh Editor. If you don't have any tools, install Blender and the OgreXML Exporter plugin. +Then you and export them to your project. +Then you use the SDK to load models, convert models, and create 3D scenes from them.

            -If you use Blender, export your models as Ogre XML meshes with materials as follows: +Example: From Blender, you export your models as Ogre XML meshes with materials as follows:

            1. Open the menu File > Export > OgreXML Exporter to open the exporter dialog.
              @@ -272,17 +274,28 @@ If you use Blender, export your models as Ogre +

              Model File Formats

              -JME3 can load Ogre XML models + materials, Ogre DotScenes, as well as Wavefront OBJ+MTL models. The loadModel() code works with these files when you run the code directly from the jMonkeyEngine SDK. +JME3 can convert and load

              +
                +
              • Ogre XML models + materials,
                +
              • +
              • Ogre DotScenes,
                +
              • +
              • Wavefront OBJ + MTL models,
                +
              • +
              • .Blend files.
                +
              • +

              -If you build the executables using the default build script, then the original model files (XML, OBJ, etc) are not included. When you run the executable, you get an error message if you try to load any models directly: + +The loadModel() method loads these original file formats when you run your code directly from the SDK. If you however build the executables using the default build script, then the original model files (XML, OBJ, etc) are not included. This means, when you run the executable outside the SDK, and load any original models directly, you get the following error message:

              com.jme3.asset.DesktopAssetManager loadAsset
               WARNING: Cannot locate resource: Models/Ninja/Ninja.mesh.xml
              @@ -291,11 +304,11 @@ SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
               java.lang.NullPointerException

              -Loading the XML/OBJ files directly is only acceptable during the development phase. If your graphic designer pushes updated files to the asset directory, you can quickly review the latest version in your development environment. +You see that loading the XML/OBJ/Blend files directly is only acceptable during the development phase in the SDK. For example, every time your graphic designer pushes updated files to the asset directory, you can quickly review the latest version in your development environment.

              -For testing and for the final release build, you use .j3o files exclusively. J3o is an optimized binary format for jME3 applications, and .j3o files are automatically included in the distributable JAR file by the build script. When you do QA test builds or are ready to release, use the SDK to convert all .obj/.scene/.xml/.blend files to .j3o files, and only load the .j3o versions. +But for QA test builds and for the final release build, you use .j3o files exclusively. J3o is an optimized binary format for jME3 applications. When you do QA test builds, or are ready to release, use the SDK to convert all .obj/.scene/.xml/.blend files to .j3o files, and update all code to load the .j3o files. The default build script automatically packages .j3o files in the executables.

              @@ -306,18 +319,20 @@ Open your JME3 Project in the jMonkeyEngine +

              Loading Models and Scenes

              @@ -346,9 +361,9 @@ rootNode.attachChild(scene);
              - +
              - +

              Excercise - How to Load Assets

              @@ -435,7 +450,7 @@ Here is a third method you must know, loading a scene/model from a .j3o file:
            2. In the projects window, browse to the assets/Scenes/town directory.
            3. -
            4. Right-click the main.scene and convert the scene to binary: The jMoneyPlatform generates a main.j3o file.
              +
            5. Right-click the main.scene and convert the scene to binary: The jMonkeyPlatform generates a main.j3o file.
            6. Add the following code under simpleInitApp() {
                  Spatial gameLevel = assetManager.loadModel("Scenes/town/main.j3o");
                   gameLevel.setLocalTranslation(0, -5.2f, 0);
              @@ -453,7 +468,7 @@ Again, you should see the Ninja+wall+teapot standing in a town. 
            - +

            Conclusion

            @@ -497,5 +512,5 @@ Let's add some action to the scene and continue with the +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html index e3dc9130b..64c73b799 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_audio.html @@ -24,11 +24,11 @@ This tutorial explains how to add 3D sound to a game, and how to make sounds pla   import com.jme3.app.SimpleApplication; import com.jme3.audio.AudioNode; +import com.jme3.input.MouseInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.MouseButtonTrigger; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box;   @@ -49,10 +49,9 @@ public class HelloAudio extends SimpleApplication { flyCam.setMoveSpeed(40);   /** just a blue box floating in space */ - Box box1 = new Box(Vector3f.ZERO, 1, 1, 1); + Box box1 = new Box(1, 1, 1); player = new Geometry("Player", box1); - Material mat1 = new Material(assetManager, - "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat1 = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md"); mat1.setColor("Color", ColorRGBA.Blue); player.setMaterial(mat1); rootNode.attachChild(player); @@ -66,23 +65,23 @@ public class HelloAudio extends SimpleApplication { private void initAudio() { /* gun shot sound is to be triggered by a mouse click. */ audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false); + audio_gun.setPositional(false); audio_gun.setLooping(false); audio_gun.setVolume(2); rootNode.attachChild(audio_gun);   /* nature sound - keeps playing in a loop. */ - audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", false); + audio_nature = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", true); audio_nature.setLooping(true); // activate continuous playing - audio_nature.setPositional(true); - audio_nature.setLocalTranslation(Vector3f.ZERO.clone()); + audio_nature.setPositional(true); audio_nature.setVolume(3); rootNode.attachChild(audio_nature); audio_nature.play(); // play continuously! }   - /** Declaring "Shoot" action, mapping it to a trigger (mouse click). */ + /** Declaring "Shoot" action, mapping it to a trigger (mouse left click). */ private void initKeys() { - inputManager.addMapping("Shoot", new MouseButtonTrigger(0)); + inputManager.addMapping("Shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); inputManager.addListener(actionListener, "Shoot"); }   @@ -110,7 +109,7 @@ When you run the sample, you should see a blue cube. You should hear a nature-li

            - +

            Understanding the Code Sample

            @@ -124,7 +123,7 @@ Let's have a closer look at initAudio() to learn how to use
            - +

            AudioNodes

            @@ -153,10 +152,17 @@ These two lines create new sound nodes from the given audio files in the AssetMa

            You want the gunshot sound to play once (you don't want it to loop). You also specify its volume as gain factor (at 0, sound is muted, at 2, it is twice as loud, etc.).

            -
                audio_gun.setLooping(false);
            +
                audio_gun.setPositional(false);
            +    audio_gun.setLooping(false);
                 audio_gun.setVolume(2);
                 rootNode.attachChild(audio_gun);
            +

            + +

            Note that setPositional(false) is pretty important when you use stereo sounds. Positional sounds must always be mono audio files, otherwise the engine will remind it to you with a crash. +

            +

            +

            The nature sound is different: You want it to loop continuously as background sound. This is why you set looping to true, and immediately call the play() method on the node. You also choose to set its volume to 3.

            @@ -184,7 +190,7 @@ Here you make audio_nature a positional sound that comes from a certain place. F

            - +

            Triggering Sound

            @@ -192,9 +198,9 @@ Here you make audio_nature a positional sound that comes from a certain place. F Let's have a closer look at initKeys(): As you learned in previous tutorials, you use the inputManager to respond to user input. Here you add a mapping for a left mouse button click, and name this new action Shoot.

            -
              /** Declaring "Shoot" action, mapping it to a trigger (mouse click). */
            +
              /** Declaring "Shoot" action, mapping it to a trigger (mouse left click). */
               private void initKeys() {
            -    inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
            +    inputManager.addMapping("Shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
                 inputManager.addListener(actionListener, "Shoot");
               }
            @@ -213,10 +219,11 @@ Setting up the ActionListener should also be familiar from previous tutorials. Y

            Since you want to be able to shoot fast repeatedly, so you do not want to wait for the previous gunshot sound to end before the next one can start. This is why you play this sound using the playInstance() method. This means that every click starts a new instance of the sound, so two instances can overlap. You set this sound not to loop, so each instance only plays once. As you would expect it of a gunshot. +

            - +

            Ambient or Situational?

            @@ -265,7 +272,7 @@ Apart from the looping boolean, another difference is where play().playIns
            - +

            Buffered or Streaming?

            @@ -274,7 +281,7 @@ Apart from the looping boolean, another difference is where play().playIns The Boolean in the AudioNode constructor defines whether the audio is buffered (false) or streamed (true). For example:

            -
            audio_nature = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false); // buffered
            +
            audio_gunshot = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false); // buffered
             ...
             audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true); // streamed 
            @@ -288,7 +295,7 @@ Note that streamed sounds can not loop (i.e. setLooping will not work as you exp

            - +

            Play() or PlayInstance()?

            @@ -305,9 +312,9 @@ Note that streamed sounds can not loop (i.e. setLooping will not work as you exp
            The same sound cannot play twice at the same time.The same sounds can play multiple times and overlap.
            - +
            - +

            Your Ear in the Scene

            @@ -329,7 +336,7 @@ If you don't do that, the results of 3D audio will be quite random.

            - +

            Global, Directional, Positional?

            @@ -356,7 +363,7 @@ In short, you must choose in every situation whether it makes sense for a sound

            - +

            Conclusion

            @@ -391,5 +398,5 @@ See also:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html index e6082fed4..7b4661f34 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_collision.html @@ -69,6 +69,11 @@ public class HelloCollision extends SimpleApplication private CharacterControl player; private Vector3f walkDirection = new Vector3f(); private boolean left = false, right = false, up = false, down = false; +  + //Temporary vectors used on each frame. + //They here to avoid instanciating new vectors on each frame + private Vector3f camDir = new Vector3f(); + private Vector3f camLeft = new Vector3f();   public static void main(String[] args) { HelloCollision app = new HelloCollision(); @@ -111,7 +116,7 @@ public class HelloCollision extends SimpleApplication player.setGravity(30); player.setPhysicsLocation(new Vector3f(0, 10, 0));   - // We attach the scene and the player to the rootNode and the physics space, + // We attach the scene and the player to the rootnode and the physics space, // to make them appear in the game world. rootNode.attachChild(sceneModel); bulletAppState.getPhysicsSpace().add(landscape); @@ -147,17 +152,17 @@ public class HelloCollision extends SimpleApplication   /** These are our custom actions triggered by key presses. * We do not walk yet, we just keep track of the direction the user pressed. */ - public void onAction(String binding, boolean value, float tpf) { + public void onAction(String binding, boolean isPressed, float tpf) { if (binding.equals("Left")) { - left = value; + left = isPressed; } else if (binding.equals("Right")) { - right = value; + right= isPressed; } else if (binding.equals("Up")) { - up = value; + up = isPressed; } else if (binding.equals("Down")) { - down = value; + down = isPressed; } else if (binding.equals("Jump")) { - player.jump(); + if (isPressed) { player.jump(); } } }   @@ -169,25 +174,34 @@ public class HelloCollision extends SimpleApplication * We also make sure here that the camera moves with player. */ @Override - public void simpleUpdate(float tpf) { - Vector3f camDir = cam.getDirection().clone().multLocal(0.6f); - Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f); - walkDirection.set(0, 0, 0); - if (left) { walkDirection.addLocal(camLeft); } - if (right) { walkDirection.addLocal(camLeft.negate()); } - if (up) { walkDirection.addLocal(camDir); } - if (down) { walkDirection.addLocal(camDir.negate()); } - player.setWalkDirection(walkDirection); - cam.setLocation(player.getPhysicsLocation()); - } + public void simpleUpdate(float tpf) { + camDir.set(cam.getDirection()).multLocal(0.6f); + camLeft.set(cam.getLeft()).multLocal(0.4f); + walkDirection.set(0, 0, 0); + if (left) { + walkDirection.addLocal(camLeft); + } + if (right) { + walkDirection.addLocal(camLeft.negate()); + } + if (up) { + walkDirection.addLocal(camDir); + } + if (down) { + walkDirection.addLocal(camDir.negate()); + } + player.setWalkDirection(walkDirection); + cam.setLocation(player.getPhysicsLocation()); + } }

            Run the sample. You should see a town square with houses and a monument. Use the WASD keys and the mouse to navigate around with a first-person perspective. Run forward and jump by pressing W and Space. Note how you step over the sidewalk, and up the steps to the monument. You can walk in the alleys between the houses, but the walls are solid. Don't walk over the edge of the world! :-) +

            - +

            Understanding the Code

            @@ -206,7 +220,12 @@ You already know that SimpleApplication is the base class for all jME3 games. Yo private RigidBodyControl landscape; private CharacterControl player; private Vector3f walkDirection = new Vector3f(); - private boolean left = false, right = false, up = false, down = false; + private boolean left = false, right = false, up = false, down = false; +  + //Temporary vectors used on each frame. + //They here to avoid instanciating new vectors on each frame + private Vector3f camDir = new Vector3f(); + private Vector3f camLeft = new Vector3f();

            You initialize a few private fields: @@ -222,15 +241,18 @@ You initialize a few private fields:

          15. The fields walkDirection and the four Booleans are used for physics-controlled navigation.
          16. +
          17. camDir and camLeft are temporary vectors used later when computing the walkingDirection from the cam position and rotation
            +
          18. Let's have a look at all the details: +

            - +

            Initializing the Game

            @@ -254,7 +276,7 @@ As usual, you initialize the game in the simpleInitApp() method.
            - +

            The Physics-Controlled Scene

            @@ -301,7 +323,7 @@ To use collision detection, you add a RigidBodyControl to the sceneModel
            - +

            The Physics-Controlled Player

            @@ -353,7 +375,7 @@ Finally we put the player in its starting position and update its state ??? reme

            - +

            PhysicsSpace

            @@ -369,7 +391,7 @@ The invisible body of the character just sits there on the physical floor. It ca

            - +

            Navigation

            @@ -387,7 +409,7 @@ In short, you must re-define the flyCam's navigational key mappings to use

            - +

            1. inputManager

            @@ -413,7 +435,7 @@ You can move this block of code into an auxiliary method setupKeys()
            - +

            2. onAction()

            @@ -444,7 +466,7 @@ For all other directions: Every time the user presses one of the WASD keys, you

            - +

            3. setWalkDirection()

            @@ -456,17 +478,25 @@ Previously in the onAction() method, you have collected the info in

            This last and most important code snippet goes into the simpleUpdate() method.

            -
              public void simpleUpdate(float tpf) {
            -    Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
            -    Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
            -    walkDirection.set(0, 0, 0);
            -    if (left)  { walkDirection.addLocal(camLeft); }
            -    if (right) { walkDirection.addLocal(camLeft.negate()); }
            -    if (up)    { walkDirection.addLocal(camDir); }
            -    if (down)  { walkDirection.addLocal(camDir.negate()); }
            -    player.setWalkDirection(walkDirection);
            -    cam.setLocation(player.getPhysicsLocation());
            -  }
            +
             public void simpleUpdate(float tpf) {
            +        camDir.set(cam.getDirection()).multLocal(0.6f);
            +        camLeft.set(cam.getLeft()).multLocal(0.4f);
            +        walkDirection.set(0, 0, 0);
            +        if (left) {
            +            walkDirection.addLocal(camLeft);
            +        }
            +        if (right) {
            +            walkDirection.addLocal(camLeft.negate());
            +        }
            +        if (up) {
            +            walkDirection.addLocal(camDir);
            +        }
            +        if (down) {
            +            walkDirection.addLocal(camDir.negate());
            +        }
            +        player.setWalkDirection(walkDirection);
            +        cam.setLocation(player.getPhysicsLocation());
            +    }

            This is how the walking is triggered: @@ -491,10 +521,11 @@ This is how the walking is triggered:

            Important: Again, do not use setLocalTranslation() to walk the player around. You will get it stuck by overlapping with another physical object. You can put the player in a start position with setPhysicalLocation() if you make sure to place it a bit above the floor and away from obstacles. +

            - +

            Conclusion

            @@ -535,5 +566,5 @@ Related info:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html index 8fcecd1da..4f7c5e1f5 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_input_system.html @@ -65,8 +65,8 @@ public class HelloInput extends SimpleApplication { inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE), new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // Add the names to the action listener. - inputManager.addListener(actionListener, new String[]{"Pause"}); - inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate"}); + inputManager.addListener(actionListener,"Pause"); + inputManager.addListener(analogListener,"Left", "Right", "Rotate");   }   @@ -112,7 +112,7 @@ Build and run the example. - +

            Defining Mappings and Triggers

            @@ -164,15 +164,16 @@ Now you need to register your trigger mappings.
                // Add the names to the action listener.
            -    inputManager.addListener(actionListener, new String[]{"Pause"});
            -    inputManager.addListener(analogListener, new String[]{"Left", "Right", "Rotate"});
            + inputManager.addListener(actionListener,"Pause"); + inputManager.addListener(analogListener,"Left", "Right", "Rotate");

            This code goes into the simpleInitApp() method. But since we will likely add many keybindings, we extract these lines and wrap them in an auxiliary method, initKeys(). The initKeys() method is not part of the Input Controls interface ??? you can name it whatever you like. Just don't forget to call your method from the initSimpleApp() method. +

            - +

            Implementing the Actions

            @@ -268,7 +269,7 @@ It's okay to use only one of the two Listeners, and not implement the other

            - +

            Analog, Pressed, or Released?

            @@ -325,7 +326,7 @@ Mappings registered to the ActionListener are digital either-or }
            - +

            Table of Triggers

            @@ -361,14 +362,14 @@ You can find the list of input constants in the files src/core/com/jme3/in KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT)
            - +

            Tip: If you don't recall an input constant during development, you benefit from an IDE's code completion functionality: Place the caret after e.g. KeyInput.| and trigger code completion to select possible input identifiers.

            - +

            Exercises

              @@ -396,7 +397,7 @@ inputManager.addMapping("Pause", new KeyTrigger(usersPauseKey

            - +

            Conclusion

            @@ -418,5 +419,5 @@ Now you can already write a little interactive game! But wouldn't it be coo
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html index 348012f9f..782b4d97e 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_main_event_loop.html @@ -21,12 +21,12 @@ Now that you know how to load assets, such as 3D models, you want to implement s import com.jme3.app.SimpleApplication; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box;   -/** Sample 4 - how to trigger repeating actions from the main update loop. - * In this example, we make the player character rotate. */ +/** Sample 4 - how to trigger repeating actions from the main event loop. + * In this example, you use the loop to make the player character + * rotate continuously. */ public class HelloLoop extends SimpleApplication {   public static void main(String[] args){ @@ -38,8 +38,8 @@ public class HelloLoop extends SimpleApplication {   @Override public void simpleInitApp() { -  - Box b = new Box(Vector3f.ZERO, 1, 1, 1); + /** this blue box is our player character */ + Box b = new Box(1, 1, 1); player = new Geometry("blue cube", b); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); @@ -48,20 +48,21 @@ public class HelloLoop extends SimpleApplication { rootNode.attachChild(player); }   - /* This is the update loop */ + /* Use the main event loop to trigger repeating actions. */ @Override public void simpleUpdate(float tpf) { - // make the player rotate + // make the player rotate: player.rotate(0, 2*tpf, 0); } }

            Build and run the file: You see a constantly rotating cube. +

            - +

            Understanding the Code

            @@ -83,7 +84,7 @@ Now have a closer look at the simpleUpdate() method ??? this is the
            - +

            Using the Update Loop

            @@ -108,38 +109,39 @@ A rotating object is just a simple example. In the update loop, you typically ha
            - +

            Init - Update - Render

            -Note the contrast: +Note the the three phases of every game:

              -
            • The simpleInitApp() method is executed only once, right at the beginning;
              +
            • Init: The simpleInitApp() method is executed only once, right at the beginning;
            • -
            • The simpleUpdate() method runs repeatedly, during the game.
              +
            • Update: The simpleUpdate() method runs repeatedly, during the game.
            • -
            • After every update, the jMonkeyEngine automatically redraws (renders) the screen for you!
              +
            • Render: After every update, the jMonkeyEngine automatically redraws (renders) the screen for you.

            -Since rendering is automatic, initialization and updating are the two most important concepts in a SimpleApplication for you right now. These methods are where you load and create game data (once), and (repeatedly) change their properties to update the game state: +Since rendering is automatic, initialization and updating are the two most important concepts in a SimpleApplication-based game for you:

              -
            • simpleInitApp() is the application's "first breath".
              +
            • The simpleInitApp() method is the application's "first breath".
              +Here you load and create game data (once).
            • -
            • simpleUpdate() is the application's heartbeat.
              -The update time unit is called ticks.
              +
            • The simpleUpdate() method is the application's "heartbeat" (the time unit is called ticks).
              +Here you change their properties to update the game state (repeatedly).

            -

            Everything in a game happens either during initialization or during the update loop. This means that these two methods grow very long over time. There are two strategies how experienced developers spread out their init and update code over several Java classes: +

            Everything in a game happens either during initialization, or during the update loop. This means that these two methods grow very long over time. Follwo these two strategies to spread out init and update code over several modular Java classes:

            • Move code blocks from the simpleInitApp() method to AppStates.
              @@ -155,7 +157,7 @@ Keep this in mind for later when your application grows.

            - +

            Exercises

            @@ -189,7 +191,7 @@ Look back at the Hell

            - +

            Conclusion

            @@ -221,5 +223,5 @@ See also:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html index 127db9b2c..628a9fb06 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_material.html @@ -14,13 +14,13 @@ The term Material includes everything that influences what the surface of a 3D m

            +

            To use the example assets in a new jMonkeyEngine SDK project, right-click your project, select "Properties", go to "Libraries", press "Add Library" and add the "jme3-test-data" library.

            -

            - +

            Sample Code

            package jme3test.helloworld;
            @@ -31,18 +31,17 @@ import com.jme3.material.Material;
             import com.jme3.material.RenderState.BlendMode;
             import com.jme3.math.ColorRGBA;
             import com.jme3.math.Vector3f;
            +import com.jme3.renderer.queue.RenderQueue.Bucket;
             import com.jme3.scene.Geometry;
             import com.jme3.scene.shape.Box;
             import com.jme3.scene.shape.Sphere;
             import com.jme3.texture.Texture;
             import com.jme3.util.TangentBinormalGenerator;
            -import com.jme3.renderer.queue.RenderQueue.Bucket;
              
             /** Sample 6 - how to give an object's surface a material and texture.
            - * How to make objects transparent, or let colors "leak" through partially
            - * transparent textures. How to make bumpy and shiny surfaces.  */
            - 
            + * How to make objects transparent. How to make bumpy and shiny surfaces.  */
             public class HelloMaterial extends SimpleApplication {
            + 
               public static void main(String[] args) {
                 HelloMaterial app = new HelloMaterial();
                 app.start();
            @@ -50,58 +49,57 @@ public class HelloMaterial extends SimpleApplication {
              
               @Override
               public void simpleInitApp() {
            + 
                 /** A simple textured cube -- in good MIP map quality. */
            -    Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);
            -    Geometry cube = new Geometry("My Textured Box", boxshape1);
            -    Material mat_stl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            -    Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
            -    mat_stl.setTexture("ColorMap", tex_ml);
            -    cube.setMaterial(mat_stl);
            -    rootNode.attachChild(cube);
            +    Box cube1Mesh = new Box( 1f,1f,1f);
            +    Geometry cube1Geo = new Geometry("My Textured Box", cube1Mesh);
            +    cube1Geo.setLocalTranslation(new Vector3f(-3f,1.1f,0f));
            +    Material cube1Mat = new Material(assetManager, 
            +        "Common/MatDefs/Misc/Unshaded.j3md");
            +    Texture cube1Tex = assetManager.loadTexture(
            +        "Interface/Logo/Monkey.jpg");
            +    cube1Mat.setTexture("ColorMap", cube1Tex);
            +    cube1Geo.setMaterial(cube1Mat);
            +    rootNode.attachChild(cube1Geo);
              
                 /** A translucent/transparent texture, similar to a window frame. */
            -    Box boxshape3 = new Box(new Vector3f(0f,0f,0f), 1f,1f,0.01f);
            -    Geometry window_frame = new Geometry("window frame", boxshape3);
            -    Material mat_tt = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            -    mat_tt.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
            -    mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
            -    window_frame.setMaterial(mat_tt);
            - 
            -    /** Objects with transparency need to be in the render bucket for transparent objects: */
            -    window_frame.setQueueBucket(Bucket.Transparent);
            -    rootNode.attachChild(window_frame);
            - 
            -    /** A cube with base color "leaking" through a partially transparent texture */
            -    Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f);
            -    Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4);
            -    Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            -    mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
            -    mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple
            -    cube_leak.setMaterial(mat_tl);
            -    rootNode.attachChild(cube_leak);
            +    Box cube2Mesh = new Box( 1f,1f,0.01f);
            +    Geometry cube2Geo = new Geometry("window frame", cube2Mesh);
            +    Material cube2Mat = new Material(assetManager, 
            +        "Common/MatDefs/Misc/Unshaded.j3md");
            +    cube2Mat.setTexture("ColorMap", 
            +        assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
            +    cube2Mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
            +    cube2Geo.setQueueBucket(Bucket.Transparent);
            +    cube2Geo.setMaterial(cube2Mat);
            +    rootNode.attachChild(cube2Geo);
              
            -    /** A bumpy rock with a shiny light effect */
            -    Sphere rock = new Sphere(32,32, 2f);
            -    Geometry shiny_rock = new Geometry("Shiny rock", rock);
            -    rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
            -    TangentBinormalGenerator.generate(rock);           // for lighting effect
            -    Material mat_lit = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
            -    mat_lit.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
            -    mat_lit.setTexture("NormalMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
            -    mat_lit.setBoolean("UseMaterialColors",true);    
            -    mat_lit.setColor("Specular",ColorRGBA.White);
            -    mat_lit.setColor("Diffuse",ColorRGBA.White);
            -    mat_lit.setFloat("Shininess", 5f); // [1,128]    
            -    shiny_rock.setMaterial(mat_lit);
            -    shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
            -    shiny_rock.rotate(1.6f, 0, 0);          // Rotate it a bit
            -    rootNode.attachChild(shiny_rock);
            +    /** A bumpy rock with a shiny light effect.*/
            +    Sphere sphereMesh = new Sphere(32,32, 2f);
            +    Geometry sphereGeo = new Geometry("Shiny rock", sphereMesh);
            +    sphereMesh.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
            +    TangentBinormalGenerator.generate(sphereMesh);           // for lighting effect
            +    Material sphereMat = new Material(assetManager, 
            +        "Common/MatDefs/Light/Lighting.j3md");
            +    sphereMat.setTexture("DiffuseMap", 
            +        assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
            +    sphereMat.setTexture("NormalMap", 
            +        assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
            +    sphereMat.setBoolean("UseMaterialColors",true);    
            +    sphereMat.setColor("Diffuse",ColorRGBA.White);
            +    sphereMat.setColor("Specular",ColorRGBA.White);
            +    sphereMat.setFloat("Shininess", 64f);  // [0,128]
            +    sphereGeo.setMaterial(sphereMat);
            +    sphereGeo.setLocalTranslation(0,2,-2); // Move it a bit
            +    sphereGeo.rotate(1.6f, 0, 0);          // Rotate it a bit
            +    rootNode.attachChild(sphereGeo);
              
                 /** Must add a light to make the lit object visible! */
                 DirectionalLight sun = new DirectionalLight();
                 sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
                 sun.setColor(ColorRGBA.White);
                 rootNode.addLight(sun);
            + 
               }
             }
            @@ -111,9 +109,7 @@ You should see
            • Left ??? A cube with a brown monkey texture.
            • -
            • Middle ??? A translucent monkey picture in front of a shiny rock.
              -
            • -
            • Right ??? A cube with a purple monkey texture.
              +
            • Right ??? A translucent monkey picture in front of a shiny bumpy rock.
            @@ -122,7 +118,7 @@ Move around with the WASD keys to have a closer look at the translucency, and th

            - +

            Simple Unshaded Texture

            @@ -130,80 +126,89 @@ Move around with the WASD keys to have a closer look at the translucency, and th Typically you want to give objects in your scene textures: It can be rock, grass, brick, wood, water, metal, paper??? A texture is a normal image file in JPG or PNG format. In this example, you create a box with a simple unshaded Monkey texture as material.

            -
                /** A simple textured cube. */
            -    Box boxshape1 = new Box(new Vector3f(-3f,1.1f,0f), 1f,1f,1f);
            -    Geometry cube = new Geometry("My Textured Box", boxshape1);
            -    Material mat_stl = new Material(assetManager, 
            +
                /** A simple textured cube -- in good MIP map quality. */
            +    Box cube1Mesh = new Box( 1f,1f,1f);
            +    Geometry cube1Geo = new Geometry("My Textured Box", cube1Mesh);
            +    cube1Geo.setLocalTranslation(new Vector3f(-3f,1.1f,0f));
            +    Material cube1Mat = new Material(assetManager, 
                     "Common/MatDefs/Misc/Unshaded.j3md");
            -    Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
            -    mat_stl.setTexture("ColorMap", tex_ml);
            -    cube.setMaterial(mat_stl);
            -    rootNode.attachChild(cube);
            + Texture cube1Tex = assetManager.loadTexture( + "Interface/Logo/Monkey.jpg"); + cube1Mat.setTexture("ColorMap", cube1Tex); + cube1Geo.setMaterial(cube1Mat); + rootNode.attachChild(cube1Geo);

            -Here is what we did: +Here is what we did: to create a textured box:

              -
            1. Create a Geometry from a Box mesh. Let's call it cube.
              +
            2. Create a Geometry cube1Geo from a Box mesh cube1Mesh.
              +
            3. +
            4. Create a Material cube1Mat based on jME3's default Unshaded.j3md material definition.
            5. -
            6. Create a Material based on jME3's default Unshaded.j3md material definition.
              +
            7. Create a texture cube1Tex from the Monkey.jpg file in the assets/Interface/Logo/ directory of the project.
            8. -
            9. Create a texture from the Monkey.jpg file and load it into the material.
              -The ColorMap is the typical material layer where textures go.
              +
            10. Load the texture cube1Tex into the ColorMap layer of the material cube1Mat.
            11. Apply the material to the cube, and attach the cube to the rootnode.
            - +

            Transparent Unshaded Texture

            -Monkey.png is the same texture as Monkey.jpg, but with an added alpha channel. The alpha channel allows you to specify which areas of the texture you want to be opaque or transparent: Black areas remain opaque, gray areas become translucent, and white areas become transparent. +Monkey.png is the same texture as Monkey.jpg, but with an added alpha channel. The alpha channel allows you to specify which areas of the texture you want to be opaque or transparent: Black areas of the alpha channel remain opaque, gray areas become translucent, and white areas become transparent.

            For a partially translucent/transparent texture, you need:

              -
            • A texture with alpha channel
              +
            • A Texture with alpha channel
            • -
            • A Texture blend mode of BlendMode.Alpha
              +
            • A Texture with blend mode of BlendMode.Alpha
            • -
            • A geometry in the Bucket.Transparent render bucket. This bucket ensures that the translucent object is drawn on top of objects behind it, and they show up correctly under the translucent parts. (For non-translucent objects the drawing order is not so important, because the z-buffer keeps track of whether a pixel is behind something else or not, and the color of a pixel doesn't depend on the pixels under it, this is why opaque Geometries can be drawn in any order.)
              +
            • A Geometry in the Bucket.Transparent render bucket.
              +This bucket ensures that the transparent object is drawn on top of objects behind it, and they show up correctly under the transparent parts.
            -
                /** A translucent/transparent texture. */
            -    Box boxshape3 = new Box(new Vector3f(0f,0f,0f), 1f,1f,0.01f);
            -    Geometry seethrough = new Geometry("see-through box", boxshape3);
            -    Material mat_tt = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            -    mat_tt.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
            -    mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); // activate transparency
            -    seethrough.setMaterial(mat_tt);
            -    seethrough.setQueueBucket(Bucket.Transparent);
            -    rootNode.attachChild(seethrough);
            +
                /** A translucent/transparent texture, similar to a window frame. */
            +    Box cube2Mesh = new Box( 1f,1f,0.01f);
            +    Geometry cube2Geo = new Geometry("window frame", cube2Mesh);
            +    Material cube2Mat = new Material(assetManager, 
            +    "Common/MatDefs/Misc/Unshaded.j3md");
            +    cube2Mat.setTexture("ColorMap", 
            +        assetManager.loadTexture("Textures/ColoredTex/Monkey.png"));
            +    cube2Mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);  // !
            +    cube2Geo.setQueueBucket(Bucket.Transparent);                        // !
            +    cube2Geo.setMaterial(cube2Mat);
            +    rootNode.attachChild(cube2Geo);
            + +

            +For non-transparent objects, the drawing order is not so important, because the z-buffer already keeps track of whether a pixel is behind something else or not, and the color of an opaque pixel doesn't depend on the pixels under it, this is why opaque Geometries can be drawn in any order. +

            -What you did is the same as before, with only one added step for the transparency. +What you did for the transparent texture is the same as before, with only one added step for the transparency.

              -
            1. Create a Geometry from a mesh. This Geometry is flat upright box.
              +
            2. Create a Geometry cube2Geo from a Box mesh cube2Mesh. This Box Geometry is flat upright box (because z=0.01f).
            3. -
            4. Create a Material based on jME3's default Unshaded.j3md material definition.
              +
            5. Create a Material cube2Mat based on jME3's default Unshaded.j3md material definition.
            6. -
            7. Create a texture from the Monkey.png file and load it into the material.
              -The ColorMap is the material layer where textures go. This PNG file must have an alpha layer.
              +
            8. Create a texture cube2Tex from the Monkey.png file in the assets/Textures/ColoredTex/ directory of the project. This PNG file must have an alpha layer.
            9. -
            10. Activate transparency in the material by setting the blend mode to Alpha!
              +
            11. Activate transparency in the material by setting the blend mode to Alpha.
            12. -
            13. Apply the material to the Geometry.
              +
            14. Set the QueueBucket of the Geometry to Bucket.Transparent.
            15. -
            16. Set the QueueBucket of the Geometry to Bucket.Transparent.
              +
            17. Load the texture cube2Tex into the ColorMap layer of the material cube2Mat.
            18. -
            19. Attach the cube to the rootnode.
              +
            20. Apply the material to the cube, and attach the cube to the rootnode.
            @@ -213,59 +218,62 @@ The ColorMap is the material layer where textures go. This +

            Shininess and Bumpiness

            -But textures are not all. Have a close look at the shiny sphere ??? you cannot get such a nice bumpy material with just a texture. JME3 also supports so-called Phong-illuminated materials: +But textures are not all. Have a close look at the shiny sphere ??? you cannot get such a nice bumpy material with just a plain texture. You see that JME3 also supports so-called Phong-illuminated materials:

            -In a lit material, the standard texture layer is refered to as Diffuse Map, any material can use this layer. A lit material can additionally have lighting effects such as Shininess used together with the Specular Map layer, and even a realistically bumpy or cracked surface with help of the Normal Map layer. +In a lit material, the standard texture layer is refered to as DiffuseMap, any material can use this layer. A lit material can additionally have lighting effects such as Shininess used together with the SpecularMap layer and Specular color. And you can even get a realistically bumpy or cracked surface with help of the NormalMap layer.

            Let's have a look at the part of the code example where you create the shiny bumpy rock.

              -
            1. Create a Geometry from a Sphere shape. Note that this shape is a normal smooth sphere mesh.
                  Sphere rock = new Sphere(32,32, 2f);
              -    Geometry shiny_rock = new Geometry("Shiny rock", rock);
              +
            2. Create a Geometry from a Sphere shape. Note that this shape is a normal smooth sphere mesh.
                  Sphere sphereMesh = new Sphere(32,32, 2f);
              +    Geometry sphereGeo = new Geometry("Shiny rock", sphereMesh);
                -
              1. (Only for Spheres) Change the sphere's TextureMode to make the square texture project better onto the sphere.
                    rock.setTextureMode(Sphere.TextureMode.Projected); 
                +
              2. (Only for Spheres) Change the sphere's TextureMode to make the square texture project better onto the sphere.
                    sphereMesh.setTextureMode(Sphere.TextureMode.Projected);
              3. -
              4. You generate TangentBinormals for the sphere mesh so you can use the NormalMap layer of the texture.
                    TangentBinormalGenerator.generate(rock);
                +
              5. You must generate TangentBinormals for the mesh so you can use the NormalMap layer of the texture.
                    TangentBinormalGenerator.generate(sphereMesh);
            3. -
            4. Create a material based on the Lighting.j3md default material.
                  Material mat_lit = new Material(assetManager, 
              -    "Common/MatDefs/Light/Lighting.j3md");
              +
            5. Create a material based on the Lighting.j3md default material.
                  Material sphereMat = new Material(assetManager, 
              +        "Common/MatDefs/Light/Lighting.j3md");
              1. Set a standard rocky texture in the DiffuseMap layer.
                -
                    mat_lit.setTexture("DiffuseMap", assetManager.loadTexture(
                -    "Textures/Terrain/Pond/Pond.jpg"));
                +
                    sphereMat.setTexture("DiffuseMap", 
                +        assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
              2. -
              3. Set the NormalMap layer that contains the bumpiness. The NormalMap was generated for this particular DiffuseMap with a special tool (e.g. Blender).
                    mat_lit.setTexture("NormalMap", assetManager.loadTexture(
                -    "Textures/Terrain/Pond/Pond_normal.png"));
                +
              4. Set the NormalMap layer that contains the bumpiness. The NormalMap was generated for this particular DiffuseMap with a special tool (e.g. Blender).
                    sphereMat.setTexture("NormalMap", 
                +        assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
              5. -
              6. Set the Material's Shininess to a value between 1 and 128. For a rock, a low fuzzy shininess is appropriate.
                    mat_lit.setFloat("Shininess", 5f); // [1,128]
                +
              7. Set the Material's Shininess to a value between 1 and 128. For a rock, a low fuzzy shininess is appropriate. Use material colors to define the shiny Specular color.
                    sphereMat.setBoolean("UseMaterialColors",true);    
                +    sphereMat.setColor("Diffuse",ColorRGBA.White);  // minimum material color
                +    sphereMat.setColor("Specular",ColorRGBA.White); // for shininess
                +    sphereMat.setFloat("Shininess", 64f); // [1,128] for shininess
            6. -
            7. Assign your newly created material to the Geometry.
                  shiny_rock.setMaterial(mat_lit);
              +
            8. Assign your newly created material to the Geometry.
                  sphereGeo.setMaterial(sphereMat);
            9. -
            10. Let's move and rotate the geometry a bit to position it better.
                  shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit
              -    shiny_rock.rotate(1.6f, 0, 0);          // Rotate it a bit
              -    rootNode.attachChild(shiny_rock);
              +
            11. Let's move and rotate the geometry a bit to position it better.
                  sphereGeo.setLocalTranslation(0,2,-2); // Move it a bit
              +    sphereGeo.rotate(1.6f, 0, 0);          // Rotate it a bit
              +    rootNode.attachChild(sphereGeo);
            @@ -280,7 +288,7 @@ Remember that any Lighting.j3md-based material requires a light source, as shown

            - +

            Default Material Definitions

            @@ -305,59 +313,70 @@ DiffuseMap, NormalMap, SpecularMap : Texture2D
            Shininess : Float
            - +

            For a game, you create custom Materials based on these existing MaterialDefintions ??? as you have just seen in the example with the shiny rock's material.

            - +

            Exercises

            - +

            Exercise 1: Custom .j3m Material

            -Look at the purple leak-through sample above again. It takes four lines to create and set the Material. +Look at the shiny rocky sphere above again. It takes several lines to create and set the Material.

              -
            • Note how it loads the Unshaded.j3md Material definition.
              +
            • Note how it loads the Lighting.j3md Material definition.
            • -
            • Note how it sets the Color parameter to purple (new ColorRGBA(1f,0f,1f,1f)).
              +
            • Note how it sets the DiffuseMap and NormalMap to a texture path.
            • -
            • Note how it sets the ColorMap to a texture path.
              +
            • Note how it activates UseMaterialColors and sets Specular and Diffuse to 4 float values (RGBA color).
              +
            • +
            • Note how it sets Shininess to 64.

            + If you want to use one custom material for several models, you can store it in a .j3m file, and save a few lines of code every time. +

            + +

            You create a j3m file as follows:

              -
            1. Create a file assets/Materials/LeakThrough.j3m in your project directory, with the following content:
              Material Leak Through : Common/MatDefs/Misc/Unshaded.j3md {
              +
            2. Create a plain text file assets/Materials/MyCustomMaterial.j3m in your project directory, with the following content:
              Material My shiny custom material : Common/MatDefs/Light/Lighting.j3md {
                    MaterialParameters {
              -         Color : 1 0 1 1
              -         ColorMap : Flip Textures/ColoredTex/Monkey.png
              +        DiffuseMap : Textures/Terrain/Pond/Pond.jpg
              +        NormalMap : Textures/Terrain/Pond/Pond_normal.png
              +        UseMaterialColors : true
              +        Specular : 1.0 1.0 1.0 1.0
              +        Diffuse : 1.0 1.0 1.0 1.0
              +        Shininess : 64.0
                    }
               }
              • Note that Material is a fixed keyword.
              • -
              • Note that Leak Through is a String that you can choose to name the material.
                +
              • Note that My shiny custom material is a String that you can choose to describe the material.
              • -
              • Note how the code sets the same three properties, Color, ColorMap, and Unshaded.j3md.
                +
              • Note how the code sets all the same properties as before!
            3. -
            4. In the code sample, comment out the three lines with mat_tl in them.
              +
            5. In the code sample, comment out the eight lines that have sphereMat in them.
            6. -
            7. Below them, add the following line:
              cube_leak.setMaterial((Material) assetManager.loadMaterial( "Materials/LeakThrough.j3m"));
              +
            8. Below this line, add the following line:
              sphereGeo.setMaterial((Material) assetManager.loadMaterial( 
              +    "Materials/MyCustomMaterial.j3m"));
            9. Run the app. The result is the same.
              @@ -366,11 +385,11 @@ You create a j3m file as follows:

              -Using this new custom material LeakThrough.j3m only takes one line. You have replaced the three lines of an on-the-fly material definition with one line that loads a custom material from a file. This method is very handy if you use the same material often. +Using this new custom material MyCustomMaterial.j3m only takes one line. You have replaced the eight lines of an on-the-fly material definition with one line that loads a custom material from a file. Using .j3m files is very handy if you use the same material often.

            10. - +

              Exercise 2: Bumpiness and Shininess

              @@ -400,7 +419,7 @@ Go back to the bumpy rock sample above:
            - +

            Conclusion

            @@ -450,5 +469,5 @@ See also
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html index 275a16653..1f8dd4dc3 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_node.html @@ -44,16 +44,15 @@ You will learn that the scene graph represents the 3D world, and why the rootNod   import com.jme3.app.SimpleApplication; import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Box; -import com.jme3.math.ColorRGBA; import com.jme3.scene.Node; +import com.jme3.scene.shape.Box;   /** Sample 2 - How to use nodes as handles to manipulate objects in the scene. * You can rotate, translate, and scale objects by manipulating their parent nodes. * The Root Node is special: Only what is attached to the Root Node appears in the scene. */ -  public class HelloNode extends SimpleApplication {   public static void main(String[] args){ @@ -65,28 +64,28 @@ public class HelloNode extends SimpleApplication { public void simpleInitApp() {   /** create a blue box at coordinates (1,-1,1) */ - Box box1 = new Box( Vector3f.ZERO, 1,1,1); + Box box1 = new Box(1,1,1); Geometry blue = new Geometry("Box", box1); + blue.setLocalTranslation(new Vector3f(1,-1,1)); Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat1.setColor("Color", ColorRGBA.Blue); blue.setMaterial(mat1); - blue.move(1,-1,1);   /** create a red box straight above the blue one at (1,3,1) */ - Box box2 = new Box( Vector3f.ZERO, 1,1,1); + Box box2 = new Box(1,1,1); Geometry red = new Geometry("Box", box2); + red.setLocalTranslation(new Vector3f(1,3,1)); Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat2.setColor("Color", ColorRGBA.Red); red.setMaterial(mat2); - red.move(1,3,1);   /** Create a pivot node at (0,0,0) and attach it to the root node */ Node pivot = new Node("pivot"); rootNode.attachChild(pivot); // put this node in the scene   - /** Attach the two boxes to the *pivot* node. */ + /** Attach the two boxes to the *pivot* node. (And transitively to the root node.) */ pivot.attachChild(blue); pivot.attachChild(red); /** Rotate the pivot node: Note that both boxes have rotated! */ @@ -99,7 +98,7 @@ Build and run the code sample. You should see two colored boxes tilted at the sa

            - +

            Understanding the Terminology

            @@ -128,7 +127,7 @@ In this tutorial, you learn some new terms: Position/move, turn, or resize an objectTranslate, or rotate, or scale an object = transform an object.
            - +

            Every JME3 application has a rootNode: Your game automatically inherits the rootNode object from SimpleApplication. Everything attached to the rootNode is part of the scene graph. The elements of the scene graph are Spatials. @@ -155,9 +154,9 @@ Every JME3 application has a rootNode: Your game automatically inherits the Examples: A box, a sphere, a player, a building, a piece of terrain, a vehicle, missiles, NPCs, etc??? The rootNode, a floor node grouping several terrains, a custom vehicle-with-passengers node, a player-with-weapon node, an audio node, etc??? - + - +

            Understanding the Code

            @@ -171,19 +170,18 @@ What happens in the code snippet? You use the simpleInitApp() metho
            • Create a Box shape with extents of (1,1,1), that makes the box 2x2x2 world units big.
            • -
            • Position the box at (1,-1,1) using the move() method. (Don't change the Vector3f.ZERO unless you want to change the center of rotation)
              +
            • Position the box at (1,-1,1) using the setLocalTranslation() method.
            • Wrap the Box shape into a Geometry.
            • Create a blue material.
            • -
            • Apply the blue material to the Box Geometry.
                  Box box1 = new Box( Vector3f.ZERO, 1,1,1);
              +
            • Apply the blue material to the Box Geometry.
                  Box box1 = new Box(1,1,1);
                   Geometry blue = new Geometry("Box", box1);
              -    Material mat1 = new Material(assetManager,
              -      "Common/MatDefs/Misc/Unshaded.j3md");
              +    blue.setLocalTranslation(new Vector3f(1,-1,1));
              +    Material mat1 = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
                   mat1.setColor("Color", ColorRGBA.Blue);
              -    blue.setMaterial(mat1);
              -    blue.move(1,-1,1);
              + blue.setMaterial(mat1);
            @@ -198,13 +196,13 @@ What happens in the code snippet? You use the simpleInitApp() metho
          19. Create a red material.
          20. -
          21. Apply the red material to the Box Geometry.
                Box box2 = new Box( Vector3f.ZERO, 1,1,1);
            +
          22. Apply the red material to the Box Geometry.
                Box box2 = new Box(1,1,1);
                 Geometry red = new Geometry("Box", box2);
            +    red.setLocalTranslation(new Vector3f(1,3,1));
                 Material mat2 = new Material(assetManager,
                   "Common/MatDefs/Misc/Unshaded.j3md");
                 mat2.setColor("Color", ColorRGBA.Red);
            -    red.setMaterial(mat2);
            -    red.move(1,3,1);
            + red.setMaterial(mat2);
          23. @@ -245,7 +243,7 @@ If you run the app with only the code up to here, you see two cubes: A red cube
            - +

            What is a Pivot Node?

            @@ -264,7 +262,7 @@ You can transform (e.g. rotate) Geometries around their own center, or around a
            - +

            How do I Populate the Scenegraph?

            @@ -298,9 +296,9 @@ thing.setMaterial(mat);
            Specify what should be loaded at the start Everything you initialize and attach to the rootNode in the simpleInitApp() method is part of the scene at the start of the game.
            - +
            - +

            How do I Transform Spatials?

            @@ -324,7 +322,7 @@ To move a Spatial to specific coordinates, such as (0,40.2f,-2), use: < +right -left+up -down+forward -backward
            -
            +
            @@ -336,7 +334,7 @@ To scale a Spatial 10 times longer, one tenth the height, and keep the same widt
            Scaling resizes Spatials X-axis Y-axis Z-axis
            lengthheightwidth
            -
            +
            @@ -352,9 +350,9 @@ To roll an object 180?? around the z axis:
            thing.rotate( 0f , 0f , 180*
             
            Rotation turns Spatials X-axis Y-axis Z-axis
            pitch = nodding your headyaw = shaking your headroll = cocking your head
            - + - +

            How do I Troubleshoot Spatials?

            @@ -371,7 +369,7 @@ If you get unexpected results, check whether you made the following common mista A created Geometry does not appear in the scene. Have you attached it to (a node that is attached to) the rootNode?
            Does it have a Material?
            What is its translation (position)? Is it behind the camera or covered up by another Geometry?
            -Is it to tiny or too gigantic to see?
            +Is it too tiny or too gigantic to see?
            Is it too far from the camera? (Try (111111f); to see further) @@ -384,9 +382,9 @@ Did you rotate around the right axis? A Geometry has an unexpected Color or Material. Did you reuse a Material from another Geometry and have inadvertently changed its properties? (If so, consider cloning it: mat2 = mat.clone(); )
            - + - +

            How do I Add Custom Data to Spatials?

            @@ -419,7 +417,7 @@ By using different Strings keys (here the key is pivot id), you can

            - +

            Conclusion

            @@ -445,5 +443,5 @@ Since standard shapes like spheres and boxes get old fast, continue with the nex
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html index 5c9efd988..d52e8af40 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_physics.html @@ -109,10 +109,10 @@ public class HelloPhysics extends SimpleApplication { sphere = new Sphere(32, 32, 0.4f, true, false); sphere.setTextureMode(TextureMode.Projected); /** Initialize the brick geometry */ - box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth); + box = new Box(brickLength, brickHeight, brickWidth); box.scaleTextureCoordinates(new Vector2f(1f, .5f)); /** Initialize the floor geometry */ - floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f); + floor = new Box(10f, 0.1f, 5f); floor.scaleTextureCoordinates(new Vector2f(3, 6)); }   @@ -251,7 +251,7 @@ You should see a brick wall. Click to shoot cannon balls. Watch the bricks fall

            - +

            A Basic Physics Application

            @@ -279,12 +279,12 @@ The BulletAppState gives the game access to a PhysicsSpace. The PhysicsSpace let

            - +

            Creating Bricks and Cannon Balls

            - +

            Geometries

            @@ -305,15 +305,15 @@ In this "shoot at the wall" example, you use Geometries such as cannon sphere = new Sphere(32, 32, 0.4f, true, false); sphere.setTextureMode(TextureMode.Projected); /** Initialize the brick geometry */ - box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth); + box = new Box(brickLength, brickHeight, brickWidth); box.scaleTextureCoordinates(new Vector2f(1f, .5f)); /** Initialize the floor geometry */ - floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f); + floor = new Box(10f, 0.1f, 5f); floor.scaleTextureCoordinates(new Vector2f(3, 6)); }
            - +

            RigidBodyControl: Brick

            @@ -375,7 +375,7 @@ This code sample does the following:
            - +

            RigidBodyControl: Cannonball

            @@ -438,7 +438,7 @@ Since you are shooting cannon balls, the last line accelerates the ball in the d

            - +

            RigidBodyControl: Floor

            @@ -493,7 +493,7 @@ This code sample does the following:
            - +

            Creating the Scene

            @@ -518,7 +518,7 @@ These methods are each called once from the simpleInitApp() method

            - +

            The Cannon Ball Shooting Action

            @@ -548,7 +548,7 @@ In the moment the cannonball appears in the scene, it flies off with the velocit

            - +

            Moving a Physical Spatial

            @@ -575,12 +575,12 @@ Learn more about static versus kinematic versus dynamic in the +

            Excercises

            - +

            Exercise 1: Debug Shapes

            @@ -597,7 +597,7 @@ Now you see the collisionShapes of the bricks and spheres, and the floor highlig

            - +

            Exercise 2: No Mo' Static

            @@ -607,7 +607,7 @@ What happens if you give a static node, such as the floor, a mass of more than 0

            - +

            Exercise 3: Behind the Curtain

            @@ -625,7 +625,7 @@ Using physics everywhere in a game sounds like a cool idea, but it is easily ove

            - +

            Conclusion

            @@ -649,5 +649,5 @@ You have learned how to activate the jBullet PhysicsSpace in an application by a
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html index b0090df45..b678e91c6 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_picking.html @@ -39,12 +39,14 @@ import com.jme3.input.MouseInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.input.controls.MouseButtonTrigger; +import com.jme3.light.DirectionalLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Ray; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; +import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere;   @@ -56,8 +58,8 @@ public class HelloPicking extends SimpleApplication { HelloPicking app = new HelloPicking(); app.start(); } - Node shootables; - Geometry mark; + private Node shootables; + private Geometry mark;   @Override public void simpleInitApp() { @@ -73,6 +75,7 @@ public class HelloPicking extends SimpleApplication { shootables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f)); shootables.attachChild(makeCube("the Deputy", 1f, 0f, -4f)); shootables.attachChild(makeFloor()); + shootables.attachChild(makeCharacter()); }   /** Declaring the "Shoot" action and mapping to its triggers. */ @@ -120,8 +123,9 @@ public class HelloPicking extends SimpleApplication {   /** A cube object for target practice */ protected Geometry makeCube(String name, float x, float y, float z) { - Box box = new Box(new Vector3f(x, y, z), 1, 1, 1); + Box box = new Box(1, 1, 1); Geometry cube = new Geometry(name, box); + cube.setLocalTranslation(x, y, z); Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat1.setColor("Color", ColorRGBA.randomColor()); cube.setMaterial(mat1); @@ -130,8 +134,9 @@ public class HelloPicking extends SimpleApplication {   /** A floor to show that the "shot" can go through several objects. */ protected Geometry makeFloor() { - Box box = new Box(new Vector3f(0, -4, -5), 15, .2f, 15); + Box box = new Box(15, .2f, 15); Geometry floor = new Geometry("the Floor", box); + floor.setLocalTranslation(0, -4, -5); Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat1.setColor("Color", ColorRGBA.Gray); floor.setMaterial(mat1); @@ -149,16 +154,28 @@ public class HelloPicking extends SimpleApplication {   /** A centred plus sign to help the player aim. */ protected void initCrossHairs() { - guiNode.detachAllChildren(); + setDisplayStatView(false); guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); BitmapText ch = new BitmapText(guiFont, false); ch.setSize(guiFont.getCharSet().getRenderedSize() * 2); ch.setText("+"); // crosshairs ch.setLocalTranslation( // center - settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2, - settings.getHeight() / 2 + ch.getLineHeight() / 2, 0); + settings.getWidth() / 2 - ch.getLineWidth()/2, settings.getHeight() / 2 + ch.getLineHeight()/2, 0); guiNode.attachChild(ch); } +  + protected Spatial makeCharacter() { + // load a character from jme3test-test-data + Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml"); + golem.scale(0.5f); + golem.setLocalTranslation(-1.0f, -1.5f, -0.6f); +  + // We must add a light to make the model visible + DirectionalLight sun = new DirectionalLight(); + sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f)); + golem.addLight(sun); + return golem; + } }

            @@ -167,10 +184,11 @@ You should see four colored cubes floating over a gray floor, and cross-hairs. A

            Keep an eye on the application's output stream, it will give you more details: The name of the mesh that was hit, the coordinates of the hit, and the distance. +

            - +

            Understanding the Helper Methods

            @@ -204,7 +222,7 @@ In this example, we attached all "shootable" objects to one custom nod

            - +

            Understanding Ray Casting for Hit Testing

            @@ -236,12 +254,12 @@ Here is our simple ray casting algorithm for picking objects:
            - +

            Implementing Hit Testing

            - +

            Loading the scene

            @@ -269,7 +287,7 @@ First initialize some shootable nodes and attach them to the scene. You will use }
            - +

            Setting Up the Input Listener

            @@ -282,12 +300,12 @@ Next you declare the shooting action. It can be triggered either by clicking, or private void initKeys() { inputManager.addMapping("Shoot", // Declare... new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar, or - new MouseButtonTrigger(0)); // trigger 2: left-button click + new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // trigger 2: left-button click inputManager.addListener(actionListener, "Shoot"); // ... and add. }
            - +

            Picking Action Using Crosshairs

            @@ -353,7 +371,7 @@ Note how it prints a lot of output to show you which hits were registered.

            - +

            Picking Action Using Mouse Pointer

            @@ -394,7 +412,7 @@ Note that since you now use the mouse for picking, you can no longer use it to r

            - +

            Exercises

            @@ -405,7 +423,7 @@ Modify the code sample to solve these exercises:

            - +

            Exercise 1: Magic Spell

            @@ -432,7 +450,7 @@ Here are some tips:
            - +

            Exercise 2: Shoot a Character

            @@ -448,7 +466,7 @@ Shooting boxes isn't very exciting ??? can you add code that loads and posi
            - +

            Exercise 3: Pick up into Inventory

            @@ -482,7 +500,7 @@ Change the code as follows to simulate the player picking up objects into the in

            - +

            Conclusion

            @@ -533,5 +551,5 @@ See also:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html index 3e18d7e6c..0a0c3589f 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/beginner/hello_simpleapplication.html @@ -56,7 +56,7 @@ If you have questions, read more about -

            Write a SimpleApplication

            +

            Extend SimpleApplication

            @@ -85,7 +85,7 @@ The SDK creates the file Hel

            - +

            Sample Code

            @@ -114,7 +114,7 @@ public class HelloJME3 extends SimpleApplication {   @Override public void simpleInitApp() { - Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create cube shape at the origin + Box b = new Box(1, 1, 1); // create cube shape Geometry geom = new Geometry("Box", b); // create cube geometry from the shape Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material @@ -143,7 +143,7 @@ Congratulations! Now let's find out how it works!

            - +

            Understanding the Code

            @@ -153,20 +153,20 @@ The code above has initialized the scene, and started the application.

            - +

            Start the SimpleApplication

            -Look at the first line. The HelloJME3.java class extends com.jme3.app.SimpleApplication. +Look at the first line. Your HelloJME3.java class extends com.jme3.app.SimpleApplication.

            public class HelloJME3 extends SimpleApplication {
               // your code...
             }

            -Every JME3 game is an instance of com.jme3.app.SimpleApplication. The SimpleApplication class manages your 3D scene graph and automatically draws it to the screen ??? that is, in short, what a game engine does for you! +Every JME3 game is an instance of the com.jme3.app.SimpleApplication class. The SimpleApplication class is the simplest example of an application: It manages a 3D scene graph, checks for user input, updates the game state, and automatically draws the scene to the screen. These are the core features of a game engine. You extend this simple application and customize it to create your game.

            @@ -184,11 +184,11 @@ You start every JME3 game from the main() method, as every standard Java applica }

            -This code opens your application window. Let's learn how you put something into the window next. +The app.start(); line opens the application window. Let's learn how you put something into this window (the scene) next.

            - +

            Understanding the Terminology

            @@ -211,14 +211,14 @@ This code opens your application window. Let's learn how you put something
            You want the cube to appear in the center.I create the Box at the origin = at Vector3f.ZERO.
            - +

            If you are unfamiliar with the vocabulary, read more about the Scene Graph here.

            - +

            Initialize the Scene

            @@ -234,7 +234,7 @@ Look at rest of the code sample. The simpleInitApp() method is auto The initialization code of a blue cube looks as follows:

                public void simpleInitApp() {
            -        Box b = new Box(Vector3f.ZERO, 1, 1, 1); // create a 1x1x1 box shape at the origin
            +        Box b = new Box(1, 1, 1); // create a 1x1x1 box shape
                     Geometry geom = new Geometry("Box", b);  // create a cube geometry from the box shape
                     Material mat = new Material(assetManager,
                       "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
            @@ -288,7 +288,7 @@ A typical JME3 game has the following initialization process:
             
             
             
            - +

            Conclusion

            @@ -349,5 +349,5 @@ See also:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html index c5268f50c..37b9f2148 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/build_jme3_sources_with_netbeans.html @@ -4,7 +4,7 @@

            -You are welcome to try out the new jME3, and contribute patches and features! This document shows how to download, set up, build, and run the latest development version from the sources. (As of Spring 2010, we are in alpha.) These instructions work in NetBeans IDE 6 or better. +You are welcome to try out the new jME3, and contribute patches and features! This document shows how to download, set up, build, and run the latest development version from the sources. These instructions work in NetBeans IDE 6 or better.

            @@ -12,7 +12,7 @@ Note: In the following, always replace "~" with the path to your home

            - +

            Downloading the Sources

            @@ -51,59 +51,85 @@ The jme3 project opens in the Project window. It already includes a working ANT

            Look into the Libraries node and confirm that the project depends on the following libraries in the classpath: +

            -
            jME3-natives-joal.jar	lwjgl.jar       gluegen-rt.jar
            -jME3-lwjgl-natives.jar	jinput.jar	swing-layout-1.0.4.jar
            -j-ogg-oggd.jar	        vecmath.jar     stack-alloc.jar
            -j-ogg-vorbisd.jar       asm-all-3.1.jar jbullet.jar	         
            -jheora-jst-debug-0.6.0.jar              xmlpull.xpp3-1.1.4c.jar
            -nifty*.jar                              eventbus-1.4.jar
            +
              +
            • j-ogg-oggd.jar
              +
            • +
            • j-ogg-vorbisd.jar
              +
            • +
            • jbullet.jar
              +
            • +
            • stack-alloc.jar
              +
            • +
            • vecmath.jar
              +
            • +
            • lwjgl.jar
              +
            • +
            • jME3-lwjgl-natives.jar
              +
            • +
            • jinput.jar
              +
            • +
            • eventbus.jar
              +
            • +
            • nifty-default-controls.jar
              +
            • +
            • nifty-examples.jar
              +
            • +
            • nifty-style-black.jar
              +
            • +
            • nifty.jar
              +
            • +
            • jglfont-core.jar
              +
            • +
            • xmlpull-xpp3.jar
              +
            • +
            • android.jar
              +
            • +
            • jME3-bullet-natives.jar
              +
            • +
            • gluegen-rt.jar
              +
            • +
            • joal.jar
              +
            • +
            • jogl-all.jar
              +
            • +
            • jME3-natives-joal.jar
              +
            • +
            • jME3-openal-soft-natives-android.jar
              +
            • +

            + For a detailed description of the separate jar files see this list.

            - -

            Build and Run

            + +

            Build the Project and Run a Sample App

            - -

            - -That's it! -

            1. Right-click the jme3 project node and "Clean and Build" the project.
            2. -
            3. In the Projects window, browse to the src/test/jme3test folder.
              +
            4. In the Projects window, open the Test folder which contains the sample apps.
              +
            5. +
            6. Every file with a Main class (for example jme3test.model/TestHoverTank.java or jme3test.game/CubeField.java) is an app.
            7. -
            8. Right-click e.g. the file src/test/jme3test/model/TestHoverTank.java and choose "Run" to run a sample.
              +
            9. Right-click a sample app and choose "Run File" (Shift-F6).
              +
            10. +
            11. Generally in sample apps:
                -
              1. In the sample application, use the mouse and the AWSD keys to move around the test object.
                +
              2. the mouse and the WASD keys control movement
              3. -
              4. Press escape to quit the sample application.
                +
              5. the Esc key exits the application
            -

            - -Sample code for cool features is in the src/test/jme3test folder. A sample game can be found in src/games/jme3game/cubefield/CubeField.java. -

            - -

            -Tips: -

            -
              -
            • To run runnable classes from the Projects window, right-click and choose Run.
              -
            • -
            • To run any runnable class that is open in the editor, press shift-F6.
              -
            • -
            -
            - +

            Optional: Javadoc Popups and Source Navigation in NetBeans

            @@ -159,5 +185,5 @@ Sources used: +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html index 7a29a58bf..abb8c1b9d 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/external/blender.html @@ -282,8 +282,54 @@ Models for live rendering should have a low polygon count. To increase the perce

            +

            +

            Be careful: The steps above lead to terrible normal maps - use this procedure instead: +

            +

            +
              +
            • uncheck "[ ] Bake from Multires"
              +
            • +
            • switch to object mode
              +
            • +
            • make a copy of your mesh (SHIFT+D)
              +
            • +
            • remove the Multires modifier from the copied model
              +
            • +
            • remove any materials from the copied model
              +
            • +
            • remove the armature modifier from the copied model
              +
            • +
            • select the original (highres) model
              +
            • +
            • go into pose mode, clear any pose transformations
              +
            • +
            • the highres and lowres models should be on top of each other now
              +
            • +
            • select the original (highres) model
              +
            • +
            • hold SHIFT and select the copied (lowres) model
              +
            • +
            • in the properties menu go to render
              +
            • +
            • use Bake > Normal
              +
            • +
            • check "[x] Selected to Active"
              +
            • +
            • use a reasonably high value for "Margin" (4+ pixels at least for 1024x1024 maps)
              +
            • +
            • don't forget to safe the normal map image
              +
            • +
            + +

            + +

            Be careful: in the Outliner the camera symbol (Restrict Render) must be on! +

            + +

            + - +

            Fixing the normal colors in Blender

            @@ -328,7 +374,7 @@ To do this, go to the Blender Node Window
            - +

            LightMap baking

            @@ -338,7 +384,7 @@ The goal of this tutorial is to explain briefly how to bake light map in blender

            - +

            Blender modeling + texturing

              @@ -387,7 +433,7 @@ The goal of this tutorial is to explain briefly how to bake light map in blender
            - +

            Importing the model in the SDK and creating the appropriate material

            @@ -424,7 +470,27 @@ The blend file, the ogre xml files and the textures can be found in the download

            - + +

            Modelling racing tracks and cars

            +
            + +

            +Follow the link below to a pdf tutorial by rhymez where I guide you to modelling a car and importing it to the jMonkeyengine correctly and edit it in the vehicle editor.Plus how to model a simple racing track. + +

            + +
            + +

            Optimizing Models for 3D games

            +
            + +

            +Follow the link below to a pdf tutorial by rhymez where I guide you on how you can optimize your models for faster rendering. + +

            + +
            +

            SkyBox baking

            @@ -548,10 +614,12 @@ If you want to do it from code, here is an example:

            - +

            Further reading

            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html index df52ea0e5..f7f7c8768 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/faq.html @@ -43,14 +43,14 @@ Yes! Actually, you MUST customize it! For your own games, you always create a cu

            -You should break app your application logic into components by spreading it out over individual AppStates. AppStates can be attached to and detached from the game. AppStates have access to all objects (rootNode, PhysicsSpace, inputManager, etc) and methods in your main application. So each AppState can bring its own subset of input handlers, GUI nodes, spatial nodes, and even its own subset of game mechanics in the update() loop. +You should break down your application logic into components by spreading it out over individual AppStates. AppStates can be attached to and detached from the game. AppStates have access to all objects (rootNode, PhysicsSpace, inputManager, etc) and methods in your main application. So each AppState can bring its own subset of input handlers, GUI nodes, spatial nodes, and even its own subset of game mechanics in the update() loop.
            Learn more: Application States.

            - +

            How do I pause/unpause a game?

            @@ -62,7 +62,7 @@ You split up your application into several AppStates and implement the setEnable

            - +

            How do I disable logger output to the console?

            @@ -86,19 +86,19 @@ For the release, switch the severity level of the default logger to print only S

            - +

            Why does the executable crash with "Cannot locate resource"?

            -Make sure to only load() models converted to .j3o binary format, not the original Ogre or Wavefront formats. If you load assets from zip files, make sure to ammend the build script to copy them ito the build. +Make sure to only load() models converted to .j3o binary format, not the original Ogre or Wavefront formats. If you load assets from zip files, make sure to ammend the build script to copy them to the build directory.
            Learn more: Asset Manager

            - +

            What is java.lang.LinkageError: Version mismatch?

            @@ -110,12 +110,12 @@ To fix this, search for .dll (Windows), .jnilib (Mac), and .so (Linux) files for

            - +

            I want to load my scene

            - +

            How do I make objects appear / disappear in the 3D scene?

            @@ -132,7 +132,7 @@ To make a spatial appear in the scene, you attach it to the rootNode (or to a no

            - +

            Why do I get AssetNotFoundException when loading X ?

            @@ -159,7 +159,7 @@ Note that you should not register every single folder containing a texture as th

            - +

            How do I Create 3-D models, textures, sounds?

            @@ -175,7 +175,7 @@ You create sounds in an audio editor, for example, Audacity, and export them as

            - +

            How do I load a 3-D model into the scene?

            @@ -195,7 +195,7 @@ Spatial ninja = assetManager.loadModel("Models/Ninja/Ninja.j3o"

            - +

            How do initialize the scene?

            @@ -206,12 +206,12 @@ Use the simpleInitApp() method in SimpleApplication (or initApp() in Application

            - +

            I want to transform objects in the scene

            - +

            How do I move or turn or resize a spatial?

            @@ -227,7 +227,7 @@ To move or turn or resize a spatial you use transformations. You can concatenate

            - +

            How do I make a spatial move by itself?

            @@ -240,7 +240,7 @@ Change the geometry's translation (position) live in the update loop using

            - +

            How do I access a named sub-mesh in Model?

            Geometry result = spatial.getName().startsWith(name);
            @@ -251,7 +251,7 @@ Change the geometry's translation (position) live in the update loop using

            - +

            How do I make procedural or custom shapes?

            @@ -262,12 +262,12 @@ You can programmatically create com.jme3.scene.Mesh'es.

            - +

            I want to change the surface of objects in the scene

            - +

            Why is my UV wrapping / texture appearance all wrong?

            @@ -285,7 +285,7 @@ You can set the boolean value in the constructor of TextureKey to flipped or not
              material.setTexture("ColorMap", this.assetManager.loadTexture(new TextureKey("myTexture.jpg", false)));
            - +

            How do I scale, mirror, or wrap a texture?

            @@ -303,7 +303,7 @@ You can choose among various com.jme3.texture.Texture.WrapModes for
            material.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
            - +

            How do I change color or shininess of an material?

            @@ -316,7 +316,7 @@ Use the AssetManager to load Materials, and change material settings.

            - +

            How do I make a surface wood, stone, metal, etc?

            @@ -329,7 +329,7 @@ Create Textures as image files. Use the AssetManager to load a Material and use

            - +

            Why are materials too bright, too dark, or flickering?

            @@ -338,7 +338,7 @@ If you use a lit material (based on Lighting.j3md) then you must attach a light

            - +

            How do I make geometries cast a shadow?

            @@ -351,7 +351,7 @@ Use com.jme3.shadow.BasicShadowRenderer together with com.jme3.light.Directional

            - +

            How do I make materials transparent?

            @@ -367,7 +367,7 @@ Assign a texture with an alpha channel to a Material and set the Material's

            - +

            How do I force or disable culling?

            @@ -391,7 +391,7 @@ You can also deactivate the com.jme3.scene.Spatial.CullHint of a wh

            - +

            Can I draw only an outline of the scene?

            @@ -407,12 +407,12 @@ Add a renders state to the material's and activate Wireframe.

            - +

            I want to control the camera

            - +

            How do I switch between third-person and first-person view ?

            @@ -434,18 +434,18 @@ chaseCam = new ChaseCamera(cam, spatial, inputManager);
            - +

            How do I increase camera speed?

            flyCam.setMoveSpeed(50f);
            - +

            Actions, Interactions, Physics

            - +

            How do I implement game logic / game mechanics?

            @@ -456,7 +456,7 @@ Use Controls to define the behaviour of types of Spatials. Use Application State

            - +

            How do I let players interact via keyboard?

            @@ -467,7 +467,7 @@ Use com.jme3.input.KeyInput and a Input Listener.

            - +

            How do I let players interact by clicking?

            @@ -480,7 +480,7 @@ Players typically click the mouse to pick up objects, to open doors, to shoot a

            - +

            How do I animate characters?

            @@ -493,7 +493,7 @@ Create an animated OgreMesh model with bones in a 3-D mesh editor (e.g. Blender)

            - +

            How do I keep players from falling through walls and floors?

            @@ -504,7 +504,7 @@ Use collision detection. The most common solution is to use jme's physics i

            - +

            How do I make balls/wheels/etc bounce and roll?

            @@ -517,7 +517,7 @@ Add physics controls to Spatials and give them spherical or cylindrical bounding

            - +

            How do I debug weird Physics behaviour?

            @@ -527,12 +527,12 @@ Maybe your collision shapes overlap ??? or they are not where you think they are
            bulletAppState.getPhysicsSpace().enableDebug(assetManager);
            - +

            How do I make a walking character?

            -You can use jBullet's CharacterControl that locks a physical object upright, so it does not tip over when moving/walking (as tall physical objects are wont to do). +You can use jBullet's CharacterControl that locks a physical object upright, so it does not tip over when moving/walking (as tall physical objects are typically wanted to).
            Learn more: CharacterControl
            @@ -540,7 +540,7 @@ Code samples: +

            How do I steer vehicles?

            @@ -553,7 +553,7 @@ Code samples: +

            Can objects swing like a pendulums, chains, ropebridges?

            @@ -565,12 +565,12 @@ Use a PhysicsControl's hinges and joints.

            - +

            Default GUI Display

            - +

            What are these FPS/Objects/Vertices/Triangles statistics?

            @@ -582,7 +582,7 @@ At the bottom left of every default SimpleGame, you see the +

            How do I get rid of the FPS/Objects statistics?

            @@ -601,7 +601,7 @@ setDisplayStatView(false); // to hide the statistics

            - +

            How do I display score, health, mini-maps, status icons?

            @@ -614,7 +614,7 @@ Attach text and pictures to the orthogonal guiNode to create a head

            - +

            How do I display buttons and UI controls?

            @@ -627,7 +627,7 @@ Sample Code: +

            How do i display a loading screen?

            @@ -638,12 +638,12 @@ Instead of having a frozen frame while your games loads, you can have a loading

            - +

            Nifty GUI

            - +

            I get NoSuchElementException when adding controls (buttons etc)!

            @@ -654,7 +654,7 @@ Verify that you include a controls definition file link in your +

            Where can I find example code of Nifty GUI's XML and Java classes?

            @@ -663,7 +663,7 @@ Verify that you include a controls definition file link in your +

            Is there Java Doc for Nifty GUI?

            @@ -672,12 +672,12 @@ Verify that you include a controls definition file link in your +

            I want to create an environment with sounds, effects, and landscapes

            - +

            How do I play sounds and noises?

            @@ -690,7 +690,7 @@ Use AudioRenderer, Listener, and AudioNode from com.jme3.audio.*.

            - +

            How do I make fire, smoke, explosions, swarms, magic spells?

            @@ -703,7 +703,7 @@ For swarm like effects you use particle emitters.

            - +

            How do I make water, waves, reflections?

            @@ -716,7 +716,7 @@ Use a special post-processor renderer from com.jme3.water.*.

            - +

            How do I make fog, bloom, blur, light scattering?

            @@ -727,7 +727,7 @@ Use special post-processor renderers from com.jme3.post.*.

            - +

            How do I generate a terrain?

            @@ -740,7 +740,7 @@ Use com.jme3.terrain.*. The JMonkeyEngine also provides you with a Terrain Edito

            - +

            How do I make a sky?

            @@ -758,12 +758,12 @@ skyGeo.setQueueBucket(Bucket.Sky)

            - +

            I want to access to back-end properties

            - +

            How do I read out graphic card capabilities?

            @@ -775,12 +775,12 @@ If your game is heavily using features that older cards do not support, you can Logger.getLogger(HelloJME3.class.getName()).log(Level.INFO, "Capabilities: {0}", caps.toString());
            - +

            How do I Run jMonkeyEngine 3 with OpenGL1?

            -In you game, add +In your game, add

            settings.setRenderer(AppSettings.LWJGL_OPENGL1)
            @@ -792,7 +792,7 @@ For the jMonkeyEngine SDK it

            - +

            How do I optimize the heck out of the Scene Graph?

            @@ -809,7 +809,7 @@ Batching means that all Geometries with the same Material are combined into one

            - +

            How do I prevent users from unzipping my JAR?

            @@ -819,12 +819,12 @@ Add an - +

            I want to do maths

            - +

            What does addLocal() / multLocal() etc mean?

            @@ -877,7 +877,7 @@ Many maths functions (mult(), add(), subtract(), etc) come as local and a non-lo
            - +

            What is the difference between World and Local coordinates?

            @@ -887,7 +887,7 @@ World coordinates of a Spatial are its absolute coordinates in the 3D scene (thi

            - +

            How do I convert Degrees to Radians?

            @@ -901,5 +901,5 @@ Multiply degree value by FastMath.DEG_TO_RAD to convert it to radians.
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html index b6df625f4..6ee0d853a 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/appsettings.html @@ -8,7 +8,7 @@ Every class that extends jme3.app.SimpleApplication has properties that can be c

            -

            Configure application settings in main(), before you call app.start() on the application object. If you change display settings during runtime, for eyample in simpleInitApp(), you must call app.restart() to make them take effect. +

            Configure application settings in main(), before you call app.start() on the application object. If you change display settings during runtime, for example in simpleInitApp(), you must call app.restart() to make them take effect.

            @@ -47,14 +47,14 @@ This example toggles the settings to fullscreen while the game is already runnin int i=0; // note: there are usually several, let's pick the first settings.setResolution(modes[i].getWidth(),modes[i].getHeight()); settings.setFrequency(modes[i].getRefreshRate()); - settings.setDepthBits(modes[i].getBitDepth()); + settings.setBitsPerPixel(modes[i].getBitDepth()); settings.setFullscreen(device.isFullScreenSupported()); app.setSettings(settings); app.restart(); // restart the context to apply changes }
            - +

            Properties

            @@ -94,7 +94,7 @@ Set VSync to false to deactivate vertical syncing (faster, but possible page tea 60 fps
            -
            +
            @@ -111,7 +111,7 @@ Set VSync to false to deactivate vertical syncing (faster, but possible page tea
            Settings Property (Input)DescriptionDefault
            setEmulateMouseFlipAxis(true,true)Flips the X or Y (or both) axes for the emulated mouse. Set the first parameter to true to flip the x axis, and the second to flip the y axis.false,false
            -
            +
            @@ -122,7 +122,7 @@ Set VSync to false to deactivate vertical syncing (faster, but possible page tea
            Settings Property (Audio)DescriptionDefault
            setStereo3D(true)Enable 3D stereo. This feature requires hardware support from the GPU driver. See . Currently, your everday user's hardware does not support this, so you can ignore it for now.false
            -
            +
            @@ -137,7 +137,7 @@ ImageIO.read(new File("")), ???});
            Settings Property (Branding)DescriptionDefault
            This specifies the little a setSettingsDialogImage("Interface/mysplashscreen.png")A custom splashscreen image in the assets/Interface directory which is displayed when the settings dialog is shown."/com/jme3/app/Monkey.png"
            - +

            You can use app.setShowSettings(true); and setSettingsDialogImage("Interface/mysplashscreen.png") to present the user with jme3's default display settings dialog when starting the game. Use app.setShowSettings(false); to hide the default settings screen. Set this boolean before calling app.start() on the SimpleApplication. @@ -145,7 +145,7 @@ ImageIO.read(new File("")), ???});This specifies the little a

            - +

            Toggling and Activating Settings

            @@ -165,9 +165,9 @@ ImageIO.read(new File("")), ???});
            This specifies the little a app.restart()Restart()ing a running game restarts the game context and applies the updated settings object. (This does not restart or reinitialize the whole game.)
            - +
            - +

            Saving and Loading Settings

            @@ -207,5 +207,5 @@ Provide the unique name of your jME3 application as the String argument. For exa
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html index b45f8f268..fe1b95cb3 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/best_practices.html @@ -158,7 +158,7 @@ Whether you work in a team or alone, keeping a version controlled repository of
            • Treat commit messages as messages to your future self. "Made some changes" is not a commit message.
            • -
            • The jMonkeyEngine SDK supports Subversion, Mercurial, and CVS.
              +
            • The jMonkeyEngine SDK supports Subversion, Mercurial, and Git.
              If you don't know which to choose, Subversion is a good choice for starters.
            • Set up your own local server, or get free remote hosting space from various open-source dev portals like , , (supports private projects), , ???
              diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html index 6f8f200d0..d13cdda5a 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/how_to_use_materials.html @@ -294,7 +294,7 @@ To make a Geometry transparent or translucent:
          24. Put the Geometry (not the Material!) in the appropriate render queue bucket.
            -Objects in the translucent bucket (e.g. particles) are not affected by SceneProcessors (e.g. shadows). Obejcts in the transparent bucket (e.g. foliage) are affected by SceneProcessors (e.g. shadows).
            +Objects in the translucent bucket (e.g. particles) are not affected by SceneProcessors (e.g. shadows). Objects in the transparent bucket (e.g. foliage) are affected by SceneProcessors (e.g. shadows).
            • geo.setQueueBucket(Bucket.Translucent); 
              diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html index a66f936a6..f788b083d 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/math.html @@ -109,7 +109,7 @@ float z = FastMath.sin(phi)*r; - +

              Local vs Non-local methods?

                @@ -122,5 +122,5 @@ float z = FastMath.sin(phi)*r;
              - +

              view online version

              \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html index 144caf8de..38830a730 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/multi-media_asset_pipeline.html @@ -20,7 +20,7 @@ Assets are files that are not code. Your multi-media assets includes, for exampl DODON'T - Save original models plus textures into assets/Textures. Don't leave textures or models in a folder outside your JME project: The game cannot load or reference them from there. + Import original models plus textures into assets/Textures. Don't leave textures or models in a folder outside your JME project: The game cannot load or reference them from there. Save sounds into assets/Sounds. Don't leave audio files in a folder outside your JME project: The game cannot load or reference them from there. @@ -41,14 +41,14 @@ Assets are files that are not code. Your multi-media assets includes, for exampl Agree on naming schemes and folder schemes with your artists early on to avoid confusion. E.g. keep naming schemes for bones and certain model parts. Try to keep your assets folder clean, its like your codes class structure.Don't mindlessly import downloaded models and other assets into your project without keeping a structure and knowing the files work. You can reimport, delete junk. - +

              Read on for details.

              - +

              Use The Assets Folder

              @@ -59,7 +59,7 @@ Store your assets in subfolders of your project's assets direc
              jMonkeyProjects/MyGame/assets/Interface/ # .font, .jpg, .png, .xml
               jMonkeyProjects/MyGame/assets/MatDefs/   # .j3md
               jMonkeyProjects/MyGame/assets/Materials/ # .j3m
              -jMonkeyProjects/MyGame/assets/Models/    # .j3o
              +jMonkeyProjects/MyGame/assets/Models/    # .blend, .j3o
               jMonkeyProjects/MyGame/assets/Scenes/    # .j3o
               jMonkeyProjects/MyGame/assets/Shaders/   # .j3f, .vert, .frag
               jMonkeyProjects/MyGame/assets/Sounds/    # .ogg, .wav
              @@ -105,7 +105,7 @@ See also:
               
            - +

            Create Textures and Materials

            @@ -140,7 +140,7 @@ Storing the textures inside your project directory is necessary for the paths in

            - +

            Create 3D Models

            @@ -189,7 +189,7 @@ See also:

            - +

            Convert 3D Models to .j3o Format

            @@ -216,7 +216,7 @@ This process ensures that the texture paths are correct, and it also keeps your

            - +

            Must I convert to .j3o? Yes!

            @@ -245,7 +245,7 @@ Use Java commands, or use the +

            See Also

              @@ -264,5 +264,5 @@ Use Java commands, or use the +

              view online version

              \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html index 1dbc1539b..54c573de9 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/intermediate/optimization.html @@ -45,15 +45,41 @@ You can optimize nodes using the SceneComposer in the +

              Avoid large objects in physics

              @@ -63,7 +89,7 @@ To offload much computation to the less CPU intense physics broadphase collision

              - +

              Check the Statistics

              @@ -94,5 +120,5 @@ Generally jME3 is well optimized and optimizes these things correctly. Read
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html index 0e2ab365d..169d5e89b 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/ios.html @@ -6,17 +6,23 @@ To use iOS deployment you need a computer running MacOSX and a version of Xcode 4.0+ installed. To deploy to a device or the Apple App Store, you need an Apple developer account.

            +

            +

            Note that at the moment iOS deployment is in alpha state. +

            +

            +

            iOS deployment works via cross-compilation to native iOS ARM code, there is no virtual machine running on the device. The Avian JVM supports this feature while maintaining general compatibility to OpenJDK and JNI for native access. The minimum compatible iOS deployment target is 4.3.

            -

            Note that at the moment this option is in pre-alpha state and the system runs on a null renderer. This means there is no visual or audio output. You can however use the current system to explore the options and test cross-compiling your applications. +

            To install the iOS deployment plugin, go to Tools???Plugins and under "Available plugins" select the "iOS Support" plugin.

            +

            - +

            Enabling iOS deployment

            @@ -39,7 +45,7 @@ After enabling deployment, a new ios directory is created in the pr

            - +

            Building the iOS binaries

            @@ -56,7 +62,7 @@ After the iOS classpath has been created the avian compiler is used to create a

            - +

            Running and deploying the application

            @@ -68,7 +74,7 @@ To run the application, open the Xcode project under ios/project in

            - +

            Creating native and java code for iOS

            @@ -97,5 +103,5 @@ Java code for iOS should be in the ios/src folder as well for clean
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html index 305bd481f..414cd9af1 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/math.html @@ -44,6 +44,30 @@ The definition of a coordinate system is defined in jME by the properties sent t +

            Homogenous coordinates

            +
            + +

            + +Homogenous coordinates have an additional W value tacked on to the end. The XYZ values are to be divided by W to give the true coordinates. +

            + +

            +This has several advantages, one technical, some relevant to application programmers: +

            + +

            +Technically, it simplifies some formulae used inside the vector math. For example, some operations need to apply the same factor to the XYZ coordinates. Chain multiple operations of that kind (and vector math tends to do that), and you can save a lot of multiplications by simply keeping the scaling factor around and doing the multiplication to XYZ at the end of the pipeline, in the 3D card (which does accept homogenous coordinates). +It also simplifies some formulae, in particular anything that is related to rotations. +

            + +

            +For application programmers, this means you can express infinitely long vectors that still have a direction - these tend to be used in lighting. Just use a W value of 0.0. + +

            + +
            +

            Transformations

            @@ -53,7 +77,7 @@ Transformations define an operation that converts points from one coordinate sys

            - +

            Visibility Determination

            @@ -63,17 +87,17 @@ Visibility Determination concerns itself with minimizing the amount of data that

            - +

            Fundamental Types

            - +

            ColorRGBA

            - +

            Definition

            @@ -87,7 +111,7 @@ ColorRGBA defines a color value in the jME library. The color value is made of t

            - +

            jME Class

            @@ -109,7 +133,7 @@ ColorRGBA will also handle interpolation between two colors. Given a second colo

            - +

            Matrix

            @@ -121,7 +145,7 @@ and - +

            Definition

            @@ -148,7 +172,7 @@ There are a few special matrices: 000
            - +

            The Identity Matrix is the matrix with 1 on the diagonal entries and 0 for all other entries. @@ -164,7 +188,7 @@ The Identity Matrix is the matrix with 1 on the diagonal entries and 0 001 - +

            A Matrix is invertible if there is a matrix M-1 where MM-1 = M-1M = I. @@ -185,7 +209,7 @@ The transpose of a matrix M = [mij] is MT< 333 123 - +

            A Matrix is symmetric if M = MT. @@ -201,7 +225,7 @@ A Matrix is symmetric if M = MT. BCX - +

            Where X, A, B, and C equal numbers

            @@ -211,7 +235,7 @@ jME includes two types of Matrix classes: Matrix3f and Matrix4f. Matrix3f is a 3

            - +

            Transformations

            @@ -251,7 +275,7 @@ A rotation matrix requires that the transpose and inverse are the same u1-u00
            - +

            Translation

            @@ -270,14 +294,14 @@ Translation requires a 4x4 matrix, where the vector (x,y,z) is mapped to (x,y,z, ST1 - +

            where M is the 3x3 matrix (containing any rotation/scale information), T is the translation vector and ST is the transpose Vector of T. 1 is just a constant.

            - +

            jME Class

            @@ -291,7 +315,7 @@ Most methods are straight forward, and I will leave documentation to the Javadoc

            - +

            Vector

            @@ -303,7 +327,7 @@ and - +

            Definition

            @@ -321,7 +345,7 @@ We have two Vectors (2f and 3f) meaning we have tuples of 2 float values or 3 fl

            - +

            Operations

            @@ -411,7 +435,7 @@ Vector3f and Vector2f store their values (x, y, z) and (x, y) respectively as fl

            - +

            Quaternion

            @@ -421,7 +445,7 @@ See - +

            Definition

            @@ -485,7 +509,7 @@ These basic operations allow us to convert various rotation representations to Q

            - +

            Angle Axis

            @@ -505,7 +529,7 @@ float angle = 3.14f; s.getLocalRotation().fromAngleAxis(angle, axis);
            - +

            Three Angles

            @@ -523,7 +547,7 @@ float[] angles = {1, 3, 0}; s.getLocalRotation().fromAngles(angles);
            - +

            Three Axes

            @@ -545,7 +569,7 @@ axes[2] = new Vector3f(0, 0.5f, 0.5f); //dir s.getLocalRotation().fromAxes(axes);
            - +

            Rotation Matrix

            @@ -570,7 +594,7 @@ As you can see there are many ways to build a Quaternion. This allows you to wor

            - +

            Slerp

            @@ -590,7 +614,7 @@ Quaternion q2; Quaternion q3 = q1.slerp(q2, 0.5f);
            - +

            Multiplication

            @@ -608,7 +632,7 @@ Vector3f myVector = new Vector3f(0,0,-1); myRotation.multLocal(myVector);
            - +

            Utility Classes

            @@ -618,7 +642,7 @@ Along with the base Math classes, jME provides a number of Math classes to make

            - +

            Fast Math

            @@ -628,7 +652,7 @@ See - +

            Definition

            @@ -638,7 +662,7 @@ FastMath provides a number of convience methods, and where possible faster versi

            - +

            Usage

            @@ -729,7 +753,7 @@ There are five major categories of functions that FastMath provides.
            - +

            Line

            @@ -739,7 +763,7 @@ See - +

            Definition

            @@ -749,7 +773,7 @@ A line is a straight one-dimensional figure having no thickness and extending in

            - +

            Usage

            @@ -763,14 +787,14 @@ jME defines a Line class that is defined by an origin and direction. In reality,

            - +

            Example 1 - Find a Random Point on a Line

            Line l = new Line(new Vector3f(0,1,0), new Vector3f(3,2,1));
             Vector3f randomPoint = l.random();
            - +

            Plane

            @@ -780,7 +804,7 @@ See - +

            Definition

            @@ -814,7 +838,7 @@ This gives us the general equation: (ax + by + cz + d = 0)

            - +

            Usage in jME

            @@ -846,7 +870,7 @@ These values are returned on a call to whichSide.

            - +

            Example 1 - Determining if a Point is On the Positive Side of a Plane

            Vector3f normal = new Vector3f(0,1,0);
            @@ -860,7 +884,7 @@ if(side == Plane.NO_SIDE) {
             }
            - +

            Example 2 - For the Layperson

            @@ -932,7 +956,7 @@ public class TestPlanes }
            - +

            Ray

            @@ -942,7 +966,7 @@ See - +

            Definition

            @@ -956,13 +980,13 @@ This Ray is used extensively in jME for +

            Example 1 - Create a Ray That Represents Where the Camera is Looking

            Ray ray = new Ray(cam.getLocation(), cam.getDirection());
            - +

            Rectangle

            @@ -972,7 +996,7 @@ See - +

            Definition

            @@ -982,7 +1006,7 @@ Rectangle defines a finite plane within three dimensional space that is specifie

            - +

            jME Usage

            @@ -992,7 +1016,7 @@ Rectangle is a straight forward data class that simply maintains values that def

            - +

            Example 1 : Define a Rectangle and Get a Point From It

            Vector3f v1 = new Vector3f(1,0,0);
            @@ -1002,7 +1026,7 @@ Rectangle r = new Rectangle(v1, v2, v3);
             Vector3f point = r.random();
            - +

            Triangle

            @@ -1012,7 +1036,7 @@ See - +

            Definition

            @@ -1022,7 +1046,7 @@ A triangle is a 3-sided polygon. Every triangle has three sides and three angles

            - +

            Usage

            @@ -1032,7 +1056,7 @@ jME's Triangle class is a simple data class. It contains three +

            Example 1 - Creating a Triangle

            //the three points that make up the triangle
            @@ -1042,12 +1066,12 @@ Vector3f p3 = new Vector3f(0,1,1);
             Triangle t = new Triangle(p1, p2, p3);
            - +

            Tips and Tricks

            - +

            How do I get height/width of a spatial?

            @@ -1062,13 +1086,13 @@ float y = ( (BoundingBox)spatial.getWorldBound()).getYEx float z = ( (BoundingBox)spatial.getWorldBound()).getZExtent();
            - +

            How do I position the center of a Geomtry?

            geo.center().move(pos);
            - +

            See Also

              @@ -1079,5 +1103,5 @@ float z = ( (BoundingBox)spatial.getWorldBound()).getZEx
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html index 05bc67ef1..20b48a131 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/jme3/terminology.html @@ -352,11 +352,11 @@ See also: Water.

            -MIP Map means that you provide one texture in two or three resolutions in one file (MIP = "multum in parvo" = "many in one"). Depending on how close (or far) the camera is, the engine automatically renders a more (or less) detailed texture for the object. Thus objects look smooth from close up, but don't waste resources with unspottable details when far away. Good for everything, but requires more time to create and more space to store textures. If you don't provide custom ones, the jMonkeyEngine creates basic MIP maps automatically as an optimization. +MIP Map means that you provide one texture in two or three resolutions in one file (MIP = "multum in parvo" = "many in one"). Depending on how close (or far) the camera is, the engine automatically renders a more (or less) detailed texture for the object. Thus objects look detailed at close up, but also look good when viewed from far away. Good for everything, but requires more time to create and more space to store textures. If you don't provide custom ones, the jMonkeyEngine creates basic MIP maps automatically as an optimization.

            - +

            Procedural Textures

            @@ -374,7 +374,7 @@ See also:

            - +

            Animation

            @@ -388,7 +388,7 @@ Unless you animate a 3D cartoon, realism of animated characters is generally a p

            - +

            Rigging and Skinning

            @@ -458,7 +458,7 @@ E.g. when the thigh bone moves, the leg is fully affected, the hips joints less

            - +

            Kinematics

              @@ -469,7 +469,7 @@ E.g. when the thigh bone moves, the leg is fully affected, the hips joints less
            - +

            Controller and Channel

            @@ -478,7 +478,7 @@ In the JME3 application, you register animated models to the Animation Controlle

            - +

            Artificial Intelligence (AI)

            @@ -529,7 +529,7 @@ There are lots of resources explaining interesting AI algorithms:
            - +

            Math

            @@ -538,7 +538,7 @@ There are lots of resources explaining interesting AI algorithms:

            - +

            Coordinates

            @@ -549,7 +549,7 @@ In contrast to a vector (which looks similar), a coordinate is a location, not a

            - +

            The Origin

            @@ -563,7 +563,7 @@ The origin is the central point in the 3D world, where the three axes meet. It&#

            - +

            Vectors

            @@ -579,7 +579,7 @@ A vector has a length and a direction, like an arrow in 3D space. A vector start Vector3f v = new Vector3f( 8f , 0f , 33f ).add(new Vector3f( 0f , -2f , -2f )); // starts at (8,-2,31)
            - +

            Unit Vectors

            @@ -603,7 +603,7 @@ Negate the vegator to change its direction, e.g. (-1, 0, 0) = left.

            - +

            Normalized Vectors

            @@ -618,7 +618,7 @@ When you normalize a vector, it still has the same direction, but you lose the i

            - +

            Surface Normal Vectors

            @@ -630,7 +630,7 @@ You calculate the Surface Normal by calculating the cross product.

            - +

            Cross Product

            @@ -647,7 +647,7 @@ In 3D space, speaking of an orthogonal only makes sense with respect to a plane.

            - +

            Transformation

            @@ -661,7 +661,7 @@ Examples: Falling and rotating bricks in 3D Tetris.

            - +

            Slerp

            @@ -679,7 +679,7 @@ Example: A burning meteorite Geometry slerps from "position p1, rotation r1

            - +

            Game Developer Jargon

              @@ -688,7 +688,7 @@ Example: A burning meteorite Geometry slerps from "position p1, rotation r1
            - +

            3D graphics Terminology Wiki book

              @@ -697,5 +697,5 @@ Example: A burning meteorite Geometry slerps from "position p1, rotation r1
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html index 796bf7b5f..caa515174 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/application_deployment.html @@ -15,10 +15,12 @@ After you have written and tested your game, you want to brand it and distribute
          25. Android mobile device (.APK)
          26. +
          27. iOS mobile device (XCode project)
            +
          28. - +

            Requirements

            @@ -28,7 +30,7 @@ Since JAR files are platform independent, your customers can play your jMonkeyEn

            - +

            Branding

            @@ -72,7 +74,7 @@ TODO: where does this info actually show up?

            - +

            Creating the Distributable

            @@ -90,7 +92,7 @@ Here are your deployment options in detail:

            - +

            Desktop Application (JAR)

            @@ -118,7 +120,7 @@ Most operating systems execute a JAR when users double-click on it, but you can

            - +

            Desktop Executables (.EXE, .APP, .JAR)

            @@ -144,7 +146,7 @@ When you build your project, zip files for each selected platform will be create

            - +

            Web Start (.JNLP)

            @@ -181,7 +183,7 @@ Also, see this +

            Browser Applet

            @@ -246,7 +248,7 @@ The dist/Applet directory now contains all the files necessary for
            - +

            Android Mobile Device

            @@ -260,13 +262,13 @@ Learn more about Android Support<

            - +

            iOS Device

            -You can set the jMonkeyEngine SDK to build an executable for iOS platforms. Mac support is work in progress. +You can set the jMonkeyEngine SDK to build an executable for iOS platforms. A Mac with XCode installed is needed.

            @@ -274,7 +276,7 @@ Learn more about iOS Support here

            - +

            Tip: Switching Build Configurations

            @@ -299,7 +301,7 @@ Now you can use the Set Project Configuration popup menu to switch

            - +

            Tip: Reduce Distribution File Size

            @@ -342,5 +344,5 @@ jme3-libraries-gui, jme3-libraries-physics, jme3-libraries-video, etc.
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html index e390c43bf..c8503d79e 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/blender.html @@ -158,15 +158,23 @@ By default a BlenderModelLoader is registered with your assetManager to load ble +
          29. Importing sky
            +
              +
            • loading world's horizon color as a background color if no sky type is used
              +
            • +
            • loading sky without the texture
              +
            • +
            • loading textured sky (including both generated and normal textures)
              +
            • +
            +
          30. - +

            Planned features.

              -
            1. Loading sky.
              -
            2. Full support for scale and offset in texture input mapping.
            3. Full support for bone and object constraints.
              @@ -180,7 +188,7 @@ By default a BlenderModelLoader is registered with your assetManager to load ble
            - +

            Known bugs/problems.

              @@ -191,7 +199,7 @@ By default a BlenderModelLoader is registered with your assetManager to load ble
            - +

            Using BlenderLoader instead of BlenderModelLoader

            @@ -252,7 +260,7 @@ You can use ModelKey as well. This will give the same result as using default Bl

            - +

            How does it work?

            @@ -298,7 +306,7 @@ Here is the list of how blender features are mapped into jme. Surface Node The surface is transformed to the proper mesh
            - +

            Using BlenderLoader can allow you to use blend file as your local assets repository. @@ -308,7 +316,7 @@ Probably versions before 2.49 will work pretty well too, but I never checked tha

            - +

            Notes

            @@ -321,6 +329,15 @@ Hope I will meet your expectations. Be mindful of the result model vertices amount. The best results are achieved when the model is smooth and has no texture. Then the vertex amount is equal to the vertex amount in blender. If the model is not smooth or has a generated texture applied then the amount of vertices is 3 times larger than mesh's triangles amount. If a 2d texture is applied with UV mapping then the vertex count will vary depending on how much the UV map is fragmented.

            +

            +When using polygon meshes in blender 2.5 and newer, better add and apply the triangulation modifier (if available in your version) or save the file with convertion from polygons to triangles. +Even though the importer supports loading of polygons as the mesh faces, if your face isn't convex, the results might contain errors. +

            + +

            +Not all modifiers are supported. If your model has modifiers and looks not the way you want in the jme scene - try to apply them and load again. +

            +

            Cheers, Marcin Roguski (Kaelthas) @@ -349,5 +366,5 @@ See also:

            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html index 5fd7aeb9a..ff5463bd0 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/development.html @@ -78,6 +78,26 @@ If you feel like you want to make an addition to jMonkeyEngine SDK. It can contain editors, simple ???Java SE Libraries??? that you can add to your projects as jar files and other things like project templates etc. + +
          31. A ???module??? is the project type that allows you to create plugins, strictly speaking all plugins are modules but there can be modules that are never shown in the plugin list and only exist as dependencies of other modules.
            +
          32. +
          33. A ???library??? is an entry for a jar file (and optionally sources and javadocs) which can be added to a SDK project to be used and distributed with it
            +
          34. +
          35. An ???extension??? is a generic name for stuff that extends the jME engine, like pathfinding algorithms or anything that can be used at the game runtime..
            +
          36. + + +

            + +So if you have some cool code that others can use in their games too, you would make your extension a library by creating a module that the users can download as a plugin :) +

            + + +

            Handy things in jMonkeyEngine SDK Core

              diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html index 0f88f5927..be7f4b7ab 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/material_editing.html @@ -112,7 +112,7 @@ Or in your Java code
            • Use a loader and a setter to assign the material to a Geometry
            -
            mywall.setMaterial(assetManager.loadAsset( "Materials/mat_wall.j3m"));
            +
            mywall.setMaterial(assetManager.loadMaterial( "Materials/mat_wall.j3m"));

            diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html index a6c55aff1..0761cae9e 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/scene_explorer.html @@ -30,11 +30,11 @@ The SceneExplorer displays Nodes in a tree that represents the tree of Spatials

            -You open the SceneExplorer by viewing a model (j3o file or other) in the jMonkeyEngine SDK. +SceneExplorer works in conjunction with SceneComposer, the default editor for J3O files in the jMonkeyEngine IDE. If SceneExplorer doesn't appear when you select "Edit in SceneComposer", choose Window ??? SceneExplorer from the menu bar to reveal the window.

            - +

            Editing Objects in the scene

              @@ -47,7 +47,7 @@ You open the SceneExplorer by viewing a model (j3o file or other) in the jMonkey
            - +

            Reorganizing Objects in the scene

              @@ -58,7 +58,7 @@ You open the SceneExplorer by viewing a model (j3o file or other) in the jMonkey
            - +

            Adding Objects to the scene

            @@ -75,5 +75,5 @@ Right-click a Spatial or Node in the SceneExplorer to add other Spatials like Pa
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html index 981fbef5d..1dc4e395b 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/troubleshooting.html @@ -4,30 +4,6 @@ -

            Specifying the JDK location

            -
            - -

            - -You should install the JDK (the one from Oracle, not OpenJDK) first, and then the jMonkey SDK. If jMonkeyEngine SDK cannot find a valid JDK although you have it installed, then you have to specify the location manually. - -

            -
              -
            1. Go to your jMonkeyEngine SDK installation directory.
              -Mac users right-click jMonkeyApplication.app (which actually is a directory) in the Finder and select "Show package contents".
              -
            2. -
            3. Navigate to the etc directory.
              -Mac users navigate to Contents/Resources/jmonkeyplatform/etc/.
              -
            4. -
            5. Open the file jmonkeyplatform.conf in a text editor.
              -
            6. -
            7. Uncomment the following line and enter the path to the JDK:
              jdkhome="/path/to/jdk"
              -
              -
            8. -
            - -
            -

            Graphics Card Driver

            @@ -37,7 +13,7 @@ Mac users navigate to Contents/Resources/jmonkeyplatform/etc/.
            - +

            Stability / Graphics issues

            @@ -80,7 +56,7 @@ Compiz on Linux might cause issues, if you set its rendering quality to "me

            - +

            Updating problems

            @@ -89,7 +65,7 @@ If you have problems updating the SDK<

            - +

            Preferences and Settings

            @@ -108,7 +84,7 @@ To completely remove and/or reinstall the +

            Log

            @@ -117,7 +93,7 @@ To see or post the error output of the
            - +

            Getting error messages and reporting issues

            @@ -126,7 +102,31 @@ When an exception happens in the SDK
            - + +

            Specifying the JDK location

            +
            + +

            + +You can install another JDK for use with the jMonkey SDK. You then have to specify the location manually. + +

            +
              +
            1. Go to your jMonkeyEngine SDK installation directory.
              +Mac users right-click jMonkeyApplication.app (which actually is a directory) in the Finder and select "Show package contents".
              +
            2. +
            3. Navigate to the etc directory.
              +Mac users navigate to Contents/Resources/jmonkeyplatform/etc/.
              +
            4. +
            5. Open the file jmonkeyplatform.conf in a text editor.
              +
            6. +
            7. Uncomment the following line and enter the path to the JDK:
              jdkhome="/path/to/jdk"
              +
              +
            8. +
            + +
            +

            Known Issues

            @@ -142,5 +142,5 @@ For a list of known issues and possible workarounds see the following link:
            - +

            view online version

            \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html index b929ecc6d..8ac499545 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/sdk/version_control.html @@ -4,7 +4,7 @@

            -Whether you work in a development team or alone: File versioning is a handy method to keep your code consistent, compare files line-by-line, and even roll back unwanted changes. This documentation shows you how to make the most of the SDK's integrated version control features for Subversion, Mercurial, and CVS. +Whether you work in a development team or alone: File versioning is a handy method to keep your code consistent, compare files line-by-line, and even roll back unwanted changes. This documentation shows you how to make the most of the SDK's integrated version control features for Subversion, Mercurial, and Git.

            @@ -31,7 +31,7 @@ Note: Since the jMonkeyEngine SDK -The jMonkeyEngine SDK supports various Version Control Systems such as Subversion, Mercurial, and CVS. No matter which of them you use, they all share a common user interface. +The jMonkeyEngine SDK supports various Version Control Systems such as Subversion, Mercurial, and Git. No matter which of them you use, they all share a common user interface.

            @@ -50,7 +50,7 @@ Requirements:

            • You must have a project that you want to version.
            • -
            • You must have version control software installed (Subversion, Mercurial, or CVS) and have initialized a repository.
              +
            • You must have version control software installed (Subversion, Mercurial, or Git) and have initialized a repository.
              • Tip: For Subversion, for example, the init command looks like this example: svnadmin create /home/joe/jMonkeyProjects/MyGame
              • @@ -70,7 +70,7 @@ Now you create a repository to store your project's files.

                  -
                1. In the jMonkeyEngine SDK, right-click the project in the Projects window and choose Versioning > Import Into CVS/Subversion Repository (or initialize Mercurial Project, respectively).
                  +
                2. In the jMonkeyEngine SDK, right-click the project in the Projects window and choose Versioning > Import Into Subversion Repository (or initialize Mercurial Project, etc, respectively).
                  • Tip: If you haven't evaluated yet which system to choose, start with Subversion for now.
                  • @@ -81,7 +81,7 @@ Now you create a repository to store your project's files.
                - +

                Checking Out a Repository (Download)

                @@ -91,7 +91,7 @@ You and your team mates check out (download) the repository to their individual

                  -
                1. Go to the Team menu and choose Subversion > Checkout (or CVS/Mercurial>Checkout respectively)
                  +
                2. Go to the Team menu and choose Subversion > Checkout (or Git or Mercurial respectively)
                3. Fill in your repo data into the wizard and click Finish.
                    @@ -117,7 +117,7 @@ Of course you can also check out existing repositories and access code from othe

                - +

                Updating and Committing Changes (Send and Receive)

                @@ -156,7 +156,7 @@ Receiving the latest changes from the team's repository is referred to as <
                - +

                Comparing and Reverting Changes

                  @@ -187,7 +187,7 @@ Receiving the latest changes from the team's repository is referred to as <
                - +

                No Version Control? Local History!

                @@ -221,5 +221,5 @@ See also:
                - +

                view online version

                \ No newline at end of file diff --git a/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml index 501eeb9fc..f4fb29835 100644 --- a/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml +++ b/sdk/jme3-documentation/src/com/jme3/gde/docs/wiki-map.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/sdk/nbproject/project.properties b/sdk/nbproject/project.properties index 55e93e79a..081d1760c 100644 --- a/sdk/nbproject/project.properties +++ b/sdk/nbproject/project.properties @@ -6,7 +6,7 @@ app.icon.icns=jmonkeyplatform.icns #version name used for application and settings folder, no spaces! app.version=3.0 #version number used for plugins, only 3 numbers (e.g. 3.1.3) -plugins.version=3.0.5 +plugins.version=3.0.6 #used netbeans platform netbeans.platform.url=http://download.netbeans.org/netbeans/7.3.1/final/zip/netbeans-7.3.1-201306052037-javase.zip #command line args