package rajawali.animation.mesh; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import rajawali.BaseObject3D; import rajawali.BufferInfo; import rajawali.Camera; import rajawali.Geometry3D.BufferType; import rajawali.materials.AAdvancedMaterial; import rajawali.parser.md5.MD5MeshParser.MD5Mesh; import rajawali.parser.md5.MD5MeshParser.MD5Vert; import rajawali.parser.md5.MD5MeshParser.MD5Weight; import rajawali.util.BufferUtil; import rajawali.util.RajLog; import android.opengl.GLES20; /* * Making Skeleton a top level objet, besouse it is shared * between all the meshes and needs to be animated only once per rendering cycle */ public class BoneAnimationObject3D extends AAnimationObject3D { private static final int FLOAT_SIZE_BYTES = 4; public int mNumJoints; public AnimationSkeleton mSkeleton; private MD5Mesh mMesh; private BoneAnimationSequence mSequence; public float[] boneWeights1, boneIndexes1, boneWeights2, boneIndexes2; protected BufferInfo mboneWeights1BufferInfo = new BufferInfo(); protected BufferInfo mboneIndexes1BufferInfo = new BufferInfo(); protected BufferInfo mboneWeights2BufferInfo = new BufferInfo(); protected BufferInfo mboneIndexes2BufferInfo = new BufferInfo(); /** * FloatBuffer containing first 4 bone weights */ protected FloatBuffer mboneWeights1; /** * FloatBuffer containing first 4 bone indexes */ protected FloatBuffer mboneIndexes1; /** * FloatBuffer containing bone weights if the maxWeightCoun > 4 */ protected FloatBuffer mboneWeights2; /** * FloatBuffer containing bone indexes if the maxWeightCoun > 4 * Current implementation supports up to 8 joint weights per vertex */ protected FloatBuffer mboneIndexes2; public BoneAnimationObject3D() { super(); mSkeleton = null; } public void setShaderParams(Camera camera) { super.setShaderParams(camera); AAdvancedMaterial material = (AAdvancedMaterial)mMaterial; material.setBone1Indexes(mboneIndexes1BufferInfo.bufferHandle); material.setBone1Weights(mboneWeights1BufferInfo.bufferHandle); if(mMesh.maxNumWeights>4){ material.setBone2Indexes(mboneIndexes2BufferInfo.bufferHandle); material.setBone2Weights(mboneWeights2BufferInfo.bufferHandle); } material.setBoneMatrix(mSkeleton.uBoneMatrix); } public void setSkeleton(BaseObject3D skeleton){ if(skeleton instanceof AnimationSkeleton){ mSkeleton = (AnimationSkeleton) skeleton; mNumJoints = mSkeleton.getJoints().length; } else throw new RuntimeException( "Skeleton must be of type AnimationSkeleton!"); } public void setMD5Mesh(MD5Mesh mesh) { mMesh = mesh; prepareBoneWeightsAndIndexes(); mboneIndexes1 = alocateBuffer(mboneIndexes1, boneIndexes1); mboneWeights1 = alocateBuffer(mboneWeights1, boneWeights1); mGeometry.createBuffer(mboneIndexes1BufferInfo, BufferType.FLOAT_BUFFER, mboneIndexes1, GLES20.GL_ARRAY_BUFFER); mGeometry.createBuffer(mboneWeights1BufferInfo, BufferType.FLOAT_BUFFER, mboneWeights1, GLES20.GL_ARRAY_BUFFER); if (mMesh.maxNumWeights>4) { mboneIndexes2 = alocateBuffer(mboneIndexes2, boneIndexes2); mboneWeights2 = alocateBuffer(mboneWeights2, boneWeights2); mGeometry.createBuffer(mboneIndexes2BufferInfo, BufferType.FLOAT_BUFFER, mboneIndexes2, GLES20.GL_ARRAY_BUFFER); mGeometry.createBuffer(mboneWeights2BufferInfo, BufferType.FLOAT_BUFFER, mboneWeights2, GLES20.GL_ARRAY_BUFFER); } } private FloatBuffer alocateBuffer(FloatBuffer buffer, float[] data){ if(buffer == null) { buffer = ByteBuffer .allocateDirect(data.length * FLOAT_SIZE_BYTES * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); BufferUtil.copy(data, buffer, data.length, 0); buffer.position(0); } else { BufferUtil.copy(data, buffer, data.length, 0); } return buffer; } public void play() { if(mSequence == null) { RajLog.e("[BoneAnimationObject3D.play()] Cannot play animation. No sequence was set."); return; } super.play(); for (int i = 0, j = mChildren.size(); i < j; i++) if (mChildren.get(i) instanceof AAnimationObject3D) ((AAnimationObject3D) mChildren.get(i)).play(); } public void setAnimationSequence(BoneAnimationSequence sequence) { mSequence = sequence; if(sequence != null && sequence.getFrames() != null) { mNumFrames = sequence.getFrames().length; for (int i = 0, j = mChildren.size(); i < j; i++) if (mChildren.get(i) instanceof BoneAnimationObject3D) ((BoneAnimationObject3D) mChildren.get(i)).setAnimationSequence(sequence); } } /* * Creates boneWeights and boneIndexes arrays * * */ public void prepareBoneWeightsAndIndexes(){ int weightStep = 4;//mMesh.maxNumWeights<4?4:8; boneWeights1 = new float[mMesh.numVerts*4]; boneIndexes1 = new float[mMesh.numVerts*4]; boneWeights2 = new float[mMesh.numVerts*4]; boneIndexes2 = new float[mMesh.numVerts*4]; for(int i=0;i<mMesh.numVerts; i++){ MD5Vert vert = mMesh.verts[i]; for (int j = 0; j < vert.weightElem; ++j) { MD5Weight weight = mMesh.weights[vert.weightIndex + j]; if(j<4){ boneWeights1[weightStep*i+j] = weight.weightValue; boneIndexes1[weightStep*i+j] = weight.jointIndex; }else { boneWeights2[weightStep*i+j] = weight.weightValue; boneIndexes2[weightStep*i+j] = weight.jointIndex; } } } } public void reload() { super.reload(); mGeometry.createBuffer(mboneIndexes1BufferInfo, BufferType.FLOAT_BUFFER, mboneIndexes1, GLES20.GL_ARRAY_BUFFER); mGeometry.createBuffer(mboneWeights1BufferInfo, BufferType.FLOAT_BUFFER, mboneWeights1, GLES20.GL_ARRAY_BUFFER); if (mMesh.maxNumWeights>4) { mGeometry.createBuffer(mboneIndexes2BufferInfo, BufferType.FLOAT_BUFFER, mboneIndexes2, GLES20.GL_ARRAY_BUFFER); mGeometry.createBuffer(mboneWeights2BufferInfo, BufferType.FLOAT_BUFFER, mboneWeights2, GLES20.GL_ARRAY_BUFFER); } } @Override public void destroy() { int[] buffers = new int[4]; if(mboneIndexes1BufferInfo != null) buffers[0] = mboneIndexes1BufferInfo.bufferHandle; if(mboneWeights1BufferInfo != null) buffers[1] = mboneIndexes1BufferInfo.bufferHandle; if(mboneIndexes2BufferInfo != null) buffers[0] = mboneIndexes2BufferInfo.bufferHandle; if(mboneWeights2BufferInfo != null) buffers[1] = mboneIndexes2BufferInfo.bufferHandle; GLES20.glDeleteBuffers(buffers.length, buffers, 0); if(mboneIndexes1 != null) mboneIndexes1.clear(); if(mboneWeights1 != null) mboneWeights1.clear(); if(mboneIndexes2 != null) mboneIndexes2.clear(); if(mboneWeights2 != null) mboneWeights2.clear(); mboneIndexes1=null; mboneWeights1=null; mboneIndexes2=null; mboneWeights2=null; if(mboneIndexes1BufferInfo != null && mboneIndexes1BufferInfo.buffer != null) { mboneIndexes1BufferInfo.buffer.clear(); mboneIndexes1BufferInfo.buffer=null; } if(mboneWeights1BufferInfo != null && mboneWeights1BufferInfo.buffer != null) { mboneWeights1BufferInfo.buffer.clear(); mboneWeights1BufferInfo.buffer=null; } if(mboneIndexes2BufferInfo != null && mboneIndexes2BufferInfo.buffer != null) { mboneIndexes2BufferInfo.buffer.clear(); mboneIndexes2BufferInfo.buffer=null; } if(mboneWeights2BufferInfo != null && mboneWeights2BufferInfo.buffer != null) { mboneWeights2BufferInfo.buffer.clear(); mboneWeights2BufferInfo.buffer=null; } super.destroy(); } }