Hi,

I am currently developing an air hockey game in java3d and have come across an issue with collision detection between the puck and sides (or goals).
I am trying to change the colour of the sides when the puck bounces off them (not implemented yet), however, not only the side that the puck is colliding against changes colour, but anything else of the same colour (ie. the bottom side). Try changing the colour of one of the sides (ie. Box tableMarginTop = new Box(3.5f, 0.1f, 0.2f, blueApp) and notice the difference. How do I avoid this problem?
Thanks in advance.

Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.PositionInterpolator;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Shape3D;

import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JApplet;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.universe.SimpleUniverse;


public class TestAlpha extends JApplet {

	Alpha alpha = null;
	PositionInterpolator posInt = null;

	private Background background = null;
	Canvas3D canvas3D =
		new Canvas3D(SimpleUniverse.getPreferredConfiguration());
	SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

	// Create a bounds for the background and behaviours
	BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

	// Create the root of the branch graph
	BranchGroup scene = createSceneGraph();
	TransformGroup tg = simpleU.getViewingPlatform().getViewPlatformTransform();

	public TestAlpha(){
		launchApplication();
	}

	private void launchApplication() {

		background = new Background();
		background.setCapability(Background.ALLOW_IMAGE_WRITE);
		background.setCapability(Background.ALLOW_COLOR_WRITE);
		background.setApplicationBounds(bounds);
		scene.addChild(background);

		this.setLayout(new BorderLayout());
		this.add(canvas3D, BorderLayout.CENTER);
		positionCamera(simpleU);

		scene.compile();
		simpleU.addBranchGraph(scene);
	}

	private void positionCamera(SimpleUniverse su) {
		// Translation + rotation
		Transform3D translation = new Transform3D();
		translation.setTranslation(new Vector3f(0, -1, 14));
		Transform3D rotationX = new Transform3D();
		rotationX.rotX(45f * Math.PI/180f);
		Transform3D trans = new Transform3D();
		trans.mul(rotationX, translation);
		tg.setTransform(trans);
	}

	private BranchGroup createSceneGraph() {
		BranchGroup parent = new BranchGroup();
		parent.addChild(createTable());

		return parent;
	}

	private BranchGroup createTable(){
		// If you were writing a pool game you could use a PickSegment (PickRaySegment) to represent the pool cue
		// and you could pick with BoundingSphere to detecting collisions between the balls.

		
		// blue
		Appearance blueApp = new Appearance();
		blueApp.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
		Color3f blueColor = new Color3f(0.3f, 0.3f, 1.0f);
		ColoringAttributes blueCA = new ColoringAttributes();
		blueCA.setColor(blueColor);
		blueApp.setColoringAttributes(blueCA);
		
		
		// medium blue
		Appearance medBlueApp = new Appearance();
		Color3f medBlueColor = new Color3f(0.1f, 0.8f, 1.0f);
		ColoringAttributes medBlueCA = new ColoringAttributes();
		medBlueCA.setColor(medBlueColor);
		medBlueApp.setColoringAttributes(medBlueCA);

		// orange
		Appearance orangeApp = new Appearance();
		orangeApp.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
		Color3f orangeColor = new Color3f(0.988f, 0.800f, 0.080f);
		ColoringAttributes orangeCA = new ColoringAttributes();
		orangeCA.setColor(orangeColor);
		orangeApp.setColoringAttributes(orangeCA);

		BranchGroup parent = new BranchGroup();
		//Shape3D hockeyTableSlate = new Box(3.3f, 5.0f, 0.001f);
		Box hockeyTableSlate = new Box(3.3f, 5.0f, 0.001f, medBlueApp);
		//hockeyTableSlate.setUserData(new String("table bottom"));

		// the top table margin
		Box tableMarginTop = new Box(3.5f, 0.1f, 0.2f, orangeApp);
		tableMarginTop.setUserData(new String("top margin1"));
		Transform3D tableMarginToptr = new Transform3D();
		tableMarginToptr.setTranslation(new Vector3f(0f, 5.1f, 0.1f));
		TransformGroup tableMarginTopTG = new TransformGroup(tableMarginToptr);
		tableMarginTopTG.addChild(tableMarginTop);
		tableMarginTopTG.setUserData(new String("top margin2"));


		// the bottom table margin
		Box tableMarginBottom = new Box(3.5f, 0.1f, 0.2f, orangeApp);
		tableMarginBottom.setUserData(new String("bottom margin"));
		//SideCollisionDetector pcd2 = new SideCollisionDetector(tableMarginBottom);
		//pcd2.setSchedulingBounds(bounds);
		Transform3D tableMarginBottomtr = new Transform3D();
		tableMarginBottomtr.setTranslation(new Vector3f(0f, -5.1f, 0.1f));
		TransformGroup tableMarginBottomTG = new TransformGroup(
				tableMarginBottomtr);
		tableMarginBottomTG.addChild(tableMarginBottom);
		//parent.addChild(tableMarginBottomTG);

		TransformGroup mainTG = new TransformGroup();		
		mainTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		mainTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		mainTG.addChild(hockeyTableSlate);
		mainTG.addChild(tableMarginTopTG);
		mainTG.addChild(tableMarginBottomTG);
		//parent.addChild(mainTG);

		// Create the rotate behavior node
		MouseRotate behavior = new MouseRotate();
		behavior.setTransformGroup(mainTG);
		parent.addChild(behavior);
		behavior.setSchedulingBounds(bounds);

		// Create the zoom behavior node
		MouseZoom behavior2 = new MouseZoom();
		behavior2.setTransformGroup(mainTG);
		parent.addChild(behavior2);
		behavior2.setSchedulingBounds(bounds);

		// Create the translate behavior node
		MouseTranslate behavior3 = new MouseTranslate();
		behavior3.setTransformGroup(mainTG);
		parent.addChild(behavior3);
		behavior3.setSchedulingBounds(bounds);


		//light blue
		Appearance ltBlueApp = new Appearance();
		ltBlueApp.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
		Color3f ltBlueColor = new Color3f(0.5f, 1.0f, 1.0f);
		ColoringAttributes ltBlueCA = new ColoringAttributes();
		ltBlueCA.setColor(ltBlueColor);
		ltBlueApp.setColoringAttributes(ltBlueCA);


		// puck
		TransformGroup transPuckTG = new TransformGroup();
		transPuckTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

		Transform3D puckTranslation3D = new Transform3D();
		puckTranslation3D.setTranslation(new Vector3f(0f, 0.0f, .2f)); //0.05
		puckTranslation3D.setRotation(new AxisAngle4d(1.0, 0.0, 0.0, Math.PI / 2.0));
		transPuckTG.setTransform(puckTranslation3D);

		Cylinder puck = new Cylinder(0.22f, 0.075f, ltBlueApp);
		puck.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
		transPuckTG.addChild(puck);
		//scd.setSchedulingBounds(bounds);

		// animation
		TransformGroup animationTG = new TransformGroup();
		animationTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Transform3D axe = new Transform3D();
		axe.setRotation(new AxisAngle4d(0.0, 0.0, 1.0, Math.PI/2));

		alpha = new Alpha(-1, 5000);
		alpha.setStartTime(System.currentTimeMillis());
		posInt = new PositionInterpolator(alpha, animationTG, axe, 0f, 6.1f);
		posInt.setSchedulingBounds(bounds);

		SideCollisionDetector scd = new SideCollisionDetector(transPuckTG,/* axe, alpha, posInt, */bounds);
		// try adding the alpha

		animationTG.addChild(posInt);


		animationTG.addChild(transPuckTG);
		animationTG.addChild(scd);
		mainTG.addChild(animationTG);

		parent.addChild(mainTG);


		parent.compile();
		return parent;
	}


	public static void main(String[] args) {
		Frame frame = new MainFrame(new TestAlpha(), 790, 600);
		frame.setResizable(false);
		frame.addWindowListener(new WindowAdapter() {

			public void windowClosing(WindowEvent winEvent) {
				System.exit(0);
			}
		});	
	}

} //TestAlpha.java
And the collision detector class:


Code:
import java.util.Enumeration;

import javax.media.j3d.Appearance;
import javax.media.j3d.Behavior;
import javax.media.j3d.Bounds;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Node;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnCollisionEntry;
import javax.media.j3d.WakeupOnCollisionExit;
import javax.media.j3d.WakeupOnCollisionMovement;
import javax.media.j3d.WakeupOr;

import javax.vecmath.Color3f;
import javax.vecmath.Vector3d;

import com.sun.j3d.utils.geometry.Primitive;


public class SideCollisionDetector extends Behavior{

	private static final Color3f highlightColor = new Color3f(0.0f, 1.0f, 0.0f);

	private Appearance sideAppearance;
	private static final ColoringAttributes highlight = new ColoringAttributes(
			highlightColor, ColoringAttributes.SHADE_GOURAUD); //smooth shading model

	/** The separate criteria used to wake up this behaviour. */
	private WakeupCriterion[] theCriteria;

	/** The OR of the separate criteria. */
	private WakeupOr oredCriteria;

	/** The transformgroup that is watched for collision. */
	private TransformGroup collidingTG;

	Vector3d v = new Vector3d();

	/**
	 * @param transG
	 * 			TransformGroup that is to be watched for collisions.
	 * @param theBounds
	 * 			Bounds that define the active region for this behaviour.
	 */
	public SideCollisionDetector(TransformGroup transTG, /*Transform3D trans3D, Alpha a, PositionInterpolator positionInt, */Bounds theBounds) {
		collidingTG = transTG;
		//collidingTrans3D = trans3D;
		//collidingAlpha = a;
		//collidingPos = positionInt;
		setSchedulingBounds(theBounds);
		//inCollision = false;
	}

	/**
	 * This creates an entry, exit and movement collision criteria. These are then OR'ed together,
	 * and the wake up condition set to the result.
	 */
	public void initialize() {
		theCriteria = new WakeupCriterion[3];
		theCriteria[0] = new WakeupOnCollisionEntry(collidingTG, WakeupOnCollisionEntry.USE_GEOMETRY);
		theCriteria[1] = new WakeupOnCollisionExit(collidingTG, WakeupOnCollisionEntry.USE_GEOMETRY);
		theCriteria[2] = new WakeupOnCollisionMovement(collidingTG, WakeupOnCollisionEntry.USE_GEOMETRY);
		oredCriteria = new WakeupOr(theCriteria);
		wakeupOn(oredCriteria);

	}

	public void processStimulus(Enumeration criteria) {

		WakeupCriterion theCriterion = (WakeupCriterion) criteria.nextElement();
		if (theCriterion instanceof WakeupOnCollisionEntry) {
			Node theLeaf = ((WakeupOnCollisionEntry) theCriterion)
			.getTriggeringPath().getObject().getParent();
			System.out.println("Collided with " + theLeaf.getUserData());

			sideAppearance = ((Primitive) theLeaf).getAppearance();
			sideAppearance.getColoringAttributes();
			sideAppearance.setColoringAttributes(highlight);

		} else if (theCriterion instanceof WakeupOnCollisionExit) {
			Node theLeaf = ((WakeupOnCollisionExit) theCriterion)
			.getTriggeringPath().getObject().getParent();
			System.out.println("Stopped colliding with  "
					+ theLeaf.getUserData());

			// orange
			Appearance orangeApp = new Appearance();
			orangeApp.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
			Color3f orangeColor = new Color3f(0.988f, 0.800f, 0.080f);
			ColoringAttributes orangeCA = new ColoringAttributes(orangeColor, ColoringAttributes.SHADE_GOURAUD);
			orangeCA.setColor(orangeColor);

			sideAppearance.setColoringAttributes(orangeCA);
		} else {

			Node theLeaf = ((WakeupOnCollisionMovement) theCriterion)
			.getTriggeringPath().getObject().getParent();
			System.out.println("Moved whilst colliding with "
					+ theLeaf.getUserData());

			sideAppearance = ((Primitive) theLeaf).getAppearance();
			sideAppearance.getColoringAttributes();
			sideAppearance.setColoringAttributes(highlight);
		}
		wakeupOn(oredCriteria); 
	}
}
//}