View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.geometry.io.euclidean.threed.obj;
18  
19  import java.io.Reader;
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.apache.commons.geometry.euclidean.threed.Vector3D;
25  import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh;
26  import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh;
27  import org.apache.commons.numbers.core.Precision;
28  
29  /** Class for reading OBJ content as a {@link TriangleMesh triangle mesh}.
30   */
31  public class ObjTriangleMeshReader extends AbstractObjPolygonReader {
32  
33      /** Object used to construct the mesh. */
34      private final SimpleTriangleMesh.Builder meshBuilder;
35  
36      /** List of normals discovered in the input. */
37      private final List<Vector3D> normals = new ArrayList<>();
38  
39      /** Construct a new instance that reads OBJ content from the given reader.
40       * @param reader reader to read from
41       * @param precision precision context used to compare floating point numbers
42       */
43      public ObjTriangleMeshReader(final Reader reader, final Precision.DoubleEquivalence precision) {
44          super(reader);
45  
46          this.meshBuilder = SimpleTriangleMesh.builder(precision);
47      }
48  
49      /** Return a {@link TriangleMesh triangle mesh} constructed from all of the OBJ content
50       * from the underlying reader. Non-triangle faces are converted to triangles using a simple
51       * triangle fan. All vertices present in the OBJ content are also present in the returned mesh,
52       * regardless of whether or not they are used in a face.
53       * @return triangle mesh containing all data from the OBJ content
54       * @throws IllegalStateException if data format error occurs
55       * @throws java.io.UncheckedIOException if an I/O error occurs
56       */
57      public TriangleMesh readTriangleMesh() {
58          PolygonObjParser.Face face;
59          Vector3D definedNormal;
60          Iterator<PolygonObjParser.VertexAttributes> attrs;
61          while ((face = readFace()) != null) {
62              // get the face attributes in the proper counter-clockwise orientation
63              definedNormal = face.getDefinedCompositeNormal(normals::get);
64              attrs = face.getVertexAttributesCounterClockwise(definedNormal, meshBuilder::getVertex).iterator();
65  
66              // add the face vertices using a triangle fan
67              final int p0 = attrs.next().getVertexIndex();
68              int p1 = attrs.next().getVertexIndex();
69              int p2;
70  
71              while (attrs.hasNext()) {
72                  p2 = attrs.next().getVertexIndex();
73  
74                  meshBuilder.addFace(p0, p1, p2);
75  
76                  p1 = p2;
77              }
78          }
79  
80          return meshBuilder.build();
81      }
82  
83      /** {@inheritDoc} */
84      @Override
85      protected void handleVertex(final Vector3D vertex) {
86          meshBuilder.addVertex(vertex);
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      protected void handleNormal(final Vector3D normal) {
92          normals.add(normal);
93      }
94  }