Advertisement
baryonSasuke

Full assimp animation loader

Dec 31st, 2020
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 15.20 KB | None | 0 0
  1. package Kurama.geometry.assimp;
  2.  
  3. import Kurama.Math.Matrix;
  4. import Kurama.Math.Quaternion;
  5. import Kurama.Math.Vector;
  6. import Kurama.Mesh.Material;
  7. import Kurama.Mesh.Mesh;
  8. import Kurama.game.Game;
  9. import Kurama.geometry.MD5.Animation;
  10. import Kurama.geometry.MD5.AnimationFrame;
  11. import Kurama.geometry.MD5.Joint;
  12. import Kurama.geometry.MD5.MD5Utils;
  13. import Kurama.geometry.MeshBuilderHints;
  14. import Kurama.model.AnimatedModel;
  15. import Kurama.utils.Utils;
  16. import org.lwjgl.PointerBuffer;
  17. import org.lwjgl.assimp.*;
  18.  
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23.  
  24. import static org.lwjgl.assimp.Assimp.*;
  25.  
  26. class Transformation {
  27.  
  28.     public Quaternion orientation;
  29.     public Vector pos;
  30.     public Vector scale;
  31.  
  32.     public Transformation(Quaternion orientation, Vector pos) {
  33.         this(orientation, pos, new Vector(1,1,1));
  34.     }
  35.  
  36.     public Transformation(Quaternion orientation, Vector pos, Vector scale) {
  37.         this.orientation = orientation;
  38.         this.scale = scale;
  39.         this.pos = pos;
  40.     }
  41.  
  42.     public Transformation(Matrix transformation) {
  43.         this.pos = transformation.getColumn(3).removeDimensionFromVec(3);
  44.  
  45.         var rotScaleMatrix = transformation.getSubMatrix(0,0,2,2);
  46.  
  47.         List<Vector> rotCols = new ArrayList<>();
  48.         var scale = new Vector(1,1,1);
  49.         for(int i = 0;i < 3;i++) {
  50.             var v = rotScaleMatrix.getColumn(i);
  51.             scale.setDataElement(i, v.getNorm());
  52.             v.normalise();
  53.             rotCols.add(v);
  54.         }
  55.  
  56.         var rotMatrix = new Matrix(rotCols);
  57.         this.orientation = new Quaternion(rotMatrix);
  58.         this.orientation.normalise();
  59.         this.scale = scale;
  60.     }
  61.  
  62.     public Matrix getTransformationMatrix() {
  63.         Matrix rotationMatrix = this.orientation.getRotationMatrix();
  64.         Matrix scalingMatrix = Matrix.getDiagonalMatrix(scale);
  65.         Matrix rotScalMatrix = rotationMatrix.matMul(scalingMatrix);
  66.  
  67.         Matrix transformationMatrix = rotScalMatrix.addColumn(this.pos);
  68.         transformationMatrix = transformationMatrix.addRow(new Vector(new float[]{0, 0, 0, 1}));
  69.  
  70.         return transformationMatrix;
  71.     }
  72.  
  73. }
  74.  
  75. public class AssimpAnimLoader2 {
  76.  
  77.     public static AnimatedModel load(Game game, String resourcePath, String texturesDir) throws Exception {
  78.         return load(game, resourcePath, texturesDir, aiProcess_JoinIdenticalVertices | aiProcess_Triangulate |
  79.                 aiProcess_FixInfacingNormals | aiProcess_LimitBoneWeights);
  80.     }
  81.  
  82.     public static AnimatedModel load(Game game, String resourcePath, String texturesDir, int flags) throws Exception {
  83.         String fileType = resourcePath.split("\\.")[1];
  84.         AIScene aiScene = aiImportFile(resourcePath, flags);
  85.         if(aiScene == null) {
  86.             throw new Exception("Error loading model");
  87.         }
  88.  
  89.         int numMaterials = aiScene.mNumMaterials();
  90.         PointerBuffer aiMaterials = aiScene.mMaterials();
  91.         List<Material> materials = new ArrayList<>();
  92.  
  93.         for(int  i=0; i < numMaterials; i++) {
  94.             AIMaterial aiMat = AIMaterial.create(aiMaterials.get(i));
  95.             var mat = AssimpStaticLoader.processMaterial(aiMat, texturesDir, fileType);
  96.             materials.add(mat);
  97.         }
  98.  
  99.         Map<String, Bone> boneList = new HashMap<>();  //Map of bones with inverse bind transforms
  100.  
  101.         int numMeshes = aiScene.mNumMeshes();
  102.         PointerBuffer aiMeshes = aiScene.mMeshes();
  103.         List<Mesh> meshes = new ArrayList<>();
  104.  
  105.         for(int i = 0;i < numMeshes; i++) {
  106.             var aiMesh = AIMesh.create(aiMeshes.get(i));
  107.             var mesh = processMesh(aiMesh, materials, resourcePath, boneList);
  108.             mesh.meshIdentifier = Utils.getUniqueID();
  109.             meshes.add(mesh);
  110.         }
  111.  
  112.         Node root = buildNodeHierarchy(aiScene.mRootNode(), null);
  113.         List<Matrix> unbindMatrices = new ArrayList<>();
  114.         getUnbindMatricesRecursive(boneList, root, unbindMatrices);
  115.         Map<String, Animation> animations = new HashMap<>();
  116.  
  117.         int numAnimations = aiScene.mNumAnimations();
  118.         PointerBuffer aiAnimations = aiScene.mAnimations();
  119.  
  120.         for(int i = 0;i < numAnimations; i++) {
  121.             AIAnimation aiAnimation = AIAnimation.create(aiAnimations.get(i));
  122.             Animation anim = createAnimation(aiAnimation, root, boneList, unbindMatrices);
  123.             animations.put(aiAnimation.mName().dataString(), anim);
  124.         }
  125.  
  126.         String key1 = (String) animations.keySet().toArray()[0];
  127.         AnimatedModel model = new AnimatedModel(game, meshes, animations, animations.get(key1), Utils.getUniqueID());
  128.         return model;
  129.     }
  130.  
  131.     public static void getUnbindMatricesRecursive(Map<String, Bone> boneMap, Node currentNode, List<Matrix> result) {
  132.         if(boneMap.containsKey(currentNode.name)) {
  133.             result.add(boneMap.get(currentNode.name).unbindMatrix);
  134.         }
  135.         for(var childNode: currentNode.children) {
  136.             getUnbindMatricesRecursive(boneMap, childNode, result);
  137.         }
  138.     }
  139.  
  140.     public static Animation createAnimation(AIAnimation aiAnimation, Node root, Map<String, Bone> boneMap, List<Matrix> unbindMatrices) {
  141.         int numNodes = aiAnimation.mNumChannels();
  142.         var numFrames = AINodeAnim.create(aiAnimation.mChannels().get(0)).mNumPositionKeys();
  143.         Animation finalAnimation = new Animation(aiAnimation.mName().dataString(), new ArrayList<>(), boneMap.size(), unbindMatrices,
  144.                 (float) (numFrames/aiAnimation.mDuration()));
  145.  
  146.         for(int frameNum = 0; frameNum < numFrames; frameNum++) {
  147.             AnimationFrame frame = new AnimationFrame(numNodes);
  148.             recursiveAnimProcess(aiAnimation, frame, boneMap, root, frameNum, Matrix.getIdentityMatrix(4));
  149.             finalAnimation.animationFrames.add(frame);
  150.         }
  151.  
  152.         return finalAnimation;
  153.     }
  154.  
  155.     public static void recursiveAnimProcess(AIAnimation aiAnimation, AnimationFrame frame, Map<String, Bone> boneMap, Node currentNode, int frameNum, Matrix parentTransform) {
  156.         Matrix nodeLocalTransform = currentNode.transformation;
  157.         Matrix accTransform = null;
  158.  
  159.         if(boneMap.containsKey(currentNode.name)) {
  160.             AINodeAnim animNode = findAIAnimNode(aiAnimation, currentNode.name);
  161.             nodeLocalTransform = buildNodeTransformationMatrix(animNode, frameNum).getTransformationMatrix();  // builds 4x4 matrix from vec3 pos, vec3 scale, quat orient
  162.             accTransform = parentTransform.matMul(nodeLocalTransform);
  163.             Transformation finalTrans = new Transformation(accTransform);
  164. //            finalTrans = new Transformation(Matrix.getIdentityMatrix(4));
  165.             frame.joints.add(new Joint(currentNode.name, -1, finalTrans.pos, finalTrans.orientation, finalTrans.scale));  //-1 simply because the parent ID would never be used later
  166.         }
  167.         else {
  168.             accTransform = parentTransform.matMul(nodeLocalTransform);
  169.         }
  170.  
  171.         for(var childNode: currentNode.children) {
  172.             recursiveAnimProcess(aiAnimation, frame, boneMap, childNode, frameNum, accTransform);
  173.         }
  174.  
  175.     }
  176.  
  177.     private static Transformation buildNodeTransformationMatrix(AINodeAnim aiNodeAnim, int frame) {
  178.  
  179.         AIVectorKey.Buffer positionKeys = aiNodeAnim.mPositionKeys();
  180.         AIVectorKey.Buffer scalingKeys = aiNodeAnim.mScalingKeys();
  181.         AIQuatKey.Buffer rotationKeys = aiNodeAnim.mRotationKeys();
  182.  
  183.         AIVectorKey aiVecKey;
  184.         AIVector3D vec;
  185.  
  186.         Vector pos = null;
  187.         Quaternion orient = null;
  188.         Vector scale = null;
  189.  
  190.         int numPositions = aiNodeAnim.mNumPositionKeys();
  191.         if (numPositions > 0) {
  192.             aiVecKey = positionKeys.get(Math.min(numPositions - 1, frame));
  193.             vec = aiVecKey.mValue();
  194.             pos = new Vector(vec.x(), vec.y(), vec.z());
  195.         }
  196.         int numRotations = aiNodeAnim.mNumRotationKeys();
  197.         if (numRotations > 0) {
  198.             AIQuatKey quatKey = rotationKeys.get(Math.min(numRotations - 1, frame));
  199.             AIQuaternion aiQuat = quatKey.mValue();
  200.             orient = new Quaternion(aiQuat.w(), aiQuat.x(), aiQuat.y(), aiQuat.z());
  201.         }
  202.         int numScalingKeys = aiNodeAnim.mNumScalingKeys();
  203.         if (numScalingKeys > 0) {
  204.             aiVecKey = scalingKeys.get(Math.min(numScalingKeys - 1, frame));
  205.             vec = aiVecKey.mValue();
  206.  
  207.             scale = new Vector(vec.x(), vec.y(), vec.z());
  208. //            scale = new Vector(1,1,1);
  209.         }
  210.  
  211.         return new Transformation(orient, pos, scale);
  212.     }
  213.  
  214.     private static AINodeAnim findAIAnimNode(AIAnimation aiAnimation, String nodeName) {
  215.         AINodeAnim result = null;
  216.         int numAnimNodes = aiAnimation.mNumChannels();
  217.         PointerBuffer aiChannels = aiAnimation.mChannels();
  218.         for (int i=0; i<numAnimNodes; i++) {
  219.             AINodeAnim aiNodeAnim = AINodeAnim.create(aiChannels.get(i));
  220.             if ( nodeName.equals(aiNodeAnim.mNodeName().dataString())) {
  221.                 result = aiNodeAnim;
  222.                 break;
  223.             }
  224.         }
  225.         return result;
  226.     }
  227.  
  228.     public static Node buildNodeHierarchy(AINode aiNode, Node parent) {
  229.  
  230.         String nodeName = aiNode.mName().dataString();
  231.         var trans = toMatrix(aiNode.mTransformation());
  232.         Node node = new Node(nodeName, parent, trans);
  233.  
  234.         int numChildren = aiNode.mNumChildren();
  235.         PointerBuffer aiChildren = aiNode.mChildren();
  236.         for(int i = 0;i < numChildren; i++) {
  237.             AINode aiChildNode = AINode.create(aiChildren.get(i));
  238.             Node childNode = buildNodeHierarchy(aiChildNode, node);
  239.             node.children.add(childNode);
  240.         }
  241.         return node;
  242.  
  243.     }
  244.  
  245.     public static Mesh processMesh(AIMesh aiMesh, List<Material> materials, String resourcePath, Map<String, Bone> bonesList) {
  246.         List<List<Vector>> vertAttribs = new ArrayList<>();
  247.        
  248.         // The processAttribute and processTextureCoords functions are sure to work, since I use them in my assimpStaticLoader and               
  249.         // models loaded this way render properly.
  250.  
  251.         List<Vector> verts = AssimpStaticLoader.processAttribute(aiMesh.mVertices());
  252.         List<Vector> textures = AssimpStaticLoader.processTextureCoords(aiMesh.mTextureCoords(0));
  253.         List<Vector> normals = AssimpStaticLoader.processAttribute(aiMesh.mNormals());
  254.         List<Vector> tangents = AssimpStaticLoader.processAttribute(aiMesh.mTangents());
  255.         List<Vector> biTangents = AssimpStaticLoader.processAttribute(aiMesh.mBitangents());
  256.         List<Integer> indices = AssimpStaticLoader.processIndices(aiMesh);
  257.  
  258.         List results = processJoints(aiMesh, bonesList);
  259.         List<Vector> jointIndices = (List<Vector>) results.get(0);
  260.         List<Vector> weight = (List<Vector>) results.get(1);
  261.  
  262.         vertAttribs.add(verts);
  263.  
  264.         List<Material> meshMaterials = new ArrayList<>();
  265.         var newMat = new Material();
  266.         int matInd = aiMesh.mMaterialIndex();
  267.         if(matInd >= 0 && matInd < materials.size()) {
  268.             newMat = materials.get(matInd);
  269.         }
  270.         meshMaterials.add(newMat);
  271.  
  272.         var newMesh = new Mesh(indices, null, vertAttribs, meshMaterials, resourcePath, null);
  273.         newMesh.setAttribute(textures, Mesh.TEXTURE);
  274.         newMesh.setAttribute(normals, Mesh.NORMAL);
  275.         newMesh.setAttribute(tangents, Mesh.TANGENT);
  276.         newMesh.setAttribute(biTangents, Mesh.BITANGENT);
  277.         newMesh.setAttribute(jointIndices, Mesh.JOINTINDICESPERVERT);
  278.         newMesh.setAttribute(weight, Mesh.WEIGHTBIASESPERVERT);
  279.  
  280.         return newMesh;
  281.     }
  282.  
  283.     public static List processJoints(AIMesh aiMesh, Map<String, Bone> bonesList) {
  284.  
  285.         List<Vector> jointIndices = new ArrayList<>();
  286.         List<Vector> weights = new ArrayList<>();
  287.  
  288.         Map<Integer, List<VertexWeight>> weightSet = new HashMap<>();
  289.         int numBones = aiMesh.mNumBones();
  290.         PointerBuffer aiBones = aiMesh.mBones();
  291.  
  292.         for(int i = 0;i < numBones; i++) {
  293.             AIBone aiBone = AIBone.create(aiBones.get(i));
  294.  
  295.             int id = bonesList.size();
  296.             var boneName = aiBone.mName().dataString();
  297.             Bone bone;
  298.             if(bonesList.containsKey(boneName)) {
  299.                 bone = bonesList.get(boneName);
  300.             }
  301.             else {
  302.                 bone = new Bone(id, boneName, toMatrix(aiBone.mOffsetMatrix()));
  303.                 bonesList.put(boneName, bone);
  304.             }
  305.  
  306.             int numWeights = aiBone.mNumWeights();
  307.             AIVertexWeight.Buffer aiWeights = aiBone.mWeights();
  308.             for(int j = 0;j < numWeights; j++) {
  309.                 AIVertexWeight aiWeight = aiWeights.get(j);
  310.                 VertexWeight vw = new VertexWeight(bone.boneId, aiWeight.mVertexId(), aiWeight.mWeight());
  311.                 weightSet.putIfAbsent(vw.vertexId, new ArrayList<>());
  312.                 var weightsList = weightSet.get(vw.vertexId);
  313.                 weightsList.add(vw);
  314.             }
  315.         }
  316.  
  317.         int numVertices = aiMesh.mNumVertices();
  318.         for(int i = 0; i < numVertices; i++) {
  319.             var weightsList = weightSet.get(i);
  320.             int size = weightsList != null ? weightsList.size() : 0;
  321.  
  322.             Vector tempWeight = new Vector(MD5Utils.MAXWEIGHTSPERVERTEX,0);
  323.             Vector tempJointIndices = new Vector(MD5Utils.MAXWEIGHTSPERVERTEX,0);
  324.             for(int j = 0; j < MD5Utils.MAXWEIGHTSPERVERTEX; j++) {
  325.                 if(j < size) {
  326.                     VertexWeight vw = weightsList.get(j);
  327.                     tempWeight.setDataElement(j, vw.weight);
  328.                     tempJointIndices.setDataElement(j, vw.boneId);
  329.                 }
  330.             }
  331.             weights.add(tempWeight);
  332.             jointIndices.add(tempJointIndices);
  333.         }
  334.  
  335.         List res = new ArrayList();
  336.         res.add(jointIndices);
  337.         res.add(weights);
  338.         return res;
  339.     }
  340.  
  341.     public static Matrix toMatrix(AIMatrix4x4 input) {
  342.         float[][] mat = new float[4][4];
  343.  
  344.         mat[0][0] = input.a1();
  345.         mat[0][1] = input.a2();
  346.         mat[0][2] = input.a3();
  347.         mat[0][3] = input.a4();
  348.  
  349.         mat[1][0] = input.b1();
  350.         mat[1][1] = input.b2();
  351.         mat[1][2] = input.b3();
  352.         mat[1][3] = input.b4();
  353.  
  354.         mat[2][0] = input.c1();
  355.         mat[2][1] = input.c2();
  356.         mat[2][2] = input.c3();
  357.         mat[2][3] = input.c4();
  358.  
  359.         mat[3][0] = input.d1();
  360.         mat[3][1] = input.d2();
  361.         mat[3][2] = input.d3();
  362.         mat[3][3] = input.d4();
  363.  
  364.         return new Matrix(mat);
  365.     }
  366.  
  367.     public static int getFlags(MeshBuilderHints hints) {
  368.         int finalFlag = aiProcess_FixInfacingNormals | aiProcess_LimitBoneWeights;
  369.         if(hints.shouldTriangulate) {
  370.             finalFlag |= aiProcess_Triangulate;
  371.         }
  372.         if(hints.shouldReverseWindingOrder) {
  373.             finalFlag |= aiProcess_FlipWindingOrder;
  374.         }
  375.         if(hints.shouldSmartBakeVertexAttributes || hints.shouldDumbBakeVertexAttributes) {
  376.             finalFlag |= aiProcess_JoinIdenticalVertices;
  377.         }
  378.         if(hints.shouldGenerateTangentBiTangent) {
  379.             finalFlag |= aiProcess_CalcTangentSpace;
  380.         }
  381.         return finalFlag;
  382.     }
  383.  
  384. }
  385.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement