sim.portrayal3d.grid.ObjectGridPortrayal3D Maven / Gradle / Ivy
Show all versions of mason Show documentation
/*
Copyright 2006 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.portrayal3d.grid;
import sim.portrayal3d.*;
import sim.portrayal.*;
import sim.portrayal.grid.*;
import sim.field.grid.*;
import sim.util.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.picking.*;
/** Portrays ObjectGrid2D and ObjectGrid3D in 3D space. A (0,0) or (0,0,0) object is centered
* on the origin. 2D fields are spread through the XY plane and are presumed to have Z=0.
*
* You should be aware that ObjectGrid3DPortrayal3D is slow, especially if objects change
* a lot at given locations. This is because it has to update
* all the objects on a per location basis rather than on a per object basis.
* This is a worst-case scenario for Java3D.
*
*
Note for Java3D users: We experimented with a number of approaches to dealing with this.
* One approach was to use shared groups and only portray an object once, then use links to draw
* it in many locations. This has two subapproaches: you can wrap the link in a BranchGroup and
* replace it and the BranchGroup when the object changes at that location; or you can just change
* the link directly. The first approach is very slow (building BranchGroups isn't efficient).
* The second approach has promise, but there are grievous bugs in Java3D's handling of links
* which generate all sorts of race conditions with array bounds exceptions and null pointer exceptions
* etc. internal to Java3D when you change the SharedGroup that a link is pointing to. Ultimately
* we just gave up and used BranchGroups wrapping whole new submodels, entire copies for each
* location. Memory inefficient, but it's the fastest method we have figured out which doesn't
* break with stupid Sun bugs. It's also fairly simple to grok. Sorry.
*/
public class ObjectGridPortrayal3D extends FieldPortrayal3D
{
protected TransformGroup createModel()
{
TransformGroup globalTG = new TransformGroup();
globalTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
// we need a group to stick stuff into, so we create a Group here
Group global = new Group();
global.setCapability(Group.ALLOW_CHILDREN_READ);
global.setCapability(Group.ALLOW_CHILDREN_WRITE);
global.setCapability(Group.ALLOW_CHILDREN_EXTEND);
global.setUserData(this); // a sufficient tag -- a Group containing me. See LocationWrapper
// this is set so he'll be in the scenegraph path
global.setCapability(Group.ENABLE_PICK_REPORTING);
globalTG.addChild(global);
if (field==null) return globalTG;
Transform3D tmpLocalT = new Transform3D();
if (field instanceof ObjectGrid2D)
{
Object[][] grid = ((ObjectGrid2D)field).field;
for(int x=0;x 0) originalTransformGroup = (TransformGroup)(bg.getChild(0)); // could be null if we've stubbed
TransformGroup newTransformGroup = null;
Object originalData = bg.getUserData();
if (originalData == o)
{
newTransformGroup = p3d.getModel(o, originalTransformGroup);
}
else
{
Bag b = (Bag)(models.get(o));
if (b!=null && b.numObjs > 0)
{
// yay, we can reuse an existing model
BranchGroup replacementBranchGroup = (BranchGroup)(b.remove(0));
originalTransformGroup = (TransformGroup)(replacementBranchGroup.getChild(0));
newTransformGroup = p3d.getModel(o,originalTransformGroup);
if (newTransformGroup == originalTransformGroup) // we can stick the BranchGroup in
global.setChild(replacementBranchGroup,count-1);
}
else
// shoot, we have to create a new model. Rebuild.
newTransformGroup = p3d.getModel(o, null);
}
// is the new transformGroup different?
if (newTransformGroup != originalTransformGroup)
{
// dang!
newTransformGroup.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
newTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
newTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
// this is set so he'll be in the scenegraph path
newTransformGroup.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
BranchGroup bg2 = new BranchGroup();
bg2.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
bg2.setCapability(BranchGroup.ALLOW_DETACH);
// this is set so he'll be in the scenegraph path
bg2.setCapability(BranchGroup.ENABLE_PICK_REPORTING);
tmpLocalT.setTranslation(new Vector3d(x,y,0));
newTransformGroup.setTransform(tmpLocalT);
newTransformGroup.setUserData(new Int2D(x,y));
bg2.addChild(newTransformGroup);
bg2.setUserData(o);
global.setChild(bg2,count-1);
// add old BranchGroup to the hashmap
Bag b = (Bag)(models.get(originalData));
if (b==null) { b = new Bag(); models.put(originalData,b); }
b.add(bg);
}
}
}
}
else // field instanceof ObjectGrid3D
{
Object[][][] grid = ((ObjectGrid3D)field).field;
for(int x=0;x 0) originalTransformGroup = (TransformGroup)(bg.getChild(0)); // could be null if we've stubbed
TransformGroup newTransformGroup = null;
Object originalData = bg.getUserData();
if (originalData == o)
{
newTransformGroup = p3d.getModel(o, originalTransformGroup);
}
else
{
Bag b = (Bag)(models.get(o));
if (b!=null && b.numObjs > 0)
{
// yay, we can reuse an existing model
BranchGroup replacementBranchGroup = (BranchGroup)(b.remove(0));
originalTransformGroup = (TransformGroup)(replacementBranchGroup.getChild(0));
newTransformGroup = p3d.getModel(o,originalTransformGroup);
if (newTransformGroup == originalTransformGroup) // we can stick the BranchGroup in
global.setChild(replacementBranchGroup,count-1);
}
else
{
// shoot, we have to create a new model. Rebuild.
newTransformGroup = p3d.getModel(o, null);
}
}
// is the new transformGroup different?
if (newTransformGroup != originalTransformGroup)
{
// dang!
newTransformGroup.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
newTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
newTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
// this is set so he'll be in the scenegraph path
newTransformGroup.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
BranchGroup bg2 = new BranchGroup();
bg2.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
bg2.setCapability(BranchGroup.ALLOW_DETACH);
// this is set so he'll be in the scenegraph path
bg2.setCapability(BranchGroup.ENABLE_PICK_REPORTING);
tmpLocalT.setTranslation(new Vector3d(x,y,z));
newTransformGroup.setTransform(tmpLocalT);
newTransformGroup.setUserData(new Int3D(x,y,z));
bg2.addChild(newTransformGroup);
bg2.setUserData(o);
global.setChild(bg2,count-1);
// add old BranchGroup to the hashmap
Bag b = (Bag)(models.get(originalData));
if (b==null) { b = new Bag(); models.put(originalData,b); }
b.add(bg);
}
}
}
}
}
}
public void setField(Object field)
{
if (field instanceof ObjectGrid3D || field instanceof ObjectGrid2D) super.setField(field);
else throw new RuntimeException("Invalid field for ObjectGridPortrayal3D: " + field);
}
// searches for an object within a short distance of a location
final static int SEARCH_DISTANCE = 2;
final static int BAG_SIZE = (SEARCH_DISTANCE * 2 + 1) * (SEARCH_DISTANCE * 2 + 1) * (SEARCH_DISTANCE * 2 + 1); // 125
IntBag xPos = new IntBag(BAG_SIZE);
IntBag yPos = new IntBag(BAG_SIZE);
IntBag zPos = new IntBag(BAG_SIZE);
Int3D searchForObject(Object object, Int3D loc)
{
ObjectGrid3D field = (ObjectGrid3D)(this.field);
Object[][][] grid = field.field;
if (grid[loc.x][loc.y][loc.z] == object)
return new Int3D(loc.x, loc.y, loc.z);
//field.getNeighborsMaxDistance(loc.x, loc.y, loc.z, SEARCH_DISTANCE, true, xPos, yPos, zPos);
field.getMooreLocations(loc.x, loc.y, loc.z, SEARCH_DISTANCE, Grid2D.TOROIDAL, true, xPos, yPos, yPos); // we include the origin but it doesn't matter
for(int i=0;i