001: /*
002: * $RCSfile: ColorInterpolator.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:20 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.Color3f;
035: import java.util.Enumeration;
036:
037: /**
038: * Color interpolation behavior. This class defines a behavior that
039: * modifies the ambient, emissive, diffuse, or specular color of its
040: * target material object by linearly interpolating between a pair of
041: * specified colors, using the value generated by the specified Alpha
042: * object.
043: * The behavior modifies the color specified by the
044: * Material's colorTarget attribute, one of: AMBIENT, EMISSIVE,
045: * DIFFUSE, SPECULAR, or AMBIENT_AND_DIFFUSE.
046: * The ALLOW_COMPONENT_READ bit must be set in the Material object in
047: * order for the Material's colorTarget to be read.
048: * If the Material object's ALLOW_COMPONENT_READ bit is <i>not</i> set, the
049: * diffuse component will be modified.
050: *
051: * @see Material
052: */
053:
054: public class ColorInterpolator extends Interpolator {
055:
056: Material target;
057: Color3f startColor = new Color3f();
058: Color3f endColor = new Color3f();
059: Color3f newColor = new Color3f();
060:
061: // We can't use a boolean flag since it is possible
062: // that after alpha change, this procedure only run
063: // once at alpha.finish(). So the best way is to
064: // detect alpha value change.
065: private float prevAlphaValue = Float.NaN;
066: private int prevColorTarget = -1;
067: private WakeupCriterion passiveWakeupCriterion = (WakeupCriterion) new WakeupOnElapsedFrames(
068: 0, true);
069:
070: // non-public, no parameter constructor used by cloneNode
071: ColorInterpolator() {
072: }
073:
074: /**
075: * Constructs a trivial color interpolator with a specified target,
076: * a starting color of black, and an ending color of white.
077: * @param alpha the alpha object for this interpolator
078: * @param target the material component object whose
079: * color is affected by this color interpolator
080: */
081: public ColorInterpolator(Alpha alpha, Material target) {
082:
083: super (alpha);
084:
085: this .target = target;
086: this .startColor.set(0.0f, 0.0f, 0.0f);
087: this .endColor.set(1.0f, 1.0f, 1.0f);
088: }
089:
090: /**
091: * Constructs a color interpolator with the specified target,
092: * starting color, and ending color.
093: * @param alpha the alpha object for this interpolator
094: * @param target the material component object whose
095: * color is affected by this color interpolator
096: * @param startColor the starting color
097: * @param endColor the ending color
098: */
099: public ColorInterpolator(Alpha alpha, Material target,
100: Color3f startColor, Color3f endColor) {
101:
102: super (alpha);
103:
104: this .target = target;
105: this .startColor.set(startColor);
106: this .endColor.set(endColor);
107: }
108:
109: /**
110: * This method sets the startColor for this interpolator.
111: * @param color the new start color
112: */
113: public void setStartColor(Color3f color) {
114: startColor.set(color);
115: prevAlphaValue = Float.NaN;
116: }
117:
118: /**
119: * This method retrieves this interpolator's startColor.
120: * @param color the vector that will receive the interpolator's start color
121: */
122: public void getStartColor(Color3f color) {
123: color.set(startColor);
124: }
125:
126: /**
127: * This method sets the endColor for this interpolator.
128: * @param color the new end color
129: */
130: public void setEndColor(Color3f color) {
131: endColor.set(color);
132: prevAlphaValue = Float.NaN;
133: }
134:
135: /**
136: * This method retrieves this interpolator's endColor.
137: * @param color the vector that will receive the interpolator's end color
138: */
139: public void getEndColor(Color3f color) {
140: color.set(endColor);
141: }
142:
143: /**
144: * This method sets the target material component object for
145: * this interpolator.
146: * @param target the material component object whose
147: * color is affected by this color interpolator
148: */
149: public void setTarget(Material target) {
150: this .target = target;
151: prevAlphaValue = Float.NaN;
152: }
153:
154: /**
155: * This method retrieves this interpolator's target material
156: * component object.
157: * @return the interpolator's target material component object
158: */
159: public Material getTarget() {
160: return target;
161: }
162:
163: // The ColorInterpolator's initialize routine uses the default
164: // initialization routine.
165:
166: /**
167: * This method is invoked by the behavior scheduler every frame.
168: * It maps the alpha value that corresponds to the current time
169: * into a color value and updates the ambient, emissive, diffuse,
170: * or specular color (or both the ambient and diffuse color) of
171: * the specified target Material object with this new color value.
172: *
173: * @param criteria an enumeration of the criteria that caused the
174: * stimulus
175: */
176: public void processStimulus(Enumeration criteria) {
177:
178: // Handle stimulus
179: WakeupCriterion criterion = passiveWakeupCriterion;
180:
181: if (alpha != null) {
182: float value = alpha.value();
183:
184: int colorTarget = Material.DIFFUSE;
185: if (target.getCapability(Material.ALLOW_COMPONENT_READ))
186: colorTarget = target.getColorTarget();
187:
188: if (value != prevAlphaValue
189: || colorTarget != prevColorTarget) {
190: newColor.x = (1.0f - value) * startColor.x + value
191: * endColor.x;
192: newColor.y = (1.0f - value) * startColor.y + value
193: * endColor.y;
194: newColor.z = (1.0f - value) * startColor.z + value
195: * endColor.z;
196:
197: switch (colorTarget) {
198: case Material.AMBIENT:
199: target.setAmbientColor(newColor);
200: break;
201: case Material.AMBIENT_AND_DIFFUSE:
202: target.setAmbientColor(newColor);
203: // fall through
204: case Material.DIFFUSE:
205: target.setDiffuseColor(newColor);
206: break;
207: case Material.EMISSIVE:
208: target.setEmissiveColor(newColor);
209: break;
210: case Material.SPECULAR:
211: target.setSpecularColor(newColor);
212: break;
213: }
214:
215: prevAlphaValue = value;
216: prevColorTarget = colorTarget;
217: }
218:
219: if (!alpha.finished() && !alpha.isPaused()) {
220: criterion = defaultWakeupCriterion;
221: }
222: }
223: wakeupOn(criterion);
224: }
225:
226: /**
227: * Used to create a new instance of the node. This routine is called
228: * by <code>cloneTree</code> to duplicate the current node.
229: * @param forceDuplicate when set to <code>true</code>, causes the
230: * <code>duplicateOnCloneTree</code> flag to be ignored. When
231: * <code>false</code>, the value of each node's
232: * <code>duplicateOnCloneTree</code> variable determines whether
233: * NodeComponent data is duplicated or copied.
234: *
235: * @see Node#cloneTree
236: * @see Node#cloneNode
237: * @see Node#duplicateNode
238: * @see NodeComponent#setDuplicateOnCloneTree
239: */
240: public Node cloneNode(boolean forceDuplicate) {
241: ColorInterpolator ci = new ColorInterpolator();
242: ci.duplicateNode(this , forceDuplicate);
243: return ci;
244: }
245:
246: /**
247: * Copies all ColorInterpolator information from
248: * <code>originalNode</code> into
249: * the current node. This method is called from the
250: * <code>cloneNode</code> method which is, in turn, called by the
251: * <code>cloneTree</code> method.<P>
252: *
253: * @param originalNode the original node to duplicate.
254: * @param forceDuplicate when set to <code>true</code>, causes the
255: * <code>duplicateOnCloneTree</code> flag to be ignored. When
256: * <code>false</code>, the value of each node's
257: * <code>duplicateOnCloneTree</code> variable determines whether
258: * NodeComponent data is duplicated or copied.
259: *
260: * @exception RestrictedAccessException if this object is part of a live
261: * or compiled scenegraph.
262: *
263: * @see Node#duplicateNode
264: * @see Node#cloneTree
265: * @see NodeComponent#setDuplicateOnCloneTree
266: */
267: void duplicateAttributes(Node originalNode, boolean forceDuplicate) {
268: super .duplicateAttributes(originalNode, forceDuplicate);
269:
270: ColorInterpolator ci = (ColorInterpolator) originalNode;
271:
272: ci.getStartColor(startColor);
273: ci.getEndColor(endColor);
274:
275: // this reference will be updated in updateNodeReferences()
276: setTarget(ci.getTarget());
277: }
278:
279: /**
280: * Callback used to allow a node to check if any scene graph objects
281: * referenced
282: * by that node have been duplicated via a call to <code>cloneTree</code>.
283: * This method is called by <code>cloneTree</code> after all nodes in
284: * the sub-graph have been duplicated. The cloned Leaf node's method
285: * will be called and the Leaf node can then look up any object references
286: * by using the <code>getNewObjectReference</code> method found in the
287: * <code>NodeReferenceTable</code> object. If a match is found, a
288: * reference to the corresponding object in the newly cloned sub-graph
289: * is returned. If no corresponding reference is found, either a
290: * DanglingReferenceException is thrown or a reference to the original
291: * object is returned depending on the value of the
292: * <code>allowDanglingReferences</code> parameter passed in the
293: * <code>cloneTree</code> call.
294: * <p>
295: * NOTE: Applications should <i>not</i> call this method directly.
296: * It should only be called by the cloneTree method.
297: *
298: * @param referenceTable a NodeReferenceTableObject that contains the
299: * <code>getNewObjectReference</code> method needed to search for
300: * new object instances.
301: * @see NodeReferenceTable
302: * @see Node#cloneTree
303: * @see DanglingReferenceException
304: */
305: public void updateNodeReferences(NodeReferenceTable referenceTable) {
306: super .updateNodeReferences(referenceTable);
307:
308: // check Material
309: NodeComponent nc = getTarget();
310:
311: if (nc != null) {
312: setTarget((Material) referenceTable
313: .getNewObjectReference(nc));
314: }
315: }
316: }
|