use of org.rajawali3d.animation.mesh.SkeletalAnimationChildObject3D.BoneWeight in project Rajawali by Rajawali.
the class LoaderMD5Mesh method calculateNormals.
private void calculateNormals() {
for (int i = 0; i < mNumMeshes; ++i) {
SkeletonMeshData mesh = mMeshes[i];
int numTriangles = mesh.numTriangles;
mesh.indices = new int[numTriangles * 3];
int index = 0;
for (int j = 0; j < numTriangles; ++j) {
int[] triangle = mesh.triangles[j];
int index0 = triangle[0];
int index1 = triangle[2];
int index2 = triangle[1];
mesh.indices[index++] = index0;
mesh.indices[index++] = index1;
mesh.indices[index++] = index2;
int index03 = index0 * 3;
int index13 = index1 * 3;
int index23 = index2 * 3;
Vector3 v0 = new Vector3(mesh.vertices[index03], mesh.vertices[index03 + 1], mesh.vertices[index03 + 2]);
Vector3 v1 = new Vector3(mesh.vertices[index13], mesh.vertices[index13 + 1], mesh.vertices[index13 + 2]);
Vector3 v2 = new Vector3(mesh.vertices[index23], mesh.vertices[index23 + 1], mesh.vertices[index23 + 2]);
Vector3 normal = Vector3.crossAndCreate(Vector3.subtractAndCreate(v2, v0), Vector3.subtractAndCreate(v1, v0));
normal.inverse();
mesh.boneVertices[index0].normal.add(normal);
mesh.boneVertices[index1].normal.add(normal);
mesh.boneVertices[index2].normal.add(normal);
}
int numVertices = mesh.numVertices;
if (mesh.normals == null)
mesh.normals = new float[numVertices * 3];
for (int j = 0; j < numVertices; ++j) {
BoneVertex vert = mesh.boneVertices[j];
Vector3 normal = vert.normal.clone();
vert.normal.normalize();
normal.normalize();
int normIndex = j * 3;
mesh.normals[normIndex] = (float) normal.x;
mesh.normals[normIndex + 1] = (float) normal.y;
mesh.normals[normIndex + 2] = (float) normal.z;
vert.normal.setAll(0, 0, 0);
// so the animated normal can be computed faster
for (int k = 0; k < vert.numWeights; ++k) {
BoneWeight weight = mesh.boneWeights[vert.weightIndex + k];
SkeletonJoint joint = mJoints[weight.jointIndex];
//We don't clone here because nothing will be able to use the quaternion scratch before we do
vert.normal.add(Vector3.scaleAndCreate(joint.getOrientation().multiply(normal), weight.weightValue));
}
}
}
}
use of org.rajawali3d.animation.mesh.SkeletalAnimationChildObject3D.BoneWeight in project Rajawali by Rajawali.
the class LoaderMD5Mesh method parseMesh.
private void parseMesh(BufferedReader buffer) {
try {
String line;
SkeletonMeshData mesh = new SkeletonMeshData();
while ((line = buffer.readLine()) != null) {
line = line.replace("\t", "");
StringTokenizer parts = new StringTokenizer(line, " ");
int numTokens = parts.countTokens();
if (line.indexOf('}') > -1) {
mMeshes[mMeshIndex++] = mesh;
return;
}
if (numTokens == 0 || line.indexOf('}') > -1)
continue;
String type = parts.nextToken();
if (type.equalsIgnoreCase(SHADER)) {
String shader = parts.nextToken();
shader = shader.replace("\"", "");
mesh.textureName = shader;
if (shader.length() == 0)
continue;
int lastDelim = shader.lastIndexOf("/");
if (lastDelim == -1)
lastDelim = shader.lastIndexOf("\\");
if (lastDelim > -1)
mesh.textureName = shader.substring(lastDelim + 1, shader.length());
int dot = shader.lastIndexOf(".");
if (dot > -1)
mesh.textureName = shader.substring(0, dot);
} else if (type.equalsIgnoreCase(NUM_VERTS)) {
mesh.numVertices = Integer.parseInt(parts.nextToken());
mesh.boneVertices = new BoneVertex[mesh.numVertices];
} else if (type.equalsIgnoreCase(VERT)) {
int index = Integer.parseInt(parts.nextToken());
BoneVertex vert = new BoneVertex();
// -- ignore '('
parts.nextToken();
vert.textureCoordinate.setAll(Float.parseFloat(parts.nextToken()), Float.parseFloat(parts.nextToken()));
// -- ignore ')'
parts.nextToken();
vert.weightIndex = Integer.parseInt(parts.nextToken());
vert.numWeights = Integer.parseInt(parts.nextToken());
mesh.numWeights += vert.numWeights;
mesh.maxBoneWeightsPerVertex = Math.max(mesh.maxBoneWeightsPerVertex, vert.numWeights);
mesh.boneVertices[index] = vert;
} else if (type.equalsIgnoreCase(NUM_TRIS)) {
mesh.numTriangles = Integer.parseInt(parts.nextToken());
mesh.triangles = new int[mesh.numTriangles][];
} else if (type.equalsIgnoreCase(TRI)) {
int index = Integer.parseInt(parts.nextToken());
mesh.triangles[index] = new int[] { Integer.parseInt(parts.nextToken()), Integer.parseInt(parts.nextToken()), Integer.parseInt(parts.nextToken()) };
} else if (type.equalsIgnoreCase(NUM_WEIGHTS)) {
mesh.numWeights = Integer.parseInt(parts.nextToken());
mesh.boneWeights = new BoneWeight[mesh.numWeights];
} else if (type.equalsIgnoreCase(WEIGHT)) {
int index = Integer.parseInt(parts.nextToken());
BoneWeight weight = new BoneWeight();
weight.jointIndex = Integer.parseInt(parts.nextToken());
weight.weightValue = Float.parseFloat(parts.nextToken());
mesh.boneWeights[index] = weight;
// -- ignore '('
parts.nextToken();
float x = Float.parseFloat(parts.nextToken());
float z = Float.parseFloat(parts.nextToken());
float y = Float.parseFloat(parts.nextToken());
weight.position.setAll(x, y, z);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
use of org.rajawali3d.animation.mesh.SkeletalAnimationChildObject3D.BoneWeight in project Rajawali by Rajawali.
the class BlockTriangleGeometry method parseBlock.
public void parseBlock(AWDLittleEndianDataInputStream dis, BlockHeader blockHeader) throws Exception {
// Lookup name
mLookupName = dis.readVarString();
// Count of sub geometries
mSubGeometryCount = dis.readUnsignedShort();
// TODO Meshes need to be joined in some fashion. This might work. Need to test it I suppose.
// One object for each sub geometry
mBaseObjects = new Object3D[mSubGeometryCount];
// Debug
if (RajLog.isDebugEnabled()) {
RajLog.d(" Lookup Name: " + mLookupName);
RajLog.d(" Sub Geometry Count: " + mSubGeometryCount);
}
// Determine the precision for the block
final boolean geoAccuracy = (blockHeader.flags & BlockHeader.FLAG_ACCURACY_GEO) == BlockHeader.FLAG_ACCURACY_GEO;
final short geoNr = geoAccuracy ? AWDLittleEndianDataInputStream.TYPE_FLOAT64 : AWDLittleEndianDataInputStream.TYPE_FLOAT32;
// Read the properties
SparseArray<Short> properties = new SparseArray<Short>();
// Scale Texture U
properties.put(1, geoNr);
// Scale Texture V
properties.put(2, geoNr);
// TODO Apply texture scales, need example of this working.
dis.readProperties(properties);
// Calculate the sizes
final int geoPrecisionSize = blockHeader.globalPrecisionGeo ? 8 : 4;
// Read each sub mesh data
for (int parsedSub = 0; parsedSub < mSubGeometryCount; ++parsedSub) {
long subMeshEnd = dis.getPosition() + dis.readUnsignedInt();
// Geometry
float[] vertices = null;
int[] indices = null;
float[] uvs = null;
float[] normals = null;
int[] joints = null;
float[] weights = null;
// Skip reading of mesh properties for now (per AWD implementation)
dis.readProperties();
// Read each data type from the mesh
while (dis.getPosition() < subMeshEnd) {
int idx = 0;
int type = dis.readUnsignedByte();
int typeF = dis.readUnsignedByte();
long subLength = dis.readUnsignedInt();
long subEnd = dis.getPosition() + subLength;
if (RajLog.isDebugEnabled())
RajLog.d(" Mesh Data: t:" + type + " tf:" + typeF + " l:" + subLength + " ls:" + dis.getPosition() + " le:" + subEnd);
// Process the mesh data by type
switch((int) type) {
case // Vertex positions
1:
vertices = new float[(int) (subLength / geoPrecisionSize)];
while (idx < vertices.length) {
// X, Y, Z
vertices[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
vertices[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
vertices[idx++] = (float) -dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
}
break;
case // Face indices
2:
indices = new int[(int) (subLength / 2)];
while (idx < indices.length) {
indices[idx + 2] = dis.readUnsignedShort();
indices[idx + 1] = dis.readUnsignedShort();
indices[idx] = dis.readUnsignedShort();
idx += 3;
}
break;
case // UV coordinates
3:
uvs = new float[(int) (subLength / geoPrecisionSize)];
while (idx < uvs.length) uvs[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
break;
case // Vertex normals
4:
normals = new float[(int) (subLength / geoPrecisionSize)];
while (idx < normals.length) {
normals[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
normals[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
normals[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
}
break;
case // Joint index
6:
joints = new int[(int) (subLength / 2)];
while (idx < joints.length) joints[idx++] = dis.readUnsignedShort();
break;
case // Joint weight
7:
weights = new float[(int) (subLength / geoPrecisionSize)];
while (idx < weights.length) weights[idx++] = (float) dis.readPrecisionNumber(blockHeader.globalPrecisionGeo);
break;
// Vertex tangents
case 5:
default:
// Unknown mesh data, skipping
dis.skip(subLength);
}
// Validate each mesh data ending. This is a sanity check against precision flags.
if (dis.getPosition() != subEnd)
throw new ParsingException("Unexpected ending. Expected " + subEnd + ". Got " + dis.getPosition());
}
dis.readUserAttributes(null);
// Verify the arrays
if (vertices == null)
vertices = new float[0];
if (normals == null)
normals = new float[0];
if (uvs == null)
uvs = new float[0];
if (indices == null)
indices = new int[0];
// FIXME This should be combining sub geometry not creating objects
if (joints != null && joints.length > 0) {
/*
* Prepares skeletal animation object as far as possible; setting mesh
* and skeletal weight data. The object will not yet have an actual
* skeleton applied to it.
*/
SkeletalAnimationChildObject3D obj = new SkeletalAnimationChildObject3D();
obj.setData(vertices, normals, uvs, null, indices, false);
int numVertices = vertices.length / 3;
// AWD stipulates all vertices have same # bindings, possibly 0 weighted
int weightsPerVertex = weights.length / numVertices;
// true WPV may be out of range, so clamp
int clampWeightsPerVertex = Math.min(weightsPerVertex, SkeletalAnimationChildObject3D.MAX_WEIGHTS_PER_VERTEX);
// one BoneVertex per actual vertex, maps to N weights & joint indices
BoneVertex[] bvertices = new BoneVertex[numVertices];
ArrayList<BoneWeight> bweights = new ArrayList<BoneWeight>();
int maxWeightsPerVertex = 0;
int vertexWeightIndex = 0;
for (int vert = 0; vert < numVertices; vert++) {
BoneVertex bone = new BoneVertex();
bvertices[vert] = bone;
// we may ignore weights, so map to our custom list
bone.weightIndex = bweights.size();
// true position in raw weight array
vertexWeightIndex = vert * weightsPerVertex;
// only add first [clamp] non-zero weights
for (int wgt = 0; wgt < clampWeightsPerVertex; wgt++) {
if (weights[vertexWeightIndex + wgt] == 0)
continue;
BoneWeight weight = new BoneWeight();
// joints and weights are indexed together
weight.jointIndex = joints[vertexWeightIndex + wgt];
weight.weightValue = weights[vertexWeightIndex + wgt];
bone.numWeights++;
bweights.add(weight);
}
maxWeightsPerVertex = Math.max(maxWeightsPerVertex, bone.numWeights);
}
// extract the clean BoneWeight array
BoneWeight[] boneweights = bweights.toArray(new BoneWeight[bweights.size()]);
obj.setMaxBoneWeightsPerVertex(maxWeightsPerVertex);
obj.setSkeletonMeshData(bvertices, boneweights);
//obj.setInverseZScale(true);
mBaseObjects[parsedSub] = obj;
} else {
mBaseObjects[parsedSub] = new Object3D();
mBaseObjects[parsedSub].setData(vertices, normals, uvs, null, indices, false);
}
}
dis.readUserAttributes(null);
}
use of org.rajawali3d.animation.mesh.SkeletalAnimationChildObject3D.BoneWeight in project Rajawali by Rajawali.
the class LoaderMD5Mesh method buildMeshes.
private void buildMeshes() {
for (int i = 0; i < mNumMeshes; ++i) {
int boneIndex = 0;
SkeletonMeshData mesh = mMeshes[i];
mesh.vertices = new float[mesh.numVertices * 3];
mesh.indices = new int[mesh.numWeights];
mesh.weights = new float[mesh.numWeights];
mesh.textureCoordinates = new float[mesh.numVertices * 2];
int numVerts = mesh.numVertices;
for (int j = 0; j < numVerts; ++j) {
BoneVertex vert = mesh.boneVertices[j];
Vector3 position = new Vector3();
for (int k = 0; k < vert.numWeights; ++k) {
BoneWeight weight = mesh.boneWeights[vert.weightIndex + k];
SkeletonJoint joint = mJoints[weight.jointIndex];
Vector3 rotPos = joint.getOrientation().multiply(weight.position);
//We don't clone here because nothing will be able to use the quaternion scratch before we do
Vector3 pos = Vector3.addAndCreate(joint.getPosition(), rotPos);
pos.multiply(weight.weightValue);
position.add(pos);
mesh.indices[boneIndex] = weight.jointIndex;
mesh.weights[boneIndex++] = weight.weightValue;
}
int vertIndex = j * 3;
mesh.vertices[vertIndex] = (float) position.x;
mesh.vertices[vertIndex + 1] = (float) position.y;
mesh.vertices[vertIndex + 2] = (float) position.z;
int uvIndex = j * 2;
mesh.textureCoordinates[uvIndex] = (float) vert.textureCoordinate.getX();
mesh.textureCoordinates[uvIndex + 1] = (float) vert.textureCoordinate.getY();
}
}
}
Aggregations