How to write and register a custom reader

From JReality Wiki
Jump to: navigation, search

We will demonstrate how to implement and register a custom file reader for a the simple format in this sample file:


# sample data file with two triangles
#
# the first triangle:
0.0 0.0 0.0
1.0 0.0 0.0
1.0 1.0 0.0
# the second triangle:
0.0 0.0 0.0
1.0 1.0 0.0
0.0 1.0 0.0


Our file format contains one vertex in each line. The x y z coordinates are text seperated by a white space. Lines starting with a # will be ignored.


A reader for jreality is a class that creates a SceneGraphComponent from a resource. The resource can be a file or a URL or also an arbitrary InputStream. The resource is wrapped into de.jreality.util.Input which provides a common API for all kinds of resources. It is also used for loading textures.


Any reader for jreality implements the interface de.jreality.reader.SceneReader or extends the abstract implementation de.jreality.reader.AbstractReader. When subclassing the AbstractReader, one only has to implement the actual file reading in the setInput(Input input) method, and assign the file content to the root field.


The Code:

public void setInput(Input input) throws IOException {
	super.setInput(input);
	
	// read all vertices into a list:
	ArrayList<double[]> vertices = new ArrayList<double[]>();
	LineNumberReader lnr = new LineNumberReader(new BufferedReader(input.getReader()));
	for (String line = lnr.readLine(); line != null; line = lnr.readLine()) {
		line = line.trim();
		if (line.startsWith("#")) continue;
		StringTokenizer st = new StringTokenizer(line);
		if (st.countTokens() != 3) {
			System.out.println("illegal line: "+line);
			continue;
		}
		double x = Double.parseDouble(st.nextToken());
		double y = Double.parseDouble(st.nextToken());
		double z = Double.parseDouble(st.nextToken());
		vertices.add(new double[]{x, y, z});
	}
	
	// convert vertex list into double[][] array:
	int nVertices = vertices.size();
	double[][] verts = vertices.toArray(new double[nVertices][]);
	
	// create triangle indices
	int nTriangles = vertices.size()/3;
	int[] indices = new int[nTriangles*3];
	for (int i=0, n=nTriangles*3; i<n; i++) indices[i]=i;
	
	// create the geometry:
	IndexedFaceSetFactory ifsf = new IndexedFaceSetFactory();
	ifsf.setGenerateFaceNormals(true);
	ifsf.setGenerateEdgesFromFaces(true);
	ifsf.setVertexCount(nVertices);
	ifsf.setFaceCount(nTriangles);
	ifsf.setVertexCoordinates(verts);
	ifsf.setFaceIndices(indices, 3);
	ifsf.update();
	
	// assign the root component of the reader and set the geometry:
	root = new SceneGraphComponent("demo-file");
	root.setGeometry(ifsf.getIndexedFaceSet());
}


With this reader implementation we can already load demo-files via java code like:

DemoReader reader = new DemoReader();
SceneGraphComponent content = reader.read(new File("sample.demo");


But the file loader dialog of the VRViewer is not aware of the new file format that can be read now. Therefore, we register our new reader class for a file format, and register a file ending (or maybe more than one) that determines the format. We choose as a format name "DEMO" and associate it to the file ending ".demo":

// register the reader class for the DEMO-format
Readers.registerReader("DEMO", DemoReader.class);
// register the file ending .demo for files containing DEMO-format data
Readers.registerFileEndings("DEMO", "demo");


Now we can load demo files via the file loader dialog or also with the Readers-class, which automatically chooses the reader class for the format, determined by the file ending:

URL fileUrl = ReaderExample.class.getResource("samplefile.demo");
SceneGraphComponent content = Readers.read(fileUrl);