package com.badlogic.gdx.graphics.g3d.model.skeleton;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.model.skeleton.Skeleton;
import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonModel;
import com.badlogic.gdx.graphics.g3d.model.skeleton.SkeletonSubMesh;
public class SkeletonModelGpuSkinned extends SkeletonModel {
public final static String BoneIndexAttribue = "a_boneIndex";
public final static String BoneWeightAttribue = "a_boneWeight";
public SkeletonModelGpuSkinned(Skeleton skeleton, SkeletonSubMesh[] subMeshes) {
super(skeleton,subMeshes);
}
// Factory Method to create a SkeletonModelGpuSkinned from a SkeletonModel
// This will destroy the skeletonModel passed in.
public static SkeletonModel CreateFromSkeletonModel(SkeletonModel skeletonModel){
if (Gdx.gl20 == null){
return skeletonModel;
}
SkeletonModelGpuSkinned model = new SkeletonModelGpuSkinned(skeletonModel.skeleton, skeletonModel.subMeshes);
model.setupGpuSkin();
return model;
}
public void setupGpuSkin(){
for (int i = 0; i < subMeshes.length; i++) {
setupGpuSkin(subMeshes[i]);
}
}
private void setupGpuSkin(SkeletonSubMesh subMesh){
VertexAttributes oldAttributes = subMesh.mesh.getVertexAttributes();
final int oldAttributeCount = oldAttributes.size();
VertexAttribute[] attributeArray = new VertexAttribute[oldAttributeCount+2];
for(int i=0;i<oldAttributeCount;i++){
attributeArray[i] = oldAttributes.get(i);
}
final int boneIndex = oldAttributeCount;
final int weightIndex = oldAttributeCount+1;
attributeArray[boneIndex] = new VertexAttribute(Usage.Generic, 4, BoneIndexAttribue);
attributeArray[weightIndex] = new VertexAttribute(Usage.Generic, 4, BoneWeightAttribue);
VertexAttributes newAttributes = new VertexAttributes(attributeArray);
//TODO: not sure if I want to generate a new mesh. But VertexAttributes was final inside mesh
Mesh newMesh = new Mesh(true, subMesh.mesh.getMaxVertices(), subMesh.mesh.getMaxIndices(), newAttributes);
final int stride = subMesh.mesh.getVertexSize() / 4;
final int newStride = newMesh.getVertexSize() / 4;
final int numVertices = subMesh.mesh.getNumVertices();
int idx = 0;
int newIdx = 0;
int bidx = -1;
int widx = -1;
for(int i=0;i<newAttributes.size();i++)
{
VertexAttribute a = newAttributes.get(i);
if(a.alias.equals(BoneIndexAttribue)){
bidx = a.offset/4;
} else if(a.alias.equals(BoneWeightAttribue)){
widx = a.offset/4;
}
}
if(bidx <0 || widx < 0){
throw new IllegalArgumentException("Need Shader with bone index and bone wieght vectors to use GPU skinning");
}
final float[] vertices = subMesh.vertices;
final float[] skinnedVertices = new float[newStride * numVertices];
for (int i = 0; i < numVertices; i++, idx += stride, newIdx += newStride, bidx += newStride, widx += newStride) {
final int[] boneIndices = subMesh.boneAssignments[i];
final float[] boneWeights = subMesh.boneWeights[i];
System.arraycopy(vertices, idx, skinnedVertices, newIdx, stride);
skinnedVertices[bidx] = boneIndices.length>0?boneIndices[0]:0;
skinnedVertices[bidx + 1] = boneIndices.length>1?boneIndices[1]:0;
skinnedVertices[bidx + 2] = boneIndices.length>2?boneIndices[2]:0;
skinnedVertices[bidx + 3] = boneIndices.length>3?boneIndices[3]:0;
skinnedVertices[widx] = boneWeights.length>0?boneWeights[0]:0;
skinnedVertices[widx + 1] = boneWeights.length>1?boneWeights[1]:0;
skinnedVertices[widx + 2] = boneWeights.length>2?boneWeights[2]:0;
skinnedVertices[widx + 3] = boneWeights.length>3?boneWeights[3]:0;
}
newMesh.setVertices(skinnedVertices);
newMesh.setIndices(subMesh.indices);
subMesh.mesh.dispose();
subMesh.mesh = newMesh;
subMesh.skinnedVertices = null;
subMesh.vertices = skinnedVertices;
}
@Override
public void setAnimation(String animation, float time, boolean loop) {
skeleton.setAnimation(animation, time);
}
}