www.jpct.net

Bones - Skeletal and Pose Animations for jPCT/jPCT-AE => Bones => Topic started by: AGP on June 16, 2016, 06:44:26 am

Title: How to Fill SkinData
Post by: AGP on June 16, 2016, 06:44:26 am
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.
Title: Re: How to Fill SkinData
Post by: raft on June 16, 2016, 09:56:52 am
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.
Title: Re: How to Fill SkinData
Post by: AGP on June 17, 2016, 11:51:46 am
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).

Code: [Select]
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;
     }
}
Title: Re: How to Fill SkinData
Post by: raft on June 17, 2016, 12:12:53 pm
Code: [Select]
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.

Code: [Select]
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
Title: Re: How to Fill SkinData
Post by: AGP on June 17, 2016, 12:24:57 pm
OK, but if I do
Code: [Select]
for (int i = 0; i < boneRefs.size(); i++)
then, what's y in

Code: [Select]
jointIndices[i][y] = boneRefs.get(i).vertexIndexReference? I can't seem to visualize the 2d array as you designed it.
Title: Re: How to Fill SkinData
Post by: raft on June 17, 2016, 12:37:40 pm
you cant do that in a single iteration IMHO.*

first iterate over boneRefs to collect vertex-bone relations

Code: [Select]
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

Code: [Select]
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
Title: Re: How to Fill SkinData
Post by: AGP on June 17, 2016, 01:26:25 pm
Same confusion: between

Code: [Select]
HashMap<Integer, java.util.List<Integer>> map = new HashMap<Integer, java.util.List<Integer>>();
for (BoneReference b: boneRefs)
and
Code: [Select]
     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?
Title: Re: How to Fill SkinData
Post by: raft on June 17, 2016, 01:50:28 pm
it's written above AGP, in the first iteration

Code: [Select]
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
Title: Re: How to Fill SkinData
Post by: AGP on June 17, 2016, 01:57:22 pm
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).
Title: Re: How to Fill SkinData
Post by: raft on June 17, 2016, 01:58:38 pm
but of course you should first create the list if it's null

Code: [Select]
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);
}
Title: Re: How to Fill SkinData
Post by: AGP on June 17, 2016, 02:22:24 pm
Ah, there you go. Thanks for holding my hand. I've no experience with hash maps. It's working now. Next step, animation!
Title: Re: How to Fill SkinData
Post by: iguatemi on July 20, 2016, 11:50:37 pm
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:
Code: [Select]
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.

Code: [Select]
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!
Title: Re: How to Fill SkinData
Post by: raft on July 22, 2016, 08:23:23 am
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
Title: Re: How to Fill SkinData
Post by: AGP on August 03, 2016, 12:21:49 am
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.

Code: [Select]
    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();
                    }

(https://dl.dropboxusercontent.com/u/93826015/NewBones.jpg)
Title: Re: How to Fill SkinData
Post by: raft on August 10, 2016, 07:22:30 am
I'm not sure if this is a weight accuracy problem. any progress on this one?
Title: Re: How to Fill SkinData
Post by: AGP on August 13, 2016, 02:40:31 pm
Kind of, sort of. I'm going to see this through, but at the moment it's still broken. I think I'll have news on Thursday.
Title: Re: How to Fill SkinData
Post by: AGP on August 24, 2016, 11:33:13 pm
OK, in all the tests the model appears to both export and import right (then again, there's that pesky UV issue). The next step was always to write an exporter for Blender, so instead we're going to be writing an IMPORTER first. Hopefully I'll have more tomorrow.
Title: Re: How to Fill SkinData
Post by: AGP on September 07, 2016, 12:16:04 am
News: UVs in Blender are proving tougher than expected. But the bones are moving better (still not perfect) than in Bones. I will post screenshots later tonight.
Title: Re: How to Fill SkinData
Post by: AGP on September 14, 2016, 09:08:59 pm
Voilá. UVs and all. Alas, I have no clue about the UV and weight problems (problem?).

UPDATE: Updated the image to include the model in Bones, for comparison's sake.

(https://dl.dropboxusercontent.com/u/93826015/NewBones1&2.jpg)
Title: Re: How to Fill SkinData
Post by: AGP on November 01, 2016, 03:12:07 pm
I'm even exporting animations, now. Trouble is Max's quaternions aren't making any sense to me, even when I call invert() (in the MaxScript). I've also included the transformation matrix, as retrieved for each vertex with bone.transform (MaxScript). Either I fix the quaternions or I extract the data from the transform matrix. The following method is used to build a jpct transformation matrix. Given that, how could I go about using the matrix?

Code: [Select]
     private Matrix getTransformationMatrix(JSONArray transformData) {
Matrix transformMatrix = new Matrix();
for (int j = 0; j < 4; j++) {
     JSONArray row = (JSONArray) transformData.get(j);
     transformMatrix.setRow(j,
     ((Number)row.get(0)).floatValue(),
     ((Number)row.get(1)).floatValue(),
     ((Number)row.get(2)).floatValue(), 1f);
}
return transformMatrix;
     }
Title: Re: How to Fill SkinData
Post by: raft on November 01, 2016, 04:54:22 pm
I have no idea how Max stores transformation infomation so I have no idea how to pull information out of that matrix.

Try Googling or Max documents.
Title: Re: How to Fill SkinData
Post by: AGP on November 01, 2016, 04:58:18 pm
Again, it's not about max. Sorry I even mentioned the max script. The question was: how do I turn a transformation matrix per vertex per frame into a SkinClip. I'll post how I did it with the quaternions in a little bit, but the matrices are easier to convert (I've already done it or I wouldn't even have a model).
Title: Re: How to Fill SkinData
Post by: raft on November 01, 2016, 05:45:08 pm
check javadoc of SkinClip and JointChannel.

think of JointChannel as a series of points in time describing Joint's transformation. times between points are interpolated.

you need to pull translations (SimpleVector) and rotations (Quaternion) data out of a series of that transformation Matrices.

Title: Re: How to Fill SkinData
Post by: AGP on November 01, 2016, 07:56:47 pm
The Matrix method is slightly better than the Quaternion one. Both methods produce a blob of animated polygons (but the Matrix blob has feet and a head somewhere in the middle). Animations are easily identifiable.

Code: [Select]
Matrix transformationMatrix = getTransformationMatrix(transformData);
times[i] = (float)i/(float)framesCount;
// translations[i] = new SimpleVector(((Number)translation.get(0)).floatValue(), ((Number)translation.get(1)).floatValue(), ((Number)translation.get(2)).floatValue());
// rotations[i] = new Quaternion(((Number)rotation.get(0)).floatValue(), ((Number)rotation.get(1)).floatValue(), ((Number)rotation.get(2)).floatValue(), ((Number)rotation.get(3)).floatValue());
translations[i] = SimpleVector.ORIGIN;//transformationMatrix.getTranslation();
rotations[i] = new Quaternion(transformationMatrix);

(https://dl.dropboxusercontent.com/u/93826015/TheBlob.jpg)
Title: Re: How to Fill SkinData
Post by: AGP on November 23, 2016, 11:16:12 am
I managed to export and re-import all the animation ciips in 3ds max (other than that UV issue, the imported model is identical to the original). But in order for them to work on Max, I'm exporting them in worldspace. The result works in max but still doesn't in jpct. Do you transform the transformation matrix at all from Ogre or Collada? If so, what do you do?
Title: Re: How to Fill SkinData
Post by: raft on November 23, 2016, 11:18:28 am
No, I don't do any transformation.
Title: Re: How to Fill SkinData
Post by: AGP on November 23, 2016, 11:11:57 pm
I expect that the answer will be the same, but just to clarify: I meant the transform matrices not of the object itself but of the animation description (per joint per frame, naturally).
Title: Re: How to Fill SkinData
Post by: raft on November 25, 2016, 03:55:05 pm
No, I don't do any transformation.
Title: Re: How to Fill SkinData
Post by: AGP on November 30, 2016, 06:22:06 am
Got animation working! Still has a weight problem. I'll post a video soon.
Title: Re: How to Fill SkinData
Post by: iguatemi on December 06, 2016, 04:26:32 pm
Hi, raft.

Should the vertices' weights be normalized?
I didn't find any reference about it in the docs or in this forum.

Thanks
Title: Re: How to Fill SkinData
Post by: raft on December 06, 2016, 05:02:14 pm
Should the vertices' weights be normalized?
you mean like sum of weights should be 1 per vertex? no there is no such requirement
Title: Re: How to Fill SkinData
Post by: iguatemi on December 06, 2016, 05:38:34 pm
Quote
you mean like sum of weights should be 1 per vertex? no there is no such requirement

great, thanks again :)
Title: Re: How to Fill SkinData
Post by: AGP on December 06, 2016, 05:49:00 pm
But actually, it's normalized out of 3ds max. So the better question is: can it be normalized or should I multiply them by some value? What is the range of the weights? Thanks in advance.
Title: Re: How to Fill SkinData
Post by: raft on December 06, 2016, 05:55:14 pm
programatically there is no restriction. if the value is too low its affect will be hardly noticable, if it's too big you will get some weird affects
Title: Re: How to Fill SkinData
Post by: AGP on December 11, 2016, 06:14:57 pm
I made a printout of the SkinDatas of the same model exported to BONES and exported to my JSON-serialized format in an effort to solve my weight problem (and, with a little luck, kill the UV issue as well with the same proverbial stone).  The following image shows that there's nearly six times more weights and jointIndices in the BONES version. SkinData doesn't hold animation information, does it? And if it's not animation, what could explain this? Thanks in advance.

(https://dl.dropboxusercontent.com/u/93826015/JSONvsBONES.png)
Title: Re: How to Fill SkinData
Post by: raft on December 11, 2016, 06:32:25 pm
Quote
SkinData doesn't hold animation information, does it?
no it doesnt.

not sure what's going on there. as a reminder Bones does not have exporter/importer code. it just uses JME's importer
Title: Re: How to Fill SkinData
Post by: iguatemi on December 23, 2016, 08:49:54 pm
hey, raft. in some models, some vertices have more than 4 joints associated to them. do you have any input on how to handle these cases?
Title: Re: How to Fill SkinData
Post by: AGP on December 25, 2016, 06:46:07 pm
And is it possible that other engines, like Unity, have some kind of logic to redistribute the weights for models that use more than four? Yet another question: is it trivial to increase the number of joints per vertices, or would that be a massive undertaking?
Title: Re: How to Fill SkinData
Post by: raft on December 25, 2016, 11:59:57 pm
hey, raft. in some models, some vertices have more than 4 joints associated to them. do you have any input on how to handle these cases?

need to check loader (jME, ardor3d) code but most possibly they are simply ignored. I cant see any good reason to support more than 4 joints. to me 4 joints are almost more than good enough
Title: Re: How to Fill SkinData
Post by: raft on December 26, 2016, 12:03:20 am
And is it possible that other engines, like Unity, have some kind of logic to redistribute the weights for models that use more than four? Yet another question: is it trivial to increase the number of joints per vertices, or would that be a massive undertaking?
assuming exporter supports it, it should be modified in loader code (jME, ardor3d). but again I dont see any good benefit for that
Title: Re: How to Fill SkinData
Post by: iguatemi on January 03, 2017, 08:53:29 pm
We have for the same model, two very different vertex counts (triple the number of triangles in jpct versus the unique vertex count which is typically less than the number of triangles in any given model), while retaining the same number of triangles, between jpct and 3ds max. But when we create our SkinData, we use only the number of unique vertices. Now we're thinking that we should be using the number of vertices in jpct. Did that make sense to you and do you agree?
Title: Re: How to Fill SkinData
Post by: raft on January 04, 2017, 10:37:22 am
what e
We have for the same model, two very different vertex counts (triple the number of triangles in jpct versus the unique vertex count which is typically less than the number of triangles in any given model), while retaining the same number of triangles, between jpct and 3ds max. But when we create our SkinData, we use only the number of unique vertices. Now we're thinking that we should be using the number of vertices in jpct. Did that make sense to you and do you agree?
what exactly is your pipeline?

if you are creating your skeletal data from scratch in code, and loading your model from 3ds format or similar, yes, you possibly need to use number of vertices instead of unique vertex count.
Title: Re: How to Fill SkinData
Post by: AGP on January 04, 2017, 05:24:18 pm
No, the bones are also defined in the file. What do you mean by our pipeline?
Title: Re: How to Fill SkinData
Post by: raft on January 04, 2017, 05:39:43 pm
I'm not sure I understand what you are trying to do.

No, the bones are also defined in the file.
then why are you creating a SkinData "manually"?
Title: Re: How to Fill SkinData
Post by: AGP on January 04, 2017, 06:08:08 pm
I saved the data from 3ds max's skin and I'm applying it to SkinData. Is there an automatic way to calculate SkinData? Am I being thick?
Title: Re: How to Fill SkinData
Post by: raft on January 04, 2017, 06:14:11 pm
we are talking about Bones' SkinData class, right?

when you import any skeletal animation into Bones, SkinData is automatically generated for you. you can access it via Animated3D.getSkinData()
Title: Re: How to Fill SkinData
Post by: AGP on January 04, 2017, 08:03:12 pm
I'd still need the very first one, for the model itself, right? We're still talking about my JSON-serialized format, you see.
Title: Re: How to Fill SkinData
Post by: raft on January 05, 2017, 12:08:20 am
I'd still need the very first one, for the model itself, right?
sorry?
We're still talking about my JSON-serialized format, you see.
in that case you shouldnt care about vertex count or unique vertex count in jPCT. your exporter from 3dsMax and importer to jPCT should agree on that.
Title: Re: How to Fill SkinData
Post by: AGP on January 13, 2017, 05:53:32 pm
Our skeleton order, on a regular Biped skeleton, goes: index 0 for the skeleton itself, 1 for "footsteps", 2 for pelvis, from which it then moves to the head before moving to the clavicles and arms. Is there any particular order (since what is basically a tree has to be converted into an ordered array) needed?
Title: Re: How to Fill SkinData
Post by: raft on January 13, 2017, 06:03:37 pm
as you intend to write the importer part too, it doesnt actually matter.

but Bones' internal logic requires joints in a skeleton are ordered such that a joint's parent joint always comes before the joint itself. this should be handled either at exporter or importer.
Title: Re: How to Fill SkinData
Post by: AGP on January 13, 2017, 10:07:51 pm
Yeah, we did that on both ends for performance. Still, we get joint issues. I'll post more on Tuesday. Thanks for the quick response.