Proposed fix for issue #635

The issue was reported at https://code.google.com/p/jmonkeyengine/issues/detail?id=635

While the presence of WireBox.fromBoundingBox() suggests that this class can be used to debug bounding boxes, the current implementation always generates a box centered on the origin.

For reusability, I think meshes should generally be origin-centered (with offsets specified in the Geometry). However, this seems like a case where users can reasonably expect to specify an offset center.

In order to preserve the existing updatePositions(float, float, float) interface, the center must be stored in a field, which requires overriding Mesh.read() and Mesh.write(). I took the liberty of adding a constructor which specifies the center. Here is the proposed replacement for the existing /branches/gradle-restructure/jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java and /trunk/engine/src/core/com/jme3/scene/debug/WireBox.java :
[java]
/*

  • Copyright Ā© 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.debug;

import com.jme3.bounding.BoundingBox;
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;
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;

public class WireBox extends Mesh {

Vector3f center;

public WireBox() {
    this(1f, 1f, 1f);
}

public WireBox(float xRadius, float yRadius, float zRadius) {
    this(Vector3f.ZERO, xRadius, yRadius, zRadius);
}

public WireBox(Vector3f center, float xRadius, float yRadius, float zRadius) {
    super();

    this.center = center.clone();
    updatePositions(xRadius, yRadius, zRadius);

    setBuffer(Type.Index, 2,
            new short[]{
        0, 1,
        1, 2,
        2, 3,
        3, 0,
        4, 5,
        5, 6,
        6, 7,
        7, 4,
        0, 4,
        1, 5,
        2, 6,
        3, 7
    });
    setMode(Mesh.Mode.Lines);

    updateCounts();
}

public void fromBoundingBox(BoundingBox bbox) {
    center.set(bbox.getCenter());
    updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
}

@Override
public void read(JmeImporter importer) throws IOException {
    super.read(importer);
    InputCapsule in = importer.getCapsule(this);
    center = (Vector3f) in.readSavable("center", Vector3f.ZERO.clone());
}

public void updatePositions(float xRadius, float yRadius, float zRadius) {
    VertexBuffer vertexBuffer = getBuffer(Type.Position);
    FloatBuffer floatBuffer;
    if (vertexBuffer == null) {
        vertexBuffer = new VertexBuffer(Type.Position);
        floatBuffer = BufferUtils.createVector3Buffer(8);
        vertexBuffer.setupData(Usage.Dynamic, 3, Format.Float, floatBuffer);
        setBuffer(vertexBuffer);
    } else {
        floatBuffer = (FloatBuffer) vertexBuffer.getData();
        vertexBuffer.updateData(floatBuffer);
    }
    floatBuffer.rewind();
    float xHigh = center.x + xRadius;
    float yHigh = center.y + yRadius;
    float zHigh = center.z + zRadius;
    float xLow = center.x - xRadius;
    float yLow = center.y - yRadius;
    float zLow = center.z - zRadius;
    floatBuffer.put(new float[]{
        xLow, yLow, zHigh,
        xHigh, yLow, zHigh,
        xHigh, yHigh, zHigh,
        xLow, yHigh, zHigh,
        xLow, yLow, zLow,
        xHigh, yLow, zLow,
        xHigh, yHigh, zLow,
        xLow, yHigh, zLow
    });
    updateBound();
}

@Override
public void write(JmeExporter exporter) throws IOException {
    super.write(exporter);
    OutputCapsule out = exporter.getCapsule(this);
    out.write(center, "center", null);
}

}
[/java]

3 Likes

I donā€™t knowā€¦ IMO this class should extends Geometry and uses a classic Box mesh.
fromBoundingBox should update the underlying mesh and set the localTranslation to the center of the bounding box.

Before you could set the center of the mesh in the Box constructor and it lead to a lot of misunderstanding. So Iā€™m not really comfortable with this class.

@nehon said: I don't know... IMO this class should extends Geometry and uses a classic Box mesh. fromBoundingBox should update the underlying mesh and set the localTranslation to the center of the bounding box.

Before you could set the center of the mesh in the Box constructor and it lead to a lot of misunderstanding. So Iā€™m not really comfortable with this class.

Except this is not a box of 12 triangles it is a box of 12 lines. It shares nothing in common with a solid Box.

@sgold thx for fixing this issue.

I had noticed this bug by playing around with octrees and I was a little scared about the jme implementation in svn/ trunk/ engine/ src/ tools/ jme3tools/ optimize/ Octree.java.

Is this octree even used in some parts inside jme or is this only outdated stuff?

@pspeed said: Except this is not a box of 12 triangles it is a box of 12 lines. It shares nothing in common with a solid Box.
Yeah but that's for debug only... That's not really a problem if we use a real box
@nehon said: Yeah but that's for debug only... That's not really a problem if we use a real box

But isnā€™t that the whole point of this thread? That using WireBox for debugging stuff is not that great because when you construct one from a bounding box then it is not positioned properly. A real Box wouldnā€™t work for that.

Yeah but a WireBox being a Geometry and not a Mesh would know how to make this work keeping the same API.
Positioning an objects by updating itā€™s underlying buffers seems a bit too muchā€¦And, on a side not would not allow proper culling (which is not really an issue since itā€™s only used for debuggingā€¦but still).

@nehon said: Yeah but a WireBox being a Geometry and not a Mesh would know how to make this work keeping the same API. Positioning an objects by updating it's underlying buffers seems a bit too much...And, on a side not would not allow proper culling (which is not really an issue since it's only used for debugging...but still).

Yeah, I just wasnā€™t sure where Box was coming in.

Culling would still work, it would just be less efficient.

WireBox is still useful as a mesh. Personally, I think maybe itā€™s the utility method that should change to return a Geometryā€¦ not a subclass. Just a Geometry with a WireBox mesh, all positioned correctly and everything.

@pspeed said: Yeah, I just wasn't sure where Box was coming in.

Culling would still work, it would just be less efficient.

WireBox is still useful as a mesh. Personally, I think maybe itā€™s the utility method that should change to return a Geometryā€¦ not a subclass. Just a Geometry with a WireBox mesh, all positioned correctly and everything.


Yeah why not that would be a reasonable solution.

@florianbauer5 said: @sgold thx for fixing this issue.

I had noticed this bug by playing around with octrees and I was a little scared about the jme implementation in svn/ trunk/ engine/ src/ tools/ jme3tools/ optimize/ Octree.java.

Is this octree even used in some parts inside jme or is this only outdated stuff?

Octree is used only in TestOctree. Iā€™m unsure what it does, but I think itā€™s intended for use by game developers, maybe to speed up rendering or as part of a collision-detection system.

@nehon said: Yeah why not that would be a reasonable solution.

I think I understand what youā€™re proposing. Is someone else planning to work on this, or do I own it for now?

Owned!

Hereā€™s a new diff. I tested it, and (with suitable changes to the test case) it works, but I wonder if we should maintain the old interface as well.

Please let me know if Iā€™m on the right track ā€¦

--- Base (BASE)
+++ Locally Modified (Based On LOCAL)
@@ -32,6 +32,8 @@
 package com.jme3.scene.debug;
 
 import com.jme3.bounding.BoundingBox;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
@@ -45,7 +47,7 @@
     public WireBox(){
         this(1,1,1);
     }
-    
+
     public WireBox(float xExt, float yExt, float zExt){
         updatePositions(xExt,yExt,zExt);
         setBuffer(Type.Index, 2,
@@ -100,8 +102,23 @@
         updateBound();
     }
 
-    public void fromBoundingBox(BoundingBox bbox){
-        updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
+    /**
+     * Create a geometry suitable for visualizing the specified bounding box.
+     *
+     * @param bbox the bounding box (not null)
+     * @return a new Geometry instance in world space
+     */
+    public static Geometry fromBoundingBox(BoundingBox bbox) {
+        float xExtent = bbox.getXExtent();
+        float yExtent = bbox.getYExtent();
+        float zExtent = bbox.getZExtent();
+        WireBox mesh = new WireBox(xExtent, yExtent, zExtent);
+        Geometry result = new Geometry("bounding box", mesh);
+
+        Vector3f center = bbox.getCenter();
+        result.setLocalTranslation(center);
+
+        return result;
     }
 
 }
1 Like

Yeah, thatā€™s what I had in mind.

Not sure about leaving the old method inā€¦ since it didnā€™t even really work properly.

Do I have permission to commit the new diff?

lol I canā€™t keep the pace :stuck_out_tongue:
go ahead and commit :wink:

1 Like

Done! With revs 11087 and 11088.

1 Like

Because my fix did not preserve the old fromBoundingBox() method, it created an issue for de-serializing models which contain a WireBox. (See discussion here)

Here is my proposed fix-fix:

--- Base (BASE)
+++ Locally Modified (Based On LOCAL)
@@ -103,12 +103,20 @@
     }
 
     /**
+     * Old method retained for de-serialization purposes.
+     */
+    @Deprecated
+    public void fromBoundingBox(BoundingBox bbox) {
+        updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
+    }
+
+    /**
      * Create a geometry suitable for visualizing the specified bounding box.
      *
      * @param bbox the bounding box (not null)
      * @return a new Geometry instance in world space
      */
-    public static Geometry fromBoundingBox(BoundingBox bbox) {
\ No newline at end of file
+    public static Geometry makeGeometry(BoundingBox bbox) {
\ No newline at end of file
         float xExtent = bbox.getXExtent();
         float yExtent = bbox.getYExtent();
         float zExtent = bbox.getZExtent();

Note that I have to rename the new method to avoid a name conflict.

Is it OK for me to commit this fix-fix?

Go ahead.
At any rate we should make sure this method is not called in the engine and in the SDKā€¦also the scene composer would benefit from using the new method you didā€¦
I donā€™t see how the SDK managed to be built with this issueā€¦

1 Like
@nehon said: At any rate we should make sure this method is not called in the engine and in the SDK...also the scene composer would benefit from using the new method you did... I don't see how the SDK managed to be built with this issue...

Thanks for the reply. Iā€™ll check the SDK, but I donā€™t expect to find any other issues.

The previous fix changed the interface, but so slightly that normal invocation still worked. Itā€™s only when fromBoundingBox() is invoked via reflection that thereā€™s any issue. I think.