// Path: extensions/bullet/src/main/java/com/simsilica/bullet/CollisionShapes.java
// public interface CollisionShapes {
//     
//     /**
//      *  Registers the specific shape for the specified ShapeInfo such that
//      *  subsequent calls to loadShape(info) will resolve to that shape.
//      *  This can be used to bypass any (potentially expensive) collision
//      *  shape loading that the CollisionShapes implementation might be attempting
//      *  for registry misses.
//      */
//     public CollisionShape register( ShapeInfo info, CollisionShape shape ); 
//  
//     /**
//      *  Returns the collision shape for the specified shape info.
//      *  This will generally reuse CollisionShapes (without cloning) if they have
//      *  already been loaded or registered... though ultimately it is up to the
//      *  specific implementation of CollisionShapes.
//      */   
//     public CollisionShape getShape( ShapeInfo shape );
// }
// 
// Path: extensions/bullet/src/main/java/com/simsilica/bullet/ShapeInfo.java
// public class ShapeInfo implements EntityComponent {
// 
//     private int id;
// 
//     protected ShapeInfo() {
//     }
//  
//     /**
//      *  Creates a new ShapeInfo component with the specified integer ID, usually
//      *  representing some specific string in the EntityData's string index.
//      */   
//     public ShapeInfo( int id ) {
//         this.id = id;
//     }
//  
//     /**
//      *  Returns the integer ID of the collision shape associated with the 
//      *  entity to which this component applies.  Usually this is an ID in the
//      *  EntityData's strings table.
//      */   
//     public int getShapeId() {
//         return id; 
//     }
//  
//     /**
//      *  Returns the shape ID name if it exists in the EntityData's string table.
//      */
//     public String getShapeName( EntityData ed ) {
//         return ed.getStrings().getString(id);
//     }
//  
//     /**
//      *  Creates a ShapeInfo for the specified name by reusing or generating
//      *  a unique ID as required.
//      */   
//     public static ShapeInfo create( String name, EntityData ed ) {
//         return new ShapeInfo(ed.getStrings().getStringId(name, true));
//     }
//  
//     /**
//      *  Returns the same string representation as toString() but if EntityData is
//      *  non-null then the ID will be resolved to a string.  If EntityData is null
//      *  then this is the same as calling toString().
//      */   
//     public String toString( EntityData ed ) {
//         String s = ed == null ? String.valueOf(id) : getShapeName(ed);
//         return getClass().getSimpleName() + "[" + s + "]";  
//     }
//     
//     @Override
//     public String toString() {
//         return toString(null);
//     } 
// }


import java.util.*;
import org.slf4j.*;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.util.DebugShapeFactory;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.*;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.*;
import com.jme3.scene.shape.Box;
import com.jme3.scene.debug.Arrow;
import com.jme3.util.SafeArrayList;
import com.simsilica.es.*;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.bullet.CollisionShapes;
import com.simsilica.bullet.ShapeInfo;

            Spatial result = debugShapeCache.get(shape);
            if( result == null && create ) {
                result = DebugShapeFactory.getDebugShape(shape);
                debugShapeCache.put(shape, result); 
            }
            return result.clone();
        } catch( Exception e ) {
            log.error("Error creating debug shape for:" + id + "  CollisionShape:" + shape);
            Box b = new Box(0.1f, 0.1f, 0.1f);
            Node node = new Node("errorShape");
            Geometry geom;
            geom = new Geometry("errorBox", b);
            node.attachChild(geom);
            
            geom = new Geometry("errorBox", b);
            geom.rotate(FastMath.QUARTER_PI, FastMath.QUARTER_PI, 0);
            node.attachChild(geom);
            
            return node;
        }
    }     
     
    private class BodyDebugView {
        private Entity entity;
        private CollisionShape shape;
        private Spatial spatial;
        private int status = BodyDebugStatus.INACTIVE;
 
        public BodyDebugView( Entity entity ) {
            this.entity = entity;

// Path: extensions/bullet/src/main/java/com/simsilica/bullet/CollisionShapes.java
// public interface CollisionShapes {
//     
//     /**
//      *  Registers the specific shape for the specified ShapeInfo such that
//      *  subsequent calls to loadShape(info) will resolve to that shape.
//      *  This can be used to bypass any (potentially expensive) collision
//      *  shape loading that the CollisionShapes implementation might be attempting
//      *  for registry misses.
//      */
//     public CollisionShape register( ShapeInfo info, CollisionShape shape ); 
//  
//     /**
//      *  Returns the collision shape for the specified shape info.
//      *  This will generally reuse CollisionShapes (without cloning) if they have
//      *  already been loaded or registered... though ultimately it is up to the
//      *  specific implementation of CollisionShapes.
//      */   
//     public CollisionShape getShape( ShapeInfo shape );
// }
// 
// Path: extensions/bullet/src/main/java/com/simsilica/bullet/ShapeInfo.java
// public class ShapeInfo implements EntityComponent {
// 
//     private int id;
// 
//     protected ShapeInfo() {
//     }
//  
//     /**
//      *  Creates a new ShapeInfo component with the specified integer ID, usually
//      *  representing some specific string in the EntityData's string index.
//      */   
//     public ShapeInfo( int id ) {
//         this.id = id;
//     }
//  
//     /**
//      *  Returns the integer ID of the collision shape associated with the 
//      *  entity to which this component applies.  Usually this is an ID in the
//      *  EntityData's strings table.
//      */   
//     public int getShapeId() {
//         return id; 
//     }
//  
//     /**
//      *  Returns the shape ID name if it exists in the EntityData's string table.
//      */
//     public String getShapeName( EntityData ed ) {
//         return ed.getStrings().getString(id);
//     }
//  
//     /**
//      *  Creates a ShapeInfo for the specified name by reusing or generating
//      *  a unique ID as required.
//      */   
//     public static ShapeInfo create( String name, EntityData ed ) {
//         return new ShapeInfo(ed.getStrings().getStringId(name, true));
//     }
//  
//     /**
//      *  Returns the same string representation as toString() but if EntityData is
//      *  non-null then the ID will be resolved to a string.  If EntityData is null
//      *  then this is the same as calling toString().
//      */   
//     public String toString( EntityData ed ) {
//         String s = ed == null ? String.valueOf(id) : getShapeName(ed);
//         return getClass().getSimpleName() + "[" + s + "]";  
//     }
//     
//     @Override
//     public String toString() {
//         return toString(null);
//     } 
// }


// Path: extensions/bullet/src/main/java/com/simsilica/bullet/debug/PhysicsDebugState.java
import java.util.*;
import org.slf4j.*;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.util.DebugShapeFactory;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.*;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.*;
import com.jme3.scene.shape.Box;
import com.jme3.scene.debug.Arrow;
import com.jme3.util.SafeArrayList;
import com.simsilica.es.*;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.bullet.CollisionShapes;
import com.simsilica.bullet.ShapeInfo;

            Spatial result = debugShapeCache.get(shape);
            if( result == null && create ) {
                result = DebugShapeFactory.getDebugShape(shape);
                debugShapeCache.put(shape, result); 
            }
            return result.clone();
        } catch( Exception e ) {
            log.error("Error creating debug shape for:" + id + "  CollisionShape:" + shape);
            Box b = new Box(0.1f, 0.1f, 0.1f);
            Node node = new Node("errorShape");
            Geometry geom;
            geom = new Geometry("errorBox", b);
            node.attachChild(geom);
            
            geom = new Geometry("errorBox", b);
            geom.rotate(FastMath.QUARTER_PI, FastMath.QUARTER_PI, 0);
            node.attachChild(geom);
            
            return node;
        }
    }     
     
    private class BodyDebugView {
        private Entity entity;
        private CollisionShape shape;
        private Spatial spatial;
        private int status = BodyDebugStatus.INACTIVE;
 
        public BodyDebugView( Entity entity ) {
            this.entity = entity;

            this.shape = shapes.getShape(entity.get(ShapeInfo.class));
