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 }