-
-
Notifications
You must be signed in to change notification settings - Fork 35.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SkinnedMesh/Skeleton serialization #11603
Conversation
Can confirm this works, the skeleton is properly serialized and loaded. More work is necessary to serialize animation data, we'll submit a separate pull request for that. There's also a bit more work needed to support cloning a mesh with skeletons (the UUIDs need to be remapped after cloning) - we'll do a separate pull request for that as well. |
|
||
if ( this.bindMode !== undefined ) object.bindMode = this.bindMode; | ||
if ( this.bindMatrix !== undefined ) object.bindMatrix = this.bindMatrix.toArray(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this.bindMatrixInverse
? For the sake of completeness we should also regard this property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bindMatrixInverse
is calculated from bindMatrix
in SkinnedMesh.bind()
so I didn't think we need to serialize bindMatrixInverse
.
Do you think we should do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just prefer to serialize all public attributes. But your point of view is also valid. The current code is definitely correct. Because i don't have a strong opinion on this, i leave it up to you 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so let's keep this code so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you please close change request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@takahirox I'm sorry! I've missed your comment. Approving...
@@ -662,6 +668,20 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, { | |||
|
|||
} | |||
|
|||
// SkinnedMesh specific | |||
|
|||
if ( this.skeleton !== undefined ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi
Why not move this to a toJSON inside SkinnedMesh i think it would make more sense to move mesh serialization specifics to the Mesh and SkinnedMesh objects!
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can wait until this gets merged and i can try to propose moving and cleanup these after maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By moving that code to a toJSON method inside SkinnedMesh :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you try to make sample code and share with me?
I tried the same idea before but ended up realizing it'd be simpler if they'd be in Object3D.toJSON()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this
SkinnedMesh.prototype.toJSON = function(meta)
{
var data = THREE.Object3D.prototype.toJSON.call(this, meta);
if(this.bindMode !== undefined)
{
data.object.bindMode = this.bindMode;
}
if(this.bindMatrix !== undefined)
{
data.object.bindMatrix = this.bindMatrix.toArray();
}
if(this.skeleton !== undefined)
{
if(meta.skeletons[this.skeleton.uuid] === undefined)
{
meta.skeletons[this.skeleton.uuid] = this.skeleton.toJSON(meta);
}
data.object.skeleton = this.skeleton.uuid;
}
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do this once - it's trickier than it seems, because of how toJSON
is written, it makes it hard to do this without copy-pasting the whole toJSON
function.
Ideally, since SkinnedMesh
extends Object3D
, you should just be able to call Object3D.prototype.toJSON.call(this, meta)
- but in practice it's trickier, because Object3D.toJSON
defines a bunch of internal functions - not sure if we need those internal functions for the skeleton, but I have run into cases in the past where this made it difficult without duplicating a lot of code, or restructuring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well im doing a ton in nunuStudio i have pretty much overrided more than half of the threejs serialization process to include resources, external lib data etc.
There is some tricks to it sometimes (specially when working with object relations) but for these cases its pretty straight forward just call the thing on Object3D and add whatever you need to add, its always better and cleaner than filling Object3D serialization with a ton of verifications to decide whether or not to include a resource from a specific class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, your code looks good, and this is definitely the way to go rather than piling it all into Object3D
. Maybe at some point we can move the serialize
and extractFromCache
local convenience functions out to be either methods of the Object3D
class or part of a utility class, to make it easier to do this for all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think your code is good enough.
I think meta
initialization and isRootObject
related codes are also necessary in SkinnedMesh.toJSON()
in addition to extractFromCache
.
Imagine what'll happen with
var skin = new THREE.SkinnesMesh( geometry, material );
skin.toJSON();
I don't really like making duplicated codes in SkinnedMesh
.
So IMO, as @jbaicoianu mentioned, we need restructuring if we move this into SkinnedMesh
.
Let's make another PR if we do.
src/loaders/ObjectLoader.js
Outdated
|
||
if ( skeleton === undefined ) { | ||
|
||
console.warn( 'THREE.ObjectLoader: Not found Skeleton whose uuid is ' + obj.sjeletonUUID ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in console.warn( 'THREE.ObjectLoa.....+ obj.skeletonUUID );
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if im being a bit annoying :), i really want this PR to be merged i was comparing it to what i was doing on nunu xD
Awesome work thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks!
@mrdoob Any concerns about this PR? |
I think we should try to move forward with SkinnedMesh/Skeleton serialization so we can enable Skinned Meshes in the editor (#8422). |
Yup, I wanna enable SkinnedMesh edit on the editor! |
@mrdoob Please let me know if there's any concerns in this PR. |
I have tested and merged this into nunuStudio everything seems to work just fine (i have to start adding mentions on my commits over there), skeleton serialization is working as expected. Im able to edit bones and load everything without any problem. Thanks a lot! I think this should be merged 👍 |
Solved the conflicts. |
Any chance of getting this merged before the codebase diverges again? |
/ping @mrdoob |
1 similar comment
/ping @mrdoob |
Fixed conflicts. |
Solved the conflicts. |
Solved the conflict. |
Can we add this PR to the r91 milestone together with #11596? 😅 |
@@ -737,6 +810,10 @@ Object.assign( ObjectLoader.prototype, { | |||
|
|||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a break;
missing here for the Mesh case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, thanks. I'll fix later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks!
@mrdoob What do you think the odds are that this will get merged in for r97? Is there anything else that needs to be done here? |
There are a number of sample glTF models for various tricky skinning cases here: It may be worth testing this PR against them and seeing which work, and which don't. We don't necessarily have to support them all — the glTF format may add restrictions to disallow some of them, even — but it seems like a good way to test this change. |
This PR enables
SkinnedMesh
/Skeleton
serialization.The code became much simpler than the one I proposed before #8978
Serialized
Bone
s are stored under scene graph("object"), but serializedSkeleton
s are stored out of scene graph withBone
s uuids andboneInverse
matrices. SerializedSkinnedMesh
has skeleton's uuid, likegeometry
andmaterial
. This's for shared skeleton which we'll support sooner or later.Bone
s are under scene graph, so any objects underBone
are also serialized correctly without any special logics.ObjectLoader
temporally undefinesgeometry.bones
at theSkinnedMesh
instantiation to preventBone
s creation inSkinnedMesh
constructor if serializedSkinnedMesh
hasSkeleton
uuid. Unless we do that, we'll have two duplicatedBone
sets (one is from serialization, another is from creation in constructor).This's an example of the result of
SkinnedMesh
.toJSON()
. (Omitting some parts)You can confirm it works by running the following code on the console in
webgl_animation_skinning_morph
example.Note: targeting
BufferGeometry
, notGeometry
so far. See #8978Left is original, right is serialized and then deserialized.
I've worked on this PR with @jbaicoianu , thanks!