ソースを参照

Fix issue 1705 (serialize 6 mesh subclasses) (#1754)

* jme3-vr:  implement serialization for CenterQuad

* ArmatureInterJointsWire:  add a no-arg constructor

* jme3-core:  implement serialization for skeleton debug meshes

* TestCloneMesh:  enable testing of skeleton-debug meshes

* Surface:  implement serialization
Stephen Gold 3 年 前
コミット
2ffe671828

+ 73 - 2
jme3-core/src/main/java/com/jme3/scene/debug/SkeletonInterBoneWire.java

@@ -36,6 +36,10 @@ import java.util.Map;
 
 import com.jme3.animation.Bone;
 import com.jme3.animation.Skeleton;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
@@ -43,6 +47,8 @@ import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * A class that displays a dotted line between a bone tail and its children's heads.
@@ -54,9 +60,9 @@ public class SkeletonInterBoneWire extends Mesh {
     /** The amount of connections between bones. */
     private int                 connectionsAmount;
     /** The skeleton that will be showed. */
-    final private Skeleton            skeleton;
+    private Skeleton            skeleton;
     /** The map between the bone index and its length. */
-    final private Map<Integer, Float> boneLengths;
+    private Map<Integer, Float> boneLengths;
 
     /**
      * Creates buffers for points. Each line has POINT_AMOUNT of points.
@@ -83,6 +89,12 @@ public class SkeletonInterBoneWire extends Mesh {
         this.updateCounts();
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected SkeletonInterBoneWire() {
+    }
+
     /**
      * This method updates the geometry according to the positions of the bones.
      */
@@ -112,6 +124,65 @@ public class SkeletonInterBoneWire extends Mesh {
         this.updateBound();
     }
 
+    /**
+     * De-serializes from the specified importer, for example when loading from
+     * a J3O file.
+     *
+     * @param importer the importer to use (not null)
+     * @throws IOException from the importer
+     */
+    @Override
+    public void read(JmeImporter importer) throws IOException {
+        super.read(importer);
+        InputCapsule capsule = importer.getCapsule(this);
+
+        connectionsAmount = capsule.readInt("connectionsAmount", 1);
+        skeleton = (Skeleton) capsule.readSavable("skeleton", null);
+
+        int[] blKeys = capsule.readIntArray("blKeys", null);
+        float[] blValues = capsule.readFloatArray("blValues", null);
+        if (blKeys == null) {
+            boneLengths = null;
+        } else {
+            assert blValues.length == blKeys.length;
+            int numLengths = blKeys.length;
+            boneLengths = new HashMap<>(numLengths);
+            for (int i = 0; i < numLengths; ++i) {
+                boneLengths.put(blKeys[i], blValues[i]);
+            }
+        }
+    }
+
+    /**
+     * Serializes to the specified exporter, for example when saving to a J3O
+     * file. The current instance is unaffected.
+     *
+     * @param exporter the exporter to use (not null)
+     * @throws IOException from the exporter
+     */
+    @Override
+    public void write(JmeExporter exporter) throws IOException {
+        super.write(exporter);
+        OutputCapsule capsule = exporter.getCapsule(this);
+
+        capsule.write(connectionsAmount, "connectionsAmount", 1);
+        capsule.write(skeleton, "skeleton", null);
+
+        if (boneLengths != null) {
+            int numLengths = boneLengths.size();
+            int[] blKeys = new int[numLengths];
+            float[] blValues = new float[numLengths];
+            int i = 0;
+            for (Map.Entry<Integer, Float> entry : boneLengths.entrySet()) {
+                blKeys[i] = entry.getKey();
+                blValues[i] = entry.getValue();
+                ++i;
+            }
+            capsule.write(blKeys, "blKeys", null);
+            capsule.write(blValues, "blValues", null);
+        }
+    }
+
     /**
      * This method counts the connections between bones.
      * @param bone

+ 69 - 0
jme3-core/src/main/java/com/jme3/scene/debug/SkeletonPoints.java

@@ -33,6 +33,10 @@ package com.jme3.scene.debug;
 
 import com.jme3.animation.Bone;
 import com.jme3.animation.Skeleton;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
@@ -40,8 +44,10 @@ import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.util.BufferUtils;
+import java.io.IOException;
 
 import java.nio.FloatBuffer;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -88,6 +94,12 @@ public class SkeletonPoints extends Mesh {
 
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected SkeletonPoints() {
+    }
+
     /**
      * The method updates the geometry according to the positions of the bones.
      */
@@ -110,4 +122,61 @@ public class SkeletonPoints extends Mesh {
 
         this.updateBound();
     }
+    
+    /**
+     * De-serializes from the specified importer, for example when loading from
+     * a J3O file.
+     *
+     * @param importer the importer to use (not null)
+     * @throws IOException from the importer
+     */
+    @Override
+    public void read(JmeImporter importer) throws IOException {
+        super.read(importer);
+        InputCapsule capsule = importer.getCapsule(this);
+
+        skeleton = (Skeleton) capsule.readSavable("skeleton", null);
+
+        int[] blKeys = capsule.readIntArray("blKeys", null);
+        float[] blValues = capsule.readFloatArray("blValues", null);
+        if (blKeys == null) {
+            boneLengths = null;
+        } else {
+            assert blValues.length == blKeys.length;
+            int numLengths = blKeys.length;
+            boneLengths = new HashMap<>(numLengths);
+            for (int i = 0; i < numLengths; ++i) {
+                boneLengths.put(blKeys[i], blValues[i]);
+            }
+        }
+    }
+
+    /**
+     * Serializes to the specified exporter, for example when saving to a J3O
+     * file. The current instance is unaffected.
+     *
+     * @param exporter the exporter to use (not null)
+     * @throws IOException from the exporter
+     */
+    @Override
+    public void write(JmeExporter exporter) throws IOException {
+        super.write(exporter);
+        OutputCapsule capsule = exporter.getCapsule(this);
+
+        capsule.write(skeleton, "skeleton", null);
+
+        if (boneLengths != null) {
+            int numLengths = boneLengths.size();
+            int[] blKeys = new int[numLengths];
+            float[] blValues = new float[numLengths];
+            int i = 0;
+            for (Map.Entry<Integer, Float> entry : boneLengths.entrySet()) {
+                blKeys[i] = entry.getKey();
+                blValues[i] = entry.getValue();
+                ++i;
+            }
+            capsule.write(blKeys, "blKeys", null);
+            capsule.write(blValues, "blValues", null);
+        }
+    }
 }

+ 71 - 0
jme3-core/src/main/java/com/jme3/scene/debug/SkeletonWire.java

@@ -37,6 +37,10 @@ import java.util.Map;
 
 import com.jme3.animation.Bone;
 import com.jme3.animation.Skeleton;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
@@ -44,6 +48,8 @@ import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.util.BufferUtils;
+import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * The class that displays either wires between the bones' heads if no length data is supplied and
@@ -111,6 +117,12 @@ public class SkeletonWire extends Mesh {
         this.updateCounts();
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected SkeletonWire() {
+    }
+
     /**
      * This method updates the geometry according to the positions of the bones.
      */
@@ -134,6 +146,65 @@ public class SkeletonWire extends Mesh {
         this.updateBound();
     }
 
+    /**
+     * De-serializes from the specified importer, for example when loading from
+     * a J3O file.
+     *
+     * @param importer the importer to use (not null)
+     * @throws IOException from the importer
+     */
+    @Override
+    public void read(JmeImporter importer) throws IOException {
+        super.read(importer);
+        InputCapsule capsule = importer.getCapsule(this);
+
+        numConnections = capsule.readInt("numConnections", 1);
+        skeleton = (Skeleton) capsule.readSavable("skeleton", null);
+
+        int[] blKeys = capsule.readIntArray("blKeys", null);
+        float[] blValues = capsule.readFloatArray("blValues", null);
+        if (blKeys == null) {
+            boneLengths = null;
+        } else {
+            assert blValues.length == blKeys.length;
+            int numLengths = blKeys.length;
+            boneLengths = new HashMap<>(numLengths);
+            for (int i = 0; i < numLengths; ++i) {
+                boneLengths.put(blKeys[i], blValues[i]);
+            }
+        }
+    }
+
+    /**
+     * Serializes to the specified exporter, for example when saving to a J3O
+     * file. The current instance is unaffected.
+     *
+     * @param exporter the exporter to use (not null)
+     * @throws IOException from the exporter
+     */
+    @Override
+    public void write(JmeExporter exporter) throws IOException {
+        super.write(exporter);
+        OutputCapsule capsule = exporter.getCapsule(this);
+
+        capsule.write(numConnections, "numConnections", 1);
+        capsule.write(skeleton, "skeleton", null);
+
+        if (boneLengths != null) {
+            int numLengths = boneLengths.size();
+            int[] blKeys = new int[numLengths];
+            float[] blValues = new float[numLengths];
+            int i = 0;
+            for (Map.Entry<Integer, Float> entry : boneLengths.entrySet()) {
+                blKeys[i] = entry.getKey();
+                blValues[i] = entry.getValue();
+                ++i;
+            }
+            capsule.write(blKeys, "blKeys", null);
+            capsule.write(blValues, "blValues", null);
+        }
+    }
+
     /**
      * This method counts the connections between bones.
      * @param bone

+ 6 - 0
jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java

@@ -54,6 +54,12 @@ public class ArmatureInterJointsWire extends Mesh {
         updateGeometry(start, ends);
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected ArmatureInterJointsWire() {
+    }
+
     protected void updateGeometry(Vector3f start, Vector3f[] ends) {
         float[] pos = new float[ends.length * 3 + 3];
         pos[0] = start.x;

+ 98 - 5
jme3-core/src/main/java/com/jme3/scene/shape/Surface.java

@@ -31,6 +31,10 @@
  */
 package com.jme3.scene.shape;
 
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.math.CurveAndSurfaceMath;
 import com.jme3.math.FastMath;
 import com.jme3.math.Spline.SplineType;
@@ -39,6 +43,7 @@ import com.jme3.math.Vector4f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.util.BufferUtils;
+import java.io.IOException;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -53,12 +58,12 @@ import java.util.Map;
  */
 public class Surface extends Mesh {
     private SplineType           type;                // the type of the surface
-    final private List<List<Vector4f>> controlPoints;       // space control points and their weights
-    final private List<Float>[]        knots;               // knots of the surface
-    final private int                  basisUFunctionDegree; // the degree of basis U function
+    private List<List<Vector4f>> controlPoints;       // space control points and their weights
+    private List<Float>[]        knots;               // knots of the surface
+    private int                  basisUFunctionDegree; // the degree of basis U function
     private int                  basisVFunctionDegree; // the degree of basis V function
-    final private int                  uSegments;           // the amount of U segments
-    final private int                  vSegments;           // the amount of V segments
+    private int                  uSegments;           // the amount of U segments
+    private int                  vSegments;           // the amount of V segments
 
     /**
      * Constructor. Constructs required surface.
@@ -87,6 +92,12 @@ public class Surface extends Mesh {
         this.buildSurface(smooth);
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected Surface() {
+    }
+
     /**
      * This method creates a NURBS surface. The created mesh is smooth by default.
      * @param controlPoints
@@ -289,6 +300,88 @@ public class Surface extends Mesh {
         return type;
     }
 
+    /**
+     * De-serializes from the specified importer, for example when loading from
+     * a J3O file.
+     *
+     * @param importer the importer to use (not null)
+     * @throws IOException from the importer
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void read(JmeImporter importer) throws IOException {
+        super.read(importer);
+        InputCapsule capsule = importer.getCapsule(this);
+
+        type = capsule.readEnum("type", SplineType.class, null);
+        basisUFunctionDegree = capsule.readInt("basisUFunctionDegree", 0);
+        basisVFunctionDegree = capsule.readInt("basisVFunctionDegree", 0);
+        uSegments = capsule.readInt("uSegments", 0);
+        vSegments = capsule.readInt("vSegments", 0);
+
+        float[][] knotArray2D = capsule.readFloatArray2D("knotArray2D", null);
+        int numKnotArrayLists = knotArray2D.length;
+        knots = new ArrayList[numKnotArrayLists];
+        for (int i = 0; i < numKnotArrayLists; ++i) {
+            float[] knotArray = knotArray2D[i];
+            knots[i] = new ArrayList<>(knotArray.length);
+            for (float knot : knotArray) {
+                knots[i].add(knot);
+            }
+        }
+
+        List[] listArray = capsule.readSavableArrayListArray("listArray", null);
+        int numControlPointLists = listArray.length;
+        controlPoints = new ArrayList<>(numControlPointLists);
+        for (int i = 0; i < numControlPointLists; ++i) {
+            List<Vector4f> list = listArray[i];
+            controlPoints.add(list);
+        }
+    }
+
+    /**
+     * Serializes to the specified exporter, for example when saving to a J3O
+     * file. The current instance is unaffected.
+     *
+     * @param exporter the exporter to use (not null)
+     * @throws IOException from the exporter
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void write(JmeExporter exporter) throws IOException {
+        super.write(exporter);
+        OutputCapsule capsule = exporter.getCapsule(this);
+
+        capsule.write(type, "type", null);
+        capsule.write(basisUFunctionDegree, "basisUFunctionDegree", 0);
+        capsule.write(basisVFunctionDegree, "basisVFunctionDegree", 0);
+        capsule.write(uSegments, "uSegments", 0);
+        capsule.write(vSegments, "vSegments", 0);
+
+        int numKnotArrayLists = knots.length;
+        float[][] knotArray2D = new float[numKnotArrayLists][];
+        for (int i = 0; i < numKnotArrayLists; ++i) {
+            List<Float> list = knots[i];
+            int numKnots = list.size();
+            float[] array = new float[numKnots];
+            for (int j = 0; j < numKnots; ++j) {
+                array[j] = list.get(j);
+            }
+            knotArray2D[i] = array;
+        }
+        capsule.write(knotArray2D, "knotArray2D", null);
+
+        int numControlPointLists = controlPoints.size();
+        ArrayList[] listArray = new ArrayList[numControlPointLists];
+        for (int i = 0; i < numControlPointLists; ++i) {
+            List<Vector4f> list = controlPoints.get(i);
+            int numVectors = list.size();
+            listArray[i] = new ArrayList<>(numVectors);
+            listArray[i].addAll(list);
+        }
+        capsule.writeSavableArrayListArray(listArray, "listArray", null);
+    }
+
     /**
      * This method returns the minimum nurb curve U knot value.
      * @return the minimum nurb curve knot value

+ 7 - 3
jme3-core/src/test/java/com/jme3/scene/debug/TestCloneMesh.java

@@ -89,6 +89,7 @@ public class TestCloneMesh {
     /**
      * Test cloning/saving/loading a SkeletonDebugger.
      */
+    @Test
     public void testCloneSkeletonDebugger() {
         Bone[] boneArray = new Bone[2];
         boneArray[0] = new Bone("rootBone");
@@ -111,8 +112,9 @@ public class TestCloneMesh {
     }
 
     /**
-     * Test cloning/saving/loading a SkeletonInterBoneWire.
+     * Test cloning/saving/loading a SkeletonInterBoneWire.  See JME issue #1705.
      */
+    @Test
     public void testCloneSkeletonInterBoneWire() {
         Bone[] boneArray = new Bone[2];
         boneArray[0] = new Bone("rootBone");
@@ -138,8 +140,9 @@ public class TestCloneMesh {
     }
 
     /**
-     * Test cloning/saving/loading a SkeletonPoints.
+     * Test cloning/saving/loading a SkeletonPoints.  See JME issue #1705.
      */
+    @Test
     public void testCloneSkeletonPoints() {
         Bone[] boneArray = new Bone[2];
         boneArray[0] = new Bone("rootBone");
@@ -161,8 +164,9 @@ public class TestCloneMesh {
     }
 
     /**
-     * Test cloning/saving/loading a SkeletonWire.
+     * Test cloning/saving/loading a SkeletonWire.  See JME issue #1705.
      */
+    @Test
     public void testCloneSkeletonWire() {
         Bone[] boneArray = new Bone[2];
         boneArray[0] = new Bone("rootBone");

+ 41 - 0
jme3-vr/src/main/java/com/jme3/scene/CenterQuad.java

@@ -31,7 +31,12 @@
  */
 package com.jme3.scene;
 
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
 import com.jme3.scene.VertexBuffer.Type;
+import java.io.IOException;
 
 /**
  * A static, indexed, Triangles-mode mesh for an axis-aligned rectangle in the
@@ -80,6 +85,12 @@ public class CenterQuad extends Mesh {
         this.setStatic();
     }
 
+    /**
+     * For serialization only. Do not use.
+     */
+    protected CenterQuad() {
+    }
+
     public float getHeight() {
         return height;
     }
@@ -128,5 +139,35 @@ public class CenterQuad extends Mesh {
         updateBound();
     }
 
+    /**
+     * De-serializes from the specified importer, for example when loading from
+     * a J3O file.
+     *
+     * @param importer the importer to use (not null)
+     * @throws IOException from the importer
+     */
+    @Override
+    public void read(JmeImporter importer) throws IOException {
+        super.read(importer);
+        InputCapsule capsule = importer.getCapsule(this);
+
+        width = capsule.readFloat("width", 0f);
+        height = capsule.readFloat("height", 0f);
+    }
+
+    /**
+     * Serializes to the specified exporter, for example when saving to a J3O
+     * file. The current instance is unaffected.
+     *
+     * @param exporter the exporter to use (not null)
+     * @throws IOException from the exporter
+     */
+    @Override
+    public void write(JmeExporter exporter) throws IOException {
+        super.write(exporter);
+        OutputCapsule capsule = exporter.getCapsule(this);
 
+        capsule.write(width, "width", 0f);
+        capsule.write(height, "height", 0f);
+    }
 }