001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.io.euclidean.threed.obj; 018 019import java.io.Reader; 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023 024import org.apache.commons.geometry.euclidean.threed.Vector3D; 025import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh; 026import org.apache.commons.geometry.euclidean.threed.mesh.TriangleMesh; 027import org.apache.commons.numbers.core.Precision; 028 029/** Class for reading OBJ content as a {@link TriangleMesh triangle mesh}. 030 */ 031public class ObjTriangleMeshReader extends AbstractObjPolygonReader { 032 033 /** Object used to construct the mesh. */ 034 private final SimpleTriangleMesh.Builder meshBuilder; 035 036 /** List of normals discovered in the input. */ 037 private final List<Vector3D> normals = new ArrayList<>(); 038 039 /** Construct a new instance that reads OBJ content from the given reader. 040 * @param reader reader to read from 041 * @param precision precision context used to compare floating point numbers 042 */ 043 public ObjTriangleMeshReader(final Reader reader, final Precision.DoubleEquivalence precision) { 044 super(reader); 045 046 this.meshBuilder = SimpleTriangleMesh.builder(precision); 047 } 048 049 /** Return a {@link TriangleMesh triangle mesh} constructed from all of the OBJ content 050 * from the underlying reader. Non-triangle faces are converted to triangles using a simple 051 * triangle fan. All vertices present in the OBJ content are also present in the returned mesh, 052 * regardless of whether or not they are used in a face. 053 * @return triangle mesh containing all data from the OBJ content 054 * @throws IllegalStateException if data format error occurs 055 * @throws java.io.UncheckedIOException if an I/O error occurs 056 */ 057 public TriangleMesh readTriangleMesh() { 058 PolygonObjParser.Face face; 059 Vector3D definedNormal; 060 Iterator<PolygonObjParser.VertexAttributes> attrs; 061 while ((face = readFace()) != null) { 062 // get the face attributes in the proper counter-clockwise orientation 063 definedNormal = face.getDefinedCompositeNormal(normals::get); 064 attrs = face.getVertexAttributesCounterClockwise(definedNormal, meshBuilder::getVertex).iterator(); 065 066 // add the face vertices using a triangle fan 067 final int p0 = attrs.next().getVertexIndex(); 068 int p1 = attrs.next().getVertexIndex(); 069 int p2; 070 071 while (attrs.hasNext()) { 072 p2 = attrs.next().getVertexIndex(); 073 074 meshBuilder.addFace(p0, p1, p2); 075 076 p1 = p2; 077 } 078 } 079 080 return meshBuilder.build(); 081 } 082 083 /** {@inheritDoc} */ 084 @Override 085 protected void handleVertex(final Vector3D vertex) { 086 meshBuilder.addVertex(vertex); 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 protected void handleNormal(final Vector3D normal) { 092 normals.add(normal); 093 } 094}