Search in sources :

Example 1 with Element

use of org.eaxy.Element in project Terasology by MovingBlocks.

the class ColladaLoader method parseSource.

private Source parseSource(Element sourceElement) throws ColladaParseException {
    Source source = new Source();
    ElementSet accessorSet = sourceElement.find("technique_common", "accessor");
    if (1 != accessorSet.size()) {
        throw new ColladaParseException("Found " + accessorSet.size() + " accessor sets for sourceElement id=" + sourceElement.id() + " name=" + sourceElement.name());
    }
    Element accessor = accessorSet.first();
    String accessorCount = accessor.attr("count");
    source.count = Integer.parseInt(accessorCount);
    String accessorStride = accessor.attr("stride");
    if (null != accessorStride) {
        source.stride = Integer.parseInt(accessorStride);
    }
    String accessorSource = accessor.attr("source");
    source.accessorSource = accessorSource;
    ElementSet paramSet = accessor.find("param");
    int paramSize = paramSet.size();
    source.parameterNames = new String[paramSize];
    source.parameterTypes = new String[paramSize];
    for (int i = 0; i < paramSize; i++) {
        Element param = paramSet.get(i);
        source.parameterNames[i] = param.attr("name");
        source.parameterTypes[i] = param.attr("type");
    }
    Element objectArray = sourceElement.select(accessorSource);
    if (null == objectArray) {
        throw new ColladaParseException("Unable to find id " + accessorSource + " for float array in sourceElement id=" + sourceElement.id() + " name=" + sourceElement.name());
    }
    String arraySizeString = objectArray.attr("count");
    int arraySize = Integer.parseInt(arraySizeString);
    String objectArrayDataString = objectArray.text().trim();
    // TODO: we should really parse each parameter type, but we'll assume they are homogeneneous for now
    if (("float".equalsIgnoreCase(source.parameterTypes[0])) || ("float4x4".equalsIgnoreCase(source.parameterTypes[0]))) {
        source.floatValues = new float[arraySize];
        String[] floatStrings = getItemsInString(objectArrayDataString);
        if (floatStrings.length != arraySize) {
            throw new ColladaParseException("Expected float array size " + arraySize + " but was " + floatStrings.length + " for sourceElement id=" + sourceElement.id() + " name=" + sourceElement.name());
        }
        for (int i = 0; i < floatStrings.length; i++) {
            String floatString = floatStrings[i];
            source.floatValues[i] = Float.parseFloat(floatString);
        }
    } else if ("name".equalsIgnoreCase(source.parameterTypes[0])) {
        source.nameValues = new String[arraySize];
        String[] nameStrings = getItemsInString(objectArrayDataString);
        if (nameStrings.length != arraySize) {
            throw new ColladaParseException("Expected name array size " + arraySize + " but was " + nameStrings.length + " for sourceElement id=" + sourceElement.id() + " name=" + sourceElement.name());
        }
        for (int i = 0; i < nameStrings.length; i++) {
            source.nameValues[i] = nameStrings[i];
        }
    } else {
        throw new ColladaParseException("Unsupported parameter type " + source.parameterTypes[0]);
    }
    return source;
}
Also used : ElementSet(org.eaxy.ElementSet) Element(org.eaxy.Element)

Example 2 with Element

use of org.eaxy.Element in project Terasology by MovingBlocks.

the class ColladaLoader method createMD5Joint.

private MD5Joint createMD5Joint(Element jointNodeElement) throws ColladaParseException {
    MD5Joint md5Joint = new MD5Joint();
    ElementSet matrixSet = jointNodeElement.find("matrix");
    if (1 == matrixSet.size()) {
        Element matrix = matrixSet.first();
        String floatStringArray = matrix.text();
        String[] floatStrings = getItemsInString(floatStringArray);
        if (floatStrings.length != 16) {
            throw new ColladaParseException("Found float list of " + floatStrings.length + " instead of 16 for joint matrix sets for element " + jointNodeElement.id());
        }
        float[] matrixDataArray = new float[16];
        for (int i = 0; i < floatStrings.length; i++) {
            String floatString = floatStrings[i];
            matrixDataArray[i] = Float.parseFloat(floatString);
        }
        Quat4f[] jointMatrix = quad4fArrayFromFloat16ArrayData(matrixDataArray);
        Vector3f[] positionVectorArray = positionFromFloat16ArrayData(matrixDataArray);
        md5Joint.position = positionVectorArray[0];
        md5Joint.orientation = jointMatrix[0];
    } else if (1 < matrixSet.size()) {
        throw new ColladaParseException("Found " + matrixSet.size() + " joint matrix sets for element " + jointNodeElement.id());
    // } else {
    // TODO: Might be translation, rotation pairs instead of a matrix
    // Or might be an unused joint node
    // throw new ColladaParseException("Found " + matrixSet.size() + " joint matrix sets for element " + jointNodeElement.id());
    }
    // boolean isJointNode;
    // String jointType = jointNodeElement.attr("type");
    // if ("JOINT".equals(jointType)) {
    // isJointNode = true;
    // } else if ("NODE".equals(jointType)) {
    // isJointNode = false;
    // } else {
    // throw new ColladaParseException("Found unknown node type of " + jointType + " for joint matrix sets" + errorLocation);
    // }
    md5Joint.element = jointNodeElement;
    md5Joint.name = jointNodeElement.id();
    md5Joint.childList = new ArrayList<>();
    return md5Joint;
}
Also used : ElementSet(org.eaxy.ElementSet) Element(org.eaxy.Element) Vector3f(org.terasology.math.geom.Vector3f) Quat4f(org.terasology.math.geom.Quat4f)

Example 3 with Element

use of org.eaxy.Element 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();
         */
}
Also used : Bone(org.terasology.rendering.assets.skeletalmesh.Bone) Arrays(java.util.Arrays) BoneWeight(org.terasology.rendering.assets.skeletalmesh.BoneWeight) TIntArrayList(gnu.trove.list.array.TIntArrayList) Element(org.eaxy.Element) LoggerFactory(org.slf4j.LoggerFactory) Vector3f(org.terasology.math.geom.Vector3f) SkeletalMeshDataBuilder(org.terasology.rendering.assets.skeletalmesh.SkeletalMeshDataBuilder) HashMap(java.util.HashMap) Deque(java.util.Deque) Document(org.eaxy.Document) Matrix4f(org.terasology.math.geom.Matrix4f) ArrayList(java.util.ArrayList) ElementSet(org.eaxy.ElementSet) Lists(com.google.common.collect.Lists) Map(java.util.Map) TFloatList(gnu.trove.list.TFloatList) LinkedList(java.util.LinkedList) TIntList(gnu.trove.list.TIntList) TFloatArrayList(gnu.trove.list.array.TFloatArrayList) Logger(org.slf4j.Logger) SkeletalMeshData(org.terasology.rendering.assets.skeletalmesh.SkeletalMeshData) NonMatchingPathException(org.eaxy.NonMatchingPathException) Vector2f(org.terasology.math.geom.Vector2f) IOException(java.io.IOException) List(java.util.List) Quat4f(org.terasology.math.geom.Quat4f) Collections(java.util.Collections) Xml(org.eaxy.Xml) InputStream(java.io.InputStream) ElementSet(org.eaxy.ElementSet) HashMap(java.util.HashMap) Element(org.eaxy.Element) TIntArrayList(gnu.trove.list.array.TIntArrayList) ArrayList(java.util.ArrayList) TFloatArrayList(gnu.trove.list.array.TFloatArrayList) BoneWeight(org.terasology.rendering.assets.skeletalmesh.BoneWeight) SkeletalMeshDataBuilder(org.terasology.rendering.assets.skeletalmesh.SkeletalMeshDataBuilder) Vector2f(org.terasology.math.geom.Vector2f) LinkedList(java.util.LinkedList) TIntArrayList(gnu.trove.list.array.TIntArrayList) Vector3f(org.terasology.math.geom.Vector3f) Bone(org.terasology.rendering.assets.skeletalmesh.Bone) TIntList(gnu.trove.list.TIntList) Quat4f(org.terasology.math.geom.Quat4f)

Example 4 with Element

use of org.eaxy.Element in project Terasology by MovingBlocks.

the class ColladaLoader method parseSkeletalMeshData.

public SkeletalMeshData parseSkeletalMeshData(InputStream inputStream) throws ColladaParseException, IOException {
    Document document = Xml.readAndClose(inputStream);
    Element rootElement = document.getRootElement();
    parseMeshData(rootElement);
    parseSkeletalMeshData(rootElement);
    return skeletonBuilder.build();
}
Also used : Element(org.eaxy.Element) Document(org.eaxy.Document)

Example 5 with Element

use of org.eaxy.Element in project Terasology by MovingBlocks.

the class ColladaLoader method createMd5JointForElementAndParent.

private void createMd5JointForElementAndParent(Map<String, MD5Joint> md5JointBySidMap, Element element, MD5Joint parentMD5Joint) throws ColladaParseException {
    MD5Joint joint = createMD5Joint(element);
    joint.element = element;
    joint.parent = parentMD5Joint;
    if (null != parentMD5Joint) {
        parentMD5Joint.addChild(joint);
    }
    ElementSet elementChildSet = element.find("node");
    for (Element childElement : elementChildSet) {
        createMd5JointForElementAndParent(md5JointBySidMap, childElement, joint);
    }
    String sid = element.attr("sid");
    if (null != sid) {
        md5JointBySidMap.put(sid, joint);
    }
}
Also used : ElementSet(org.eaxy.ElementSet) Element(org.eaxy.Element)

Aggregations

Element (org.eaxy.Element)9 ElementSet (org.eaxy.ElementSet)6 TFloatArrayList (gnu.trove.list.array.TFloatArrayList)3 TIntArrayList (gnu.trove.list.array.TIntArrayList)3 Document (org.eaxy.Document)3 TIntList (gnu.trove.list.TIntList)2 ArrayList (java.util.ArrayList)2 NonMatchingPathException (org.eaxy.NonMatchingPathException)2 Quat4f (org.terasology.math.geom.Quat4f)2 Vector3f (org.terasology.math.geom.Vector3f)2 Lists (com.google.common.collect.Lists)1 TFloatList (gnu.trove.list.TFloatList)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 Deque (java.util.Deque)1 HashMap (java.util.HashMap)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1