mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.58k stars 35.36k forks source link

toJSON() and fromJSON() #11266

Open whatisor opened 7 years ago

whatisor commented 7 years ago
Description of the problem

We have toJSON api. As a consistency, I think we should have fromJSON. It will be powerful for data management.

Three.js version
Browser
OS
Hardware Requirements (graphics card, VR Device, ...)
tentone commented 7 years ago

This would actually be a great ideia, we could have JSON serialization all in one place for every object, objects could register in the loaders and this would avoid really big handwritten type check lists in the loaders.

Something like this would be cool

Object.prototype.toJSON = ...

Object.fromJSON = ...

XPTOLoader.register("type", Object);

It would be also nice to apply this to resources, that main problem here is the fact that is kinda of a big code change, but i think its worth.

whatisor commented 6 years ago

I am using toJSON (for object, not for Geometry), ObjectLoader can load back. However, it lacks Animation.

Mugen87 commented 6 years ago

However, it lacks Animation.

Can you explain a bit? ObjectLoader is able to parse animations. Besides, AnimationClip has a toJSON() method.

whatisor commented 6 years ago

@Mugen87 My flow is:

donmccurdy commented 6 years ago

A bit of clarification — the mesh does not include animation because the mesh does not "have" animation, or own a reference to it. A single animation clip might be targeting many meshes, or non-mesh objects, or even objects in multiple scenes. Because animation is not a descendant of the scene, or attached to a descendant, you will have to export it separately. Similarly, when exporting to glTF you'd need to pass in a list of animations explicitly:

exporter.parse( scene, function ( gltf ) {
  // ...
}, { animations: [clip1, clip2, ...] } );

That aside, the idea of distributing the ObjectLoader implementation into a fromJSON method on each class sounds promising...

whatisor commented 6 years ago

@donmccurdy I mean to export original Object loaded from ObjectLoader, not mesh inside. It is THREE.Group which include animations

donmccurdy commented 6 years ago

I understand that seems like it should work, but technically a THREE.Group has no official way of "including animations", even though a JSON file does, and I don't think there's any way to implement that without an extra step to include the animations with the object(s). I still like the idea of having a fromJSON method on objects.

Itee commented 6 years ago

Should it be interesting to inherit an "serializable interface" with type, toJson and fromJson ?

HypnosNova commented 5 years ago

I think it would be better to let classes such as BufferGeometry(BoxBufferGeometry, SphereBufferGeometry ...etc), Material(MeshBasicMaterial, ...etc), Mesh to have their own "fromJSON" method. The ObjectLoader is just like a factory design pattern. If we use ObjectLoader to parse json, we must load a json file but actually loading json and parsing json are 2 different things. we might use parse method without loading a json file, maybe we already has a json object and want to parse it into instance object. So each class has fromJSON method to handle their json format, and ObjectLoader just use these classes' method to parse json.

donmccurdy commented 4 years ago

For materials, material.fromJSON( json ) would work with inheritance quite well. MeshPhysicalMaterial can deserialize its own properties, and call super.fromJSON( ... ) to get the inherited properties. It would import Color and Texture classes but not much else.

But what about scene.fromJSON( json )? The scene could have any kind of object as children ... Object3D, Mesh, InstancedMesh, PointLight, SpotLight, LightProbe, ... and ideally the Scene class should not import all of that, to ensure that tree-shaking works. It would be even better if it's possible for a custom object, like MyCustomMesh, to implement toJSON() and fromJSON() and "just work" when serializing/deserializing a scene that contains it.

I'm not sure how to structure that. We need to avoid import * as THREE to maintain tree-shaking. Maybe it needs a factory argument, like fromJSON( json, loader ) and the user can provide the whole THREE namespace if they don't care about tree-shaking, a subset if they do, or custom objects in addition to the core library?

// Give me everything, forget tree-shaking.
import * as THREE from 'three';

var loader = new THREE.ObjectLoader().register( THREE );
var scene = new THREE.Scene().fromJSON( json, loader );

// Tree-shaking, please.
import {Scene, Mesh, Object3D, Group} from 'three';

var loader = new THREE.ObjectLoader().register( {
  Scene, Mesh, Object3D, Group
} );
var scene = new THREE.Scene().fromJSON( json, loader );

// Tree-shaking++.
import {Scene, Mesh, Object3D, Group} from 'three';
import {MagicMesh} from 'three-magic-mesh';

var loader = new THREE.ObjectLoader().register( {
  Scene, Mesh, Object3D, Group, MagicMesh
} );
var scene = new THREE.Scene().fromJSON( json, loader );

I only mention objects above, but it's similar for materials and geometries. :/

markeasting commented 1 year ago

I'm running into this right now - it seems like all responsibility of (re)constructing objects is in the hands of ObjectLoader, e.g.

https://github.com/mrdoob/three.js/blob/aef215178070679b8407832a9a71123978807456/src/loaders/ObjectLoader.js#L900-L904

It would be easier to extend the loader if most classes were to implement a to/fromJSON method.

Also, because of this large switch case on the object's type parameter, it's difficult to add another custom type. It's seems tightly coupled with setting the objects other properties, like matrices, animations and such. I'd like to see that step separated out.