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 }