1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts.verifier.sensors.sixdof.Renderer.RenderUtils; 17 18 import android.content.res.Resources; 19 import android.util.Log; 20 21 import java.io.BufferedReader; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.InputStreamReader; 25 import java.util.ArrayList; 26 import java.util.StringTokenizer; 27 28 /** 29 * Imports an .obj file into an ObjectData class so that it can be rendered by OpenGl. Based on the 30 * .obj importer from Rajawali. 31 */ 32 public class ObjImporter { 33 protected static final String TAG = "ObjImporter"; 34 protected static final String VERTEX = "v"; 35 protected static final String FACE = "f"; 36 protected static final String NORMAL = "vn"; 37 38 protected static class ObjIndexData { 39 40 public ArrayList<Integer> vertexIndices; 41 public ArrayList<Integer> texCoordIndices; 42 public ArrayList<Integer> colorIndices; 43 public ArrayList<Integer> normalIndices; 44 45 public ObjIndexData() { 46 vertexIndices = new ArrayList<Integer>(); 47 texCoordIndices = new ArrayList<Integer>(); 48 colorIndices = new ArrayList<Integer>(); 49 normalIndices = new ArrayList<Integer>(); 50 } 51 } 52 53 public static class ObjectData { 54 float[] mVertexData; 55 float[] mNormalsData; 56 int[] mIndicesData; 57 58 protected ObjectData(float[] vertexData, float[] normalsData, int[] indicesData) { 59 mVertexData = vertexData; 60 mNormalsData = normalsData; 61 mIndicesData = indicesData; 62 } 63 64 public float[] getVertexData() { 65 return mVertexData; 66 } 67 68 public float[] getNormalsData() { 69 return mNormalsData; 70 } 71 72 public int[] getIndicesData() { 73 return mIndicesData; 74 } 75 } 76 77 public static ObjectData parse(Resources mResources, int mResourceId) { 78 BufferedReader buffer = null; 79 InputStream fileIn = mResources.openRawResource(mResourceId); 80 buffer = new BufferedReader(new InputStreamReader(fileIn)); 81 String line; 82 ObjIndexData currObjIndexData = new ObjIndexData(); 83 84 ArrayList<Float> vertices = new ArrayList<Float>(); 85 ArrayList<Float> texCoords = new ArrayList<Float>(); 86 ArrayList<Float> normals = new ArrayList<Float>(); 87 88 try { 89 while ((line = buffer.readLine()) != null) { 90 // Skip comments and empty lines. 91 if (line.length() == 0 || line.charAt(0) == '#') 92 continue; 93 StringTokenizer parts = new StringTokenizer(line, " "); 94 int numTokens = parts.countTokens(); 95 96 if (numTokens == 0) 97 continue; 98 String type = parts.nextToken(); 99 100 if (type.equals(VERTEX)) { 101 vertices.add(Float.parseFloat(parts.nextToken())); 102 vertices.add(Float.parseFloat(parts.nextToken())); 103 vertices.add(Float.parseFloat(parts.nextToken())); 104 } else if (type.equals(FACE)) { 105 boolean isQuad = numTokens == 5; 106 int[] quadvids = new int[4]; 107 int[] quadtids = new int[4]; 108 int[] quadnids = new int[4]; 109 110 boolean emptyVt = line.indexOf("//") > -1; 111 if (emptyVt) line = line.replace("//", "/"); 112 113 parts = new StringTokenizer(line); 114 115 parts.nextToken(); 116 StringTokenizer subParts = new StringTokenizer(parts.nextToken(), "/"); 117 int partLength = subParts.countTokens(); 118 119 boolean hasuv = partLength >= 2 && !emptyVt; 120 boolean hasn = partLength == 3 || (partLength == 2 && emptyVt); 121 int idx; 122 123 for (int i = 1; i < numTokens; i++) { 124 if (i > 1) 125 subParts = new StringTokenizer(parts.nextToken(), "/"); 126 idx = Integer.parseInt(subParts.nextToken()); 127 128 if (idx < 0) idx = (vertices.size() / 3) + idx; 129 else idx -= 1; 130 if (!isQuad) 131 currObjIndexData.vertexIndices.add(idx); 132 else 133 quadvids[i - 1] = idx; 134 if (hasuv) { 135 idx = Integer.parseInt(subParts.nextToken()); 136 if (idx < 0) idx = (texCoords.size() / 2) + idx; 137 else idx -= 1; 138 if (!isQuad) 139 currObjIndexData.texCoordIndices.add(idx); 140 else 141 quadtids[i - 1] = idx; 142 } 143 if (hasn) { 144 idx = Integer.parseInt(subParts.nextToken()); 145 if (idx < 0) idx = (normals.size() / 3) + idx; 146 else idx -= 1; 147 if (!isQuad) 148 currObjIndexData.normalIndices.add(idx); 149 else 150 quadnids[i - 1] = idx; 151 } 152 } 153 154 if (isQuad) { 155 int[] indices = new int[]{0, 1, 2, 0, 2, 3}; 156 157 for (int i = 0; i < 6; ++i) { 158 int index = indices[i]; 159 currObjIndexData.vertexIndices.add(quadvids[index]); 160 currObjIndexData.texCoordIndices.add(quadtids[index]); 161 currObjIndexData.normalIndices.add(quadnids[index]); 162 } 163 } 164 } else if (type.equals(NORMAL)) { 165 normals.add(Float.parseFloat(parts.nextToken())); 166 normals.add(Float.parseFloat(parts.nextToken())); 167 normals.add(Float.parseFloat(parts.nextToken())); 168 } 169 } 170 171 buffer.close(); 172 } catch (IOException e) { 173 Log.e(TAG, "failed to parse", e); 174 } 175 176 int i; 177 float[] aVertices = new float[currObjIndexData.vertexIndices.size() * 3]; 178 float[] aNormals = new float[currObjIndexData.normalIndices.size() * 3]; 179 int[] aIndices = new int[currObjIndexData.vertexIndices.size()]; 180 181 for (i = 0; i < currObjIndexData.vertexIndices.size(); ++i) { 182 int faceIndex = currObjIndexData.vertexIndices.get(i) * 3; 183 int vertexIndex = i * 3; 184 try { 185 aVertices[vertexIndex] = vertices.get(faceIndex); 186 aVertices[vertexIndex + 1] = vertices.get(faceIndex + 1); 187 aVertices[vertexIndex + 2] = vertices.get(faceIndex + 2); 188 aIndices[i] = i; 189 } catch (ArrayIndexOutOfBoundsException e) { 190 Log.d(TAG, "Obj array index out of bounds: " + vertexIndex + ", " + faceIndex); 191 } 192 } 193 for (i = 0; i < currObjIndexData.normalIndices.size(); ++i) { 194 int normalIndex = currObjIndexData.normalIndices.get(i) * 3; 195 int ni = i * 3; 196 if (normals.size() == 0) { 197 Log.e(TAG, "There are no normals specified for this model. " + 198 "Please re-export with normals."); 199 throw new RuntimeException("[" + TAG + "] There are no normals specified " + 200 "for this model. Please re-export with normals."); 201 } 202 aNormals[ni] = normals.get(normalIndex); 203 aNormals[ni + 1] = normals.get(normalIndex + 1); 204 aNormals[ni + 2] = normals.get(normalIndex + 2); 205 } 206 207 return new ObjectData(aVertices, aNormals, aIndices); 208 } 209 } 210