Quellcode durchsuchen

Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine.git

iwgeric vor 10 Jahren
Ursprung
Commit
3ee52c38ed
43 geänderte Dateien mit 2166 neuen und 270 gelöschten Zeilen
  1. 1 0
      .gitignore
  2. 2 1
      jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
  3. 4 0
      jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java
  4. 26 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  5. 16 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  6. 23 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java
  7. 16 21
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  8. 101 131
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  9. 6 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java
  10. 6 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  11. 21 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java
  12. 21 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  13. 10 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  14. 50 31
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  15. 19 11
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  16. 19 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  17. 12 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java
  18. 6 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java
  19. 10 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  20. 13 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  21. 11 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  22. 13 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java
  23. 7 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java
  24. 6 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java
  25. 453 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java
  26. 170 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java
  27. 867 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java
  28. 2 1
      jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java
  29. 14 1
      jme3-core/src/main/java/com/jme3/renderer/Renderer.java
  30. 10 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  31. 3 0
      jme3-core/src/main/java/com/jme3/system/NullRenderer.java
  32. 173 21
      jme3-core/src/main/java/com/jme3/util/SkyFactory.java
  33. 2 0
      jme3-core/src/main/resources/Common/MatDefs/Misc/Sky.j3md
  34. 18 3
      jme3-core/src/main/resources/Common/ShaderLib/Optics.glsllib
  35. 2 1
      jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
  36. 2 2
      jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java
  37. 1 1
      jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java
  38. 2 1
      jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java
  39. 4 0
      jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
  40. 4 0
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java
  41. 9 4
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java
  42. 6 1
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java
  43. 5 0
      jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java

+ 1 - 0
.gitignore

@@ -123,3 +123,4 @@
 *.so
 *.jnilib
 *.dylib
+/sdk/dist/

+ 2 - 1
jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java

@@ -42,6 +42,7 @@ import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.system.JmeSystem;
 import com.jme3.system.Timer;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
 import com.jme3.util.AndroidScreenshots;
 import com.jme3.util.BufferUtils;
 import java.io.File;
@@ -234,7 +235,7 @@ public class VideoRecorderAppState extends AbstractAppState {
                 final WorkItem item = freeItems.take();
                 usedItems.add(item);
                 item.buffer.clear();
-                renderer.readFrameBuffer(out, item.buffer);
+                renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8);
                 executor.submit(new Callable<Void>() {
 
                     public Void call() throws Exception {

+ 4 - 0
jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java

@@ -2564,4 +2564,8 @@ public class OGLESShaderRenderer implements Renderer {
     public void setLinearizeSrgbImages(boolean linearize) {
         //TODO once opglES3.0 is supported maybe....
     }
+    
+    public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
+        throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly"); 
+    }
 }

+ 26 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java

@@ -67,9 +67,9 @@ public class BoneContext {
     private float                length;
     /** The bone's deform envelope. */
     private BoneEnvelope         boneEnvelope;
-    
+
     // The below data is used only for IK constraint computations.
-    
+
     /** The bone's stretch value. */
     private float                ikStretch;
     /** Bone's rotation minimum values. */
@@ -366,6 +366,30 @@ public class BoneContext {
         return (flag & flagMask) != 0;
     }
 
+    /**
+     * @return the root bone context of this bone context
+     */
+    public BoneContext getRoot() {
+        BoneContext result = this;
+        while (result.parent != null) {
+            result = result.parent;
+        }
+        return result;
+    }
+
+    /**
+     * @return a number of bones from this bone to its root
+     */
+    public int getDistanceFromRoot() {
+        int result = 0;
+        BoneContext boneContext = this;
+        while (boneContext.parent != null) {
+            boneContext = boneContext.parent;
+            ++result;
+        }
+        return result;
+    }
+
     @Override
     public String toString() {
         return "BoneContext: " + boneName;

+ 16 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java

@@ -62,7 +62,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
                 }
             }
         }
-        return true;
+        return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
     }
 
     @Override
@@ -70,4 +70,19 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
         super.apply(frame);
         blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms();
     }
+    
+    @Override
+    public Long getTargetOMA() {
+        if(targetOMA != null && subtargetName != null && !subtargetName.trim().isEmpty()) {
+            Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
+            if(nodeTarget != null) {
+                if(blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
+                    BoneContext boneContext = blenderContext.getBoneByName(targetOMA, subtargetName);
+                    return boneContext != null ? boneContext.getBoneOma() : 0L;
+                }
+                return targetOMA;
+            }
+        }
+        return 0L;
+    }
 }

+ 23 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java

@@ -116,14 +116,31 @@ public abstract class Constraint {
      */
     public abstract boolean validate();
 
+    /**
+     * @return the OMA of the target or 0 if no target is specified for the constraint
+     */
+    public abstract Long getTargetOMA();
+
     /**
      * Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
      * @param frame
      *            the frame of the animation
      */
     public void apply(int frame) {
+        if (LOGGER.isLoggable(Level.FINEST)) {
+            LOGGER.log(Level.FINEST, "Applying constraint: {0} for frame {1}", new Object[] { name, frame });
+        }
         Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
-        constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float)ipo.calculateValue(frame));
+        constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float) ipo.calculateValue(frame));
+    }
+
+    /**
+     * @return determines if the definition of the constraint will change the bone in any way; in most cases
+     *         it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
+     *         computing to improve the computation speed and lower the computations complexity
+     */
+    public boolean isTrackToBeChanged() {
+        return constraintDefinition == null ? false : constraintDefinition.isTrackToBeChanged();
     }
 
     @Override
@@ -163,4 +180,9 @@ public abstract class Constraint {
         }
         return true;
     }
+
+    @Override
+    public String toString() {
+        return "Constraint(name = " + name + ", def = " + constraintDefinition + ")";
+    }
 }

+ 16 - 21
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java

@@ -2,8 +2,10 @@ package com.jme3.scene.plugins.blender.constraints;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.logging.Logger;
 
 import com.jme3.animation.Bone;
@@ -170,31 +172,24 @@ public class ConstraintHelper extends AbstractBlenderHelper {
      *            the blender context
      */
     public void bakeConstraints(BlenderContext blenderContext) {
-        List<SimulationNode> simulationRootNodes = new ArrayList<SimulationNode>();
+        Set<Long> owners = new HashSet<Long>();
         for (Constraint constraint : blenderContext.getAllConstraints()) {
-            boolean constraintUsed = false;
-            for (SimulationNode node : simulationRootNodes) {
-                if (node.contains(constraint)) {
-                    constraintUsed = true;
-                    break;
-                }
-            }
-
-            if (!constraintUsed) {
-                if (constraint instanceof BoneConstraint) {
-                    BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA);
-                    simulationRootNodes.add(new SimulationNode(boneContext.getArmatureObjectOMA(), blenderContext));
-                } else if (constraint instanceof SpatialConstraint) {
-                    Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE);
-                    while (spatial.getParent() != null) {
-                        spatial = spatial.getParent();
-                    }
-                    simulationRootNodes.add(new SimulationNode((Long) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial), blenderContext));
-                } else {
-                    throw new IllegalStateException("Unsupported constraint type: " + constraint);
+            if(constraint instanceof BoneConstraint) {
+                BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA);
+                owners.add(boneContext.getArmatureObjectOMA());
+            } else {
+                Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedDataType.FEATURE);
+                while (spatial.getParent() != null) {
+                    spatial = spatial.getParent();
                 }
+                owners.add((Long)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, spatial));
             }
         }
+        
+        List<SimulationNode> simulationRootNodes = new ArrayList<SimulationNode>(owners.size());
+        for(Long ownerOMA : owners) {
+            simulationRootNodes.add(new SimulationNode(ownerOMA, blenderContext));
+        }
 
         for (SimulationNode node : simulationRootNodes) {
             node.simulate();

+ 101 - 131
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java

@@ -7,7 +7,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.jme3.animation.AnimChannel;
@@ -41,14 +40,13 @@ import com.jme3.util.TempVars;
 public class SimulationNode {
     private static final Logger  LOGGER   = Logger.getLogger(SimulationNode.class.getName());
 
+    private Long featureOMA;
     /** The blender context. */
     private BlenderContext       blenderContext;
     /** The name of the node (for debugging purposes). */
     private String               name;
     /** A list of children for the node (either bones or child spatials). */
     private List<SimulationNode> children = new ArrayList<SimulationNode>();
-    /** A list of constraints that the current node has. */
-    private List<Constraint>     constraints;
     /** A list of node's animations. */
     private List<Animation>      animations;
 
@@ -66,7 +64,7 @@ public class SimulationNode {
     private Transform            spatialStartTransform;
     /** Star transformations for bones. Needed to properly reset the bones. */
     private Map<Bone, Transform> boneStartTransforms;
-
+    
     /**
      * Builds the nodes tree for the given feature. The feature (bone or
      * spatial) is found by its OMA. The feature must be a root bone or a root
@@ -92,6 +90,7 @@ public class SimulationNode {
      *            indicates if the feature is a root bone or root spatial or not
      */
     private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
+        this.featureOMA = featureOMA;
         this.blenderContext = blenderContext;
         Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedDataType.FEATURE);
         if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
@@ -115,24 +114,10 @@ public class SimulationNode {
 
         name = '>' + spatial.getName() + '<';
 
-        constraints = this.findConstraints(featureOMA, blenderContext);
-        if (constraints == null) {
-            constraints = new ArrayList<Constraint>();
-        }
-
         // add children nodes
         if (skeleton != null) {
-            // bone with index 0 is a root bone and should not be considered
-            // here
-            for (int i = 1; i < skeleton.getBoneCount(); ++i) {
-                BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(i));
-                List<Constraint> boneConstraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
-                if (boneConstraints != null) {
-                    constraints.addAll(boneConstraints);
-                }
-            }
             Node node = blenderContext.getControlledNode(skeleton);
-            Long animatedNodeOMA = ((Number)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
+            Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
             animations = blenderContext.getAnimations(animatedNodeOMA);
         } else {
             animations = blenderContext.getAnimations(featureOMA);
@@ -142,38 +127,6 @@ public class SimulationNode {
                 }
             }
         }
-
-        LOGGER.info("Removing invalid constraints.");
-        List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
-        for (Constraint constraint : constraints) {
-            if (constraint.validate()) {
-                validConstraints.add(constraint);
-            } else {
-                LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
-            }
-        }
-        constraints = validConstraints;
-    }
-
-    /**
-     * Tells if the node already contains the given constraint (so that it is
-     * not applied twice).
-     * 
-     * @param constraint
-     *            the constraint to be checked
-     * @return <b>true</b> if the constraint already is stored in the node and
-     *         <b>false</b> otherwise
-     */
-    public boolean contains(Constraint constraint) {
-        boolean result = false;
-        if (constraints != null && constraints.size() > 0) {
-            for (Constraint c : constraints) {
-                if (c.equals(constraint)) {
-                    return true;
-                }
-            }
-        }
-        return result;
     }
 
     /**
@@ -189,6 +142,7 @@ public class SimulationNode {
             for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
                 Transform t = entry.getValue();
                 entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
+                entry.getKey().updateModelTransforms();
             }
             skeleton.reset();
         }
@@ -198,7 +152,9 @@ public class SimulationNode {
      * Simulates the spatial node.
      */
     private void simulateSpatial() {
+        List<Constraint> constraints = blenderContext.getConstraints(featureOMA);
         if (constraints != null && constraints.size() > 0) {
+            LOGGER.fine("Simulating spatial.");
             boolean applyStaticConstraints = true;
             if (animations != null) {
                 for (Animation animation : animations) {
@@ -206,7 +162,7 @@ public class SimulationNode {
                     int maxFrame = (int) animationTimeBoundaries[0];
                     float maxTime = animationTimeBoundaries[1];
 
-                    VirtualTrack vTrack = new VirtualTrack(maxFrame, maxTime);
+                    VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime);
                     for (Track track : animation.getTracks()) {
                         for (int frame = 0; frame < maxFrame; ++frame) {
                             spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
@@ -246,84 +202,86 @@ public class SimulationNode {
      * Simulates the bone node.
      */
     private void simulateSkeleton() {
-        if (constraints != null && constraints.size() > 0) {
-            Set<Long> alteredOmas = new HashSet<Long>();
-
-            if (animations != null) {
-                TempVars vars = TempVars.get();
-                AnimChannel animChannel = animControl.createChannel();
-                for (Animation animation : animations) {
-                    float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
-                    int maxFrame = (int) animationTimeBoundaries[0];
-                    float maxTime = animationTimeBoundaries[1];
-
-                    Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
-                    for (int frame = 0; frame < maxFrame; ++frame) {
-                        // this MUST be done here, otherwise setting next frame of animation will
-                        // lead to possible errors
-                        this.reset();
+        LOGGER.fine("Simulating skeleton.");
+        Set<Long> alteredOmas = new HashSet<Long>();
+
+        if (animations != null) {
+            TempVars vars = TempVars.get();
+            AnimChannel animChannel = animControl.createChannel();
+            
+            // List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
+            for (Animation animation : animations) {
+                float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
+                int maxFrame = (int) animationTimeBoundaries[0];
+                float maxTime = animationTimeBoundaries[1];
+
+                Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
+                for (int frame = 0; frame < maxFrame; ++frame) {
+                    // this MUST be done here, otherwise setting next frame of animation will
+                    // lead to possible errors
+                    this.reset();
+
+                    // first set proper time for all bones in all the tracks ...
+                    for (Track track : animation.getTracks()) {
+                        float time = ((BoneTrack) track).getTimes()[frame];
+                        track.setTime(time, 1, animControl, animChannel, vars);
+                        skeleton.updateWorldVectors();
+                    }
 
-                        // first set proper time for all bones in all the tracks ...
-                        for (Track track : animation.getTracks()) {
-                            float time = ((BoneTrack) track).getTimes()[frame];
-                            track.setTime(time, 1, animControl, animChannel, vars);
-                            skeleton.updateWorldVectors();
+                    // ... and then apply constraints from the root bone to the last child ...
+                    Set<Long> applied = new HashSet<Long>();
+                    for (Bone rootBone : skeleton.getRoots()) {
+                        // ignore the 0-indexed bone
+                        if (skeleton.getBoneIndex(rootBone) > 0) {
+                            this.applyConstraints(rootBone, alteredOmas, applied, frame);
                         }
+                    }
 
-                        // ... and then apply constraints from the root bone to the last child ...
-                        for (Bone rootBone : skeleton.getRoots()) {
-                            if (skeleton.getBoneIndex(rootBone) > 0) {
-                                // ommit the 0 - indexed root bone as it is the bone added by importer
-                                this.applyConstraints(rootBone, alteredOmas, frame);
-                            }
-                        }
-
-                        // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
-                        for (Long boneOMA : alteredOmas) {
-                            BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
-                            int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
-                            if (!tracks.containsKey(boneIndex)) {
-                                tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime));
-                            }
-                        }
-                        alteredOmas.clear();
-
-                        // ... and fill in another frame in the result track
-                        for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
-                            Bone bone = skeleton.getBone(trackEntry.getKey());
-                            Transform startTransform = boneStartTransforms.get(bone);
-                            
-                            // track contains differences between the frame position and bind positions of bones/spatials
-                            Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
-                            Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
-                            Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
-                            
-                            trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
+                    // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
+                    for (Long boneOMA : alteredOmas) {
+                        BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
+                        int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
+                        if (!tracks.containsKey(boneIndex)) {
+                            tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
                         }
                     }
+                    alteredOmas.clear();
 
+                    // ... and fill in another frame in the result track
                     for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
-                        Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
-                        if (newTrack != null) {
-                            boolean trackReplaced = false;
-                            for (Track track : animation.getTracks()) {
-                                if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
-                                    animation.removeTrack(track);
-                                    animation.addTrack(newTrack);
-                                    trackReplaced = true;
-                                    break;
-                                }
-                            }
-                            if (!trackReplaced) {
+                        Bone bone = skeleton.getBone(trackEntry.getKey());
+                        Transform startTransform = boneStartTransforms.get(bone);
+
+                        // track contains differences between the frame position and bind positions of bones/spatials
+                        Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
+                        Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
+                        Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
+
+                        trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
+                    }
+                }
+
+                for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
+                    Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
+                    if (newTrack != null) {
+                        boolean trackReplaced = false;
+                        for (Track track : animation.getTracks()) {
+                            if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
+                                animation.removeTrack(track);
                                 animation.addTrack(newTrack);
+                                trackReplaced = true;
+                                break;
                             }
                         }
+                        if (!trackReplaced) {
+                            animation.addTrack(newTrack);
+                        }
                     }
                 }
-                vars.release();
-                animControl.clearChannels();
-                this.reset();
             }
+            vars.release();
+            animControl.clearChannels();
+            this.reset();
         }
     }
 
@@ -337,20 +295,33 @@ public class SimulationNode {
      * @param frame
      *            the current frame of the animation
      */
-    private void applyConstraints(Bone bone, Set<Long> alteredOmas, int frame) {
+    private void applyConstraints(Bone bone, Set<Long> alteredOmas, Set<Long> applied, int frame) {
         BoneContext boneContext = blenderContext.getBoneContext(bone);
-        List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
-        if (constraints != null && constraints.size() > 0) {
-            for (Constraint constraint : constraints) {
-                constraint.apply(frame);
-                if (constraint.getAlteredOmas() != null) {
-                    alteredOmas.addAll(constraint.getAlteredOmas());
+        if(!applied.contains(boneContext.getBoneOma())) {
+            List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
+            if (constraints != null && constraints.size() > 0) {
+                // TODO: BEWARE OF INFINITE LOOPS !!!!!!!!!!!!!!!!!!!!!!!!!!
+                for (Constraint constraint : constraints) {
+                    if (constraint.getTargetOMA() != null && constraint.getTargetOMA() > 0L) {
+                        // first apply constraints of the target bone
+                        BoneContext targetBone = blenderContext.getBoneContext(constraint.getTargetOMA());
+                        this.applyConstraints(targetBone.getBone(), alteredOmas, applied, frame);
+                    }
+                    constraint.apply(frame);
+                    if (constraint.getAlteredOmas() != null) {
+                        alteredOmas.addAll(constraint.getAlteredOmas());
+                    }
+                    alteredOmas.add(boneContext.getBoneOma());
                 }
-                alteredOmas.add(boneContext.getBoneOma());
             }
+            applied.add(boneContext.getBoneOma());
         }
-        for (Bone child : bone.getChildren()) {
-            this.applyConstraints(child, alteredOmas, frame);
+        
+        List<Bone> children = bone.getChildren();
+        if (children != null && children.size() > 0) {
+            for (Bone child : bone.getChildren()) {
+                this.applyConstraints(child, alteredOmas, applied, frame);
+            }
         }
     }
 
@@ -376,7 +347,7 @@ public class SimulationNode {
      */
     private float[] computeAnimationTimeBoundaries(Animation animation) {
         int maxFrame = Integer.MIN_VALUE;
-        float maxTime = Float.MIN_VALUE;
+        float maxTime = -Float.MAX_VALUE;
         for (Track track : animation.getTracks()) {
             if (track instanceof BoneTrack) {
                 maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length);
@@ -405,11 +376,10 @@ public class SimulationNode {
         List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
         if (constraints != null) {
             for (Constraint constraint : constraints) {
-                if (constraint.isImplemented() && constraint.validate()) {
+                if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
                     result.add(constraint);
-                } else {
-                    LOGGER.log(Level.WARNING, "Constraint named: ''{0}'' of type ''{1}'' is not implemented and will NOT be applied!", new Object[] { constraint.name, constraint.getConstraintTypeName() });
                 }
+                // TODO: add proper warnings to some map or set so that they are not logged on every frame
             }
         }
         return result.size() > 0 ? result : null;

+ 6 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java

@@ -32,4 +32,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public void apply(int frame) {
         LOGGER.warning("Applying constraints to skeleton is not supported.");
     }
+    
+    @Override
+    public Long getTargetOMA() {
+        LOGGER.warning("Constraints for skeleton are not supported.");
+        return null;
+    }
 }

+ 6 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java

@@ -22,6 +22,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
         if (targetOMA != null) {
             return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null;
         }
-        return true;
+        return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
+    }
+    
+    @Override
+    public Long getTargetOMA() {
+        return targetOMA;
     }
 }

+ 21 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java

@@ -16,6 +16,8 @@ import com.jme3.math.Vector3f;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class VirtualTrack {
+    /** The name of the track (for debugging purposes). */
+    private String               name;
     /** The last frame for the track. */
     public int                   maxFrame;
     /** The max time for the track. */
@@ -35,7 +37,8 @@ import com.jme3.math.Vector3f;
      * @param maxTime
      *            the max time for the track
      */
-    public VirtualTrack(int maxFrame, float maxTime) {
+    public VirtualTrack(String name, int maxFrame, float maxTime) {
+        this.name = name;
         this.maxFrame = maxFrame;
         this.maxTime = maxTime;
     }
@@ -101,7 +104,7 @@ import com.jme3.math.Vector3f;
      */
     private float[] createTimes() {
         float[] times = new float[maxFrame];
-        float dT = maxTime / (float) maxFrame;
+        float dT = maxTime / maxFrame;
         float t = 0;
         for (int i = 0; i < maxFrame; ++i) {
             times[i] = t;
@@ -143,4 +146,20 @@ import com.jme3.math.Vector3f;
             list.add(element);
         }
     }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder(2048);
+        result.append("TRACK: ").append(name).append('\n');
+        if (translations != null && translations.size() > 0) {
+            result.append("TRANSLATIONS: ").append(translations.toString()).append('\n');
+        }
+        if (rotations != null && rotations.size() > 0) {
+            result.append("ROTATIONS:    ").append(rotations.toString()).append('\n');
+        }
+        if (scales != null && scales.size() > 0) {
+            result.append("SCALES:       ").append(scales.toString()).append('\n');
+        }
+        return result.toString();
+    }
 }

+ 21 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java

@@ -28,6 +28,8 @@ public abstract class ConstraintDefinition {
     protected Long             ownerOMA;
     /** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
     protected Set<Long>        alteredOmas;
+    /** The variable that determines if the constraint will alter the track in any way. */
+    protected boolean          trackToBeChanged = true;
 
     /**
      * Loads a constraint definition based on the constraint definition
@@ -52,6 +54,20 @@ public abstract class ConstraintDefinition {
         this.ownerOMA = ownerOMA;
     }
 
+    /**
+     * @return determines if the definition of the constraint will change the bone in any way; in most cases
+     *         it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
+     *         computing to improve the computation speed and lower the computations complexity
+     */
+    public boolean isTrackToBeChanged() {
+        return trackToBeChanged;
+    }
+
+    /**
+     * @return determines if this constraint definition requires a defined target or not
+     */
+    public abstract boolean isTargetRequired();
+
     /**
      * This method is here because we have no guarantee that the owner is loaded
      * when constraint is being created. So use it to get the owner when it is
@@ -132,4 +148,9 @@ public abstract class ConstraintDefinition {
      *            the influence of the constraint (from range <0; 1>)
      */
     public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
+
+    @Override
+    public String toString() {
+        return this.getConstraintTypeName();
+    }
 }

+ 10 - 6
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java

@@ -26,18 +26,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
         mode = ((Number) constraintData.getFieldValue("mode")).intValue();
         dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
     }
-    
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
-            blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
+        if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
             // distance limit does not work on bones who are connected to their parent
             return;
         }
-        if(influence == 0 || targetTransform == null) {
-            return ;// no need to do anything
+        if (influence == 0 || targetTransform == null) {
+            return;// no need to do anything
         }
-        
+
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
 
         Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
@@ -73,6 +72,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
+
     @Override
     public String getConstraintTypeName() {
         return "Limit distance";

+ 50 - 31
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java

@@ -6,13 +6,15 @@ import java.util.HashSet;
 import java.util.List;
 
 import com.jme3.animation.Bone;
-import com.jme3.math.Quaternion;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.math.DQuaternion;
+import com.jme3.scene.plugins.blender.math.DTransform;
+import com.jme3.scene.plugins.blender.math.Vector3d;
 
 /**
  * The Inverse Kinematics constraint.
@@ -28,9 +30,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
     /** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
     private int                bonesAffected;
     /** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */
-    private float              chainLength;
-    /** Tells if there is anything to compute at all. */
-    private boolean            needToCompute = true;
+    private double              chainLength;
     /** Indicates if the tail of the bone should be used or not. */
     private boolean            useTail;
     /** The amount of iterations of the algorithm. */
@@ -43,26 +43,26 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
         useTail = (flag & FLAG_USE_TAIL) != 0;
 
         if ((flag & FLAG_POSITION) == 0) {
-            needToCompute = false;
+            trackToBeChanged = false;
         }
 
-        if (needToCompute) {
+        if (trackToBeChanged) {
             alteredOmas = new HashSet<Long>();
         }
     }
 
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if (influence == 0 || !needToCompute || targetTransform == null) {
+        if (influence == 0 || !trackToBeChanged || targetTransform == null) {
             return;// no need to do anything
         }
-        Quaternion q = new Quaternion();
-        Vector3f t = targetTransform.getTranslation();
+        DQuaternion q = new DQuaternion();
+        Vector3d t = new Vector3d(targetTransform.getTranslation());
         List<BoneContext> bones = this.loadBones();
-        if(bones.size() == 0) {
+        if (bones.size() == 0) {
             return;// no need to do anything
         }
-        float distanceFromTarget = Float.MAX_VALUE;
+        double distanceFromTarget = Double.MAX_VALUE;
 
         int iterations = this.iterations;
         if (bones.size() == 1) {
@@ -72,21 +72,21 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
             // in this case only one iteration will be needed, computed from the root to top bone
             BoneContext rootBone = bones.get(bones.size() - 1);
             Transform rootBoneTransform = constraintHelper.getTransform(rootBone.getArmatureObjectOMA(), rootBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
-            if (t.distance(rootBoneTransform.getTranslation()) >= chainLength) {
+            if (t.distance(new Vector3d(rootBoneTransform.getTranslation())) >= chainLength) {
                 Collections.reverse(bones);
 
                 for (BoneContext boneContext : bones) {
                     Bone bone = boneContext.getBone();
-                    Transform boneTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
+                    DTransform boneTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD));
 
-                    Vector3f e = boneTransform.getTranslation().add(boneTransform.getRotation().mult(Vector3f.UNIT_Y).multLocal(boneContext.getLength()));// effector
-                    Vector3f j = boneTransform.getTranslation(); // current join position
+                    Vector3d e = boneTransform.getTranslation().add(boneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(boneContext.getLength()));// effector
+                    Vector3d j = boneTransform.getTranslation(); // current join position
 
-                    Vector3f currentDir = e.subtractLocal(j).normalizeLocal();
-                    Vector3f target = t.subtract(j).normalizeLocal();
-                    float angle = currentDir.angleBetween(target);
+                    Vector3d currentDir = e.subtractLocal(j).normalizeLocal();
+                    Vector3d target = t.subtract(j).normalizeLocal();
+                    double angle = currentDir.angleBetween(target);
                     if (angle != 0) {
-                        Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
+                        Vector3d cross = currentDir.crossLocal(target).normalizeLocal();
                         q.fromAngleAxis(angle, cross);
                         if (boneContext.isLockX()) {
                             q.set(0, q.getY(), q.getZ(), q.getW());
@@ -99,7 +99,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
                         }
 
                         boneTransform.getRotation().set(q.multLocal(boneTransform.getRotation()));
-                        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform);
+                        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform.toTransform());
                     }
                 }
 
@@ -111,17 +111,17 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
         for (int i = 0; i < iterations && distanceFromTarget > MIN_DISTANCE; ++i) {
             for (BoneContext boneContext : bones) {
                 Bone bone = boneContext.getBone();
-                Transform topBoneTransform = constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
-                Transform boneWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
+                DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD));
+                DTransform boneWorldTransform = new DTransform(constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD));
 
-                Vector3f e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3f.UNIT_Y).multLocal(topBone.getLength()));// effector
-                Vector3f j = boneWorldTransform.getTranslation(); // current join position
+                Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
+                Vector3d j = boneWorldTransform.getTranslation(); // current join position
 
-                Vector3f currentDir = e.subtractLocal(j).normalizeLocal();
-                Vector3f target = t.subtract(j).normalizeLocal();
-                float angle = currentDir.angleBetween(target);
+                Vector3d currentDir = e.subtractLocal(j).normalizeLocal();
+                Vector3d target = t.subtract(j).normalizeLocal();
+                double angle = currentDir.angleBetween(target);
                 if (angle != 0) {
-                    Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
+                    Vector3d cross = currentDir.crossLocal(target).normalizeLocal();
                     q.fromAngleAxis(angle, cross);
 
                     if (boneContext.isLockX()) {
@@ -135,12 +135,15 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
                     }
 
                     boneWorldTransform.getRotation().set(q.multLocal(boneWorldTransform.getRotation()));
-                    constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
+                    constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform.toTransform());
+                } else {
+                    iterations = 0;
+                    break;
                 }
             }
 
-            Transform topBoneTransform = constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
-            Vector3f e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3f.UNIT_Y).multLocal(topBone.getLength()));// effector
+            DTransform topBoneTransform = new DTransform(constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD));
+            Vector3d e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3d.UNIT_Y).multLocal(topBone.getLength()));// effector
             distanceFromTarget = e.distance(t);
         }
     }
@@ -186,4 +189,20 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
         }
         return bones;
     }
+
+    @Override
+    public boolean isTrackToBeChanged() {
+        if (trackToBeChanged) {
+            // need to check the bone structure too (when constructor was called not all of the bones might have been loaded yet)
+            // that is why it is also checked here
+            List<BoneContext> bones = this.loadBones();
+            trackToBeChanged = bones.size() > 0;
+        }
+        return trackToBeChanged;
+    }
+
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
 }

+ 19 - 11
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java

@@ -34,27 +34,30 @@ import com.jme3.scene.plugins.blender.file.Structure;
             int invZ = flag & LOCLIKE_Z_INVERT;
             // clear the other flags to swap them
             flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;
-            
+
             flag |= y << 1;
             flag |= invY << 1;
             flag |= z >> 1;
             flag |= invZ >> 1;
+
+            trackToBeChanged = (flag & LOCLIKE_X) != 0 || (flag & LOCLIKE_Y) != 0 || (flag & LOCLIKE_Z) != 0;
         }
     }
-    
+
+    @Override
+    public boolean isTrackToBeChanged() {
+        // location copy does not work on bones who are connected to their parent
+        return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
+    }
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
-            blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
-            // location copy does not work on bones who are connected to their parent
+        if (influence == 0 || targetTransform == null || !this.isTrackToBeChanged()) {
             return;
         }
-        if(influence == 0 || targetTransform == null) {
-            return ;// no need to do anything
-        }
-        
+
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         Vector3f ownerLocation = ownerTransform.getTranslation();
         Vector3f targetLocation = targetTransform.getTranslation();
 
@@ -88,7 +91,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
             startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
             ownerLocation.addLocal(startLocation);
         }
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -96,4 +99,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Copy location";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
 }

+ 19 - 8
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java

@@ -52,18 +52,24 @@ import com.jme3.scene.plugins.blender.file.Structure;
             limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
             limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
         }
+
+        trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0;
+    }
+
+    @Override
+    public boolean isTrackToBeChanged() {
+        // location limit does not work on bones who are connected to their parent
+        return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
     }
-    
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
-            blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
-            // location limit does not work on bones who are connected to their parent
-            return;
+        if (influence == 0 || !this.isTrackToBeChanged()) {
+            return;// no need to do anything
         }
-        
+
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         Vector3f translation = ownerTransform.getTranslation();
 
         if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
@@ -84,7 +90,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
         if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
             translation.z -= (translation.z - limits[2][1]) * influence;
         }
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -92,4 +98,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Limit location";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 12 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java

@@ -6,6 +6,11 @@ import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
+/**
+ * This class represents 'Maintain volume' constraint type in blender.
+ * 
+ * @author Marcin Roguski (Kaelthas)
+ */
 public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
     private static final int FLAG_MASK_X = 0;
     private static final int FLAG_MASK_Y = 1;
@@ -16,11 +21,12 @@ public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
     public ConstraintDefinitionMaintainVolume(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
         super(constraintData, ownerOMA, blenderContext);
         volume = (float) Math.sqrt(((Number) constraintData.getFieldValue("volume")).floatValue());
+        trackToBeChanged = volume != 1 && (flag & (FLAG_MASK_X | FLAG_MASK_Y | FLAG_MASK_Z)) != 0;
     }
 
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if (volume != 1 && influence > 0) {
+        if (trackToBeChanged && influence > 0) {
             // the maintain volume constraint is applied directly to object's scale, so no need to do it again
             // but in case of bones we need to make computations
             if (this.getOwner() instanceof Bone) {
@@ -47,4 +53,9 @@ public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
     public String getConstraintTypeName() {
         return "Maintain volume";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 6 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java

@@ -14,6 +14,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
 
     public ConstraintDefinitionNull(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
         super(constraintData, ownerOMA, blenderContext);
+        trackToBeChanged = false;
     }
 
     @Override
@@ -25,4 +26,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Null";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 10 - 4
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java

@@ -25,15 +25,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
 
     public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
         super(constraintData, ownerOMA, blenderContext);
+        trackToBeChanged = (flag & (ROTLIKE_X | ROTLIKE_Y | ROTLIKE_Z)) != 0;
     }
 
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if(influence == 0 || targetTransform == null) {
-            return ;// no need to do anything
+        if (influence == 0 || targetTransform == null || !trackToBeChanged) {
+            return;// no need to do anything
         }
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         Quaternion ownerRotation = ownerTransform.getRotation();
         ownerAngles = ownerRotation.toAngles(ownerAngles);
         targetAngles = targetTransform.getRotation().toAngles(targetAngles);
@@ -70,7 +71,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
             // ownerLocation.addLocal(startLocation);
             // TODO
         }
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -78,4 +79,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Copy rotation";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
 }

+ 13 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java

@@ -65,12 +65,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
          * if(limits[i][0] > limits[i][1]) { float temp = limits[i][0];
          * limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
          */
+
+        trackToBeChanged = (flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT)) != 0;
     }
-    
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        if (influence == 0 || !trackToBeChanged) {
+            return;
+        }
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         ownerTransform.getRotation().toAngles(angles);
         // make sure that the rotations are always in range [0, 2PI)
         // TODO: same comment as in constructor
@@ -108,7 +113,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
             angles[2] -= difference;
         }
         ownerTransform.getRotation().fromAngles(angles);
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -116,4 +121,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Limit rotation";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 11 - 4
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java

@@ -27,16 +27,18 @@ import com.jme3.scene.plugins.blender.file.Structure;
                                                 // them
             flag |= y << 1;
             flag |= z >> 1;
+
+            trackToBeChanged = (flag & (SIZELIKE_X | SIZELIKE_Y | SIZELIKE_Z)) != 0;
         }
     }
-    
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if(influence == 0 || targetTransform == null) {
+        if (influence == 0 || targetTransform == null || !trackToBeChanged) {
             return;// no need to do anything
         }
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         Vector3f ownerScale = ownerTransform.getScale();
         Vector3f targetScale = targetTransform.getScale();
 
@@ -56,7 +58,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
             ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
         }
         ownerScale.addLocal(offset);
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -64,4 +66,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Copy scale";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
 }

+ 13 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java

@@ -50,12 +50,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
             limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
             limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
         }
+
+        trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0;
     }
-    
+
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        if (influence == 0 || !trackToBeChanged) {
+            return;
+        }
         Transform ownerTransform = this.getOwnerTransform(ownerSpace);
-        
+
         Vector3f scale = ownerTransform.getScale();
         if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
             scale.x -= (scale.x - limits[0][0]) * influence;
@@ -75,7 +80,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
         if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
             scale.z -= (scale.z - limits[2][1]) * influence;
         }
-        
+
         this.applyOwnerTransform(ownerTransform, ownerSpace);
     }
 
@@ -83,4 +88,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public String getConstraintTypeName() {
         return "Limit scale";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 7 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java

@@ -35,8 +35,8 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
 
     @Override
     public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
-        if(influence == 0 || targetTransform == null) {
-            return ;// no need to do anything
+        if (influence == 0 || targetTransform == null) {
+            return;// no need to do anything
         }
         Object target = this.getTarget();// Bone or Node
         Object owner = this.getOwner();// Bone or Node
@@ -73,4 +73,9 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
     public String getConstraintTypeName() {
         return "Copy transforms";
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return true;
+    }
 }

+ 6 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java

@@ -16,6 +16,7 @@ import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
     public UnsupportedConstraintDefinition(String typeName) {
         super(null, null, null);
         this.typeName = typeName;
+        trackToBeChanged = false;
     }
 
     @Override
@@ -31,4 +32,9 @@ import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
     public String getConstraintTypeName() {
         return typeName;
     }
+
+    @Override
+    public boolean isTargetRequired() {
+        return false;
+    }
 }

+ 453 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DQuaternion.java

@@ -0,0 +1,453 @@
+/*
+ * 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.plugins.blender.math;
+
+import java.io.IOException;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.Quaternion;
+
+/**
+ * <code>DQuaternion</code> defines a single example of a more general class of
+ * hypercomplex numbers. DQuaternions extends a rotation in three dimensions to a
+ * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth
+ * continuous rotation.
+ * 
+ * <code>DQuaternion</code> is defined by four double point numbers: {x y z w}.
+ * 
+ * This class's only purpose is to give better accuracy in floating point operations during computations.
+ * This is made by copying the original Quaternion class from jme3 core and leaving only required methods and basic computation methods, so that
+ * the class is smaller and easier to maintain.
+ * Should any other methods be needed, they will be added.
+ * 
+ * @author Mark Powell
+ * @author Joshua Slack
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class DQuaternion implements Savable, Cloneable, java.io.Serializable {
+    private static final long       serialVersionUID = 5009180713885017539L;
+
+    /**
+     * Represents the identity quaternion rotation (0, 0, 0, 1).
+     */
+    public static final DQuaternion IDENTITY         = new DQuaternion();
+    public static final DQuaternion DIRECTION_Z      = new DQuaternion();
+    public static final DQuaternion ZERO             = new DQuaternion(0, 0, 0, 0);
+    protected double                x, y, z, w = 1;
+
+    /**
+     * Constructor instantiates a new <code>DQuaternion</code> object
+     * initializing all values to zero, except w which is initialized to 1.
+     *
+     */
+    public DQuaternion() {
+    }
+
+    /**
+     * Constructor instantiates a new <code>DQuaternion</code> object from the
+     * given list of parameters.
+     *
+     * @param x
+     *            the x value of the quaternion.
+     * @param y
+     *            the y value of the quaternion.
+     * @param z
+     *            the z value of the quaternion.
+     * @param w
+     *            the w value of the quaternion.
+     */
+    public DQuaternion(double x, double y, double z, double w) {
+        this.set(x, y, z, w);
+    }
+
+    public DQuaternion(Quaternion q) {
+        this(q.getX(), q.getY(), q.getZ(), q.getW());
+    }
+
+    public Quaternion toQuaternion() {
+        return new Quaternion((float) x, (float) y, (float) z, (float) w);
+    }
+
+    public double getX() {
+        return x;
+    }
+
+    public double getY() {
+        return y;
+    }
+
+    public double getZ() {
+        return z;
+    }
+
+    public double getW() {
+        return w;
+    }
+
+    /**
+     * sets the data in a <code>DQuaternion</code> object from the given list
+     * of parameters.
+     *
+     * @param x
+     *            the x value of the quaternion.
+     * @param y
+     *            the y value of the quaternion.
+     * @param z
+     *            the z value of the quaternion.
+     * @param w
+     *            the w value of the quaternion.
+     * @return this
+     */
+    public DQuaternion set(double x, double y, double z, double w) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.w = w;
+        return this;
+    }
+
+    /**
+     * Sets the data in this <code>DQuaternion</code> object to be equal to the
+     * passed <code>DQuaternion</code> object. The values are copied producing
+     * a new object.
+     *
+     * @param q
+     *            The DQuaternion to copy values from.
+     * @return this
+     */
+    public DQuaternion set(DQuaternion q) {
+        x = q.x;
+        y = q.y;
+        z = q.z;
+        w = q.w;
+        return this;
+    }
+
+    /**
+     * Sets this DQuaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1).
+     */
+    public void loadIdentity() {
+        x = y = z = 0;
+        w = 1;
+    }
+
+    /**
+     * <code>fromAngleAxis</code> sets this quaternion to the values specified
+     * by an angle and an axis of rotation. This method creates an object, so
+     * use fromAngleNormalAxis if your axis is already normalized.
+     *
+     * @param angle
+     *            the angle to rotate (in radians).
+     * @param axis
+     *            the axis of rotation.
+     * @return this quaternion
+     */
+    public DQuaternion fromAngleAxis(double angle, Vector3d axis) {
+        Vector3d normAxis = axis.normalize();
+        this.fromAngleNormalAxis(angle, normAxis);
+        return this;
+    }
+
+    /**
+     * <code>fromAngleNormalAxis</code> sets this quaternion to the values
+     * specified by an angle and a normalized axis of rotation.
+     *
+     * @param angle
+     *            the angle to rotate (in radians).
+     * @param axis
+     *            the axis of rotation (already normalized).
+     */
+    public DQuaternion fromAngleNormalAxis(double angle, Vector3d axis) {
+        if (axis.x == 0 && axis.y == 0 && axis.z == 0) {
+            this.loadIdentity();
+        } else {
+            double halfAngle = 0.5f * angle;
+            double sin = Math.sin(halfAngle);
+            w = Math.cos(halfAngle);
+            x = sin * axis.x;
+            y = sin * axis.y;
+            z = sin * axis.z;
+        }
+        return this;
+    }
+
+    /**
+     * <code>add</code> adds the values of this quaternion to those of the
+     * parameter quaternion. The result is returned as a new quaternion.
+     *
+     * @param q
+     *            the quaternion to add to this.
+     * @return the new quaternion.
+     */
+    public DQuaternion add(DQuaternion q) {
+        return new DQuaternion(x + q.x, y + q.y, z + q.z, w + q.w);
+    }
+
+    /**
+     * <code>add</code> adds the values of this quaternion to those of the
+     * parameter quaternion. The result is stored in this DQuaternion.
+     *
+     * @param q
+     *            the quaternion to add to this.
+     * @return This DQuaternion after addition.
+     */
+    public DQuaternion addLocal(DQuaternion q) {
+        x += q.x;
+        y += q.y;
+        z += q.z;
+        w += q.w;
+        return this;
+    }
+
+    /**
+     * <code>subtract</code> subtracts the values of the parameter quaternion
+     * from those of this quaternion. The result is returned as a new
+     * quaternion.
+     *
+     * @param q
+     *            the quaternion to subtract from this.
+     * @return the new quaternion.
+     */
+    public DQuaternion subtract(DQuaternion q) {
+        return new DQuaternion(x - q.x, y - q.y, z - q.z, w - q.w);
+    }
+
+    /**
+     * <code>subtract</code> subtracts the values of the parameter quaternion
+     * from those of this quaternion. The result is stored in this DQuaternion.
+     *
+     * @param q
+     *            the quaternion to subtract from this.
+     * @return This DQuaternion after subtraction.
+     */
+    public DQuaternion subtractLocal(DQuaternion q) {
+        x -= q.x;
+        y -= q.y;
+        z -= q.z;
+        w -= q.w;
+        return this;
+    }
+
+    /**
+     * <code>mult</code> multiplies this quaternion by a parameter quaternion.
+     * The result is returned as a new quaternion. It should be noted that
+     * quaternion multiplication is not commutative so q * p != p * q.
+     *
+     * @param q
+     *            the quaternion to multiply this quaternion by.
+     * @return the new quaternion.
+     */
+    public DQuaternion mult(DQuaternion q) {
+        return this.mult(q, null);
+    }
+
+    /**
+     * <code>mult</code> multiplies this quaternion by a parameter quaternion.
+     * The result is returned as a new quaternion. It should be noted that
+     * quaternion multiplication is not commutative so q * p != p * q.
+     *
+     * It IS safe for q and res to be the same object.
+     * It IS NOT safe for this and res to be the same object.
+     *
+     * @param q
+     *            the quaternion to multiply this quaternion by.
+     * @param res
+     *            the quaternion to store the result in.
+     * @return the new quaternion.
+     */
+    public DQuaternion mult(DQuaternion q, DQuaternion res) {
+        if (res == null) {
+            res = new DQuaternion();
+        }
+        double qw = q.w, qx = q.x, qy = q.y, qz = q.z;
+        res.x = x * qw + y * qz - z * qy + w * qx;
+        res.y = -x * qz + y * qw + z * qx + w * qy;
+        res.z = x * qy - y * qx + z * qw + w * qz;
+        res.w = -x * qx - y * qy - z * qz + w * qw;
+        return res;
+    }
+
+    /**
+     * <code>mult</code> multiplies this quaternion by a parameter vector. The
+     * result is returned as a new vector.
+     *
+     * @param v
+     *            the vector to multiply this quaternion by.
+     * @return the new vector.
+     */
+    public Vector3d mult(Vector3d v) {
+        return this.mult(v, null);
+    }
+
+    /**
+     * Multiplies this DQuaternion by the supplied quaternion. The result is
+     * stored in this DQuaternion, which is also returned for chaining. Similar
+     * to this *= q.
+     *
+     * @param q
+     *            The DQuaternion to multiply this one by.
+     * @return This DQuaternion, after multiplication.
+     */
+    public DQuaternion multLocal(DQuaternion q) {
+        double x1 = x * q.w + y * q.z - z * q.y + w * q.x;
+        double y1 = -x * q.z + y * q.w + z * q.x + w * q.y;
+        double z1 = x * q.y - y * q.x + z * q.w + w * q.z;
+        w = -x * q.x - y * q.y - z * q.z + w * q.w;
+        x = x1;
+        y = y1;
+        z = z1;
+        return this;
+    }
+
+    /**
+     * <code>mult</code> multiplies this quaternion by a parameter vector. The
+     * result is returned as a new vector.
+     * 
+     * @param v
+     *            the vector to multiply this quaternion by.
+     * @param store
+     *            the vector to store the result in. It IS safe for v and store
+     *            to be the same object.
+     * @return the result vector.
+     */
+    public Vector3d mult(Vector3d v, Vector3d store) {
+        if (store == null) {
+            store = new Vector3d();
+        }
+        if (v.x == 0 && v.y == 0 && v.z == 0) {
+            store.set(0, 0, 0);
+        } else {
+            double vx = v.x, vy = v.y, vz = v.z;
+            store.x = w * w * vx + 2 * y * w * vz - 2 * z * w * vy + x * x * vx + 2 * y * x * vy + 2 * z * x * vz - z * z * vx - y * y * vx;
+            store.y = 2 * x * y * vx + y * y * vy + 2 * z * y * vz + 2 * w * z * vx - z * z * vy + w * w * vy - 2 * x * w * vz - x * x * vy;
+            store.z = 2 * x * z * vx + 2 * y * z * vy + z * z * vz - 2 * w * y * vx - y * y * vz + 2 * w * x * vy - x * x * vz + w * w * vz;
+        }
+        return store;
+    }
+
+    /**
+     *
+     * <code>toString</code> creates the string representation of this <code>DQuaternion</code>. The values of the quaternion are displaced (x,
+     * y, z, w), in the following manner: <br>
+     * (x, y, z, w)
+     *
+     * @return the string representation of this object.
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "(" + x + ", " + y + ", " + z + ", " + w + ")";
+    }
+
+    /**
+     * <code>equals</code> determines if two quaternions are logically equal,
+     * that is, if the values of (x, y, z, w) are the same for both quaternions.
+     *
+     * @param o
+     *            the object to compare for equality
+     * @return true if they are equal, false otherwise.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof DQuaternion)) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        DQuaternion comp = (DQuaternion) o;
+        if (Double.compare(x, comp.x) != 0) {
+            return false;
+        }
+        if (Double.compare(y, comp.y) != 0) {
+            return false;
+        }
+        if (Double.compare(z, comp.z) != 0) {
+            return false;
+        }
+        if (Double.compare(w, comp.w) != 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 
+     * <code>hashCode</code> returns the hash code value as an integer and is
+     * supported for the benefit of hashing based collection classes such as
+     * Hashtable, HashMap, HashSet etc.
+     * 
+     * @return the hashcode for this instance of DQuaternion.
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        long hash = 37;
+        hash = 37 * hash + Double.doubleToLongBits(x);
+        hash = 37 * hash + Double.doubleToLongBits(y);
+        hash = 37 * hash + Double.doubleToLongBits(z);
+        hash = 37 * hash + Double.doubleToLongBits(w);
+        return (int) hash;
+
+    }
+
+    public void write(JmeExporter e) throws IOException {
+        OutputCapsule cap = e.getCapsule(this);
+        cap.write(x, "x", 0);
+        cap.write(y, "y", 0);
+        cap.write(z, "z", 0);
+        cap.write(w, "w", 1);
+    }
+
+    public void read(JmeImporter e) throws IOException {
+        InputCapsule cap = e.getCapsule(this);
+        x = cap.readFloat("x", 0);
+        y = cap.readFloat("y", 0);
+        z = cap.readFloat("z", 0);
+        w = cap.readFloat("w", 1);
+    }
+
+    @Override
+    public DQuaternion clone() {
+        try {
+            return (DQuaternion) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(); // can not happen
+        }
+    }
+}

+ 170 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/DTransform.java

@@ -0,0 +1,170 @@
+/*
+ * 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.plugins.blender.math;
+
+import com.jme3.export.*;
+import com.jme3.math.Transform;
+
+import java.io.IOException;
+
+/**
+ * Started Date: Jul 16, 2004<br>
+ * <br>
+ * Represents a translation, rotation and scale in one object.
+ * 
+ * This class's only purpose is to give better accuracy in floating point operations during computations.
+ * This is made by copying the original Transfrom class from jme3 core and removing unnecessary methods so that
+ * the class is smaller and easier to maintain.
+ * Should any other methods be needed, they will be added.
+ * 
+ * @author Jack Lindamood
+ * @author Joshua Slack
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class DTransform implements Savable, Cloneable, java.io.Serializable {
+    private static final long serialVersionUID = 7812915425940606722L;
+
+    private DQuaternion       rotation;
+    private Vector3d          translation;
+    private Vector3d          scale;
+
+    public DTransform(Transform transform) {
+        translation = new Vector3d(transform.getTranslation());
+        rotation = new DQuaternion(transform.getRotation());
+        scale = new Vector3d(transform.getScale());
+    }
+
+    public Transform toTransform() {
+        return new Transform(translation.toVector3f(), rotation.toQuaternion(), scale.toVector3f());
+    }
+
+    /**
+     * Sets this translation to the given value.
+     * @param trans
+     *            The new translation for this matrix.
+     * @return this
+     */
+    public DTransform setTranslation(Vector3d trans) {
+        translation.set(trans);
+        return this;
+    }
+
+    /**
+     * Sets this rotation to the given DQuaternion value.
+     * @param rot
+     *            The new rotation for this matrix.
+     * @return this
+     */
+    public DTransform setRotation(DQuaternion rot) {
+        rotation.set(rot);
+        return this;
+    }
+
+    /**
+     * Sets this scale to the given value.
+     * @param scale
+     *            The new scale for this matrix.
+     * @return this
+     */
+    public DTransform setScale(Vector3d scale) {
+        this.scale.set(scale);
+        return this;
+    }
+
+    /**
+     * Sets this scale to the given value.
+     * @param scale
+     *            The new scale for this matrix.
+     * @return this
+     */
+    public DTransform setScale(float scale) {
+        this.scale.set(scale, scale, scale);
+        return this;
+    }
+
+    /**
+     * Return the translation vector in this matrix.
+     * @return translation vector.
+     */
+    public Vector3d getTranslation() {
+        return translation;
+    }
+
+    /**
+     * Return the rotation quaternion in this matrix.
+     * @return rotation quaternion.
+     */
+    public DQuaternion getRotation() {
+        return rotation;
+    }
+
+    /**
+     * Return the scale vector in this matrix.
+     * @return scale vector.
+     */
+    public Vector3d getScale() {
+        return scale;
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + "[ " + rotation.x + ", " + rotation.y + ", " + rotation.z + ", " + rotation.w + "]\n" + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";
+    }
+
+    public void write(JmeExporter e) throws IOException {
+        OutputCapsule capsule = e.getCapsule(this);
+        capsule.write(rotation, "rot", new DQuaternion());
+        capsule.write(translation, "translation", Vector3d.ZERO);
+        capsule.write(scale, "scale", Vector3d.UNIT_XYZ);
+    }
+
+    public void read(JmeImporter e) throws IOException {
+        InputCapsule capsule = e.getCapsule(this);
+
+        rotation = (DQuaternion) capsule.readSavable("rot", new DQuaternion());
+        translation = (Vector3d) capsule.readSavable("translation", Vector3d.ZERO);
+        scale = (Vector3d) capsule.readSavable("scale", Vector3d.UNIT_XYZ);
+    }
+
+    @Override
+    public DTransform clone() {
+        try {
+            DTransform tq = (DTransform) super.clone();
+            tq.rotation = rotation.clone();
+            tq.scale = scale.clone();
+            tq.translation = translation.clone();
+            return tq;
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+    }
+}

+ 867 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java

@@ -0,0 +1,867 @@
+/*
+ * 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.plugins.blender.math;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.logging.Logger;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+
+/*
+ * -- Added *Local methods to cut down on object creation - JS
+ */
+
+/**
+ * <code>Vector3d</code> defines a Vector for a three float value tuple. <code>Vector3d</code> can represent any three dimensional value, such as a
+ * vertex, a normal, etc. Utility methods are also included to aid in
+ * mathematical calculations.
+ *
+ * This class's only purpose is to give better accuracy in floating point operations during computations.
+ * This is made by copying the original Vector3f class from jme3 core and leaving only required methods and basic computation methods, so that
+ * the class is smaller and easier to maintain.
+ * Should any other methods be needed, they will be added.
+ *
+ * @author Mark Powell
+ * @author Joshua Slack
+ * @author Marcin Roguski (Kaelthas)
+ */
+public final class Vector3d implements Savable, Cloneable, Serializable {
+    private static final long    serialVersionUID = 3090477054277293078L;
+
+    private static final Logger  LOGGER           = Logger.getLogger(Vector3d.class.getName());
+
+    public final static Vector3d ZERO             = new Vector3d();
+    public final static Vector3d UNIT_XYZ         = new Vector3d(1, 1, 1);
+    public final static Vector3d UNIT_X           = new Vector3d(1, 0, 0);
+    public final static Vector3d UNIT_Y           = new Vector3d(0, 1, 0);
+    public final static Vector3d UNIT_Z           = new Vector3d(0, 0, 1);
+
+    /**
+     * the x value of the vector.
+     */
+    public double                x;
+
+    /**
+     * the y value of the vector.
+     */
+    public double                y;
+
+    /**
+     * the z value of the vector.
+     */
+    public double                z;
+
+    /**
+     * Constructor instantiates a new <code>Vector3d</code> with default
+     * values of (0,0,0).
+     *
+     */
+    public Vector3d() {
+    }
+
+    /**
+     * Constructor instantiates a new <code>Vector3d</code> with provides
+     * values.
+     *
+     * @param x
+     *            the x value of the vector.
+     * @param y
+     *            the y value of the vector.
+     * @param z
+     *            the z value of the vector.
+     */
+    public Vector3d(double x, double y, double z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    /**
+     * Constructor instantiates a new <code>Vector3d</code> that is a copy
+     * of the provided vector
+     * @param copy
+     *            The Vector3d to copy
+     */
+    public Vector3d(Vector3f vector3f) {
+        this(vector3f.x, vector3f.y, vector3f.z);
+    }
+
+    public Vector3f toVector3f() {
+        return new Vector3f((float) x, (float) y, (float) z);
+    }
+
+    /**
+     * <code>set</code> sets the x,y,z values of the vector based on passed
+     * parameters.
+     *
+     * @param x
+     *            the x value of the vector.
+     * @param y
+     *            the y value of the vector.
+     * @param z
+     *            the z value of the vector.
+     * @return this vector
+     */
+    public Vector3d set(double x, double y, double z) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        return this;
+    }
+
+    /**
+     * <code>set</code> sets the x,y,z values of the vector by copying the
+     * supplied vector.
+     *
+     * @param vect
+     *            the vector to copy.
+     * @return this vector
+     */
+    public Vector3d set(Vector3d vect) {
+        return this.set(vect.x, vect.y, vect.z);
+    }
+
+    /**
+     *
+     * <code>add</code> adds a provided vector to this vector creating a
+     * resultant vector which is returned. If the provided vector is null, null
+     * is returned.
+     *
+     * @param vec
+     *            the vector to add to this.
+     * @return the resultant vector.
+     */
+    public Vector3d add(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        return new Vector3d(x + vec.x, y + vec.y, z + vec.z);
+    }
+
+    /**
+     *
+     * <code>add</code> adds the values of a provided vector storing the
+     * values in the supplied vector.
+     *
+     * @param vec
+     *            the vector to add to this
+     * @param result
+     *            the vector to store the result in
+     * @return result returns the supplied result vector.
+     */
+    public Vector3d add(Vector3d vec, Vector3d result) {
+        result.x = x + vec.x;
+        result.y = y + vec.y;
+        result.z = z + vec.z;
+        return result;
+    }
+
+    /**
+     * <code>addLocal</code> adds a provided vector to this vector internally,
+     * and returns a handle to this vector for easy chaining of calls. If the
+     * provided vector is null, null is returned.
+     *
+     * @param vec
+     *            the vector to add to this vector.
+     * @return this
+     */
+    public Vector3d addLocal(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        x += vec.x;
+        y += vec.y;
+        z += vec.z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>add</code> adds the provided values to this vector, creating a
+     * new vector that is then returned.
+     *
+     * @param addX
+     *            the x value to add.
+     * @param addY
+     *            the y value to add.
+     * @param addZ
+     *            the z value to add.
+     * @return the result vector.
+     */
+    public Vector3d add(double addX, double addY, double addZ) {
+        return new Vector3d(x + addX, y + addY, z + addZ);
+    }
+
+    /**
+     * <code>addLocal</code> adds the provided values to this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls.
+     *
+     * @param addX
+     *            value to add to x
+     * @param addY
+     *            value to add to y
+     * @param addZ
+     *            value to add to z
+     * @return this
+     */
+    public Vector3d addLocal(double addX, double addY, double addZ) {
+        x += addX;
+        y += addY;
+        z += addZ;
+        return this;
+    }
+
+    /**
+     *
+     * <code>scaleAdd</code> multiplies this vector by a scalar then adds the
+     * given Vector3d.
+     *
+     * @param scalar
+     *            the value to multiply this vector by.
+     * @param add
+     *            the value to add
+     */
+    public Vector3d scaleAdd(double scalar, Vector3d add) {
+        x = x * scalar + add.x;
+        y = y * scalar + add.y;
+        z = z * scalar + add.z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>scaleAdd</code> multiplies the given vector by a scalar then adds
+     * the given vector.
+     *
+     * @param scalar
+     *            the value to multiply this vector by.
+     * @param mult
+     *            the value to multiply the scalar by
+     * @param add
+     *            the value to add
+     */
+    public Vector3d scaleAdd(double scalar, Vector3d mult, Vector3d add) {
+        x = mult.x * scalar + add.x;
+        y = mult.y * scalar + add.y;
+        z = mult.z * scalar + add.z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>dot</code> calculates the dot product of this vector with a
+     * provided vector. If the provided vector is null, 0 is returned.
+     *
+     * @param vec
+     *            the vector to dot with this vector.
+     * @return the resultant dot product of this vector and a given vector.
+     */
+    public double dot(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, 0 returned.");
+            return 0;
+        }
+        return x * vec.x + y * vec.y + z * vec.z;
+    }
+
+    /**
+     * <code>cross</code> calculates the cross product of this vector with a
+     * parameter vector v.
+     *
+     * @param v
+     *            the vector to take the cross product of with this.
+     * @return the cross product vector.
+     */
+    public Vector3d cross(Vector3d v) {
+        return this.cross(v, null);
+    }
+
+    /**
+     * <code>cross</code> calculates the cross product of this vector with a
+     * parameter vector v. The result is stored in <code>result</code>
+     *
+     * @param v
+     *            the vector to take the cross product of with this.
+     * @param result
+     *            the vector to store the cross product result.
+     * @return result, after recieving the cross product vector.
+     */
+    public Vector3d cross(Vector3d v, Vector3d result) {
+        return this.cross(v.x, v.y, v.z, result);
+    }
+
+    /**
+     * <code>cross</code> calculates the cross product of this vector with a
+     * parameter vector v. The result is stored in <code>result</code>
+     *
+     * @param otherX
+     *            x component of the vector to take the cross product of with this.
+     * @param otherY
+     *            y component of the vector to take the cross product of with this.
+     * @param otherZ
+     *            z component of the vector to take the cross product of with this.
+     * @param result
+     *            the vector to store the cross product result.
+     * @return result, after recieving the cross product vector.
+     */
+    public Vector3d cross(double otherX, double otherY, double otherZ, Vector3d result) {
+        if (result == null) {
+            result = new Vector3d();
+        }
+        double resX = y * otherZ - z * otherY;
+        double resY = z * otherX - x * otherZ;
+        double resZ = x * otherY - y * otherX;
+        result.set(resX, resY, resZ);
+        return result;
+    }
+
+    /**
+     * <code>crossLocal</code> calculates the cross product of this vector
+     * with a parameter vector v.
+     *
+     * @param v
+     *            the vector to take the cross product of with this.
+     * @return this.
+     */
+    public Vector3d crossLocal(Vector3d v) {
+        return this.crossLocal(v.x, v.y, v.z);
+    }
+
+    /**
+     * <code>crossLocal</code> calculates the cross product of this vector
+     * with a parameter vector v.
+     *
+     * @param otherX
+     *            x component of the vector to take the cross product of with this.
+     * @param otherY
+     *            y component of the vector to take the cross product of with this.
+     * @param otherZ
+     *            z component of the vector to take the cross product of with this.
+     * @return this.
+     */
+    public Vector3d crossLocal(double otherX, double otherY, double otherZ) {
+        double tempx = y * otherZ - z * otherY;
+        double tempy = z * otherX - x * otherZ;
+        z = x * otherY - y * otherX;
+        x = tempx;
+        y = tempy;
+        return this;
+    }
+
+    /**
+     * <code>length</code> calculates the magnitude of this vector.
+     *
+     * @return the length or magnitude of the vector.
+     */
+    public double length() {
+        return Math.sqrt(this.lengthSquared());
+    }
+
+    /**
+     * <code>lengthSquared</code> calculates the squared value of the
+     * magnitude of the vector.
+     *
+     * @return the magnitude squared of the vector.
+     */
+    public double lengthSquared() {
+        return x * x + y * y + z * z;
+    }
+
+    /**
+     * <code>distanceSquared</code> calculates the distance squared between
+     * this vector and vector v.
+     *
+     * @param v
+     *            the second vector to determine the distance squared.
+     * @return the distance squared between the two vectors.
+     */
+    public double distanceSquared(Vector3d v) {
+        double dx = x - v.x;
+        double dy = y - v.y;
+        double dz = z - v.z;
+        return dx * dx + dy * dy + dz * dz;
+    }
+
+    /**
+     * <code>distance</code> calculates the distance between this vector and
+     * vector v.
+     *
+     * @param v
+     *            the second vector to determine the distance.
+     * @return the distance between the two vectors.
+     */
+    public double distance(Vector3d v) {
+        return Math.sqrt(this.distanceSquared(v));
+    }
+
+    /**
+     *
+     * <code>mult</code> multiplies this vector by a scalar. The resultant
+     * vector is returned.
+     *
+     * @param scalar
+     *            the value to multiply this vector by.
+     * @return the new vector.
+     */
+    public Vector3d mult(double scalar) {
+        return new Vector3d(x * scalar, y * scalar, z * scalar);
+    }
+
+    /**
+     *
+     * <code>mult</code> multiplies this vector by a scalar. The resultant
+     * vector is supplied as the second parameter and returned.
+     *
+     * @param scalar
+     *            the scalar to multiply this vector by.
+     * @param product
+     *            the product to store the result in.
+     * @return product
+     */
+    public Vector3d mult(double scalar, Vector3d product) {
+        if (null == product) {
+            product = new Vector3d();
+        }
+
+        product.x = x * scalar;
+        product.y = y * scalar;
+        product.z = z * scalar;
+        return product;
+    }
+
+    /**
+     * <code>multLocal</code> multiplies this vector by a scalar internally,
+     * and returns a handle to this vector for easy chaining of calls.
+     *
+     * @param scalar
+     *            the value to multiply this vector by.
+     * @return this
+     */
+    public Vector3d multLocal(double scalar) {
+        x *= scalar;
+        y *= scalar;
+        z *= scalar;
+        return this;
+    }
+
+    /**
+     * <code>multLocal</code> multiplies a provided vector to this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls. If the provided vector is null, null is returned.
+     *
+     * @param vec
+     *            the vector to mult to this vector.
+     * @return this
+     */
+    public Vector3d multLocal(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        x *= vec.x;
+        y *= vec.y;
+        z *= vec.z;
+        return this;
+    }
+
+    /**
+     * <code>multLocal</code> multiplies this vector by 3 scalars
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls.
+     *
+     * @param x
+     * @param y
+     * @param z
+     * @return this
+     */
+    public Vector3d multLocal(double x, double y, double z) {
+        this.x *= x;
+        this.y *= y;
+        this.z *= z;
+        return this;
+    }
+
+    /**
+     * <code>multLocal</code> multiplies a provided vector to this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls. If the provided vector is null, null is returned.
+     *
+     * @param vec
+     *            the vector to mult to this vector.
+     * @return this
+     */
+    public Vector3d mult(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        return this.mult(vec, null);
+    }
+
+    /**
+     * <code>multLocal</code> multiplies a provided vector to this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls. If the provided vector is null, null is returned.
+     *
+     * @param vec
+     *            the vector to mult to this vector.
+     * @param store
+     *            result vector (null to create a new vector)
+     * @return this
+     */
+    public Vector3d mult(Vector3d vec, Vector3d store) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        if (store == null) {
+            store = new Vector3d();
+        }
+        return store.set(x * vec.x, y * vec.y, z * vec.z);
+    }
+
+    /**
+     * <code>divide</code> divides the values of this vector by a scalar and
+     * returns the result. The values of this vector remain untouched.
+     *
+     * @param scalar
+     *            the value to divide this vectors attributes by.
+     * @return the result <code>Vector</code>.
+     */
+    public Vector3d divide(double scalar) {
+        scalar = 1f / scalar;
+        return new Vector3d(x * scalar, y * scalar, z * scalar);
+    }
+
+    /**
+     * <code>divideLocal</code> divides this vector by a scalar internally,
+     * and returns a handle to this vector for easy chaining of calls. Dividing
+     * by zero will result in an exception.
+     *
+     * @param scalar
+     *            the value to divides this vector by.
+     * @return this
+     */
+    public Vector3d divideLocal(double scalar) {
+        scalar = 1f / scalar;
+        x *= scalar;
+        y *= scalar;
+        z *= scalar;
+        return this;
+    }
+
+    /**
+     * <code>divide</code> divides the values of this vector by a scalar and
+     * returns the result. The values of this vector remain untouched.
+     *
+     * @param scalar
+     *            the value to divide this vectors attributes by.
+     * @return the result <code>Vector</code>.
+     */
+    public Vector3d divide(Vector3d scalar) {
+        return new Vector3d(x / scalar.x, y / scalar.y, z / scalar.z);
+    }
+
+    /**
+     * <code>divideLocal</code> divides this vector by a scalar internally,
+     * and returns a handle to this vector for easy chaining of calls. Dividing
+     * by zero will result in an exception.
+     *
+     * @param scalar
+     *            the value to divides this vector by.
+     * @return this
+     */
+    public Vector3d divideLocal(Vector3d scalar) {
+        x /= scalar.x;
+        y /= scalar.y;
+        z /= scalar.z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>negate</code> returns the negative of this vector. All values are
+     * negated and set to a new vector.
+     *
+     * @return the negated vector.
+     */
+    public Vector3d negate() {
+        return new Vector3d(-x, -y, -z);
+    }
+
+    /**
+     *
+     * <code>negateLocal</code> negates the internal values of this vector.
+     *
+     * @return this.
+     */
+    public Vector3d negateLocal() {
+        x = -x;
+        y = -y;
+        z = -z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>subtract</code> subtracts the values of a given vector from those
+     * of this vector creating a new vector object. If the provided vector is
+     * null, null is returned.
+     *
+     * @param vec
+     *            the vector to subtract from this vector.
+     * @return the result vector.
+     */
+    public Vector3d subtract(Vector3d vec) {
+        return new Vector3d(x - vec.x, y - vec.y, z - vec.z);
+    }
+
+    /**
+     * <code>subtractLocal</code> subtracts a provided vector to this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls. If the provided vector is null, null is returned.
+     *
+     * @param vec
+     *            the vector to subtract
+     * @return this
+     */
+    public Vector3d subtractLocal(Vector3d vec) {
+        if (null == vec) {
+            LOGGER.warning("Provided vector is null, null returned.");
+            return null;
+        }
+        x -= vec.x;
+        y -= vec.y;
+        z -= vec.z;
+        return this;
+    }
+
+    /**
+     *
+     * <code>subtract</code>
+     *
+     * @param vec
+     *            the vector to subtract from this
+     * @param result
+     *            the vector to store the result in
+     * @return result
+     */
+    public Vector3d subtract(Vector3d vec, Vector3d result) {
+        if (result == null) {
+            result = new Vector3d();
+        }
+        result.x = x - vec.x;
+        result.y = y - vec.y;
+        result.z = z - vec.z;
+        return result;
+    }
+
+    /**
+     *
+     * <code>subtract</code> subtracts the provided values from this vector,
+     * creating a new vector that is then returned.
+     *
+     * @param subtractX
+     *            the x value to subtract.
+     * @param subtractY
+     *            the y value to subtract.
+     * @param subtractZ
+     *            the z value to subtract.
+     * @return the result vector.
+     */
+    public Vector3d subtract(double subtractX, double subtractY, double subtractZ) {
+        return new Vector3d(x - subtractX, y - subtractY, z - subtractZ);
+    }
+
+    /**
+     * <code>subtractLocal</code> subtracts the provided values from this vector
+     * internally, and returns a handle to this vector for easy chaining of
+     * calls.
+     *
+     * @param subtractX
+     *            the x value to subtract.
+     * @param subtractY
+     *            the y value to subtract.
+     * @param subtractZ
+     *            the z value to subtract.
+     * @return this
+     */
+    public Vector3d subtractLocal(double subtractX, double subtractY, double subtractZ) {
+        x -= subtractX;
+        y -= subtractY;
+        z -= subtractZ;
+        return this;
+    }
+
+    /**
+     * <code>normalize</code> returns the unit vector of this vector.
+     *
+     * @return unit vector of this vector.
+     */
+    public Vector3d normalize() {
+        double length = x * x + y * y + z * z;
+        if (length != 1f && length != 0f) {
+            length = 1.0f / Math.sqrt(length);
+            return new Vector3d(x * length, y * length, z * length);
+        }
+        return this.clone();
+    }
+
+    /**
+     * <code>normalizeLocal</code> makes this vector into a unit vector of
+     * itself.
+     *
+     * @return this.
+     */
+    public Vector3d normalizeLocal() {
+        // NOTE: this implementation is more optimized
+        // than the old jme normalize as this method
+        // is commonly used.
+        double length = x * x + y * y + z * z;
+        if (length != 1f && length != 0f) {
+            length = 1.0f / Math.sqrt(length);
+            x *= length;
+            y *= length;
+            z *= length;
+        }
+        return this;
+    }
+
+    /**
+     * <code>angleBetween</code> returns (in radians) the angle between two vectors.
+     * It is assumed that both this vector and the given vector are unit vectors (iow, normalized).
+     * 
+     * @param otherVector
+     *            a unit vector to find the angle against
+     * @return the angle in radians.
+     */
+    public double angleBetween(Vector3d otherVector) {
+        double dot = this.dot(otherVector);
+        // the vectors are normalized, but if they are parallel then the dot product migh get a value like: 1.000000000000000002
+        // which is caused by floating point operations; in such case, the acos function will return NaN so we need to clamp this value
+        dot = FastMath.clamp((float) dot, -1, 1);
+        return Math.acos(dot);
+    }
+
+    @Override
+    public Vector3d clone() {
+        try {
+            return (Vector3d) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(); // can not happen
+        }
+    }
+
+    /**
+     * are these two vectors the same? they are is they both have the same x,y,
+     * and z values.
+     *
+     * @param o
+     *            the object to compare for equality
+     * @return true if they are equal
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof Vector3d)) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        Vector3d comp = (Vector3d) o;
+        if (Double.compare(x, comp.x) != 0) {
+            return false;
+        }
+        if (Double.compare(y, comp.y) != 0) {
+            return false;
+        }
+        if (Double.compare(z, comp.z) != 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * <code>hashCode</code> returns a unique code for this vector object based
+     * on it's values. If two vectors are logically equivalent, they will return
+     * the same hash code value.
+     * @return the hash code value of this vector.
+     */
+    @Override
+    public int hashCode() {
+        long hash = 37;
+        hash += 37 * hash + Double.doubleToLongBits(x);
+        hash += 37 * hash + Double.doubleToLongBits(y);
+        hash += 37 * hash + Double.doubleToLongBits(z);
+        return (int) hash;
+    }
+
+    /**
+     * <code>toString</code> returns the string representation of this vector.
+     * The format is:
+     *
+     * org.jme.math.Vector3d [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ]
+     *
+     * @return the string representation of this vector.
+     */
+    @Override
+    public String toString() {
+        return "(" + x + ", " + y + ", " + z + ")";
+    }
+
+    public void write(JmeExporter e) throws IOException {
+        OutputCapsule capsule = e.getCapsule(this);
+        capsule.write(x, "x", 0);
+        capsule.write(y, "y", 0);
+        capsule.write(z, "z", 0);
+    }
+
+    public void read(JmeImporter e) throws IOException {
+        InputCapsule capsule = e.getCapsule(this);
+        x = capsule.readDouble("x", 0);
+        y = capsule.readDouble("y", 0);
+        z = capsule.readDouble("z", 0);
+    }
+}

+ 2 - 1
jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java

@@ -44,6 +44,7 @@ import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.system.JmeSystem;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
 import com.jme3.util.BufferUtils;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -229,7 +230,7 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
             int viewHeight = (int) ((curCamera.getViewPortTop() - curCamera.getViewPortBottom()) * curCamera.getHeight());
 
             renderer.setViewPort(0, 0, width, height);
-            renderer.readFrameBuffer(out, outBuf);
+            renderer.readFrameBufferWithFormat(out, outBuf, Image.Format.BGRA8);
             renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
 
             File file;

+ 14 - 1
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -222,7 +222,7 @@ public interface Renderer {
     /**
      * Reads the pixels currently stored in the specified framebuffer
      * into the given ByteBuffer object. 
-     * Only color pixels are transferred, the format is BGRA with 8 bits 
+     * Only color pixels are transferred, the format is RGBA with 8 bits 
      * per component. The given byte buffer should have at least
      * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
      * 
@@ -230,6 +230,19 @@ public interface Renderer {
      * @param byteBuf The bytebuffer to transfer color data to
      */
     public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf);
+    
+    /**
+     * Reads the pixels currently stored in the specified framebuffer
+     * into the given ByteBuffer object. 
+     * Only color pixels are transferred, witht hte given format. 
+     * The given byte buffer should have at least
+     * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
+     * 
+     * @param fb The framebuffer to read from
+     * @param byteBuf The bytebuffer to transfer color data to
+     * @param format the image format to use when reading the frameBuffer.
+     */
+    public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format);
 
     /**
      * Deletes a framebuffer and all attached renderbuffers

+ 10 - 1
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -1629,6 +1629,10 @@ public class GLRenderer implements Renderer {
     }
 
     public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
+        readFrameBufferWithGLFormat(fb, byteBuf, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
+    }
+    
+    private void readFrameBufferWithGLFormat(FrameBuffer fb, ByteBuffer byteBuf, int glFormat, int dataType) {
         if (fb != null) {
             RenderBuffer rb = fb.getColorBuffer();
             if (rb == null) {
@@ -1647,7 +1651,12 @@ public class GLRenderer implements Renderer {
             setFrameBuffer(null);
         }
 
-        gl.glReadPixels(vpX, vpY, vpW, vpH, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, byteBuf);
+        gl.glReadPixels(vpX, vpY, vpW, vpH, glFormat, dataType, byteBuf);
+    }
+    
+     public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {         
+        GLImageFormat glFormat = texUtil.getImageFormatWithError(format, false);
+        readFrameBufferWithGLFormat(fb, byteBuf, glFormat.format, glFormat.dataType);
     }
 
     private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {

+ 3 - 0
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -161,4 +161,7 @@ public class NullRenderer implements Renderer {
     public void setLinearizeSrgbImages(boolean linearize) {    
     }
 
+    public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {        
+    }
+
 }

+ 173 - 21
jme3-core/src/main/java/com/jme3/util/SkyFactory.java

@@ -55,6 +55,32 @@ import java.util.ArrayList;
  */
 public class SkyFactory {
 
+    
+    /**
+     * The type of map fed to the shader 
+     */
+    public enum EnvMapType{
+        /**
+         * The env map is a cube map see {@link TextureCubeMap} or 6 separate images that form a cube map
+         * The texture is either a {@link TextureCubeMap} or 6 {@link Texture2D}. 
+         * In the latter case, a TextureCubeMap is build from the 6 2d maps.
+         */
+        CubeMap,
+        /**
+         * The env map is a Sphere map. The texture is a Texture2D with the pixels arranged for
+         * <a href="http://en.wikipedia.org/wiki/Sphere_mapping">sphere
+         * mapping</a>.
+         */
+        SphereMap,
+        /**
+         * The env map is an Equirectangular map. A 2D textures with pixels 
+         * arranged for <a href="http://en.wikipedia.org/wiki/Equirectangular_projection">equirectangular 
+         * projection mapping.</a>.
+         * 
+         */
+        EquirectMap
+    }
+    
     /**
      * Create a sky with radius=10 using the given cubemap or spheremap texture.
      *
@@ -77,12 +103,33 @@ public class SkyFactory {
      * </ul>
      * @return a new spatial representing the sky, ready to be attached to the
      * scene graph
+     * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType)}
      */
+    @Deprecated 
     public static Spatial createSky(AssetManager assetManager, Texture texture,
             Vector3f normalScale, boolean sphereMap) {
         return createSky(assetManager, texture, normalScale, sphereMap, 10);
     }
 
+    /**
+     * 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 envMapType see {@link EnvMapType}
+     * @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, EnvMapType envMapType) {
+        return createSky(assetManager, texture, normalScale, envMapType, 10);
+    }
     /**
      * Create a sky using the given cubemap or spheremap texture.
      *
@@ -105,9 +152,31 @@ public class SkyFactory {
      * frustrum
      * @return a new spatial representing the sky, ready to be attached to the
      * scene graph
+     * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType, int)}
      */
+    @Deprecated
     public static Spatial createSky(AssetManager assetManager, Texture texture,
             Vector3f normalScale, boolean sphereMap, int sphereRadius) {
+        return createSky(assetManager, texture, normalScale, sphereMap?EnvMapType.SphereMap:EnvMapType.CubeMap, sphereRadius);
+    }
+    
+     /**
+     * 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 envMapType see {@link EnvMapType}
+     * @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, EnvMapType envMapType, int sphereRadius) {
         if (texture == null) {
             throw new IllegalArgumentException("texture cannot be null");
         }
@@ -121,13 +190,19 @@ public class SkyFactory {
         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);
+        switch (envMapType){
+            case CubeMap : 
+                // make sure its a cubemap
+                Image img = texture.getImage();
+                texture = new TextureCubeMap();
+                texture.setImage(img);
+                break;
+            case SphereMap :     
+                skyMat.setBoolean("SphereMap", true);
+                break;
+            case EquirectMap : 
+                skyMat.setBoolean("EquirectMap", true);
+                break;
         }
         texture.setMagFilter(Texture.MagFilter.Bilinear);
         texture.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
@@ -136,6 +211,84 @@ public class SkyFactory {
 
         return sky;
     }
+     
+    /**
+    * Create a sky using the given cubemap or spheremap texture.
+    *
+    * @param assetManager from which to load materials
+    * @param texture to use    *
+    * @param sphereMap determines how the texture is used:<br>
+    * <ul>
+    * <li>true: The texture is a Texture2D with the pixels arranged for
+    * <a href="http://en.wikipedia.org/wiki/Sphere_mapping">sphere
+    * mapping</a>.</li>
+    * <li>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</li>
+    * </ul> 
+    * @return a new spatial representing the sky, ready to be attached to the
+    * scene graph
+    * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType)}
+    */  
+    @Deprecated
+    public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) {
+        return createSky(assetManager, texture, Vector3f.UNIT_XYZ,  sphereMap?EnvMapType.SphereMap:EnvMapType.CubeMap);
+    }
+
+    /**
+    * Create a sky using the given cubemap or spheremap texture.
+    *
+    * @param assetManager from which to load materials
+    * @param textureName the path to the texture asset to use    
+    * @param sphereMap determines how the texture is used:<br>
+    * <ul>
+    * <li>true: The texture is a Texture2D with the pixels arranged for
+    * <a href="http://en.wikipedia.org/wiki/Sphere_mapping">sphere
+    * mapping</a>.</li>
+    * <li>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</li>
+    * </ul> 
+    * @return a new spatial representing the sky, ready to be attached to the
+    * scene graph
+    * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, java.lang.String, com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType)}
+    */  
+    @Deprecated
+    public static Spatial createSky(AssetManager assetManager, String textureName, boolean sphereMap) {
+        return createSky(assetManager, textureName,  sphereMap?EnvMapType.SphereMap:EnvMapType.CubeMap);
+    }
+    
+    /**
+    * Create a sky using the given cubemap or spheremap texture.
+    *
+    * @param assetManager from which to load materials
+    * @param texture to use  
+    * @param envMapType see {@link EnvMapType}
+    * @return a new spatial representing the sky, ready to be attached to the
+    * scene graph    
+    */  
+    public static Spatial createSky(AssetManager assetManager, Texture texture, EnvMapType envMapType) {
+        return createSky(assetManager, texture, Vector3f.UNIT_XYZ, envMapType);
+    }
+    
+    /**
+    * Create a sky using the given cubemap or spheremap texture.
+    *
+    * @param assetManager from which to load materials
+    * @param textureName the path to the texture asset to use    
+    * @param envMapType see {@link EnvMapType}
+    * @return a new spatial representing the sky, ready to be attached to the
+    * scene graph    
+    */  
+    public static Spatial createSky(AssetManager assetManager, String textureName, EnvMapType envMapType) {
+        TextureKey key = new TextureKey(textureName, true);
+        key.setGenerateMips(false);
+        if (envMapType == EnvMapType.CubeMap) {
+            key.setTextureTypeHint(Texture.Type.CubeMap);
+        }
+        Texture tex = assetManager.loadTexture(key);
+        return createSky(assetManager, tex, envMapType);
+    }
 
     private static void checkImage(Image image) {
 //        if (image.getDepth() != 1)
@@ -282,21 +435,20 @@ public class SkyFactory {
         return sky;
     }
 
+    /**
+    * 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     * 
+    * @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) {
         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(false);
-        if (!sphereMap) {
-            key.setTextureTypeHint(Texture.Type.CubeMap);
-        }
-        Texture tex = assetManager.loadTexture(key);
-        return createSky(assetManager, tex, sphereMap);
-    }
 }

+ 2 - 0
jme3-core/src/main/resources/Common/MatDefs/Misc/Sky.j3md

@@ -2,6 +2,7 @@ MaterialDef Sky Plane {
     MaterialParameters {
         TextureCubeMap Texture
         Boolean SphereMap
+		Boolean EquirectMap
         Vector3 NormalScale
     }
     Technique {
@@ -20,6 +21,7 @@ MaterialDef Sky Plane {
 
         Defines {
             SPHERE_MAP : SphereMap
+			EQUIRECT_MAP : EquirectMap
         }
     }
     Technique {

+ 18 - 3
jme3-core/src/main/resources/Common/ShaderLib/Optics.glsllib

@@ -1,4 +1,4 @@
-#ifdef SPHERE_MAP
+#if defined(SPHERE_MAP) || defined(EQUIRECT_MAP)
 #define ENVMAP sampler2D
 #define TEXENV texture2D
 #else
@@ -23,10 +23,25 @@ vec2 Optics_SphereCoord(in vec3 dir){
     return (dir.xy * vec2(inv_two_p)) + vec2(0.5);
 }
 
+#ifndef PI
+    #define PI 3.14159265358979323846264
+#endif
+//should be vec2(1.0 / (PI * 2.0), 1.0 / PI) but it's precomputed.
+const vec2 Optics_Glsllib_Rads = vec2(0.159154943091895, 0.318309886183790);
+vec2 Optics_LonLatCoords(in ENVMAP envMap, in vec3 dir){
+ float lon = atan(dir.z, dir.x)+ PI;
+ float lat = acos(dir.y); 
+ return vec2(lon, lat) * Optics_Glsllib_Rads; 
+}
+
 vec4 Optics_GetEnvColor(in ENVMAP envMap, in vec3 dir){
     #ifdef SPHERE_MAP
     return texture2D(envMap, Optics_SphereCoord(dir));
     #else
-    return textureCube(envMap, dir);
+        #ifdef EQUIRECT_MAP
+            return texture2D(envMap, Optics_LonLatCoords(envMap,dir));            
+        #else
+            return textureCube(envMap, dir);
+        #endif
     #endif
-}
+}

+ 2 - 1
jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java

@@ -40,6 +40,7 @@ import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.system.Timer;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.Screenshots;
 import java.awt.image.BufferedImage;
@@ -228,7 +229,7 @@ public class VideoRecorderAppState extends AbstractAppState {
                 final WorkItem item = freeItems.take();
                 usedItems.add(item);
                 item.buffer.clear();
-                renderer.readFrameBuffer(out, item.buffer);
+                renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8);
                 executor.submit(new Callable<Void>() {
 
                     public Void call() throws Exception {

+ 2 - 2
jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java

@@ -155,7 +155,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
     public void repaintInThread(){
         // Convert screenshot.
         byteBuf.clear();
-        rm.getRenderer().readFrameBuffer(fb, byteBuf);
+        rm.getRenderer().readFrameBufferWithFormat(fb, byteBuf,Format.BGRA8);
         
         synchronized (lock){
             // All operations on img must be synchronized
@@ -168,7 +168,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
     public void drawFrameInThread(){
         // Convert screenshot.
         byteBuf.clear();
-        rm.getRenderer().readFrameBuffer(fb, byteBuf);
+        rm.getRenderer().readFrameBufferWithFormat(fb, byteBuf,Format.BGRA8);
         Screenshots.convertScreenShot2(intBuf, img);
         
         synchronized (lock){

+ 1 - 1
jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java

@@ -97,7 +97,7 @@ public class TestRenderToCubemap  extends SimpleApplication {
         offView.setOutputFrameBuffer(offBuffer);
  
         // setup framebuffer's scene
-        Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
+        Box boxMesh = new Box( 1,1,1);
         Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m");
         offBox = new Geometry("box", boxMesh);
         offBox.setMaterial(material);

+ 2 - 1
jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java

@@ -48,6 +48,7 @@ import com.jme3.scene.shape.Box;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext.Type;
 import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture2D;
 import com.jme3.util.BufferUtils;
@@ -158,7 +159,7 @@ public class TestRenderToMemory extends SimpleApplication implements SceneProces
 
     public void updateImageContents(){
         cpuBuf.clear();
-        renderer.readFrameBuffer(offBuffer, cpuBuf);
+        renderer.readFrameBufferWithFormat(offBuffer, cpuBuf, Image.Format.BGRA8);
 
         synchronized (image) {
             Screenshots.convertScreenShot(cpuBuf, image);    

+ 4 - 0
jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java

@@ -2593,4 +2593,8 @@ public class IGLESShaderRenderer implements Renderer {
     public void setLinearizeSrgbImages(boolean linearize) {
       
     }
+    
+    public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
+        throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly"); 
+    }
 }

+ 4 - 0
jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java

@@ -2713,4 +2713,8 @@ public class JoglRenderer implements Renderer {
 	public void setLinearizeSrgbImages(boolean linearize) {
         linearizeSrgbImages = linearize;
     }
+
+    public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
+        throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly"); 
+    }
 }

+ 9 - 4
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -84,8 +84,13 @@ import static org.lwjgl.opengl.GL15.*;
 import static org.lwjgl.opengl.GL20.*;
 import org.lwjgl.opengl.GL30;
 
-
-public class LwjglRenderer implements Renderer {
+/**
+ * 
+ * Should not be used, has been replaced by Unified Rendering Architechture.
+ * @deprecated
+ */
+@Deprecated 
+public class LwjglRenderer  {
 
     private static final Logger logger = Logger.getLogger(LwjglRenderer.class.getName());
     private static final boolean VALIDATE_SHADER = false;
@@ -135,12 +140,12 @@ public class LwjglRenderer implements Renderer {
         nameBuf.rewind();
     }
 
-    @Override
+//    @Override
     public Statistics getStatistics() {
         return statistics;
     }
 
-    @Override
+ //   @Override
     public EnumSet<Caps> getCaps() {
         return caps;
     }

+ 6 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java

@@ -49,7 +49,6 @@ import static org.lwjgl.opengl.ARBTextureMultisample.*;
 import static org.lwjgl.opengl.EXTPackedDepthStencil.*;
 import static org.lwjgl.opengl.EXTPackedFloat.*;
 import static org.lwjgl.opengl.EXTTextureArray.*;
-import static org.lwjgl.opengl.EXTTextureCompressionLATC.*;
 import static org.lwjgl.opengl.EXTTextureCompressionS3TC.*;
 import static org.lwjgl.opengl.EXTTextureSRGB.*;
 import static org.lwjgl.opengl.EXTTextureSharedExponent.*;
@@ -58,6 +57,12 @@ import static org.lwjgl.opengl.GL12.*;
 import static org.lwjgl.opengl.GL13.*;
 import static org.lwjgl.opengl.GL14.*;
 
+/**
+ * 
+ * Should not be used, has been replaced by Unified Rendering Architechture.
+ * @deprecated
+ */
+@Deprecated 
 class TextureUtil {
 
     static class GLImageFormat {

+ 5 - 0
jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java

@@ -159,6 +159,11 @@ public class MaterialLoader implements AssetLoader {
         String[] split = statement.getLine().split(" ", 2);
         String keyword = split[0];
         if (keyword.equals("texture")){
+            if (split.length < 2) {
+                logger.log(Level.WARNING, "Invalid texture directive, no image specified at [{0}]", 
+                                            statement.getLineNumber());
+                return;
+            }
             readTextureImage(split[1]);
         }else if (keyword.equals("tex_address_mode")){
             String mode = split[1];