com.ardor3d.extension.model.collada.jdom.ColladaNodeUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.ardor3d.extension.model.collada.jdom.ColladaNodeUtils.java

Source

/**
 * Copyright (c) 2008-2012 Ardor Labs, Inc.
 *
 * This file is part of Ardor3D.
 *
 * Ardor3D is free software: you can redistribute it and/or modify it 
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
 */

package com.ardor3d.extension.model.collada.jdom;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom2.Element;

import com.ardor3d.extension.animation.skeletal.AttachmentPoint;
import com.ardor3d.extension.animation.skeletal.Joint;
import com.ardor3d.extension.animation.skeletal.Skeleton;
import com.ardor3d.extension.model.collada.jdom.data.AssetData;
import com.ardor3d.extension.model.collada.jdom.data.ControllerStore;
import com.ardor3d.extension.model.collada.jdom.data.DataCache;
import com.ardor3d.extension.model.collada.jdom.data.JointNode;
import com.ardor3d.extension.model.collada.jdom.data.NodeType;
import com.ardor3d.math.MathUtils;
import com.ardor3d.math.Matrix3;
import com.ardor3d.math.Matrix4;
import com.ardor3d.math.Transform;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.Vector4;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.Spatial;
import com.google.common.collect.Lists;

/**
 * Methods for parsing Collada data related to scenes and node hierarchy.
 */
public class ColladaNodeUtils {
    private static final Logger logger = Logger.getLogger(ColladaNodeUtils.class.getName());

    private final DataCache _dataCache;
    private final ColladaDOMUtil _colladaDOMUtil;
    private final ColladaMaterialUtils _colladaMaterialUtils;
    private final ColladaMeshUtils _colladaMeshUtils;
    private final ColladaAnimUtils _colladaAnimUtils;

    public ColladaNodeUtils(final DataCache dataCache, final ColladaDOMUtil colladaDOMUtil,
            final ColladaMaterialUtils colladaMaterialUtils, final ColladaMeshUtils colladaMeshUtils,
            final ColladaAnimUtils colladaAnimUtils) {
        _dataCache = dataCache;
        _colladaDOMUtil = colladaDOMUtil;
        _colladaMaterialUtils = colladaMaterialUtils;
        _colladaMeshUtils = colladaMeshUtils;
        _colladaAnimUtils = colladaAnimUtils;
    }

    /**
     * Retrieves the scene and returns it as an Ardor3D Node.
     * 
     * @param colladaRoot
     *            The collada root element
     * @return Scene as an Node or null if not found
     */
    @SuppressWarnings("unchecked")
    public Node getVisualScene(final Element colladaRoot) {
        if (colladaRoot.getChild("scene") == null) {
            logger.warning("No scene found in collada file!");
            return null;
        }

        final Element instance_visual_scene = colladaRoot.getChild("scene").getChild("instance_visual_scene");
        if (instance_visual_scene == null) {
            logger.warning("No instance_visual_scene found in collada file!");
            return null;
        }

        final Element visualScene = _colladaDOMUtil
                .findTargetWithId(instance_visual_scene.getAttributeValue("url"));

        if (visualScene != null) {
            final Node sceneRoot = new Node(
                    visualScene.getAttributeValue("name") != null ? visualScene.getAttributeValue("name")
                            : "Collada Root");

            // Load each sub node and attach
            final JointNode baseJointNode = new JointNode(null);
            _dataCache.setRootJointNode(baseJointNode);
            for (final Element n : visualScene.getChildren("node")) {
                final Node subNode = buildNode(n, baseJointNode);
                if (subNode != null) {
                    sceneRoot.attachChild(subNode);
                }
            }

            // build a list of joints - one list per skeleton - and build a skeleton for each joint list.
            for (final JointNode jointChildNode : _dataCache.getRootJointNode().getChildren()) {
                final List<Joint> jointList = Lists.newArrayList();
                buildJointLists(jointChildNode, jointList);
                final Joint[] joints = jointList.toArray(new Joint[jointList.size()]);
                final Skeleton skeleton = new Skeleton(joints[0].getName() + "_skeleton", joints);
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("skeleton created: " + skeleton.getName());
                }
                for (final Joint joint : jointList) {
                    _dataCache.getJointSkeletonMapping().put(joint, skeleton);
                    if (logger.isLoggable(Level.FINE)) {
                        logger.fine("- Joint " + joint.getName() + " - index: " + joint.getIndex()
                                + " parent index: " + joint.getParentIndex());
                    }
                }
                _dataCache.addSkeleton(skeleton);
            }

            // update our world transforms so we can use them to init the default bind matrix of any joints.
            sceneRoot.updateWorldTransform(true);
            initDefaultJointTransforms(baseJointNode);

            // Setup our skinned mesh objects.
            for (final ControllerStore controllerStore : _dataCache.getControllers()) {
                _colladaMaterialUtils.bindMaterials(controllerStore.instanceController.getChild("bind_material"));
                _colladaAnimUtils.buildController(controllerStore.ardorParentNode,
                        controllerStore.instanceController);
                _colladaMaterialUtils.unbindMaterials(controllerStore.instanceController.getChild("bind_material"));
            }

            return sceneRoot;
        }
        return null;
    }

    private void buildJointLists(final JointNode jointNode, final List<Joint> jointList) {
        final Joint joint = jointNode.getJoint();
        joint.setIndex((short) jointList.size());
        if (jointNode.getParent().getJoint() != null) {
            joint.setParentIndex(jointNode.getParent().getJoint().getIndex());
        } else {
            joint.setParentIndex(Joint.NO_PARENT);
        }
        jointList.add(joint);
        for (final JointNode jointChildNode : jointNode.getChildren()) {
            buildJointLists(jointChildNode, jointList);
        }
    }

    private void initDefaultJointTransforms(final JointNode jointNode) {
        final Joint j = jointNode.getJoint();
        if (j != null && jointNode.getSceneNode() != null) {
            j.setInverseBindPose(jointNode.getSceneNode().getWorldTransform().invert(null));
        }
        for (final JointNode jointChildNode : jointNode.getChildren()) {
            initDefaultJointTransforms(jointChildNode);
        }
    }

    /**
     * Parse an asset element into an AssetData object.
     * 
     * @param asset
     * @return
     */
    @SuppressWarnings("unchecked")
    public AssetData parseAsset(final Element asset) {
        final AssetData assetData = new AssetData();

        for (final Element child : asset.getChildren()) {
            if ("contributor".equals(child.getName())) {
                parseContributor(assetData, child);
            } else if ("created".equals(child.getName())) {
                assetData.setCreated(child.getText());
            } else if ("keywords".equals(child.getName())) {
                assetData.setKeywords(child.getText());
            } else if ("modified".equals(child.getName())) {
                assetData.setModified(child.getText());
            } else if ("revision".equals(child.getName())) {
                assetData.setRevision(child.getText());
            } else if ("subject".equals(child.getName())) {
                assetData.setSubject(child.getText());
            } else if ("title".equals(child.getName())) {
                assetData.setTitle(child.getText());
            } else if ("unit".equals(child.getName())) {
                final String name = child.getAttributeValue("name");
                if (name != null) {
                    assetData.setUnitName(name);
                }
                final String meter = child.getAttributeValue("meter");
                if (meter != null) {
                    assetData.setUnitMeter(Float.parseFloat(meter.replace(",", ".")));
                }
            } else if ("up_axis".equals(child.getName())) {
                final String axis = child.getText();
                if ("X_UP".equals(axis)) {
                    assetData.setUpAxis(new Vector3());
                } else if ("Y_UP".equals(axis)) {
                    assetData.setUpAxis(Vector3.UNIT_Y);
                } else if ("Z_UP".equals(axis)) {
                    assetData.setUpAxis(Vector3.UNIT_Z);
                }
            }
        }

        return assetData;
    }

    @SuppressWarnings("unchecked")
    private void parseContributor(final AssetData assetData, final Element contributor) {
        for (final Element child : contributor.getChildren()) {
            if ("author".equals(child.getName())) {
                assetData.setAuthor(child.getText());
            } else if ("authoringTool".equals(child.getName())) {
                assetData.setCreated(child.getText());
            } else if ("comments".equals(child.getName())) {
                assetData.setComments(child.getText());
            } else if ("copyright".equals(child.getName())) {
                assetData.setCopyright(child.getText());
            } else if ("source_data".equals(child.getName())) {
                assetData.setSourceData(child.getText());
            }
        }
    }

    /**
     * @param instanceNode
     * @return a new Ardor3D node, created from the <node> pointed to by the given <instance_node> element
     */
    public Node getNode(final Element instanceNode, final JointNode jointNode) {
        final Element node = _colladaDOMUtil.findTargetWithId(instanceNode.getAttributeValue("url"));

        if (node == null) {
            throw new ColladaException("No node with id: " + instanceNode.getAttributeValue("url") + " found",
                    instanceNode);
        }

        return buildNode(node, jointNode);
    }

    /**
     * Recursively parse the node hierarcy.
     * 
     * @param dNode
     * @return a new Ardor3D node, created from the given <node> element
     */
    @SuppressWarnings("unchecked")
    private Node buildNode(final Element dNode, JointNode jointNode) {
        final NodeType nodeType = getNodeType(dNode);
        final JointNode jointChildNode;
        if (nodeType == NodeType.JOINT) {
            String name = dNode.getAttributeValue("name");
            if (name == null) {
                name = dNode.getAttributeValue("id");
            }
            if (name == null) {
                name = dNode.getAttributeValue("sid");
            }
            final Joint joint = new Joint(name);
            jointChildNode = new JointNode(joint);
            jointChildNode.setParent(jointNode);
            jointNode.getChildren().add(jointChildNode);
            jointNode = jointChildNode;

            _dataCache.getElementJointMapping().put(dNode, joint);
        } else {
            jointChildNode = null;
        }

        String nodeName = dNode.getAttributeValue("name", (String) null);
        if (nodeName == null) { // use id if name doesn't exist
            nodeName = dNode.getAttributeValue("id", dNode.getName());
        }
        final Node node = new Node(nodeName);

        final List<Element> transforms = new ArrayList<Element>();
        for (final Element child : dNode.getChildren()) {
            if (_dataCache.getTransformTypes().contains(child.getName())) {
                transforms.add(child);
            }
        }

        // process any transform information.
        if (!transforms.isEmpty()) {
            final Transform localTransform = getNodeTransforms(transforms);

            node.setTransform(localTransform);
            if (jointChildNode != null) {
                jointChildNode.setSceneNode(node);
            }
        }

        // process any instance geometries
        for (final Element instance_geometry : dNode.getChildren("instance_geometry")) {
            _colladaMaterialUtils.bindMaterials(instance_geometry.getChild("bind_material"));

            final Spatial mesh = _colladaMeshUtils.getGeometryMesh(instance_geometry);
            if (mesh != null) {
                node.attachChild(mesh);
            }

            _colladaMaterialUtils.unbindMaterials(instance_geometry.getChild("bind_material"));
        }

        // process any instance controllers
        for (final Element instanceController : dNode.getChildren("instance_controller")) {
            _dataCache.getControllers().add(new ControllerStore(node, instanceController));
        }

        // process any instance nodes
        for (final Element in : dNode.getChildren("instance_node")) {
            final Node subNode = getNode(in, jointNode);
            if (subNode != null) {
                node.attachChild(subNode);
                if (nodeType == NodeType.JOINT && getNodeType(
                        _colladaDOMUtil.findTargetWithId(in.getAttributeValue("url"))) == NodeType.NODE) {
                    // make attachment
                    createJointAttachment(jointChildNode, node, subNode);
                }
            }
        }

        // process any concrete child nodes.
        for (final Element n : dNode.getChildren("node")) {
            final Node subNode = buildNode(n, jointNode);
            if (subNode != null) {
                node.attachChild(subNode);
                if (nodeType == NodeType.JOINT && getNodeType(n) == NodeType.NODE) {
                    // make attachment
                    createJointAttachment(jointChildNode, node, subNode);
                }
            }
        }

        // Cache reference
        _dataCache.getElementSpatialMapping().put(dNode, node);

        return node;
    }

    protected void createJointAttachment(final JointNode jointChildNode, final Node node, final Node subNode) {
        final AttachmentPoint attach = new AttachmentPoint("attach-" + node.getName(), (short) 0, subNode,
                new Transform(subNode.getTransform()));
        _dataCache.addAttachmentPoint(jointChildNode.getJoint(), attach);
        // we will attach to scene instead.
        subNode.removeFromParent();
    }

    private NodeType getNodeType(final Element dNode) {
        if (dNode.getAttribute("type") != null) {
            return Enum.valueOf(NodeType.class, dNode.getAttributeValue("type"));
        } else {
            return NodeType.NODE;
        }
    }

    /**
     * Combines a list of transform elements into an Ardor3D Transform object.
     * 
     * @param transforms
     *            List of transform elements
     * @return an Ardor3D Transform object
     */
    public Transform getNodeTransforms(final List<Element> transforms) {
        final Matrix4 workingMat = Matrix4.fetchTempInstance();
        final Matrix4 finalMat = Matrix4.fetchTempInstance();
        finalMat.setIdentity();
        for (final Element transform : transforms) {
            final double[] array = _colladaDOMUtil.parseDoubleArray(transform);
            if ("translate".equals(transform.getName())) {
                workingMat.setIdentity();
                workingMat.setColumn(3, new Vector4(array[0], array[1], array[2], 1.0));
                finalMat.multiplyLocal(workingMat);
            } else if ("rotate".equals(transform.getName())) {
                if (array[3] != 0) {
                    workingMat.setIdentity();
                    final Matrix3 rotate = new Matrix3().fromAngleAxis(array[3] * MathUtils.DEG_TO_RAD,
                            new Vector3(array[0], array[1], array[2]));
                    workingMat.set(rotate);
                    finalMat.multiplyLocal(workingMat);
                }
            } else if ("scale".equals(transform.getName())) {
                workingMat.setIdentity();
                workingMat.scale(new Vector4(array[0], array[1], array[2], 1), workingMat);
                finalMat.multiplyLocal(workingMat);
            } else if ("matrix".equals(transform.getName())) {
                workingMat.fromArray(array);
                finalMat.multiplyLocal(workingMat);
            } else if ("lookat".equals(transform.getName())) {
                final Vector3 pos = new Vector3(array[0], array[1], array[2]);
                final Vector3 target = new Vector3(array[3], array[4], array[5]);
                final Vector3 up = new Vector3(array[6], array[7], array[8]);
                final Matrix3 rot = new Matrix3();
                rot.lookAt(target.subtractLocal(pos), up);
                workingMat.set(rot);
                workingMat.setColumn(3, new Vector4(array[0], array[1], array[2], 1));
                finalMat.multiplyLocal(workingMat);
            } else {
                logger.warning("transform not currently supported: " + transform.getClass().getCanonicalName());
            }
        }
        return new Transform().fromHomogeneousMatrix(finalMat);
    }

    public void reattachAttachments(final Node scene) {
        for (final AttachmentPoint point : _dataCache.getAttachmentPoints().values()) {
            scene.attachChild(point.getAttachment());
        }
    }
}