diff options
author | Kenneth Russel <[email protected]> | 2007-03-18 23:30:00 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2007-03-18 23:30:00 +0000 |
commit | 44ded763ea870d0daad90a0f7355a5792b16b774 (patch) | |
tree | 1d8ff1f40e46793f0e9856d10e7e2cfc3c366da2 | |
parent | e1f4a731f55ab29e3f23f87102af1b11c67cb0da (diff) |
Added extensible action method mechanism via ActionTable, decoupling
Action implementations from the set of Nodes in the library.
Refactored OpenGL rendering into node instances to potentially share
more code among different Action subclasses.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/joglutils/trunk@45 83d24430-9974-4f80-8418-2cc3294053b9
24 files changed, 305 insertions, 164 deletions
diff --git a/build/joglutils.jar b/build/joglutils.jar Binary files differindex 759c929..33d954e 100644 --- a/build/joglutils.jar +++ b/build/joglutils.jar diff --git a/src/net/java/joglutils/msg/actions/Action.java b/src/net/java/joglutils/msg/actions/Action.java index 21ca2bc..34f14ea 100644 --- a/src/net/java/joglutils/msg/actions/Action.java +++ b/src/net/java/joglutils/msg/actions/Action.java @@ -37,47 +37,51 @@ package net.java.joglutils.msg.actions; +import java.lang.reflect.*; + import net.java.joglutils.msg.misc.*; import net.java.joglutils.msg.nodes.*; /** The base class of all actions, which are applied to nodes in the scene graph to implement operations such as rendering. <P> - Subclasses of Action should define, by convention, a public static - method <CODE>getDefaultState</CODE>, returning a {@link - net.java.joglutils.msg.misc.State State} object, which is used to - enable the elements in the state which should be updated by the - action's traversal of nodes. Each Action instance maintains a - State object internally which is initialized from this - default. Note that different actions may enable different elements - of the global state. + Subclasses of Action should define, by convention, a method <BR> + <PRE> public static State getDefaultState()</PRE> + returning a {@link net.java.joglutils.msg.misc.State State} + object, which is used to enable the elements in the state which + should be updated by the action's traversal of nodes. Each Action + instance maintains a State object internally which is initialized + from this default. Note that different actions may enable + different elements of the global state. <P> + + Subclasses of Action should also define, by convention, a method <BR> + <PRE> public static void addActionMethod(Class<? extends Node> nodeType, Method m)</PRE> + which is used to add action methods to the particular Action + class. This may be used by developers to attach new functionality + to the Action for new node types they define. */ public abstract class Action { - /** Applies this Action to a particular node. This is how operations such as rendering are initiated. */ - public void apply(Node node) { - node.doAction(this); - } + public abstract void apply(Node node); /** Returns the global state this action encompasses, which is altered by the nodes the action traverses. */ public abstract State getState(); - // Visitor methods, one per node class - - // FIXME: should rethink this mechanism and make it extensible as - // per the original Open Inventor - public abstract void visit (Blend blend); - public abstract void visit (Color4 colors); - public abstract void visit (Coordinate3 coords); - public abstract void visit (IndexedTriangleSet tris); - public abstract void visit (PerspectiveCamera camera); - public abstract void visitPre (Separator sep); - public abstract void visitPost(Separator sep); - public abstract void visit (Texture2 texture); - public abstract void visit (TextureCoordinate2 texCoords); - public abstract void visit (Transform transform); - public abstract void visit (TriangleSet tris); + private Object[] argTmp = new Object[2]; + /** Invokes the appropriate action method for the given Node. */ + protected void apply(ActionTable table, Node node) { + try { + Method m = table.lookupActionMethod(node); + if (m != null) { + argTmp[0] = this; + argTmp[1] = node; + m.invoke(null, argTmp); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/net/java/joglutils/msg/actions/GLRenderAction.java b/src/net/java/joglutils/msg/actions/GLRenderAction.java index 2274af1..7b2fb56 100644 --- a/src/net/java/joglutils/msg/actions/GLRenderAction.java +++ b/src/net/java/joglutils/msg/actions/GLRenderAction.java @@ -37,12 +37,12 @@ package net.java.joglutils.msg.actions; +import java.lang.reflect.*; + import javax.media.opengl.*; import javax.media.opengl.glu.*; -import com.sun.opengl.util.texture.*; import net.java.joglutils.msg.elements.*; -import net.java.joglutils.msg.math.*; import net.java.joglutils.msg.misc.*; import net.java.joglutils.msg.nodes.*; @@ -64,6 +64,14 @@ public class GLRenderAction extends Action { public static State getDefaultState() { return defaults; } + private static ActionTable table = new ActionTable(GLRenderAction.class); + + /** Adds an action method for the given node type to this action. + This should only be called by developers adding new node types + and not desiring to use the standard overriding mechanisms. */ + public static void addActionMethod(Class<? extends Node> nodeType, Method m) { + table.addActionMethod(nodeType, m); + } private State state = new State(defaults); public State getState() { @@ -71,34 +79,31 @@ public class GLRenderAction extends Action { } static { - // FIXME: may need to rethink when and where these elements are enabled - - // In Open Inventor, this is folded into the node's initialization - // (i.e., the node needs to know which elements it might affect). - // That in theory allows for fewer elements to be enabled, but it - // isn't clear that this makes a difference, or that it results in - // correct code elsewhere where certain elements implicitly expect - // others to be enabled. - GLBlendElement .enable(getDefaultState()); - GLColorElement .enable(getDefaultState()); - GLCoordinateElement .enable(getDefaultState()); - GLModelMatrixElement .enable(getDefaultState()); - GLProjectionMatrixElement .enable(getDefaultState()); - GLViewingMatrixElement .enable(getDefaultState()); - GLTextureCoordinateElement.enable(getDefaultState()); - GLTextureElement .enable(getDefaultState()); + // Set up action methods + // Note that because this action is built-in, we use virtual + // method dispatch to allow overriding of rendering functionality. + // We could optionally pull in all of the action methods into this + // class. However, factoring the setting of the elements into the + // nodes provides for more sharing of common functionality among + // actions. + try { + addActionMethod(Node.class, GLRenderAction.class.getMethod("render", GLRenderAction.class, Node.class)); + } catch (Exception e) { + throw new RuntimeException("Error initializing action method for GLRenderAction class", e); + } } // For automatically setting the aspect ratios of cameras we encounter private float curAspectRatio = 1.0f; private int applyDepth = 0; + private GL gl; public void apply(Node node) { - GL gl = GLU.getCurrentGL(); int depth = applyDepth++; try { if (depth == 0) { + gl = GLU.getCurrentGL(); // Applying to the root of the scene graph // Push necessary GL state // FIXME: add in additional bits as we add more capabilities @@ -115,116 +120,30 @@ public class GLRenderAction extends Action { gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0); curAspectRatio = (float) viewport[2] / (float) viewport[3]; } - super.apply(node); + apply(table, node); } finally { if (depth == 0) { gl.glPopClientAttrib(); gl.glPopAttrib(); + gl = null; } --applyDepth; } } - public void visit(Blend blend) { - GLBlendElement.set(state, - blend.getEnabled(), - blend.getBlendColor(), - blend.getSourceFunc(), - blend.getDestFunc(), - blend.getBlendEquation()); - } - - public void visit(Color4 colors) { - GLColorElement.set(state, colors.getData().getData()); - } - - public void visit(Coordinate3 coords) { - GLCoordinateElement.set(state, coords.getData().getData()); - } - - public void visit(IndexedTriangleSet tris) { - throw new RuntimeException("Not yet implemented"); - } - - public void visit(PerspectiveCamera camera) { - // FIXME: unclear whether we should be doing this, or whether we - // should have a mechanism which doesn't require mutation of the - // camera - camera.setAspectRatio(curAspectRatio); - - GLViewingMatrixElement.set(state, camera.getViewingMatrix()); - GLProjectionMatrixElement.set(state, camera.getProjectionMatrix()); - } - - public void visitPre(Separator sep) { - state.push(); - } - - public void visitPost(Separator sep) { - state.pop(); + /** Returns the GL instance being used for rendering. */ + public GL getGL() { + return gl; } - public void visit(Texture2 texture) { - GLTextureElement.set(state, texture.getTexture(), texture.getTexEnvMode()); - } - - public void visit(TextureCoordinate2 texCoords) { - GLTextureCoordinateElement.set(state, texCoords.getData().getData()); - } - - public void visit(Transform transform) { - GLModelMatrixElement.mult(state, transform.getTransform()); - } - - public void visit(TriangleSet tris) { - if (CoordinateElement.get(state) != null) { - // OK, we have coordinates to send down, at least - - GL gl = GLU.getCurrentGL(); - - Texture tex = GLTextureElement.get(state); - boolean haveTexCoords = (GLTextureCoordinateElement.get(state) != null); - - if (tex != null) { - // Set up the texture matrix to uniformly map [0..1] to the used - // portion of the texture image - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPushMatrix(); - gl.glLoadTransposeMatrixf(getTextureMatrix(tex).getRowMajorData(), 0); - gl.glMatrixMode(GL.GL_MODELVIEW); - } else if (haveTexCoords) { - // Want to turn off the use of texture coordinates to avoid errors - // FIXME: not 100% sure whether we need to do this, but think we should - gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY); - } - - // For now, assume the triangle set and the number of available - // coordinates match -- may want to add debugging information - // for this later - gl.glDrawArrays(GL.GL_TRIANGLES, 0, 3 * tris.getNumTriangles()); - - if (tex != null) { - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPopMatrix(); - gl.glMatrixMode(GL.GL_MODELVIEW); - } else if (haveTexCoords) { - // Might want this the next time we render a shape - gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); - } - } + /** Fetches the current aspect ratio of the viewport this + GLRenderAction is rendering into. */ + public float getCurAspectRatio() { + return curAspectRatio; } - private Mat4f textureMatrix = new Mat4f(); - private Mat4f getTextureMatrix(Texture texture) { - textureMatrix.makeIdent(); - TextureCoords coords = texture.getImageTexCoords(); - // Horizontal scale - textureMatrix.set(0, 0, coords.right() - coords.left()); - // Vertical scale (may be negative if texture needs to be flipped vertically) - float vertScale = coords.top() - coords.bottom(); - textureMatrix.set(1, 1, vertScale); - textureMatrix.set(0, 3, coords.left()); - textureMatrix.set(1, 3, coords.bottom()); - return textureMatrix; + /** Action method which dispatches to per-node rendering functionality. */ + public static void render(GLRenderAction action, Node node) { + node.render(action); } } diff --git a/src/net/java/joglutils/msg/elements/BlendElement.java b/src/net/java/joglutils/msg/elements/BlendElement.java index 2a0d488..422811f 100644 --- a/src/net/java/joglutils/msg/elements/BlendElement.java +++ b/src/net/java/joglutils/msg/elements/BlendElement.java @@ -63,6 +63,11 @@ public class BlendElement extends Element { BlendElement tmp = new BlendElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // These defaults match those in the Blend node -- is there a better way of factoring them out? diff --git a/src/net/java/joglutils/msg/elements/ColorElement.java b/src/net/java/joglutils/msg/elements/ColorElement.java index a725d61..a3a92d4 100644 --- a/src/net/java/joglutils/msg/elements/ColorElement.java +++ b/src/net/java/joglutils/msg/elements/ColorElement.java @@ -62,6 +62,11 @@ public class ColorElement extends Element { ColorElement tmp = new ColorElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The actual color data protected FloatBuffer colors; diff --git a/src/net/java/joglutils/msg/elements/CoordinateElement.java b/src/net/java/joglutils/msg/elements/CoordinateElement.java index 34b16bc..58a57c7 100644 --- a/src/net/java/joglutils/msg/elements/CoordinateElement.java +++ b/src/net/java/joglutils/msg/elements/CoordinateElement.java @@ -52,13 +52,21 @@ public class CoordinateElement extends Element { public Element newInstance() { return new CoordinateElement(); } + /** Returns the instance of this element in the passed State. */ public static CoordinateElement getInstance(State state) { return (CoordinateElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { CoordinateElement tmp = new CoordinateElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The actual coordinate data protected FloatBuffer coords; diff --git a/src/net/java/joglutils/msg/elements/ModelMatrixElement.java b/src/net/java/joglutils/msg/elements/ModelMatrixElement.java index cfe726e..e4cdbb0 100644 --- a/src/net/java/joglutils/msg/elements/ModelMatrixElement.java +++ b/src/net/java/joglutils/msg/elements/ModelMatrixElement.java @@ -50,13 +50,21 @@ public class ModelMatrixElement extends Element { public Element newInstance() { return new ModelMatrixElement(); } + /** Returns the instance of this element in the passed State. */ public static ModelMatrixElement getInstance(State state) { return (ModelMatrixElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { Element tmp = new ModelMatrixElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The matrix data protected Mat4f matrix; diff --git a/src/net/java/joglutils/msg/elements/ProjectionMatrixElement.java b/src/net/java/joglutils/msg/elements/ProjectionMatrixElement.java index dd70ca3..4635a83 100644 --- a/src/net/java/joglutils/msg/elements/ProjectionMatrixElement.java +++ b/src/net/java/joglutils/msg/elements/ProjectionMatrixElement.java @@ -50,13 +50,21 @@ public class ProjectionMatrixElement extends Element { public Element newInstance() { return new ProjectionMatrixElement(); } + /** Returns the instance of this element in the passed State. */ public static ProjectionMatrixElement getInstance(State state) { return (ProjectionMatrixElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { Element tmp = new ProjectionMatrixElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The matrix data protected Mat4f matrix; diff --git a/src/net/java/joglutils/msg/elements/TextureCoordinateElement.java b/src/net/java/joglutils/msg/elements/TextureCoordinateElement.java index 6d36c6c..8d24bfc 100644 --- a/src/net/java/joglutils/msg/elements/TextureCoordinateElement.java +++ b/src/net/java/joglutils/msg/elements/TextureCoordinateElement.java @@ -52,13 +52,21 @@ public class TextureCoordinateElement extends Element { public Element newInstance() { return new TextureCoordinateElement(); } + /** Returns the instance of this element in the passed State. */ public static TextureCoordinateElement getInstance(State state) { return (TextureCoordinateElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { TextureCoordinateElement tmp = new TextureCoordinateElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The actual coordinate data protected FloatBuffer coords; diff --git a/src/net/java/joglutils/msg/elements/TextureElement.java b/src/net/java/joglutils/msg/elements/TextureElement.java index 90a9ebc..199a113 100644 --- a/src/net/java/joglutils/msg/elements/TextureElement.java +++ b/src/net/java/joglutils/msg/elements/TextureElement.java @@ -57,13 +57,21 @@ public class TextureElement extends Element { public Element newInstance() { return new TextureElement(); } + /** Returns the instance of this element in the passed State. */ public static TextureElement getInstance(State state) { return (TextureElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { TextureElement tmp = new TextureElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The actual Texture object protected Texture texture; diff --git a/src/net/java/joglutils/msg/elements/ViewingMatrixElement.java b/src/net/java/joglutils/msg/elements/ViewingMatrixElement.java index 5142678..b561bd8 100644 --- a/src/net/java/joglutils/msg/elements/ViewingMatrixElement.java +++ b/src/net/java/joglutils/msg/elements/ViewingMatrixElement.java @@ -50,13 +50,21 @@ public class ViewingMatrixElement extends Element { public Element newInstance() { return new ViewingMatrixElement(); } + /** Returns the instance of this element in the passed State. */ public static ViewingMatrixElement getInstance(State state) { return (ViewingMatrixElement) state.getElement(index); } + /** Enables this element in the passed state, which should be the + default for a given action. */ public static void enable(State defaultState) { Element tmp = new ViewingMatrixElement(); defaultState.setElement(tmp.getStateIndex(), tmp); } + /** Indicates whether this element is enabled in the given default + state for a particular action. */ + public static boolean isEnabled(State state) { + return (state.getDefaults().getElement(index) != null); + } // The matrix data protected Mat4f matrix; diff --git a/src/net/java/joglutils/msg/misc/State.java b/src/net/java/joglutils/msg/misc/State.java index c718f71..b0279b7 100644 --- a/src/net/java/joglutils/msg/misc/State.java +++ b/src/net/java/joglutils/msg/misc/State.java @@ -87,6 +87,14 @@ public class State { push(); } + /** Returns the default State, or this State if it corresponds to + the defaults for a given Action subclass. */ + public State getDefaults() { + if (defaults != null) + return defaults; + return this; + } + /** Gets the state element at the given index. */ public Element getElement(StateIndex index) { // The comments in the Open Inventor implementation indicate that diff --git a/src/net/java/joglutils/msg/nodes/Blend.java b/src/net/java/joglutils/msg/nodes/Blend.java index 18cd5a6..af95e00 100644 --- a/src/net/java/joglutils/msg/nodes/Blend.java +++ b/src/net/java/joglutils/msg/nodes/Blend.java @@ -38,6 +38,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.math.*; /** Provides control over OpenGL blending modes. */ @@ -49,6 +50,11 @@ public class Blend extends Node { private int destFunc = ZERO; private int blendEquation = FUNC_ADD; + static { + // Enable the elements this node affects for known actions + GLBlendElement.enable(GLRenderAction.getDefaultState()); + } + /** One of the blend functions. See the OpenGL documentation for glBlendFunc for more details. */ public static final int ZERO = 1; /** One of the blend functions. See the OpenGL documentation for glBlendFunc for more details. */ @@ -163,6 +169,13 @@ public class Blend extends Node { } public void doAction(Action action) { - action.visit(this); + if (BlendElement.isEnabled(action.getState())) { + BlendElement.set(action.getState(), + getEnabled(), + getBlendColor(), + getSourceFunc(), + getDestFunc(), + getBlendEquation()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/Camera.java b/src/net/java/joglutils/msg/nodes/Camera.java index 9d1f965..5d405fe 100644 --- a/src/net/java/joglutils/msg/nodes/Camera.java +++ b/src/net/java/joglutils/msg/nodes/Camera.java @@ -37,6 +37,8 @@ package net.java.joglutils.msg.nodes; +import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.math.*; /** Represents a camera which is used to view the scene. The camera @@ -62,6 +64,14 @@ public abstract class Camera extends Node { protected Mat4f projMatrix; protected Mat4f viewMatrix; + static { + // Enable the elements this node affects for known actions + // Note that all of these elements are interdependent + GLModelMatrixElement .enable(GLRenderAction.getDefaultState()); + GLProjectionMatrixElement .enable(GLRenderAction.getDefaultState()); + GLViewingMatrixElement .enable(GLRenderAction.getDefaultState()); + } + public Camera() { position = new Vec3f(0, 0, 1); orientation = new Rotf(); @@ -123,4 +133,13 @@ public abstract class Camera extends Node { /** Returns the projection matrix associated with this camera's parameters. */ public abstract Mat4f getProjectionMatrix(); + + public void doAction(Action action) { + if (ViewingMatrixElement.isEnabled(action.getState())) { + ViewingMatrixElement.set(action.getState(), getViewingMatrix()); + } + if (ProjectionMatrixElement.isEnabled(action.getState())) { + ProjectionMatrixElement.set(action.getState(), getProjectionMatrix()); + } + } } diff --git a/src/net/java/joglutils/msg/nodes/Color4.java b/src/net/java/joglutils/msg/nodes/Color4.java index d6568e3..8d6696f 100644 --- a/src/net/java/joglutils/msg/nodes/Color4.java +++ b/src/net/java/joglutils/msg/nodes/Color4.java @@ -38,6 +38,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.collections.*; /** Represents a set of 4-valued colors which are applied on a @@ -55,6 +56,11 @@ import net.java.joglutils.msg.collections.*; public class Color4 extends Node { private Vec4fCollection data; + static { + // Enable the elements this node affects for known actions + GLColorElement.enable(GLRenderAction.getDefaultState()); + } + // private int colorBinding = AMBIENT_AND_DIFFUSE; /****** @@ -90,6 +96,8 @@ public class Color4 extends Node { } public void doAction(Action action) { - action.visit(this); + if (ColorElement.isEnabled(action.getState())) { + ColorElement.set(action.getState(), getData().getData()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/Coordinate3.java b/src/net/java/joglutils/msg/nodes/Coordinate3.java index 4e2b94b..e8e2631 100644 --- a/src/net/java/joglutils/msg/nodes/Coordinate3.java +++ b/src/net/java/joglutils/msg/nodes/Coordinate3.java @@ -38,6 +38,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.collections.*; /** Represents a set of 3-dimensional vertices which can be assembled @@ -46,6 +47,11 @@ import net.java.joglutils.msg.collections.*; public class Coordinate3 extends Node { private Vec3fCollection data; + static { + // Enable the elements this node affects for known actions + GLCoordinateElement.enable(GLRenderAction.getDefaultState()); + } + /** Sets the coordinate data in this node. */ public void setData(Vec3fCollection data) { this.data = data; @@ -57,6 +63,8 @@ public class Coordinate3 extends Node { } public void doAction(Action action) { - action.visit(this); + if (CoordinateElement.isEnabled(action.getState())) { + CoordinateElement.set(action.getState(), getData().getData()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java b/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java index d2932c8..5e45367 100644 --- a/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java +++ b/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java @@ -62,6 +62,6 @@ public class IndexedTriangleSet extends Node { } public void doAction(Action action) { - action.visit(this); + throw new RuntimeException("Not yet implemented"); } } diff --git a/src/net/java/joglutils/msg/nodes/Node.java b/src/net/java/joglutils/msg/nodes/Node.java index 6fc4eb9..e9eefc4 100644 --- a/src/net/java/joglutils/msg/nodes/Node.java +++ b/src/net/java/joglutils/msg/nodes/Node.java @@ -42,11 +42,13 @@ import net.java.joglutils.msg.actions.*; /** The base class for all nodes in the scene graph. */ public class Node { + /** Performs the "typical" operation for this node when an action is + applied to it. The default implementation does nothing. */ + public void doAction(Action action) {} - /** This is an internal API not intended for public use. To apply an - action to the scene graph or a portion of the scene graph, use - {@link net.java.joglutils.msg.actions.Action#apply - Action.apply}. */ - public void doAction(Action action) { - } + /** Support for the built-in GLRenderAction. Note that supplying + virtual methods in Node subclasses to support various actions is + not required due to the framework supporting action methods, but + for built-in actions it may make it simpler. */ + public void render(GLRenderAction action) { doAction(action); } } diff --git a/src/net/java/joglutils/msg/nodes/PerspectiveCamera.java b/src/net/java/joglutils/msg/nodes/PerspectiveCamera.java index b72507a..50c646b 100644 --- a/src/net/java/joglutils/msg/nodes/PerspectiveCamera.java +++ b/src/net/java/joglutils/msg/nodes/PerspectiveCamera.java @@ -91,7 +91,11 @@ public class PerspectiveCamera extends Camera { return vertFOVScale * DEFAULT_HEIGHT_ANGLE; } - public void doAction(Action action) { - action.visit(this); + public void render(GLRenderAction action) { + // FIXME: unclear whether we should be doing this, or whether we + // should have a mechanism which doesn't require mutation of the + // camera + setAspectRatio(action.getCurAspectRatio()); + doAction(action); } } diff --git a/src/net/java/joglutils/msg/nodes/Separator.java b/src/net/java/joglutils/msg/nodes/Separator.java index c60ba45..fdebece 100644 --- a/src/net/java/joglutils/msg/nodes/Separator.java +++ b/src/net/java/joglutils/msg/nodes/Separator.java @@ -38,6 +38,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.misc.*; /** Represents a push / pop of OpenGL state, "separating" the sub-graph below this separator from the nodes which follow it in @@ -45,8 +46,12 @@ import net.java.joglutils.msg.actions.*; public class Separator extends Group { public void doAction(Action action) { - action.visitPre(this); - super.doAction(action); - action.visitPost(this); + State state = action.getState(); + state.push(); + try { + super.doAction(action); + } finally { + state.pop(); + } } } diff --git a/src/net/java/joglutils/msg/nodes/Texture2.java b/src/net/java/joglutils/msg/nodes/Texture2.java index 441c2f5..fd1b82a 100644 --- a/src/net/java/joglutils/msg/nodes/Texture2.java +++ b/src/net/java/joglutils/msg/nodes/Texture2.java @@ -45,6 +45,7 @@ import javax.media.opengl.*; import com.sun.opengl.util.texture.*; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; /** Represents a two-dimensional texture. */ @@ -54,6 +55,11 @@ public class Texture2 extends Node { private int texEnvMode = MODULATE; private boolean dirty; + static { + // Enable the elements this node affects for known actions + GLTextureElement.enable(GLRenderAction.getDefaultState()); + } + /** Represents the OpenGL MODULATE texture environment mode. */ public static final int MODULATE = 1; /** Represents the OpenGL DECAL texture environment mode. */ @@ -134,6 +140,8 @@ public class Texture2 extends Node { } public void doAction(Action action) { - action.visit(this); + if (TextureElement.isEnabled(action.getState())) { + TextureElement.set(action.getState(), getTexture(), getTexEnvMode()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java b/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java index ed4ecf8..f7720d2 100644 --- a/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java +++ b/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java @@ -39,6 +39,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; import net.java.joglutils.msg.collections.*; +import net.java.joglutils.msg.elements.*; /** Represents a set of 2-dimensional texture coordinates which can be used to texture geometric shapes. */ @@ -46,6 +47,11 @@ import net.java.joglutils.msg.collections.*; public class TextureCoordinate2 extends Node { private Vec2fCollection data; + static { + // Enable the elements this node affects for known actions + GLTextureCoordinateElement.enable(GLRenderAction.getDefaultState()); + } + /** Sets the texture coordinate data in this node. */ public void setData(Vec2fCollection data) { this.data = data; @@ -57,6 +63,8 @@ public class TextureCoordinate2 extends Node { } public void doAction(Action action) { - action.visit(this); + if (TextureCoordinateElement.isEnabled(action.getState())) { + TextureCoordinateElement.set(action.getState(), getData().getData()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/Transform.java b/src/net/java/joglutils/msg/nodes/Transform.java index 0faf8e6..ad5e1bd 100644 --- a/src/net/java/joglutils/msg/nodes/Transform.java +++ b/src/net/java/joglutils/msg/nodes/Transform.java @@ -38,6 +38,7 @@ package net.java.joglutils.msg.nodes; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.math.*; /** Represents a generalized 4x4 matrix transformation. */ @@ -45,6 +46,14 @@ import net.java.joglutils.msg.math.*; public class Transform extends Node { private Mat4f transform; + static { + // Enable the elements this node affects for known actions + // Note that all of these elements are interdependent + GLModelMatrixElement .enable(GLRenderAction.getDefaultState()); + GLProjectionMatrixElement .enable(GLRenderAction.getDefaultState()); + GLViewingMatrixElement .enable(GLRenderAction.getDefaultState()); + } + public Transform() { transform = new Mat4f(); transform.makeIdent(); @@ -61,6 +70,8 @@ public class Transform extends Node { } public void doAction(Action action) { - action.visit(this); + if (ModelMatrixElement.isEnabled(action.getState())) { + ModelMatrixElement.mult(action.getState(), getTransform()); + } } } diff --git a/src/net/java/joglutils/msg/nodes/TriangleSet.java b/src/net/java/joglutils/msg/nodes/TriangleSet.java index 775c622..0692738 100644 --- a/src/net/java/joglutils/msg/nodes/TriangleSet.java +++ b/src/net/java/joglutils/msg/nodes/TriangleSet.java @@ -37,7 +37,13 @@ package net.java.joglutils.msg.nodes; +import javax.media.opengl.*; +import com.sun.opengl.util.texture.*; + import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; +import net.java.joglutils.msg.math.*; +import net.java.joglutils.msg.misc.*; /** A TriangleSet assembles the coordinates specified by a Coordinate3 node, and any auxiliary nodes such as a TextureCoordinate2 node, @@ -56,7 +62,67 @@ public class TriangleSet extends Node { return numTriangles; } - public void doAction(Action action) { - action.visit(this); + public void render(GLRenderAction action) { + State state = action.getState(); + if (!CoordinateElement.isEnabled(state)) + return; + + if (CoordinateElement.get(state) != null) { + // OK, we have coordinates to send down, at least + + GL gl = action.getGL(); + + Texture tex = null; + boolean haveTexCoords = false; + + if (GLTextureElement.isEnabled(state) && + GLTextureCoordinateElement.isEnabled(state)) { + tex = GLTextureElement.get(state); + haveTexCoords = (GLTextureCoordinateElement.get(state) != null); + } + + if (tex != null) { + // Set up the texture matrix to uniformly map [0..1] to the used + // portion of the texture image + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadTransposeMatrixf(getTextureMatrix(tex).getRowMajorData(), 0); + gl.glMatrixMode(GL.GL_MODELVIEW); + } else if (haveTexCoords) { + // Want to turn off the use of texture coordinates to avoid errors + // FIXME: not 100% sure whether we need to do this, but think we should + gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY); + } + + // For now, assume the triangle set and the number of available + // coordinates match -- may want to add debugging information + // for this later + gl.glDrawArrays(GL.GL_TRIANGLES, 0, 3 * getNumTriangles()); + + if (tex != null) { + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_MODELVIEW); + } else if (haveTexCoords) { + // Might want this the next time we render a shape + gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); + } + } + } + + // Helper routine for setting up a texture matrix to allow texture + // coords in the scene graph to always be specified from (0..1) + private Mat4f textureMatrix = new Mat4f(); + private Mat4f getTextureMatrix(Texture texture) { + textureMatrix.makeIdent(); + TextureCoords coords = texture.getImageTexCoords(); + // Horizontal scale + textureMatrix.set(0, 0, coords.right() - coords.left()); + // Vertical scale (may be negative if texture needs to be flipped vertically) + float vertScale = coords.top() - coords.bottom(); + textureMatrix.set(1, 1, vertScale); + textureMatrix.set(0, 3, coords.left()); + textureMatrix.set(1, 3, coords.bottom()); + return textureMatrix; } } |