/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
******************************************************************************/
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);
}
}