diff options
author | Kenneth Russel <[email protected]> | 2007-03-24 01:02:23 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2007-03-24 01:02:23 +0000 |
commit | 96a0041f4b46bba34d044ade4d55b6ab58176336 (patch) | |
tree | aee8ccddd42b55cee82c55e218e776460ac10ff8 | |
parent | 1b9cde01700475163b8735db9d29cc54e961c92a (diff) |
Added ray picking functionality via RayPickAction. Added unprojection
functionality to Camera class. Added Shape and TriangleBasedShape as
superclasses in the node hierarchy. Added TriangleCallback mechanism
for iterating vertices of triangles and PrimitiveVertex to represent
all data associated with a given vertex. Made Actions keep track of
the path through the scene graph they are currently on. Added
RayTriangleIntersection based on paper from Journal of Graphics
Tools. Augmented DisplayShelf demo with click-selection of movie
titles and reduced priority of background loading thread to avoid
rendering hiccups.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/joglutils/trunk@47 83d24430-9974-4f80-8418-2cc3294053b9
23 files changed, 1162 insertions, 56 deletions
diff --git a/build/joglutils.jar b/build/joglutils.jar Binary files differindex 33d954e..e46dd2a 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 34f14ea..b206cdb 100644 --- a/src/net/java/joglutils/msg/actions/Action.java +++ b/src/net/java/joglutils/msg/actions/Action.java @@ -70,6 +70,13 @@ public abstract class Action { altered by the nodes the action traverses. */ public abstract State getState(); + /** Returns the path to the node currently being traversed. The + caller should make a copy of this path if it is desired to keep + it around. */ + public Path getPath() { + return path; + } + private Object[] argTmp = new Object[2]; /** Invokes the appropriate action method for the given Node. */ protected void apply(ActionTable table, Node node) { @@ -78,10 +85,28 @@ public abstract class Action { if (m != null) { argTmp[0] = this; argTmp[1] = node; - m.invoke(null, argTmp); + push(node); + try { + m.invoke(null, argTmp); + } finally { + pop(); + } } } catch (Exception e) { throw new RuntimeException(e); } } + + private Path path = new Path(); + /** Pushes the given Node onto the internal Path this Action + maintains during traversal. */ + private void push(Node node) { + path.add(node); + } + + /** Pops the given Node off the internal Path this Action maintains + during traversal. */ + private void pop() { + path.remove(path.size() - 1); + } } diff --git a/src/net/java/joglutils/msg/actions/GLRenderAction.java b/src/net/java/joglutils/msg/actions/GLRenderAction.java index 7b2fb56..b058e13 100644 --- a/src/net/java/joglutils/msg/actions/GLRenderAction.java +++ b/src/net/java/joglutils/msg/actions/GLRenderAction.java @@ -42,7 +42,6 @@ import java.lang.reflect.*; import javax.media.opengl.*; import javax.media.opengl.glu.*; -import net.java.joglutils.msg.elements.*; import net.java.joglutils.msg.misc.*; import net.java.joglutils.msg.nodes.*; diff --git a/src/net/java/joglutils/msg/actions/RayPickAction.java b/src/net/java/joglutils/msg/actions/RayPickAction.java new file mode 100644 index 0000000..633d7c4 --- /dev/null +++ b/src/net/java/joglutils/msg/actions/RayPickAction.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.actions; + +import java.awt.Component; +import java.lang.reflect.*; +import java.util.*; + +import net.java.joglutils.msg.math.*; +import net.java.joglutils.msg.misc.*; +import net.java.joglutils.msg.nodes.*; + +public class RayPickAction extends Action { + // Boilerplate + private static State defaults = new State(); + /** Returns the default state all instances of this class are initialized with. */ + public static State getDefaultState() { + return defaults; + } + private static ActionTable table = new ActionTable(RayPickAction.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() { + return state; + } + + static { + // 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, RayPickAction.class.getMethod("rayPick", RayPickAction.class, Node.class)); + } catch (Exception e) { + throw new RuntimeException("Error initializing action method for RayPickAction class", e); + } + } + + private int applyDepth = 0; + private Vec2f normalizedPoint; + private Line ray; + + private Line computedRay; + + static class RayPickedPoint implements Comparable<RayPickedPoint> { + float t; + PickedPoint point; + + RayPickedPoint(float t, PickedPoint point) { + this.t = t; + this.point = point; + } + + public boolean equals(Object o) { + return (o == this); + } + + public int compareTo(RayPickedPoint p) { + return (int) Math.signum(t - p.t); + } + } + + // While we're in the process of traversing the scene graph, we + // memorize both the paths to intersected geometry as well as the + // t parameter indicating the distance to the geometry. + private List<RayPickedPoint> tempPickedPoints = new ArrayList<RayPickedPoint>(); + + // The list of picked points, sorted by increasing distance from the camera + private List<PickedPoint> pickedPoints = new ArrayList<PickedPoint>(); + + public void apply(Node node) { + int depth = applyDepth++; + try { + if (depth == 0) { + reset(); + } + apply(table, node); + } finally { + --applyDepth; + if (depth == 0) { + tabulate(); + } + } + } + + /** Sets the point for this RayPickAction based on x and y + coordinates relative to the specified AWT Component. */ + public void setPoint(int x, int y, Component component) { + setNormalizedPoint(new Vec2f((float) x / (float) component.getWidth(), + 1.0f - ((float) y / (float) component.getHeight()))); + } + + /** Sets the normalized point for this RayPickAction, where x and y + are relative to the lower-left of the viewport and range from + [0..1]. */ + public void setNormalizedPoint(Vec2f normalizedPoint) { + this.normalizedPoint = normalizedPoint; + ray = null; + computedRay = null; + } + + /** Sets the ray in world coordinates that this RayPickAction should + use for its computation. */ + public void setRay(Line ray) { + this.ray = ray; + computedRay = ray; + normalizedPoint = null; + } + + /** Returns the list of points this action selected during the last + traversal, sorted in increasing order of distance from the + origin. Typically applications will only need to deal with the + first point in the returned list. */ + public List<PickedPoint> getPickedPoints() { + return pickedPoints; + } + + /** Returns the computed 3D ray in world coordinates that this + RayPickAction is using for its picking. If the action is + configured with on-screen coordinates instead of with a 3D ray, + then this is automatically updated every time the action + traverses a Camera node. May return null if this has not been + computed yet. End users should not need to call this method. */ + public Line getComputedRay() { + return computedRay; + } + + /** Called during scene graph traversal to update the 3D ray + associated with this action if it was configured with on-screen + coordinates. End users should not need to call this method. */ + public void recomputeRay(Camera camera) { + if (normalizedPoint != null && ray == null) { + if (computedRay == null) { + computedRay = new Line(); + } + camera.unproject(normalizedPoint, computedRay); + } + } + + /** Registers a picked point with the RayPickAction during scene + graph traversal. The t argument is the time parameter indicating + the distance from the camera. A reference to the PickedPoint is + maintained internally so the caller should add a copy if the + original is still mutable. End users should not need to call + this method. */ + public void addPickedPoint(PickedPoint p, float t) { + tempPickedPoints.add(new RayPickedPoint(t, p)); + } + + private void reset() { + tempPickedPoints.clear(); + pickedPoints.clear(); + } + + private void tabulate() { + Collections.sort(tempPickedPoints); + for (RayPickedPoint p : tempPickedPoints) { + pickedPoints.add(p.point); + } + } + + /** Action method which dispatches to per-node rendering functionality. */ + public static void rayPick(RayPickAction action, Node node) { + node.rayPick(action); + } +} diff --git a/src/net/java/joglutils/msg/impl/RayTriangleIntersection.java b/src/net/java/joglutils/msg/impl/RayTriangleIntersection.java new file mode 100644 index 0000000..13d49e9 --- /dev/null +++ b/src/net/java/joglutils/msg/impl/RayTriangleIntersection.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.impl; + +import net.java.joglutils.msg.math.*; + +/** Intersection of ray with triangle. Computes parameteric t along + with barycentric coordinates (u, v) indicating weight of vert1 and + vert2 (weight of vert0 = 1 - u - v) which can be used to + interpolate normals and texture coordinates. Two versions, one for + two-sided triangle casts and one including backface + culling. Algorithm from Moller, Trumbore, "Fast, Minimum Storage + Ray / Triangle Intersection", Journal of Graphics Tools, Volume 2, + Number 1, 1997, pp. 21-28. */ + +public class RayTriangleIntersection { + private Vec3f edge1 = new Vec3f(); + private Vec3f edge2 = new Vec3f(); + private Vec3f tvec = new Vec3f(); + private Vec3f pvec = new Vec3f(); + private Vec3f qvec = new Vec3f(); + + private static final float EPSILON = 0.000001f; + + public boolean intersectTriangle(Line ray, + Vec3f vert0, + Vec3f vert1, + Vec3f vert2, + Vec3f tuv) { + // Find vectors for two edges sharing vert0 + edge1.sub(vert1, vert0); + edge2.sub(vert2, vert0); + + // Begin calculating determinant -- also used to calculate U parameter + pvec.cross(ray.getDirection(), edge2); + + // If determinant is near zero, ray lies in plane of triangle + float det = edge1.dot(pvec); + + if (det > -EPSILON && det < EPSILON) + return false; + + float invDet = 1.0f / det; + + // Calculate distance from vert0 to ray origin + tvec.sub(ray.getPoint(), vert0); + + // Calculate U parameter and test bounds + float u = tvec.dot(pvec) * invDet; + if (u < 0.0f || u > 1.0f) + return false; + + // Prepare to test V parameter + qvec.cross(tvec, edge1); + + // Calculate V parameter and test bounds + float v = ray.getDirection().dot(qvec) * invDet; + if (v < 0.0f || (u + v) > 1.0f) + return false; + + // Calculate t, ray intersects triangle + float t = edge2.dot(qvec) * invDet; + + tuv.set(t, u, v); + return true; + } + + public boolean intersectTriangleBackfaceCulling(Line ray, + Vec3f vert0, + Vec3f vert1, + Vec3f vert2, + Vec3f tuv) { + // Find vectors for two edges sharing vert0 + edge1.sub(vert1, vert0); + edge2.sub(vert2, vert0); + + // Begin calculating determinant -- also used to calculate U parameter + pvec.cross(ray.getDirection(), edge2); + + // If determinant is near zero, ray lies in plane of triangle + float det = edge1.dot(pvec); + + if (det < EPSILON) + return false; + + // Calculate distance from vert0 to ray origin + tvec.sub(ray.getPoint(), vert0); + + // Calculate U parameter and test bounds + float u = tvec.dot(pvec); + if (u < 0.0f || u > det) + return false; + + // Prepare to test V parameter + qvec.cross(tvec, edge1); + + // Calculate V parameter and test bounds + float v = ray.getDirection().dot(qvec); + if (v < 0.0f || (u + v) > det) + return false; + + // Calculate t, scale parameters, ray intersects triangle + float t = edge2.dot(qvec); + float invDet = 1.0f / det; + t *= invDet; + u *= invDet; + v *= invDet; + tuv.set(t, u, v); + return true; + } +} diff --git a/src/net/java/joglutils/msg/math/Line.java b/src/net/java/joglutils/msg/math/Line.java index 2004c08..c599e60 100644 --- a/src/net/java/joglutils/msg/math/Line.java +++ b/src/net/java/joglutils/msg/math/Line.java @@ -59,9 +59,9 @@ public class Line { <b>point</b>. <b>direction</b> does not need to be normalized but must not be the zero vector. */ public Line(Vec3f direction, Vec3f point) { - direction = new Vec3f(direction); - direction.normalize(); - point = new Vec3f(point); + this.direction = new Vec3f(direction); + this.direction.normalize(); + this.point = new Vec3f(point); alongVec = new Vec3f(); recalc(); } diff --git a/src/net/java/joglutils/msg/math/Mat4f.java b/src/net/java/joglutils/msg/math/Mat4f.java index b0cc67e..fc4d955 100644 --- a/src/net/java/joglutils/msg/math/Mat4f.java +++ b/src/net/java/joglutils/msg/math/Mat4f.java @@ -239,6 +239,16 @@ public class Mat4f { dest.set(rc, tmp); } } + + /** Transforms the given line (origin plus direction) by this + matrix. */ + public Line xformLine(Line line) { + Vec3f pt = new Vec3f(); + Vec3f dir = new Vec3f(); + xformPt(line.getPoint(), pt); + xformDir(line.getDirection(), dir); + return new Line(dir, pt); + } /** Copies data in column-major (OpenGL format) order into passed float array, which must have length 16 or greater. */ diff --git a/src/net/java/joglutils/msg/misc/Path.java b/src/net/java/joglutils/msg/misc/Path.java new file mode 100644 index 0000000..0743e86 --- /dev/null +++ b/src/net/java/joglutils/msg/misc/Path.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.misc; + +import java.util.*; + +import net.java.joglutils.msg.nodes.*; + +/** Represents a path through the scene graph. The topmost node is at + index 0 and subsequent child nodes are at later indices. */ + +public class Path extends ArrayList<Node> { + /** Returns a copy of this path. */ + public Path copy() { + return (Path) clone(); + } +} diff --git a/src/net/java/joglutils/msg/misc/PickedPoint.java b/src/net/java/joglutils/msg/misc/PickedPoint.java new file mode 100644 index 0000000..20874b4 --- /dev/null +++ b/src/net/java/joglutils/msg/misc/PickedPoint.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.misc; + +/** Represents a point on a piece of geometry picked for example by a + RayPickAction. */ + +public class PickedPoint extends PrimitiveVertex { + private Path path; + + public Object clone() { + PickedPoint point = (PickedPoint) super.clone(); + if (path != null) { + point.path = path.copy(); + } + return point; + } + + /** Performs a "deep copy" of this PickedPoint so that it shares + none of its contents with this one. */ + public PickedPoint copy() { + return (PickedPoint) clone(); + } + + /** Sets the path to the picked geometry. Note that this simply + retains a reference to the given Path, so the Path should be + copied if it can later be changed (for example, if it the + internal Path in an Action). */ + public void setPath(Path path) { + this.path = path; + } + + /** Returns the path to the picked geometry. */ + public Path getPath() { + return path; + } +} diff --git a/src/net/java/joglutils/msg/misc/PrimitiveVertex.java b/src/net/java/joglutils/msg/misc/PrimitiveVertex.java new file mode 100644 index 0000000..24b9e46 --- /dev/null +++ b/src/net/java/joglutils/msg/misc/PrimitiveVertex.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.misc; + +import net.java.joglutils.msg.math.*; + +/** Represents a vertex on a primitive, including pieces of relevant + information such as location, surface normal and texture + coordinates. */ + +public class PrimitiveVertex implements Cloneable { + private Vec3f coord; + private Vec2f texCoord; + private Vec4f color; + private Vec3f normal; + + public PrimitiveVertex() {} + + public Object clone() { + try { + PrimitiveVertex vtx = (PrimitiveVertex) super.clone(); + if (coord != null) { + vtx.setCoord(new Vec3f(coord)); + } + if (texCoord != null) { + vtx.setTexCoord(new Vec2f(texCoord)); + } + if (color != null) { + vtx.setColor(new Vec4f(color)); + } + if (normal != null) { + vtx.setNormal(new Vec3f(normal)); + } + return vtx; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + /** Performs a "deep copy" of this PrimitiveVertex so that it shares + none of its contents with this one. */ + public PrimitiveVertex copy() { + return (PrimitiveVertex) clone(); + } + + /** Sets the coordinate in this PrimitiveVertex. Refers to the passed vector by reference. */ + public void setCoord(Vec3f coord) { this.coord = coord; } + /** Returns the coordinate in this PrimitiveVertex, or null if it is not known. */ + public Vec3f getCoord() { return coord; } + + /** Sets the texture coordinate in this PrimitiveVertex. Refers to the passed vector by reference. */ + public void setTexCoord(Vec2f texCoord) { this.texCoord = texCoord; } + /** Returns the texture coordinate in this PrimitiveVertex, or null if it is not known. */ + public Vec2f getTexCoord() { return texCoord; } + + /** Sets the color in this PrimitiveVertex. Refers to the passed vector by reference. */ + public void setColor(Vec4f color) { this.color = color; } + /** Returns the color in this PrimitiveVertex, or null if it is not known. */ + public Vec4f getColor() { return color; } + + /** Sets the normal in this PrimitiveVertex. Refers to the passed vector by reference. */ + public void setNormal(Vec3f normal) { this.normal = normal; } + /** Returns the normal in this PrimitiveVertex, or null if it is not known. */ + public Vec3f getNormal() { return normal; } +} diff --git a/src/net/java/joglutils/msg/misc/TriangleCallback.java b/src/net/java/joglutils/msg/misc/TriangleCallback.java new file mode 100644 index 0000000..61140e2 --- /dev/null +++ b/src/net/java/joglutils/msg/misc/TriangleCallback.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.misc; + +/** Supports iteration over the triangles inside shapes. */ + +public interface TriangleCallback { + /** Called for each triangle generated during enumeration of the + triangles in a particular shape. Certain elements of the + PrimitiveVertex objects may be null if not set, such as normals + or texture coordinates. Note that the PrimitiveVertex objects + may be reused between calls, so users should copy them if + necessary. + + @param triangleIndex the index within the set of triangles [0..num tris - 1] + @param v0 primitive vertex 0 + @param i0 index of primitive vertex 0 in the current coordinates + @param v1 primitive vertex 1 + @param i1 index of primitive vertex 1 in the current coordinates + @param v2 primitive vertex 2 + @param i2 index of primitive vertex 2 in the current coordinates + */ + public void triangleCB(int triangleIndex, + PrimitiveVertex v0, + int i0, + PrimitiveVertex v1, + int i1, + PrimitiveVertex v2, + int i2); +} diff --git a/src/net/java/joglutils/msg/nodes/Camera.java b/src/net/java/joglutils/msg/nodes/Camera.java index 5d405fe..5333f81 100644 --- a/src/net/java/joglutils/msg/nodes/Camera.java +++ b/src/net/java/joglutils/msg/nodes/Camera.java @@ -70,6 +70,10 @@ public abstract class Camera extends Node { GLModelMatrixElement .enable(GLRenderAction.getDefaultState()); GLProjectionMatrixElement .enable(GLRenderAction.getDefaultState()); GLViewingMatrixElement .enable(GLRenderAction.getDefaultState()); + + ModelMatrixElement .enable(RayPickAction.getDefaultState()); + ProjectionMatrixElement.enable(RayPickAction.getDefaultState()); + ViewingMatrixElement .enable(RayPickAction.getDefaultState()); } public Camera() { @@ -84,38 +88,74 @@ public abstract class Camera extends Node { } /** Sets the position of the camera. */ - public void setPosition(Vec3f position) { this.position.set(position); viewDirty = true; } + public void setPosition(Vec3f position) { + this.position.set(position); + viewDirty = true; + } + /** Returns the position of the camera. */ - public Vec3f getPosition() { return position; } + public Vec3f getPosition() { + return position; + } /** Sets the orientation of the camera. */ - public void setOrientation(Rotf orientation) { this.orientation.set(orientation); viewDirty = true; } + public void setOrientation(Rotf orientation) { + this.orientation.set(orientation); + viewDirty = true; + } + /** Returns the orientation of the camera. */ - public Rotf getOrientation() { return orientation; } + public Rotf getOrientation() { + return orientation; + } /** Sets the aspect ratio of the camera. */ - public void setAspectRatio(float aspectRatio) { this.aspectRatio = aspectRatio; projDirty = true; } + public void setAspectRatio(float aspectRatio) { + this.aspectRatio = aspectRatio; + projDirty = true; + } + /** Returns the aspect ratio of the camera. */ - public float getAspectRatio() { return aspectRatio; } + public float getAspectRatio() { + return aspectRatio; + } /** Sets the distance from the eye point to the near clipping plane. */ - public void setNearDistance(float nearDistance) { this.nearDistance = nearDistance; projDirty = true; } + public void setNearDistance(float nearDistance) { + this.nearDistance = nearDistance; + projDirty = true; + } + /** Returns the distance from the eye point to the near clipping plane. */ - public float getNearDistance() { return nearDistance; } + public float getNearDistance() { + return nearDistance; + } /** Sets the distance from the eye point to the far clipping plane. */ - public void setFarDistance(float farDistance) { this.farDistance = farDistance; projDirty = true; } + public void setFarDistance(float farDistance) { + this.farDistance = farDistance; + projDirty = true; + } + /** Returns the distance from the eye point to the far clipping plane. */ - public float getFarDistance() { return farDistance; } + public float getFarDistance() { + return farDistance; + } /** Sets the distance from the eye point to the focal point of the scene. This is only used for mouse-based interaction with the scene and is not factored in to the rendering process. */ - public void setFocalDistance(float focalDistance) { this.focalDistance = focalDistance; projDirty = true; } + public void setFocalDistance(float focalDistance) { + this.focalDistance = focalDistance; + projDirty = true; + } + /** Returns the distance from the eye point to the focal point of the scene. This is only used for mouse-based interaction with the scene and is not factored in to the rendering process. */ - public float getFocalDistance() { return focalDistance; } + public float getFocalDistance() { + return focalDistance; + } /** Returns the viewing matrix associated with this camera's parameters. */ public Mat4f getViewingMatrix() { @@ -134,6 +174,59 @@ public abstract class Camera extends Node { /** Returns the projection matrix associated with this camera's parameters. */ public abstract Mat4f getProjectionMatrix(); + /** Un-projects the given on-screen point to a line in 3D space + which can be used for picking or other operations. The x and y + coordinates of the point must be in normalized coordinates, + where (0, 0) is the lower-left corner of the viewport and (1, 1) + is the upper-right. Allocates new storage for the returned + Line. */ + public Line unproject(Vec2f point) { + Line line = new Line(); + unproject(point, line); + return line; + } + + /** Un-projects the given on-screen point in to the given line in 3D + space (in world coordinates) which can be used for picking or + other operations. The x and y coordinates of the point must be + in normalized coordinates, where (0, 0) is the lower-left corner + of the viewport and (1, 1) is the upper-right. */ + public void unproject(Vec2f point, Line line) throws SingularMatrixException { + // First, we are going to compute the 3D point which corresponds + // to the given point on the near plane. Map the screen + // coordinates to the (-1, 1) range. + Vec4f pt3d = new Vec4f(2 * point.x() - 1, + 2 * point.y() - 1, + getNearDistance(), + 1); + // Compute the cumulative view and projection matrices + Mat4f mat = new Mat4f(); + mat.mul(getProjectionMatrix(), getViewingMatrix()); + // Compute the inverse of this matrix + mat.invert(); + // Multiply + Vec4f unproj = new Vec4f(); + mat.xformVec(pt3d, unproj); + if (unproj.z() == 0) { + // FIXME: is this the right exception to throw in this case? + throw new SingularMatrixException(); + } + float ooZ = 1.0f / unproj.w(); + Vec3f to = new Vec3f(unproj.x() * ooZ, + unproj.y() * ooZ, + unproj.z() * ooZ); + // FIXME: for orthographic projections, need to do something + // different; can't just use the eye point + Vec3f from = getPosition(); + Vec3f dir = to.minus(from); + + // System.err.println("unprojected point: " + from); + // System.err.println("unprojected dir : " + dir); + + line.setPoint(from); + line.setDirection(dir); + } + public void doAction(Action action) { if (ViewingMatrixElement.isEnabled(action.getState())) { ViewingMatrixElement.set(action.getState(), getViewingMatrix()); @@ -142,4 +235,9 @@ public abstract class Camera extends Node { ProjectionMatrixElement.set(action.getState(), getProjectionMatrix()); } } + + public void rayPick(RayPickAction action) { + doAction(action); + action.recomputeRay(this); + } } diff --git a/src/net/java/joglutils/msg/nodes/Coordinate3.java b/src/net/java/joglutils/msg/nodes/Coordinate3.java index e8e2631..7cae14b 100644 --- a/src/net/java/joglutils/msg/nodes/Coordinate3.java +++ b/src/net/java/joglutils/msg/nodes/Coordinate3.java @@ -50,6 +50,8 @@ public class Coordinate3 extends Node { static { // Enable the elements this node affects for known actions GLCoordinateElement.enable(GLRenderAction.getDefaultState()); + + CoordinateElement .enable(RayPickAction.getDefaultState()); } /** Sets the coordinate data in this node. */ diff --git a/src/net/java/joglutils/msg/nodes/Group.java b/src/net/java/joglutils/msg/nodes/Group.java index f9a961b..df3232d 100644 --- a/src/net/java/joglutils/msg/nodes/Group.java +++ b/src/net/java/joglutils/msg/nodes/Group.java @@ -52,7 +52,7 @@ public class Group extends Node { } /** Adds a child so that it becomes the one with the given index. */ - public void insertChild(Node child, int index) { + public void insertChild(int index, Node child) { children.add(index, child); } @@ -103,7 +103,7 @@ public class Group extends Node { */ public void replaceChild(int index, Node newChild) throws IndexOutOfBoundsException { removeChild(index); - insertChild(newChild, index); + insertChild(index, newChild); } /** Replaces the old child with the new child. This is a convenience diff --git a/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java b/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java index 5e45367..e244f17 100644 --- a/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java +++ b/src/net/java/joglutils/msg/nodes/IndexedTriangleSet.java @@ -40,6 +40,7 @@ package net.java.joglutils.msg.nodes; import java.nio.*; import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.misc.*; /** An IndexedTriangleSet assembles the coordinates specified by a Coordinate3 node, and any auxiliary nodes such as a @@ -48,7 +49,7 @@ import net.java.joglutils.msg.actions.*; (FIXME) rendering support for this node is not yet implemented.) */ -public class IndexedTriangleSet extends Node { +public class IndexedTriangleSet extends TriangleBasedShape { private IntBuffer indices; /** Sets the indices this node uses to group vertices into triangles. */ @@ -64,4 +65,8 @@ public class IndexedTriangleSet extends Node { public void doAction(Action action) { throw new RuntimeException("Not yet implemented"); } + + public void generateTriangles(Action action, TriangleCallback cb) { + 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 e9eefc4..f2ab658 100644 --- a/src/net/java/joglutils/msg/nodes/Node.java +++ b/src/net/java/joglutils/msg/nodes/Node.java @@ -51,4 +51,10 @@ public class Node { 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); } + + /** Support for the built-in RayPickAction. 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 rayPick(RayPickAction action) { doAction(action); } } diff --git a/src/net/java/joglutils/msg/nodes/Shape.java b/src/net/java/joglutils/msg/nodes/Shape.java new file mode 100644 index 0000000..9c88e9e --- /dev/null +++ b/src/net/java/joglutils/msg/nodes/Shape.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.nodes; + +/** The abstract base class for all shapes in the scene graph. */ + +public abstract class Shape extends Node { +} diff --git a/src/net/java/joglutils/msg/nodes/Texture2.java b/src/net/java/joglutils/msg/nodes/Texture2.java index fd1b82a..29ed969 100644 --- a/src/net/java/joglutils/msg/nodes/Texture2.java +++ b/src/net/java/joglutils/msg/nodes/Texture2.java @@ -58,6 +58,8 @@ public class Texture2 extends Node { static { // Enable the elements this node affects for known actions GLTextureElement.enable(GLRenderAction.getDefaultState()); + + TextureElement .enable(RayPickAction.getDefaultState()); } /** Represents the OpenGL MODULATE texture environment mode. */ @@ -144,4 +146,11 @@ public class Texture2 extends Node { TextureElement.set(action.getState(), getTexture(), getTexEnvMode()); } } + + public void rayPick(RayPickAction action) { + // FIXME: because of the issue of potentially not having an OpenGL + // context at the time this is called, the TextureElement should + // be updated to hold a reference to this node, and only the + // GLTextureElement should poll the texture + } } diff --git a/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java b/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java index f7720d2..2782da1 100644 --- a/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java +++ b/src/net/java/joglutils/msg/nodes/TextureCoordinate2.java @@ -50,6 +50,8 @@ public class TextureCoordinate2 extends Node { static { // Enable the elements this node affects for known actions GLTextureCoordinateElement.enable(GLRenderAction.getDefaultState()); + + TextureCoordinateElement .enable(RayPickAction.getDefaultState()); } /** Sets the texture coordinate data in this node. */ diff --git a/src/net/java/joglutils/msg/nodes/Transform.java b/src/net/java/joglutils/msg/nodes/Transform.java index ad5e1bd..044c1b7 100644 --- a/src/net/java/joglutils/msg/nodes/Transform.java +++ b/src/net/java/joglutils/msg/nodes/Transform.java @@ -49,9 +49,13 @@ public class Transform extends Node { 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()); + GLModelMatrixElement .enable(GLRenderAction.getDefaultState()); + GLProjectionMatrixElement.enable(GLRenderAction.getDefaultState()); + GLViewingMatrixElement .enable(GLRenderAction.getDefaultState()); + + ModelMatrixElement .enable(RayPickAction.getDefaultState()); + ProjectionMatrixElement.enable(RayPickAction.getDefaultState()); + ViewingMatrixElement .enable(RayPickAction.getDefaultState()); } public Transform() { diff --git a/src/net/java/joglutils/msg/nodes/TriangleBasedShape.java b/src/net/java/joglutils/msg/nodes/TriangleBasedShape.java new file mode 100644 index 0000000..c234695 --- /dev/null +++ b/src/net/java/joglutils/msg/nodes/TriangleBasedShape.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + */ + +package net.java.joglutils.msg.nodes; + +import java.util.*; + +import net.java.joglutils.msg.actions.*; +import net.java.joglutils.msg.elements.*; +import net.java.joglutils.msg.impl.*; +import net.java.joglutils.msg.math.*; +import net.java.joglutils.msg.misc.*; + +/** The abstract base class for all shapes in the scene graph which + render themselves as a collection of triangles. */ + +public abstract class TriangleBasedShape extends Shape { + /** Based on the state in the given Action, calls the specified + triangle callback for each triangle in the shape. Coordinates + are specified in the local coordinate system of this shape; the + caller must transform them into the world coordinate system if + necessary. */ + public abstract void generateTriangles(Action action, + TriangleCallback cb); + + public void rayPick(final RayPickAction action) { + // The RayPickAction holds the picking ray in world coordinates. + // Transform this ray into local coordinates to do intersection testing + // Fetch the current local-to-world matrix + Mat4f mat = new Mat4f(ModelMatrixElement.getInstance(action.getState()).getMatrix()); + // Invert it to get the world-to-local matrix + mat.invert(); + // Transform the RayPickAction's ray by this matrix + final Line ray = mat.xformLine(action.getComputedRay()); + + // Temporaries + final RayTriangleIntersection rti = new RayTriangleIntersection(); + final Vec3f tuv = new Vec3f(); + + // OK, ready to test + generateTriangles(action, new TriangleCallback() { + public void triangleCB(int triangleIndex, + PrimitiveVertex v0, + int i0, + PrimitiveVertex v1, + int i1, + PrimitiveVertex v2, + int i2) { + if (rti.intersectTriangle(ray, + v0.getCoord(), + v1.getCoord(), + v2.getCoord(), + tuv)) { + // Compute at least the 3D coordinate of the intersection + // point for now + // FIXME: need to compute other things such as the texture + // coordinates + PickedPoint p = new PickedPoint(); + // Compute weights of three vertices + float a = 1.0f - tuv.y() - tuv.z(); + float b = tuv.y(); + float c = tuv.z(); + Vec3f loc = v0.getCoord().times(a).plus(v1.getCoord().times(b)).plus(v2.getCoord().times(c)); + p.setCoord(loc); + p.setPath(action.getPath().copy()); + action.addPickedPoint(p, tuv.x()); + } + } + }); + } +} diff --git a/src/net/java/joglutils/msg/nodes/TriangleSet.java b/src/net/java/joglutils/msg/nodes/TriangleSet.java index 0692738..8f708cb 100644 --- a/src/net/java/joglutils/msg/nodes/TriangleSet.java +++ b/src/net/java/joglutils/msg/nodes/TriangleSet.java @@ -37,6 +37,9 @@ package net.java.joglutils.msg.nodes; +import java.nio.*; +import java.util.*; + import javax.media.opengl.*; import com.sun.opengl.util.texture.*; @@ -49,7 +52,7 @@ import net.java.joglutils.msg.misc.*; node, and any auxiliary nodes such as a TextureCoordinate2 node, into a set of triangles. */ -public class TriangleSet extends Node { +public class TriangleSet extends TriangleBasedShape { private int numTriangles; /** Sets the number of triangles this TriangleSet references. */ @@ -110,6 +113,102 @@ public class TriangleSet extends Node { } } + public void generateTriangles(Action action, TriangleCallback cb) { + State state = action.getState(); + FloatBuffer coords = null; + FloatBuffer texCoords = null; + // FIXME: normals and lighting not supported yet + // FloatBuffer normals = null; + FloatBuffer colors = null; + if (CoordinateElement.isEnabled(state)) { + coords = CoordinateElement.get(state); + } + // No point in continuing if we don't have coordinates + if (coords == null) + return; + if (TextureCoordinateElement.isEnabled(state)) { + texCoords = TextureCoordinateElement.get(state); + } + // if (NormalElement.isEnabled(state)) { + // texCoords = NormalElement.get(state); + // } + if (ColorElement.isEnabled(state)) { + colors = ColorElement.get(state); + } + PrimitiveVertex v0 = new PrimitiveVertex(); + PrimitiveVertex v1 = new PrimitiveVertex(); + PrimitiveVertex v2 = new PrimitiveVertex(); + v0.setCoord(new Vec3f()); + v1.setCoord(new Vec3f()); + v2.setCoord(new Vec3f()); + if (texCoords != null) { + v0.setTexCoord(new Vec2f()); + v1.setTexCoord(new Vec2f()); + v2.setTexCoord(new Vec2f()); + } + if (colors != null) { + v0.setColor(new Vec4f()); + v1.setColor(new Vec4f()); + v2.setColor(new Vec4f()); + } + + int coordIdx = 0; + for (int i = 0; i < numTriangles; i++) { + // Vertex 0 + v0.getCoord().set(coords.get(3 * coordIdx + 0), + coords.get(3 * coordIdx + 1), + coords.get(3 * coordIdx + 2)); + if (texCoords != null) { + v0.getTexCoord().set(texCoords.get(2 * coordIdx + 0), + texCoords.get(2 * coordIdx + 1)); + } + if (colors != null) { + v0.getColor().set(colors.get(4 * coordIdx + 0), + colors.get(4 * coordIdx + 1), + colors.get(4 * coordIdx + 2), + colors.get(4 * coordIdx + 3)); + } + + // Vertex 1 + v1.getCoord().set(coords.get(3 * (coordIdx + 1) + 0), + coords.get(3 * (coordIdx + 1) + 1), + coords.get(3 * (coordIdx + 1) + 2)); + if (texCoords != null) { + v1.getTexCoord().set(texCoords.get(2 * (coordIdx + 1) + 0), + texCoords.get(2 * (coordIdx + 1) + 1)); + } + if (colors != null) { + v1.getColor().set(colors.get(4 * (coordIdx + 1) + 0), + colors.get(4 * (coordIdx + 1) + 1), + colors.get(4 * (coordIdx + 1) + 2), + colors.get(4 * (coordIdx + 1) + 3)); + } + + // Vertex 2 + v2.getCoord().set(coords.get(3 * (coordIdx + 2) + 0), + coords.get(3 * (coordIdx + 2) + 1), + coords.get(3 * (coordIdx + 2) + 2)); + if (texCoords != null) { + v2.getTexCoord().set(texCoords.get(2 * (coordIdx + 2) + 0), + texCoords.get(2 * (coordIdx + 2) + 1)); + } + if (colors != null) { + v2.getColor().set(colors.get(4 * (coordIdx + 2) + 0), + colors.get(4 * (coordIdx + 2) + 1), + colors.get(4 * (coordIdx + 2) + 2), + colors.get(4 * (coordIdx + 2) + 3)); + } + + // Call callback + cb.triangleCB(i, + v0, 3 * i + 0, + v1, 3 * i + 1, + v2, 3 * i + 2); + + coordIdx += 3; + } + } + // 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(); diff --git a/src/net/java/joglutils/msg/test/DisplayShelf.java b/src/net/java/joglutils/msg/test/DisplayShelf.java index 73a63de..c6ae2b7 100644 --- a/src/net/java/joglutils/msg/test/DisplayShelf.java +++ b/src/net/java/joglutils/msg/test/DisplayShelf.java @@ -73,6 +73,7 @@ public class DisplayShelf extends Container { static class TitleGraph { String url; + Separator sep = new Separator(); Transform xform = new Transform(); Texture2 texture = new Texture2(); Coordinate3 coords = new Coordinate3(); @@ -83,9 +84,11 @@ public class DisplayShelf extends Container { } private Group root; + private Separator imageRoot; private String[] images; private List<TitleGraph> titles = new ArrayList<TitleGraph>(); private int targetIndex; + private JSlider slider; // This encodes both the current position and the animation alpha private float currentIndex; // If the difference between the current index and target index is > @@ -130,7 +133,7 @@ public class DisplayShelf extends Container { final List<TitleGraph> queuedGraphs = new ArrayList<TitleGraph>(); queuedGraphs.addAll(titles); - new Thread(new Runnable() { + Thread loaderThread = new Thread(new Runnable() { public void run() { while (queuedGraphs.size() > 0) { TitleGraph graph = queuedGraphs.remove(0); @@ -152,7 +155,10 @@ public class DisplayShelf extends Container { } } } - }).start(); + }); + // Avoid having the loader thread preempt the rendering thread + loaderThread.setPriority(Thread.MIN_PRIORITY + 1); + loaderThread.start(); } private void setTargetIndex(int index) { @@ -219,8 +225,9 @@ public class DisplayShelf extends Container { camera.setFarDistance(20.0f); canvas = new GLCanvas(); canvas.addGLEventListener(new Listener()); + canvas.addMouseListener(new MListener()); add(canvas, BorderLayout.CENTER); - final JSlider slider = new JSlider(0, images.length - 1, 0); + slider = new JSlider(0, images.length - 1, 0); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { setTargetIndex(slider.getValue()); @@ -239,7 +246,7 @@ public class DisplayShelf extends Container { root.removeAllChildren(); // The images - Separator imageRoot = new Separator(); + imageRoot = new Separator(); // The mirrored images, under the floor Separator mirrorRoot = new Separator(); @@ -274,7 +281,7 @@ public class DisplayShelf extends Container { titles.add(graph); computeCoords(graph.coords, DEFAULT_ASPECT_RATIO); graph.xform.getTransform().setTranslation(new Vec3f(i, 0, 0)); - Separator sep = new Separator(); + Separator sep = graph.sep; sep.addChild(graph.xform); sep.addChild(graph.coords); sep.addChild(graph.texture); @@ -375,6 +382,27 @@ public class DisplayShelf extends Container { public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} } + class MListener extends MouseAdapter { + RayPickAction ra = new RayPickAction(); + + public void mousePressed(MouseEvent e) { + ra.setPoint(e.getX(), e.getY(), e.getComponent()); + // Apply to the scene root + ra.apply(root); + List<PickedPoint> pickedPoints = ra.getPickedPoints(); + Path p = null; + if (!pickedPoints.isEmpty()) + p = pickedPoints.get(0).getPath(); + if (p != null && p.size() > 1) { + int idx = imageRoot.findChild(p.get(p.size() - 2)); + if (idx >= 0) { + // Need to keep the slider and this mechanism in sync + slider.setValue(idx); + } + } + } + } + public static void main(String[] args) { Frame f = new Frame("Display Shelf test"); f.setLayout(new BorderLayout()); @@ -390,33 +418,58 @@ public class DisplayShelf extends Container { // The images to configure the shelf with String[] images = { - "http://a1.phobos.apple.com/r10/Music/05/7d/c3/dj.umbuvrfe.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/cb/9a/b3/mzi.krksguze.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/94/8d/83/dj.jionwnuf.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/26/43/02/dj.dgnjindw.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/69/2a/63/mzi.wpfmtfzp.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/17/e1/88/dj.gcajwhco.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/21/f6/32/dj.glzycglj.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/d1/6b/3b/mzi.pajmxsmk.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/f6/a7/b2/dj.lamcsbwx.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/84/a5/4f/dj.nqvsikaq.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/7d/c3/23/dj.elyzoipc.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/80/a5/8c/dj.oidpsvzg.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/d1/b2/cf/dj.moyzjiht.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/49/a3/59/mzi.ssjpuxwt.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/9b/8f/7c/dj.qizpbris.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/b1/4f/c8/dj.uadqyjbr.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/d4/31/df/mzi.pqzeferc.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/4b/88/a7/dj.jhotijvb.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/a8/a9/36/dj.asztraij.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/d6/6b/c4/mzi.dricykdh.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Features/d4/81/a3/dj.tpysowpf.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/4f/2c/a6/dj.cawuddxy.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/d8/9c/8a/mzi.vmajyyha.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/00/5c/31/mzi.tuyoxwib.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/da/c8/e2/mzi.sanzeosx.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/43/cc/0e/dj.zfqfgoas.200x200-75.jpg", - "http://a1.phobos.apple.com/r10/Music/73/70/13/mzi.uswlslxx.200x200-75.jpg" + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.jsepedzf.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.wvbmknhn.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.oorrjicu.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.woofnkar.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.tapbaxpy.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.awlngumx.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.bpuzrjch.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.nqarjlzt.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.hgadlawz.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.sdfnrwzj.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.vtbicehh.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.lhgtckcs.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.tbwyqyqm.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.eimndamh.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.nxvdfcwt.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.njoydoqk.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.ikfbfqzh.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.niqwioqm.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.tqqldmqe.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.ynokefwv.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.jodjmgxs.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.yhdaeino.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.xmgrrxef.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.pahnmknr.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.sbkwhrik.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.hwbcjnfx.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.umbuvrfe.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.krksguze.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.jionwnuf.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.dgnjindw.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.wpfmtfzp.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.gcajwhco.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.glzycglj.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.pajmxsmk.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.lamcsbwx.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.nqvsikaq.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.elyzoipc.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.oidpsvzg.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.moyzjiht.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.qizpbris.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.uadqyjbr.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.pqzeferc.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.jhotijvb.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.asztraij.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.dricykdh.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.tpysowpf.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.cawuddxy.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.vmajyyha.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.tuyoxwib.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.sanzeosx.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/dj.zfqfgoas.200x200-75.jpg", + "http://download.java.net/media/jogl/builds/ds_tmp/mzi.uswlslxx.200x200-75.jpg" }; Separator root = new Separator(); |