use of gnu.trove.list.TIntList in project Terasology by MovingBlocks.
the class ColladaMeshFormat method load.
@Override
public MeshData load(ResourceUrn urn, List<AssetDataFile> inputs) throws IOException {
logger.info("Loading mesh for " + urn);
ColladaLoader loader = new ColladaLoader();
try (BufferedInputStream stream = inputs.get(0).openStream()) {
loader.parseMeshData(stream);
} catch (ColladaParseException e) {
throw new IOException("Error loading collada mesh for " + urn, e);
}
MeshData data = new MeshData();
TFloatList colorsMesh = data.getColors();
TFloatList verticesMesh = data.getVertices();
TFloatList texCoord0Mesh = data.getTexCoord0();
TFloatList normalsMesh = data.getNormals();
TIntList indicesMesh = data.getIndices();
// Scale vertices coordinates by unitsPerMeter
for (int i = 0; i < loader.getVertices().size(); i++) {
float originalVertexValue = loader.getVertices().get(i);
float adjustedVertexValue = (float) (originalVertexValue * loader.getUnitsPerMeter());
verticesMesh.add(adjustedVertexValue);
}
colorsMesh.addAll(loader.getColors());
texCoord0Mesh.addAll(loader.getTexCoord0());
normalsMesh.addAll(loader.getNormals());
indicesMesh.addAll(loader.getIndices());
if (data.getVertices() == null) {
throw new IOException("No vertices define");
}
if (((null == data.getColors()) || (0 == data.getColors().size())) && ((null == data.getTexCoord0()) || (0 == data.getTexCoord0().size()))) {
throw new IOException("There must be either texture coordinates or vertex colors provided.");
}
if ((null != data.getTexCoord0()) && (0 != data.getTexCoord0().size())) {
if (data.getTexCoord0().size() / 2 != data.getVertices().size() / 3) {
throw new IOException("The number of tex coords (" + data.getTexCoord0().size() / 2 + ") does not match the number of vertices (" + data.getVertices().size() / 3 + ").");
}
}
if ((null != data.getColors()) && (0 != data.getColors().size())) {
if (data.getColors().size() / 4 != data.getVertices().size() / 3) {
throw new IOException("The number of vertex colors (" + data.getColors().size() / 4 + ") does not match the number of vertices (" + data.getVertices().size() / 3 + ").");
}
}
return data;
}
use of gnu.trove.list.TIntList in project Terasology by MovingBlocks.
the class ObjMeshFormat method processData.
private MeshData processData(List<Vector3f> rawVertices, List<Vector3f> rawNormals, List<Vector2f> rawTexCoords, List<Vector3i[]> rawIndices) throws IOException {
MeshData result = new MeshData();
TFloatList vertices = result.getVertices();
TFloatList texCoord0 = result.getTexCoord0();
TFloatList normals = result.getNormals();
TIntList indices = result.getIndices();
int vertCount = 0;
for (Vector3i[] face : rawIndices) {
for (Vector3i indexSet : face) {
if (indexSet.x > rawVertices.size()) {
throw new IOException("Vertex index out of range: " + indexSet.x);
}
Vector3f vertex = rawVertices.get(indexSet.x - 1);
vertices.add(vertex.x);
vertices.add(vertex.y);
vertices.add(vertex.z);
if (indexSet.y != -1) {
if (indexSet.y > rawTexCoords.size()) {
throw new IOException("TexCoord index out of range: " + indexSet.y);
}
Vector2f texCoord = rawTexCoords.get(indexSet.y - 1);
texCoord0.add(texCoord.x);
texCoord0.add(1 - texCoord.y);
}
if (indexSet.z != -1) {
if (indexSet.z > rawNormals.size()) {
throw new IOException("Normal index out of range: " + indexSet.z);
}
Vector3f normal = rawNormals.get(indexSet.z - 1);
normals.add(normal.x);
normals.add(normal.y);
normals.add(normal.z);
}
}
for (int i = 0; i < face.length - 2; ++i) {
indices.add(vertCount);
indices.add(vertCount + i + 1);
indices.add(vertCount + i + 2);
}
vertCount += face.length;
}
return result;
}
use of gnu.trove.list.TIntList in project Terasology by MovingBlocks.
the class SkeletalMeshDataBuilder method addMesh.
public SkeletalMeshDataBuilder addMesh(Bone bone, MeshData data) {
TFloatList meshVertices = data.getVertices();
TIntList meshIndices = data.getIndices();
TFloatList texCoord0 = data.getTexCoord0();
int weightsStart = weights.size();
addBone(bone);
for (int i = 0; i < meshVertices.size() / 3; i++) {
float x = meshVertices.get(i * 3);
float y = meshVertices.get(i * 3 + 1);
float z = meshVertices.get(i * 3 + 2);
Vector3f pos = new Vector3f(x, y, z);
BoneWeight weight = new BoneWeight(pos, 1, bone.getIndex());
// TODO Meshes may contain normal vectors and we may copy them to the weight here
// - but they are recalculated later on in either case. needs some rework
addWeight(weight);
vertexStartWeights.add(weightsStart + i);
vertexWeightCounts.add(1);
uvs.add(new Vector2f(texCoord0.get(i * 2), texCoord0.get(i * 2 + 1)));
}
for (int i = 0; i < meshIndices.size(); i++) {
indices.add(meshIndices.get(i) + weightsStart);
}
return this;
}
use of gnu.trove.list.TIntList in project Terasology by MovingBlocks.
the class ColladaLoader method parseSkeletalMeshData.
protected void parseSkeletalMeshData(Element rootElement) throws ColladaParseException {
List<MD5Joint> md5JointList = new ArrayList<>();
List<MD5Mesh> md5MeshList = new ArrayList<>();
skeletonBuilder = new SkeletalMeshDataBuilder();
// TODO: we need a better way to construct the parent/child nodes, especially for the non-joint nodes
// MAYBE we can construct all of the nodes up-front, and then fill in the missing data for the ones of type JOINT later
// And only keep the MD5 nodes in the final list if they are used?
Map<String, MD5Joint> md5JointBySidMap = new HashMap<>();
MD5Joint parentMD5Joint = null;
ElementSet nodeParentSet = rootElement.find("library_visual_scenes", "visual_scene", "node");
for (Element element : nodeParentSet) {
createMd5JointForElementAndParent(md5JointBySidMap, element, parentMD5Joint);
}
ElementSet controllerSet = rootElement.find("library_controllers", "controller");
for (Element controller : controllerSet) {
ElementSet skinSet = controller.find("skin");
if (1 != skinSet.size()) {
throw new ColladaParseException("Found " + skinSet.size() + " skin sets for controller id=" + controller.id() + " name=" + controller.name());
}
Element skin = skinSet.first();
ElementSet jointsSet = skin.find("joints");
if (1 != jointsSet.size()) {
throw new ColladaParseException("Found " + jointsSet.size() + " joints sets for controller id=" + controller.id() + " name=" + controller.name());
}
Element joints = jointsSet.first();
ElementSet vertexWeightsSet = skin.find("vertex_weights");
if (1 != vertexWeightsSet.size()) {
throw new ColladaParseException("Found " + vertexWeightsSet.size() + " vertex weights sets for controller id=" + controller.id() + " name=" + controller.name());
}
Element vertexWeights = vertexWeightsSet.first();
String vertexWeightsCountString = vertexWeights.attr("count");
int vertexWeightsCount = Integer.parseInt(vertexWeightsCountString);
String[] jointNameArray = null;
float[] inverseBindMatrixArray;
Quat4f[] rotationArray;
ElementSet jointsInputSet = joints.find("input");
List<Input> inputList = parseInputs(jointsInputSet);
for (Input jointsInput : inputList) {
if ("JOINT".equals(jointsInput.semantic)) {
Element jointNameSourceElement = skin.select(jointsInput.sourceName);
Source jointNameSource = parseSource(jointNameSourceElement);
jointNameArray = jointNameSource.nameValues;
}
if ("INV_BIND_MATRIX".equals(jointsInput.semantic)) {
Element jointMatrixSourceElement = skin.select(jointsInput.sourceName);
Source jointMatrixSource = parseSource(jointMatrixSourceElement);
inverseBindMatrixArray = jointMatrixSource.floatValues;
rotationArray = quad4fArrayFromFloat16ArrayData(inverseBindMatrixArray);
}
}
List<MD5Weight> md5WeightList = Lists.newArrayList();
float[] weightsArray = null;
ElementSet vertexWeightsInputSet = vertexWeights.find("input");
List<Input> vertexWeightsInputList = parseInputs(vertexWeightsInputSet);
// TODO: for now, assume the offsets will always perfectly match the sorted-by-offset list indexes
Collections.sort(vertexWeightsInputList, (i1, i2) -> i1.offset - i2.offset);
for (int i = 0; i < vertexWeightsInputList.size(); i++) {
Input input = vertexWeightsInputList.get(i);
if (input.offset != i) {
throw new ColladaParseException("vertex weights input list offset does not match list index for vertex weights input " + input + " for controller id=" + controller.id() + " name=" + controller.name());
}
}
for (Input vertexWeightsInput : vertexWeightsInputList) {
// }
if ("WEIGHT".equals(vertexWeightsInput.semantic)) {
Element jointMatrixSourceElement = skin.select(vertexWeightsInput.sourceName);
Source weightsArraySource = parseSource(jointMatrixSourceElement);
weightsArray = weightsArraySource.floatValues;
}
}
ElementSet vertexWeightsVCountDataSet = vertexWeights.find("vcount");
if (1 != vertexWeightsVCountDataSet.size()) {
throw new ColladaParseException("Found " + vertexWeightsVCountDataSet.size() + " vertex weights vcount sets for controller id=" + controller.id() + " name=" + controller.name());
}
Element vertexWeightsVCountData = vertexWeightsVCountDataSet.first();
String vertexWeightsVCountString = vertexWeightsVCountData.text();
String[] vertexWeightsVCountStrings = getItemsInString(vertexWeightsVCountString);
if (vertexWeightsVCountStrings.length != vertexWeightsCount) {
throw new ColladaParseException("Expected " + vertexWeightsCount + " but was " + vertexWeightsVCountStrings.length + " for controller id=" + controller.id() + " name=" + controller.name());
}
ElementSet vertexWeightsVDataSet = vertexWeights.find("v");
if (1 != vertexWeightsVDataSet.size()) {
throw new ColladaParseException("Found " + vertexWeightsVDataSet.size() + " vertex weights v sets for controller id=" + controller.id() + " name=" + controller.name());
}
Element vertexWeightsVData = vertexWeightsVDataSet.first();
String vertexWeightsVDataString = vertexWeightsVData.text();
String[] vertexWeightsVStrings = getItemsInString(vertexWeightsVDataString);
// if (vertexWeightsVStrings.length != (vertexWeightsCount * vertexWeightsInputList.size())) {
// throw new ColladaParseException("Expected " + vertexWeightsCount + " * input count of "
// + vertexWeightsInputList.size() + " but was "
// + vertexWeightsVStrings.length + " for controller id=" + controller.id() + " name=" + controller.name());
// }
// TODO: these aren't actually needed once we are populating MD5Weight records
String[] vertexWeightsJointNameArray = new String[vertexWeightsCount];
float[] vertexWeightsArray = new float[vertexWeightsCount];
int vertexWeightsVDataIndex = -1;
for (int vertexWeightsIndex = 0; vertexWeightsIndex < vertexWeightsCount; vertexWeightsIndex++) {
MD5Weight md5Weight = new MD5Weight();
Vector3f vertexPosition = new Vector3f();
vertexPosition.x = vertices.get(3 * vertexWeightsIndex + 0);
vertexPosition.y = vertices.get(3 * vertexWeightsIndex + 1);
vertexPosition.z = vertices.get(3 * vertexWeightsIndex + 2);
md5Weight.position = vertexPosition;
md5WeightList.add(md5Weight);
String vCountString = vertexWeightsVCountStrings[vertexWeightsIndex];
int vCount = Integer.parseInt(vCountString);
for (int vCountIndex = 0; vCountIndex < vCount; vCountIndex++) {
for (Input vertexWeightsInput : vertexWeightsInputList) {
// vCount varies each time
++vertexWeightsVDataIndex;
String indexString = vertexWeightsVStrings[vertexWeightsVDataIndex];
int index = Integer.parseInt(indexString);
if (-1 == index) {
throw new ColladaParseException("We do not support indexing into the bind shape yet");
}
if ("JOINT".equals(vertexWeightsInput.semantic)) {
md5Weight.jointIndex = index;
vertexWeightsJointNameArray[vertexWeightsIndex] = jointNameArray[index];
// logger.debug(String.valueOf(vertexWeightsVDataIndex) + ": " + "jointName=" + vertexWeightsJointNameArray[vertexWeightsIndex]);
} else if ("WEIGHT".equals(vertexWeightsInput.semantic)) {
md5Weight.bias = weightsArray[index];
vertexWeightsArray[vertexWeightsIndex] = weightsArray[index];
// logger.debug(String.valueOf(vertexWeightsVDataIndex) + ": " + "weight=" + vertexWeightsArray[vertexWeightsIndex]);
} else {
throw new ColladaParseException("Found unexpected vertex weights Input semantic " + vertexWeightsInput.semantic + " for controller id=" + controller.id() + " name=" + controller.name());
}
}
}
}
MD5Mesh md5Mesh = new MD5Mesh();
md5Mesh.weightList = md5WeightList;
// Find a node with sid="joint-name"
for (String jointName : jointNameArray) {
MD5Joint md5Joint = md5JointBySidMap.get(jointName);
if (null == md5Joint) {
throw new ColladaParseException("Cannot find joint node for node sid value for joint " + jointName + " in nodes for library_visual_scenes");
}
md5JointList.add(md5Joint);
}
}
Deque<MD5Joint> jointsToProcess = new LinkedList<>(md5JointList);
while (!jointsToProcess.isEmpty()) {
MD5Joint joint = jointsToProcess.pop();
MD5Joint parentJoint = joint.parent;
if (null != parentJoint) {
if (!md5JointList.contains(parentJoint)) {
md5JointList.add(parentJoint);
jointsToProcess.push(parentJoint);
}
}
}
for (MD5Joint joint : md5JointList) {
if (null == joint.position) {
throw new ColladaParseException("no joint position for joint with element id " + joint.element.id());
}
if (null == joint.orientation) {
throw new ColladaParseException("no joint orientation for joint with element id " + joint.element.id());
}
// index argument is not used for anything currently, so we'll just set it to -1
joint.bone = new Bone(-1, joint.name, joint.position, joint.orientation);
}
for (MD5Joint joint : md5JointList) {
// We can probably skip unused end nodes
joint.childList.stream().filter(childJoint -> childJoint.bone != null).forEach(childJoint -> joint.bone.addChild(childJoint.bone));
}
for (MD5Joint joint : md5JointList) {
skeletonBuilder.addBone(joint.bone);
}
if (md5MeshList.size() > 0) {
// TODO: Support multiple mesh somehow?
MD5Mesh mesh = md5MeshList.get(0);
for (MD5Weight weight : mesh.weightList) {
skeletonBuilder.addWeight(new BoneWeight(weight.position, weight.bias, weight.jointIndex));
}
List<Vector2f> uvs = Lists.newArrayList();
TIntList vertexStartWeight = new TIntArrayList(vertices.size() / 3);
TIntList vertexWeightCount = new TIntArrayList(vertices.size() / 3);
for (int i = 0; i < vertices.size() / 3; i++) {
vertexStartWeight.add(i);
vertexWeightCount.add(1);
}
skeletonBuilder.setVertexWeights(vertexStartWeight, vertexWeightCount);
for (int i = 0; i < normals.size() / 2; i++) {
uvs.add(new Vector2f(normals.get(i * 2 + 0), normals.get(i * 2 + 1)));
}
skeletonBuilder.setUvs(uvs);
skeletonBuilder.setIndices(indices);
}
// Now if you have come this far, you should be able to read the geometry data,
// as well as the skeleton and skinning data from COLLADA documents. And you should be able to draw
// the model in raw triangles, as well as draw the skeleton. Although I haven't discussed how you
// can accumulate the world matrices for each joint and then draw in world coordinates for debugging
// purposes but I think I gave a hint that we have to multiply parent joint's world matrix with current
// joint's Joint matrix and save the result in current joint's world matrix. We have to start this
// process from the root bone. So that we don't have dirty world matrices from parents, and the root
// Joint's world matrix becomes the Joint matrix, since root don't have any parent. If you are also
// reading the COLLADA specification version 1.5 you can find the skinning equation so you should also
// be able to put the model in bind shape. How can we animate this model is still not covered and will
// be covered in the following sections.
// THIS IS THE TARGET GOAL:
/*
Bones
- String name
- int index
- V3 object position
- Quat4f obj rotation
- parent / children bones
SkeletalMesh
// This part may not be required if we can implement SkeletalMeshData methods without it
//////////////
public SkeletalMeshData(List<Bone> bones, List<BoneWeight> weights,
List<Vector2f> uvs,
TIntList vertexStartWeights, TIntList vertexWeightCounts,
TIntList indices) {
BoneWeight
Vector3f position = new Vector3f();
float bias;
int boneIndex;
Vector3f normal = new Vector3f();
//////////////
public Collection<Bone> getBones();
public Bone getRootBone();
public Bone getBone(String name);
public int getVertexCount();
public List<Vector3f> getBindPoseVertexPositions();
public List<Vector3f> getVertexPositions(List<Vector3f> bonePositions, List<Quat4f> boneRotations);
public List<Vector3f> getBindPoseVertexNormals();
public List<Vector3f> getVertexNormals(List<Vector3f> bonePositions, List<Quat4f> boneRotations);
public TIntList getIndices();
public List<Vector2f> getUVs();
*/
}
use of gnu.trove.list.TIntList in project Terasology by MovingBlocks.
the class ColladaLoader method parseMeshData.
protected void parseMeshData(Element rootElement) throws ColladaParseException {
vertices = new TFloatArrayList();
texCoord0 = new TFloatArrayList();
texCoord1 = new TFloatArrayList();
normals = new TFloatArrayList();
colors = new TFloatArrayList();
indices = new TIntArrayList();
int vertCount = 0;
ElementSet upAxisSet = rootElement.find("asset", "up_axis");
if (1 != upAxisSet.size()) {
throw new ColladaParseException("Found multiple up_axis asset values");
}
Element upAxisElement = upAxisSet.first();
String upAxis = upAxisElement.text();
ElementSet unitSet = rootElement.find("asset", "unit");
if (1 != unitSet.size()) {
throw new ColladaParseException("Found multiple unit asset values");
}
Element unitElement = unitSet.first();
String unitsPerMeterString = unitElement.attr("meter");
if (null != unitsPerMeterString) {
unitsPerMeter = Double.parseDouble(unitsPerMeterString);
}
boolean yUp = "Y_UP".equals(upAxis);
boolean zUp = "Z_UP".equals(upAxis);
boolean xUp = "X_UP".equals(upAxis);
if (xUp) {
throw new ColladaParseException("Not supporting X_UP as the upAxis value yet.");
}
// TODO: we shouldn't just cram everything into a single mesh, but should expect separate meshes with differing materials
ElementSet geometrySet = rootElement.find("library_geometries", "geometry");
for (Element geometry : geometrySet) {
ElementSet meshSet = geometry.find("mesh");
if (1 != meshSet.size()) {
throw new ColladaParseException("Found " + meshSet.size() + " mesh sets for geometry id=" + geometry.id() + " name=" + geometry.name());
}
logger.info("Parsing geometry id=" + geometry.id() + " name=" + geometry.name());
for (Element mesh : meshSet) {
ElementSet trianglesSet = mesh.find("triangles");
for (Element triangles : trianglesSet) {
vertCount = parseTriangles(rootElement, vertices, texCoord0, normals, indices, colors, vertCount, geometry, mesh, triangles, yUp, zUp);
}
ElementSet polylistSet = mesh.find("polylist");
for (Element polylist : polylistSet) {
ElementSet vCountSet = polylist.find("vcount");
if (1 != vCountSet.size()) {
throw new ColladaParseException("Found " + vCountSet.size() + " vcount sets for polylist in geometry id=" + geometry.id() + " name=" + geometry.name());
}
Element vCountElement = vCountSet.first();
TIntList vcountList = new TIntArrayList();
String[] vCountStrings = getItemsInString(vCountElement.text());
for (String string : vCountStrings) {
int vCount = Integer.parseInt(string);
vcountList.add(vCount);
}
vertCount = parseFaces(rootElement, vcountList, vertices, texCoord0, normals, indices, colors, vertCount, geometry, mesh, polylist, yUp, zUp);
}
}
}
}
Aggregations