Commits

Michael Ludwig  committed e41b273

Fix the all (or most) display list bugs, and fix the problems in the bound volumes.

  • Participants
  • Parent commits dffee0d

Comments (0)

Files changed (45)

File src/com/ferox/math/AxisAlignedBox.java

 
 	@Override
 	public BoundVolume clone(BoundVolume result) {
-		if (result == null || !(result instanceof AxisAlignedBox))
-			result = new AxisAlignedBox();
-		AxisAlignedBox b = (AxisAlignedBox)result;
-		b.worldMax.set(this.worldMax);
-		b.worldMin.set(this.worldMin);
-		return b;
+		if (result instanceof AxisAlignedBox) {
+			AxisAlignedBox b = (AxisAlignedBox) result;
+			b.worldMax.set(this.worldMax);
+			b.worldMin.set(this.worldMin);
+			return b;
+		} else if (result instanceof BoundSphere) {
+			BoundSphere s = (BoundSphere) result;
+			Vector3f c = s.getCenter();
+			c.sub(this.worldMax, this.worldMin);
+			s.setRadius(c.length() / 2f);
+			this.getCenter(c);
+			
+			return s;
+		} else
+			return this.clone(new AxisAlignedBox());
 	}
 
 	@Override
 				   ((a.worldMax.y >= this.worldMin.y && a.worldMax.y <= this.worldMax.y) || (a.worldMin.y >= this.worldMin.y && a.worldMin.y <= this.worldMax.y)) &&
 				   ((a.worldMax.z >= this.worldMin.z && a.worldMax.z <= this.worldMax.z) || (a.worldMin.z >= this.worldMin.z && a.worldMin.z <= this.worldMax.z));
 		} else if (other instanceof BoundSphere) {
-			Vector3f c = AxisAlignedBox.c.get();
+			/*Vector3f c = AxisAlignedBox.c.get();
 			BoundSphere s = (BoundSphere)other;
 			this.getCenter(c);
 			c.sub(s.getCenter());
 			this.getExtent(c, true, c);
 			c.sub(s.getCenter());
-			return c.lengthSquared() <= s.getRadius() * s.getRadius();
+			return c.lengthSquared() <= s.getRadius() * s.getRadius();*/
+			// Idea taken from "Simple Intersection Tests for Games" by Miguel Gomez - Gamasutra
+			BoundSphere s = (BoundSphere) other;
+			Vector3f sphereCenter = s.getCenter();
+			float totalDistance = 0;
+			
+			float borderDistance;
+			// x (or i == 0)
+			if (sphereCenter.x < this.worldMin.x) {
+				borderDistance = this.worldMin.x - sphereCenter.x;
+				totalDistance += borderDistance * borderDistance;
+			} else if (sphereCenter.x > this.worldMax.x) {
+				borderDistance = sphereCenter.x - this.worldMax.x;
+				totalDistance += borderDistance * borderDistance;
+			}
+			
+			// y (or i == 1)
+			if (sphereCenter.y < this.worldMin.y) {
+				borderDistance = this.worldMin.y - sphereCenter.y;
+				totalDistance += borderDistance * borderDistance;
+			} else if (sphereCenter.y > this.worldMax.y) {
+				borderDistance = sphereCenter.y - this.worldMax.y;
+				totalDistance += borderDistance * borderDistance;
+			}
+			
+			// z (or i == 2)
+			if (sphereCenter.z < this.worldMin.z) {
+				borderDistance = this.worldMin.z - sphereCenter.z;
+				totalDistance += borderDistance * borderDistance;
+			} else if (sphereCenter.z > this.worldMax.z) {
+				borderDistance = sphereCenter.z - this.worldMax.z;
+				totalDistance += borderDistance * borderDistance;
+			}
+			
+			return totalDistance <= s.getRadius() * s.getRadius();
 		} else
 			throw new UnsupportedOperationException("Unable to compute intersection for type: " + other);
 	}
 		this.worldMax.set(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
 		this.worldMin.set(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
 		
-		float fourth, invFourth;
-		for (int i = 0; i < vertexCount; i++) {
-			fourth = vertices.getVertex(i, 3);
-			if (Math.abs(1f - fourth) < .0001f) {
-				this.enclosePoint(vertices.getVertex(i, 0), vertices.getVertex(i, 1), vertices.getVertex(i, 2));
-			} else {
-				invFourth = 1 / fourth;
-				this.enclosePoint(vertices.getVertex(i, 0) * invFourth, vertices.getVertex(i, 1) * invFourth, vertices.getVertex(i, 2) * invFourth);
-			}
-		}
+		for (int i = 0; i < vertexCount; i++)
+			this.enclosePoint(vertices.getVertex(i, 0), vertices.getVertex(i, 1), vertices.getVertex(i, 2));
 	}
 	
 	private void enclosePoint(float x, float y, float z) {

File src/com/ferox/math/BoundSphere.java

 	private static final float radiusEpsilon = 1.00001f;
 	
 	private float radius;
-	private final Vector3f centerOffset;
+	private final Vector3f center;
 	private int lastFailedPlane;
 	
 	/** Create a sphere with 1 radius at the origin. */
 	public BoundSphere() {
 		super();
 		this.radius = 1;
-		this.centerOffset = new Vector3f();
+		this.center = new Vector3f();
 		this.lastFailedPlane = -1;
 	}
 	
 	
 	@Override
 	public BoundVolume clone(BoundVolume result) {
-		if (result == null || !(result instanceof BoundSphere))
-			result = new BoundSphere();
-		BoundSphere s = (BoundSphere)result;
-		s.radius = this.radius;
-		s.centerOffset.set(this.centerOffset);
-		return s;
+		if (result instanceof BoundSphere) {
+			BoundSphere s = (BoundSphere) result;
+			s.radius = this.radius;
+			s.center.set(this.center);
+			
+			return s;
+		} else if (result instanceof AxisAlignedBox) {
+			AxisAlignedBox b = (AxisAlignedBox) result;
+			b.setMin(this.center.x - this.radius, this.center.y - this.radius, this.center.z - this.radius);
+			b.setMax(this.center.x + this.radius, this.center.y + this.radius, this.center.z + this.radius);
+			
+			return b;
+		} else
+			return this.clone(new BoundSphere());
 	}
 	
 	/** Set the radius, clamps it to be above .00001. */
 	/** Copy the vector into the center location.  If center is null, sets it to the origin. */
 	public void setCenter(Vector3f center) {
 		if (center == null)
-			this.centerOffset.set(0f, 0f, 0f);
+			this.center.set(0f, 0f, 0f);
 		else
-			this.centerOffset.set(center);
+			this.center.set(center);
 	}
 	
 	/** Set the center location of the sphere. */
 	public void setCenter(float x, float y, float z) {
-		this.centerOffset.set(x, y, z);
+		this.center.set(x, y, z);
 	}
 	
 	/** Get the center location of the sphere. */
 	public Vector3f getCenter() {
-		return this.centerOffset;
+		return this.center;
 	}
 	
 	/** Get the radius of the sphere. */
 	public void applyTransform(Transform trans) {
 		if (trans == null)
 			return;
-		trans.transform(this.centerOffset);
+		trans.transform(this.center);
 		float s = trans.getScale();
 		this.radius *= s;
 	}
 	}
 
 	private void mergeAABB(AxisAlignedBox aabb) {
-		Vector3f diff = BoundSphere.tempA.get();
-		Vector3f tempB = BoundSphere.tempB.get();
+		// tempA is used in merge()
+		Vector3f center = BoundSphere.tempB.get();
+		Vector3f extents = BoundSphere.tempC.get();
 		
-		aabb.getCenter(diff);
-		diff.sub(this.centerOffset);
-		aabb.getExtent(diff, false, tempB);
-		diff.sub(tempB, this.centerOffset);
-		float dist = diff.length();
-		
-		if (dist > this.radius) {
-			float or = this.radius;
-			this.radius = (dist + or) / 2f;
-			this.centerOffset.scaleAdd((this.radius - or) / dist, diff, this.centerOffset);
-		}
+		aabb.getCenter(center);
+		extents.sub(aabb.getMax(), aabb.getMin());
+		this.merge(center, extents.length() / 2f);
 	}
 	
 	private void mergeSphere(BoundSphere sphere) {
+		this.merge(sphere.center, sphere.radius);
+	}
+	
+	private void merge(Vector3f center, float radius) {
 		Vector3f diff = BoundSphere.tempA.get();
 		
-		diff.sub(this.centerOffset, sphere.centerOffset);
+		diff.sub(center, this.center);
 		float dist = diff.length();
 		
-		if (dist + sphere.radius > this.radius) {
-			this.radius = (dist + sphere.radius + this.radius) / 2f;
-			this.centerOffset.scaleAdd((this.radius - sphere.radius) / dist, diff, sphere.centerOffset);
+		if (dist != 0f) {
+			if (radius > this.radius + dist) {
+				// this sphere is inside other sphere
+				this.radius = radius;
+				this.center.set(center);
+			} else if (dist + radius > this.radius) {
+				// other sphere is at least partially outside of us
+				float or = this.radius;
+				this.radius = (dist + radius + this.radius) / 2f;
+				this.center.scaleAdd((this.radius - or) / dist, diff, this.center);
+			} // else we already enclose it, so do nothing
+		} else {
+			// don't need to move the center, just take the largest radius
+			this.radius = Math.max(radius, this.radius);
 		}
 	}
 
 				plane = i;
 
 			if ((planeState & (1 << plane)) == 0) {
-				dist = view.getWorldPlane(plane).signedDistance(this.centerOffset);
+				dist = view.getWorldPlane(plane).signedDistance(this.center);
 
 				if (dist < -this.radius) {
 					view.setPlaneState(planeState);
 			throw new NullPointerException("Can't compute extent for a null direction");
 		if (result == null)
 			result = new Vector3f();
-		result.set(this.centerOffset);
+		result.set(this.center);
 
 		if (reverse) {
 			result.x -= dir.x * this.radius;
 			Vector3f cross = BoundSphere.tempA.get();
 			
 			BoundSphere s = (BoundSphere)other;
-			cross.sub(this.centerOffset, s.centerOffset);
+			cross.sub(this.center, s.center);
 			return cross.lengthSquared() <= (this.radius + s.radius) * (this.radius + s.radius);
 		} else
 			throw new UnsupportedOperationException("Unable to compute intersection between the given type: " + other);
 		 switch (b) {
 	        case 0:
 	            this.radius = 0;
-	            this.centerOffset.set(0f, 0f, 0f);
+	            this.center.set(0f, 0f, 0f);
 	            break;
 	        case 1:
 	            this.radius = 1f - radiusEpsilon;
-	            populateFromArray(this.centerOffset, points, ap - 1);
+	            populateFromArray(this.center, points, ap - 1);
 	            break;
 	        case 2:
 	            populateFromArray(tempA, points, ap - 1);
 	        }
 	        for (int i = 0; i < p; i++) {
 	            populateFromArray(tempA, points, i + ap);
-	            float d = ((tempA.x - this.centerOffset.x) * (tempA.x - this.centerOffset.x) + 
-		            	   (tempA.y - this.centerOffset.y) * (tempA.y - this.centerOffset.y) +  
-		            	   (tempA.z - this.centerOffset.z) * (tempA.z - this.centerOffset.z));
+	            float d = ((tempA.x - this.center.x) * (tempA.x - this.center.x) + 
+		            	   (tempA.y - this.center.y) * (tempA.y - this.center.y) +  
+		            	   (tempA.z - this.center.z) * (tempA.z - this.center.z));
 	            if (d - (this.radius * this.radius) > radiusEpsilon - 1f) {
 	            	for (int j = i; j > 0; j--) {
 	                    populateFromArray(tempB, points, j + ap);
 		
 		float denom = 2.0f * (tA.x * (tB.y * tC.z - tC.y * tB.z) - tB.x * (tA.y * tC.z - tC.y * tA.z) + tC.x * (tA.y * tB.z - tB.y * tA.z));
 		if (denom == 0) {
-			this.centerOffset.set(0f, 0f, 0f);
+			this.center.set(0f, 0f, 0f);
 			this.radius = 0f;
 		} else {
 			cross.cross(tA, tB); cross.scale(tC.lengthSquared());
 			cross.scale(1f / denom);
 
 	        this.radius = cross.length() * radiusEpsilon;
-	        this.centerOffset.add(o, cross);
+	        this.center.add(o, cross);
 		}
 	}
 
 		float denom = 2f * cross.lengthSquared();
 
 		if (denom == 0) {
-			this.centerOffset.set(0f, 0f, 0f);
+			this.center.set(0f, 0f, 0f);
 			this.radius = 0f;
 		} else {
 			tC.cross(cross, tA); tC.scale(tB.lengthSquared());
 			tC.add(tD); tC.scale(1f / denom);
 
 			this.radius = tC.length() * radiusEpsilon;
-			this.centerOffset.add(o, tC);
+			this.center.add(o, tC);
 		}
 	}
 
 	private void setSphere(Vector3f o, Vector3f a) {
 		this.radius = (float)Math.sqrt(((a.x - o.x) * (a.x - o.x) + (a.y - o.y) * (a.y - o.y) + (a.z - o.z) * (a.z - o.z)) / 4f) + radiusEpsilon - 1f;
 	    
-		this.centerOffset.scale(.5f, o);
-	    this.centerOffset.scaleAdd(.5f, a, this.centerOffset);
+		this.center.scale(.5f, o);
+	    this.center.scaleAdd(.5f, a, this.center);
 	}
 
 	private static void fillPointsArray(float[] points, Boundable verts) {
 		int vertexCount = verts.getVertexCount();
 		
-		float fourth, invFourth;
 		for (int i = 0; i < vertexCount; i++) {
-			fourth = verts.getVertex(i, 3);
-			if (Math.abs(1f - fourth) < .0001f) {
-				points[i * 3] = verts.getVertex(i, 0);
-				points[i * 3 + 1] = verts.getVertex(i, 1);
-				points[i * 3 + 2] = verts.getVertex(i, 2);
-			} else {
-				invFourth = 1 / fourth;
-				points[i * 3] = verts.getVertex(i, 0) * invFourth;
-				points[i * 3 + 1] = verts.getVertex(i, 1) * invFourth;
-				points[i * 3 + 2] = verts.getVertex(i, 2) * invFourth;
-			}
+			points[i * 3] = verts.getVertex(i, 0);
+			points[i * 3 + 1] = verts.getVertex(i, 1);
+			points[i * 3 + 2] = verts.getVertex(i, 2);
 		}
 	}
 

File src/com/ferox/math/BoundVolume.java

  *
  */
 public interface BoundVolume {
-	/** Clone this BoundVolume into result.  If result is not the same type, or is null, create a new instance of the correct type
-	 * and return that. */
+	/** Clone this BoundVolume into result.  If result is of an unsupported type or null, create a new BoundVolume
+	 * of this volume's type to store the clone. */
 	public BoundVolume clone(BoundVolume result);
 	
 	/** Grow this BoundVolume to completely enclose the given BoundVolume. Should throw an exception if it doesn't know how to

File src/com/ferox/math/Boundable.java

 	public static final int X_COORD = 0;
 	public static final int Y_COORD = 1;
 	public static final int Z_COORD = 2;
-	public static final int W_COORD = 3;
 	
 	/** Store this Boundable's bounds into result. It is recommended to
 	 * use some caching mechanism so bounds don't have to be recomputed each frame. */
 	 * x = 0
 	 * y = 1
 	 * z = 2 (if present) default = 0
-	 * w = 3 (if present) default = 1 -> should never return 0, causes undefined results
 	 * 
 	 * coord must be between 0-3, if not fail.  If this Boundable does
-	 * not have a 3rd or 4th dimension, return the default value for that coord. 
-	 * Fail if index isn't between 0 and getVertexCount() - 1. */
+	 * not have a 3rd dimension, return the default value for that coord. 
+	 * 
+	 * Fail if index isn't between 0 and getVertexCount() - 1, or if coord isn't 0, 1, or 2. */
 	public float getVertex(int index, int coord) throws IllegalArgumentException;
 }

File src/com/ferox/renderer/RenderQueue.java

 	 * Repeated calls to flush() without intermittent calls to clear() should perform the
 	 * same operations on the Renderer as the first call to flush().
 	 * 
-	 * Do nothing if renderer or view are null. */
-	public void flush(Renderer renderer, View view) throws RenderException;
+	 * Do nothing if renderer or view are null.
+	 * Return the total number of polygons rendered. */
+	public int flush(Renderer renderer, View view) throws RenderException;
 	
 	/** Add the given atom to be rendered by this RenderQueue.  If an atom is added twice, then 
 	 * that atom will be rendered twice. It is the user of a RenderQueue's responsibility to

File src/com/ferox/renderer/Renderer.java

 	/** Return a single Geometry instance that represents the list of render atoms.  If
 	 * submitted with renderAtom(), this rendered Geometry will represent the what would
 	 * be rendered if the list of atoms were submitted to renderAtom() in order (accounting
-	 * for the current viewport and transform).
-	 * 
-	 * Because the returned Geometry uses the Appearances stored in atoms, it must return
-	 * true from isAppearanceIgnored().
-	 * If the specified list is empty, return null.
+	 * for the current viewport and transform).  Any subsequent changes to the appearances,
+	 * positions, or geometries will not be visible in the returned Geometry.
 	 * 
 	 * The returned Geometry can usually be rendered much faster than if the list were
 	 * rendered.  It is intended for static atom's, and because of this, updating the returned
 	 * geometry has no effect (it must still be cleaned-up, but subsequent updates will then
 	 * fail).  
 	 * 
+	 * If the specified list is empty, return null.
+	 * 
+	 * Because the returned Geometry uses the Appearances stored in atoms, it must return
+	 * true from isAppearanceIgnored().  Depending on what is submitted to the Renderer,
+	 * influence atoms can still affect the rendering.  If a state's role is never used during
+	 * the compile process, it will not be ignored.
+	 * 
+	 * Depending on the implementation, there may be undefined results with states that rely,
+	 * implicitly on the View's current view transform (such as individual lights, or EYE
+	 * texture coord generation).  This is because the low-level operations apply the current
+	 * view transform when the call is executed.  Thus changes to the view will not be visible
+	 * in the returned Geometry's appearances.
+	 * 
 	 * Implementations do not need to provide a useful implementation of getVertex(), so
 	 * programmers should use getBounds() instead of bounds.enclose() to get the BoundVolume
 	 * for this Geometry.
 	 * The render atom will not be rendered if it has a null geometry or null transform.  However, influences
 	 * must still be cleared in this case.
 	 * 
+	 * Return the number of polygons rendered for this atom.
+	 * 
 	 * Throw an exception if the renderer is destroyed, if it's not rendering render passes, if
 	 * atom is null, or if any geometries, resources or states used by the atom are unsupported. */
-	public void renderAtom(RenderAtom atom) throws RenderException;
+	public int renderAtom(RenderAtom atom) throws RenderException;
 }

File src/com/ferox/renderer/impl/AbstractRenderer.java

  * @author Michael Ludwig
  *
  */
-// TODO: remove currentPass == null/frameStates == null checks and when necessary just
-// assign them dummy values so that they still function.
-// TODO: in compile() make sure there is better default state before calling the driver
 public class AbstractRenderer implements Renderer {
 	/** Represents all possible states of an AbstractRenderer. */
 	public static enum RenderState {
 			ResourceData data = new ResourceData(AbstractRenderer.this, compiler.getDriver());
 			// setup the default state
 			transform.setView(null, 0, 0);
-			setAppearance(null);
+			restoreDefaultState(null);
 			this.compiledGeom = compiler.compile(this.atoms, data);
 			this.compiledGeom.setResourceData(data);
 		}
 	}
 	
 	@Override
-	public final void renderAtom(RenderAtom atom) throws RenderException {
+	public final int renderAtom(RenderAtom atom) throws RenderException {
 		this.ensure(RenderState.RENDERING);
 		if (atom == null)
 			throw new RenderException("Cannot call renderAtom with a null RenderAtom");
 
 			// set the model transform and render
 			this.transform.setModelTransform(model); 
-			this.frameStats.add(1, geom.getVertexCount(), driver.render(geom, gd)); // update stats
+			int polyCount = driver.render(geom, gd);
+			this.frameStats.add(1, geom.getVertexCount(), polyCount); // update stats
 			this.transform.resetModel();
 			
-			return; // end now
+			return polyCount; // end now
 		}
 		
 		if (this.getResourceDriver(geom.getClass()) == null)
 			// reset the influence atoms for the next render atom
 			for (int i = 0; i < this.stateTypeCounter; i++)
 				this.stateDrivers[i].reset();
+			
+			return 0;
 		}
 	}
 	
 		}
 	}
 	
-	/** Adjust the current record so that the given Appearance
-	 * is active.  If a is null, the record is set to be the
-	 * default appearance used when rendering atoms.
+	/** Restore all state modified by the drivers of the given Roles, so
+	 * that they are completely back to the default state.  
 	 * 
-	 * This will ignore any queued influence atoms.
+	 * If the Roles[] array is null, then all currently used drivers
+	 * will be restored to their defaults.
 	 * 
 	 * This can only be called when the renderer is in the
 	 * RENDERING state and from the gl thread. */
-	public final void setAppearance(Appearance a) {
+	public final void restoreDefaultState(Role[] roles) {
 		this.ensure(RenderState.RENDERING);
 		if (!this.factory.isGraphicsThread())
 			throw new RenderException("renderAtom() cannot be invoked on this thread");
-		
-		// reset, so we're on a clean slate
-		for (int i = 0; i < this.stateTypeCounter; i++)
-			this.stateDrivers[i].reset();
-		
-		this.queueAppearance(this.dfltAppearance);
-		if (a != null)
-			this.queueAppearance(a);
 
-		// apply the queued appearances
-		for (int i = 0; i < this.stateTypeCounter; i++)
-			this.stateDrivers[i].doApply();
+		if (roles == null) {
+			// do it for all roles
+			for (int i = 0; i < this.stateTypeCounter; i++)
+				this.stateDrivers[i].restoreDefaults();
+		} else {
+			// only restore defaults for roles we recognize in the array
+			Integer index;
+			for (int i = 0; i < roles.length; i++) {
+				index = this.stateTypeIdMap.get(roles[i]);
+				if (index != null) // we have a driver for this
+					this.stateDrivers[index.intValue()].restoreDefaults();
+			}
+		}
 	}
 	
 	/** To be used by CompiledGeometryDrivers to forcibly reset the

File src/com/ferox/renderer/impl/StateDriver.java

 	 * provide a means by which to get at this information. */
 	public void doApply();
 	
+	/** Perform a similar operation to doApply() except it must guarantee that all
+	 * state modified this driver has been reset to the defaults (not just perform
+	 * the same operations as the default).
+	 * 
+	 * This should ignore and reset any queud States, and can perform low-level
+	 * operations.
+	 * 
+	 * It is primarily used for compiling and rendering compiled geometries, which
+	 * need a very predictable state record to function correctly. */
+	public void restoreDefaults();
+	
 	/** Clear any queued states that weren't used by a call to doApply().
 	 * This could be called because of an end of the frame, or because
 	 * a render atom is ignored, or to clean-up after an exception was thrown.

File src/com/ferox/renderer/impl/jogl/JoglSurfaceFactory.java

 
 				this.currentDrawable = renderAction.getGLAutoDrawable();
 				this.currentRecord = renderAction.getStateRecord();
+				//this.currentGL = (this.debugGL ? new TraceGL(new DebugGL(this.currentDrawable.getGL()), System.out) : this.currentDrawable.getGL());
 				this.currentGL = (this.debugGL ? new DebugGL(this.currentDrawable.getGL()) : this.currentDrawable.getGL());
 
 				renderAction.render();

File src/com/ferox/renderer/impl/jogl/drivers/DisplayListGeometry.java

 		if (bounds instanceof AxisAlignedBox) {
 			this.box = (AxisAlignedBox) bounds;
 			this.sphere = new BoundSphere();
-
-			this.sphere.setCenter(this.box.getCenter(null));
-			this.sphere.setRadius(0f);
-			this.sphere.enclose(this.box);
+			this.box.clone(this.sphere);
 		} else if (bounds instanceof BoundSphere) {
 			this.sphere = (BoundSphere) bounds;
 			this.box = new AxisAlignedBox();
-
-			this.box.setMax(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
-			this.box.setMin(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
-			this.box.enclose(this.sphere);
+			this.sphere.clone(this.box);
 		} else {
 			// use default bounds
 			this.sphere = new BoundSphere();
 
 	@Override
 	public float getVertex(int index, int coord) throws IllegalArgumentException {
-		return 0f;
+		throw new UnsupportedOperationException("getVertex() not implemented for compiled geometry");
 	}
 
 	@Override

File src/com/ferox/renderer/impl/jogl/drivers/JoglAlphaTestStateDriver.java

 	public JoglAlphaTestStateDriver(JoglSurfaceFactory factory) {
 		super(null, AlphaTest.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		PixelOpRecord pr = record.pixelOpRecord;
+		
+		if (pr.enableAlphaTest) {
+			pr.enableAlphaTest = false;
+			gl.glDisable(GL.GL_ALPHA_TEST);
+		}
+		if (pr.alphaTestFunc != GL.GL_ALWAYS || pr.alphaTestRef != 0f) {
+			pr.alphaTestFunc = GL.GL_ALWAYS;
+			pr.alphaTestRef = 0f;
+			gl.glAlphaFunc(GL.GL_ALWAYS, 0f);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, AlphaTest nextState) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglBlendModeStateDriver.java

 	public JoglBlendModeStateDriver(JoglSurfaceFactory factory) {
 		super(null, BlendMode.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		PixelOpRecord pr = record.pixelOpRecord;
+		
+		if (pr.enableBlend) {
+			pr.enableBlend = false;
+			gl.glDisable(GL.GL_BLEND);
+		}
+		
+		if (pr.blendEquationAlpha != GL.GL_FUNC_ADD || pr.blendEquationRgb != GL.GL_FUNC_ADD) {
+			pr.blendEquationAlpha = GL.GL_FUNC_ADD;
+			pr.blendEquationRgb = GL.GL_FUNC_ADD;
+			gl.glBlendEquation(GL.GL_FUNC_ADD);
+		}
+		
+		if (pr.blendDstAlpha != GL.GL_ZERO || pr.blendDstRgb != GL.GL_ZERO ||
+			pr.blendSrcAlpha != GL.GL_ONE || pr.blendSrcRgb != GL.GL_ONE) {
+			pr.blendDstAlpha = GL.GL_ZERO;
+			pr.blendDstRgb = GL.GL_ZERO;
+			pr.blendSrcAlpha = GL.GL_ZERO;
+			pr.blendSrcRgb = GL.GL_ZERO;
+			gl.glBlendFunc(GL.GL_ONE, GL.GL_ZERO);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, BlendMode nextState) {
 			// we don't have to bother checking or updating the blend color
 		}
 	}
-	
-	
 }

File src/com/ferox/renderer/impl/jogl/drivers/JoglDepthTestStateDriver.java

 	public JoglDepthTestStateDriver(JoglSurfaceFactory factory) {
 		super(new DepthTest(), DepthTest.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		PixelOpRecord pr = record.pixelOpRecord;
+		FramebufferRecord fr = record.frameRecord;
+		
+		if (pr.enableDepthTest) {
+			pr.enableDepthTest = false;
+			gl.glDisable(GL.GL_DEPTH_TEST);
+		}
+		
+		if (pr.depthFunc != GL.GL_LESS) {
+			pr.depthFunc = GL.GL_LESS;
+			gl.glDepthFunc(GL.GL_LESS);
+		}
+		
+		if (!fr.depthWriteMask) {
+			fr.depthWriteMask = true;
+			gl.glDepthMask(true);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, DepthTest nextState) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglDisplayListGeometryDriver.java

 package com.ferox.renderer.impl.jogl.drivers;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.media.opengl.GL;
 
 import com.ferox.renderer.RenderAtom;
+import com.ferox.renderer.View;
 import com.ferox.renderer.impl.AbstractRenderer;
 import com.ferox.renderer.impl.CompiledGeometryDriver;
 import com.ferox.renderer.impl.GeometryDriver;
 import com.ferox.renderer.impl.ResourceData;
 import com.ferox.renderer.impl.ResourceData.Handle;
 import com.ferox.renderer.impl.jogl.JoglSurfaceFactory;
+import com.ferox.renderer.util.StateSortingRenderQueue;
 import com.ferox.resource.Geometry;
 import com.ferox.resource.Resource;
 import com.ferox.resource.Resource.Status;
+import com.ferox.state.Appearance;
+import com.ferox.state.State;
+import com.ferox.state.State.Role;
 
 /** Uses display lists to compile a list of render atoms into one Geometry.
  * 
 	private static class DisplayListHandle implements Handle {
 		private static int displayListIdCounter = 1;
 
+		private Role[] modifiedRoles;
 		private int id = displayListIdCounter++;
 		private int polyCount;
 		
 	
 	@Override
 	public Geometry compile(List<RenderAtom> atoms, ResourceData data) {
-		GL gl = this.factory.getGL();
-		AbstractRenderer renderer = this.factory.getRenderer();
-
+		// prepare for submission to the display list
+		View dummyView = new View();
+		dummyView.updateView();
+		
+		Set<Role> allRoles = new HashSet<Role>();
+		StateSortingRenderQueue queue = new StateSortingRenderQueue();
+		
+		// compute the used Roles, and put everything in a sorting queue
+		Appearance a;
+		List<State> s;
+		int numAtoms = atoms.size();
+		for (int i = 0; i < numAtoms; i++) {
+			queue.add(atoms.get(i));
+			
+			// add all roles
+			a = atoms.get(i).getAppearance();
+			if (a != null) {
+				// use roles present
+				s = a.getStates();
+				for (int u = 0; u < s.size(); u++)
+					allRoles.add(s.get(u).getRole());
+			}
+		}
+		
+		// add roles for the default appearance
+		allRoles.add(Role.MATERIAL);
+		allRoles.add(Role.DEPTH_TEST);
+		allRoles.add(Role.POLYGON_DRAW_STYLE);
 		
 		DisplayListGeometry geom = new DisplayListGeometry(atoms);
 		DisplayListHandle handle = new DisplayListHandle();
+		handle.modifiedRoles = allRoles.toArray(new Role[allRoles.size()]);
 		data.setHandle(handle);
 		
+		AbstractRenderer renderer = this.factory.getRenderer();
+		GL gl = this.factory.getGL();
+
 		// we have a clean slate, so just make the list
 		gl.glNewList(handle.id, GL.GL_COMPILE);
-			int numAtoms = atoms.size();
-			for (int i = 0; i < numAtoms; i++) {
-				renderer.renderAtom(atoms.get(i));
-			}
+			handle.polyCount = queue.flush(renderer, dummyView);
 			// reset the record in the dl, so that we have a predictable record when rendering
 			renderer.resetGeometryDriver();
-			renderer.setAppearance(null);
+			renderer.restoreDefaultState(handle.modifiedRoles);
 		gl.glEndList();
 		return geom;
 	}
 		GL gl = this.factory.getGL();
 		DisplayListHandle h = (DisplayListHandle) data.getHandle();
 		
-		// we know the current record is the default appearance
+		// make necessary state back to the default
+		this.factory.getRenderer().restoreDefaultState(h.modifiedRoles);
 		gl.glCallList(h.id);
 		
 		return h.polyCount;

File src/com/ferox/renderer/impl/jogl/drivers/JoglFogColorStateDriver.java

  *
  */
 public class JoglFogColorStateDriver extends SingleStateDriver<Fog> {
+	private static final Color DEFAULT_COLOR = new Color(0f, 0f, 0f, 0f);
+	
 	public JoglFogColorStateDriver(JoglSurfaceFactory factory) {
 		super(new Fog(new Color(), 0, 1, 1, FogEquation.EXP, Quality.DONT_CARE), Fog.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		ColoringRecord cr = record.colorRecord;
+		HintRecord hr = record.hintRecord;
+		
+		this.setRecord(gl, cr, hr, DEFAULT_COLOR, 1f, 0f, 1f, GL.GL_EXP, GL.GL_DONT_CARE);
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, Fog nextState) {
 		ColoringRecord cr = record.colorRecord;
 		HintRecord hr = record.hintRecord;
 		
+		this.setRecord(gl, cr, hr, nextState.getColor(), nextState.getDensity(), 
+					   nextState.getStartDistance(), nextState.getEndDistance(), 
+					   EnumUtil.getGLFogMode(nextState.getEquation()), 
+					   EnumUtil.getGLHint(nextState.getQuality()));
+	}
+	
+	private void setRecord(GL gl, ColoringRecord cr, HintRecord hr, Color color, float density, float start, float end, int mode, int hint) {
 		// color
-		if (!nextState.getColor().equals(cr.fogColor)) {
-			nextState.getColor().get(cr.fogColor);
+		if (!color.equals(cr.fogColor)) {
+			color.get(cr.fogColor);
 			gl.glFogfv(GL.GL_FOG_COLOR, cr.fogColor, 0);
 		}
 		// density
-		if (nextState.getDensity() != cr.fogDensity) {
-			cr.fogDensity = nextState.getDensity();
+		if (density != cr.fogDensity) {
+			cr.fogDensity = density;
 			gl.glFogf(GL.GL_FOG_DENSITY, cr.fogDensity);
 		}
 		// start
-		if (nextState.getStartDistance() != cr.fogStart) {
-			cr.fogStart = nextState.getStartDistance();
+		if (start != cr.fogStart) {
+			cr.fogStart = start;
 			gl.glFogf(GL.GL_FOG_START, cr.fogStart);
 		}
 		// end
-		if (nextState.getEndDistance() != cr.fogEnd) {
-			cr.fogEnd = nextState.getEndDistance();
+		if (end != cr.fogEnd) {
+			cr.fogEnd = end;
 			gl.glFogf(GL.GL_FOG_END, cr.fogEnd);
 		}
 		// mode
-		int mode = EnumUtil.getGLFogMode(nextState.getEquation());
 		if (mode != cr.fogMode) {
 			cr.fogMode = mode;
 			gl.glFogi(GL.GL_FOG_MODE, mode);
 		}
 		// hint
-		int hint = EnumUtil.getGLHint(nextState.getQuality());
 		if (hint != hr.fogHint) {
 			hr.fogHint = hint;
 			gl.glHint(GL.GL_FOG_HINT, hint);
 		}
-		// fog src
-		if (cr.fogCoordSrc != GL.GL_FRAGMENT_DEPTH) {
-			cr.fogCoordSrc = GL.GL_FRAGMENT_DEPTH;
-			gl.glFogi(GL.GL_FOG_COORD_SRC, GL.GL_FRAGMENT_DEPTH);
-		}
 	}
 }

File src/com/ferox/renderer/impl/jogl/drivers/JoglFogEnablerStateDriver.java

 	public JoglFogEnablerStateDriver(JoglSurfaceFactory factory) {
 		super(null, FogReceiver.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		ColoringRecord cr = record.colorRecord;
+		setFogEnabled(gl, cr, false);
+		if (cr.fogCoordSrc != GL.GL_FRAGMENT_DEPTH) {
+			cr.fogCoordSrc = GL.GL_FRAGMENT_DEPTH;
+			gl.glFogi(GL.GL_FOG_COORD_SRC, GL.GL_FRAGMENT_DEPTH);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, FogReceiver nextState) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglGlobalLightingStateDriver.java

  *
  */
 public class JoglGlobalLightingStateDriver extends SingleStateDriver<LightReceiver> {
+	private static final Color DEFAULT_AMB = new Color(.2f, .2f, .2f, 1f);
+	
 	public JoglGlobalLightingStateDriver(JoglSurfaceFactory factory) {
 		super(null, LightReceiver.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		LightingRecord lr = record.lightRecord;
+		
+		if (lr.enableLighting) {
+			lr.enableLighting = false;
+			gl.glDisable(GL.GL_LIGHTING);
+		}
+		
+		this.setLightModel(gl, lr, DEFAULT_AMB, false, GL.GL_SINGLE_COLOR, false);
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, LightReceiver nextState) {
 				gl.glEnable(GL.GL_LIGHTING);
 			}
 			
-			// ambient color
-			Color c = nextState.getGlobalAmbient();
-			if (!c.equals(lr.lightModelAmbient)) {
-				c.get(lr.lightModelAmbient);
-				gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, lr.lightModelAmbient, 0);
-			}
-			// local viewer
-			if (nextState.isLocalViewer() != lr.lightModelLocalViewer) {
-				lr.lightModelLocalViewer = nextState.isLocalViewer();
-				gl.glLightModeli(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, (lr.lightModelLocalViewer ? GL.GL_TRUE : GL.GL_FALSE));
-			}
-			// separate specular
-			int control = (nextState.getSeparateSpecular() ? GL.GL_SEPARATE_SPECULAR_COLOR : GL.GL_SINGLE_COLOR);
-			if (control != lr.lightModelColorControl) {
-				lr.lightModelColorControl = control;
-				gl.glLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, control);
-			}
-			// two-sided lighting
-			if (nextState.getTwoSidedLighting() != lr.lightModelTwoSided) {
-				lr.lightModelTwoSided = nextState.getTwoSidedLighting();
-				gl.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, (lr.lightModelTwoSided ? GL.GL_TRUE : GL.GL_FALSE));
-			}
+			this.setLightModel(gl, lr, nextState.getGlobalAmbient(), nextState.isLocalViewer(), 
+							   (nextState.getSeparateSpecular() ? GL.GL_SEPARATE_SPECULAR_COLOR : GL.GL_SINGLE_COLOR),
+							   nextState.getTwoSidedLighting());
+		}
+	}
+	
+	private void setLightModel(GL gl, LightingRecord lr, Color ambient, boolean localViewer, int colorControl, boolean twoSided) {
+		// ambient color
+		if (!ambient.equals(lr.lightModelAmbient)) {
+			ambient.get(lr.lightModelAmbient);
+			gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, lr.lightModelAmbient, 0);
+		}
+		// local viewer
+		if (localViewer != lr.lightModelLocalViewer) {
+			lr.lightModelLocalViewer = localViewer;
+			gl.glLightModeli(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, (lr.lightModelLocalViewer ? GL.GL_TRUE : GL.GL_FALSE));
+		}
+		// separate specular
+		if (colorControl != lr.lightModelColorControl) {
+			lr.lightModelColorControl = colorControl;
+			gl.glLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, colorControl);
+		}
+		// two-sided lighting
+		if (twoSided != lr.lightModelTwoSided) {
+			lr.lightModelTwoSided = twoSided;
+			gl.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, (lr.lightModelTwoSided ? GL.GL_TRUE : GL.GL_FALSE));
 		}
 	}
 }

File src/com/ferox/renderer/impl/jogl/drivers/JoglLightingStateDriver.java

 public class JoglLightingStateDriver extends MultiStateDriver<Light> {
 	private static final int MAX_LIGHTS = 8;
 	
+	private static final Color DEFAULT_L0_DIFF_SPEC = new Color(1f, 1f, 1f, 1f);
+	private static final Color DEFAULT_Li_COLOR = new Color(0f, 0f, 0f, 1f);
+	
 	private final Light[] appliedLights;
 	private Vector3f p;
 	
 	}
 	
 	@Override
+	protected void restore(GL gl, JoglStateRecord record, int unit) {
+		LightRecord lr = record.lightRecord.lightUnits[unit];
+		int glUnit = GL.GL_LIGHT0 + unit;
+		
+		if (lr.enabled) {
+			lr.enabled = false;
+			gl.glDisable(glUnit);
+		}
+		
+		this.setLightColors(gl, lr, glUnit, DEFAULT_Li_COLOR, (unit == 0 ? DEFAULT_L0_DIFF_SPEC : DEFAULT_Li_COLOR), 
+							(unit == 0 ? DEFAULT_L0_DIFF_SPEC : DEFAULT_Li_COLOR));
+		this.setSpotParameters(gl, lr, glUnit, 180f, 1f, 0f, 0f);
+		
+		lr.spotDirection[0] = 0f; lr.spotDirection[1] = 0f; lr.spotDirection[2] = -1f;
+		gl.glLightfv(glUnit, GL.GL_SPOT_DIRECTION, lr.spotDirection, 0);
+		
+		lr.position[0] = 0f; lr.position[1] = 0f; lr.position[2] = 1f; lr.position[3] = 0f;
+		gl.glLightfv(glUnit, GL.GL_POSITION, lr.position, 0);
+	}
+	
+	@Override
 	protected void apply(GL gl, JoglStateRecord record, int unit, Light next) {
 		LightRecord lr = record.lightRecord.lightUnits[unit];
 		int glUnit = GL.GL_LIGHT0 + unit;
 				this.appliedLights[unit] = next;
 				
 				// set lighting colors
-				// ambient
-				Color c = next.getAmbient();
-				if (!c.equals(lr.ambient)) {
-					c.get(lr.ambient);
-					gl.glLightfv(glUnit, GL.GL_AMBIENT, lr.ambient, 0);
-				}
-				// diffuse
-				c = next.getDiffuse();
-				if (!c.equals(lr.diffuse)) {
-					c.get(lr.diffuse);
-					gl.glLightfv(glUnit, GL.GL_DIFFUSE, lr.diffuse, 0);
-				}
-				// specular
-				c = next.getSpecular();
-				if (!c.equals(lr.specular)) {
-					c.get(lr.specular);
-					gl.glLightfv(glUnit, GL.GL_SPECULAR, lr.specular, 0);
-				}
+				this.setLightColors(gl, lr, glUnit, next.getAmbient(), next.getDiffuse(), next.getSpecular());
 
 				// setup other properties
 				if (next instanceof SpotLight)
 		}
 	}
 	
+	private void setLightColors(GL gl, LightRecord lr, int glUnit, Color amb, Color diff, Color spec) {
+		// ambient
+		if (!amb.equals(lr.ambient)) {
+			amb.get(lr.ambient);
+			gl.glLightfv(glUnit, GL.GL_AMBIENT, lr.ambient, 0);
+		}
+		// diffuse
+		if (!diff.equals(lr.diffuse)) {
+			diff.get(lr.diffuse);
+			gl.glLightfv(glUnit, GL.GL_DIFFUSE, lr.diffuse, 0);
+		}
+		// specular
+		if (!spec.equals(lr.specular)) {
+			spec.get(lr.specular);
+			gl.glLightfv(glUnit, GL.GL_SPECULAR, lr.specular, 0);
+		}
+	}
+	
+	private void setSpotParameters(GL gl, LightRecord lr, int glUnit, float spotCutoff, float constant, float linear, float quad) {
+		// spotCutoff
+		if (lr.spotCutoff != spotCutoff) {
+			lr.spotCutoff = spotCutoff;
+			gl.glLightf(glUnit, GL.GL_SPOT_CUTOFF, lr.spotCutoff);
+		}
+		// constant att.
+		if (lr.constantAttenuation != constant) {
+			lr.constantAttenuation = constant;
+			gl.glLightf(glUnit, GL.GL_CONSTANT_ATTENUATION, lr.constantAttenuation);
+		}
+		// linear att.
+		if (lr.linearAttenuation != linear) {
+			lr.linearAttenuation = linear;
+			gl.glLightf(glUnit, GL.GL_LINEAR_ATTENUATION, lr.linearAttenuation);
+		}
+		// quadratic att.
+		if (lr.quadraticAttenuation != quad) {
+			lr.quadraticAttenuation = quad;
+			gl.glLightf(glUnit, GL.GL_QUADRATIC_ATTENUATION, lr.quadraticAttenuation);
+		}
+	}
+	
 	private void setupSpotLight(GL gl, LightRecord lr, int glUnit, SpotLight light) {
 		// setup the pos and direction
 		this.p = light.getDirection();
 		gl.glLightfv(glUnit, GL.GL_POSITION, lr.position, 0);
 		gl.glLightfv(glUnit, GL.GL_SPOT_DIRECTION, lr.spotDirection, 0);
 		
-		// spotCutoff
-		if (lr.spotCutoff != light.getSpotCutoff()) {
-			lr.spotCutoff = light.getSpotCutoff();
-			gl.glLightf(glUnit, GL.GL_SPOT_CUTOFF, lr.spotCutoff);
-		}
-		// constant att.
-		if (lr.constantAttenuation != light.getConstantAttenuation()) {
-			lr.constantAttenuation = light.getConstantAttenuation();
-			gl.glLightf(glUnit, GL.GL_CONSTANT_ATTENUATION, lr.constantAttenuation);
-		}
-		// linear att.
-		if (lr.linearAttenuation != light.getLinearAttenuation()) {
-			lr.linearAttenuation = light.getLinearAttenuation();
-			gl.glLightf(glUnit, GL.GL_LINEAR_ATTENUATION, lr.linearAttenuation);
-		}
-		// quadratic att.
-		if (lr.quadraticAttenuation != light.getQuadraticAttenuation()) {
-			lr.quadraticAttenuation = light.getQuadraticAttenuation();
-			gl.glLightf(glUnit, GL.GL_QUADRATIC_ATTENUATION, lr.quadraticAttenuation);
-		}
+		this.setSpotParameters(gl, lr, glUnit, light.getSpotCutoff(), light.getConstantAttenuation(), 
+							   light.getLinearAttenuation(), light.getQuadraticAttenuation());
 		
 		gl.glPopMatrix();
 	}

File src/com/ferox/renderer/impl/jogl/drivers/JoglLineDrawStyleStateDriver.java

  *
  */
 public class JoglLineDrawStyleStateDriver extends SingleStateDriver<LineStyle> {
+	private static final short DEFAULT_PATTERN = (short) ~0;
+	
 	public JoglLineDrawStyleStateDriver(JoglSurfaceFactory factory) {
 		super(new LineStyle(), LineStyle.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		RasterizationRecord rr = record.rasterRecord;
+		
+		setSmoothingEnabled(gl, rr, false);
+		if (rr.enableLineStipple) {
+			rr.enableLineStipple = false;
+			gl.glDisable(GL.GL_LINE_STIPPLE);
+		}
+		
+		if (rr.lineStipplePattern != DEFAULT_PATTERN || rr.lineStippleRepeat != 1) {
+			rr.lineStipplePattern = DEFAULT_PATTERN;
+			rr.lineStippleRepeat = 1;
+			gl.glLineStipple(1, DEFAULT_PATTERN);
+		}
+		
+		if (rr.lineWidth != 1f) {
+			rr.lineWidth = 1f;
+			gl.glLineWidth(1f);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, LineStyle nextState) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglMaterialStateDriver.java

  *
  */
 public class JoglMaterialStateDriver extends SingleStateDriver<Material> {
+	private final Material restoredMaterial;
+	
 	public JoglMaterialStateDriver(JoglSurfaceFactory factory) {
 		super(new Material(), Material.class, factory);
+		// this is different than the default state for the Renderer
+		this.restoredMaterial = new Material(new Color(.2f, .2f, .2f, 1f), new Color(0f, 0f, 0f, 1f), new Color(.8f, .8f, .8f, 1f), 0f);
+	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		this.apply(gl, record, this.restoredMaterial);
 	}
 
 	@Override

File src/com/ferox/renderer/impl/jogl/drivers/JoglPointDrawStyleStateDriver.java

  *
  */
 public class JoglPointDrawStyleStateDriver extends SingleStateDriver<PointStyle> {
+	private static final Vector3f DEFAULT_DIST_AT = new Vector3f(1f, 0f, 0f);
+	
 	private boolean glslSupport;
 	private boolean pointSpriteSupport;
 	
 		this.glslSupport = factory.getRenderer().getCapabilities().getGlslSupport();
 		this.pointSpriteSupport = factory.getRenderer().getCapabilities().getPointSpriteSupport();
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		RasterizationRecord rr = record.rasterRecord;
+		setPoint(gl, rr, 1f, DEFAULT_DIST_AT, 0f, Float.MAX_VALUE, false);
+		this.setVertexShaderEnabled(gl, rr, false);
+
+		// point sprites
+		if (this.pointSpriteSupport) {
+			if (rr.enablePointSprite) {
+				rr.enablePointSprite = false;
+				gl.glDisable(GL.GL_POINT_SPRITE_ARB);
+			}
+			setPointSpriteOrigin(gl, rr, PointSpriteOrigin.UPPER_LEFT);
+			enableCoordReplace(gl, record.textureRecord, -1);
+			
+			if (record.textureRecord.activeTexture != 0) {
+				gl.glActiveTexture(GL.GL_TEXTURE0);
+				record.textureRecord.activeTexture = 0;
+			}
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, PointStyle nextState) {
 		RasterizationRecord rr = record.rasterRecord;
 		
-		// size
-		if (rr.pointSize != nextState.getPointSize()) {
-			rr.pointSize = nextState.getPointSize();
-			gl.glPointSize(rr.pointSize);
-		}
-		// distance atten.
-		Vector3f at = nextState.getDistanceAttenuation();
-		if (at.x != rr.pointDistanceAttenuation[0] || at.y != rr.pointDistanceAttenuation[1] ||
-		    at.z != rr.pointDistanceAttenuation[2]) {
-			at.get(rr.pointDistanceAttenuation);
-			gl.glPointParameterfv(GL.GL_POINT_DISTANCE_ATTENUATION, rr.pointDistanceAttenuation, 0);
-		}
-		// maximum
-		if (nextState.getPointSizeMax() != rr.pointSizeMax) {
-			rr.pointSizeMax = nextState.getPointSizeMax();
-			gl.glPointParameterf(GL.GL_POINT_SIZE_MAX, rr.pointSizeMax);
-		}
-		// minimum and fade threshold
-		float min = nextState.getPointSizeMin();
-		if (min != rr.pointSizeMin) {
-			rr.pointSizeMin = min;
-			gl.glPointParameterf(GL.GL_POINT_SIZE_MIN, min);
-		}
-		if (min != rr.pointFadeThresholdSize) {
-			rr.pointFadeThresholdSize = min;
-			gl.glPointParameterf(GL.GL_POINT_FADE_THRESHOLD_SIZE, min);
-		}
-		// smoothing
-		if (nextState.isSmoothingEnabled() != rr.enablePointSmooth) {
-			rr.enablePointSmooth = nextState.isSmoothingEnabled();
-			if (rr.enablePointSmooth)
-				gl.glEnable(GL.GL_POINT_SMOOTH);
-			else
-				gl.glDisable(GL.GL_POINT_SMOOTH);
-		}
-		// vertex shader
-		if (this.glslSupport && nextState.isVertexShaderSizingEnabled() != rr.enableVertexShaderSize) {
-			// only do this if glsl is supported
-			rr.enableVertexShaderSize = nextState.isVertexShaderSizingEnabled();
-			if (rr.enableVertexShaderSize)
-				gl.glEnable(GL.GL_VERTEX_PROGRAM_POINT_SIZE);
-			else
-				gl.glDisable(GL.GL_VERTEX_PROGRAM_POINT_SIZE);
-		}
+		setPoint(gl, rr, nextState.getPointSize(), nextState.getDistanceAttenuation(), nextState.getPointSizeMin(), 
+				 nextState.getPointSizeMax(), nextState.isSmoothingEnabled());
+
+		this.setVertexShaderEnabled(gl, rr, nextState.isVertexShaderSizingEnabled());
+		
 		// point sprites
 		if (this.pointSpriteSupport) {
 			// only do this if point sprites are supported
 		}
 	}
 	
+	private void setVertexShaderEnabled(GL gl, RasterizationRecord rr, boolean enable) {
+		// vertex shader
+		if (this.glslSupport && enable != rr.enableVertexShaderSize) {
+			// only do this if glsl is supported
+			rr.enableVertexShaderSize = enable;
+			if (rr.enableVertexShaderSize)
+				gl.glEnable(GL.GL_VERTEX_PROGRAM_POINT_SIZE);
+			else
+				gl.glDisable(GL.GL_VERTEX_PROGRAM_POINT_SIZE);
+		}
+	}
+	
+	private static void setPoint(GL gl, RasterizationRecord rr, float pointSize, Vector3f distance, float min, float max, boolean smoothing) {
+		// size
+		if (rr.pointSize != pointSize) {
+			rr.pointSize = pointSize;
+			gl.glPointSize(rr.pointSize);
+		}
+		// distance atten.
+		Vector3f at = distance;
+		if (at.x != rr.pointDistanceAttenuation[0] || at.y != rr.pointDistanceAttenuation[1] ||
+		    at.z != rr.pointDistanceAttenuation[2]) {
+			at.get(rr.pointDistanceAttenuation);
+			gl.glPointParameterfv(GL.GL_POINT_DISTANCE_ATTENUATION, rr.pointDistanceAttenuation, 0);
+		}
+		// maximum
+		if (max != rr.pointSizeMax) {
+			rr.pointSizeMax = max;
+			gl.glPointParameterf(GL.GL_POINT_SIZE_MAX, rr.pointSizeMax);
+		}
+		// minimum and fade threshold
+		if (min != rr.pointSizeMin) {
+			rr.pointSizeMin = min;
+			gl.glPointParameterf(GL.GL_POINT_SIZE_MIN, min);
+		}
+		if (min != rr.pointFadeThresholdSize) {
+			rr.pointFadeThresholdSize = min;
+			gl.glPointParameterf(GL.GL_POINT_FADE_THRESHOLD_SIZE, min);
+		}
+		// smoothing
+		if (smoothing != rr.enablePointSmooth) {
+			rr.enablePointSmooth = smoothing;
+			if (rr.enablePointSmooth)
+				gl.glEnable(GL.GL_POINT_SMOOTH);
+			else
+				gl.glDisable(GL.GL_POINT_SMOOTH);
+		}
+	}
+	
 	/* Enables coord replace on the given unit and disables it on every other one. 
 	 * If enableUnit doesn't equal a valid texture unit (TEXTURE0 ...), this will disable
 	 * all units. */

File src/com/ferox/renderer/impl/jogl/drivers/JoglPolygonDrawStyleStateDriver.java

 	public JoglPolygonDrawStyleStateDriver(JoglSurfaceFactory factory) {
 		super(new PolygonStyle(), PolygonStyle.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		RasterizationRecord rr = record.rasterRecord;
+		
+		setSmoothingEnabled(gl, rr, false);
+		setPointOffsetEnabled(gl, rr, false);
+		setLineOffsetEnabled(gl, rr, false);
+		setFillOffsetEnabled(gl, rr, false);
+		
+		setCullingEnabled(gl, rr, false);
+		setFrontFace(gl, rr, GL.GL_CCW);
+		if (rr.cullFaceMode != GL.GL_BACK) {
+			rr.cullFaceMode = GL.GL_BACK;
+			gl.glCullFace(GL.GL_BACK);
+		}
+		
+		setFrontStyle(gl, rr, DrawStyle.SOLID);
+		setBackStyle(gl, rr, DrawStyle.SOLID);
+		
+		if (rr.polygonOffsetFactor != 0f || rr.polygonOffsetUnits != 0f) {
+			rr.polygonOffsetFactor = 0f;
+			rr.polygonOffsetUnits = 0f;
+			gl.glPolygonOffset(0f, 0f);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, PolygonStyle nextState) {
 				setFillOffsetEnabled(gl, rr, false);
 			} else {
 				// enable offsetting
-				setPointOffsetEnabled(gl, rr, false);
-				setLineOffsetEnabled(gl, rr, false);
-				setFillOffsetEnabled(gl, rr, false);
+				setPointOffsetEnabled(gl, rr, true);
+				setLineOffsetEnabled(gl, rr, true);
+				setFillOffsetEnabled(gl, rr, true);
 				
 				// set the offset
 				if (rr.polygonOffsetFactor != offset || rr.polygonOffsetUnits != 0) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglShaderStateDriver.java

 		// although it's really just each surface
 		this.perFrameUniforms.clear();
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		GlslShaderRecord sr = record.shaderRecord;
+		
+		if (sr.glslProgramBinding != 0) {
+			sr.glslProgramBinding = 0;
+			gl.glUseProgram(0);
+		}
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, GlslShader nextState) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglStencilTestStateDriver.java

 	public JoglStencilTestStateDriver(JoglSurfaceFactory factory) {
 		super(null, StencilTest.class, factory);
 	}
+	
+	@Override
+	protected void restore(GL gl, JoglStateRecord record) {
+		PixelOpRecord pr = record.pixelOpRecord;
+		FramebufferRecord fr = record.frameRecord;
+		
+		if (pr.enableStencilTest) {
+			pr.enableStencilTest = false;
+			gl.glDisable(GL.GL_STENCIL_TEST);
+		}
+		
+		this.setWriteMask(gl, fr, FULL_MASK);
+		this.setStencilOp(gl, pr, GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);
+		this.setStencilFunc(gl, pr, GL.GL_ALWAYS, 0, FULL_MASK);
+	}
 
 	@Override
 	protected void apply(GL gl, JoglStateRecord record, StencilTest nextState) {
-		PixelOpRecord pr =record.pixelOpRecord;
+		PixelOpRecord pr = record.pixelOpRecord;
 		FramebufferRecord fr = record.frameRecord;
 		
 		if (nextState == null) {
 				gl.glDisable(GL.GL_STENCIL_TEST);
 			}
 			// we must reset the mask so stencil clearing works properly
-			if (fr.stencilWriteMask != FULL_MASK || fr.stencilBackWriteMask != FULL_MASK) {
-				fr.stencilBackWriteMask = FULL_MASK;
-				fr.stencilWriteMask = FULL_MASK;
-				gl.glStencilMask(~0);
-			}
+			this.setWriteMask(gl, fr, FULL_MASK);
 		} else {
 			// enable, make sure the stencil record is correct
 			if (!pr.enableStencilTest) {
 				gl.glEnable(GL.GL_STENCIL_TEST);
 			}
 			
+			// set the stencil write mask
+			this.setWriteMask(gl, fr, nextState.getWriteMask());
+			
 			// set the stencil function
-			int func = EnumUtil.getGLPixelTest(nextState.getTest());
-			int ref = nextState.getReferenceValue();
-			int mask = nextState.getTestMask();
-			if ((func != pr.stencilFunc || ref != pr.stencilRef || ref != pr.stencilValueMask) ||
-				(func != pr.stencilBackFunc || ref != pr.stencilBackRef || ref != pr.stencilBackValueMask)) {
-				// update the record
-				pr.stencilFunc = func; pr.stencilBackFunc = func;
-				pr.stencilRef = ref; pr.stencilBackRef = ref;
-				pr.stencilValueMask = mask;	pr.stencilBackValueMask = mask;
-				
-				gl.glStencilFunc(func, ref, mask);
-			}
+			this.setStencilFunc(gl, pr, EnumUtil.getGLPixelTest(nextState.getTest()), nextState.getReferenceValue(), nextState.getTestMask());
 			
 			// set the stencil operations
-			int sfail = EnumUtil.getGLStencilOp(nextState.getStencilFailOp());
-			int dfail = EnumUtil.getGLStencilOp(nextState.getDepthFailOp());
-			int dpass = EnumUtil.getGLStencilOp(nextState.getDepthPassOp());
-			if (sfail != pr.stencilFail || sfail != pr.stencilBackFail ||
-			    dfail != pr.stencilPassDepthFail || dfail != pr.stencilBackPassDepthFail ||
-			    dpass != pr.stencilPassDepthPass || dpass != pr.stencilBackPassDepthPass) {
-				// update the record
-				pr.stencilFail = sfail; pr.stencilBackFail = sfail;
-				pr.stencilPassDepthFail = dfail; pr.stencilBackPassDepthFail = dfail;
-				pr.stencilPassDepthPass = dpass; pr.stencilBackPassDepthPass = dpass;
-				
-				gl.glStencilOp(sfail, dfail, dpass);
-			}
+			this.setStencilOp(gl, pr, EnumUtil.getGLStencilOp(nextState.getStencilFailOp()), 
+									  EnumUtil.getGLStencilOp(nextState.getDepthFailOp()), 
+									  EnumUtil.getGLStencilOp(nextState.getDepthPassOp()));
+		}
+	}
+	
+	private void setWriteMask(GL gl, FramebufferRecord fr, int writeMask) {
+		if (fr.stencilWriteMask != writeMask || fr.stencilBackWriteMask != writeMask) {
+			fr.stencilBackWriteMask = writeMask;
+			fr.stencilWriteMask = writeMask;
+			gl.glStencilMask(writeMask);
+		}
+	}
+	
+	private void setStencilOp(GL gl, PixelOpRecord pr, int sfail, int dfail, int dpass) {
+		if (sfail != pr.stencilFail || sfail != pr.stencilBackFail ||
+				dfail != pr.stencilPassDepthFail || dfail != pr.stencilBackPassDepthFail ||
+				dpass != pr.stencilPassDepthPass || dpass != pr.stencilBackPassDepthPass) {
+			// update the record
+			pr.stencilFail = sfail; pr.stencilBackFail = sfail;
+			pr.stencilPassDepthFail = dfail; pr.stencilBackPassDepthFail = dfail;
+			pr.stencilPassDepthPass = dpass; pr.stencilBackPassDepthPass = dpass;
+
+			gl.glStencilOp(sfail, dfail, dpass);
+		}
+	}
+	
+	private void setStencilFunc(GL gl, PixelOpRecord pr, int func, int ref, int mask) {
+		if ((func != pr.stencilFunc || ref != pr.stencilRef || ref != pr.stencilValueMask) ||
+				(func != pr.stencilBackFunc || ref != pr.stencilBackRef || ref != pr.stencilBackValueMask)) {
+			// update the record
+			pr.stencilFunc = func; pr.stencilBackFunc = func;
+			pr.stencilRef = ref; pr.stencilBackRef = ref;
+			pr.stencilValueMask = mask;	pr.stencilBackValueMask = mask;
+
+			gl.glStencilFunc(func, ref, mask);
 		}
 	}
 }

File src/com/ferox/renderer/impl/jogl/drivers/JoglTextureStateDriver.java

 package com.ferox.renderer.impl.jogl.drivers;
 
+import java.util.Arrays;
 import java.util.List;
 
 import javax.media.opengl.GL;
 import com.ferox.state.MultiTexture;
 import com.ferox.state.State;
 import com.ferox.state.Texture;
+import com.ferox.state.Texture.CombineAlpha;
+import com.ferox.state.Texture.CombineOperand;
+import com.ferox.state.Texture.CombineRgb;
+import com.ferox.state.Texture.CombineSource;
+import com.ferox.state.Texture.EnvMode;
 import com.ferox.state.Texture.TexCoordGen;
 
 /** JoglTextureStateDriver provides support for the TEXTURE role
  *
  */
 public class JoglTextureStateDriver implements StateDriver {
+	private static final float[] DEF_PLANE_S = {1f, 0f, 0f, 0f};
+	private static final float[] DEF_PLANE_T = {0f, 1f, 0f, 0f};
+	private static final float[] DEF_PLANE_RQ = {0f, 0f, 0f, 0f};
+	
+	private Texture defaultEnv;
+	
 	private MultiTexture lastApplied;
 	private MultiTexture queuedTexture;
 	private float queuedInfluence;
 		this.queuedTexture = null;
 		this.lastAppliedDirty = false;
 		this.queuedInfluence = -1f;		
+		
+		this.defaultEnv = new Texture();
+		
+		this.defaultEnv.setTextureEnvMode(EnvMode.MODULATE);
+		this.defaultEnv.setTextureEnvColor(new Color(0f, 0f, 0f, 0f));
+		
+		this.defaultEnv.setTextureTransform(null);
+		this.defaultEnv.setTexCoordGenSTR(TexCoordGen.NONE);
+		
+		this.defaultEnv.setCombineAlphaEquation(CombineAlpha.MODULATE);
+		this.defaultEnv.setCombineRgbEquation(CombineRgb.MODULATE);
+		this.defaultEnv.setSourceRgb(CombineSource.CURR_TEX, CombineSource.PREV_TEX, CombineSource.BLEND_COLOR);
+		this.defaultEnv.setSourceAlpha(CombineSource.CURR_TEX, CombineSource.PREV_TEX, CombineSource.BLEND_COLOR);
+		this.defaultEnv.setOperandRgb(CombineOperand.COLOR, CombineOperand.COLOR, CombineOperand.ALPHA);
+		this.defaultEnv.setOperandAlpha(CombineOperand.ALPHA, CombineOperand.ALPHA, CombineOperand.ALPHA);
 	}
 	
 	@Override
 			this.apply(this.factory.getRenderer(), this.factory.getRecord(), null);
 		
 		this.lastApplied = this.queuedTexture;
-		
 		this.reset();
 	}
 
 		this.lastAppliedDirty = false;
 	}
 	
+	@Override
+	public void restoreDefaults() {
+		this.reset();
+		this.lastApplied = null;
+		
+		GL gl = this.factory.getGL();
+		TextureRecord tr = this.factory.getRecord().textureRecord;
+		
+		TextureUnit tu;
+		for (int i = 0; i < tr.textureUnits.length; i++) {
+			gl.glActiveTexture(GL.GL_TEXTURE0 + i);
+			
+			tu = tr.textureUnits[i];
+			// sets all env except for the combine mode, and texgen
+			this.setTexEnv(gl, tu, this.defaultEnv);
+			// force combine mode to be set
+			setCombineEnv(gl, tu, this.defaultEnv);
+			// at this point, texgen is disabled, but must reset other values
+			setDefaultTexGen(gl, GL.GL_S, tu.texGenS, DEF_PLANE_S);
+			setDefaultTexGen(gl, GL.GL_T, tu.texGenT, DEF_PLANE_T);
+			setDefaultTexGen(gl, GL.GL_R, tu.texGenR, DEF_PLANE_RQ);
+			
+			// unbind the texture
+			bindTexture(gl, tu, -1, 0);
+		}
+		gl.glActiveTexture(GL.GL_TEXTURE0);
+		tr.activeTexture = 0;
+	}
+	
 	/* Modify the given context so that its TextureRecord matches the given MultiTexture.
 	 * If next is null, all texturing will be disabled instead. */
 	private void apply(AbstractRenderer renderer, JoglStateRecord record, MultiTexture next) {
 		if (nextH == null) {
 			// disable the texture
 			bindTexture(gl, tu, -1, 0); // unbind the bound object
+			// disable tex-gen, too
+			setTexGen(gl, GL.GL_S, GL.GL_TEXTURE_GEN_S, tu.texGenS, TexCoordGen.NONE, null);
+			setTexGen(gl, GL.GL_T, GL.GL_TEXTURE_GEN_T, tu.texGenT, TexCoordGen.NONE, null);
+			setTexGen(gl, GL.GL_R, GL.GL_TEXTURE_GEN_R, tu.texGenR, TexCoordGen.NONE, null);
 		} else {
 			// enable the texture
 			bindTexture(gl, tu, nextH.glTarget, nextH.id);
 			unit.isTextureMatrixIdentity = false;
 		}
 		// tc_s
-		this.setTexGen(gl, GL.GL_S, GL.GL_TEXTURE_GEN_S, unit.texGenS, tex.getTexCoordGenS(), tex.getTexCoordGenPlaneS());
+		setTexGen(gl, GL.GL_S, GL.GL_TEXTURE_GEN_S, unit.texGenS, tex.getTexCoordGenS(), tex.getTexCoordGenPlaneS());
 		// tc_t
-		this.setTexGen(gl, GL.GL_T, GL.GL_TEXTURE_GEN_T, unit.texGenT, tex.getTexCoordGenT(), tex.getTexCoordGenPlaneT());
+		setTexGen(gl, GL.GL_T, GL.GL_TEXTURE_GEN_T, unit.texGenT, tex.getTexCoordGenT(), tex.getTexCoordGenPlaneT());
 		// tc_r
-		this.setTexGen(gl, GL.GL_R, GL.GL_TEXTURE_GEN_R, unit.texGenR, tex.getTexCoordGenR(), tex.getTexCoordGenPlaneR());
+		setTexGen(gl, GL.GL_R, GL.GL_TEXTURE_GEN_R, unit.texGenR, tex.getTexCoordGenR(), tex.getTexCoordGenPlaneR());
 		
 		// env color
 		Color blend = tex.getTextureEnvColor();
 		}
 	}
 	
+	/* Set the defaults for the given record, it changes the mode to eye linear,
+	 * and sets the object and eye planes to the given array. */
+	private static void setDefaultTexGen(GL gl, int coord, TextureGenRecord tgr, float[] plane) {
+		if (tgr.textureGenMode != GL.GL_EYE_LINEAR) {
+			tgr.textureGenMode = GL.GL_EYE_LINEAR;
+			gl.glTexGeni(coord, GL.GL_TEXTURE_GEN_MODE, GL.GL_EYE_LINEAR);
+		}
+		
+		if (!Arrays.equals(tgr.eyePlane, plane)) {
+			System.arraycopy(plane, 0, tgr.eyePlane, 0, plane.length);
+			gl.glTexGenfv(coord, GL.GL_EYE_PLANE, plane, 0);
+		}
+		
+		if (!Arrays.equals(tgr.objectPlane, plane)) {
+			System.arraycopy(plane, 0, tgr.objectPlane, 0, plane.length);
+			gl.glTexGenfv(coord, GL.GL_OBJECT_PLANE, plane, 0);
+		}
+	}
+	
 	/* Set the texture gen for the given coordinate.  coord should be one of
 	 * GL_S/T/R, and boolMode would then be GL_TEXTURE_GEN_x, and tgr is the
 	 * matching record.  If genMode is NONE, generation is disabled, otherwise
 	 * its enabled and set, possibly resetting the texture plane. */
-	private void setTexGen(GL gl, int coord, int boolMode, TextureGenRecord tgr, TexCoordGen genMode, Plane eyeOrObject) {
+	private static void setTexGen(GL gl, int coord, int boolMode, TextureGenRecord tgr, TexCoordGen genMode, Plane eyeOrObject) {
 		if (genMode == TexCoordGen.NONE) {
 			// disable coordinate generation for this coord
 			if (tgr.enableTexGen) {
 			// possibly set the planes
 			if (genMode == TexCoordGen.EYE) {
 				// always push the eye-plane through
-				this.factory.getTransformDriver().loadMatrix(gl, this.factory.getViewTransform());
-				//gl.glLoadIdentity();
 				eyeOrObject.get(tgr.eyePlane);
 				gl.glTexGenfv(coord, GL.GL_EYE_PLANE, tgr.eyePlane, 0);
 			} else if (genMode == TexCoordGen.OBJECT) {

File src/com/ferox/renderer/impl/jogl/drivers/JoglTransformDriver.java

  * set back to MODELVIEW before this driver is used again,
  * or errors will occur.
  * 
- * To minimize the amount of gl operations (including
- * removing extraneous glPush and glPopMatrix() calls), the
- * modelview stack is effectively not used.  Instead, the current
- * view is multipled to the desired world transform, and then
- * a glLoadMatrixf() call should be used.  Compared to the overhead
- * of executing the jni gl call, the matrix multiplication is free.
+ * When applying the transform state, this uses the push-pop
+ * mechanism.  The view portion of the modelview matrix is
+ * set, and then each model transform is pushed on top of that.
+ * Thus when states are applied, the view transform will be the
+ * current modelview matrix.
  * 
  * @author Michael Ludwig
  *

File src/com/ferox/renderer/impl/jogl/drivers/MultiStateDriver.java

 		this.queueSize = 0;
 	}
 	
+	protected abstract void restore(GL gl, JoglStateRecord record, int unit);
+	
 	protected abstract void apply(GL gl, JoglStateRecord record, int unit, T next);
 	
 	@Override
+	public void restoreDefaults() {
+		GL gl = this.factory.getGL();
+		JoglStateRecord r = this.factory.getRecord();
+		// reset everything
+		for (int i = 0; i < this.apply.length; i++) {
+			this.apply[i].priority = INVALID_PRIORTY;
+			this.apply[i].state = null;
+			
+			// perform changes
+			this.restore(gl, r, i);
+		}
+		this.reset();
+	}
+	
+	@Override
 	public void doApply() {
 		int i;