Unwrap a geometry using a factory

From JReality Wiki
Revision as of 08:59, 13 July 2009 by Paulpeters (Talk | contribs)

(diff) ←Older revision | view current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

Source file: CubeUnwrapped


Run as Java webstart


A geometry in the jReality scene graph does not allow vertex attributes to be specified on a per face basis. This can sometimes lead to problems.

For example, suppose you have a cylindrical surface. Think of it as a piece of paper rolled up and glued together. To achieve correct lighting, one needs to remove the duplicate vertices along the seam when specifying the face indices of the surface. But, suppose at the same time you wish to apply a texture map to the surface. In this case, you need to duplicate the texture coordinates provided along the seam; one set should have the value 0, the other set, belonging to faces on the other side of the seam, should have the value 1.


One solution would be to allow different sets of face indices to be provided to the IndexedFaceSet class. Then one could provide vertex face indices and texture coordinate face indices independently. This is however not possible.


Instead, the IndexedFaceSetFactory has been extended to provide the same functionality. It works like this. In the above example, you would figure out how many texture coordinates you need. Then, extend the list of vertex coordinates to be the same length by duplicating the vertices which are to be identified. Continue to use the original index array in the call to setFaceIndices(), but create a new one with the unwrapped indices (containing indices into the texture coordinates array) and pass this to setUnwrapFaceIndices().


In effect, the original indices will be used to set the vertex coordinate connectivity; the unwrapped indices will be used to calculate the texture coordinate connectivity. The unwrapFaceIndices are written into the created IndexedFaceSet. The faceIndices are used to generate attributes, in particular to generate correct vertex normals and edges. The vertices that represent the same vertex are detected from correspondence in faceIndices and unwrapFaceIndices, and not from the vertex coordinates.


Note that the number of faces must be the same in setFaceCount, setFaceIndices, and setUnwrapFaceIndices().


From this example you may also learn

  • how to add a simple tool that allows user interaction
  • how to add a texture to a geometry


 
package de.jreality.tutorial.geom;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import de.jreality.geometry.IndexedFaceSetFactory;
import de.jreality.math.MatrixBuilder;
import de.jreality.plugin.JRViewer;
import de.jreality.scene.Appearance;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.shader.ImageData;
import de.jreality.shader.Texture2D;
import de.jreality.shader.TextureUtility;
import de.jreality.tools.ActionTool;
import de.jreality.util.Input;
 
public class CubeUnwrapped {
	
	public static void main(String[] args) {
		
		final double [][] vertices  = new double[][] {
			//The 8 vertices of the cube
			{0,  0,  0}, {1,  0,  0}, {1,  1,  0}, {0,  1,  0}, //0-3
			{0,  0,  1}, {1,  0,  1}, {1,  1,  1}, {0,  1,  1}, //4-7
		};
		//The 6 faces of the "wrapped" cube in space
		final int [][] indices = new int [][] {
			{ 0, 1, 2, 3 }, { 7, 6, 5, 4 }, { 0, 1, 5, 4 }, 
			{ 1, 2, 6, 5 }, { 2, 3, 7, 6 }, { 3, 0, 4, 7 } 
		};
		//The 6 faces of the usual unwrapping of the cube 
		final int [][] unwrapIndices = new int [][] {
			//The first 3 faces are connected to each other as on the cube,
			//so their indices stay the same
			{ 0, 1, 2, 3 }, { 7, 6, 5, 4 }, { 0, 1, 5, 4 },
			//The following 3 faces are not connected
			//to some of the faces they are connected on the cube.
			//This is done using vertex indices 8-13.
			{ 1, 8, 9, 5 }, { 8, 10, 11, 9 }, { 12, 0, 4, 13 }
		};
		//The texture coordinates of the vertices. Make a sketch of them and
		//you will see the unwrapped cube
		final double [][] unwrapTextureCoordinates = new double[][] {
			{ .25, .5},{ .5, .5},{ .5, .75},{ .25, .75}, //0-3
			{ .25, .25 },{ .5, .25 },{ .5, .0 },{ .25, .0 },//4-7
			{ .75, .5 },{ .75, .25 },{ 1., .5 },{ 1., .25 },{ 0., .5 },{ 0., .25 },//8-13
		};
	
		final IndexedFaceSetFactory ifsf = new IndexedFaceSetFactory();
		
		// set all 14 vertices and their coordinates
		ifsf.setVertexCount( 14 );
		// We need to unwrap the vertex coordinates for the 14 vertices
		// This is done by unwrapVertexAttributes, which determines the correspondence of vertices
		// from correspondence in indices and unwrapIndices. For the cube:
		// 8->2, 9->6, 10->3, 11->7, 12 -> 3, 13 -> 7	 
 
		ifsf.setVertexCoordinates( IndexedFaceSetFactory.unwrapVertexAttributes(vertices, indices, unwrapIndices, 14));
		ifsf.setVertexTextureCoordinates(unwrapTextureCoordinates);
		
		// The 6 faces of the cube, use the "wrapped" face indices
		ifsf.setFaceCount( 6 );	
		ifsf.setFaceIndices( indices );
 
		// Generation of vertexNormals and edges is according to the wrapped indices rather than
		// the unwrapped
		ifsf.setGenerateVertexNormals( true );
		ifsf.setGenerateEdgesFromFaces( true );
		// The edge labels indicate that edges are doubled only in mode 2 below
		ifsf.setGenerateEdgeLabels(true);
		// Crude documentation 
		ifsf.setVertexLabels(new String[]{"","","","","","    Klick with middle mouse","","","","","","","",""});
		ifsf.update();
		
		SceneGraphComponent sgc = new SceneGraphComponent("scene");
		
		/* An action tool that cycles through 3 modes when the cube 
		 * is clicked with the middle mouse button 
		 * mode0: the texture cube with texture jumps, because no unwrapped indices are set
		 * mode1: the nicely textured unwrapped cube
		 * mode3: also nicely textured unwrapped cube, but now edges are doubled 
		 * and broken vertex normals
		 */
		ActionTool tool = new ActionTool("PrimaryMenu");
		tool.addActionListener(new ActionListener(){
			int mode=0;
			public void actionPerformed(ActionEvent e) {
				mode = (mode +1) % 3;
				if (mode==0) {
					ifsf.setFaceIndices( indices ); 
					ifsf.setUnwrapFaceIndices((int[][]) null); 
					ifsf.setVertexLabels(new String[]{"","","","","","    NO unwrapped face indices","","","","","","","",""});
				}
				if (mode==1) {
					ifsf.setFaceIndices( indices ); 
					ifsf.setUnwrapFaceIndices( unwrapIndices ); 
					ifsf.setVertexLabels(new String[]{"","","","","","    wrapped and UNWRAPPED face indices","","","","","","","",""});
					}
				if (mode==2) {
					ifsf.setFaceIndices( unwrapIndices ); 
					ifsf.setUnwrapFaceIndices( (int[][]) null ); 
					ifsf.setVertexLabels(new String[]{"","","","","","    DOUBLED edges and BROKEN vertex normals, unwrapped indices","","","","","","","",""});
					}
				ifsf.update();
			}
		});
		sgc.addTool(tool);
		sgc.setGeometry(ifsf.getIndexedFaceSet());
		
		// Add the texture
		sgc.setAppearance(new Appearance());
		Texture2D tex;
		try{
			tex=TextureUtility.createTexture(
				sgc.getAppearance(),       
				"polygonShader", 
				ImageData.load(Input.getInput("de/jreality/tutorial/geom/black_cross.png")),
				false);
			tex.setTextureMatrix(MatrixBuilder.euclidean().scale(12).getMatrix());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// scale the cube in order to have relatively smaller labels
		MatrixBuilder.euclidean().scale(4).assignTo(sgc);
 
		JRViewer.display(sgc);
	}
 
}


Previous: Attach face colors using a factory Developer Tutorial: Contents Next: Use a quad mesh factory