/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* 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.
******************************************************************************/
package de.swagner.piratesbigsea.com.badlogic.gdx.graphics.g3d.loaders.g3d;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.keyframed.Keyframe;
import com.badlogic.gdx.graphics.g3d.keyframed.KeyframedModel;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.ObjectMap;
import de.swagner.piratesbigsea.com.badlogic.gdx.graphics.g3d.ModelLoaderHints;
import de.swagner.piratesbigsea.com.badlogic.gdx.graphics.g3d.loaders.StillModelLoader;
import de.swagner.piratesbigsea.com.badlogic.gdx.graphics.g3d.model.still.StillModel;
import de.swagner.piratesbigsea.com.badlogic.gdx.graphics.g3d.model.still.StillSubMesh;
/** Class to import the G3D text format.
* @author mzechner */
public class G3dtLoader {
public static StillModel loadStillModel (FileHandle handle, boolean flipV) {
return loadStillModel(handle.read(), flipV);
}
static int lineNum = 0;
static String line = null;
public static StillModel loadStillModel (InputStream stream, boolean flipV) {
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
lineNum = 1;
try {
String version = readString(in);
if (!version.equals("g3dt-still-1.0")) throw new GdxRuntimeException("incorrect version");
int numMeshes = readInt(in);
StillSubMesh[] subMeshes = new StillSubMesh[numMeshes];
for (int i = 0; i < numMeshes; i++) {
subMeshes[i] = readStillSubMesh(in, flipV);
}
StillModel model = new StillModel(subMeshes);
return model;
} catch (Throwable e) {
throw new GdxRuntimeException("Couldn't read keyframed model, error in line " + lineNum + ", '" + line + "' : "
+ e.getMessage(), e);
}
}
private static StillSubMesh readStillSubMesh (BufferedReader in, boolean flipV) throws IOException {
String name = readString(in);
IntArray indices = readFaces(in);
int numVertices = readInt(in);
int numAttributes = readInt(in);
if (!readString(in).equals("position")) throw new GdxRuntimeException("first attribute must be position.");
int numUvs = 0;
boolean hasNormals = false;
for (int i = 1; i < numAttributes; i++) {
String attributeType = readString(in);
if (!attributeType.equals("normal") && !attributeType.equals("uv"))
throw new GdxRuntimeException("attribute name must be normal or uv");
if (attributeType.equals("normal")) {
if (i != 1) throw new GdxRuntimeException("attribute normal must be second attribute");
hasNormals = true;
}
if (attributeType.equals("uv")) {
numUvs++;
}
}
VertexAttribute[] vertexAttributes = createVertexAttributes(hasNormals, numUvs);
int vertexSize = new VertexAttributes(vertexAttributes).vertexSize / 4;
float[] vertices = new float[numVertices * vertexSize];
int idx = 0;
int uvOffset = hasNormals ? 6 : 3;
for (int i = 0; i < numVertices; i++) {
readFloatArray(in, vertices, idx);
if (flipV) {
for (int j = idx + uvOffset + 1; j < idx + uvOffset + numUvs * 2; j += 2) {
vertices[j] = 1 - vertices[j];
}
}
idx += vertexSize;
}
Mesh mesh = new Mesh(true, numVertices, indices.size, vertexAttributes);
mesh.setVertices(vertices);
mesh.setIndices(convertToShortArray(indices));
return new StillSubMesh(name, mesh, GL10.GL_TRIANGLES);
}
private static float[] buildVertices (int numVertices, boolean hasNormals, Array<FloatArray> uvSets) {
float[] vertices = new float[numVertices * (3 + (hasNormals ? 3 : 0) + uvSets.size * 2)];
int idx = 0;
int idxUv = 0;
for (int i = 0; i < numVertices; i++) {
vertices[idx++] = 0;
vertices[idx++] = 0;
vertices[idx++] = 0;
if (hasNormals) {
vertices[idx++] = 0;
vertices[idx++] = 0;
vertices[idx++] = 0;
}
for (int j = 0; j < uvSets.size; j++) {
vertices[idx++] = uvSets.get(j).get(idxUv);
vertices[idx++] = uvSets.get(j).get(idxUv + 1);
}
idxUv += 2;
}
return vertices;
}
private static VertexAttribute[] createVertexAttributes (boolean hasNormals, int uvs) {
VertexAttribute[] attributes = new VertexAttribute[1 + (hasNormals ? 1 : 0) + uvs];
int idx = 0;
attributes[idx++] = new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE);
if (hasNormals) attributes[idx++] = new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE);
for (int i = 0; i < uvs; i++) {
attributes[idx++] = new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + i);
}
return attributes;
}
private static FloatArray readUVSet (BufferedReader in, int numVertices, boolean flipV) throws IOException {
FloatArray uvSet = new FloatArray(numVertices * 2);
FloatArray uv = new FloatArray(2);
for (int i = 0; i < numVertices; i++) {
readFloatArray(in, uv);
uvSet.add(uv.items[0]);
uvSet.add(flipV ? 1 - uv.items[1] : uv.items[1]);
}
return uvSet;
}
private static IntArray readFaces (BufferedReader in) throws NumberFormatException, IOException {
int numFaces = readInt(in);
IntArray faceIndices = new IntArray();
IntArray triangles = new IntArray();
IntArray indices = new IntArray();
for (int face = 0; face < numFaces; face++) {
readIntArray(in, faceIndices);
int numIndices = faceIndices.get(0);
triangles.clear();
int baseIndex = faceIndices.get(1);
for (int i = 2; i < numIndices; i++) {
triangles.add(baseIndex);
triangles.add(faceIndices.items[i]);
triangles.add(faceIndices.items[i + 1]);
}
indices.addAll(triangles);
}
indices.shrink();
return indices;
}
private static short[] convertToShortArray (IntArray array) {
short[] shortArray = new short[array.size];
for (int i = 0; i < array.size; i++) {
shortArray[i] = (short)array.items[i];
}
return shortArray;
}
private static float readFloat (BufferedReader in) throws NumberFormatException, IOException {
lineNum++;
return Float.parseFloat(read(in).trim());
}
private static int readInt (BufferedReader in) throws NumberFormatException, IOException {
lineNum++;
return (int)(Math.floor(Float.parseFloat(read(in).trim())));
}
private static String readString (BufferedReader in) throws IOException {
lineNum++;
return read(in);
}
private static void readFloatArray (BufferedReader in, FloatArray array) throws IOException {
lineNum++;
String[] tokens = read(in).split(",");
int len = tokens.length;
array.clear();
for (int i = 0; i < len; i++) {
array.add(Float.parseFloat(tokens[i].trim()));
}
}
private static int readFloatArray (BufferedReader in, float[] array, int idx) throws IOException {
lineNum++;
String[] tokens = read(in).split(",");
int len = tokens.length;
for (int i = 0; i < len; i++) {
array[idx++] = Float.parseFloat(tokens[i].trim());
}
return idx;
}
private static void readIntArray (BufferedReader in, IntArray array) throws IOException {
String[] tokens = read(in).split(",");
int len = tokens.length;
array.clear();
for (int i = 0; i < len; i++) {
array.add(Integer.parseInt(tokens[i].trim()));
}
}
private static String read (BufferedReader in) throws IOException {
line = in.readLine();
return line;
}
public static class G3dtStillModelLoader implements StillModelLoader {
@Override
public StillModel load (FileHandle handle, ModelLoaderHints hints) {
return G3dtLoader.loadStillModel(handle, hints.flipV);
}
}
}