How to Fill SkinData

Started by AGP, June 16, 2016, 06:44:26 AM

Previous topic - Next topic

AGP

I've exported into a JSON-serialized format of my creation all the weights of all the joints in each vertex. How, now, do I fill the 2-dimensional arrays that are weights and jointIndices? Thanks in advance.

raft

MAX_JOINTS_PER_VERTEX = 4

jointIndices is a [mesh size][MAX_JOINTS_PER_VERTEX] short array, describing which joints in skeleton is affecting each vertex.

weights is a [mesh size][MAX_JOINTS_PER_VERTEX] float array, describing how much each vertex is affected by corresponding joint in skeleton. if weight is zero, no calculation is done and corresponding jointIndex is irrelevant.

AGP

#2
I have filled arrays with the Bone and BoneReference classes below. How would you fill your arrays given this data? The following was my first, unsuccessful attempt (I don't understand how the data is arranged in your arrays, and I assumed that mesh_size is maxTriangles).


float[][] weights = new float[maxTriangles][Skeleton.MAX_JOINTS_PER_VERTEX];//Skeleton.MAX_JOINTS_PER_VERTEX IS 4
short[][] jointIndices = new short[maxTriangles][Skeleton.MAX_JOINTS_PER_VERTEX];
for (int y = 0; y < weights[0].length; y++) {
     for (int x = 0; x < weights.length; x++) {
weights[x][y] = boneRefs.get(x).vertexWeight;
jointIndices[x][y] = boneRefs.get(x).vertexIndexReference;
     }
}
class Bone {
     protected String name;
     protected SimpleVector position;
     protected Matrix transform;
     protected int index, parentIndex;
     public Bone(String name, SimpleVector position, Matrix transform, int index, int parentIndex) {
this.name = name;
this.position = position;
this.transform = transform;
this.index = index;
this.parentIndex = parentIndex;
     }
}
class BoneReference {
     public short vertexIndexReference;
     public short boneIndexReference;
     public float vertexWeight;
     public BoneReference(short vertexIndexReference, short boneIndexReference, float vertexWeight) {
          this.vertexIndexReference = vertexIndexReference;
          this.boneIndexReference = boneIndexReference;
          this.vertexWeight = vertexWeight;
     }
}

raft


weights[x][y] = boneRefs.get(x).vertexWeight;
jointIndices[x][y] = boneRefs.get(x).vertexIndexReference;


if I understand your stucture correct, these should be just the opposite.

jointIndices[x][y] says vertex x is influenced by joint y, NOT joint x influences vertex y. similar for weights.

looks like you shoud loop over boneRefs and fill in jointIndices and weights accordingly. not the other way around

AGP

#4
OK, but if I do
for (int i = 0; i < boneRefs.size(); i++)

then, what's y in

jointIndices[i][y] = boneRefs.get(i).vertexIndexReference
? I can't seem to visualize the 2d array as you designed it.

raft

you cant do that in a single iteration IMHO.*

first iterate over boneRefs to collect vertex-bone relations

map = Map<Integer, List<Integer>>();
for (b: boneRefs) {
  map.get(b.vertexIndexReference).add(b.boneIndexReference);
}


then loop over your map to place into jointIndices array. sth like this

for (e : map.entrySet) {
  count = 0;
  for (index : e.value) {
    jointIndices[e.key][count++] = index;
  }
}


hope this helps
r a f t

(*) actually you can but will be ugly

AGP

Same confusion: between


HashMap<Integer, java.util.List<Integer>> map = new HashMap<Integer, java.util.List<Integer>>();
for (BoneReference b: boneRefs)

and

     map.get(b.vertexIndexReference).add((int)b.boneIndexReference);
//then loop over your map to place into jointIndices array. sth like this
for (Map.Entry<Integer, java.util.List<Integer>> e : map.entrySet()) {
     int count = 0;
     for (Integer index : e.getValue())//index : e.value
jointIndices[e.getKey()][count++] = index.shortValue();
}

how do I fill the HashMap?

raft

it's written above AGP, in the first iteration

map = Map<Integer, List<Integer>>();
for (b: boneRefs) {
  map.get(b.vertexIndexReference).add(b.boneIndexReference);
}


but of course you should first create the list if it's null

AGP

boneRefs is neither null nor size 0. But map.get(b.vertexIndexReference).add((int)b.boneIndexReference) produces a NullPointerExecption (because get(...) returns a null value).

raft

Quote from: raft on June 17, 2016, 01:50:28 PM
but of course you should first create the list if it's null

map = Map<Integer, List<Integer>>();
for (b: boneRefs) {
  list = map.get(b.vertexIndexReference);
  if (list == null) {
    list = new ArrayList<Integer>();
    map.put(b.vertexIndexReference, list);
  }
  list.add(b.boneIndexReference);
}

AGP

Ah, there you go. Thanks for holding my hand. I've no experience with hash maps. It's working now. Next step, animation!

iguatemi

Hey, guys.
I'm working with AGP and I got stuck.

About the weights and jointsIndices, I'm not sure if I got it right. Here is how I'm building the skin data:

float[][] weights = new float[vertices.size()][Skeleton.MAX_JOINTS_PER_VERTEX];
short[][] jointIndices = new short[bones.size()][Skeleton.MAX_JOINTS_PER_VERTEX];
int[] weightCounters = new int[vertices.size()];
int[] jointIndicesCounters = new int[bones.size()];

for (BoneReference aBoneRef: boneRefs) {
    int weightCounter = weightCounters[aBoneRef.vertexIndexReference];
    if (weightCounter < Skeleton.MAX_JOINTS_PER_VERTEX) {
        weights[aBoneRef.vertexIndexReference][weightCounter] = aBoneRef.vertexWeight;
        weightCounters[aBoneRef.vertexIndexReference]++;
    }
    int jointIndicesCounter = jointIndicesCounters[aBoneRef.boneIndexReference];
    if (jointIndicesCounter < Skeleton.MAX_JOINTS_PER_VERTEX) {
        jointIndices[aBoneRef.boneIndexReference][jointIndicesCounter] = aBoneRef.vertexIndexReference;
        jointIndicesCounters[aBoneRef.boneIndexReference]++;
    }
}


Everything goes well untill I try to  applySkeletonPose. I get index out of bounds.


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 249
at raft.jpct.bones.Animated3D.applySkeletonPose(Animated3D.java:523)
at Importer.loop(Importer.java:344)
at Importer.<init>(Importer.java:56)
at Importer.main(Importer.java:422)



I'm using the SkeletonDebugger to check the bones and they're look fine.

Do you have any idea on how to make it work?

Thanks!

raft

it's hard to guess what your code exactly does but I've noticed a logical error. both weights' and jointIndices' first dimension is vertex index. looks like you use bone index as jointIndices' first dimension.

see the message above:
http://www.jpct.net/forum2/index.php/topic,4706.msg32344.html#msg32344

AGP

Joint number 9 is the left upper arm. The following code produces the following image. It looks a lot like it's a weight accuracy problem, now, doesn't it? The UVs themselves mostly work but are somewhat screwy too. I don't suppose that you would have any insight on this problem.


    else if (state.getKeyCode() == KeyEvent.VK_X) {
                         Animated3D animated = ((Animated3D)toLoad);
                         Skeleton skeleton = animated.getSkeleton();
                         SkeletonPose pose = animated.getSkeletonPose();

                         SkeletonPose pose2 = pose.clone();
                         pose2.setToBindPose();

                         int jointCount = skeleton.getNumberOfJoints();
                         pose2.getLocal(9).rotateX(1f);
                         pose2.updateTransforms();
                         animated.setSkeletonPose(pose2);
                         this.skeletonDebugger.update(pose2);
                         animated.applySkeletonPose();
                         animated.applyAnimation();
                    }



raft

I'm not sure if this is a weight accuracy problem. any progress on this one?