001: /*
002: * $RCSfile: RotationPathInterpolator.java,v $
003: *
004: * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.5 $
028: * $Date: 2008/02/28 20:17:29 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.Quat4f;
035:
036: /**
037: * RotationPathInterpolator behavior. This class defines a behavior
038: * that varies the rotational component of its target TransformGroup
039: * by linearly interpolating among a series of predefined knot/orientation
040: * pairs (using the value generated by the specified Alpha object). The
041: * interpolated orientation is used to generate a rotation transform in
042: * the local coordinate system. The first knot must have a value of 0.0.
043: * The last knot must have a value
044: * of 1.0. An intermediate knot with index k must have a value strictly
045: * greater than any knot with index less than k.
046: */
047:
048: public class RotationPathInterpolator extends PathInterpolator {
049: private Transform3D rotation = new Transform3D();
050:
051: private Quat4f tQuat = new Quat4f();
052:
053: // Array of quaternions at each knot
054: private Quat4f quats[];
055: private float prevInterpolationValue = Float.NaN;
056:
057: // We can't use a boolean flag since it is possible
058: // that after alpha change, this procedure only run
059: // once at alpha.finish(). So the best way is to
060: // detect alpha value change.
061: private float prevAlphaValue = Float.NaN;
062: private WakeupCriterion passiveWakeupCriterion = (WakeupCriterion) new WakeupOnElapsedFrames(
063: 0, true);
064:
065: // non-public, default constructor used by cloneNode
066: RotationPathInterpolator() {
067: }
068:
069: /**
070: * Constructs a new RotationPathInterpolator object that varies the
071: * target TransformGroup node's transform.
072: * @param alpha the alpha object of this interpolator
073: * @param target the TransformGroup node affected by this interpolator
074: * @param axisOfTransform the transform that defines the local coordinate
075: * system in which this interpolator operates
076: * @param knots an array of knot values that specify interpolation points
077: * @param quats an array of quaternion values at the knots
078: * @exception IllegalArgumentException if the lengths of the
079: * knots and quats arrays are not the same.
080: */
081: public RotationPathInterpolator(Alpha alpha, TransformGroup target,
082: Transform3D axisOfTransform, float[] knots, Quat4f[] quats) {
083: super (alpha, target, axisOfTransform, knots);
084:
085: if (knots.length != quats.length)
086: throw new IllegalArgumentException(J3dI18N
087: .getString("RotationPathInterpolator0"));
088:
089: setPathArrays(quats);
090: }
091:
092: /**
093: * Sets the quat value at the specified index for this
094: * interpolator.
095: * @param index the index to be changed
096: * @param quat the new quat value at the index
097: */
098: public void setQuat(int index, Quat4f quat) {
099: this .quats[index].set(quat);
100: }
101:
102: /**
103: * Retrieves the quat value at the specified index.
104: * @param index the index of the value requested
105: * @param quat the quat object that will have the
106: * quat value at index copied into it.
107: */
108: public void getQuat(int index, Quat4f quat) {
109: quat.set(this .quats[index]);
110: }
111:
112: /**
113: * Replaces the existing arrays of knot values and quaternion
114: * values with the specified arrays.
115: * The arrays of knots and quats are copied
116: * into this interpolator object.
117: * @param knots a new array of knot values that specify
118: * interpolation points
119: * @param quats a new array of quaternion values at the knots
120: * @exception IllegalArgumentException if the lengths of the
121: * knots and quats arrays are not the same.
122: *
123: * @since Java 3D 1.2
124: */
125: public void setPathArrays(float[] knots, Quat4f[] quats) {
126: if (knots.length != quats.length)
127: throw new IllegalArgumentException(J3dI18N
128: .getString("RotationPathInterpolator0"));
129:
130: setKnots(knots);
131: setPathArrays(quats);
132: }
133:
134: // Set the specific arrays for this path interpolator
135: private void setPathArrays(Quat4f[] quats) {
136: this .quats = new Quat4f[quats.length];
137: for (int i = 0; i < quats.length; i++) {
138: this .quats[i] = new Quat4f();
139: this .quats[i].set(quats[i]);
140: }
141: }
142:
143: /**
144: * Copies the array of quaternion values from this interpolator
145: * into the specified array.
146: * The array must be large enough to hold all of the quats.
147: * The individual array elements must be allocated by the caller.
148: * @param quats array that will receive the quats
149: *
150: * @since Java 3D 1.2
151: */
152: public void getQuats(Quat4f[] quats) {
153: for (int i = 0; i < this .quats.length; i++) {
154: quats[i].set(this .quats[i]);
155: }
156: }
157:
158: /**
159: * @deprecated As of Java 3D version 1.3, replaced by
160: * <code>TransformInterpolator.seTransformAxis(Transform3D)</code>
161: */
162: public void setAxisOfRotation(Transform3D axisOfRotation) {
163: setTransformAxis(axisOfRotation);
164: }
165:
166: /**
167: * @deprecated As of Java 3D version 1.3, replaced by
168: * <code>TransformInterpolator.getTransformAxis()</code>
169: */
170: public Transform3D getAxisOfRotation() {
171: return getTransformAxis();
172: }
173:
174: // The RotationPathInterpolator's initialize routine uses the default
175: // initialization routine.
176:
177: /**
178: * Computes the new transform for this interpolator for a given
179: * alpha value.
180: *
181: * @param alphaValue alpha value between 0.0 and 1.0
182: * @param transform object that receives the computed transform for
183: * the specified alpha value
184: *
185: * @since Java 3D 1.3
186: */
187: public void computeTransform(float alphaValue, Transform3D transform) {
188: float tt;
189: double quatDot;
190: computePathInterpolation(alphaValue);
191: // For RPATH, take quaternion average and set rotation in TransformGroup
192:
193: if (currentKnotIndex == 0 && currentInterpolationValue == 0f) {
194: tQuat.x = quats[0].x;
195: tQuat.y = quats[0].y;
196: tQuat.z = quats[0].z;
197: tQuat.w = quats[0].w;
198: } else {
199: quatDot = quats[currentKnotIndex].x
200: * quats[currentKnotIndex + 1].x
201: + quats[currentKnotIndex].y
202: * quats[currentKnotIndex + 1].y
203: + quats[currentKnotIndex].z
204: * quats[currentKnotIndex + 1].z
205: + quats[currentKnotIndex].w
206: * quats[currentKnotIndex + 1].w;
207: if (quatDot < 0) {
208: tQuat.x = quats[currentKnotIndex].x
209: + (-quats[currentKnotIndex + 1].x - quats[currentKnotIndex].x)
210: * currentInterpolationValue;
211: tQuat.y = quats[currentKnotIndex].y
212: + (-quats[currentKnotIndex + 1].y - quats[currentKnotIndex].y)
213: * currentInterpolationValue;
214: tQuat.z = quats[currentKnotIndex].z
215: + (-quats[currentKnotIndex + 1].z - quats[currentKnotIndex].z)
216: * currentInterpolationValue;
217: tQuat.w = quats[currentKnotIndex].w
218: + (-quats[currentKnotIndex + 1].w - quats[currentKnotIndex].w)
219: * currentInterpolationValue;
220: } else {
221: tQuat.x = quats[currentKnotIndex].x
222: + (quats[currentKnotIndex + 1].x - quats[currentKnotIndex].x)
223: * currentInterpolationValue;
224: tQuat.y = quats[currentKnotIndex].y
225: + (quats[currentKnotIndex + 1].y - quats[currentKnotIndex].y)
226: * currentInterpolationValue;
227: tQuat.z = quats[currentKnotIndex].z
228: + (quats[currentKnotIndex + 1].z - quats[currentKnotIndex].z)
229: * currentInterpolationValue;
230: tQuat.w = quats[currentKnotIndex].w
231: + (quats[currentKnotIndex + 1].w - quats[currentKnotIndex].w)
232: * currentInterpolationValue;
233: }
234: }
235:
236: tQuat.normalize();
237: rotation.set(tQuat);
238:
239: // construct a Transform3D from: axis * rotation * axisInverse
240: transform.mul(axis, rotation);
241: transform.mul(transform, axisInverse);
242: }
243:
244: /**
245: * Used to create a new instance of the node. This routine is called
246: * by <code>cloneTree</code> to duplicate the current node.
247: * @param forceDuplicate when set to <code>true</code>, causes the
248: * <code>duplicateOnCloneTree</code> flag to be ignored. When
249: * <code>false</code>, the value of each node's
250: * <code>duplicateOnCloneTree</code> variable determines whether
251: * NodeComponent data is duplicated or copied.
252: *
253: * @see Node#cloneTree
254: * @see Node#cloneNode
255: * @see Node#duplicateNode
256: * @see NodeComponent#setDuplicateOnCloneTree
257: */
258: public Node cloneNode(boolean forceDuplicate) {
259: RotationPathInterpolator rpi = new RotationPathInterpolator();
260: rpi.duplicateNode(this , forceDuplicate);
261: return rpi;
262: }
263:
264: /**
265: * Copies all RotationPathInterpolator information from
266: * <code>originalNode</code> into
267: * the current node. This method is called from the
268: * <code>cloneNode</code> method which is, in turn, called by the
269: * <code>cloneTree</code> method.<P>
270: *
271: * @param originalNode the original node to duplicate.
272: * @param forceDuplicate when set to <code>true</code>, causes the
273: * <code>duplicateOnCloneTree</code> flag to be ignored. When
274: * <code>false</code>, the value of each node's
275: * <code>duplicateOnCloneTree</code> variable determines whether
276: * NodeComponent data is duplicated or copied.
277: *
278: * @exception RestrictedAccessException if this object is part of a live
279: * or compiled scenegraph.
280: *
281: * @see Node#duplicateNode
282: * @see Node#cloneTree
283: * @see NodeComponent#setDuplicateOnCloneTree
284: */
285: void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
286: super .duplicateAttributes(originalNode, forceDuplicate);
287:
288: RotationPathInterpolator ri = (RotationPathInterpolator) originalNode;
289:
290: int len = ri.getArrayLengths();
291:
292: // No API available to change size of array, so set here explicitly
293: quats = new Quat4f[len];
294: Quat4f quat = new Quat4f();
295:
296: for (int i = 0; i < len; i++) {
297: quats[i] = new Quat4f();
298: ri.getQuat(i, quat);
299: setQuat(i, quat);
300: }
301:
302: }
303: }
|