package com.gebauz.Bauzoid.graphics.model;
import java.io.IOException;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;
import com.gebauz.Bauzoid.app.Consts;
import com.gebauz.Bauzoid.file.File;
import com.gebauz.Bauzoid.file.FileUtil;
import com.gebauz.Bauzoid.graphics.Graphics;
import com.gebauz.Bauzoid.graphics.model.Mesh;
import com.gebauz.Bauzoid.math.BoundingBox;
import com.gebauz.Bauzoid.math.Matrix4;
import com.gebauz.Bauzoid.math.Vector2;
import com.gebauz.Bauzoid.math.Vector3;
import com.gebauz.Bauzoid.math.Vector4;
public class ModelUtil
{
private ModelUtil
() {}
public static Model loadModelFromAsset
(Graphics graphics,
String asset
) throws IOException
{
File file = FileUtil.
loadFileFromAsset(graphics.
getResources(), asset
);
Model model = loadModel
(graphics, file
);
file.
close();
return model
;
}
public static Model loadModelFromResource
(Graphics graphics,
int id
) throws IOException
{
File file = FileUtil.
loadFileFromResource(graphics.
getResources(), id
);
Model model = loadModel
(graphics, file
);
file.
close();
return model
;
}
public static Model loadModel
(Graphics graphics,
File modelFile
) throws IOException
{
Log.
v(Consts.
LOG_TAG,
"Loading file");
byte byteOrdering = modelFile.
readByte();
Log.
v(Consts.
LOG_TAG,
"Byte Ordering: " +
(byteOrdering ==
0 ? "Little Endian" :
"Big Endian"));
if (byteOrdering ==
0)
modelFile.
setEndianness(File.
Endianness.
LITTLE_ENDIAN);
else
modelFile.
setEndianness(File.
Endianness.
BIG_ENDIAN);
String header = modelFile.
readString(8);
Log.
v(Consts.
LOG_TAG,
"Header: " + header
);
if (header.
compareTo("SUXMODEL") !=
0)
throw new IOException("Header incorrect, must be 'SUXMODEL': " + header
);
int version = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Version: " + version
);
if (version
!=
0)
throw new IOException("Version incorrect, must be 0: " + version
);
String name = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"Name: " + name
);
int meshCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Mesh Count: " + meshCount
);
Model model =
new Model
(name
);
Mesh
[] meshes =
new Mesh
[meshCount
];
for (int i =
0; i
< meshCount
; i++
)
{
String meshName = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"[" + i +
"] Mesh Name: " + meshName
);
meshes
[i
] =
new Mesh
(graphics, meshName
);
BoundingBox box =
new BoundingBox
();
box.
min = modelFile.
readVector3();
Log.
v(Consts.
LOG_TAG,
"[" + i +
"] BB Min: " + box.
min.
x +
", " + box.
min.
y +
", " + box.
min.
z);
box.
max = modelFile.
readVector3();
Log.
v(Consts.
LOG_TAG,
"[" + i +
"] BB Max: " + box.
max.
x +
", " + box.
max.
y +
", " + box.
max.
z);
meshes
[i
].
setBoundingBox(box
);
boolean dynamic = modelFile.
readBool();
Log.
v(Consts.
LOG_TAG,
"[" + i +
"] Dynamic: " +
(dynamic
? "yes" :
"no"));
boolean deleteSource = modelFile.
readBool();
Log.
v(Consts.
LOG_TAG,
"[" + i +
"] DeleteSource: " +
(deleteSource
? "yes" :
"no"));
// geometry
Geometry geometry = loadGeometry
(graphics, modelFile
);
meshes
[i
].
setGeometry(geometry
);
// bones - currently ignored
int boneCount = modelFile.
readInt();
for (int bone =
0; bone
< boneCount
; bone++
)
{
String boneName = modelFile.
readString();
Matrix4 boneTransform = modelFile.
readMatrix4();
Matrix4 boneTransformInverse = modelFile.
readMatrix4();
Vector3 bonePosition = modelFile.
readVector3();
// TODO: Quaternion
Vector4 boneOrientation = modelFile.
readVector4();
Vector3 boneScale = modelFile.
readVector3();
}
for (int bone =
0; bone
< boneCount
; bone++
)
{
int parentBoneIndex = modelFile.
readInt();
}
// groups
int groupCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Group Count: " + groupCount
);
MeshGroup
[] groups =
new MeshGroup
[groupCount
];
for (int group =
0; group
< groupCount
; group++
)
{
groups
[group
] = loadMeshGroup
(meshes
[i
], modelFile
);
}
meshes
[i
].
setGroups(groups
);
break;
}
model.
setMeshes(meshes
);
Log.
v(Consts.
LOG_TAG,
"Loading finished");
return model
;
}
public static Geometry loadGeometry
(Graphics graphics,
File modelFile
) throws IOException
{
Log.
v(Consts.
LOG_TAG,
"=== Geometry Begin ===");
int version = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Geometry Version: " + version
);
if (version
!=
2)
throw new IOException("Geometry Version incorrect, must be 2: " + version
);
int primitiveType = modelFile.
readInt();
switch (primitiveType
)
{
case 0:
Log.
v(Consts.
LOG_TAG,
"PrimitiveType: Triangles");
break;
case 1:
Log.
v(Consts.
LOG_TAG,
"PrimitiveType: Triangle Strip");
break;
case 2:
Log.
v(Consts.
LOG_TAG,
"PrimitiveType: Lines");
break;
case 3:
Log.
v(Consts.
LOG_TAG,
"PrimitiveType: Line Strip");
break;
case 4:
Log.
v(Consts.
LOG_TAG,
"PrimitiveType: Points");
break;
}
int vertexCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Vertex Count: " + vertexCount
);
int indexCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Index Count: " + indexCount
);
int streamCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Stream Count: " + streamCount
);
VertexStream
[] streams =
new VertexStream
[streamCount
];
for (int i =
0; i
< streamCount
; i++
)
{
Log.
v(Consts.
LOG_TAG,
"=== BEGIN VertexStream [" + i +
"] ===");
streams
[i
] = loadVertexStream
(graphics, modelFile
);
Log.
v(Consts.
LOG_TAG,
"=== END VertexStream [" + i +
"] ===");
}
boolean hasIndexStream = modelFile.
readBool();
Log.
v(Consts.
LOG_TAG,
"Has Index Stream" +
(hasIndexStream
? "Yes" :
"No"));
IndexStream indexStream =
null;
if (hasIndexStream
)
{
Log.
v(Consts.
LOG_TAG,
"=== BEGIN IndexStream ===");
indexStream = loadIndexStream
(graphics, modelFile
);
Log.
v(Consts.
LOG_TAG,
"=== END IndexStream ===");
}
Geometry geometry =
new Geometry
(graphics, Geometry.
PrimitiveType.
values()[primitiveType
], vertexCount, indexCount
);
geometry.
setVertexStreams(streams
);
geometry.
setIndexStream(indexStream
);
Log.
v(Consts.
LOG_TAG,
"=== Geometry End ===");
return geometry
;
}
public static VertexStream loadVertexStream
(Graphics graphics,
File modelFile
) throws IOException
{
VertexStream.
StreamType streamType =
(modelFile.
readInt() ==
1) ? VertexStream.
StreamType.
INDIVIDUAL : VertexStream.
StreamType.
INTERLEAVED;
Log.
v(Consts.
LOG_TAG,
"Stream Type: " + streamType.
toString());
String streamName = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"Stream Name: " + streamName
);
int streamVertexCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Stream Vertex Count: " + streamVertexCount
);
int coordsPerElement = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Coords per Element: " + coordsPerElement
);
int bytesPerElement = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Bytes per Element: " + bytesPerElement
);
// Stream Begin
byte[] streamBuffer = loadStream
(modelFile
);
// Stream End
VertexAttribute
[] attribs =
null;
if (streamType == VertexStream.
StreamType.
INDIVIDUAL)
{
attribs =
new VertexAttribute
[1];
attribs
[0] = loadAttribute
(modelFile
);
}
else if (streamType == VertexStream.
StreamType.
INTERLEAVED)
{
int vertexAttribCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Vertex Attrib Count: " + vertexAttribCount
);
attribs =
new VertexAttribute
[vertexAttribCount
];
for (int attrib =
0; attrib
< vertexAttribCount
; attrib++
)
{
Log.
v(Consts.
LOG_TAG,
"=== BEGIN VertexAttribute [" + attrib +
"] ===");
// Vertex Attribute
attribs
[attrib
] = loadAttribute
(modelFile
);
Log.
v(Consts.
LOG_TAG,
"=== END VertexAttribute [" + attrib +
"] ===");
}
}
else
{
throw new IOException();
}
VertexStream stream =
new VertexStream
(graphics, streamType, streamName, attribs
);
stream.
setData(streamBuffer, streamVertexCount, coordsPerElement, bytesPerElement
);
return stream
;
}
public static VertexAttribute loadAttribute
(File modelFile
) throws IOException
{
int suxType = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Attrib Data Type: " + suxType
);
if (!VertexAttribute.
isValidType(suxType
))
throw new IOException("Invalid vertex attribute type: " + suxType
);
int dataFormat = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Attrib Data Format: " + dataFormat
);
if (dataFormat
!=
0)
throw new IOException("Unsupported Data Format (must be 0 / Float): " + dataFormat
);
int coordsPerElement = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Attrib Coords Per Element: " + coordsPerElement
);
int byteOffset = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Attrib Byte Offset: " + byteOffset
);
VertexAttribute attrib =
new VertexAttribute
(VertexAttribute.
toInternalType(suxType
), coordsPerElement, byteOffset
);
return attrib
;
}
public static IndexStream loadIndexStream
(Graphics graphics,
File modelFile
) throws IOException
{
int indexCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Index Count: " + indexCount
);
int dataFormat = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Data Format: " + dataFormat
);
if (dataFormat
!=
1)
throw new IOException("Data Format unsupported, must be 1 (Int16): " + dataFormat
);
// Stream Begin
byte[] streamBuffer = loadStream
(modelFile
);
// Stream End
IndexStream indexStream =
new IndexStream
(graphics, indexCount, streamBuffer
);
return indexStream
;
}
public static byte[] loadStream
(File modelFile
) throws IOException
{
// Store endianness because the stream might change it
File.
Endianness endianness = modelFile.
getEndianness();
byte byteOrdering = modelFile.
readByte();
Log.
v(Consts.
LOG_TAG,
"Stream Byte Ordering: " +
(byteOrdering ==
0 ? "Little Endian" :
"Big Endian"));
if (byteOrdering ==
0)
modelFile.
setEndianness(File.
Endianness.
LITTLE_ENDIAN);
else
modelFile.
setEndianness(File.
Endianness.
BIG_ENDIAN);
byte charFormat = modelFile.
readByte();
Log.
v(Consts.
LOG_TAG,
"Stream CharFormat: " +
(charFormat ==
0 ? "ASCII" :
"UTF16"));
int byteLength = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Stream Byte Length: " + byteLength
);
byte[] buffer =
new byte[byteLength
];
modelFile.
readFully(buffer,
0, byteLength
);
// Restore endianness
modelFile.
setEndianness(endianness
);
return buffer
;
}
public static MeshGroup loadMeshGroup
(Mesh mesh,
File modelFile
) throws IOException
{
Log.
v(Consts.
LOG_TAG,
"=== Mesh.Group Begin ===");
String name = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"Name: " + name
);
String effectFile = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"-- Effect file: " + effectFile
);
int techniqueCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"-- Technique Count: " + techniqueCount
);
for (int i =
0; i
< techniqueCount
; i++
)
{
String staticTechniqueName = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"-- Static Technique: " + staticTechniqueName
);
String skinnedTechniqueName = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"-- Skinned Technique: " + skinnedTechniqueName
);
}
String bonesParameterName = modelFile.
readString();
int firstIndex = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"First Index: " + firstIndex
);
int lastIndex = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Last Index: " + lastIndex
);
int firstVertex = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"First Vertex: " + firstVertex
);
int lastVertex = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"Last Vertex: " + lastVertex
);
MeshGroup group =
new MeshGroup
(mesh, name, firstIndex, lastIndex, firstVertex, lastVertex
);
// TODO: make render state class
loadRenderState
(modelFile
);
int variablesCount = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"== Variable Count: " + variablesCount
);
for (int i =
0; i
< variablesCount
; i++
)
{
String variableName = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Name: " + variableName
);
int variableType = modelFile.
readInt();
switch (variableType
)
{
case 0:
// Texture2D
case 1:
// Texture3D
case 2:
// TextureCube
String texFile = modelFile.
readString();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: Texture (" + texFile +
")");
break;
case 3:
// Bool
boolean b = modelFile.
readBool();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: bool (" +
(b
? "true" :
"false") +
")");
break;
case 4:
// Int
int n = modelFile.
readInt();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: int (" + n +
")");
break;
case 5:
// Float
float f = modelFile.
readFloat();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: float (" + f +
")");
break;
case 6:
// Vector2
Vector2 v2 = modelFile.
readVector2();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: Vector2 (" + v2.
x +
", " + v2.
y +
")");
break;
case 7:
// Vector3
Vector3 v3 = modelFile.
readVector3();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: Vector3 (" + v3.
x +
", " + v3.
y +
", " + v3.
z +
")");
break;
case 8:
// Vector4
Vector4 v4 = modelFile.
readVector4();
Log.
v(Consts.
LOG_TAG,
"-- [" + i +
"] Variable Type: Vector3 (" + v4.
x +
", " + v4.
y +
", " + v4.
z +
", " + v4.
w +
")");
break;
}
}
// Bone Reindexing for tighter usage of uniforms
int boneIndexCount = modelFile.
readInt();
for (int i =
0; i
< boneIndexCount
; i++
)
{
int originalIndex = modelFile.
readInt();
int newIndex = modelFile.
readInt();
}
Log.
v(Consts.
LOG_TAG,
"=== Mesh.Group End===");
return group
;
}
public static void loadRenderState
(File modelFile
) throws IOException
{
boolean alphaTestEnabled = modelFile.
readBool();
float alphaTestThreshold = modelFile.
readFloat();
int alphaTestComparisonFunc = modelFile.
readInt();
boolean blendingEnabled = modelFile.
readBool();
int blendingMode = modelFile.
readInt();
boolean cullingEnabled = modelFile.
readBool();
int cullingWinding = modelFile.
readInt();
boolean depthTestEnabled = modelFile.
readBool();
boolean depthBufferWriteEnabled = modelFile.
readBool();
int depthTestComparisonFunc = modelFile.
readInt();
int polygonMode = modelFile.
readInt();
}
}