/* * Copyright 2016 Nathan Howard * * This file is part of OpenGrave * * OpenGrave is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenGrave is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenGrave. If not, see <http://www.gnu.org/licenses/>. */ package com.opengrave.og.models; import java.util.ArrayList; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.opengrave.common.xml.XML; import com.opengrave.og.util.Matrix4f; public class DAESceneNode { boolean valid = false; DAESceneNode parent = null; public ArrayList<DAESceneNode> children = new ArrayList<DAESceneNode>(); public Matrix4f total = new Matrix4f(); public Matrix4f local = new Matrix4f(); public String nodeName = null; @Override public boolean equals(Object object) { if (object instanceof DAESceneNode) { DAESceneNode other = (DAESceneNode) object; // System.out.println("Comparing bones "+this.nodeName+":"+this.children.size()+" and "+other.nodeName+":"+other.children.size()); if (other.nodeName.equals(this.nodeName)) { return other.children.size() == this.children.size(); // Close enough } // System.out.println("Node Names do not match"); return false; } // System.out.println("Types do not match"); return false; } public DAESceneNode() { } public static DAESceneNode make(DAEFile model, DAESceneNode parent, Element node) { DAESceneNode sn = new DAESceneNode(); if (node.getAttribute("type").equalsIgnoreCase("JOINT")) { sn.joint = ((Element) node).getAttribute("sid"); sn.jointName = ((Element) node).getAttribute("name"); } sn.parent = parent; if (!node.getNodeName().equalsIgnoreCase("node") && !node.getNodeName().equalsIgnoreCase("visual_scene")) { // System.out.println(node.getNodeName()+" is not a node"); return null; } sn.nodeName = ((Element) node).getAttribute("id"); Element matrix = XML.getChild(node, "matrix"); Element geom = XML.getChild(node, "instance_geometry"); Element controller = XML.getChild(node, "instance_controller"); if (matrix == null) { // System.out.println("Has no local matrix. Assume Ident"); } else { sn.local = DAEFile.readMatrix(matrix.getTextContent()); } sn.total = sn.parent.total.mult(sn.local, null); parent.children.add(sn); sn.valid = true; // Apply to geometries if needed if (geom != null) { // It's a static mesh intended for drawing static DAEMesh obj = model.getMesh(geom.getAttribute("url").substring(1)); if (obj != null) { DAEMeshInstance minst = new DAEMeshInstance(node.getAttribute("name"), obj, sn.total); model.addMeshInstance(minst); // obj.setMatrix(sn.total); } } if (controller != null) { // It's a skeleton to attach to an otherwise // static mesh for drawing animated Node skele = XML.getChild(controller, "skeleton"); // Assume first is best. Bad assumption? String skeleNode = null; if (skele != null) { skeleNode = skele.getTextContent().substring(1); } else { } String skinNode = controller.getAttribute("url").substring(1); model.addController(node.getAttribute("id"), skeleNode, skinNode, sn.total); } // Do all children recursively. This sounds so wrong for (Element cNode : XML.getChildren(node, "node")) { DAESceneNode.make(model, sn, cNode); } return sn; } public DAESceneNode getNodeId(String name) { if (nodeName != null && nodeName.equalsIgnoreCase(name)) { return this; } for (DAESceneNode child : children) { DAESceneNode x = child.getNodeId(name); if (x != null) { return x; } } return null; } public String joint = null, jointName = null; public Matrix4f skinningMatrix, inverseBindMatrix, animatedWorldMatrix;// , // animatedLocalMatrix; public int index = -1; // public DAEAnimChannelCollection animation = null; public ArrayList<DAEAnimClip> animation = null; /** * Return a DAESceneNode/DAEBone that is a bone which was named the same as * input name. Recurses through children, returning null if no node-bone had * that name * * @param name * @return */ public DAESceneNode getBone(String name) { if (joint != null && joint.equalsIgnoreCase(name)) { return this; } for (DAESceneNode child : children) { DAESceneNode cResult = child.getBone(name); if (cResult != null) { return cResult; } } return null; } public DAESceneNode getBoneNamed(String name) { if (jointName != null && jointName.equalsIgnoreCase(name)) { return this; } for (DAESceneNode child : children) { DAESceneNode cResult = child.getBoneNamed(name); if (cResult != null) { return cResult; } } return null; } public DAESceneNode getBone(int index) { if (this.index == index) { return this; } for (DAESceneNode child : children) { DAESceneNode cResult = child.getBone(index); if (cResult != null) { return cResult; } } return null; } public void setWorldMatrix(Matrix4f world) { animatedWorldMatrix = world; skinningMatrix = world.mult(inverseBindMatrix, null); // skinningMatrix = Matrix4f.mul(world, new Matrix4f(), null); } public void applyAnimations(ArrayList<DAEAnimClip> animation) { for (DAESceneNode child : children) { this.animation = animation; child.applyAnimations(animation); } } /* * public void applyAnimations(ArrayList<DAEAnimation> animation) { for * (DAEAnimation anim : animation) { if * (anim.channels.containsKey(nodeName)) { if (this.animation != null) { * System.out.println(nodeName + " node replaced animation... Bone " + * joint); System.out.println(anim.channels.get(nodeName).target + * " is replacing " + this.animation.target); } this.animation = * anim.channels.get(nodeName); } } if (this.animation == null) { // * System.out.println(nodeName+" : no animation channel given"); } for * (DAESceneNode child : children) { child.applyAnimations(animation); } } */ public static DAESceneNode createSkeleton(DAESceneNode root) { DAESceneNode skeleton = root.clone(null); skeleton.local = new Matrix4f(); skeleton.total = new Matrix4f(); for (DAESceneNode child : root.children) { addChildToSkeleton(skeleton, root, new Matrix4f(), child); } return skeleton; } public static void addChildToSkeleton(DAESceneNode newParent, DAESceneNode parent, Matrix4f runningMatrix, DAESceneNode node) { if (node.joint == null) { // This isn't a bone and should not be added // But check children for bones Matrix4f nextMat = runningMatrix.mult(parent.local, null); // System.out.println("Matrix passed on from Non-Joint Node "+nextMat); for (DAESceneNode child : node.children) { addChildToSkeleton(newParent, parent, nextMat, child); } } else { DAESceneNode nNode = node.clone(newParent); nNode.local = node.local.mult(runningMatrix, null); nNode.total = nNode.local.mult(newParent.total, null); for (DAESceneNode child : node.children) { addChildToSkeleton(nNode, node, new Matrix4f(), child); } } } /** * Create a clone for Node -> Skeleton Bone formation */ public DAESceneNode clone(DAESceneNode newParent) { DAESceneNode copy = new DAESceneNode(); copy.parent = newParent; if (newParent != null) { newParent.children.add(copy); } copy.animatedWorldMatrix = animatedWorldMatrix; copy.animation = animation; copy.index = index; copy.inverseBindMatrix = inverseBindMatrix; copy.joint = joint; copy.jointName = jointName; copy.local = null; copy.nodeName = nodeName; copy.skinningMatrix = skinningMatrix; copy.total = null; copy.valid = valid; return copy; } public String toString(int indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.append(" "); } sb.append(joint); if (children.size() > 0) { sb.append("{").append("\n"); for (DAESceneNode node : children) { sb.append(node.toString(indent + 1)); } } else { sb.append("\n"); } for (int i = 0; i < indent; i++) { sb.append(" "); } sb.append("}").append("\n"); return sb.toString(); } public String toString() { return toString(0); } public int getBoneCount() { int i = 1; for (DAESceneNode child : children) { i += child.getBoneCount(); } return i; } }