Java/3D/Texture
Содержание
- 1 Create geometry to display the texture image mapped onto a triangulated polygon
- 2 ExTexture - illustrate use of textures
- 3 Illustrates dynamic texture coordinate generation using the TexCoordGeneration class
- 4 Illustrates how a texture image can be dynamically rotated at runtime
- 5 Image Component By Reference
- 6 Interleaved Test
- 7 LocalEyeApp creates a single plane with texture mapping
- 8 Multi Texture
- 9 TexCoordGeneration class to automatically define the texture coordinates
- 10 Texture By Reference
- 11 Texture Image
- 12 Texture Mapping
- 13 Texture: picture ball
- 14 The simple application of textures
Create geometry to display the texture image mapped onto a triangulated polygon
<source lang="java">
/**********************************************************
Copyright (C) 2001 Daniel Selman First distributed with the book "Java 3D Programming" by Daniel Selman and published by Manning Publications. http://manning.ru/selman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Authors can be contacted at: Daniel Selman: daniel@selman.org If you make changes you think others would like, please contact one of the authors or someone at the www.j3d.org web site. **************************************************************/
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.io.File; import java.io.FileReader; import java.net.URL; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.Interpolator; import javax.media.j3d.Locale; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.RotPosPathInterpolator; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Quat4f; import javax.vecmath.Vector3d; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.GeometryInfo; import com.sun.j3d.utils.geometry.NormalGenerator; import com.sun.j3d.utils.geometry.Triangulator; import com.sun.j3d.utils.image.TextureLoader; /**
* This example reads in the name of a texture image and texture coordinates * from a file and creates geometry to display the texture image mapped onto a * triangulated polygon. */
public class TextureTest extends Java3dApplet {
private static int m_kWidth = 600; private static int m_kHeight = 600; public TextureTest() { initJava3d(); } protected int getCanvas3dWidth(Canvas3D c3d) { return m_kWidth - 5; } protected int getCanvas3dHeight(Canvas3D c3d) { return m_kHeight - 5; } protected BranchGroup createSceneBranchGroup() { BranchGroup objRoot = super.createSceneBranchGroup(); TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); // add an Interpolator to rotate the scene objTrans.addChild(createInterpolator(objTrans)); // process the texture input files and add the geometry objTrans.addChild(createTextureGroup("ann.txt", -5, -5, -3, false)); objTrans.addChild(createTextureGroup("daniel.txt", -5, 5, 3, false)); objTrans.addChild(createTextureGroup("ann.txt", 5, 5, 3, false)); objTrans.addChild(createTextureGroup("daniel.txt", 5, -5, -3, false)); objRoot.addChild(objTrans); return objRoot; } // creates a TransformGroup and positions it and adds // the texture geometry as a child node protected TransformGroup createTextureGroup(String szFile, double x, double y, double z, boolean bWireframe) { TransformGroup tg = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setTranslation(new Vector3d(x, y, z)); tg.setTransform(t3d); Shape3D texShape = createTextureGeometry(szFile, bWireframe); if (texShape != null) tg.addChild(texShape); return tg; } // return a Shape3D that is a triangulated texture mapped polygon // based on the texture coordinates and name of texture image in the // input file protected Shape3D createTextureGeometry(String szFile, boolean bWireframe) { // load all the texture data from the file and create the geometry // coordinates TextureGeometryInfo texInfo = createTextureCoordinates(szFile); if (texInfo == null) { System.err .println("Could not load texture info for file:" + szFile); return null; } // print some stats on the loaded file System.out.println("Loaded File: " + szFile); System.out.println(" Texture image: " + texInfo.m_szImage); System.out.println(" Texture coordinates: " + texInfo.m_TexCoordArray.length); // create an Appearance and assign a Material Appearance app = new Appearance(); PolygonAttributes polyAttribs = null; // create the PolygonAttributes and attach to the Appearance, // note that we use CULL_NONE so that the "rear" side of the geometry // is visible with the applied texture image if (bWireframe == false) polyAttribs = new PolygonAttributes(PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0); else polyAttribs = new PolygonAttributes(PolygonAttributes.POLYGON_LINE, PolygonAttributes.CULL_NONE, 0); app.setPolygonAttributes(polyAttribs); // load the texture image and assign to the appearance TextureLoader texLoader = new TextureLoader(texInfo.m_szImage, Texture.RGB, this); Texture tex = texLoader.getTexture(); app.setTexture(tex); // create a GeometryInfo for the QuadArray that was populated. GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY); gi.setCoordinates(texInfo.m_CoordArray); gi.setTextureCoordinates(texInfo.m_TexCoordArray); // use the triangulator utility to triangulate the polygon int[] stripCountArray = { texInfo.m_CoordArray.length }; int[] countourCountArray = { stripCountArray.length }; gi.setContourCounts(countourCountArray); gi.setStripCounts(stripCountArray); Triangulator triangulator = new Triangulator(); triangulator.triangulate(gi); // generate normal vectors for the triangles, not // strictly necessary as we are not lighting the scene // but generally useful NormalGenerator normalGenerator = new NormalGenerator(); normalGenerator.generateNormals(gi); // wrap the GeometryArray in a Shape3D and assign appearance return new Shape3D(gi.getGeometryArray(), app); } // handles the nitty-gritty details of loading the input // file and reading (in order): // - texture file image name // - size of the geometry in the X direction // - Y direction scale factor // - number of texture coordinates // - each texture coordinate (X Y) // This could all be easily accomplished using a scenegraph // loader but this simple code is included for reference. protected TextureGeometryInfo createTextureCoordinates(String szFile) { // create a simple wrapper class to package our // return values TextureGeometryInfo texInfo = new TextureGeometryInfo(); // allocate a temporary buffer to store the input file StringBuffer szBufferData = new StringBuffer(); float sizeGeometryX = 0; float factorY = 1; int nNumPoints = 0; Point2f boundsPoint = new Point2f(); try { // attach a reader to the input file FileReader fileIn = new FileReader(szFile); int nChar = 0; // read the entire file into the StringBuffer while (true) { nChar = fileIn.read(); // if we have not hit the end of file // add the character to the StringBuffer if (nChar != -1) szBufferData.append((char) nChar); else // EOF break; } // create a tokenizer to tokenize the input file at whitespace java.util.StringTokenizer tokenizer = new java.util.StringTokenizer( szBufferData.toString()); // read the name of the texture image texInfo.m_szImage = tokenizer.nextToken(); // read the size of the generated geometry in the X dimension sizeGeometryX = Float.parseFloat(tokenizer.nextToken()); // read the Y scale factor factorY = Float.parseFloat(tokenizer.nextToken()); // read the number of texture coordinates nNumPoints = Integer.parseInt(tokenizer.nextToken()); // read each texture coordinate texInfo.m_TexCoordArray = new Point2f[nNumPoints]; Point2f texPoint2f = null; for (int n = 0; n < nNumPoints; n++) { // JAVA 3D 1.2 change - the Y coordinates // have been flipped, so we have to subtract the Y coordinate // from 1 texPoint2f = new Point2f(Float .parseFloat(tokenizer.nextToken()), 1.0f - Float .parseFloat(tokenizer.nextToken())); texInfo.m_TexCoordArray[n] = texPoint2f; // keep an eye on the extents of the texture coordinates // so we can automatically center the geometry if (n == 0 || texPoint2f.x > boundsPoint.x) boundsPoint.x = texPoint2f.x; if (n == 0 || texPoint2f.y > boundsPoint.y) boundsPoint.y = texPoint2f.y; } } catch (Exception e) { System.err.println(e.toString()); return null; } // build the array of coordinates texInfo.m_CoordArray = new Point3f[nNumPoints]; for (int n = 0; n < nNumPoints; n++) { // scale and center the geometry based on the texture coordinates texInfo.m_CoordArray[n] = new Point3f( sizeGeometryX * (texInfo.m_TexCoordArray[n].x - boundsPoint.x / 2), factorY * sizeGeometryX * (texInfo.m_TexCoordArray[n].y - boundsPoint.y / 2), 0); } return texInfo; } // creates a fancy RotPosPathInterpolator to spin the scene // between various positions and rotations. protected Interpolator createInterpolator(TransformGroup objTrans) { Transform3D t3d = new Transform3D(); float[] knots = { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.6f, 0.8f, 0.9f, 1.0f }; Quat4f[] quats = new Quat4f[9]; Point3f[] positions = new Point3f[9]; AxisAngle4f axis = new AxisAngle4f(1.0f, 0.0f, 0.0f, 0.0f); t3d.set(axis); quats[0] = new Quat4f(0.3f, 1.0f, 1.0f, 0.0f); quats[1] = new Quat4f(1.0f, 0.0f, 0.0f, 0.3f); quats[2] = new Quat4f(0.2f, 1.0f, 0.0f, 0.0f); quats[3] = new Quat4f(0.0f, 0.2f, 1.0f, 0.0f); quats[4] = new Quat4f(1.0f, 0.0f, 0.4f, 0.0f); quats[5] = new Quat4f(0.0f, 1.0f, 1.0f, 0.2f); quats[6] = new Quat4f(0.3f, 0.3f, 0.0f, 0.0f); quats[7] = new Quat4f(1.0f, 0.0f, 1.0f, 1.0f); quats[8] = quats[0]; positions[0] = new Point3f(0.0f, 0.0f, -1.0f); positions[1] = new Point3f(1.0f, -2.0f, -2.0f); positions[2] = new Point3f(-2.0f, 2.0f, -3.0f); positions[3] = new Point3f(1.0f, 1.0f, -4.0f); positions[4] = new Point3f(-4.0f, -2.0f, -5.0f); positions[5] = new Point3f(2.0f, 0.3f, -6.0f); positions[6] = new Point3f(-4.0f, 0.5f, -7.0f); positions[7] = new Point3f(0.0f, -1.5f, -4.0f); positions[8] = positions[0]; Alpha alpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 10000, 0, 0, 0, 0, 0); RotPosPathInterpolator rotPosPath = new RotPosPathInterpolator(alpha, objTrans, t3d, knots, quats, positions); rotPosPath.setSchedulingBounds(createApplicationBounds()); return rotPosPath; } public static void main(String[] args) { TextureTest textureTest = new TextureTest(); textureTest.saveCommandLineArguments(args); new MainFrame(textureTest, m_kWidth, m_kHeight); }
} //simple container class we use to package a return value. class TextureGeometryInfo {
public String m_szImage = null; public Point2f[] m_TexCoordArray = null; public Point3f[] m_CoordArray = null;
} /*******************************************************************************
* Copyright (C) 2001 Daniel Selman * * First distributed with the book "Java 3D Programming" by Daniel Selman and * published by Manning Publications. http://manning.ru/selman * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html * * Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite * 330, Boston, MA 02111-1307, USA. * * Authors can be contacted at: Daniel Selman: daniel@selman.org * * If you make changes you think others would like, please contact one of the * authors or someone at the www.j3d.org web site. ******************************************************************************/
//***************************************************************************** /**
* Java3dApplet * * Base class for defining a Java 3D applet. Contains some useful methods for * defining views and scenegraphs etc. * * @author Daniel Selman * @version 1.0 */
//***************************************************************************** abstract class Java3dApplet extends Applet {
public static int m_kWidth = 300; public static int m_kHeight = 300; protected String[] m_szCommandLineArray = null; protected VirtualUniverse m_Universe = null; protected BranchGroup m_SceneBranchGroup = null; protected Bounds m_ApplicationBounds = null; // protected com.tornadolabs.j3dtree.Java3dTree m_Java3dTree = null; public Java3dApplet() { } public boolean isApplet() { try { System.getProperty("user.dir"); System.out.println("Running as Application."); return false; } catch (Exception e) { } System.out.println("Running as Applet."); return true; } public URL getWorkingDirectory() throws java.net.MalformedURLException { URL url = null; try { File file = new File(System.getProperty("user.dir")); System.out.println("Running as Application:"); System.out.println(" " + file.toURL()); return file.toURL(); } catch (Exception e) { } System.out.println("Running as Applet:"); System.out.println(" " + getCodeBase()); return getCodeBase(); } public VirtualUniverse getVirtualUniverse() { return m_Universe; } //public com.tornadolabs.j3dtree.Java3dTree getJ3dTree() { //return m_Java3dTree; // } public Locale getFirstLocale() { java.util.Enumeration e = m_Universe.getAllLocales(); if (e.hasMoreElements() != false) return (Locale) e.nextElement(); return null; } protected Bounds getApplicationBounds() { if (m_ApplicationBounds == null) m_ApplicationBounds = createApplicationBounds(); return m_ApplicationBounds; } protected Bounds createApplicationBounds() { m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); return m_ApplicationBounds; } protected Background createBackground() { Background back = new Background(new Color3f(0.9f, 0.9f, 0.9f)); back.setApplicationBounds(createApplicationBounds()); return back; } public void initJava3d() { // m_Java3dTree = new com.tornadolabs.j3dtree.Java3dTree(); m_Universe = createVirtualUniverse(); Locale locale = createLocale(m_Universe); BranchGroup sceneBranchGroup = createSceneBranchGroup(); ViewPlatform vp = createViewPlatform(); BranchGroup viewBranchGroup = createViewBranchGroup( getViewTransformGroupArray(), vp); createView(vp); Background background = createBackground(); if (background != null) sceneBranchGroup.addChild(background); // m_Java3dTree.recursiveApplyCapability(sceneBranchGroup); // m_Java3dTree.recursiveApplyCapability(viewBranchGroup); locale.addBranchGraph(sceneBranchGroup); addViewBranchGroup(locale, viewBranchGroup); onDoneInit(); } protected void onDoneInit() { // m_Java3dTree.updateNodes(m_Universe); } protected double getScale() { return 1.0; } public TransformGroup[] getViewTransformGroupArray() { TransformGroup[] tgArray = new TransformGroup[1]; tgArray[0] = new TransformGroup(); // move the camera BACK a little... // note that we have to invert the matrix as // we are moving the viewer Transform3D t3d = new Transform3D(); t3d.setScale(getScale()); t3d.setTranslation(new Vector3d(0.0, 0.0, -20.0)); t3d.invert(); tgArray[0].setTransform(t3d); return tgArray; } protected void addViewBranchGroup(Locale locale, BranchGroup bg) { locale.addBranchGraph(bg); } protected Locale createLocale(VirtualUniverse u) { return new Locale(u); } protected BranchGroup createSceneBranchGroup() { m_SceneBranchGroup = new BranchGroup(); return m_SceneBranchGroup; } protected View createView(ViewPlatform vp) { View view = new View(); PhysicalBody pb = createPhysicalBody(); PhysicalEnvironment pe = createPhysicalEnvironment(); AudioDevice audioDevice = createAudioDevice(pe); if (audioDevice != null) { pe.setAudioDevice(audioDevice); audioDevice.initialize(); } view.setPhysicalEnvironment(pe); view.setPhysicalBody(pb); if (vp != null) view.attachViewPlatform(vp); view.setBackClipDistance(getBackClipDistance()); view.setFrontClipDistance(getFrontClipDistance()); Canvas3D c3d = createCanvas3D(); view.addCanvas3D(c3d); addCanvas3D(c3d); return view; } protected PhysicalBody createPhysicalBody() { return new PhysicalBody(); } protected AudioDevice createAudioDevice(PhysicalEnvironment pe) { JavaSoundMixer javaSoundMixer = new JavaSoundMixer(pe); if (javaSoundMixer == null) System.out.println("create of audiodevice failed"); return javaSoundMixer; } protected PhysicalEnvironment createPhysicalEnvironment() { return new PhysicalEnvironment(); } protected float getViewPlatformActivationRadius() { return 100; } protected ViewPlatform createViewPlatform() { ViewPlatform vp = new ViewPlatform(); vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW); vp.setActivationRadius(getViewPlatformActivationRadius()); return vp; } protected Canvas3D createCanvas3D() { GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D(); gc3D.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED); GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment() .getScreenDevices(); Canvas3D c3d = new Canvas3D(gd[0].getBestConfiguration(gc3D)); c3d.setSize(getCanvas3dWidth(c3d), getCanvas3dHeight(c3d)); return c3d; } protected int getCanvas3dWidth(Canvas3D c3d) { return m_kWidth; } protected int getCanvas3dHeight(Canvas3D c3d) { return m_kHeight; } protected double getBackClipDistance() { return 100.0; } protected double getFrontClipDistance() { return 1.0; } protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray, ViewPlatform vp) { BranchGroup vpBranchGroup = new BranchGroup(); if (tgArray != null && tgArray.length > 0) { Group parentGroup = vpBranchGroup; TransformGroup curTg = null; for (int n = 0; n < tgArray.length; n++) { curTg = tgArray[n]; parentGroup.addChild(curTg); parentGroup = curTg; } tgArray[tgArray.length - 1].addChild(vp); } else vpBranchGroup.addChild(vp); return vpBranchGroup; } protected void addCanvas3D(Canvas3D c3d) { setLayout(new BorderLayout()); add(c3d, BorderLayout.CENTER); doLayout(); } protected VirtualUniverse createVirtualUniverse() { return new VirtualUniverse(); } protected void saveCommandLineArguments(String[] szArgs) { m_szCommandLineArray = szArgs; } protected String[] getCommandLineArguments() { return m_szCommandLineArray; }
}
</source>
ExTexture - illustrate use of textures
<source lang="java">
// //CLASS //ExTexture - illustrate use of textures // //LESSON //Use Texture2D and TextureAttributes to apply a texture image //to a shape. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; import javax.media.j3d.Appearance; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExTexture extends Java3DFrame {
//-------------------------------------------------------------- // SCENE CONTENT //-------------------------------------------------------------- // // Nodes (updated via menu) // private Shape3D shape = null; // overall scene shape private Appearance app = null; // geometry appearance private Appearance dummyApp = null; // temporary appearance private TextureAttributes texatt = null;// texture attributes private TextureAttributes dummyAtt = null;// temporary texture attributes private Texture2D tex = null; // current texture // // Build scene // public Group buildScene() { // Get the current menu choices for appearance attributes int textureMode = ((Integer) modes[currentMode].value).intValue(); Color3f color = (Color3f) colors[currentColor].value; Color3f blendColor = (Color3f) colors[currentBlendColor].value; // Turn on the example headlight setHeadlightEnable(true); // Default to examine navigation setNavigationType(Examine); // Disable scene graph compilation for this example setCompilable(false); // Create the scene group Group scene = new Group(); // BEGIN EXAMPLE TOPIC // Set up a basic material Material mat = new Material(); mat.setAmbientColor(0.2f, 0.2f, 0.2f); mat.setDiffuseColor(1.0f, 1.0f, 1.0f); mat.setSpecularColor(0.0f, 0.0f, 0.0f); mat.setLightingEnable(true); // Set up the texturing attributes with an initial // texture mode, texture transform, and blend color texatt = new TextureAttributes(); texatt.setPerspectiveCorrectionMode(TextureAttributes.NICEST); texatt.setTextureMode(textureMode); texatt.setTextureTransform(new Transform3D()); // Identity texatt.setTextureBlendColor(blendColor.x, blendColor.y, blendColor.z, 0.5f); // Enable changing these while the node component is live texatt.setCapability(TextureAttributes.ALLOW_MODE_WRITE); texatt.setCapability(TextureAttributes.ALLOW_BLEND_COLOR_WRITE); texatt.setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE); // Create an appearance using these attributes app = new Appearance(); app.setMaterial(mat); app.setTextureAttributes(texatt); app.setTexture(tex); // And enable changing these while the node component is live app.setCapability(Appearance.ALLOW_TEXTURE_WRITE); app.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE); // Build a shape and enable changing its appearance shape = new Shape3D(buildGeometry(), app); shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); // END EXAMPLE TOPIC // Create some dummy appearance and tex attribute node components // In response to menu choices, we quickly switch the shape to // use one of these, then diddle with the main appearance or // tex attribute, then switch the shape back. This effectively // makes the appearance or tex attributes we want to change // become un-live during a change. We have to do this approach // because some texture features have no capability bits to set // to allow changes while live. dummyApp = new Appearance(); dummyAtt = new TextureAttributes(); scene.addChild(shape); return scene; } //-------------------------------------------------------------- // USER INTERFACE //-------------------------------------------------------------- // // Main // public static void main(String[] args) { ExTexture ex = new ExTexture(); ex.initialize(args); ex.buildUniverse(); ex.showFrame(); } // Texture enable/disable private boolean textureOnOff = true; private CheckboxMenuItem textureOnOffMenu = null; // Texture image choices private NameValue[] images = { new NameValue("Red bricks", "brick.jpg"), new NameValue("Stone bricks", "stonebrk2.jpg"), new NameValue("Marble", "granite07rev.jpg"), new NameValue("Mud", "mud01.jpg"), new NameValue("Wood", "flooring.jpg"), new NameValue("Earth", "earthmap.jpg"), }; private CheckboxMenu imageMenu = null; private int currentImage = 0; // Texture boundary mode choices private NameValue[] boundaries = { new NameValue("CLAMP", new Integer(Texture.CLAMP)), new NameValue("WRAP", new Integer(Texture.WRAP)), }; private CheckboxMenu boundaryMenu = null; private int currentBoundary = 0; // Texture boundary color choices private NameValue[] colors = { new NameValue("White", White), new NameValue("Gray", Gray), new NameValue("Dark Gray", DarkGray), new NameValue("Black", Black), new NameValue("Red", Red), new NameValue("Dark Red", DarkRed), new NameValue("Green", Green), new NameValue("Dark Green", DarkGreen), new NameValue("Blue", Blue), new NameValue("Dark Blue", DarkBlue), }; private CheckboxMenu colorMenu = null; private int currentColor = 6; // Texture filter choices private NameValue[] filters = { new NameValue("POINT", new Integer(Texture.BASE_LEVEL_POINT)), new NameValue("LINEAR", new Integer(Texture.BASE_LEVEL_LINEAR)), }; private CheckboxMenu filterMenu = null; private int currentFilter = 0; // Texture attributes mode choices private NameValue[] modes = { new NameValue("BLEND", new Integer(TextureAttributes.BLEND)), new NameValue("DECAL", new Integer(TextureAttributes.DECAL)), new NameValue("MODULATE", new Integer(TextureAttributes.MODULATE)), new NameValue("REPLACE", new Integer(TextureAttributes.REPLACE)), }; private CheckboxMenu modeMenu = null; private int currentMode = 2; // Texture attributes blend color choices private CheckboxMenu blendColorMenu = null; private int currentBlendColor = 6; private NameValue[] xforms = { new NameValue("Identity", new Integer(0)), new NameValue("Scale by 2", new Integer(1)), new NameValue("Scale by 4", new Integer(2)), new NameValue("Rotate by 45 degrees", new Integer(3)), new NameValue("Translate by 0.25", new Integer(4)), }; private CheckboxMenu xformMenu = null; private int currentXform = 0; private Texture2D[] textureComponents; // // Initialize the GUI (application and applet) // public void initialize(String[] args) { // Initialize the window, menubar, etc. super.initialize(args); exampleFrame.setTitle("Java 3D Texture Mapping Example"); // // Add a menubar menu to change texture parameters // Image --> // Boundary mode --> // Boundary color --> // Filter mode --> // Menu m = new Menu("Texture"); textureOnOffMenu = new CheckboxMenuItem("Texturing enabled", textureOnOff); textureOnOffMenu.addItemListener(this); m.add(textureOnOffMenu); imageMenu = new CheckboxMenu("Image", images, currentImage, this); m.add(imageMenu); boundaryMenu = new CheckboxMenu("Boundary mode", boundaries, currentBoundary, this); m.add(boundaryMenu); colorMenu = new CheckboxMenu("Boundary color", colors, currentColor, this); m.add(colorMenu); filterMenu = new CheckboxMenu("Filter mode", filters, currentFilter, this); m.add(filterMenu); exampleMenuBar.add(m); // // Add a menubar menu to change texture attributes parameters // Mode --> // Blend color --> // m = new Menu("TextureAttributes"); modeMenu = new CheckboxMenu("Mode", modes, currentMode, this); m.add(modeMenu); blendColorMenu = new CheckboxMenu("Blend color", colors, currentBlendColor, this); m.add(blendColorMenu); xformMenu = new CheckboxMenu("Transform", xforms, currentXform, this); m.add(xformMenu); exampleMenuBar.add(m); // Preload the texture images // Use the texture loading utility to read in the texture // files and process them into an ImageComponent2D // for use in the Background node. if (debug) System.err.println("Loading textures..."); textureComponents = new Texture2D[images.length]; String value = null; for (int i = 0; i < images.length; i++) { value = (String) images[i].value; textureComponents[i] = loadTexture(value); } tex = textureComponents[currentImage]; } // // Handle checkboxes and menu choices // public void checkboxChanged(CheckboxMenu menu, int check) { if (menu == imageMenu) { // Change the texture image currentImage = check; Texture tex = textureComponents[currentImage]; int mode = ((Integer) boundaries[currentBoundary].value).intValue(); Color3f color = (Color3f) colors[currentColor].value; int filter = ((Integer) filters[currentFilter].value).intValue(); shape.setAppearance(dummyApp); tex.setEnable(textureOnOff); tex.setBoundaryModeS(mode); tex.setBoundaryModeT(mode); tex.setBoundaryColor(color.x, color.y, color.z, 0.0f); tex.setMagFilter(filter); tex.setMinFilter(filter); app.setTexture(tex); shape.setAppearance(app); return; } if (menu == boundaryMenu) { // Change the texture boundary mode currentBoundary = check; Texture tex = textureComponents[currentImage]; int mode = ((Integer) boundaries[currentBoundary].value).intValue(); shape.setAppearance(dummyApp); tex.setBoundaryModeS(mode); tex.setBoundaryModeT(mode); app.setTexture(tex); shape.setAppearance(app); return; } if (menu == colorMenu) { // Change the boundary color currentColor = check; Color3f color = (Color3f) colors[currentColor].value; Texture tex = textureComponents[currentImage]; shape.setAppearance(dummyApp); tex.setBoundaryColor(color.x, color.y, color.z, 0.0f); app.setTexture(tex); shape.setAppearance(app); return; } if (menu == filterMenu) { // Change the filter mode currentFilter = check; int filter = ((Integer) filters[currentFilter].value).intValue(); Texture tex = textureComponents[currentImage]; shape.setAppearance(dummyApp); tex.setMagFilter(filter); tex.setMinFilter(filter); app.setTexture(tex); shape.setAppearance(app); return; } if (menu == modeMenu) { // Change the texture mode currentMode = check; int mode = ((Integer) modes[currentMode].value).intValue(); app.setTextureAttributes(dummyAtt); texatt.setTextureMode(mode); app.setTextureAttributes(texatt); return; } if (menu == blendColorMenu) { // Change the boundary color currentBlendColor = check; Color3f color = (Color3f) colors[currentBlendColor].value; app.setTextureAttributes(dummyAtt); texatt.setTextureBlendColor(color.x, color.y, color.z, 0.5f); app.setTextureAttributes(texatt); return; } if (menu == xformMenu) { // Change the texture transform currentXform = check; Transform3D tt = new Transform3D(); switch (currentXform) { default: case 0: // Identity texatt.setTextureTransform(tt); return; case 1: // Scale by 2 tt.setScale(2.0); texatt.setTextureTransform(tt); return; case 2: // Scale by 4 tt.setScale(4.0); texatt.setTextureTransform(tt); return; case 3: // Z rotate by 45 degrees tt.rotZ(Math.PI / 4.0); texatt.setTextureTransform(tt); return; case 4: // Translate by 0.25 tt.set(new Vector3f(0.25f, 0.0f, 0.0f)); texatt.setTextureTransform(tt); return; } } // Handle all other checkboxes super.checkboxChanged(menu, check); } public void itemStateChanged(ItemEvent event) { Object src = event.getSource(); // Check if it is the texture on/off choice if (src == textureOnOffMenu) { textureOnOff = textureOnOffMenu.getState(); Texture tex = textureComponents[currentImage]; tex.setEnable(textureOnOff); } // Handle all other checkboxes super.itemStateChanged(event); } //-------------------------------------------------------------- // UTILITY METHODS //-------------------------------------------------------------- // // Handle loading a texture and setting up its attributes // private Texture2D loadTexture(String filename) { // Load the texture image file if (debug) System.err.println("Loading texture "" + filename + """); TextureLoader texLoader = new TextureLoader(filename, this); // If the image is NULL, something went wrong ImageComponent2D ic = texLoader.getImage(); if (ic == null) { System.err.println("Cannot load texture "" + filename + """); return null; } // Configure a Texture2D with the image Texture2D t = (Texture2D) texLoader.getTexture(); int mode = ((Integer) boundaries[currentBoundary].value).intValue(); t.setBoundaryModeS(mode); t.setBoundaryModeT(mode); Color3f color = (Color3f) colors[currentColor].value; t.setBoundaryColor(color.x, color.y, color.z, 0.0f); int filter = ((Integer) filters[currentFilter].value).intValue(); t.setMagFilter(filter); t.setMinFilter(filter); t.setMipMapMode(Texture.BASE_LEVEL); // Turn it on and allow future changes t.setEnable(true); t.setCapability(Texture.ALLOW_ENABLE_WRITE); return t; } // // Build a cube using a QuadArray // public QuadArray buildGeometry() { QuadArray cube = new QuadArray(24, GeometryArray.COORDINATES | GeometryArray.NORMALS | GeometryArray.TEXTURE_COORDINATE_2); cube.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE); cube.setCapability(GeometryArray.ALLOW_TEXCOORD_WRITE); VertexList vl = new VertexList(cube); float MAX = 1.0f; float MIN = 0.0f; // Coordinate Normal Texture // X Y Z I J K S T // Front vl.xyzijkst(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MIN, MIN); vl.xyzijkst(1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MAX, MIN); vl.xyzijkst(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MAX, MAX); vl.xyzijkst(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, MIN, MAX); // Back vl.xyzijkst(1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MAX, MIN); vl.xyzijkst(-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MIN, MIN); vl.xyzijkst(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MIN, MAX); vl.xyzijkst(1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, MAX, MAX); // Right vl.xyzijkst(1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, MIN, MAX); vl.xyzijkst(1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, MIN, MIN); vl.xyzijkst(1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, MAX, MIN); vl.xyzijkst(1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, MAX, MAX); // Left vl.xyzijkst(-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, MIN, MIN); vl.xyzijkst(-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, MIN, MAX); vl.xyzijkst(-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, MAX, MAX); vl.xyzijkst(-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, MAX, MIN); // Top vl.xyzijkst(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, MIN, MAX); vl.xyzijkst(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, MAX, MAX); vl.xyzijkst(1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, MAX, MIN); vl.xyzijkst(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, MIN, MIN); // Bottom vl.xyzijkst(-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, MIN, MIN); vl.xyzijkst(1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, MAX, MIN); vl.xyzijkst(1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, MAX, MAX); vl.xyzijkst(-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, MIN, MAX); return cube; } // // Use a helper class to manage the coordinate lists // private class VertexList { private int index = 0; private GeometryArray ga = null; public VertexList(GeometryArray newga) { index = 0; ga = newga; } public void xyzst(float x, float y, float z, float s, float t) { ga.setCoordinate(index, new Point3f(x, y, z)); ga.setTextureCoordinate(index, new Point2f(s, t)); index++; } public void xyzijkst(float x, float y, float z, float i, float j, float k, float s, float t) { ga.setCoordinate(index, new Point3f(x, y, z)); ga.setNormal(index, new Vector3f(i, j, k)); ga.setTextureCoordinate(index, new Point2f(s, t)); index++; } }
} /**
* The Example class is a base class extended by example applications. The class * provides basic features to create a top-level frame, add a menubar and * Canvas3D, build the universe, set up "examine" and "walk" style navigation * behaviors, and provide hooks so that subclasses can add 3D content to the * example"s universe.*
* Using this Example class simplifies the construction of example applications, * enabling the author to focus upon 3D content and not the busywork of creating * windows, menus, and universes. * * @version 1.0, 98/04/16 * @author David R. Nadeau, San Diego Supercomputer Center */ class Java3DFrame extends Applet implements WindowListener, ActionListener, ItemListener, CheckboxMenuListener { // Navigation types public final static int Walk = 0; public final static int Examine = 1; // Should the scene be compiled? private boolean shouldCompile = true; // GUI objects for our subclasses protected Java3DFrame example = null; protected Frame exampleFrame = null; protected MenuBar exampleMenuBar = null; protected Canvas3D exampleCanvas = null; protected TransformGroup exampleViewTransform = null; protected TransformGroup exampleSceneTransform = null; protected boolean debug = false; // Private GUI objects and state private boolean headlightOnOff = true; private int navigationType = Examine; private CheckboxMenuItem headlightMenuItem = null; private CheckboxMenuItem walkMenuItem = null; private CheckboxMenuItem examineMenuItem = null; private DirectionalLight headlight = null; private ExamineViewerBehavior examineBehavior = null; private WalkViewerBehavior walkBehavior = null; //-------------------------------------------------------------- // ADMINISTRATION //-------------------------------------------------------------- /** * The main program entry point when invoked as an application. Each example * application that extends this class must define their own main. * * @param args * a String array of command-line arguments */ public static void main(String[] args) { Java3DFrame ex = new Java3DFrame(); ex.initialize(args); ex.buildUniverse(); ex.showFrame(); } /** * Constructs a new Example object. * * @return a new Example that draws no 3D content */ public Java3DFrame() { // Do nothing } /** * Initializes the application when invoked as an applet. */ public void init() { // Collect properties into String array String[] args = new String[2]; // NOTE: to be done still... this.initialize(args); this.buildUniverse(); this.showFrame(); // NOTE: add something to the browser page? } /** * Initializes the Example by parsing command-line arguments, building an * AWT Frame, constructing a menubar, and creating the 3D canvas. * * @param args * a String array of command-line arguments */ protected void initialize(String[] args) { example = this; // Parse incoming arguments parseArgs(args); // Build the frame if (debug) System.err.println("Building GUI..."); exampleFrame = new Frame(); exampleFrame.setSize(640, 480); exampleFrame.setTitle("Java 3D Example"); exampleFrame.setLayout(new BorderLayout()); // Set up a close behavior exampleFrame.addWindowListener(this); // Create a canvas exampleCanvas = new Canvas3D(null); exampleCanvas.setSize(630, 460); exampleFrame.add("Center", exampleCanvas); // Build the menubar exampleMenuBar = this.buildMenuBar(); exampleFrame.setMenuBar(exampleMenuBar); // Pack exampleFrame.pack(); exampleFrame.validate(); // exampleFrame.setVisible( true ); } /** * Parses incoming command-line arguments. Applications that subclass this * class may override this method to support their own command-line * arguments. * * @param args * a String array of command-line arguments */ protected void parseArgs(String[] args) { for (int i = 0; i < args.length; i++) { if (args[i].equals("-d")) debug = true; } } //-------------------------------------------------------------- // SCENE CONTENT //-------------------------------------------------------------- /** * Builds the 3D universe by constructing a virtual universe (via * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via * SimpleUniverse). A headlight is added and a set of behaviors initialized * to handle navigation types. */ protected void buildUniverse() { // // Create a SimpleUniverse object, which builds: // // - a Locale using the given hi-res coordinate origin // // - a ViewingPlatform which in turn builds: // - a MultiTransformGroup with which to move the // the ViewPlatform about // // - a ViewPlatform to hold the view // // - a BranchGroup to hold avatar geometry (if any) // // - a BranchGroup to hold view platform // geometry (if any) // // - a Viewer which in turn builds: // - a PhysicalBody which characterizes the user"s // viewing preferences and abilities // // - a PhysicalEnvironment which characterizes the // user"s rendering hardware and software // // - a JavaSoundMixer which initializes sound // support within the 3D environment // // - a View which renders the scene into a Canvas3D // // All of these actions could be done explicitly, but // using the SimpleUniverse utilities simplifies the code. // if (debug) System.err.println("Building scene graph..."); SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate // for the origin - // use default 1, // Number of transforms in MultiTransformGroup exampleCanvas, // Canvas3D into which to draw null); // URL for user configuration file - use defaults // // Get the viewer and create an audio device so that // sound will be enabled in this content. // Viewer viewer = universe.getViewer(); viewer.createAudioDevice(); // // Get the viewing platform created by SimpleUniverse. // From that platform, get the inner-most TransformGroup // in the MultiTransformGroup. That inner-most group // contains the ViewPlatform. It is this inner-most // TransformGroup we need in order to: // // - add a "headlight" that always aims forward from // the viewer // // - change the viewing direction in a "walk" style // // The inner-most TransformGroup"s transform will be // changed by the walk behavior (when enabled). // ViewingPlatform viewingPlatform = universe.getViewingPlatform(); exampleViewTransform = viewingPlatform.getViewPlatformTransform(); // // Create a "headlight" as a forward-facing directional light. // Set the light"s bounds to huge. Since we want the light // on the viewer"s "head", we need the light within the // TransformGroup containing the ViewPlatform. The // ViewingPlatform class creates a handy hook to do this // called "platform geometry". The PlatformGeometry class is // subclassed off of BranchGroup, and is intended to contain // a description of the 3D platform itself... PLUS a headlight! // So, to add the headlight, create a new PlatformGeometry group, // add the light to it, then add that platform geometry to the // ViewingPlatform. // BoundingSphere allBounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), 100000.0); PlatformGeometry pg = new PlatformGeometry(); headlight = new DirectionalLight(); headlight.setColor(White); headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f)); headlight.setInfluencingBounds(allBounds); headlight.setCapability(Light.ALLOW_STATE_WRITE); pg.addChild(headlight); viewingPlatform.setPlatformGeometry(pg); // // Create the 3D content BranchGroup, containing: // // - a TransformGroup who"s transform the examine behavior // will change (when enabled). // // - 3D geometry to view // // Build the scene root BranchGroup sceneRoot = new BranchGroup(); // Build a transform that we can modify exampleSceneTransform = new TransformGroup(); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_READ); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND); // // Build the scene, add it to the transform, and add // the transform to the scene root // if (debug) System.err.println(" scene..."); Group scene = this.buildScene(); exampleSceneTransform.addChild(scene); sceneRoot.addChild(exampleSceneTransform); // // Create a pair of behaviors to implement two navigation // types: // // - "examine": a style where mouse drags rotate about // the scene"s origin as if it is an object under // examination. This is similar to the "Examine" // navigation type used by VRML browsers. // // - "walk": a style where mouse drags rotate about // the viewer"s center as if the viewer is turning // about to look at a scene they are in. This is // similar to the "Walk" navigation type used by // VRML browsers. // // Aim the examine behavior at the scene"s TransformGroup // and add the behavior to the scene root. // // Aim the walk behavior at the viewing platform"s // TransformGroup and add the behavior to the scene root. // // Enable one (and only one!) of the two behaviors // depending upon the current navigation type. // examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform // gorup // to // modify exampleFrame); // Parent frame for cusor changes examineBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(examineBehavior); walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform // group to // modify exampleFrame); // Parent frame for cusor changes walkBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(walkBehavior); if (navigationType == Walk) { examineBehavior.setEnable(false); walkBehavior.setEnable(true); } else { examineBehavior.setEnable(true); walkBehavior.setEnable(false); } // // Compile the scene branch group and add it to the // SimpleUniverse. // if (shouldCompile) sceneRoot.rupile(); universe.addBranchGraph(sceneRoot); reset(); } /** * Builds the scene. Example application subclasses should replace this * method with their own method to build 3D content. * * @return a Group containing 3D content to display */ public Group buildScene() { // Build the scene group containing nothing Group scene = new Group(); return scene; } //-------------------------------------------------------------- // SET/GET METHODS //-------------------------------------------------------------- /** * Sets the headlight on/off state. The headlight faces forward in the * direction the viewer is facing. Example applications that add their own * lights will typically turn the headlight off. A standard menu item * enables the headlight to be turned on and off via user control. * * @param onOff * a boolean turning the light on (true) or off (false) */ public void setHeadlightEnable(boolean onOff) { headlightOnOff = onOff; if (headlight != null) headlight.setEnable(headlightOnOff); if (headlightMenuItem != null) headlightMenuItem.setState(headlightOnOff); } /** * Gets the headlight on/off state. * * @return a boolean indicating if the headlight is on or off */ public boolean getHeadlightEnable() { return headlightOnOff; } /** * Sets the navigation type to be either Examine or Walk. The Examine * navigation type sets up behaviors that use mouse drags to rotate and * translate scene content as if it is an object held at arm"s length and * under examination. The Walk navigation type uses mouse drags to rotate * and translate the viewer as if they are walking through the content. The * Examine type is the default. * * @param nav * either Walk or Examine */ public void setNavigationType(int nav) { if (nav == Walk) { navigationType = Walk; if (walkMenuItem != null) walkMenuItem.setState(true); if (examineMenuItem != null) examineMenuItem.setState(false); if (walkBehavior != null) walkBehavior.setEnable(true); if (examineBehavior != null) examineBehavior.setEnable(false); } else { navigationType = Examine; if (walkMenuItem != null) walkMenuItem.setState(false); if (examineMenuItem != null) examineMenuItem.setState(true); if (walkBehavior != null) walkBehavior.setEnable(false); if (examineBehavior != null) examineBehavior.setEnable(true); } } /** * Gets the current navigation type, returning either Walk or Examine. * * @return either Walk or Examine */ public int getNavigationType() { return navigationType; } /** * Sets whether the scene graph should be compiled or not. Normally this is * always a good idea. For some example applications that use this Example * framework, it is useful to disable compilation - particularly when nodes * and node components will need to be made un-live in order to make * changes. Once compiled, such components can be made un-live, but they are * still unchangable unless appropriate capabilities have been set. * * @param onOff * a boolean turning compilation on (true) or off (false) */ public void setCompilable(boolean onOff) { shouldCompile = onOff; } /** * Gets whether the scene graph will be compiled or not. * * @return a boolean indicating if scene graph compilation is on or off */ public boolean getCompilable() { return shouldCompile; } //These methods will be replaced // Set the view position and direction public void setViewpoint(Point3f position, Vector3f direction) { Transform3D t = new Transform3D(); t.set(new Vector3f(position)); exampleViewTransform.setTransform(t); // how to set direction? } // Reset transforms public void reset() { Transform3D trans = new Transform3D(); exampleSceneTransform.setTransform(trans); trans.set(new Vector3f(0.0f, 0.0f, 10.0f)); exampleViewTransform.setTransform(trans); setNavigationType(navigationType); } // // Gets the URL (with file: prepended) for the current directory. // This is a terrible hack needed in the Alpha release of Java3D // in order to build a full path URL for loading sounds with // MediaContainer. When MediaContainer is fully implemented, // it should handle relative path names, but not yet. // public String getCurrentDirectory() { // Create a bogus file so that we can query it"s path File dummy = new File("dummy.tmp"); String dummyPath = dummy.getAbsolutePath(); // strip "/dummy.tmp" from end of dummyPath and put into "path" if (dummyPath.endsWith(File.separator + "dummy.tmp")) { int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp"); if (index >= 0) { int pathLength = index + 5; // pre-pend "file:" char[] charPath = new char[pathLength]; dummyPath.getChars(0, index, charPath, 5); String path = new String(charPath, 0, pathLength); path = "file:" + path.substring(5, pathLength); return path + File.separator; } } return dummyPath + File.separator; } //-------------------------------------------------------------- // USER INTERFACE //-------------------------------------------------------------- /** * Builds the example AWT Frame menubar. Standard menus and their options * are added. Applications that subclass this class should build their * menubar additions within their initialize method. * * @return a MenuBar for the AWT Frame */ private MenuBar buildMenuBar() { // Build the menubar MenuBar menuBar = new MenuBar(); // File menu Menu m = new Menu("File"); m.addActionListener(this); m.add("Exit"); menuBar.add(m); // View menu m = new Menu("View"); m.addActionListener(this); m.add("Reset view"); m.addSeparator(); walkMenuItem = new CheckboxMenuItem("Walk"); walkMenuItem.addItemListener(this); m.add(walkMenuItem); examineMenuItem = new CheckboxMenuItem("Examine"); examineMenuItem.addItemListener(this); m.add(examineMenuItem); if (navigationType == Walk) { walkMenuItem.setState(true); examineMenuItem.setState(false); } else { walkMenuItem.setState(false); examineMenuItem.setState(true); } m.addSeparator(); headlightMenuItem = new CheckboxMenuItem("Headlight on/off"); headlightMenuItem.addItemListener(this); headlightMenuItem.setState(headlightOnOff); m.add(headlightMenuItem); menuBar.add(m); return menuBar; } /** * Shows the application"s frame, making it and its menubar, 3D canvas, and * 3D content visible. */ public void showFrame() { exampleFrame.show(); } /** * Quits the application. */ public void quit() { System.exit(0); } /** * Handles menu selections. * * @param event * an ActionEvent indicating what menu action requires handling */ public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg.equals("Reset view")) reset(); else if (arg.equals("Exit")) quit(); } /** * Handles checkbox items on a CheckboxMenu. The Example class has none of * its own, but subclasses may have some. * * @param menu * which CheckboxMenu needs action * @param check * which CheckboxMenu item has changed */ public void checkboxChanged(CheckboxMenu menu, int check) { // None for us } /** * Handles on/off checkbox items on a standard menu. * * @param event * an ItemEvent indicating what requires handling */ public void itemStateChanged(ItemEvent event) { Object src = event.getSource(); boolean state; if (src == headlightMenuItem) { state = headlightMenuItem.getState(); headlight.setEnable(state); } else if (src == walkMenuItem) setNavigationType(Walk); else if (src == examineMenuItem) setNavigationType(Examine); } /** * Handles a window closing event notifying the application that the user * has chosen to close the application without selecting the "Exit" menu * item. * * @param event * a WindowEvent indicating the window is closing */ public void windowClosing(WindowEvent event) { quit(); } public void windowClosed(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } // Well known colors, positions, and directions public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f); public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f); public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f); public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f); public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f); public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f); public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f); public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f); public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f); public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f); public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f); public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f); public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f); public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f); public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f); public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f); public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f); public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f); public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f); public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f); public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f); public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f); public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f); public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f); public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f); public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f); public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f); } // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener { public void checkboxChanged(CheckboxMenu menu, int check); } /** * ExamineViewerBehavior * * @version 1.0, 98/04/16 */ /** * Wakeup on mouse button presses, releases, and mouse movements and generate * transforms in an "examination style" that enables the user to rotate, * translation, and zoom an object as if it is held at arm"s length. Such an * examination style is similar to the "Examine" navigation type used by VRML * browsers. * * The behavior maps mouse drags to different transforms depending upon the * mosue button held down: * * Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement --> * X-axis rotation * * Button 2 (middle) Horizontal movement --> nothing Vertical movement --> * Z-axis translation * * Button 3 (right) Horizontal movement --> X-axis translation Vertical movement * --> Y-axis translation * * To support systems with 2 or 1 mouse buttons, the following alternate * mappings are supported while dragging with any mouse button held down and * zero or more keyboard modifiers held down: * * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3 * * The behavior automatically modifies a TransformGroup provided to the * constructor. The TransformGroup"s transform can be set at any time by the * application or other behaviors to cause the examine rotation and translation * to be reset. */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior { // Previous cursor location protected int previousX = 0; protected int previousY = 0; // Saved standard cursor protected Cursor savedCursor = null; /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. */ public ExamineViewerBehavior() { super(); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. * * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(Component parent) { super(parent); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. */ public ExamineViewerBehavior(TransformGroup transformGroup) { super(); subjectTransformGroup = transformGroup; } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) { super(parent); subjectTransformGroup = transformGroup; } /** * Respond to a button1 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton1(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a rotation // // Compute the delta in X and Y from the previous // position. Use the delta to compute rotation // angles with the mapping: // // positive X mouse delta --> positive Y-axis rotation // positive Y mouse delta --> positive X-axis rotation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xRotationAngle = deltaY * XRotationFactor; double yRotationAngle = deltaX * YRotationFactor; // // Build transforms // transform1.rotX(xRotationAngle); transform2.rotY(yRotationAngle); // Get and save the current transform matrix subjectTransformGroup.getTransform(currentTransform); currentTransform.get(matrix); translate.set(matrix.m03, matrix.m13, matrix.m23); // Translate to the origin, rotate, then translate back currentTransform.setTranslation(origin); currentTransform.mul(transform1, currentTransform); currentTransform.mul(transform2, currentTransform); currentTransform.setTranslation(translate); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button2 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton2(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive Y mouse delta --> positive Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaY = y - previousY; if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double zTranslationDistance = deltaY * ZTranslationFactor; // // Build transforms // translate.set(0.0, 0.0, zTranslationDistance); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button3 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton3(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in X and Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive X mouse delta --> positive X-axis translation // positive Y mouse delta --> negative Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xTranslationDistance = deltaX * XTranslationFactor; double yTranslationDistance = -deltaY * YTranslationFactor; // // Build transforms // translate.set(xTranslationDistance, yTranslationDistance, 0.0); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to an elapsed frames event (assuming subclass has set up a wakeup * criterion for it). * * @param time * A WakeupOnElapsedFrames criterion to respond to. */ public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) { // Can"t happen } } /* * * Copyright (c) 1998 David R. Nadeau * */ /** * WalkViewerBehavior is a utility class that creates a "walking style" * navigation symantic. * * The behavior wakes up on mouse button presses, releases, and mouse movements * and generates transforms in a "walk style" that enables the user to walk * through a scene, translating and turning about as if they are within the * scene. Such a walk style is similar to the "Walk" navigation type used by * VRML browsers. * <P> * The behavior maps mouse drags to different transforms depending upon the * mouse button held down: *
-
*
- Button 1 (left) *
- Horizontal movement --> Y-axis rotation
*
- Vertical movement --> Z-axis translation * *
- Button 2 (middle) *
- Horizontal movement --> Y-axis rotation
*
- Vertical movement --> X-axis rotation * *
- Button 3 (right) *
- Horizontal movement --> X-axis translation
*
- Vertical movement --> Y-axis translation *
* * To support systems with 2 or 1 mouse buttons, the following alternate * mappings are supported while dragging with any mouse button held down and * zero or more keyboard modifiers held down:*
-
*
- No modifiers = Button 1 *
- ALT = Button 2 *
- Meta = Button 3 *
- Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the * constructor. The TransformGroup"s transform can be set at any time by the * application or other behaviors to cause the walk rotation and translation to * be reset. * <P> * While a mouse button is down, the behavior automatically changes the cursor * in a given parent AWT Component. If no parent Component is given, no cursor * changes are attempted. * * @version 1.0, 98/04/16 * @author David R. Nadeau, San Diego Supercomputer Center */
class WalkViewerBehavior extends ViewerBehavior {
// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Walk" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. // Previous and initial cursor locations protected int previousX = 0; protected int previousY = 0; protected int initialX = 0; protected int initialY = 0; // Deadzone size (delta from initial XY for which no // translate or rotate action is taken protected static final int DELTAX_DEADZONE = 10; protected static final int DELTAY_DEADZONE = 10; // Keep a set of wakeup criterion for animation-generated // event types. protected WakeupCriterion[] mouseAndAnimationEvents = null; protected WakeupOr mouseAndAnimationCriterion = null; protected WakeupOr savedMouseCriterion = null; // Saved standard cursor protected Cursor savedCursor = null; /** * Default Rotation and translation scaling factors for animated movements * (Button 1 press). */ public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002; public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01; protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR; protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR; /** * Constructs a new walk behavior that converts mouse actions into rotations * and translations. Rotations and translations are written into a * TransformGroup that must be set using the setTransformGroup method. The * cursor will be changed during mouse actions if the parent frame is set * using the setParentComponent method. * * @return a new WalkViewerBehavior that needs its TransformGroup and parent * Component set */ public WalkViewerBehavior() { super(); } /** * Constructs a new walk behavior that converts mouse actions into rotations * and translations. Rotations and translations are written into a * TransformGroup that must be set using the setTransformGroup method. The * cursor will be changed within the given AWT parent Component during mouse * drags. * * @param parent * a parent AWT Component within which the cursor will change * during mouse drags * * @return a new WalkViewerBehavior that needs its TransformGroup and parent * Component set */ public WalkViewerBehavior(Component parent) { super(parent); } /** * Constructs a new walk behavior that converts mouse actions into rotations * and translations. Rotations and translations are written into the given * TransformGroup. The cursor will be changed during mouse actions if the * parent frame is set using the setParentComponent method. * * @param transformGroup * a TransformGroup whos transform is read and written by the * behavior * * @return a new WalkViewerBehavior that needs its TransformGroup and parent * Component set */ public WalkViewerBehavior(TransformGroup transformGroup) { super(); subjectTransformGroup = transformGroup; } /** * Constructs a new walk behavior that converts mouse actions into rotations * and translations. Rotations and translations are written into the given * TransformGroup. The cursor will be changed within the given AWT parent * Component during mouse drags. * * @param transformGroup * a TransformGroup whos transform is read and written by the * behavior * * @param parent * a parent AWT Component within which the cursor will change * during mouse drags * * @return a new WalkViewerBehavior that needs its TransformGroup and parent * Component set */ public WalkViewerBehavior(TransformGroup transformGroup, Component parent) { super(parent); subjectTransformGroup = transformGroup; } /** * Initializes the behavior. */ public void initialize() { super.initialize(); savedMouseCriterion = mouseCriterion; // from parent class mouseAndAnimationEvents = new WakeupCriterion[4]; mouseAndAnimationEvents[0] = new WakeupOnAWTEvent( MouseEvent.MOUSE_DRAGGED); mouseAndAnimationEvents[1] = new WakeupOnAWTEvent( MouseEvent.MOUSE_PRESSED); mouseAndAnimationEvents[2] = new WakeupOnAWTEvent( MouseEvent.MOUSE_RELEASED); mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0); mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents); // Don"t use the above criterion until a button 1 down event } /** * Sets the Y rotation animation scaling factor for Y-axis rotations. This * scaling factor is used to control the speed of Y rotation when button 1 * is pressed and dragged. * * @param factor * the double Y rotation scaling factor */ public void setYRotationAnimationFactor(double factor) { YRotationAnimationFactor = factor; } /** * Gets the current Y animation rotation scaling factor for Y-axis * rotations. * * @return the double Y rotation scaling factor */ public double getYRotationAnimationFactor() { return YRotationAnimationFactor; } /** * Sets the Z animation translation scaling factor for Z-axis translations. * This scaling factor is used to control the speed of Z translation when * button 1 is pressed and dragged. * * @param factor * the double Z translation scaling factor */ public void setZTranslationAnimationFactor(double factor) { ZTranslationAnimationFactor = factor; } /** * Gets the current Z animation translation scaling factor for Z-axis * translations. * * @return the double Z translation scaling factor */ public double getZTranslationAnimationFactor() { return ZTranslationAnimationFactor; } /** * Responds to an elapsed frames event. Such an event is generated on every * frame while button 1 is held down. On each call, this method computes new * Y-axis rotation and Z-axis translation values and writes them to the * behavior"s TransformGroup. The translation and rotation amounts are * computed based upon the distance between the current cursor location and * the cursor location when button 1 was pressed. As this distance * increases, the translation or rotation amount increases. * * @param time * the WakeupOnElapsedFrames criterion to respond to */ public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) { // // Time elapsed while button down: create a rotation and // a translation. // // Compute the delta in X and Y from the initial position to // the previous position. Multiply the delta times a scaling // factor to compute an offset to add to the current translation // and rotation. Use the mapping: // // positive X mouse delta --> negative Y-axis rotation // positive Y mouse delta --> positive Z-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // if (buttonPressed != BUTTON1) return; int deltaX = previousX - initialX; int deltaY = previousY - initialY; double yRotationAngle = -deltaX * YRotationAnimationFactor; double zTranslationDistance = deltaY * ZTranslationAnimationFactor; // // Build transforms // transform1.rotY(yRotationAngle); translate.set(0.0, 0.0, zTranslationDistance); // Get and save the current transform matrix subjectTransformGroup.getTransform(currentTransform); currentTransform.get(matrix); // Translate to the origin, rotate, then translate back currentTransform.setTranslation(origin); currentTransform.mul(transform1, currentTransform); // Translate back from the origin by the original translation // distance, plus the new walk translation... but force walk // to travel on a plane by ignoring the Y component of a // transformed translation vector. currentTransform.transform(translate); translate.x += matrix.m03; // add in existing X translation translate.y = matrix.m13; // use Y translation translate.z += matrix.m23; // add in existing Z translation currentTransform.setTranslation(translate); // Update the transform group subjectTransformGroup.setTransform(currentTransform); } /** * Responds to a button1 event (press, release, or drag). On a press, the * method adds a wakeup criterion to the behavior"s set, callling for the * behavior to be awoken on each frame. On a button prelease, this criterion * is removed from the set. * * @param mouseEvent * the MouseEvent to respond to */ public void onButton1(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position and change // the wakeup criterion to include elapsed time wakeups // so we can animate. previousX = x; previousY = y; initialX = x; initialY = y; // Swap criterion... parent class will not reschedule us mouseCriterion = mouseAndAnimationCriterion; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: restore original wakeup // criterion which only includes mouse activity, not // elapsed time mouseCriterion = savedMouseCriterion; // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } previousX = x; previousY = y; } /** * Responds to a button2 event (press, release, or drag). On a press, the * method records the initial cursor location. On a drag, the difference * between the current and previous cursor location provides a delta that * controls the amount by which to rotate in X and Y. * * @param mouseEvent * the MouseEvent to respond to */ public void onButton2(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; initialX = x; initialY = y; // Change to a "rotate" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a rotation // // Compute the delta in X and Y from the previous // position. Use the delta to compute rotation // angles with the mapping: // // positive X mouse delta --> negative Y-axis rotation // positive Y mouse delta --> negative X-axis rotation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = 0; if (Math.abs(y - initialY) > DELTAY_DEADZONE) { // Cursor has moved far enough vertically to consider // it intentional, so get it"s delta. deltaY = y - previousY; } if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xRotationAngle = -deltaY * XRotationFactor; double yRotationAngle = -deltaX * YRotationFactor; // // Build transforms // transform1.rotX(xRotationAngle); transform2.rotY(yRotationAngle); // Get and save the current transform matrix subjectTransformGroup.getTransform(currentTransform); currentTransform.get(matrix); translate.set(matrix.m03, matrix.m13, matrix.m23); // Translate to the origin, rotate, then translate back currentTransform.setTranslation(origin); currentTransform.mul(transform2, currentTransform); currentTransform.mul(transform1); currentTransform.setTranslation(translate); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Responds to a button3 event (press, release, or drag). On a drag, the * difference between the current and previous cursor location provides a * delta that controls the amount by which to translate in X and Y. * * @param mouseEvent * the MouseEvent to respond to */ public void onButton3(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in X and Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive X mouse delta --> positive X-axis translation // positive Y mouse delta --> negative Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xTranslationDistance = deltaX * XTranslationFactor; double yTranslationDistance = -deltaY * YTranslationFactor; // // Build transforms // translate.set(xTranslationDistance, yTranslationDistance, 0.0); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; }
} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {
// State protected CheckboxMenuItem[] checks = null; protected int current = 0; protected CheckboxMenuListener listener = null; // Construct public CheckboxMenu(String name, NameValue[] items, CheckboxMenuListener listen) { this(name, items, 0, listen); } public CheckboxMenu(String name, NameValue[] items, int cur, CheckboxMenuListener listen) { super(name); current = cur; listener = listen; if (items == null) return; checks = new CheckboxMenuItem[items.length]; for (int i = 0; i < items.length; i++) { checks[i] = new CheckboxMenuItem(items[i].name, false); checks[i].addItemListener(this); add(checks[i]); } checks[cur].setState(true); } // Handle checkbox changed events public void itemStateChanged(ItemEvent event) { Object src = event.getSource(); for (int i = 0; i < checks.length; i++) { if (src == checks[i]) { // Update the checkboxes checks[current].setState(false); current = i; checks[current].setState(true); if (listener != null) listener.checkboxChanged(this, i); return; } } } // Methods to get and set state public int getCurrent() { return current; } public void setCurrent(int cur) { if (cur < 0 || cur >= checks.length) return; // ignore out of range choices if (checks == null) return; checks[current].setState(false); current = cur; checks[current].setState(true); } public CheckboxMenuItem getSelectedCheckbox() { if (checks == null) return null; return checks[current]; } public void setSelectedCheckbox(CheckboxMenuItem item) { if (checks == null) return; for (int i = 0; i < checks.length; i++) { if (item == checks[i]) { checks[i].setState(false); current = i; checks[i].setState(true); } } }
} /**
* ViewerBehavior * * @version 1.0, 98/04/16 */
/**
* Wakeup on mouse button presses, releases, and mouse movements and generate * transforms for a transform group. Classes that extend this class impose * specific symantics, such as "Examine" or "Walk" viewing, similar to the * navigation types used by VRML browsers. * * To support systems with 2 or 1 mouse buttons, the following alternate * mappings are supported while dragging with any mouse button held down and * zero or more keyboard modifiers held down: * * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3 * * The behavior automatically modifies a TransformGroup provided to the * constructor. The TransformGroup"s transform can be set at any time by the * application or other behaviors to cause the viewer"s rotation and translation * to be reset. */
// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {
// Keep track of the transform group who"s transform we modify // during mouse motion. protected TransformGroup subjectTransformGroup = null; // Keep a set of wakeup criterion for different mouse-generated // event types. protected WakeupCriterion[] mouseEvents = null; protected WakeupOr mouseCriterion = null; // Track which button was last pressed protected static final int BUTTONNONE = -1; protected static final int BUTTON1 = 0; protected static final int BUTTON2 = 1; protected static final int BUTTON3 = 2; protected int buttonPressed = BUTTONNONE; // Keep a few Transform3Ds for use during event processing. This // avoids having to allocate new ones on each event. protected Transform3D currentTransform = new Transform3D(); protected Transform3D transform1 = new Transform3D(); protected Transform3D transform2 = new Transform3D(); protected Matrix4d matrix = new Matrix4d(); protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0); protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0); // Unusual X and Y delta limits. protected static final int UNUSUAL_XDELTA = 400; protected static final int UNUSUAL_YDELTA = 400; protected Component parentComponent = null; /** * Construct a viewer behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. */ public ViewerBehavior() { super(); } /** * Construct a viewer behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. * * @param parent * The AWT Component that contains the area generating mouse * events. */ public ViewerBehavior(Component parent) { super(); parentComponent = parent; } /** * Construct a viewer behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. */ public ViewerBehavior(TransformGroup transformGroup) { super(); subjectTransformGroup = transformGroup; } /** * Construct a viewer behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. * @param parent * The AWT Component that contains the area generating mouse * events. */ public ViewerBehavior(TransformGroup transformGroup, Component parent) { super(); subjectTransformGroup = transformGroup; parentComponent = parent; } /** * Set the transform group modified by the viewer behavior. Setting the * transform group to null disables the behavior until the transform group * is again set to an existing group. * * @param transformGroup * The new transform group to be modified by the behavior. */ public void setTransformGroup(TransformGroup transformGroup) { subjectTransformGroup = transformGroup; } /** * Get the transform group modified by the viewer behavior. */ public TransformGroup getTransformGroup() { return subjectTransformGroup; } /** * Sets the parent component who"s cursor will be changed during mouse * drags. If no component is given is given to the constructor, or set via * this method, no cursor changes will be done. * * @param parent * the AWT Component, such as a Frame, within which cursor * changes should take place during mouse drags */ public void setParentComponent(Component parent) { parentComponent = parent; } /* * Gets the parent frame within which the cursor changes during mouse drags. * * @return the AWT Component, such as a Frame, within which cursor changes * should take place during mouse drags. Returns null if no parent is set. */ public Component getParentComponent() { return parentComponent; } /** * Initialize the behavior. */ public void initialize() { // Wakeup when the mouse is dragged or when a mouse button // is pressed or released. mouseEvents = new WakeupCriterion[3]; mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED); mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED); mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED); mouseCriterion = new WakeupOr(mouseEvents); wakeupOn(mouseCriterion); } /** * Process a new wakeup. Interpret mouse button presses, releases, and mouse * drags. * * @param criteria * The wakeup criteria causing the behavior wakeup. */ public void processStimulus(Enumeration criteria) { WakeupCriterion wakeup = null; AWTEvent[] event = null; int whichButton = BUTTONNONE; // Process all pending wakeups while (criteria.hasMoreElements()) { wakeup = (WakeupCriterion) criteria.nextElement(); if (wakeup instanceof WakeupOnAWTEvent) { event = ((WakeupOnAWTEvent) wakeup).getAWTEvent(); // Process all pending events for (int i = 0; i < event.length; i++) { if (event[i].getID() != MouseEvent.MOUSE_PRESSED && event[i].getID() != MouseEvent.MOUSE_RELEASED && event[i].getID() != MouseEvent.MOUSE_DRAGGED) // Ignore uninteresting mouse events continue; // // Regretably, Java event handling (or perhaps // underlying OS event handling) doesn"t always // catch button bounces (redundant presses and // releases), or order events so that the last // drag event is delivered before a release. // This means we can get stray events that we // filter out here. // if (event[i].getID() == MouseEvent.MOUSE_PRESSED && buttonPressed != BUTTONNONE) // Ignore additional button presses until a release continue; if (event[i].getID() == MouseEvent.MOUSE_RELEASED && buttonPressed == BUTTONNONE) // Ignore additional button releases until a press continue; if (event[i].getID() == MouseEvent.MOUSE_DRAGGED && buttonPressed == BUTTONNONE) // Ignore drags until a press continue; MouseEvent mev = (MouseEvent) event[i]; int modifiers = mev.getModifiers(); // // Unfortunately, the underlying event handling // doesn"t do a "grab" operation when a mouse button // is pressed. This means that once a button is // pressed, if another mouse button or a keyboard // modifier key is pressed, the delivered mouse event // will show that a different button is being held // down. For instance: // // Action Event // Button 1 press Button 1 press // Drag with button 1 down Button 1 drag // ALT press - // Drag with ALT & button 1 down Button 2 drag // Button 1 release Button 2 release // // The upshot is that we can get a button press // without a matching release, and the button // associated with a drag can change mid-drag. // // To fix this, we watch for an initial button // press, and thenceforth consider that button // to be the one held down, even if additional // buttons get pressed, and despite what is // reported in the event. Only when a button is // released, do we end such a grab. // if (buttonPressed == BUTTONNONE) { // No button is pressed yet, figure out which // button is down now and how to direct events if (((modifiers & InputEvent.BUTTON3_MASK) != 0) || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) { // Button 3 activity (META or CTRL down) whichButton = BUTTON3; } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { // Button 2 activity (ALT down) whichButton = BUTTON2; } else { // Button 1 activity (no modifiers down) whichButton = BUTTON1; } // If the event is to press a button, then // record that that button is now down if (event[i].getID() == MouseEvent.MOUSE_PRESSED) buttonPressed = whichButton; } else { // Otherwise a button was pressed earlier and // hasn"t been released yet. Assign all further // events to it, even if ALT, META, CTRL, or // another button has been pressed as well. whichButton = buttonPressed; } // Distribute the event switch (whichButton) { case BUTTON1: onButton1(mev); break; case BUTTON2: onButton2(mev); break; case BUTTON3: onButton3(mev); break; default: break; } // If the event is to release a button, then // record that that button is now up if (event[i].getID() == MouseEvent.MOUSE_RELEASED) buttonPressed = BUTTONNONE; } continue; } if (wakeup instanceof WakeupOnElapsedFrames) { onElapsedFrames((WakeupOnElapsedFrames) wakeup); continue; } } // Reschedule us for another wakeup wakeupOn(mouseCriterion); } /** * Default X and Y rotation factors, and XYZ translation factors. */ public static final double DEFAULT_XROTATION_FACTOR = 0.02; public static final double DEFAULT_YROTATION_FACTOR = 0.005; public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02; public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02; public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04; protected double XRotationFactor = DEFAULT_XROTATION_FACTOR; protected double YRotationFactor = DEFAULT_YROTATION_FACTOR; protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR; protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR; protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR; /** * Set the X rotation scaling factor for X-axis rotations. * * @param factor * The new scaling factor. */ public void setXRotationFactor(double factor) { XRotationFactor = factor; } /** * Get the current X rotation scaling factor for X-axis rotations. */ public double getXRotationFactor() { return XRotationFactor; } /** * Set the Y rotation scaling factor for Y-axis rotations. * * @param factor * The new scaling factor. */ public void setYRotationFactor(double factor) { YRotationFactor = factor; } /** * Get the current Y rotation scaling factor for Y-axis rotations. */ public double getYRotationFactor() { return YRotationFactor; } /** * Set the X translation scaling factor for X-axis translations. * * @param factor * The new scaling factor. */ public void setXTranslationFactor(double factor) { XTranslationFactor = factor; } /** * Get the current X translation scaling factor for X-axis translations. */ public double getXTranslationFactor() { return XTranslationFactor; } /** * Set the Y translation scaling factor for Y-axis translations. * * @param factor * The new scaling factor. */ public void setYTranslationFactor(double factor) { YTranslationFactor = factor; } /** * Get the current Y translation scaling factor for Y-axis translations. */ public double getYTranslationFactor() { return YTranslationFactor; } /** * Set the Z translation scaling factor for Z-axis translations. * * @param factor * The new scaling factor. */ public void setZTranslationFactor(double factor) { ZTranslationFactor = factor; } /** * Get the current Z translation scaling factor for Z-axis translations. */ public double getZTranslationFactor() { return ZTranslationFactor; } /** * Respond to a button1 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public abstract void onButton1(MouseEvent mouseEvent); /** * Respond to a button2 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public abstract void onButton2(MouseEvent mouseEvent); /** * Responed to a button3 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public abstract void onButton3(MouseEvent mouseEvent); /** * Respond to an elapsed frames event (assuming subclass has set up a wakeup * criterion for it). * * @param time * A WakeupOnElapsedFrames criterion to respond to. */ public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);
} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {
public String name; public Object value; public NameValue(String n, Object v) { name = n; value = v; }
}
</source>
Illustrates dynamic texture coordinate generation using the TexCoordGeneration class
<source lang="java">
/**********************************************************
Copyright (C) 2001 Daniel Selman First distributed with the book "Java 3D Programming" by Daniel Selman and published by Manning Publications. http://manning.ru/selman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Authors can be contacted at: Daniel Selman: daniel@selman.org If you make changes you think others would like, please contact one of the authors or someone at the www.j3d.org web site. **************************************************************/
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Button; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.net.URL; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.PositionInterpolator; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Shape3D; import javax.media.j3d.TexCoordGeneration; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.GeometryInfo; import com.sun.j3d.utils.geometry.NormalGenerator; import com.sun.j3d.utils.image.TextureLoader; /**
* This example illustrates dynamic texture coordinate generation using the * TexCoordGeneration class. The OBJECT_LINEAR, EYE_LINEAR and SPHERE_MAP modes * are illustrated. The application creates a DEM landscape (from a sin/cos * curve) and uses dynamic texture coordinate generation to map contours onto * the landscape - either relative to its relative position (OBJECT_LINEAR) or * absolute position (EYE_LINEAR). SPHERE_MAP maps the contours onto the * landscape as a environment map. The "Rotate" and "Translate" buttons toggle * two Interpolators that rotate and translate the entire scene respectivly. */
public class TexCoordTest extends Java3dApplet implements ActionListener {
private static int m_kWidth = 500; private static int m_kHeight = 400; // we need to track these as we modify them inside // the actionPerformed method Appearance m_Appearance = null; TexCoordGeneration m_TexGen = null; PositionInterpolator m_PositionInterpolator = null; RotationInterpolator m_RotationInterpolator = null; public TexCoordTest() { initJava3d(); } protected void addCanvas3D(Canvas3D c3d) { setLayout(new BorderLayout()); add(c3d, BorderLayout.CENTER); Panel controlPanel = new Panel(); // creates some GUI components so we can modify the // scene and texure coordinate generation at runtime Button eyeButton = new Button("EYE_LINEAR"); eyeButton.addActionListener(this); controlPanel.add(eyeButton); Button objectButton = new Button("OBJECT_LINEAR"); objectButton.addActionListener(this); controlPanel.add(objectButton); Button sphereButton = new Button("SPHERE_MAP"); sphereButton.addActionListener(this); controlPanel.add(sphereButton); Button rotateButton = new Button("Rotate"); rotateButton.addActionListener(this); controlPanel.add(rotateButton); Button translateButton = new Button("Translate"); translateButton.addActionListener(this); controlPanel.add(translateButton); add(controlPanel, BorderLayout.SOUTH); doLayout(); } // overidden as we don"t want a background for this example protected Background createBackground() { return null; } // we are using a big landscape so we have to scale it down protected double getScale() { return 0.05; } // handle event from the GUI components we created public void actionPerformed(ActionEvent event) { if (event.getActionCommand().equals("EYE_LINEAR") != false) m_TexGen.setGenMode(TexCoordGeneration.EYE_LINEAR); else if (event.getActionCommand().equals("OBJECT_LINEAR") != false) m_TexGen.setGenMode(TexCoordGeneration.OBJECT_LINEAR); else if (event.getActionCommand().equals("SPHERE_MAP") != false) m_TexGen.setGenMode(TexCoordGeneration.SPHERE_MAP); else if (event.getActionCommand().equals("Rotate") != false) m_RotationInterpolator.setEnable(!m_RotationInterpolator .getEnable()); else if (event.getActionCommand().equals("Translate") != false) m_PositionInterpolator.setEnable(!m_PositionInterpolator .getEnable()); // apply any changes to the TexCoordGeneration and copy into the // appearance TexCoordGeneration texCoordGeneration = new TexCoordGeneration(); texCoordGeneration = (TexCoordGeneration) m_TexGen .cloneNodeComponent(true); m_Appearance.setTexCoordGeneration(texCoordGeneration); } // position the viewer in the scene public TransformGroup[] getViewTransformGroupArray() { TransformGroup[] tgArray = new TransformGroup[1]; tgArray[0] = new TransformGroup(); // move the camera BACK a little... // note that we have to invert the matrix as // we are moving the viewer Transform3D t3d = new Transform3D(); t3d.rotX(0.4); t3d.setScale(getScale()); t3d.setTranslation(new Vector3d(0.0, 0, -20.0)); t3d.invert(); tgArray[0].setTransform(t3d); return tgArray; } // we create a scene composed of two nested interpolators // one for translation, one for rotation that control // a DEM landscape created from a single Shape3D containing // a QuadArray. protected BranchGroup createSceneBranchGroup() { BranchGroup objRoot = super.createSceneBranchGroup(); TransformGroup objPosition = new TransformGroup(); objPosition.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); TransformGroup objRotate = new TransformGroup(); objRotate.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); Transform3D axisTranslate = new Transform3D(); axisTranslate.rotZ(Math.toRadians(90)); Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 6000, 0, 0, 0, 0, 0); m_PositionInterpolator = new PositionInterpolator(rotationAlpha, objPosition, axisTranslate, 0, 70); m_PositionInterpolator.setSchedulingBounds(createApplicationBounds()); objPosition.addChild(m_PositionInterpolator); m_PositionInterpolator.setEnable(false); m_RotationInterpolator = new RotationInterpolator(rotationAlpha, objRotate, new Transform3D(), 0.0f, (float) Math.PI * 2.0f); m_RotationInterpolator.setSchedulingBounds(getApplicationBounds()); objRotate.addChild(m_RotationInterpolator); m_RotationInterpolator.setEnable(true); TransformGroup tgLand = new TransformGroup(); Transform3D t3dLand = new Transform3D(); t3dLand.setTranslation(new Vector3d(0, -30, 0)); tgLand.setTransform(t3dLand); tgLand.addChild(createDemLandscape()); objRotate.addChild(tgLand); objPosition.addChild(objRotate); objRoot.addChild(objPosition); // create some lights for the scene Color3f lColor1 = new Color3f(0.3f, 0.3f, 0.3f); Vector3f lDir1 = new Vector3f(-1.0f, -1.0f, -1.0f); Color3f alColor = new Color3f(0.1f, 0.1f, 0.1f); AmbientLight aLgt = new AmbientLight(alColor); aLgt.setInfluencingBounds(getApplicationBounds()); DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1); lgt1.setInfluencingBounds(getApplicationBounds()); // add the lights to the parent BranchGroup objRoot.addChild(aLgt); objRoot.addChild(lgt1); return objRoot; } // create the Appearance for the landscape Shape3D // and initialize all the texture generation paramters // the yMaxHeight paramters is the maximum height of the // landscape. It defines a scale factor to convert from // landscape Y coordinates to texture coordinates // (in the range 0 to 1). void createAppearance(double yMaxHeight) { // create an Appearance and assign a Material m_Appearance = new Appearance(); m_Appearance.setCapability(Appearance.ALLOW_TEXGEN_WRITE); Color3f black = new Color3f(0, 0.2f, 0); Color3f objColor = new Color3f(0.1f, 0.7f, 0.2f); m_Appearance.setMaterial(new Material(objColor, black, objColor, black, 0.8f)); // load the texture image TextureLoader texLoader = new TextureLoader("stripes.gif", Texture.RGB, this); // clamp the coordinates in the S dimension, that way they // will not wrap when the calculated texture coordinate falls outside // the range 0 to 1. When the texture coordinate is outside this range // no texture coordinate will be used Texture tex = texLoader.getTexture(); tex.setBoundaryModeS(Texture.CLAMP); // assign the texute image to the appearance m_Appearance.setTexture(tex); // create the TexCoordGeneration object. // we are only using 1D texture coordinates (S) - texture coordinates // are calculated from a vertex"s distance from the Y = 0 plane // the 4th parameter to the Vector4f is the distance in *texture // coordinates* // that we shift the texture coordinates by (i.e. Y = 0 corresponds to S // = 0.5) TexCoordGeneration texGen = new TexCoordGeneration( TexCoordGeneration.OBJECT_LINEAR, TexCoordGeneration.TEXTURE_COORDINATE_2, new Vector4f(0, (float) (1.0 / (2 * yMaxHeight)), 0, 0.5f), new Vector4f(0, 0, 0, 0), new Vector4f(0, 0, 0, 0)); // create our "non-live" TexCoordGeneration object so we // can update the "genMode" parameter after we go live. m_TexGen = (TexCoordGeneration) texGen.cloneNodeComponent(true); // assign the TexCoordGeneration to the Appearance m_Appearance.setTexCoordGeneration(texGen); // we just glue the texture image to the surface, we are not // blending or modulating the texture image based on material // attributes. You can experiment with MODULATE or BLEND. TextureAttributes texAttribs = new TextureAttributes(); texAttribs.setTextureMode(TextureAttributes.DECAL); m_Appearance.setTextureAttributes(texAttribs); } // create the Shape3D and geometry for the DEM landscape BranchGroup createDemLandscape() { final double LAND_WIDTH = 200; final double LAND_LENGTH = 200; final double nTileSize = 10; final double yMaxHeight = LAND_WIDTH / 8; // calculate how many vertices we need to store all the "tiles" that // compose the QuadArray. final int nNumTiles = (int) (((LAND_LENGTH / nTileSize) * 2) * ((LAND_WIDTH / nTileSize) * 2)); final int nVertexCount = 4 * nNumTiles; Point3f[] coordArray = new Point3f[nVertexCount]; Point2f[] texCoordArray = new Point2f[nVertexCount]; // create the Appearance for the landscape and initialize all // the texture coordinate generation parameters createAppearance(yMaxHeight); // create the parent BranchGroup BranchGroup bg = new BranchGroup(); // create the geometry and populate a QuadArray // we use a simple sin/cos function to create an undulating surface // in the X/Z dimensions. Y dimension is the distance "above sea-level". int nItem = 0; double yValue0 = 0; double yValue1 = 0; double yValue2 = 0; double yValue3 = 0; final double xFactor = LAND_WIDTH / 5; final double zFactor = LAND_LENGTH / 3; // loop over all the tiles in the environment for (double x = -LAND_WIDTH; x <= LAND_WIDTH; x += nTileSize) { for (double z = -LAND_LENGTH; z <= LAND_LENGTH; z += nTileSize) { // if we are not on the last row or column create a "tile" // and add to the QuadArray. Use CCW winding if (z < LAND_LENGTH && x < LAND_WIDTH) { yValue0 = yMaxHeight * Math.sin(x / xFactor) * Math.cos(z / zFactor); yValue1 = yMaxHeight * Math.sin(x / xFactor) * Math.cos((z + nTileSize) / zFactor); yValue2 = yMaxHeight * Math.sin((x + nTileSize) / xFactor) * Math.cos((z + nTileSize) / zFactor); yValue3 = yMaxHeight * Math.sin((x + nTileSize) / xFactor) * Math.cos(z / zFactor); // note, we do not assign any texture coordinates! coordArray[nItem++] = new Point3f((float) x, (float) yValue0, (float) z); coordArray[nItem++] = new Point3f((float) x, (float) yValue1, (float) (z + nTileSize)); coordArray[nItem++] = new Point3f((float) (x + nTileSize), (float) yValue2, (float) (z + nTileSize)); coordArray[nItem++] = new Point3f((float) (x + nTileSize), (float) yValue3, (float) z); } } } // create a GeometryInfo and assign the coordinates GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY); gi.setCoordinates(coordArray); // generate Normal vectors for the QuadArray that was populated. NormalGenerator normalGenerator = new NormalGenerator(); normalGenerator.generateNormals(gi); // wrap the GeometryArray in a Shape3D Shape3D shape = new Shape3D(gi.getGeometryArray(), m_Appearance); // add the Shape3D to a BranchGroup and return bg.addChild(shape); return bg; } public static void main(String[] args) { TexCoordTest texCoordTest = new TexCoordTest(); texCoordTest.saveCommandLineArguments(args); new MainFrame(texCoordTest, m_kWidth, m_kHeight); }
} /*******************************************************************************
* Copyright (C) 2001 Daniel Selman * * First distributed with the book "Java 3D Programming" by Daniel Selman and * published by Manning Publications. http://manning.ru/selman * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html * * Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite * 330, Boston, MA 02111-1307, USA. * * Authors can be contacted at: Daniel Selman: daniel@selman.org * * If you make changes you think others would like, please contact one of the * authors or someone at the www.j3d.org web site. ******************************************************************************/
//***************************************************************************** /**
* Java3dApplet * * Base class for defining a Java 3D applet. Contains some useful methods for * defining views and scenegraphs etc. * * @author Daniel Selman * @version 1.0 */
//***************************************************************************** abstract class Java3dApplet extends Applet {
public static int m_kWidth = 300; public static int m_kHeight = 300; protected String[] m_szCommandLineArray = null; protected VirtualUniverse m_Universe = null; protected BranchGroup m_SceneBranchGroup = null; protected Bounds m_ApplicationBounds = null; // protected com.tornadolabs.j3dtree.Java3dTree m_Java3dTree = null; public Java3dApplet() { } public boolean isApplet() { try { System.getProperty("user.dir"); System.out.println("Running as Application."); return false; } catch (Exception e) { } System.out.println("Running as Applet."); return true; } public URL getWorkingDirectory() throws java.net.MalformedURLException { URL url = null; try { File file = new File(System.getProperty("user.dir")); System.out.println("Running as Application:"); System.out.println(" " + file.toURL()); return file.toURL(); } catch (Exception e) { } System.out.println("Running as Applet:"); System.out.println(" " + getCodeBase()); return getCodeBase(); } public VirtualUniverse getVirtualUniverse() { return m_Universe; } //public com.tornadolabs.j3dtree.Java3dTree getJ3dTree() { //return m_Java3dTree; // } public Locale getFirstLocale() { java.util.Enumeration e = m_Universe.getAllLocales(); if (e.hasMoreElements() != false) return (Locale) e.nextElement(); return null; } protected Bounds getApplicationBounds() { if (m_ApplicationBounds == null) m_ApplicationBounds = createApplicationBounds(); return m_ApplicationBounds; } protected Bounds createApplicationBounds() { m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); return m_ApplicationBounds; } protected Background createBackground() { Background back = new Background(new Color3f(0.9f, 0.9f, 0.9f)); back.setApplicationBounds(createApplicationBounds()); return back; } public void initJava3d() { // m_Java3dTree = new com.tornadolabs.j3dtree.Java3dTree(); m_Universe = createVirtualUniverse(); Locale locale = createLocale(m_Universe); BranchGroup sceneBranchGroup = createSceneBranchGroup(); ViewPlatform vp = createViewPlatform(); BranchGroup viewBranchGroup = createViewBranchGroup( getViewTransformGroupArray(), vp); createView(vp); Background background = createBackground(); if (background != null) sceneBranchGroup.addChild(background); // m_Java3dTree.recursiveApplyCapability(sceneBranchGroup); // m_Java3dTree.recursiveApplyCapability(viewBranchGroup); locale.addBranchGraph(sceneBranchGroup); addViewBranchGroup(locale, viewBranchGroup); onDoneInit(); } protected void onDoneInit() { // m_Java3dTree.updateNodes(m_Universe); } protected double getScale() { return 1.0; } public TransformGroup[] getViewTransformGroupArray() { TransformGroup[] tgArray = new TransformGroup[1]; tgArray[0] = new TransformGroup(); // move the camera BACK a little... // note that we have to invert the matrix as // we are moving the viewer Transform3D t3d = new Transform3D(); t3d.setScale(getScale()); t3d.setTranslation(new Vector3d(0.0, 0.0, -20.0)); t3d.invert(); tgArray[0].setTransform(t3d); return tgArray; } protected void addViewBranchGroup(Locale locale, BranchGroup bg) { locale.addBranchGraph(bg); } protected Locale createLocale(VirtualUniverse u) { return new Locale(u); } protected BranchGroup createSceneBranchGroup() { m_SceneBranchGroup = new BranchGroup(); return m_SceneBranchGroup; } protected View createView(ViewPlatform vp) { View view = new View(); PhysicalBody pb = createPhysicalBody(); PhysicalEnvironment pe = createPhysicalEnvironment(); AudioDevice audioDevice = createAudioDevice(pe); if (audioDevice != null) { pe.setAudioDevice(audioDevice); audioDevice.initialize(); } view.setPhysicalEnvironment(pe); view.setPhysicalBody(pb); if (vp != null) view.attachViewPlatform(vp); view.setBackClipDistance(getBackClipDistance()); view.setFrontClipDistance(getFrontClipDistance()); Canvas3D c3d = createCanvas3D(); view.addCanvas3D(c3d); addCanvas3D(c3d); return view; } protected PhysicalBody createPhysicalBody() { return new PhysicalBody(); } protected AudioDevice createAudioDevice(PhysicalEnvironment pe) { JavaSoundMixer javaSoundMixer = new JavaSoundMixer(pe); if (javaSoundMixer == null) System.out.println("create of audiodevice failed"); return javaSoundMixer; } protected PhysicalEnvironment createPhysicalEnvironment() { return new PhysicalEnvironment(); } protected float getViewPlatformActivationRadius() { return 100; } protected ViewPlatform createViewPlatform() { ViewPlatform vp = new ViewPlatform(); vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW); vp.setActivationRadius(getViewPlatformActivationRadius()); return vp; } protected Canvas3D createCanvas3D() { GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D(); gc3D.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED); GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment() .getScreenDevices(); Canvas3D c3d = new Canvas3D(gd[0].getBestConfiguration(gc3D)); c3d.setSize(getCanvas3dWidth(c3d), getCanvas3dHeight(c3d)); return c3d; } protected int getCanvas3dWidth(Canvas3D c3d) { return m_kWidth; } protected int getCanvas3dHeight(Canvas3D c3d) { return m_kHeight; } protected double getBackClipDistance() { return 100.0; } protected double getFrontClipDistance() { return 1.0; } protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray, ViewPlatform vp) { BranchGroup vpBranchGroup = new BranchGroup(); if (tgArray != null && tgArray.length > 0) { Group parentGroup = vpBranchGroup; TransformGroup curTg = null; for (int n = 0; n < tgArray.length; n++) { curTg = tgArray[n]; parentGroup.addChild(curTg); parentGroup = curTg; } tgArray[tgArray.length - 1].addChild(vp); } else vpBranchGroup.addChild(vp); return vpBranchGroup; } protected void addCanvas3D(Canvas3D c3d) { setLayout(new BorderLayout()); add(c3d, BorderLayout.CENTER); doLayout(); } protected VirtualUniverse createVirtualUniverse() { return new VirtualUniverse(); } protected void saveCommandLineArguments(String[] szArgs) { m_szCommandLineArray = szArgs; } protected String[] getCommandLineArguments() { return m_szCommandLineArray; }
}
</source>
Illustrates how a texture image can be dynamically rotated at runtime
<source lang="java">
/**********************************************************
Copyright (C) 2001 Daniel Selman First distributed with the book "Java 3D Programming" by Daniel Selman and published by Manning Publications. http://manning.ru/selman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Authors can be contacted at: Daniel Selman: daniel@selman.org If you make changes you think others would like, please contact one of the authors or someone at the www.j3d.org web site. **************************************************************/
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.io.File; import java.net.URL; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.Locale; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.mouse.MouseBehaviorCallback; import com.sun.j3d.utils.behaviors.mouse.MouseRotate; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.image.TextureLoader; /**
* This example illustrates how a texture image can be dynamically rotated at * runtime. A MouseRotate behavior (use the left mouse button) controls the * rotation of the applied texture image */
public class TextureTransformTest extends Java3dApplet implements
MouseBehaviorCallback { private static int m_kWidth = 400; private static int m_kHeight = 400; TextureAttributes m_TextureAttributes = null; public TextureTransformTest() { initJava3d(); } protected double getScale() { return 0.08; } // create a Box with an applied Texture image and // a RotationInterpolator to rotate the box protected BranchGroup createSceneBranchGroup() { BranchGroup objRoot = super.createSceneBranchGroup(); TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); Transform3D yAxis = new Transform3D(); Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0); // create the rotation interpolator to rotate the scene RotationInterpolator rotator = new RotationInterpolator(rotationAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f); rotator.setSchedulingBounds(createApplicationBounds()); objTrans.addChild(rotator); // create the box final int nScale = 50; Appearance app = new Appearance(); Box box = new Box(nScale, nScale, nScale, Primitive.GENERATE_NORMALS | Primitive.GENERATE_TEXTURE_COORDS, app); // load the texture image TextureLoader texLoader = new TextureLoader("texture.gif", this); app.setTexture(texLoader.getTexture()); // set the texture attributes and ensure we // can write to the Transform for the texture attributes m_TextureAttributes = new TextureAttributes(); m_TextureAttributes .setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE); app.setTextureAttributes(m_TextureAttributes); // connect all the elements objTrans.addChild(box); objRoot.addChild(objTrans); objRoot.addChild(createRotator()); return objRoot; } private TransformGroup createRotator() { // create a ColorCube to illustrate the current rotation TransformGroup transTg = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setTranslation(new Vector3d(-70, -70, 50)); transTg.setTransform(t3d); TransformGroup subTg = new TransformGroup(); subTg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); subTg.addChild(new ColorCube(10.0)); // attach a MouseRotate behavior so we can // rotate the color cube with the left mouse button MouseRotate mouseRot = new MouseRotate(subTg); subTg.addChild(mouseRot); // assign a transformChanged callback as we want to be // notified whenever the rotation of the ColorCube // changed ("this" implements MouseBehaviorCallback ); mouseRot.setupCallback(this); mouseRot.setSchedulingBounds(getApplicationBounds()); transTg.addChild(subTg); return transTg; } // this is a callback method that the MouseRotate behavior // calls when its Transform3D has been modified (by the user) public void transformChanged(int type, Transform3D transform) { // update the rotation of the TextureAttributes m_TextureAttributes.setTextureTransform(transform); } public static void main(String[] args) { TextureTransformTest textureTest = new TextureTransformTest(); textureTest.saveCommandLineArguments(args); new MainFrame(textureTest, m_kWidth, m_kHeight); }
} /*******************************************************************************
* Copyright (C) 2001 Daniel Selman * * First distributed with the book "Java 3D Programming" by Daniel Selman and * published by Manning Publications. http://manning.ru/selman * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html * * Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite * 330, Boston, MA 02111-1307, USA. * * Authors can be contacted at: Daniel Selman: daniel@selman.org * * If you make changes you think others would like, please contact one of the * authors or someone at the www.j3d.org web site. ******************************************************************************/
//***************************************************************************** /**
* Java3dApplet * * Base class for defining a Java 3D applet. Contains some useful methods for * defining views and scenegraphs etc. * * @author Daniel Selman * @version 1.0 */
//***************************************************************************** abstract class Java3dApplet extends Applet {
public static int m_kWidth = 300; public static int m_kHeight = 300; protected String[] m_szCommandLineArray = null; protected VirtualUniverse m_Universe = null; protected BranchGroup m_SceneBranchGroup = null; protected Bounds m_ApplicationBounds = null; // protected com.tornadolabs.j3dtree.Java3dTree m_Java3dTree = null; public Java3dApplet() { } public boolean isApplet() { try { System.getProperty("user.dir"); System.out.println("Running as Application."); return false; } catch (Exception e) { } System.out.println("Running as Applet."); return true; } public URL getWorkingDirectory() throws java.net.MalformedURLException { URL url = null; try { File file = new File(System.getProperty("user.dir")); System.out.println("Running as Application:"); System.out.println(" " + file.toURL()); return file.toURL(); } catch (Exception e) { } System.out.println("Running as Applet:"); System.out.println(" " + getCodeBase()); return getCodeBase(); } public VirtualUniverse getVirtualUniverse() { return m_Universe; } //public com.tornadolabs.j3dtree.Java3dTree getJ3dTree() { //return m_Java3dTree; // } public Locale getFirstLocale() { java.util.Enumeration e = m_Universe.getAllLocales(); if (e.hasMoreElements() != false) return (Locale) e.nextElement(); return null; } protected Bounds getApplicationBounds() { if (m_ApplicationBounds == null) m_ApplicationBounds = createApplicationBounds(); return m_ApplicationBounds; } protected Bounds createApplicationBounds() { m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); return m_ApplicationBounds; } protected Background createBackground() { Background back = new Background(new Color3f(0.9f, 0.9f, 0.9f)); back.setApplicationBounds(createApplicationBounds()); return back; } public void initJava3d() { // m_Java3dTree = new com.tornadolabs.j3dtree.Java3dTree(); m_Universe = createVirtualUniverse(); Locale locale = createLocale(m_Universe); BranchGroup sceneBranchGroup = createSceneBranchGroup(); ViewPlatform vp = createViewPlatform(); BranchGroup viewBranchGroup = createViewBranchGroup( getViewTransformGroupArray(), vp); createView(vp); Background background = createBackground(); if (background != null) sceneBranchGroup.addChild(background); // m_Java3dTree.recursiveApplyCapability(sceneBranchGroup); // m_Java3dTree.recursiveApplyCapability(viewBranchGroup); locale.addBranchGraph(sceneBranchGroup); addViewBranchGroup(locale, viewBranchGroup); onDoneInit(); } protected void onDoneInit() { // m_Java3dTree.updateNodes(m_Universe); } protected double getScale() { return 1.0; } public TransformGroup[] getViewTransformGroupArray() { TransformGroup[] tgArray = new TransformGroup[1]; tgArray[0] = new TransformGroup(); // move the camera BACK a little... // note that we have to invert the matrix as // we are moving the viewer Transform3D t3d = new Transform3D(); t3d.setScale(getScale()); t3d.setTranslation(new Vector3d(0.0, 0.0, -20.0)); t3d.invert(); tgArray[0].setTransform(t3d); return tgArray; } protected void addViewBranchGroup(Locale locale, BranchGroup bg) { locale.addBranchGraph(bg); } protected Locale createLocale(VirtualUniverse u) { return new Locale(u); } protected BranchGroup createSceneBranchGroup() { m_SceneBranchGroup = new BranchGroup(); return m_SceneBranchGroup; } protected View createView(ViewPlatform vp) { View view = new View(); PhysicalBody pb = createPhysicalBody(); PhysicalEnvironment pe = createPhysicalEnvironment(); AudioDevice audioDevice = createAudioDevice(pe); if (audioDevice != null) { pe.setAudioDevice(audioDevice); audioDevice.initialize(); } view.setPhysicalEnvironment(pe); view.setPhysicalBody(pb); if (vp != null) view.attachViewPlatform(vp); view.setBackClipDistance(getBackClipDistance()); view.setFrontClipDistance(getFrontClipDistance()); Canvas3D c3d = createCanvas3D(); view.addCanvas3D(c3d); addCanvas3D(c3d); return view; } protected PhysicalBody createPhysicalBody() { return new PhysicalBody(); } protected AudioDevice createAudioDevice(PhysicalEnvironment pe) { JavaSoundMixer javaSoundMixer = new JavaSoundMixer(pe); if (javaSoundMixer == null) System.out.println("create of audiodevice failed"); return javaSoundMixer; } protected PhysicalEnvironment createPhysicalEnvironment() { return new PhysicalEnvironment(); } protected float getViewPlatformActivationRadius() { return 100; } protected ViewPlatform createViewPlatform() { ViewPlatform vp = new ViewPlatform(); vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW); vp.setActivationRadius(getViewPlatformActivationRadius()); return vp; } protected Canvas3D createCanvas3D() { GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D(); gc3D.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED); GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment() .getScreenDevices(); Canvas3D c3d = new Canvas3D(gd[0].getBestConfiguration(gc3D)); c3d.setSize(getCanvas3dWidth(c3d), getCanvas3dHeight(c3d)); return c3d; } protected int getCanvas3dWidth(Canvas3D c3d) { return m_kWidth; } protected int getCanvas3dHeight(Canvas3D c3d) { return m_kHeight; } protected double getBackClipDistance() { return 100.0; } protected double getFrontClipDistance() { return 1.0; } protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray, ViewPlatform vp) { BranchGroup vpBranchGroup = new BranchGroup(); if (tgArray != null && tgArray.length > 0) { Group parentGroup = vpBranchGroup; TransformGroup curTg = null; for (int n = 0; n < tgArray.length; n++) { curTg = tgArray[n]; parentGroup.addChild(curTg); parentGroup = curTg; } tgArray[tgArray.length - 1].addChild(vp); } else vpBranchGroup.addChild(vp); return vpBranchGroup; } protected void addCanvas3D(Canvas3D c3d) { setLayout(new BorderLayout()); add(c3d, BorderLayout.CENTER); doLayout(); } protected VirtualUniverse createVirtualUniverse() { return new VirtualUniverse(); } protected void saveCommandLineArguments(String[] szArgs) { m_szCommandLineArray = szArgs; } protected String[] getCommandLineArguments() { return m_szCommandLineArray; }
}
</source>
Image Component By Reference
<source lang="java">
/*
* @(#)ImageComponentByReferenceTest.java 1.14 02/10/21 13:41:45 * * Copyright (c) 1996-2002 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: * - Redistributions 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY * OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
import java.awt.Container; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ruponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Vector; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.swing.BoxLayout; import javax.swing.JApplet; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; public class ImageComponentByReferenceTest extends JApplet implements
ActionListener { Shape3D s1, s2; TextureLoader t0, t1, t2; int count = 0; Appearance app = new Appearance(); BranchGroup objRoot = new BranchGroup(); TransformGroup objTrans = new TransformGroup(); BufferedImage bImage1; TiledImage checkBoard; boolean yUp = false; boolean byRef = true; JComboBox rasterType, texType; ImageComponent2D[] image = new ImageComponent2D[8]; Appearance dummyApp = new Appearance(); Texture2D texOne, texCheckBoard; javax.media.j3d.Raster raster; Box textureCube; Shape3D boxShape; int w1 = 64, h1 = 32, checkw = 16, checkh = 16; private java.net.URL texImage = null; private SimpleUniverse u = null; public BranchGroup createSceneGraph() { objRoot = new BranchGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(Group.ALLOW_CHILDREN_WRITE); objRoot.addChild(objTrans); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); app.setCapability(Appearance.ALLOW_TEXTURE_WRITE); textureCube = new Box(0.4f, 0.4f, 0.4f, Box.GENERATE_TEXTURE_COORDS | Box.GENERATE_NORMALS, app); boxShape = textureCube.getShape(Box.FRONT); boxShape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); objTrans.addChild(textureCube); checkBoard = new TiledImage(); TextureLoader texLoader = new TextureLoader(texImage, this); ImageComponent2D oneImage = texLoader.getImage(); bImage1 = oneImage.getImage(); int index = 0; image[index++] = new ImageComponent2D(oneImage.getFormat(), (RenderedImage) bImage1, false, true); image[index++] = new ImageComponent2D(oneImage.getFormat(), (RenderedImage) bImage1, true, true); image[index++] = new ImageComponent2D(oneImage.getFormat(), (RenderedImage) bImage1, false, false); image[index++] = new ImageComponent2D(oneImage.getFormat(), (RenderedImage) bImage1, true, false); createRaster(objRoot); image[index++] = new ImageComponent2D(ImageComponent.FORMAT_RGBA, checkBoard, false, true); image[index++] = new ImageComponent2D(ImageComponent.FORMAT_RGBA, checkBoard, true, true); image[index++] = new ImageComponent2D(ImageComponent.FORMAT_RGBA, checkBoard, false, false); image[index++] = new ImageComponent2D(ImageComponent.FORMAT_RGBA, checkBoard, true, false); texOne = new Texture2D(Texture.BASE_LEVEL, Texture.RGBA, image[2] .getWidth(), image[2].getHeight()); texOne.setCapability(Texture.ALLOW_IMAGE_WRITE); texOne.setImage(0, image[2]); app.setTexture(texOne); texCheckBoard = new Texture2D(Texture.BASE_LEVEL, Texture.RGBA, image[4].getWidth(), image[4].getHeight()); texCheckBoard.setCapability(Texture.ALLOW_IMAGE_WRITE); objRoot.rupile(); return objRoot; } public void actionPerformed(ActionEvent e) { Object target = e.getSource(); if (target == rasterType) { if (rasterType.getSelectedIndex() < 4) { raster.setSize(w1, h1); } else { raster.setSize(checkw, checkh); } raster.setImage(image[rasterType.getSelectedIndex()]); } else if (target == texType) { boxShape.setAppearance(dummyApp); if (texType.getSelectedIndex() < 4) { texOne.setImage(0, image[texType.getSelectedIndex()]); app.setTexture(texOne); } else { texCheckBoard.setImage(0, image[texType.getSelectedIndex()]); app.setTexture(texCheckBoard); } boxShape.setAppearance(app); } } JPanel createImagePanel() { JPanel panel = new JPanel(); String texVals[] = { "One_Yup_ByCopy", "One_Yup_ByReference", "One_Ydown_ByCopy", "One_Ydown_ByReference", "Checkered_Yup_ByCopy", "Checkered_Yup_ByReference", "Checkered_Ydown_ByCopy", "Checkered_Ydown_ByReference" }; rasterType = new JComboBox(texVals); rasterType.setLightWeightPopupEnabled(false); rasterType.addActionListener(this); rasterType.setSelectedIndex(2); panel.add(new JLabel("Raster Image")); panel.add(rasterType); texType = new JComboBox(texVals); texType.setLightWeightPopupEnabled(false); texType.addActionListener(this); texType.setSelectedIndex(2); panel.add(new JLabel("Texture Image")); panel.add(texType); return panel; } public ImageComponentByReferenceTest() { } public ImageComponentByReferenceTest(java.net.URL url) { texImage = url; } public void init() { if (texImage == null) { // the path to the image for an applet try { texImage = new java.net.URL(getCodeBase().toString() + "/one.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } Canvas3D c = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); Container contentPane = getContentPane(); JPanel p = new JPanel(); BoxLayout boxlayout = new BoxLayout(p, BoxLayout.Y_AXIS); p.setLayout(boxlayout); contentPane.add("Center", c); contentPane.add("South", p); p.add(createImagePanel()); } public void destroy() { u.cleanup(); } public static void main(String[] args) { java.net.URL url = null; // the path to the image file for an application try { url = new java.net.URL("file:one.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } new MainFrame(new ImageComponentByReferenceTest(url), 800, 700); } void createRaster(BranchGroup scene) { // Create raster geometries and shapes Vector3f trans = new Vector3f(); Transform3D tr = new Transform3D(); TransformGroup tg; // Left raster = new javax.media.j3d.Raster(); raster.setCapability(javax.media.j3d.Raster.ALLOW_IMAGE_WRITE); raster.setCapability(javax.media.j3d.Raster.ALLOW_SIZE_WRITE); raster.setPosition(new Point3f(-0.9f, 0.75f, 0.0f)); raster.setType(javax.media.j3d.Raster.RASTER_COLOR); raster.setOffset(0, 0); raster.setSize(image[2].getWidth(), image[2].getHeight()); raster.setImage(image[2]); Shape3D sh = new Shape3D(raster, new Appearance()); scene.addChild(sh); }
} class TiledImage extends Object implements RenderedImage {
WritableRaster[][] tile = new WritableRaster[3][3]; WritableRaster bigTile; ComponentColorModel colorModel; BufferedImage checkBoard; int minX = -2; int minY = -1; TiledImage() { ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] nBits = { 8, 8, 8, 8 }; int i, j, k, cc = 255; int[] bandOffset = new int[4]; colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.OPAQUE, 0); // Create 9 tiles bandOffset[0] = 3; bandOffset[1] = 2; bandOffset[2] = 1; bandOffset[3] = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { tile[i][j] = Raster.createInterleavedRaster( DataBuffer.TYPE_BYTE, 8, 8, 32, 4, bandOffset, null); } } // tile {-2, -1} byte[] byteData = ((DataBufferByte) tile[0][0].getDataBuffer()) .getData(); for (i = 4, k = 8 * 4 * 4 + 4 * 4; i < 8; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) cc; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } // tile {-1, -1} byteData = ((DataBufferByte) tile[1][0].getDataBuffer()).getData(); for (i = 4, k = 8 * 4 * 4; i < 8; i++) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) cc; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } // tile {1, -1} byteData = ((DataBufferByte) tile[2][0].getDataBuffer()).getData(); for (i = 4, k = 8 * 4 * 4; i < 8; i++, k += 16) { for (j = 0; j < 4; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } // tile {-2, 0} byteData = ((DataBufferByte) tile[0][1].getDataBuffer()).getData(); for (i = 0, k = 16; i < 4; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) cc; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } for (i = 4, k = 8 * 4 * 4 + 16; i < 8; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) 0; } } // tile {-1, 0} byteData = ((DataBufferByte) tile[1][1].getDataBuffer()).getData(); for (i = 0, k = 0; i < 4; i++) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) cc; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } for (i = 0, k = 8 * 4 * 4; i < 4; i++) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) 0; } for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) cc; } } // tile {0, 0} byteData = ((DataBufferByte) tile[2][1].getDataBuffer()).getData(); for (i = 0, k = 0; i < 4; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } for (i = 4, k = 8 * 4 * 4; i < 8; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) cc; } } // tile {-2, 1} byteData = ((DataBufferByte) tile[0][2].getDataBuffer()).getData(); for (i = 4, k = 16; i < 8; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) 0; } } // tile {-1, 1} byteData = ((DataBufferByte) tile[1][2].getDataBuffer()).getData(); for (i = 0, k = 0; i < 8; i++) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) 0; } for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) cc; } } // tile {0, 1} byteData = ((DataBufferByte) tile[2][2].getDataBuffer()).getData(); for (i = 4, k = 0; i < 8; i++, k += 16) { for (j = 4; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) cc; } } bigTile = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 16, 16, 64, 4, bandOffset, null); ; byteData = ((DataBufferByte) bigTile.getDataBuffer()).getData(); for (i = 0, k = 0; i < 8; i++) { for (j = 0; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) cc; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } for (; j < 16; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) 0; byteData[k + 3] = (byte) cc; } } for (; i < 16; i++) { for (j = 0; j < 8; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) 0; } for (; j < 16; j++, k += 4) { byteData[k] = (byte) 0; byteData[k + 1] = (byte) 0; byteData[k + 2] = (byte) cc; byteData[k + 3] = (byte) cc; } } checkBoard = new BufferedImage(colorModel, bigTile, false, null); } // create four tiles {r, g, b, y} public WritableRaster copyData(WritableRaster raster) { return checkBoard.copyData(raster); } public ColorModel getColorModel() { return checkBoard.getColorModel(); } public Raster getData() { return checkBoard.getData(); } public Raster getData(Rectangle rect) { return checkBoard.getData(rect); } public int getHeight() { return 16; } public int getMinTileX() { return minX; } public int getMinTileY() { return minY; } public int getMinX() { return -8; } public int getMinY() { return -8; } public int getNumXTiles() { return 3; } public int getNumYTiles() { return 3; } public Object getProperty(String name) { return checkBoard.getProperty(name); } public String[] getPropertyNames() { return checkBoard.getPropertyNames(); } public SampleModel getSampleModel() { return checkBoard.getSampleModel(); } public Vector getSources() { return null; } public Raster getTile(int tileX, int tileY) { return tile[tileX - minX][tileY - minY]; } public int getTileGridXOffset() { return 4; } public int getTileGridYOffset() { return -4; } public int getTileHeight() { return 8; } public int getTileWidth() { return 8; } public int getWidth() { return 16; }
}
</source>
Interleaved Test
<source lang="java">
/*
* @(#)InterleavedTest.java 1.13 02/10/21 13:41:02 * * Copyright (c) 1996-2002 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: * - Redistributions 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY * OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
import java.awt.Container; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.IndexedTriangleArray; import javax.media.j3d.IndexedTriangleStripArray; import javax.media.j3d.Material; import javax.media.j3d.RenderingAttributes; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.TextureUnitState; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TransparencyAttributes; import javax.media.j3d.TriangleArray; import javax.media.j3d.TriangleStripArray; import javax.swing.BoxLayout; import javax.swing.JApplet; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.TitledBorder; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class InterleavedTest extends JApplet implements ActionListener {
RenderingAttributes ra; ColoringAttributes ca; Material mat; Appearance app; JComboBox geomType; JCheckBox transparency; JCheckBox textureBox; Shape3D shape; TransparencyAttributes transp; GeometryArray tetraRegular, tetraStrip, tetraIndexed, tetraIndexedStrip; GeometryArray[] geoArrays = new GeometryArray[4]; // Globally used colors Color3f white = new Color3f(1.0f, 1.0f, 1.0f); Color3f red = new Color3f(1.0f, 0.0f, 0.0f); Color3f green = new Color3f(0.0f, 1.0f, 0.0f); Color3f blue = new Color3f(0.0f, 0.0f, 1.0f); Color3f[] colors = { white, red, green, blue }; private static final float sqrt3 = (float) Math.sqrt(3.0); private static final float sqrt3_3 = sqrt3 / 3.0f; private static final float sqrt24_3 = (float) Math.sqrt(24.0) / 3.0f; private static final float ycenter = 0.5f * sqrt24_3; private static final float zcenter = -sqrt3_3; private static final Point3f p1 = new Point3f(-1.0f, -ycenter, -zcenter); private static final Point3f p2 = new Point3f(1.0f, -ycenter, -zcenter); private static final Point3f p3 = new Point3f(0.0f, -ycenter, -sqrt3 - zcenter); private static final Point3f p4 = new Point3f(0.0f, sqrt24_3 - ycenter, 0.0f); private static final Point2f t1 = new Point2f(0.0f, 0.0f); private static final Point2f t2 = new Point2f(0.5f, 1.0f); private static final Point2f t3 = new Point2f(1.0f, 0.0f); private static final Point2f t4 = new Point2f(1.0f, 1.0f); private static final Color3f c1 = new Color3f(1.0f, 0.0f, 0.0f); private static final Color3f c2 = new Color3f(0.0f, 1.0f, 0.0f); private static final Color3f c3 = new Color3f(0.0f, 1.0f, 1.0f); private static final Color3f c4 = new Color3f(1.0f, 1.0f, 0.0f); private static final float[] interleaved = { t1.x, t1.y, t1.x, t1.y, c1.x, c1.y, c1.z, // front face p1.x, p1.y, p1.z, // front face t2.x, t2.y, t2.x, t2.y, c2.x, c2.y, c2.z, p2.x, p2.y, p2.z, t4.x, t4.y, t4.x, t4.y, c4.x, c4.y, c4.z, p4.x, p4.y, p4.z, t1.x, t1.y, t1.x, t1.y, c1.x, c1.y, c1.z,// left, back face p1.x, p1.y, p1.z,// left, back face t4.x, t4.y, t4.x, t4.y, c4.x, c4.y, c4.z, p4.x, p4.y, p4.z, t3.x, t3.y, t3.x, t3.y, c3.x, c3.y, c3.z, p3.x, p3.y, p3.z, t2.x, t2.y, t2.x, t2.y, c2.x, c2.y, c2.z,// right, back face p2.x, p2.y, p2.z,// right, back face t3.x, t3.y, t3.x, t3.y, c3.x, c3.y, c3.z, p3.x, p3.y, p3.z, t4.x, t4.y, t4.x, t4.y, c4.x, c4.y, c4.z, p4.x, p4.y, p4.z, t1.x, t1.y, t1.x, t1.y, c1.x, c1.y, c1.z,// bottom face p1.x, p1.y, p1.z,// bottom face t3.x, t3.y, t3.x, t3.y, c3.x, c3.y, c3.z, p3.x, p3.y, p3.z, t2.x, t2.y, t2.x, t2.y, c2.x, c2.y, c2.z, p2.x, p2.y, p2.z, }; private static final float[] indexedInterleaved = { t1.x, t1.y, t1.x, t1.y, c1.x, c1.y, c1.z, p1.x, p1.y, p1.z, t2.x, t2.y, t2.x, t2.y, c2.x, c2.y, c2.z, p2.x, p2.y, p2.z, t3.x, t3.y, t3.x, t3.y, c3.x, c3.y, c3.z, p3.x, p3.y, p3.z, t4.x, t4.y, t4.x, t4.y, c4.x, c4.y, c4.z, p4.x, p4.y, p4.z, }; private static final int[] indices = { 0, 1, 3, 0, 3, 2, 1, 2, 3, 0, 2, 1 }; private int[] stripVertexCounts = { 3, 3, 3, 3 }; TextureUnitState textureUnitState[] = new TextureUnitState[2]; Texture tex1; Texture tex2; private java.net.URL texImage1 = null; private java.net.URL texImage2 = null; private SimpleUniverse u; BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup(); // Set up attributes to render lines app = new Appearance(); app.setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_WRITE); transp = new TransparencyAttributes(); transp.setTransparency(0.5f); transp.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE); transp.setTransparencyMode(TransparencyAttributes.NONE); app.setTransparencyAttributes(transp); // load textures TextureAttributes texAttr1 = new TextureAttributes(); texAttr1.setTextureMode(TextureAttributes.DECAL); TextureAttributes texAttr2 = new TextureAttributes(); texAttr2.setTextureMode(TextureAttributes.MODULATE); TextureLoader tex = new TextureLoader(texImage1, new String("RGB"), this); if (tex == null) return null; tex1 = tex.getTexture(); tex = new TextureLoader(texImage2, new String("RGB"), this); if (tex == null) return null; tex2 = tex.getTexture(); textureUnitState[0] = new TextureUnitState(tex1, texAttr1, null); textureUnitState[1] = new TextureUnitState(tex2, texAttr2, null); tetraRegular = createGeometry(1); tetraStrip = createGeometry(2); tetraIndexed = createGeometry(3); tetraIndexedStrip = createGeometry(4); geoArrays[0] = tetraRegular; geoArrays[1] = tetraStrip; geoArrays[2] = tetraIndexed; geoArrays[3] = tetraIndexedStrip; shape = new Shape3D(tetraRegular, app); shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); Transform3D t = new Transform3D(); // move the object upwards t.set(new Vector3f(0.0f, 0.3f, 0.0f)); // rotate the shape Transform3D temp = new Transform3D(); temp.rotX(Math.PI / 4.0d); t.mul(temp); temp.rotY(Math.PI / 4.0d); t.mul(temp); // Shrink the object t.setScale(0.6); TransformGroup trans = new TransformGroup(t); trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); objRoot.addChild(trans); trans.addChild(shape); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); // Set up the global lights Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f); Vector3f lDir1 = new Vector3f(-1.0f, -1.0f, -1.0f); Color3f alColor = new Color3f(0.2f, 0.2f, 0.2f); AmbientLight aLgt = new AmbientLight(alColor); aLgt.setInfluencingBounds(bounds); DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1); lgt1.setInfluencingBounds(bounds); objRoot.addChild(aLgt); objRoot.addChild(lgt1); // Let Java 3D perform optimizations on this scene graph. objRoot.rupile(); return objRoot; } JPanel createGeometryByReferencePanel() { JPanel panel = new JPanel(); panel.setBorder(new TitledBorder("Geometry Type")); String values[] = { "Array", "Strip", "Indexed", "IndexedStrip" }; geomType = new JComboBox(values); geomType.setLightWeightPopupEnabled(false); geomType.addActionListener(this); geomType.setSelectedIndex(0); panel.add(new JLabel("Geometry Type")); panel.add(geomType); transparency = new JCheckBox("EnableTransparency", false); transparency.addActionListener(this); panel.add(transparency); textureBox = new JCheckBox("EnableTexture", false); textureBox.addActionListener(this); panel.add(textureBox); return panel; } public InterleavedTest() { } public InterleavedTest(java.net.URL texURL1, java.net.URL texURL2) { texImage1 = texURL1; texImage2 = texURL2; } public void init() { // create textures if (texImage1 == null) { // the path to the image for an applet try { texImage1 = new java.net.URL(getCodeBase().toString() + "/bg.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } if (texImage2 == null) { // the path to the image for an applet try { texImage2 = new java.net.URL(getCodeBase().toString() + "/one.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } Container contentPane = getContentPane(); Canvas3D c = new Canvas3D(SimpleUniverse.getPreferredConfiguration()); contentPane.add("Center", c); BranchGroup scene = createSceneGraph(); // SimpleUniverse is a Convenience Utility class u = new SimpleUniverse(c); // add mouse behaviors to the viewingPlatform ViewingPlatform viewingPlatform = u.getViewingPlatform(); // This will move the ViewPlatform back a bit so the // objects in the scene can be viewed. viewingPlatform.setNominalViewingTransform(); // add Orbit behavior to the viewing platform OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); orbit.setSchedulingBounds(bounds); viewingPlatform.setViewPlatformBehavior(orbit); u.addBranchGraph(scene); // Create GUI JPanel p = new JPanel(); BoxLayout boxlayout = new BoxLayout(p, BoxLayout.Y_AXIS); p.add(createGeometryByReferencePanel()); p.setLayout(boxlayout); contentPane.add("South", p); } public void destroy() { u.cleanup(); } public void actionPerformed(ActionEvent e) { Object target = e.getSource(); if (target == geomType) { shape.setGeometry(geoArrays[geomType.getSelectedIndex()]); } else if (target == transparency) { if (transparency.isSelected()) { transp.setTransparencyMode(TransparencyAttributes.BLENDED); } else { transp.setTransparencyMode(TransparencyAttributes.NONE); } } else if (target == textureBox) { if (textureBox.isSelected()) { app.setTextureUnitState(textureUnitState); } else { app.setTextureUnitState(null); } } } public static void main(String[] args) { java.net.URL texURL1 = null; java.net.URL texURL2 = null; // the path to the image for an application try { texURL1 = new java.net.URL("file:bg.jpg"); texURL2 = new java.net.URL("file:one.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } Frame frame = new MainFrame(new InterleavedTest(texURL1, texURL2), 800, 800); } public GeometryArray createGeometry(int type) { GeometryArray tetra = null; int texCoordSetMap[] = { 0, 0 }; if (type == 1) { tetra = new TriangleArray(12, TriangleArray.COORDINATES | TriangleArray.COLOR_3 | /* * TriangleArray.NORMAL_3| */ TriangleArray.TEXTURE_COORDINATE_2 | TriangleArray.INTERLEAVED | TriangleArray.BY_REFERENCE, 2, texCoordSetMap); tetra.setInterleavedVertices(interleaved); } else if (type == 2) { tetra = new TriangleStripArray(12, TriangleStripArray.COORDINATES | TriangleStripArray.COLOR_3 | /* * TriangleArray.NORMAL_3| */ TriangleArray.TEXTURE_COORDINATE_2 | TriangleStripArray.INTERLEAVED | TriangleStripArray.BY_REFERENCE, 2, texCoordSetMap, stripVertexCounts); tetra.setInterleavedVertices(interleaved); } else if (type == 3) { // Indexed Geometry tetra = new IndexedTriangleArray(4, IndexedTriangleArray.COORDINATES | IndexedTriangleArray.COLOR_3 | /* * IndexedTriangleArray.NORMAL_3| */ IndexedTriangleArray.TEXTURE_COORDINATE_2 | IndexedTriangleArray.INTERLEAVED | IndexedTriangleArray.BY_REFERENCE, 2, texCoordSetMap, 12); tetra.setInterleavedVertices(indexedInterleaved); ((IndexedTriangleArray) tetra).setCoordinateIndices(0, indices); ((IndexedTriangleArray) tetra).setColorIndices(0, indices); ((IndexedTriangleArray) tetra).setTextureCoordinateIndices(0, 0, indices); ((IndexedTriangleArray) tetra).setTextureCoordinateIndices(1, 0, indices); } else if (type == 4) { // Indexed strip geometry tetra = new IndexedTriangleStripArray(4, IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.COLOR_3 | /* * IndexedTriangleArray.NORMAL_3| */ IndexedTriangleArray.TEXTURE_COORDINATE_2 | IndexedTriangleStripArray.INTERLEAVED | IndexedTriangleStripArray.BY_REFERENCE, 2, texCoordSetMap, 12, stripVertexCounts); tetra.setInterleavedVertices(indexedInterleaved); ((IndexedTriangleStripArray) tetra) .setCoordinateIndices(0, indices); ((IndexedTriangleStripArray) tetra).setColorIndices(0, indices); ((IndexedTriangleStripArray) tetra).setTextureCoordinateIndices(0, 0, indices); ((IndexedTriangleStripArray) tetra).setTextureCoordinateIndices(1, 0, indices); } else if (type == 5) { // Interleaved array } return tetra; }
}
</source>
LocalEyeApp creates a single plane with texture mapping
<source lang="java">
/*
* LocalEyeApp.java 1.0 99/04/12 * * Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved. * * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, * modify and redistribute this software in source and binary code form, * provided that i) this copyright notice and license appear on all copies of * the software; and ii) Licensee does not utilize the software in a manner * which is disparaging to Sun. * * 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * This software is not designed or intended for use in on-line control of * aircraft, air traffic, aircraft navigation or aircraft communications; or in * the design, construction, operation or maintenance of any nuclear * facility. Licensee represents and warrants that it will not use or * redistribute the Software for such purposes. */
/*
* This program demonstrates the difference between Local Eye Lighting * and Infinite Eye Lighting. To see the difference, you need to * comment out the appropriate line in the code, recompile and rerun * the application. * * This version of the program is different than the one used to * generate images for the tutorial document. This this version * both a directional and point light are included in the scene. * the directional light is red, the point light is white. The spheres * are blue (diffuse) with white specular material. * * The red specular reflections are from the directional light. * The white specular reflections are from the point light. * The blue diffuse reflection is from the directional light. * * This application (or a version of it) generated one or more * of the images in Chapter 6 of Getting Started with the Java 3D API. * The Java 3D Turtorial. * * See http://www.sun.ru/desktop/java3d/collateral for more information. * */
import java.applet.Applet; import java.awt.BorderLayout; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Material; import javax.media.j3d.PointLight; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Color3f; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; /**
* LocalEyeApp creates a single plane with texture mapping. */
public class LocalEyeApp extends Applet {
TransformGroup createTG(float x, float y, float z){ Vector3f position = new Vector3f(x, y, z); Transform3D translate = new Transform3D(); translate.set(position); TransformGroup trans1 = new TransformGroup(translate); return trans1; } Appearance createMatAppear(Color3f dColor, Color3f sColor, float shine) { Appearance appear = new Appearance(); Material material = new Material(); material.setDiffuseColor(dColor); material.setSpecularColor(sColor); material.setShininess(shine); appear.setMaterial(material); return appear; }
public LocalEyeApp (){ setLayout(new BorderLayout()); Canvas3D c = new Canvas3D(null); add("Center", c); Color3f white = new Color3f(1.0f, 1.0f, 1.0f); Color3f red = new Color3f(1.0f, 0.0f, 0.0f); Color3f blue = new Color3f(0.0f, 0.0f, 1.0f); BranchGroup scene = new BranchGroup(); TransformGroup trans11 = createTG(-0.7f, 0.7f, -0.5f); scene.addChild(trans11); trans11.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans12 = createTG(0.0f, 0.7f, -0.5f); scene.addChild(trans12); trans12.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans13 = createTG(0.7f, 0.7f, -0.5f); scene.addChild(trans13); trans13.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans21 = createTG(-0.7f, 0.0f, -0.5f); scene.addChild(trans21); trans21.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans22 = createTG(0.0f, 0.0f, -0.5f); scene.addChild(trans22); trans22.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans23 = createTG(0.7f, 0.0f, -0.5f); scene.addChild(trans23); trans23.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f)));
TransformGroup trans31 = createTG(-0.7f, -0.7f, -0.5f); scene.addChild(trans31); trans31.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans32 = createTG(0.0f, -0.7f, -0.5f); scene.addChild(trans32); trans32.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); TransformGroup trans33 = createTG(0.7f, -0.7f, -0.5f); scene.addChild(trans33); trans33.addChild(new Sphere(0.3f, Sphere.GENERATE_NORMALS, 60, createMatAppear(blue, white, 1000.0f))); AmbientLight lightA = new AmbientLight(); lightA.setInfluencingBounds(new BoundingSphere()); scene.addChild(lightA); DirectionalLight lightD1 = new DirectionalLight(); lightD1.setInfluencingBounds(new BoundingSphere()); Vector3f direction = new Vector3f(0.0f, 0.0f, -1.0f); direction.normalize(); lightD1.setDirection(direction); lightD1.setColor(red); scene.addChild(lightD1); PointLight lightP1 = new PointLight(); lightP1.setInfluencingBounds(new BoundingSphere()); Point3f position = new Point3f(0.0f, 0.0f, 1.0f); lightP1.setPosition(position); scene.addChild(lightP1); Background background = new Background(); background.setApplicationBounds(new BoundingSphere()); background.setColor(white); scene.addChild(background); SimpleUniverse u = new SimpleUniverse(c); // This will move the ViewPlatform back a bit so the // objects in the scene can be viewed. u.getViewingPlatform().setNominalViewingTransform(); //Enable Local Eye Lighting u.getViewer().getView().setLocalEyeLightingEnable(true); u.addBranchGraph(scene); } public static void main(String argv[]) { System.out.println("This scene has a red directional light and white point light."); System.out.println("The spheres are blue (diffuse) with white specular material."); System.out.println("The red specular reflections are from the directional light."); System.out.println("The white specular reflections are from the point light."); System.out.println("The blue diffuse reflection is from the point light."); new MainFrame(new LocalEyeApp(), 256, 256); }
}
</source>
Multi Texture
<source lang="java">
/*
* @(#)MultiTextureTest.java 1.13 02/10/21 13:57:36 * * Copyright (c) 1996-2002 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: * - Redistributions 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY * OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Choice; import java.awt.GraphicsConfiguration; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.image.BufferedImage; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.TextureUnitState; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class MultiTextureTest extends Applet implements ItemListener {
Choice choice; TextureUnitState textureUnitState[] = new TextureUnitState[2]; Texture stoneTex; Texture skyTex; Texture lightTex; private java.net.URL stoneImage = null; private java.net.URL skyImage = null; private SimpleUniverse u = null; public Texture createLightMap() { int width = 128; int height = 128; BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); int[] rgbArray = new int[width * height]; int index, index2; int rgbInc = 256 / (width / 2 - 20); int rgbValue = 0; int k = width / 2 - 5; int i, j, rgb; rgb = 0xff; rgbValue = rgb | (rgb << 8) | (rgb << 16) | (rgb << 24); for (i = width / 2 - 1, j = 0; j < 10; j++, i--) { rgbArray[i] = rgbValue; } for (; i > 8; i--, rgb -= rgbInc) { rgbValue = rgb | (rgb << 8) | (rgb << 16) | (rgb << 24); rgbArray[i] = rgbValue; } for (; i >= 0; i--) { rgbArray[i] = rgbValue; } for (i = 0; i < width / 2; i++) { rgbValue = rgbArray[i]; index = i; index2 = (width - i - 1); for (j = 0; j < height; j++) { rgbArray[index] = rgbArray[index2] = rgbValue; index += width; index2 += width; } } bimage.setRGB(0, 0, width, height, rgbArray, 0, width); ImageComponent2D grayImage = new ImageComponent2D( ImageComponent.FORMAT_RGB, bimage); lightTex = new Texture2D(Texture.BASE_LEVEL, Texture.RGB, width, height); lightTex.setImage(0, grayImage); return lightTex; } public BranchGroup createSceneGraph() { // Create the root of the branch graph BranchGroup objRoot = new BranchGroup(); // Create a Transformgroup to scale all objects so they // appear in the scene. TransformGroup objScale = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setScale(0.4); objScale.setTransform(t3d); objRoot.addChild(objScale); TransformGroup objTrans = new TransformGroup(); //write-enable for behaviors objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); objTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING); objScale.addChild(objTrans); Appearance ap = new Appearance(); // load textures TextureAttributes texAttr1 = new TextureAttributes(); texAttr1.setTextureMode(TextureAttributes.DECAL); TextureAttributes texAttr2 = new TextureAttributes(); texAttr2.setTextureMode(TextureAttributes.MODULATE); TextureLoader tex = new TextureLoader(stoneImage, new String("RGB"), this); if (tex == null) return null; stoneTex = tex.getTexture(); tex = new TextureLoader(skyImage, new String("RGB"), this); if (tex == null) return null; skyTex = tex.getTexture(); lightTex = createLightMap(); textureUnitState[0] = new TextureUnitState(stoneTex, texAttr1, null); textureUnitState[0].setCapability(TextureUnitState.ALLOW_STATE_WRITE); textureUnitState[1] = new TextureUnitState(lightTex, texAttr2, null); textureUnitState[1].setCapability(TextureUnitState.ALLOW_STATE_WRITE); ap.setTextureUnitState(textureUnitState); //Create a Box Box BoxObj = new Box(1.5f, 1.5f, 0.8f, Box.GENERATE_NORMALS | Box.GENERATE_TEXTURE_COORDS, ap, 2); // add it to the scene graph. objTrans.addChild(BoxObj); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); //Shine it with two lights. Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f); Color3f lColor2 = new Color3f(0.2f, 0.2f, 0.1f); Vector3f lDir1 = new Vector3f(-1.0f, -1.0f, -1.0f); Vector3f lDir2 = new Vector3f(0.0f, 0.0f, -1.0f); DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1); DirectionalLight lgt2 = new DirectionalLight(lColor2, lDir2); lgt1.setInfluencingBounds(bounds); lgt2.setInfluencingBounds(bounds); objScale.addChild(lgt1); objScale.addChild(lgt2); // Let Java 3D perform optimizations on this scene graph. objRoot.rupile(); return objRoot; } public MultiTextureTest() { } public MultiTextureTest(java.net.URL stoneURL, java.net.URL skyURL) { stoneImage = stoneURL; skyImage = skyURL; } public void init() { if (stoneImage == null) { // the path to the image for an applet try { stoneImage = new java.net.URL(getCodeBase().toString() + "/stone.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } if (skyImage == null) { // the path to the image for an applet try { skyImage = new java.net.URL(getCodeBase().toString() + "/bg.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse .getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); ViewingPlatform viewingPlatform = u.getViewingPlatform(); // This will move the ViewPlatform back a bit so the // objects in the scene can be viewed. viewingPlatform.setNominalViewingTransform(); // add orbit behavior but disable translate OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL | OrbitBehavior.DISABLE_TRANSLATE); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); orbit.setSchedulingBounds(bounds); viewingPlatform.setViewPlatformBehavior(orbit); u.addBranchGraph(scene); // create the gui choice = new Choice(); choice.addItem("stone + light"); choice.addItem("stone"); choice.addItem("lightMap"); choice.addItem("sky"); choice.addItem("stone + sky"); choice.addItemListener(this); add("North", choice); } public void destroy() { // Cleanup reference to Java3D textureUnitState = new TextureUnitState[2]; u.cleanup(); } public void itemStateChanged(ItemEvent e) { int index = choice.getSelectedIndex(); switch (index) { case 0: /* stone + light */ textureUnitState[0].setTexture(stoneTex); textureUnitState[1].setTexture(lightTex); break; case 1: /* stone */ textureUnitState[0].setTexture(stoneTex); textureUnitState[1].setTexture(null); break; case 2: /* light */ textureUnitState[0].setTexture(null); textureUnitState[1].setTexture(lightTex); break; case 3: /* sky */ textureUnitState[0].setTexture(null); textureUnitState[1].setTexture(skyTex); break; case 4: /* stone + sky */ textureUnitState[0].setTexture(stoneTex); textureUnitState[1].setTexture(skyTex); break; default: /* both */ break; } } public static void main(String argv[]) { java.net.URL stoneURL = null; java.net.URL skyURL = null; // the path to the image for an application try { stoneURL = new java.net.URL("file:stone.jpg"); skyURL = new java.net.URL("file:bg.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } new MainFrame(new MultiTextureTest(stoneURL, skyURL), 750, 750); }
}
</source>
TexCoordGeneration class to automatically define the texture coordinates
<source lang="java">
/* Essential Java 3D Fast Ian Palmer Publisher: Springer-Verlag ISBN: 1-85233-394-4
- /
import java.awt.BorderLayout; import java.awt.Button; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.Node; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.Shape3D; import javax.media.j3d.TexCoordGeneration; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.image.TextureLoader; /**
* This uses a TexCoordGeneration class to automatically define the texture * coordinates. * * @author I.J.Palmer * @version 1.0 */
public class SimpleTextureGen extends Frame implements ActionListener {
protected Canvas3D myCanvas3D = new Canvas3D(null); protected Button myButton = new Button("Exit"); /** * This function builds the view branch of the scene graph. It creates a * branch group and then creates the necessary view elements to give a * useful view of our content. * * @param c * Canvas3D that will display the view * @return BranchGroup that is the root of the view elements */ protected BranchGroup buildViewBranch(Canvas3D c) { BranchGroup viewBranch = new BranchGroup(); Transform3D viewXfm = new Transform3D(); viewXfm.set(new Vector3f(0.0f, 0.0f, 10.0f)); TransformGroup viewXfmGroup = new TransformGroup(viewXfm); ViewPlatform myViewPlatform = new ViewPlatform(); PhysicalBody myBody = new PhysicalBody(); PhysicalEnvironment myEnvironment = new PhysicalEnvironment(); viewXfmGroup.addChild(myViewPlatform); viewBranch.addChild(viewXfmGroup); View myView = new View(); myView.addCanvas3D(c); myView.attachViewPlatform(myViewPlatform); myView.setPhysicalBody(myBody); myView.setPhysicalEnvironment(myEnvironment); return viewBranch; } /** * Add some lights so that we can illuminate the scene. This adds one * ambient light to bring up the overall lighting level and one directional * shape to show the shape of the objects in the scene. * * @param b * BranchGroup that the lights are to be added to. */ protected void addLights(BranchGroup b) { BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); Color3f lightColour1 = new Color3f(1.0f, 1.0f, 1.0f); Vector3f lightDir1 = new Vector3f(-1.0f, -1.0f, -1.0f); Color3f lightColour2 = new Color3f(1.0f, 1.0f, 1.0f); Vector3f lightDir2 = new Vector3f(0.0f, 0.0f, -1.0f); Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f); AmbientLight ambientLight1 = new AmbientLight(ambientColour); ambientLight1.setInfluencingBounds(bounds); DirectionalLight directionalLight1 = new DirectionalLight(lightColour1, lightDir1); directionalLight1.setInfluencingBounds(bounds); b.addChild(ambientLight1); b.addChild(directionalLight1); } /** * This builds the content branch of our scene graph. The shape supplied as * a parameter is slightly tilted to reveal its 3D shape. It also uses the * addLights function to add some lights to the scene. * * @param shape * Node that represents the geometry for the content * @return BranchGroup that is the root of the content branch */ protected BranchGroup buildContentBranch(Node shape) { BranchGroup contentBranch = new BranchGroup(); Transform3D rotateCube = new Transform3D(); rotateCube.set(new AxisAngle4d(1.0, 1.0, 0.0, Math.PI / 4.0)); TransformGroup rotationGroup = new TransformGroup(rotateCube); contentBranch.addChild(rotationGroup); rotationGroup.addChild(shape); addLights(contentBranch); return contentBranch; } /** * This defines the appearance for the shape using a texture. It uses a * TextureLoader to load the texture image from an external file and a * TexCoordGeneration to define the texture coordinates. * * @return Appearance that uses a texture. */ protected Appearance DefineAppearance() { //This is used to automatically define the texture coordinates. //The coordinates are generated in object coordinates, but by //commented out the line with "OBJECT_LINEAR" and uncommenting //the line "EYE_LINEAR" the program will use eye coordinates. TexCoordGeneration textCoorder = new TexCoordGeneration( TexCoordGeneration.OBJECT_LINEAR, //TexCoordGeneration.EYE_LINEAR, TexCoordGeneration.TEXTURE_COORDINATE_2); //Load the texture from the external image file TextureLoader textLoad = new TextureLoader("housebrick.jpg", this); //Access the image from the loaded texture ImageComponent2D textImage = textLoad.getImage(); //Create a two dimensional texture Texture2D texture = new Texture2D(Texture2D.BASE_LEVEL, Texture.RGB, textImage.getWidth(), textImage.getHeight()); //Set the texture from the image loaded texture.setImage(0, textImage); //Create the appearance that will use the texture Appearance app = new Appearance(); app.setTexture(texture); //Pass the coordinate generator to the appearance app.setTexCoordGeneration(textCoorder); //Define how the texture will be mapped onto the surface //by creating the appropriate texture attributes TextureAttributes textAttr = new TextureAttributes(); textAttr.setTextureMode(TextureAttributes.REPLACE); app.setTextureAttributes(textAttr); app.setMaterial(new Material()); return app; } /** * Build a cube from an IndexedQuadArray. This method creates the vertices * as a set of eight points and the normals as a set of six vectors (one for * each face). The data is then defined such that each vertex has a * different normal associated with it when it is being used for a different * face. The shape doesn"t have texture coordinates or nornmals defined * since the texture coordinate generator will define the necessary data. * * @return Node that is the shape. */ protected Node buildShape() { IndexedQuadArray indexedCube = new IndexedQuadArray(8, IndexedQuadArray.COORDINATES, 24); Point3f[] cubeCoordinates = { new Point3f(1.0f, 1.0f, 1.0f), new Point3f(-1.0f, 1.0f, 1.0f), new Point3f(-1.0f, -1.0f, 1.0f), new Point3f(1.0f, -1.0f, 1.0f), new Point3f(1.0f, 1.0f, -1.0f), new Point3f(-1.0f, 1.0f, -1.0f), new Point3f(-1.0f, -1.0f, -1.0f), new Point3f(1.0f, -1.0f, -1.0f) }; int coordIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6, 2, 1, 0, 4, 5, 1, 6, 7, 3, 2 }; int normalIndices[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; int textIndices[] = { 0, 1, 2, 3, 3, 0, 1, 2, 1, 2, 3, 0, 1, 2, 3, 0, 3, 0, 1, 2, 1, 2, 3, 0 }; indexedCube.setCoordinates(0, cubeCoordinates); indexedCube.setCoordinateIndices(0, coordIndices); return new Shape3D(indexedCube, DefineAppearance()); } /** * Handles the exit button action to quit the program. */ public void actionPerformed(ActionEvent e) { dispose(); System.exit(0); } public SimpleTextureGen() { VirtualUniverse myUniverse = new VirtualUniverse(); Locale myLocale = new Locale(myUniverse); myLocale.addBranchGraph(buildViewBranch(myCanvas3D)); myLocale.addBranchGraph(buildContentBranch(buildShape())); setTitle("SimpleWorld"); setSize(400, 400); setLayout(new BorderLayout()); add("Center", myCanvas3D); add("South", myButton); myButton.addActionListener(this); setVisible(true); } public static void main(String[] args) { SimpleTextureGen sw = new SimpleTextureGen(); }
}
</source>
Texture By Reference
<source lang="java">
/*
* @(#)TextureByReference.java 1.14 02/10/21 14:36:22 * * Copyright (c) 1996-2002 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: - * Redistributions 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY * OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.ruponent; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ruponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.util.Enumeration; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Material; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleArray; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnElapsedFrames; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.TexCoord2f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; public class TextureByReference extends Applet implements ItemListener,
ActionListener, ChangeListener { // need reference to animation behavior private AnimateTexturesBehavior animate; // need reference to tetrahedron private Tetrahedron tetra; // the gui buttons private JCheckBox flipB; private JRadioButton texByRef; private JRadioButton texByCopy; private JRadioButton geomByRef; private JRadioButton geomByCopy; private JRadioButton img4ByteABGR; private JRadioButton img3ByteBGR; private JRadioButton imgIntARGB; private JRadioButton imgCustomRGBA; private JRadioButton imgCustomRGB; private JRadioButton yUp; private JRadioButton yDown; private JButton animationB; private JSlider frameDelay; private SimpleUniverse universe = null; // image files used for the Texture animation for the applet, // or if no parameters are passed in for the application public static final String[] defaultFiles = { "animation1.gif", "animation2.gif", "animation3.gif", "animation4.gif", "animation5.gif", "animation6.gif", "animation7.gif", "animation8.gif", "animation9.gif", "animation10.gif" }; private java.net.URL[] urls = null; public TextureByReference() { } public TextureByReference(java.net.URL[] fnamesP) { urls = fnamesP; } public void init() { if (urls == null) { urls = new java.net.URL[defaultFiles.length]; for (int i = 0; i < defaultFiles.length; i++) { try { urls[i] = new java.net.URL(getCodeBase().toString() + defaultFiles[i]); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } } setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse .getPreferredConfiguration(); Canvas3D canvas = new Canvas3D(config); add("Center", canvas); // create a simple scene graph and attach it to a simple universe BranchGroup scene = createSceneGraph(); universe = new SimpleUniverse(canvas); universe.getViewingPlatform().setNominalViewingTransform(); universe.addBranchGraph(scene); // create the gui JPanel gui = buildGui(); this.add("South", gui); } public void destroy() { universe.cleanup(); } public JPanel buildGui() { flipB = new JCheckBox("flip image", true); flipB.addItemListener(this); javax.swing.Box flipBox = new javax.swing.Box(BoxLayout.Y_AXIS); flipBox.add(flipB); Component strut1 = flipBox .createVerticalStrut(flipB.getPreferredSize().height); Component strut2 = flipBox .createVerticalStrut(flipB.getPreferredSize().height); Component strut3 = flipBox .createVerticalStrut(flipB.getPreferredSize().height); Component strut4 = flipBox .createVerticalStrut(flipB.getPreferredSize().height); Component strut5 = flipBox .createVerticalStrut(flipB.getPreferredSize().height); flipBox.add(strut1); flipBox.add(strut2); flipBox.add(strut3); flipBox.add(strut4); flipBox.add(strut5); yUp = new JRadioButton("y up"); yUp.addActionListener(this); yUp.setSelected(true); yDown = new JRadioButton("y down"); yDown.addActionListener(this); ButtonGroup yGroup = new ButtonGroup(); yGroup.add(yUp); yGroup.add(yDown); JLabel yLabel = new JLabel("Image Orientation:"); javax.swing.Box yBox = new javax.swing.Box(BoxLayout.Y_AXIS); yBox.add(yLabel); yBox.add(yUp); yBox.add(yDown); strut1 = yBox.createVerticalStrut(yUp.getPreferredSize().height); strut2 = yBox.createVerticalStrut(yUp.getPreferredSize().height); strut3 = yBox.createVerticalStrut(yUp.getPreferredSize().height); yBox.add(strut1); yBox.add(strut2); yBox.add(strut3); texByRef = new JRadioButton("by reference"); texByRef.addActionListener(this); texByRef.setSelected(true); texByCopy = new JRadioButton("by copy"); texByCopy.addActionListener(this); ButtonGroup texGroup = new ButtonGroup(); texGroup.add(texByRef); texGroup.add(texByCopy); JLabel texLabel = new JLabel("Texture:*"); javax.swing.Box texBox = new javax.swing.Box(BoxLayout.Y_AXIS); texBox.add(texLabel); texBox.add(texByRef); texBox.add(texByCopy); strut1 = texBox.createVerticalStrut(texByRef.getPreferredSize().height); strut2 = texBox.createVerticalStrut(texByRef.getPreferredSize().height); strut3 = texBox.createVerticalStrut(texByRef.getPreferredSize().height); texBox.add(strut1); texBox.add(strut2); texBox.add(strut3); geomByRef = new JRadioButton("by reference"); geomByRef.addActionListener(this); geomByRef.setSelected(true); geomByCopy = new JRadioButton("by copy"); geomByCopy.addActionListener(this); ButtonGroup geomGroup = new ButtonGroup(); geomGroup.add(geomByRef); geomGroup.add(geomByCopy); JLabel geomLabel = new JLabel("Geometry:"); javax.swing.Box geomBox = new javax.swing.Box(BoxLayout.Y_AXIS); geomBox.add(geomLabel); geomBox.add(geomByRef); geomBox.add(geomByCopy); strut1 = geomBox .createVerticalStrut(geomByRef.getPreferredSize().height); strut2 = geomBox .createVerticalStrut(geomByRef.getPreferredSize().height); strut3 = geomBox .createVerticalStrut(geomByRef.getPreferredSize().height); geomBox.add(strut1); geomBox.add(strut2); geomBox.add(strut3); img4ByteABGR = new JRadioButton("TYPE_4BYTE_ABGR"); img4ByteABGR.addActionListener(this); img4ByteABGR.setSelected(true); img3ByteBGR = new JRadioButton("TYPE_3BYTE_BGR"); img3ByteBGR.addActionListener(this); imgIntARGB = new JRadioButton("TYPE_INT_ARGB"); imgIntARGB.addActionListener(this); imgCustomRGBA = new JRadioButton("TYPE_CUSTOM RGBA"); imgCustomRGBA.addActionListener(this); imgCustomRGB = new JRadioButton("TYPE_CUSTOM RGB"); imgCustomRGB.addActionListener(this); ButtonGroup imgGroup = new ButtonGroup(); imgGroup.add(img4ByteABGR); imgGroup.add(img3ByteBGR); imgGroup.add(imgIntARGB); imgGroup.add(imgCustomRGBA); imgGroup.add(imgCustomRGB); JLabel imgLabel = new JLabel("Image Type:*"); javax.swing.Box imgBox = new javax.swing.Box(BoxLayout.Y_AXIS); imgBox.add(imgLabel); imgBox.add(img4ByteABGR); imgBox.add(img3ByteBGR); imgBox.add(imgIntARGB); imgBox.add(imgCustomRGBA); imgBox.add(imgCustomRGB); javax.swing.Box topBox = new javax.swing.Box(BoxLayout.X_AXIS); topBox.add(flipBox); topBox.add(texBox); topBox.add(geomBox); topBox.add(yBox); Component strut = topBox.createRigidArea(new Dimension(10, 10)); topBox.add(strut); topBox.add(imgBox); frameDelay = new JSlider(0, 50, 0); frameDelay.addChangeListener(this); frameDelay.setSnapToTicks(true); frameDelay.setPaintTicks(true); frameDelay.setPaintLabels(true); frameDelay.setMajorTickSpacing(10); frameDelay.setMinorTickSpacing(1); frameDelay.setValue(20); JLabel delayL = new JLabel("frame delay"); javax.swing.Box delayBox = new javax.swing.Box(BoxLayout.X_AXIS); delayBox.add(delayL); delayBox.add(frameDelay); animationB = new JButton(" stop animation "); animationB.addActionListener(this); JLabel texInfo1 = new JLabel( "*To use ImageComponent by reference feature, use TYPE_4BYTE_ABGR on Solaris"); JLabel texInfo2 = new JLabel("and TYPE_3BYTE_BGR on Windows"); JPanel buttonP = new JPanel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); buttonP.setLayout(gridbag); c.anchor = GridBagConstraints.CENTER; c.gridwidth = GridBagConstraints.REMAINDER; gridbag.setConstraints(topBox, c); buttonP.add(topBox); gridbag.setConstraints(delayBox, c); buttonP.add(delayBox); gridbag.setConstraints(animationB, c); buttonP.add(animationB); gridbag.setConstraints(texInfo1, c); buttonP.add(texInfo1); gridbag.setConstraints(texInfo2, c); buttonP.add(texInfo2); return buttonP; } public BranchGroup createSceneGraph() { // create the root of the branch group BranchGroup objRoot = new BranchGroup(); // create the transform group node and initialize it // enable the TRANSFORM_WRITE capability so that it can be modified // at runtime. Add it to the root of the subgraph Transform3D rotate = new Transform3D(); TransformGroup objTrans = new TransformGroup(rotate); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objRoot.addChild(objTrans); // bounds BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); // set up some light Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f); Vector3f lDir1 = new Vector3f(-1.0f, -0.5f, -1.0f); Color3f alColor = new Color3f(0.2f, 0.2f, 0.2f); AmbientLight aLgt = new AmbientLight(alColor); aLgt.setInfluencingBounds(bounds); DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1); lgt1.setInfluencingBounds(bounds); objRoot.addChild(aLgt); objRoot.addChild(lgt1); Appearance appearance = new Appearance(); // enable the TEXTURE_WRITE so we can modify it at runtime appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE); // load the first texture TextureLoader loader = new TextureLoader(urls[0], TextureLoader.BY_REFERENCE | TextureLoader.Y_UP, this); // get the texture from the loader Texture2D tex = (Texture2D) loader.getTexture(); // get the BufferedImage to convert to TYPE_4BYTE_ABGR and flip // get the ImageComponent because we need it anyway ImageComponent2D imageComp = (ImageComponent2D) tex.getImage(0); BufferedImage bImage = imageComp.getImage(); // convert the image bImage = ImageOps.convertImage(bImage, BufferedImage.TYPE_4BYTE_ABGR); // flip the image ImageOps.flipImage(bImage); imageComp.set(bImage); tex.setCapability(Texture.ALLOW_IMAGE_WRITE); tex.setBoundaryModeS(Texture.CLAMP); tex.setBoundaryModeT(Texture.CLAMP); tex.setBoundaryColor(1.0f, 1.0f, 1.0f, 1.0f); // set the image of the texture tex.setImage(0, imageComp); // set the texture on the appearance appearance.setTexture(tex); // set texture attributes TextureAttributes texAttr = new TextureAttributes(); texAttr.setTextureMode(TextureAttributes.MODULATE); appearance.setTextureAttributes(texAttr); // set material properties Color3f black = new Color3f(0.0f, 0.0f, 0.0f); Color3f white = new Color3f(1.0f, 1.0f, 1.0f); appearance.setMaterial(new Material(white, black, white, black, 1.0f)); // create a scale transform Transform3D scale = new Transform3D(); scale.set(.6); TransformGroup objScale = new TransformGroup(scale); objTrans.addChild(objScale); tetra = new Tetrahedron(true); tetra.setAppearance(appearance); objScale.addChild(tetra); // create the behavior animate = new AnimateTexturesBehavior(tex, urls, appearance, this); animate.setSchedulingBounds(bounds); objTrans.addChild(animate); // add a rotation behavior so we can see all sides of the tetrahedron Transform3D yAxis = new Transform3D(); Alpha rotorAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0); RotationInterpolator rotator = new RotationInterpolator(rotorAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f); rotator.setSchedulingBounds(bounds); objTrans.addChild(rotator); // have java3d perform optimizations on this scene graph objRoot.rupile(); return objRoot; } // callback for the animation button and delay text field public void actionPerformed(ActionEvent e) { Object o = e.getSource(); // for the animation button if (o == animationB) { if (animate.getEnable()) { animate.setEnable(false); animationB.setText("start animation"); } else { animate.setEnable(true); animationB.setText(" stop animation "); } } // for the texByRef button else if (o == texByRef && texByRef.isSelected()) { animate.setByReference(true); } // texByCopy button else if (o == texByCopy && texByCopy.isSelected()) { animate.setByReference(false); } // yUp button else if (o == yUp && yUp.isSelected()) { animate.setYUp(true); } // ydown button else if (o == yDown && yDown.isSelected()) { animate.setYUp(false); } //geomByRef button else if (o == geomByRef) { tetra.setByReference(true); } // geomByCopy button else if (o == geomByCopy) { tetra.setByReference(false); } // TYPE_INT_ARGB else if (o == imgIntARGB) { animate.setImageType(BufferedImage.TYPE_INT_ARGB); } // TYPE_4BYTE_ABGR else if (o == img4ByteABGR) { animate.setImageType(BufferedImage.TYPE_4BYTE_ABGR); } // TYPE_3BYTE_BGR else if (o == img3ByteBGR) { animate.setImageType(BufferedImage.TYPE_3BYTE_BGR); } // TYPE_CUSTOM RGBA else if (o == imgCustomRGBA) { animate.setImageTypeCustomRGBA(); } // TYPE_CUSTOM RGB else if (o == imgCustomRGB) { animate.setImageTypeCustomRGB(); } } // callback for the checkboxes public void itemStateChanged(ItemEvent e) { Object o = e.getSource(); // for the flip checkbox if (o == flipB) { if (e.getStateChange() == ItemEvent.DESELECTED) { animate.setFlipImages(false); } else animate.setFlipImages(true); } } // callback for the slider public void stateChanged(ChangeEvent e) { Object o = e.getSource(); // for the frame delay if (o == frameDelay) { animate.setFrameDelay(frameDelay.getValue()); } } // allows TextureByReference to be run as an application as well as an // applet public static void main(String[] args) { java.net.URL fnames[] = null; if (args.length > 1) { fnames = new java.net.URL[args.length]; for (int i = 0; i < args.length; i++) { try { fnames[i] = new java.net.URL("file:" + args[i]); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); } } } else { fnames = new java.net.URL[TextureByReference.defaultFiles.length]; for (int i = 0; i < TextureByReference.defaultFiles.length; i++) { try { fnames[i] = new java.net.URL("file:" + TextureByReference.defaultFiles[i]); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } } new MainFrame((new TextureByReference(fnames)), 650, 750); }
} class AnimateTexturesBehavior extends Behavior {
// what image are we on private int current; private int max; // the images private ImageComponent2D[] images; // the target private Texture2D texture; private Appearance appearance; // the wakeup criterion private WakeupCriterion wakeupC; // are the images flipped? private boolean flip; // need the current type because by copy changes all images // to TYPE_INT_ARGB private int currentType; // for custom types public static final int TYPE_CUSTOM_RGBA = 0x01; public static final int TYPE_CUSTOM_RGB = 0x02; private int customType; // create a new AnimateTextureBehavior // initialize the images public AnimateTexturesBehavior(Texture2D texP, java.net.URL[] fnames, Appearance appP, TextureByReference applet) { int size = fnames.length; images = new ImageComponent2D[size]; BufferedImage bImage; TextureLoader loader; for (int i = 0; i < size; i++) { loader = new TextureLoader(fnames[i], TextureLoader.BY_REFERENCE | TextureLoader.Y_UP, applet); images[i] = loader.getImage(); bImage = images[i].getImage(); // convert the image to TYPE_4BYTE_ABGR currentType = BufferedImage.TYPE_4BYTE_ABGR; bImage = ImageOps.convertImage(bImage, currentType); // flip the image flip = true; ImageOps.flipImage(bImage); // set the image on the ImageComponent to the new one images[i].set(bImage); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } texture = texP; current = 0; max = size; wakeupC = new WakeupOnElapsedFrames(20); appearance = appP; } // initialize to the first image public void initialize() { texture.setImage(0, images[current]); if (current < max - 1) current++; else current = 0; wakeupOn(wakeupC); } // procesStimulus changes the ImageComponent of the texture public void processStimulus(Enumeration criteria) { // ImageOps.printType(images[current].getImage()); texture.setImage(0, images[current]); appearance.setTexture(texture); if (current < max - 1) current++; else current = 0; wakeupOn(wakeupC); } // flip the image -- useful depending on yUp public void setFlipImages(boolean b) { // double check that flipping is necessary if (b != flip) { BufferedImage bImage; // these are the same for all images so get info once int format = images[0].getFormat(); boolean byRef = images[0].isByReference(); boolean yUp = images[0].isYUp(); // flip all the images // have to new ImageComponents because can"t set the image at // runtime for (int i = 0; i < images.length; i++) { bImage = images[i].getImage(); ImageOps.flipImage(bImage); // if we are byRef and the bImage type does not match // currentType // we need to convert it. If we are not byRef we will // save converting until it is changed to byRef if (byRef && bImage.getType() != currentType) { if (currentType != BufferedImage.TYPE_CUSTOM) { bImage = ImageOps.convertImage(bImage, currentType); } else if (customType == this.TYPE_CUSTOM_RGBA) { bImage = ImageOps.convertToCustomRGBA(bImage); } else { bImage = ImageOps.convertToCustomRGB(bImage); } } images[i] = new ImageComponent2D(format, bImage, byRef, yUp); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } // set flip to new value flip = b; } } // create new ImageComponents with yUp set to the parameter. yUp on // an ImageComponent cannot be changed at runtim public void setYUp(boolean b) { // double check that changing yUp is necessary if (b != images[0].isYUp()) { // these are the same for all images so get info once int format = images[0].getFormat(); boolean byRef = images[0].isByReference(); // reset yUp on all the images -- have to new ImageComponents // because // cannot change the value at runtime for (int i = 0; i < images.length; i++) { // if we are byRef and the bImage type does not match // currentType // we need to convert it. If we are not byRef we will // save converting until it is changed to byRef BufferedImage bImage = images[i].getImage(); if (byRef && bImage.getType() != currentType) { // bImage = ImageOps.convertImage(bImage, currentType); if (currentType != BufferedImage.TYPE_CUSTOM) { bImage = ImageOps.convertImage(bImage, currentType); } else if (customType == this.TYPE_CUSTOM_RGBA) { bImage = ImageOps.convertToCustomRGBA(bImage); } else { bImage = ImageOps.convertToCustomRGB(bImage); } } images[i] = new ImageComponent2D(format, bImage, byRef, b); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } } } // create new ImageComponents with ByReference set by parameter. // by reference cannot be changed on an image component at runtime public void setByReference(boolean b) { // double check that changing is necessary if (b != images[0].isByReference()) { // these are the same for all images so get info once int format = images[0].getFormat(); boolean yUp = images[0].isYUp(); // reset yUp on all the images // have to new ImageComponents because cannot set value for (int i = 0; i < images.length; i++) { // if the bImage type does not match currentType and we are // setting // to byRef we need to convert it BufferedImage bImage = images[i].getImage(); if (bImage.getType() != currentType && b) { // bImage = ImageOps.convertImage(bImage, currentType); if (currentType != BufferedImage.TYPE_CUSTOM) { bImage = ImageOps.convertImage(bImage, currentType); } else if (customType == this.TYPE_CUSTOM_RGBA) { bImage = ImageOps.convertToCustomRGBA(bImage); } else { bImage = ImageOps.convertToCustomRGB(bImage); } } images[i] = new ImageComponent2D(format, bImage, b, yUp); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } } } // make a new wakeup criterion object based on the new delay time public void setFrameDelay(int delay) { wakeupC = new WakeupOnElapsedFrames(delay); } //change the type of image public void setImageType(int newType) { currentType = newType; // only need to change the images if we are byRef otherwise will change // them when we chnage to byRef if (images[0].isByReference() == true) { // this information is the same for all int format = images[0].getFormat(); boolean yUp = images[0].isYUp(); boolean byRef = true; for (int i = 0; i < images.length; i++) { BufferedImage bImage = images[i].getImage(); bImage = ImageOps.convertImage(bImage, currentType); images[i] = new ImageComponent2D(format, bImage, byRef, yUp); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } } } public void setImageTypeCustomRGBA() { currentType = BufferedImage.TYPE_CUSTOM; customType = this.TYPE_CUSTOM_RGBA; // only need to change images if we are byRef otherwise will change // them when we change to byRef if (images[0].isByReference()) { // this information is the same for all int format = images[0].getFormat(); boolean yUp = images[0].isYUp(); boolean byRef = true; for (int i = 0; i < images.length; i++) { BufferedImage bImage = images[i].getImage(); bImage = ImageOps.convertToCustomRGBA(bImage); images[i] = new ImageComponent2D(format, bImage, byRef, yUp); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } } } public void setImageTypeCustomRGB() { currentType = BufferedImage.TYPE_CUSTOM; customType = this.TYPE_CUSTOM_RGB; // only need to change images if we are byRef otherwise will change // them when we change to byRef if (images[0].isByReference()) { // this information is the same for all int format = images[0].getFormat(); boolean yUp = images[0].isYUp(); boolean byRef = true; for (int i = 0; i < images.length; i++) { BufferedImage bImage = images[i].getImage(); bImage = ImageOps.convertToCustomRGB(bImage); images[i] = new ImageComponent2D(format, bImage, byRef, yUp); images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ); images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ); } } }
} class Tetrahedron extends Shape3D {
private static final float sqrt3 = (float) Math.sqrt(3.0); private static final float sqrt3_3 = sqrt3 / 3.0f; private static final float sqrt24_3 = (float) Math.sqrt(24.0) / 3.0f; private static final float ycenter = 0.5f * sqrt24_3; private static final float zcenter = -sqrt3_3; private static final Point3f p1 = new Point3f(-1.0f, -ycenter, -zcenter); private static final Point3f p2 = new Point3f(1.0f, -ycenter, -zcenter); private static final Point3f p3 = new Point3f(0.0f, -ycenter, -sqrt3 - zcenter); private static final Point3f p4 = new Point3f(0.0f, sqrt24_3 - ycenter, 0.0f); private static final Point3f[] verts = { p1, p2, p4, // front face p1, p4, p3, // left, back face p2, p3, p4, // right, back face p1, p3, p2, // bottom face }; private Point2f texCoord[] = { new Point2f(-0.25f, 0.0f), new Point2f(1.25f, 0.0f), new Point2f(0.5f, 2.0f), }; private TriangleArray geometryByRef; private TriangleArray geometryByCopy; // for geometry by reference private Point3f[] verticesArray = new Point3f[12]; private TexCoord2f[] textureCoordsArray = new TexCoord2f[12]; private Vector3f[] normalsArray = new Vector3f[12]; // default to geometry by copy public Tetrahedron() { this(false); } // creates a tetrahedron with geometry by reference or by copy depending on // the byRef parameter public Tetrahedron(boolean byRef) { if (byRef) { createGeometryByRef(); this.setGeometry(geometryByRef); } else { createGeometryByCopy(); this.setGeometry(geometryByCopy); } this.setCapability(Shape3D.ALLOW_GEOMETRY_READ); this.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE); setAppearance(new Appearance()); } // create the geometry by reference and // store it in the geometryByRef variable public void createGeometryByRef() { // System.out.println("createGeometryByRef"); geometryByRef = new TriangleArray(12, TriangleArray.COORDINATES | TriangleArray.NORMALS | TriangleArray.TEXTURE_COORDINATE_2 | TriangleArray.BY_REFERENCE); int i; // the coordinates for (i = 0; i < 12; i++) { verticesArray[i] = new Point3f(verts[i]); } geometryByRef.setCoordRef3f(verticesArray); // System.out.println("coordinates set"); // Point3f[] temp1 = geometryByRef.getCoordRef3f(); // for (i = 0; i < 12; i++) { // System.out.println(temp1[i]); // } // the texture coordinates for (i = 0; i < 12; i++) { textureCoordsArray[i] = new TexCoord2f(texCoord[i % 3]); } geometryByRef.setTexCoordRef2f(0, textureCoordsArray); // System.out.println("texture coords set"); // TexCoord2f[] temp2 = geometryByRef.getTexCoordRef2f(0); // for (i = 0; i < 12; i++) { // System.out.println(temp2[i]); // } // the normals Vector3f normal = new Vector3f(); Vector3f v1 = new Vector3f(); Vector3f v2 = new Vector3f(); Point3f[] pts = new Point3f[3]; for (int face = 0; face < 4; face++) { pts[0] = new Point3f(verts[face * 3]); pts[1] = new Point3f(verts[face * 3 + 1]); pts[2] = new Point3f(verts[face * 3 + 2]); v1.sub(pts[1], pts[0]); v2.sub(pts[2], pts[0]); normal.cross(v1, v2); normal.normalize(); for (i = 0; i < 3; i++) { normalsArray[face * 3 + i] = new Vector3f(normal); } } geometryByRef.setNormalRef3f(normalsArray); // System.out.println("normals set"); // Vector3f[] temp3 = geometryByRef.getNormalRef3f(); // for (i = 0; i < 12; i++) { // System.out.println(temp3[i]); // } } // create the geometry by copy and store it in the geometryByCopy variable public void createGeometryByCopy() { int i; geometryByCopy = new TriangleArray(12, TriangleArray.COORDINATES | TriangleArray.NORMALS | TriangleArray.TEXTURE_COORDINATE_2); geometryByCopy.setCoordinates(0, verts); for (i = 0; i < 12; i++) { geometryByCopy.setTextureCoordinate(0, i, new TexCoord2f( texCoord[i % 3])); } int face; Vector3f normal = new Vector3f(); Vector3f v1 = new Vector3f(); Vector3f v2 = new Vector3f(); Point3f[] pts = new Point3f[3]; for (i = 0; i < 3; i++) pts[i] = new Point3f(); for (face = 0; face < 4; face++) { geometryByCopy.getCoordinates(face * 3, pts); v1.sub(pts[1], pts[0]); v2.sub(pts[2], pts[0]); normal.cross(v1, v2); normal.normalize(); for (i = 0; i < 3; i++) { geometryByCopy.setNormal((face * 3 + i), normal); } } } // set the geometry to geometryByRef or geometryByCopy depending on the // parameter. Create geometryByRef or geometryByCopy if necessary public void setByReference(boolean b) { // System.out.println("Tetrahedron.setByReference " + b); // by reference is true if (b) { // if there is no geometryByRef, create it if (geometryByRef == null) { createGeometryByRef(); } // set the geometry this.setGeometry(geometryByRef); } // by reference is false else { // if there is no geometryByCopy, create it if (geometryByCopy == null) { createGeometryByCopy(); } // set the geometry this.setGeometry(geometryByCopy); } }
} //some useful, static image operations class ImageOps {
// flip the image public static void flipImage(BufferedImage bImage) { int width = bImage.getWidth(); int height = bImage.getHeight(); int[] rgbArray = new int[width * height]; bImage.getRGB(0, 0, width, height, rgbArray, 0, width); int[] tempArray = new int[width * height]; int y2 = 0; for (int y = height - 1; y >= 0; y--) { for (int x = 0; x < width; x++) { tempArray[y2 * width + x] = rgbArray[y * width + x]; } y2++; } bImage.setRGB(0, 0, width, height, tempArray, 0, width); } // convert the image to a specified BufferedImage type and return it public static BufferedImage convertImage(BufferedImage bImage, int type) { int width = bImage.getWidth(); int height = bImage.getHeight(); BufferedImage newImage = new BufferedImage(width, height, type); int[] rgbArray = new int[width * height]; bImage.getRGB(0, 0, width, height, rgbArray, 0, width); newImage.setRGB(0, 0, width, height, rgbArray, 0, width); return newImage; } // print out some of the types of BufferedImages static void printType(BufferedImage bImage) { int type = bImage.getType(); if (type == BufferedImage.TYPE_4BYTE_ABGR) { System.out.println("TYPE_4BYTE_ABGR"); } else if (type == BufferedImage.TYPE_INT_ARGB) { System.out.println("TYPE_INT_ARGB"); } else if (type == BufferedImage.TYPE_3BYTE_BGR) { System.out.println("TYPE_3BYTE_BGR"); } else if (type == BufferedImage.TYPE_CUSTOM) { System.out.println("TYPE_CUSTOM"); } else System.out.println(type); } public static BufferedImage convertToCustomRGBA(BufferedImage bImage) { if (bImage.getType() != BufferedImage.TYPE_INT_ARGB) { ImageOps.convertImage(bImage, BufferedImage.TYPE_INT_ARGB); } int width = bImage.getWidth(); int height = bImage.getHeight(); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] nBits = { 8, 8, 8, 8 }; ColorModel cm = new ComponentColorModel(cs, nBits, true, false, Transparency.OPAQUE, 0); int[] bandOffset = { 0, 1, 2, 3 }; WritableRaster newRaster = Raster.createInterleavedRaster( DataBuffer.TYPE_BYTE, width, height, width * 4, 4, bandOffset, null); byte[] byteData = ((DataBufferByte) newRaster.getDataBuffer()) .getData(); Raster origRaster = bImage.getData(); int[] pixel = new int[4]; int k = 0; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { pixel = origRaster.getPixel(i, j, pixel); byteData[k++] = (byte) (pixel[0]); byteData[k++] = (byte) (pixel[1]); byteData[k++] = (byte) (pixel[2]); byteData[k++] = (byte) (pixel[3]); } } BufferedImage newImage = new BufferedImage(cm, newRaster, false, null); // if (newImage.getType() == BufferedImage.TYPE_CUSTOM) { // System.out.println("Type is custom"); // } return newImage; } public static BufferedImage convertToCustomRGB(BufferedImage bImage) { if (bImage.getType() != BufferedImage.TYPE_INT_ARGB) { ImageOps.convertImage(bImage, BufferedImage.TYPE_INT_ARGB); } int width = bImage.getWidth(); int height = bImage.getHeight(); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); int[] nBits = { 8, 8, 8 }; ColorModel cm = new ComponentColorModel(cs, nBits, false, false, Transparency.OPAQUE, 0); int[] bandOffset = { 0, 1, 2 }; WritableRaster newRaster = Raster.createInterleavedRaster( DataBuffer.TYPE_BYTE, width, height, width * 3, 3, bandOffset, null); byte[] byteData = ((DataBufferByte) newRaster.getDataBuffer()) .getData(); Raster origRaster = bImage.getData(); int[] pixel = new int[4]; int k = 0; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { pixel = origRaster.getPixel(i, j, pixel); byteData[k++] = (byte) (pixel[0]); byteData[k++] = (byte) (pixel[1]); byteData[k++] = (byte) (pixel[2]); } } BufferedImage newImage = new BufferedImage(cm, newRaster, false, null); // if (newImage.getType() == BufferedImage.TYPE_CUSTOM) { // System.out.println("Type is custom"); // } return newImage; }
}
</source>
Texture Image
<source lang="java">
/*
* @(#)TextureImage.java 1.26 02/10/21 13:57:32 * * Copyright (c) 1996-2002 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: * - Redistributions 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 AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE 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 SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY * OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.GraphicsConfiguration; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Point3d; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; public class TextureImage extends Applet {
private java.net.URL texImage = null; private SimpleUniverse u = null; public BranchGroup createSceneGraph() { // Create the root of the branch graph BranchGroup objRoot = new BranchGroup(); // Create the transform group node and initialize it to the // identity. Enable the TRANSFORM_WRITE capability so that // our behavior code can modify it at runtime. Add it to the // root of the subgraph. TransformGroup objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objRoot.addChild(objTrans); // Create appearance object for textured cube Appearance app = new Appearance(); Texture tex = new TextureLoader(texImage, this).getTexture(); app.setTexture(tex); TextureAttributes texAttr = new TextureAttributes(); texAttr.setTextureMode(TextureAttributes.MODULATE); app.setTextureAttributes(texAttr); // Create textured cube and add it to the scene graph. Box textureCube = new Box(0.4f, 0.4f, 0.4f, Box.GENERATE_TEXTURE_COORDS, app); objTrans.addChild(textureCube); // Create a new Behavior object that will perform the desired // operation on the specified transform object and add it into // the scene graph. Transform3D yAxis = new Transform3D(); Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0); RotationInterpolator rotator = new RotationInterpolator(rotationAlpha, objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); rotator.setSchedulingBounds(bounds); objTrans.addChild(rotator); // Have Java 3D perform optimizations on this scene graph. objRoot.rupile(); return objRoot; } public TextureImage() { } public TextureImage(java.net.URL url) { texImage = url; } public void init() { if (texImage == null) { // the path to the image for an applet try { texImage = new java.net.URL(getCodeBase().toString() + "/stone.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse .getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); add("Center", c); // Create a simple scene and attach it to the virtual universe BranchGroup scene = createSceneGraph(); u = new SimpleUniverse(c); // This will move the ViewPlatform back a bit so the // objects in the scene can be viewed. u.getViewingPlatform().setNominalViewingTransform(); u.addBranchGraph(scene); } public void destroy() { u.cleanup(); } // // The following allows TextureImage to be run as an application // as well as an applet // public static void main(String[] args) { java.net.URL url = null; if (args.length > 0) { try { url = new java.net.URL("file:" + args[0]); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } else { // the path to the image for an application try { url = new java.net.URL("file:stone.jpg"); } catch (java.net.MalformedURLException ex) { System.out.println(ex.getMessage()); System.exit(1); } } new MainFrame(new TextureImage(url), 256, 256); }
}
</source>
Texture Mapping
<source lang="java">
import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.net.MalformedURLException; import java.net.URL; import javax.media.j3d.Appearance; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Texture2D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; /**
* !Diese Klasse wurde fur das Laden uber ein JAR-Archiv und Applet welches ein * JAR - Archiv nutzt angepasst Um das Programm als einfache Applikation uber * einen class-File laufen zu lassen bitte auf den Code zum Einladen der Textur * im Tutorial zuruckgreifen! */
public class TextureMapping extends Applet {
/** * init Methoden fur die Darstellung als Applet */ public void init() { setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse .getPreferredConfiguration(); canvas3D = new Canvas3D(config); add("Center", canvas3D); BranchGroup szene = macheSzene(); szene.rupile(); universe = new SimpleUniverse(canvas3D); universe.getViewingPlatform().setNominalViewingTransform(); universe.addBranchGraph(szene); } /** * Erstellt den Szenegraphen * * @return BranchGroup */ public BranchGroup macheSzene() { BranchGroup objWurzel = new BranchGroup(); // Transformation, 2 Rotationen: Transform3D drehung = new Transform3D(); Transform3D drehung2 = new Transform3D(); drehung.rotX(Math.PI / 4.0d); drehung2.rotY(Math.PI / 5.0d); drehung.mul(drehung2); TransformGroup objDreh = new TransformGroup(drehung); objDreh.addChild(new Box(0.5f, 0.5f, 0.5f, Box.GENERATE_TEXTURE_COORDS, makeAppearance())); objWurzel.addChild(objDreh); return objWurzel; } /** * Wurfeldarstellung * * @return Appearance */ private Appearance makeAppearance() { Appearance a = new Appearance(); TextureLoader loader = null; if (getCodeBase() != null) try { // Laden der Obj Datei als Applet mit jar loader = new TextureLoader(new URL("jar:" + getCodeBase() + "TextureMapping.jar!/burnstone.jpg"), null); } catch (MalformedURLException e) { e.printStackTrace(); } else // Laden der Obj Datei mittels jar loader = new TextureLoader(ClassLoader .getSystemResource("burnstone.jpg"), null); Texture2D texture = (Texture2D) loader.getTexture(); a.setTexture(texture); return a; } /** * gibt speicher frei */ public void destroy() { universe.removeAllLocales(); } public static void main(String[] args) { frame = new MainFrame(new TextureMapping(), 500, 500); frame.setTitle("Texture"); } //---- Attribute ----------------------- private SimpleUniverse universe; private Canvas3D canvas3D; private static Frame frame;
}
</source>
Texture: picture ball
<source lang="java">
/* The Joy of Java 3D by Greg Hopkins Copyright Copyright 2001
- /
/* Materials make change the appearance of a whole shape, but sometimes even the shiniest objects can seem dull. By adding texture you can produce more interesting effects like marbling or wrapping a two-dimensional image around your object. The TextureLoader class enables you to load an image to use as a texture. The dimensions of your image must be powers of two, for example 128 pixels by 256. When you load the texture you can also specify how you want to use the image. For example, RGB to use the color of the image or LUMINANCE to see the image in black and white. After the texture is loaded, you can change the TextureAttributes to say whether you want the image to replace the object underneath or modulate the underlying color. You can also apply it as a decal or blend the image with the color of your choice. If you are using a simple object like a sphere then you will also have to enable texturing
by setting the "primitive flags". These can be set to Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS when you create the object.
In case this is starting to sound a bit complicated, here is an example. You can experiment with the texture settings in this example and compare the results. You can download the picture I used from http://www.java3d.org/Arizona.jpg or you can substitute a picture of your own.
- /
import java.awt.Container; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Material; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.vecmath.Color3f; import javax.vecmath.Color4f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; public class PictureBall {
public PictureBall() { // Create the universe SimpleUniverse universe = new SimpleUniverse(); // Create a structure to contain objects BranchGroup group = new BranchGroup(); // Set up colors Color3f black = new Color3f(0.0f, 0.0f, 0.0f); Color3f white = new Color3f(1.0f, 1.0f, 1.0f); Color3f red = new Color3f(0.7f, .15f, .15f); // Set up the texture map TextureLoader loader = new TextureLoader("K:\\3d\\Arizona.jpg", "LUMINANCE", new Container()); Texture texture = loader.getTexture(); texture.setBoundaryModeS(Texture.WRAP); texture.setBoundaryModeT(Texture.WRAP); texture.setBoundaryColor(new Color4f(0.0f, 1.0f, 0.0f, 0.0f)); // Set up the texture attributes //could be REPLACE, BLEND or DECAL instead of MODULATE TextureAttributes texAttr = new TextureAttributes(); texAttr.setTextureMode(TextureAttributes.MODULATE); Appearance ap = new Appearance(); ap.setTexture(texture); ap.setTextureAttributes(texAttr); //set up the material ap.setMaterial(new Material(red, black, red, black, 1.0f)); // Create a ball to demonstrate textures int primflags = Primitive.GENERATE_NORMALS + Primitive.GENERATE_TEXTURE_COORDS; Sphere sphere = new Sphere(0.5f, primflags, ap); group.addChild(sphere); // Create lights Color3f light1Color = new Color3f(1f, 1f, 1f); BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); Vector3f light1Direction = new Vector3f(4.0f, -7.0f, -12.0f); DirectionalLight light1 = new DirectionalLight(light1Color, light1Direction); light1.setInfluencingBounds(bounds); group.addChild(light1); AmbientLight ambientLight = new AmbientLight(new Color3f(.5f, .5f, .5f)); ambientLight.setInfluencingBounds(bounds); group.addChild(ambientLight); // look towards the ball universe.getViewingPlatform().setNominalViewingTransform(); // add the group of objects to the Universe universe.addBranchGraph(group); } public static void main(String[] args) { new PictureBall(); }
}
</source>
The simple application of textures
<source lang="java">
/* Essential Java 3D Fast Ian Palmer Publisher: Springer-Verlag ISBN: 1-85233-394-4
- /
import java.awt.BorderLayout; import java.awt.Button; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.Node; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.TexCoord2f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.image.TextureLoader; /**
* This demonstrates the simple application of textures. Each face of a cube has * an image mapped onto it exactly once. The image is loaded from an external * file. * * @author I.J.Palmer * @version 1.0 */
public class SimpleTexture extends Frame implements ActionListener {
protected Canvas3D myCanvas3D = new Canvas3D(null); protected Button myButton = new Button("Exit"); /** * This function builds the view branch of the scene graph. It creates a * branch group and then creates the necessary view elements to give a * useful view of our content. * * @param c * Canvas3D that will display the view * @return BranchGroup that is the root of the view elements */ protected BranchGroup buildViewBranch(Canvas3D c) { BranchGroup viewBranch = new BranchGroup(); Transform3D viewXfm = new Transform3D(); viewXfm.set(new Vector3f(0.0f, 0.0f, 5.0f)); TransformGroup viewXfmGroup = new TransformGroup(viewXfm); ViewPlatform myViewPlatform = new ViewPlatform(); PhysicalBody myBody = new PhysicalBody(); PhysicalEnvironment myEnvironment = new PhysicalEnvironment(); viewXfmGroup.addChild(myViewPlatform); viewBranch.addChild(viewXfmGroup); View myView = new View(); myView.addCanvas3D(c); myView.attachViewPlatform(myViewPlatform); myView.setPhysicalBody(myBody); myView.setPhysicalEnvironment(myEnvironment); return viewBranch; } /** * Add some lights so that we can illuminate the scene. This adds one * ambient light to bring up the overall lighting level and one directional * shape to show the shape of the objects in the scene. * * @param b * BranchGroup that the lights are to be added to. */ protected void addLights(BranchGroup b) { BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); Color3f lightColour1 = new Color3f(1.0f, 1.0f, 1.0f); Vector3f lightDir1 = new Vector3f(-1.0f, -1.0f, -1.0f); Color3f lightColour2 = new Color3f(1.0f, 1.0f, 1.0f); Vector3f lightDir2 = new Vector3f(0.0f, 0.0f, -1.0f); Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f); AmbientLight ambientLight1 = new AmbientLight(ambientColour); ambientLight1.setInfluencingBounds(bounds); DirectionalLight directionalLight1 = new DirectionalLight(lightColour1, lightDir1); directionalLight1.setInfluencingBounds(bounds); b.addChild(ambientLight1); b.addChild(directionalLight1); } /** * This builds the content branch of our scene graph. The shape supplied as * a parameter is slightly tilted to reveal its 3D shape. It also uses the * addLights function to add some lights to the scene. * * @param shape * Node that represents the geometry for the content * @return BranchGroup that is the root of the content branch */ protected BranchGroup buildContentBranch(Node shape) { BranchGroup contentBranch = new BranchGroup(); Transform3D rotateCube = new Transform3D(); rotateCube.set(new AxisAngle4d(1.0, 1.0, 0.0, Math.PI / 4.0)); TransformGroup rotationGroup = new TransformGroup(rotateCube); contentBranch.addChild(rotationGroup); rotationGroup.addChild(shape); addLights(contentBranch); return contentBranch; } /** * This defines the appearance with a texture. The texture is loaded from an * external file. * * @return Appearance that uses the texture. */ protected Appearance DefineAppearance() { //Load the texture from the external image file TextureLoader textLoad = new TextureLoader("housebrick.jpg", this); //Access the image from the loaded texture ImageComponent2D textImage = textLoad.getImage(); //Create a two dimensional texture Texture2D texture = new Texture2D(Texture2D.BASE_LEVEL, Texture.RGB, textImage.getWidth(), textImage.getHeight()); //Set the texture from the image loaded texture.setImage(0, textImage); //Create the appearance that will use the texture Appearance app = new Appearance(); app.setTexture(texture); //Define how the texture will be mapped onto the surface //by creating the appropriate texture attributes TextureAttributes textAttr = new TextureAttributes(); textAttr.setTextureMode(TextureAttributes.REPLACE); app.setTextureAttributes(textAttr); app.setMaterial(new Material()); return app; } /** * Build a cube from an IndexedQuadArray. This method creates the vertices * as a set of eight points and the normals as a set of six vectors (one for * each face). The data is then defined such that each vertex has a * different normal associated with it when it is being used for a different * face. The shape is created with texture coordinates so that when the * appearance is set it will use the appearance texture on the surface. * * @return Node that is the shape. */ protected Node buildShape() { IndexedQuadArray indexedCube = new IndexedQuadArray(8, IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS | IndexedQuadArray.TEXTURE_COORDINATE_2, 24); Point3f[] cubeCoordinates = { new Point3f(1.0f, 1.0f, 1.0f), new Point3f(-1.0f, 1.0f, 1.0f), new Point3f(-1.0f, -1.0f, 1.0f), new Point3f(1.0f, -1.0f, 1.0f), new Point3f(1.0f, 1.0f, -1.0f), new Point3f(-1.0f, 1.0f, -1.0f), new Point3f(-1.0f, -1.0f, -1.0f), new Point3f(1.0f, -1.0f, -1.0f) }; Vector3f[] normals = { new Vector3f(0.0f, 0.0f, 1.0f), new Vector3f(0.0f, 0.0f, -1.0f), new Vector3f(1.0f, 0.0f, 0.0f), new Vector3f(-1.0f, 0.0f, 0.0f), new Vector3f(0.0f, 1.0f, 0.0f), new Vector3f(0.0f, -1.0f, 0.0f) }; //Define the texture coordinates. These are defined //as floating point pairs of values that are used to //map the corners of the texture image onto the vertices //of the face. We then define the indices into this //array of values in a similar way to that used for //the vertices and normals. TexCoord2f[] textCoord = { new TexCoord2f(1.0f, 1.0f), new TexCoord2f(0.0f, 1.0f), new TexCoord2f(0.0f, 0.0f), new TexCoord2f(1.0f, 0.0f) }; int coordIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6, 2, 1, 0, 4, 5, 1, 6, 7, 3, 2 }; int normalIndices[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; int textIndices[] = { 0, 1, 2, 3, 3, 0, 1, 2, 1, 2, 3, 0, 1, 2, 3, 0, 3, 0, 1, 2, 1, 2, 3, 0 }; indexedCube.setCoordinates(0, cubeCoordinates); indexedCube.setCoordinateIndices(0, coordIndices); indexedCube.setNormals(0, normals); indexedCube.setNormalIndices(0, normalIndices); indexedCube.setTextureCoordinates(0, 0, textCoord); indexedCube.setTextureCoordinateIndices(0, 0, textIndices); return new Shape3D(indexedCube, DefineAppearance()); } /** * Handles the exit button action to quit the program. */ public void actionPerformed(ActionEvent e) { dispose(); System.exit(0); } public SimpleTexture() { VirtualUniverse myUniverse = new VirtualUniverse(); Locale myLocale = new Locale(myUniverse); myLocale.addBranchGraph(buildViewBranch(myCanvas3D)); myLocale.addBranchGraph(buildContentBranch(buildShape())); setTitle("SimpleTexture"); setSize(400, 400); setLayout(new BorderLayout()); add("Center", myCanvas3D); add("South", myButton); myButton.addActionListener(this); setVisible(true); } public static void main(String[] args) { SimpleTexture st = new SimpleTexture(); }
}
</source>