How to write and register a custom reader
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);