Package examples.syn3d.plugin.java3d

Source Code of examples.syn3d.plugin.java3d.XYZResultNodeJava3D

/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2004, by :
*     Corporate:
*         EADS Corporate Research Center
*     Individual:
*         Nicolas Brodu
*
* $Id: XYZResultNodeJava3D.java,v 1.2 2005/09/02 12:18:02 ogor Exp $
*
* Changes
* -------
* 01-Jun-2004 : Creation date (NB);
*
*/
package examples.syn3d.plugin.java3d;

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.List;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.J3DBuffer;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TriangleArray;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import syn3d.base.ActiveNode;
import syn3d.nodes.java3d.SceneNodeJava3D;
import syn3d.nodes.java3d.ShapeNodeJava3D;

import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.picking.PickIntersection;
import com.sun.j3d.utils.picking.PickResult;
import com.sun.j3d.utils.picking.PickTool;


/**
* This specialisation of XYZResult node creates 3D objects corresponding
* to the file content, using Java3D.
*
* It also handles picking, as an example how to do this very useful
* feature with Java3D.
*
* @see XYZResultNode
*/
public class XYZResultNodeJava3D extends XYZResultNode {

  /** This node creates a tranform group so as to easily center and scale the shape */
  protected TransformGroup transformGroup;

  /** Used internally, the file name is displayed in the tree */
  protected String fileName;
 
  /**
   * The file may contain multiple variables per point (temperature, voltage...), but
   * only one can be displayed at a given time. This gives the current variable number,
   * it should be greater than 3 (since the first variables in the file are supposed to
   * be X, Y, Z).
   */
  protected int currentVariable;
 
  /**
   * This specialisation of XYZResultNode creates a Java3D tranform group
   * to hold the shape defined by the data in the file.
   * A transformation will then be applied to scale and center the shape. 
   */
  public XYZResultNodeJava3D(ActiveNode parent) {
    super(parent);
        transformGroup = new TransformGroup();
        transformGroup.setUserData(this);
        transformGroup.setPickable(true);
        transformGroup.setCapability(TransformGroup.ALLOW_BOUNDS_READ);
        transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transformGroup.setCapability(Group.ALLOW_CHILDREN_EXTEND);
        transformGroup.setBoundsAutoCompute(true);
        if (parent.get3DObject() instanceof Group) {
        SceneNodeJava3D.detach(this);
          // Parent should have allowed children extend in Syn3D, no need to detach the scene
            ((Group)parent.get3DObject()).addChild(transformGroup);
        SceneNodeJava3D.attach(this);
        }
  }
 
  /* (non-Javadoc)
   * @see syn3d.base.ActiveNode#get3DObject()
   */
  public Object get3DObject() {
    return transformGroup;
  }
 
  /* (non-Javadoc)
   * @see syn3d.base.ActiveNode#remove()
   */
  public void remove() {
       SceneNodeJava3D.removeChildFromParentGroup(transformGroup);
  }
 
  // delegate action to children if a file is already loaded
  public List getActions() {
    // delegate action to children if a file is already loaded
    if (children.size()>0) {
      return ((ActiveNode)children.get(0)).getActions();
    }
    return super.getActions();
  }
 
  // delegate action to children if a file is already loaded
  public void doAction(Object action) {
    if (children.size()>0) {
      ((ActiveNode)children.get(0)).doAction(action);
      return;
    }
    super.doAction(action);
  }
 
  // Overload parent loading to add the creation of a shape using Java3D
  protected void load(File file) throws IOException {

    // parent method loads the data from the file
    super.load(file);
   
    // Create the geometry
    // Use NIO buffers in Java3D to increase performances
    // => JVM buffers needs to be copied twice each rendering (JVM => memory => video RAM)
    // => NIO buffers are already in main memory
    // In addition to improving the performance, this also dramatically decreases the
    // lag effect of loading a scene larger than the video RAM. Indeed, when using JVM
    // memory, the sanction is immediate : the scene becomes quite impossible to manipulate.
    // With NIO buffers, there is a progressive degradation of performances when the scene
    // doesn't fit entirely in the 3D card RAM.
    DoubleBuffer nioCoords = ByteBuffer.allocateDirect(triangles.length*3 * 8).order(ByteOrder.nativeOrder()).asDoubleBuffer();
    FloatBuffer nioColors = ByteBuffer.allocateDirect(triangles.length*3 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    FloatBuffer nioNormals = ByteBuffer.allocateDirect(triangles.length * 3 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();

    // Set up the coordinates loaded by the parent method into the
    // NIO buffers.
    // This also expand the geometry. The file is described in indexed mode : triangles
    // are defined by indexing points.
    // Java3D also has an indexed mode, and using it would be immediate.
    // Unfortunately, when using an indexed mode, the triangle vertices are shared by definition.
    // It is thus not possible to change the values for the vertices for only one triangle
    // For example, when highlighting a triangle, it is necessary to modify the colors of
    // this triangle only, not its neighbors.
    // On the other hand, if picking/highlighting was done on vertices only, then modifying
    // all triangles using this vertex could be a good idea.
    // In this example, a triangle is picked, and the closest vertex is also shown, but
    // highlighted only in this triangle. This is not that complicated, but shows potential
    // problems. One should thus be able to easily adapt this example to simpler cases.
    for (int i=0; i<triangles.length; i+=3) {
      int pt = triangles[i];
      nioCoords.put(data[0][pt]);
      nioCoords.put(data[1][pt]);
      nioCoords.put(data[2][pt]);
      pt = triangles[i+1];
      nioCoords.put(data[0][pt]);
      nioCoords.put(data[1][pt]);
      nioCoords.put(data[2][pt]);
      pt = triangles[i+2];
      nioCoords.put(data[0][pt]);
      nioCoords.put(data[1][pt]);
      nioCoords.put(data[2][pt]);
    }

    // Create a triangle array using NIO.
    // Unfortunately, Sun picking utilities don't handle NIO very well, thus
    // we need to overload a method
    TriangleArray ta = new TriangleArray(triangles.length, GeometryArray.BY_REFERENCE|GeometryArray.USE_NIO_BUFFER|GeometryArray.COORDINATES|GeometryArray.NORMALS|GeometryArray.COLOR_3) {
      public double[] getCoordRefDouble() {
        // When picking, tests have shown that NIO buffer copy has no noticeable impact
        // on performance. So, it is actually better to copy the array each time the picking is
        // done than to hog memory with a permanent copy.
        // It is also still better to copy the buffer only when picking rather than at each rendering
        double[] array = new double[getVertexCount()*3];
        DoubleBuffer db = (DoubleBuffer)super.getCoordRefBuffer().getBuffer();
        db.rewind();
        db.get(array); // optimized get
        return array;
      }
    };
      ta.setCoordRefBuffer(new J3DBuffer(nioCoords));

      // Use Java3D utilities to compute normals
    GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
    double[] coords = new double[data[0].length*3];
    nioCoords.position(0);
    nioCoords.get(coords);
    gi.setCoordinates(coords);
        // generate normals
        NormalGenerator ng = new NormalGenerator();
        ng.generateNormals(gi);
        Vector3f[] n3f = gi.getNormals();
        int[] ni = gi.getNormalIndices();
        float[] tmp = new float[3];
    for (int i=0; i<ni.length; ++i) {
      n3f[ni[i]].get(tmp);
      nioNormals.put(tmp);
    }
    ta.setNormalRefBuffer(new J3DBuffer(nioNormals));
       
    // Setup color buffer
    ta.setColorRefBuffer(new J3DBuffer(nioColors));
      ta.setCapability(TriangleArray.ALLOW_COLOR_WRITE);
      ta.setCapabilityIsFrequent(TriangleArray.ALLOW_COLOR_WRITE);
     
      Shape3D shape3d = new Shape3D(ta);
     
    // The appearance of a shape has many interesting features.
      // In particular, in case one wants to display lines (edges) over the shape,
      // it is possible to set a polygon offset to the triangles. This offset is in fact
      // a distance in the Z-buffer => an offset of 1 for example on the triangles,
      // will place them behind the edges. @see PolygonAttributes.setPolygonOffset,
      // and the code in syn3d.nodes.java3d.Shape3DJava3D
      // Here the shape is created without an appearance and it is managed
      // by ShapeNodeJava3D. See this class for more information
        shape3d.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
        shape3d.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);

        // The shape will be pickable, which means the user can click on the shape in 3D
        // space from the 2D window
        PickTool.setCapabilities(shape3d,PickTool.INTERSECT_FULL);
        shape3d.setCapability(Shape3D.ALLOW_PICKABLE_READ);
        shape3d.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);
    shape3d.setPickable(true);

        // Create a node in the JTree for this shape
    new ShapeNodeJava3D(this, shape3d);

    // Syn3D picking code uses the user data of a Java3D object to find back a node
        // that handles the picking. Let's declare this object as the handler instead of
    // ShapeNodeJava3D
    shape3d.setUserData(this);
   
    // scale the shape to have a reasonable size
    // and translate it to the origin
    Bounds bounds = transformGroup.getBounds();
    if (bounds instanceof BoundingSphere) {
      Point3d center = new Point3d();
      ((BoundingSphere)bounds).getCenter(center);
      Transform3D tr = new Transform3D();
      tr.setScale(10.0/((BoundingSphere)bounds).getRadius());
      Transform3D tr2 = new Transform3D();
      center.negate();
      tr2.setTranslation(new Vector3f(center));
      tr.mul(tr2);
      transformGroup.setTransform(tr);
    }

    // Cleanup file name, use it for this node
    fileName = file.getName();
    fileName = fileName.substring(0, fileName.indexOf('.'));

    setName(fileName);
   
    // now set up the colors using one of the variables => first one by default
    showResult(0);
  }


  // see parent comments.
  // This method is called directly by the plugin on key press, as an example how to do it
  public void showResult(int num) {

    // Get the shape back from the children list. Ignore case when there is no file loaded
    if (children.size()<1) return;
    Shape3D shape3d = (Shape3D)((ShapeNodeJava3D)children.get(0)).get3DObject();
   
    // Find the color buffer
    FloatBuffer colors = (FloatBuffer)(((TriangleArray)shape3d.getGeometry()).getColorRefBuffer().getBuffer());
   
    // helper variables
    int d = num+3;
    if (d>=names.length) return; // ignore keypressed events for unknown variables
    currentVariable = d;
   
    float[] color = new float[3];
   
    // Update the buffer
    for (int i=0; i<triangles.length; i+=3) {
      getColorForValue(data[d][triangles[i]]).getRGBColorComponents(color);
      colors.position(i*3);
      colors.put(color);
      getColorForValue(data[d][triangles[i+1]]).getRGBColorComponents(color);
      colors.position((i+1)*3);
      colors.put(color);
      getColorForValue(data[d][triangles[i+2]]).getRGBColorComponents(color);
      colors.position((i+2)*3);
      colors.put(color);
    }

    // Update the name in the tree to show the selected variable
    ((ActiveNode)children.get(0)).setName(names[d]);
   
    // Syn3D internal event system. Handles refreshing of the scene and the JTree, when
    // they are visible.
    notifyInternalChange();
  }
 
  // see comments on super implementation
  // To make it short : this is the method receiving picking events, for the 3D shape that
  // declared this object as its user data.
  public void highlight(boolean on, Object parameter) {
   
    // When picking, the parameter is always a PickResult
    // When selecting from the JTree, the parameter is not a PickResult
    // => ignore the JTree selection of the whole shape in this example
    if (!(parameter instanceof PickResult)) return;

    PickResult result = (PickResult)parameter;
    result.setFirstIntersectOnly(true);
    PickIntersection pi = result.getIntersection(0);

    // indices of the picked triangle
    // should always be triangle at this point
    // Indices are set to vertex indices, as this is not an Index Geometry object
    int[] idx = pi.getPrimitiveColorIndices();
    FloatBuffer colors = (FloatBuffer)(pi.getGeometryArray()).getColorRefBuffer().getBuffer();
    float[] color = new float[3];
   
    // The index returned by this method is relative the the array returned above,
    // not the shape geometry array!
    int closest = pi.getClosestVertexIndex();

    // This method is called both when highlighting the shape and when de-selecting it
    // => handles both, so as to restore the original colors
    // Tip : try to hold CTRL while picking elements, for multiple selection
    // => this method is called exactly once for each element state change
    if (on) {
      // Choose a color for highlighting that is never used usually
      //Color.getHSBColor(5f/6f,1,1).getRGBColorComponents(color);
     
      // Let's do something more useful as an example : also highlight the
      // closest point in the picked triangle using the color saturation.
      // Tip: when using indexed geometry, one could change only the color of the
      // closest point and this would affect all triangles sharing it. On the other
      // hand, many applications need the selection of a triangle (or quad), and
      // don't want side effects on neighboring triangles.
      // This method gives a way to do both point and tiangle selection as an example.
      // It should be quite straightforward to adapt according to the needs of a
      // real application.
      getColorForValue(data[currentVariable][triangles[idx[0]]],(closest==0) ? 0.1f : 0.5f).getRGBColorComponents(color);
      colors.position(idx[0]*3);
      colors.put(color);
      getColorForValue(data[currentVariable][triangles[idx[1]]],(closest==1) ? 0.1f : 0.5f).getRGBColorComponents(color);
      colors.position(idx[1]*3);
      colors.put(color);
      getColorForValue(data[currentVariable][triangles[idx[2]]],(closest==2) ? 0.1f : 0.5f).getRGBColorComponents(color);
      colors.position(idx[2]*3);
      colors.put(color);
     
      // Also display the selected values to standard output
      System.out.print("Values for the picked triangle ("+names[currentVariable]+"): ");
      System.out.print(data[currentVariable][triangles[idx[0]]]);
      System.out.print(closest==0 ? " *  " : "    " );
      System.out.print(data[currentVariable][triangles[idx[1]]]);
      System.out.print(closest==1 ? " *  " : "    " );
      System.out.print(data[currentVariable][triangles[idx[2]]]);
      System.out.print(closest==2 ? " *  " : "    " );
      System.out.println(" with * = closest point");
    }
    else {
      // restore original colors
      getColorForValue(data[currentVariable][triangles[idx[0]]]).getRGBColorComponents(color);
      colors.position(idx[0]*3);
      colors.put(color);
      getColorForValue(data[currentVariable][triangles[idx[1]]]).getRGBColorComponents(color);
      colors.position(idx[1]*3);
      colors.put(color);
      getColorForValue(data[currentVariable][triangles[idx[2]]]).getRGBColorComponents(color);
      colors.position(idx[2]*3);
      colors.put(color);
    }

    // Syn3D event propagation, refresh the scene, etc...
    super.highlight(on,parameter);
  }

  // Helper method: Return the pure color for the given value using the currently selected
  // variable.
  protected Color getColorForValue(double value) {
    return getColorForValue(value, 1f);
  }
 
  // Helper method: Return the color for the given value using the currently selected variable
  // but also apply a saturation modifier.
  protected Color getColorForValue(double value, float saturation) {
    double hue = 2.0 * (max[currentVariable] - value) / ( 3.0 * (max[currentVariable] - min[currentVariable]));
    return Color.getHSBColor((float)hue, saturation, 1.0f);
  }
 
}
TOP

Related Classes of examples.syn3d.plugin.java3d.XYZResultNodeJava3D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.