206 lines
7.0 KiB
Java
206 lines
7.0 KiB
Java
|
// Copyright 2021 The MediaPipe Authors.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||
|
|
||
|
import java.io.BufferedWriter;
|
||
|
import java.io.File;
|
||
|
import java.io.FileFilter;
|
||
|
import java.io.FileOutputStream;
|
||
|
import java.io.OutputStream;
|
||
|
import java.io.OutputStreamWriter;
|
||
|
import java.io.PrintWriter;
|
||
|
import java.nio.ByteBuffer;
|
||
|
import java.nio.ByteOrder;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Arrays;
|
||
|
|
||
|
/**
|
||
|
* Class for running desktop-side parsing/packing routines on .obj AR assets. Usage: ObjParser
|
||
|
* --input_dir=[INPUT_DIRECTORY] --output_dir=[OUTPUT_DIRECTORY] where INPUT_DIRECTORY is the folder
|
||
|
* with asset obj files to process, and OUTPUT_DIRECTORY is the folder where processed asset uuu
|
||
|
* file should be placed.
|
||
|
*
|
||
|
* <p>NOTE: Directories are assumed to be absolute paths.
|
||
|
*/
|
||
|
public final class ObjParserMain {
|
||
|
// Simple FileFilter implementation to let us walk over only our .obj files in a particular
|
||
|
// directory.
|
||
|
private static final class ObjFileFilter implements FileFilter {
|
||
|
ObjFileFilter() {
|
||
|
// Nothing to do here.
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean accept(File file) {
|
||
|
return file.getName().endsWith(".obj");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// File extension for binary output files; tagged onto end of initial file extension.
|
||
|
private static final String BINARY_FILE_EXT = ".uuu";
|
||
|
private static final String INPUT_DIR_FLAG = "--input_dir=";
|
||
|
private static final String OUTPUT_DIR_FLAG = "--output_dir=";
|
||
|
private static final float DEFAULT_VERTEX_SCALE_FACTOR = 30.0f;
|
||
|
private static final double NS_TO_SECONDS = 1e9;
|
||
|
|
||
|
public final PrintWriter writer;
|
||
|
|
||
|
public ObjParserMain() {
|
||
|
super();
|
||
|
this.writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
|
||
|
}
|
||
|
|
||
|
// Simple overridable logging function.
|
||
|
protected void logString(String infoLog) {
|
||
|
writer.println(infoLog);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Main program logic: parse command-line arguments and perform actions.
|
||
|
*/
|
||
|
public void run(String inDirectory, String outDirectory) {
|
||
|
if (inDirectory.isEmpty()) {
|
||
|
logString("Error: Must provide input directory with " + INPUT_DIR_FLAG);
|
||
|
return;
|
||
|
}
|
||
|
if (outDirectory.isEmpty()) {
|
||
|
logString("Error: Must provide output directory with " + OUTPUT_DIR_FLAG);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
File dirAsFile = new File(inDirectory);
|
||
|
ObjFileFilter objFileFilter = new ObjFileFilter();
|
||
|
File[] objFiles = dirAsFile.listFiles(objFileFilter);
|
||
|
|
||
|
FileOutputStream outputStream = null;
|
||
|
logString("Parsing directory: " + inDirectory);
|
||
|
// We need frames processed in correct order.
|
||
|
Arrays.sort(objFiles);
|
||
|
|
||
|
for (File objFile : objFiles) {
|
||
|
String fileName = objFile.getAbsolutePath();
|
||
|
|
||
|
// Just take the file name of the first processed frame.
|
||
|
if (outputStream == null) {
|
||
|
String outputFileName = outDirectory + objFile.getName() + BINARY_FILE_EXT;
|
||
|
try {
|
||
|
// Create new file here, if we can.
|
||
|
outputStream = new FileOutputStream(outputFileName);
|
||
|
logString("Created outfile: " + outputFileName);
|
||
|
} catch (Exception e) {
|
||
|
logString("Error creating outfile: " + e.toString());
|
||
|
e.printStackTrace(writer);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process each file into the stream.
|
||
|
logString("Processing file: " + fileName);
|
||
|
processFile(fileName, outputStream);
|
||
|
}
|
||
|
|
||
|
// Finally close the stream out.
|
||
|
try {
|
||
|
if (outputStream != null) {
|
||
|
outputStream.close();
|
||
|
}
|
||
|
} catch (Exception e) {
|
||
|
logString("Error trying to close output stream: " + e.toString());
|
||
|
e.printStackTrace(writer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Entrypoint for command-line executable.
|
||
|
*/
|
||
|
public static void main(String[] args) {
|
||
|
// Parse flags
|
||
|
String inDirectory = "";
|
||
|
String outDirectory = "";
|
||
|
for (int i = 0; i < args.length; i++) {
|
||
|
if (args[i].startsWith(INPUT_DIR_FLAG)) {
|
||
|
inDirectory = args[i].substring(INPUT_DIR_FLAG.length());
|
||
|
// Make sure this will be treated as a directory
|
||
|
if (!inDirectory.endsWith("/")) {
|
||
|
inDirectory += "/";
|
||
|
}
|
||
|
}
|
||
|
if (args[i].startsWith(OUTPUT_DIR_FLAG)) {
|
||
|
outDirectory = args[i].substring(OUTPUT_DIR_FLAG.length());
|
||
|
// Make sure this will be treated as a directory
|
||
|
if (!outDirectory.endsWith("/")) {
|
||
|
outDirectory += "/";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ObjParserMain parser = new ObjParserMain();
|
||
|
parser.run(inDirectory, outDirectory);
|
||
|
parser.writer.flush();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Internal helper function to parse a .obj from an infile name and stream the resulting data
|
||
|
* directly out in binary-dump format to outputStream.
|
||
|
*/
|
||
|
private void processFile(String infileName, OutputStream outputStream) {
|
||
|
long start = System.nanoTime();
|
||
|
|
||
|
// First we parse the obj.
|
||
|
SimpleObjParser objParser = new SimpleObjParser(infileName, DEFAULT_VERTEX_SCALE_FACTOR);
|
||
|
if (!objParser.parse()) {
|
||
|
logString("Error parsing .obj file before processing");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
final float[] vertices = objParser.getVertices();
|
||
|
final float[] textureCoords = objParser.getTextureCoords();
|
||
|
final ArrayList<Short> triangleList = objParser.getTriangles();
|
||
|
|
||
|
// Overall byte count to stream: 12 for the 3 list-length ints, and then 4 for each vertex and
|
||
|
// texCoord int, and finally 2 for each triangle index short.
|
||
|
final int bbSize =
|
||
|
12 + 4 * vertices.length + 4 * textureCoords.length + 2 * triangleList.size();
|
||
|
|
||
|
// Ensure ByteBuffer is native order, just like we want to read it in, but is NOT direct, so
|
||
|
// we can call .array() on it.
|
||
|
ByteBuffer bb = ByteBuffer.allocate(bbSize);
|
||
|
bb.order(ByteOrder.nativeOrder());
|
||
|
|
||
|
bb.putInt(vertices.length);
|
||
|
bb.putInt(textureCoords.length);
|
||
|
bb.putInt(triangleList.size());
|
||
|
logString(String.format("Writing... Vertices: %d, TextureCoords: %d, Indices: %d.%n",
|
||
|
vertices.length, textureCoords.length, triangleList.size()));
|
||
|
for (float vertex : vertices) {
|
||
|
bb.putFloat(vertex);
|
||
|
}
|
||
|
for (float textureCoord : textureCoords) {
|
||
|
bb.putFloat(textureCoord);
|
||
|
}
|
||
|
for (Short vertexIndex : triangleList) {
|
||
|
bb.putShort(vertexIndex.shortValue());
|
||
|
}
|
||
|
bb.position(0);
|
||
|
try {
|
||
|
outputStream.write(bb.array(), 0, bbSize);
|
||
|
logString(String.format("Processing successful! Took %.4f seconds.%n",
|
||
|
(System.nanoTime() - start) / NS_TO_SECONDS));
|
||
|
} catch (Exception e) {
|
||
|
logString("Error writing during processing: " + e.toString());
|
||
|
e.printStackTrace(writer);
|
||
|
}
|
||
|
}
|
||
|
}
|