diff options
Diffstat (limited to 'src')
23 files changed, 305 insertions, 164 deletions
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; } } |