001: /*
002: * $RCSfile: Dot3Demo.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.3 $
041: * $Date: 2007/02/09 17:21:36 $
042: * $State: Exp $
043: */
044:
045: package org.jdesktop.j3d.examples.dot3;
046:
047: import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
048: import com.sun.j3d.utils.geometry.GeometryInfo;
049: import com.sun.j3d.utils.image.TextureLoader;
050: import com.sun.j3d.utils.universe.SimpleUniverse;
051: import com.sun.j3d.utils.universe.ViewingPlatform;
052:
053: import java.awt.BorderLayout;
054: import java.awt.Color;
055: import java.awt.Dimension;
056: import java.awt.Graphics2D;
057: import java.awt.GraphicsConfiguration;
058: import java.awt.image.BufferedImage;
059:
060: import java.util.Enumeration;
061:
062: import javax.media.j3d.AmbientLight;
063: import javax.media.j3d.Appearance;
064: import javax.media.j3d.Background;
065: import javax.media.j3d.Behavior;
066: import javax.media.j3d.BoundingSphere;
067: import javax.media.j3d.BranchGroup;
068: import javax.media.j3d.Canvas3D;
069: import javax.media.j3d.DirectionalLight;
070: import javax.media.j3d.GeometryArray;
071: import javax.media.j3d.ImageComponent2D;
072: import javax.media.j3d.Material;
073: import javax.media.j3d.PolygonAttributes;
074: import javax.media.j3d.Shape3D;
075: import javax.media.j3d.TexCoordGeneration;
076: import javax.media.j3d.Texture;
077: import javax.media.j3d.Texture2D;
078: import javax.media.j3d.TextureAttributes;
079: import javax.media.j3d.TextureUnitState;
080: import javax.media.j3d.WakeupOnElapsedFrames;
081:
082: import javax.swing.JFrame;
083: import javax.swing.JPanel;
084:
085: import javax.vecmath.Color3f;
086: import javax.vecmath.Point3d;
087: import javax.vecmath.Vector3f;
088: import org.jdesktop.j3d.examples.Resources;
089:
090: /**
091: * This example program is contributed by Alessandro Borges
092: */
093:
094: /**
095: * <pre>
096: * DOT3 per-pixel lighting demo.
097: * It uses a Normal map and a Light map, both coded as independent textures.
098: * Each pixel color is a vector coded, where color range [0,255] is mapped
099: * as vector in range [-1.0,+1.0].
100: *
101: * A math operation called DOT3 applied to Light vector and Normal vector results
102: * a scalar value, interpreted as light intensity. This operation is made for each
103: * pixel on texture.
104: * Light Intensity = DOT3(light, normal);
105: *
106: * This technique allows complex lighting effects, as bumps, on low polygon count
107: * geometries.
108: * </pre>
109: *
110: */
111:
112: public class Dot3Demo extends JFrame {
113: // a external control panel for this demo
114: private TextureControlPanel ctrlPanel = null;
115: // default bounds used in this application
116: private BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,
117: 0.0, 0.0), 100.0);
118: // TextureUnitStates used in this application
119: TextureUnitState tuLightMap;
120: TextureUnitState tuDOT3NormalMap;
121: TextureUnitState tuColor;
122:
123: /** Where the TUs are applied **/
124: TextureUnitState[] tusArr;
125: /** appearance will be changed at runtime **/
126: Appearance appearance;
127: /** polygonAttributes will be changed at runtime **/
128: PolygonAttributes polygonAttributes;
129:
130: // textures used
131: Texture textureColor;
132: Texture textureDOT3NormalMap;
133: Texture2D textureLightMap;
134: // needs for runtime updates on lightMap
135: ImageComponent2D imageLightMap;
136:
137: // default texture names used
138: String textureColorName = "resources/images/wood.jpg";
139: String textureDOT3NormalMapName = "resources/images/Java3Ddot3.jpg";
140:
141: /**
142: * Constructor.
143: */
144: public Dot3Demo() {
145: super ("Java3D DOT3 demo");
146: try {
147: init();
148: } catch (Exception e) {
149: e.printStackTrace();
150: }
151: }
152:
153: private void init() throws Exception {
154: this .setSize(new Dimension(400, 400));
155: this .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
156:
157: JPanel mainPanel = new JPanel();
158: this .getContentPane().add(mainPanel, null);
159: mainPanel.setLayout(new BorderLayout());
160: // get default configuration for 3D
161: GraphicsConfiguration conf = SimpleUniverse
162: .getPreferredConfiguration();
163: Canvas3D canvas = new Canvas3D(conf);
164: // create simpleUniverse
165: SimpleUniverse su = new SimpleUniverse(canvas);
166: // create sceneGraph and add it to universe
167: BranchGroup sceneGraph = createSceneGraph();
168: su.addBranchGraph(sceneGraph);
169:
170: // This will move the ViewPlatform back a bit so the
171: // objects in the scene can be viewed.
172: su.getViewingPlatform().setNominalViewingTransform();
173:
174: // Ensure at least 5 msec per frame (i.e., < 200Hz)
175: su.getViewer().getView().setMinimumFrameCycleTime(5);
176:
177: // add the behaviors to the ViewingPlatform
178: ViewingPlatform viewingPlatform = su.getViewingPlatform();
179: viewingPlatform.setNominalViewingTransform();
180:
181: // add orbit behavior to ViewingPlatform
182: OrbitBehavior orbit = new OrbitBehavior(canvas,
183: OrbitBehavior.REVERSE_ALL | OrbitBehavior.STOP_ZOOM);
184: orbit.setSchedulingBounds(bounds);
185: viewingPlatform.setViewPlatformBehavior(orbit);
186:
187: mainPanel.add(canvas, BorderLayout.CENTER);
188: this .setVisible(true);
189: //create a control panel to user interaction
190: ctrlPanel = new TextureControlPanel(this );
191: ctrlPanel.setVisible(true);
192: ctrlPanel.setLocation(410, 10);
193: }
194:
195: /**
196: * loads all needed textures, and creates light map texture
197: */
198: private void loadTextures() {
199: try {
200: //java.net.URL urlColor = new java.net.URL("file:" + textureColorName);
201: //java.net.URL urlDot3 = new java.net.URL("file:" + textureDOT3NormalMapName);
202: java.net.URL urlColor = Resources
203: .getResource(textureColorName);
204: java.net.URL urlDot3 = Resources
205: .getResource(textureDOT3NormalMapName);
206:
207: // loading textures
208: textureColor = new TextureLoader(urlColor, this )
209: .getTexture();
210: textureDOT3NormalMap = new TextureLoader(urlDot3, this )
211: .getTexture();
212:
213: // create Image for textureLightMap
214: BufferedImage image = new BufferedImage(256, 256,
215: BufferedImage.TYPE_INT_RGB);
216: Graphics2D graphics = image.createGraphics();
217: graphics.setPaint(new Color(130, 130, 250));
218: graphics
219: .fillRect(0, 0, image.getWidth(), image.getHeight());
220: graphics.dispose();
221:
222: imageLightMap = new ImageComponent2D(
223: ImageComponent2D.FORMAT_RGB, image, false, false);
224: imageLightMap
225: .setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);
226: imageLightMap
227: .setCapability(ImageComponent2D.ALLOW_IMAGE_READ);
228:
229: //create textureLightMap with above imageLightMap
230: textureLightMap = new Texture2D(Texture2D.BASE_LEVEL,
231: Texture2D.RGB, 256, 256);
232: textureLightMap.setImage(0, imageLightMap);
233: textureLightMap.setMagFilter(Texture2D.NICEST);
234: textureLightMap.setMinFilter(Texture2D.NICEST);
235:
236: // application with update textureLightMap at runtime, so lets enable some caps
237: textureLightMap.setCapability(Texture2D.ALLOW_ENABLE_WRITE);
238: textureLightMap.setCapability(Texture2D.ALLOW_ENABLE_READ);
239: textureLightMap.setCapability(Texture2D.ALLOW_IMAGE_WRITE);
240: textureLightMap.setCapability(Texture2D.ALLOW_IMAGE_READ);
241:
242: } catch (Exception e) {
243: System.err.println("Failed to load textures");
244: e.printStackTrace();
245: }
246: }
247:
248: /**
249: * setup TextureUnitStates used in this demo. *
250: * @return
251: */
252: private TextureUnitState[] setupTextureUnitState() {
253: //texture Attributes for DOT3 normal map
254: TextureAttributes textAttDot3 = new TextureAttributes();
255:
256: // lightMap uses TextureAttributes with default REPLACE mode
257: TextureAttributes textAttLightMap = new TextureAttributes();
258:
259: TextureAttributes texAttColor = new TextureAttributes();
260: texAttColor.setTextureMode(TextureAttributes.COMBINE);
261:
262: //CombineRgbMode could be also COMBINE_ADD or COMBINE_ADD_SIGNED, with
263: //different results
264: texAttColor
265: .setCombineRgbMode(TextureAttributes.COMBINE_MODULATE);
266: // increase light depth effect
267: texAttColor.setCombineRgbScale(2);
268:
269: textAttDot3.setTextureMode(TextureAttributes.COMBINE);
270: textAttDot3.setCombineRgbMode(TextureAttributes.COMBINE_DOT3);
271: textAttDot3.setCombineAlphaMode(TextureAttributes.COMBINE_DOT3);
272: textAttDot3.setTextureBlendColor(1.f, 1.0f, 1.0f, 0.0f);
273: // increase light intesity
274: textAttDot3.setCombineRgbScale(2);
275: // setup functions
276: textAttDot3.setCombineRgbFunction(0,
277: TextureAttributes.COMBINE_SRC_COLOR);
278: textAttDot3.setCombineRgbFunction(1,
279: TextureAttributes.COMBINE_SRC_COLOR);
280: textAttDot3.setCombineRgbFunction(2,
281: TextureAttributes.COMBINE_SRC_COLOR);
282: //combine with previous TUS, lightMap
283: textAttDot3.setCombineRgbSource(0,
284: TextureAttributes.COMBINE_PREVIOUS_TEXTURE_UNIT_STATE);
285: textAttDot3.setCombineRgbSource(1,
286: TextureAttributes.COMBINE_TEXTURE_COLOR);
287: textAttDot3.setCombineRgbSource(2,
288: TextureAttributes.COMBINE_OBJECT_COLOR);
289:
290: TexCoordGeneration tcg1 = null;
291: // SphereMap tcg can add nice dynamic effects for curved surfaces, because it
292: // distributes texture like a light bean over geometry.
293: // It os not used in this demo, but you can try yourself at home
294: // with *complex* lightmaps, i.e., spherical light distributions,
295: // multi light sorces, degradee, waves,etc
296: /*
297: tcg1 = new TexCoordGeneration(TexCoordGeneration.SPHERE_MAP,
298: TexCoordGeneration.TEXTURE_COORDINATE_3);
299: */
300:
301: // create TUS
302: tuLightMap = new TextureUnitState(textureLightMap,
303: textAttLightMap, tcg1);
304: tuDOT3NormalMap = new TextureUnitState(textureDOT3NormalMap,
305: textAttDot3, null);
306: tuColor = new TextureUnitState(textureColor, texAttColor, null);
307:
308: // this TUS array is used by geometry at runtime
309: TextureUnitState[] tus = new TextureUnitState[3];
310: tus[0] = tuLightMap;
311: tus[1] = tuDOT3NormalMap;
312: tus[2] = tuColor;
313: // enable texture units for read/write at runtime
314: for (int i = 0; i < tus.length; i++) {
315: tus[i].setCapability(TextureUnitState.ALLOW_STATE_WRITE);
316: tus[i].setCapability(TextureUnitState.ALLOW_STATE_READ);
317: }
318:
319: return tus;
320: }
321:
322: /**
323: * creates a single Quad geometry with 4 TextureCoordinateMaps, for multitexture use.<br>
324: * Dimension is scale*(2m , 1m)
325: * @param scale a scale for this quad
326: * @return quad geometry for multitexture use
327: */
328: private GeometryArray createGeometry(float scale) {
329: // vertex coordinates
330: float[] verts = { 2.0f, -1.0f, 0.0f, 2.0f, 1.0f, 0.0f, -2.0f,
331: 1.0f, 0.0f, -2.0f, -1.0f, 0.0f };
332: // 2D texture Coords - each texture unit will use one set of this
333: float[] texCoords = { 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
334: 0.0f };
335: // all texture units will use texCoords from unit 0
336: int[] texCoordSetMap = { 0, 0, 0, 0 };
337: // normals
338: Vector3f normal = new Vector3f(0.0f, 0.0f, 1.0f);
339: Vector3f[] normals = { normal, normal, normal, normal };
340: // resize quad dimension
341: for (int i = 0; i < verts.length; i++) {
342: verts[i] *= scale;
343: }
344: // create geometry using GeometryInfo
345: GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
346:
347: gi.setCoordinates(verts);
348: gi.setNormals(normals);
349: // preparing for multitexture
350: // To get up to 4 TUS, it needs 4 sets of 2D texture
351: gi.setTextureCoordinateParams(4, 2);
352: gi.setTexCoordSetMap(texCoordSetMap);
353:
354: // this demo needs just 3 TUS, but geometry
355: // is prepared for up to 4 TUS stages
356: gi.setTextureCoordinates(0, texCoords);
357: gi.setTextureCoordinates(1, texCoords);
358: gi.setTextureCoordinates(2, texCoords);
359: gi.setTextureCoordinates(3, texCoords);
360:
361: return gi.getGeometryArray();
362: }
363:
364: /**
365: * Creates scenegraphs
366: * @return a BranchGroup with all needed objects in scene
367: */
368: private BranchGroup createSceneGraph() {
369: BranchGroup bgRoot = new BranchGroup();
370: CheckNewLightMapBehavior checkNewLightMapBehavior = new CheckNewLightMapBehavior();
371:
372: bgRoot.addChild(checkNewLightMapBehavior);
373:
374: // a blue background
375: Background background = new Background(0.4f, 0.4f, 0.8f);
376: background.setApplicationBounds(bounds);
377: bgRoot.addChild(background);
378:
379: AmbientLight alit = new AmbientLight(true, new Color3f(0.4f,
380: 0.4f, 0.4f));
381: bgRoot.addChild(alit);
382:
383: // Set up some directional lights
384: // DOT3 doesnot need light, because it is a perpixel lighting technique
385: //but we add this lights to show
386: // geometry when using non-DOT3 lighting, as color texture only and
387: // light map texture mode
388: Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
389: Vector3f light1Direction = new Vector3f(1.0f, 1.0f, 1.0f);
390: Color3f light2Color = new Color3f(1.0f, 1.0f, 0.9f);
391: Vector3f light2Direction = new Vector3f(-1.0f, -1.0f, -1.0f);
392:
393: DirectionalLight light1 = new DirectionalLight(light1Color,
394: light1Direction);
395: light1.setInfluencingBounds(bounds);
396: bgRoot.addChild(light1);
397:
398: DirectionalLight light2 = new DirectionalLight(light2Color,
399: light2Direction);
400: light2.setInfluencingBounds(bounds);
401: bgRoot.addChild(light2);
402:
403: //loading color and DOT3 normal map textures from disk,
404: //and creating light map at runtime
405: loadTextures();
406: //our single Quad geometry, enabled for multitexture
407: GeometryArray geo = createGeometry(0.4f);
408: // a appearance for our geometry
409: appearance = new Appearance();
410: // polygon and texture unit will be updated at runtime
411: // so we must enabled read/write operations for then
412: appearance
413: .setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_READ);
414: appearance
415: .setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE);
416: appearance
417: .setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_READ);
418: appearance
419: .setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_WRITE);
420:
421: //use a default material. It is necessary when running
422: //on non per-pixel lighting mod, i.e., using non DOT3 textures
423: appearance.setMaterial(new Material());
424:
425: polygonAttributes = new PolygonAttributes();
426: polygonAttributes
427: .setCapability(PolygonAttributes.ALLOW_MODE_WRITE);
428: polygonAttributes.setCullFace(PolygonAttributes.CULL_NONE);
429:
430: appearance.setPolygonAttributes(polygonAttributes);
431:
432: // uses a TUS dot3 enabled
433: tusArr = setupTextureUnitState();
434: appearance.setTextureUnitState(tusArr);
435:
436: // joining geometry and appearance in a shape3D
437: Shape3D shape3D = new Shape3D(geo, appearance);
438: shape3D.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
439: shape3D.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
440:
441: bgRoot.addChild(shape3D);
442:
443: bgRoot.compile();
444:
445: return bgRoot;
446: }
447:
448: /**
449: * Toggles wireframe mode
450: * @param mode true for wireframe, false for fill polygon
451: */
452: public void setWireframeMode(boolean mode) {
453: if (mode)
454: polygonAttributes
455: .setPolygonMode(PolygonAttributes.POLYGON_LINE);
456: else
457: polygonAttributes
458: .setPolygonMode(PolygonAttributes.POLYGON_FILL);
459: }
460:
461: /**
462: * This method togles on/off textures and updates TextureUnitState in correct Order.
463: * Some video drivers does not accept TextureUnitState arrays with null values among
464: * non-null values
465: * @param showLightMap togles LightMap texture
466: * @param showDot3 togles DOT3 Normal texture
467: * @param showColor togles Color texture
468: */
469: public void showTextures(boolean showLightMap, boolean showDot3,
470: boolean showColor) {
471: int bitSet = 0;
472: bitSet |= showLightMap ? 4 : 0;
473: bitSet |= showDot3 ? 2 : 0;
474: bitSet |= showColor ? 1 : 0;
475:
476: tusArr[0] = null;
477: tusArr[1] = null;
478: tusArr[2] = null;
479:
480: switch (bitSet) {
481: case 7: { //all bit == all tus
482: tusArr[0] = tuLightMap;
483: tusArr[1] = tuDOT3NormalMap;
484: tusArr[2] = tuColor;
485: }
486: break;
487: case 6: { //no Color
488: tusArr[0] = tuLightMap;
489: tusArr[1] = tuDOT3NormalMap;
490: }
491: break;
492: case 5: { //no Dot3
493: tusArr[0] = tuLightMap;
494: tusArr[1] = tuColor;
495: }
496: break;
497: case 4: { //lightMap only
498: tusArr[0] = tuLightMap;
499: }
500: break;
501: case 3: { //no LightMap
502: tusArr[0] = tuDOT3NormalMap;
503: tusArr[1] = tuColor;
504: }
505: break;
506: case 2: { //Dot3 Only
507: tusArr[0] = tuDOT3NormalMap;
508: }
509: break;
510: case 1: { // Color Only
511: tusArr[0] = tuColor;
512: }
513: break;
514: default: { // case 0, no textures shows at all
515: }
516: break;
517: }
518: appearance.setTextureUnitState(tusArr);
519: }
520:
521: /**
522: * updates LightMap texture.
523: * This method is called from checkNewLightMapBehavior
524: * @param image new image to be applied
525: */
526: public void updateLighMap(BufferedImage image) {
527: imageLightMap.setSubImage(image, image.getWidth(), image
528: .getHeight(), 0, 0, 0, 0);
529: }
530:
531: private BufferedImage tempImage;
532: private boolean lockTempImage = false;
533:
534: /**
535: * main method
536: * @param args
537: */
538: public static void main(String[] args) {
539: javax.swing.SwingUtilities.invokeLater(new Runnable() {
540: public void run() {
541: new Dot3Demo();
542: }
543: });
544:
545: }
546:
547: /**
548: * A internal class to check if there is a new Light Map to be applied
549: */
550: class CheckNewLightMapBehavior extends Behavior {
551: WakeupOnElapsedFrames wakeup = new WakeupOnElapsedFrames(0);
552:
553: public CheckNewLightMapBehavior() {// auto enable and set schedulling bounds
554: setEnable(true);
555: setSchedulingBounds(bounds);
556: }
557:
558: public void initialize() {
559: wakeupOn(wakeup);
560: }
561:
562: public void processStimulus(Enumeration e) {
563: // check if there are a new light map ready to use
564: if (ctrlPanel.hasTextureImageReady()) {
565: updateLighMap(ctrlPanel.getTextureImage());
566: }
567: //wake up on next frame
568: wakeupOn(wakeup);
569: }
570: }
571:
572: }
|