Serialization
Easy serialization of all threepipe and most three.js objects are supported out of the box using the Asset Manager. Fine control over serialization is also supported using the ThreeSerialization class
Call ThreeSerialization.serialize
on any object to serialize it. and ThreeSerialization.deserialize
to deserialize the serialized object.
This is done by performing a nested serialization of all the properties of the object. It's possible to implement custom serializers for custom types and classes and is done for three.js primitives, objects and plugins in threepipe.
Serializable Objects
To make a custom data class that is serializable, mark it using @serializable
decorator and any properties using @serialize
decorator.
@serializable('DataClass')
class DataClass{
@serialize() prop1 = 1
@serialize() prop2 = 'string'
@serialize() prop3 = new Vector3()
@serialize() prop4 = new PhysicalMaterial()
@serialize() prop4 = {
prop1: 1,
prop2: 'string',
prop3: new Vector3(),
prop4: new PhysicalMaterial(),
}
}
const data = new DataClass()
const serialized = ThreeSerialization.serialize(data)
const deserialized = ThreeSerialization.deserialize(serialized)
The classes without a @serializable
decorator are serialized as plain objects. These can still include @serialize
decorator to mark the properties are serializable but these classes cannot be deserialized into a new instance of the class. The ThreeViewer and plugins are an example of these. When deserialized they need an object to deserialize into. This ensures there is always just one instance. With this, the serialization system works like toJSON
and fromJSON
methods in three.js.
Check the plugin system below for more details on how to mark properties as serializable for plugins.
class CustomClass{
@serialize() prop1 = 1
@serialize() prop2 = 'string'
@serialize() prop3 = new Vector3()
@serialize() prop4 = new PhysicalMaterial()
}
const obj = new DataClass()
const serialized = ThreeSerialization.serialize(data)
// now to deserialize we need to pass in the object to deserialize into
ThreeSerialization.deserialize(serialized, obj)
toJSON and fromJSON
You can also implement the toJSON and fromJSON functions ot the class to customize the serialization and deserialization process.
class MyClass{
// ...
toJSON(meta?: SerializationMetaType): ISerializedConfig {
const data: any = ThreeSerialization.Serialize(this, meta, true) // last param set to true to indicate not to call toJSON.
data.type = 'MyType'
data.assetType = 'config'
this.dispatchEvent({type: 'serialize', data}) // optional
return data
}
fromJSON(data: ISerializedConfig, meta?: SerializationMetaType): this|null|Promise<this|null> {
if (data.type !== 'MyType') return null
ThreeSerialization.Deserialize(data, this, meta, true) // last param set to true to indicate not to call fromJSON.
this.dispatchEvent({type: 'deserialize', data, meta}) // optional
return this
}
}
When calling ThreeSerialization.Serialize
on an object of MyClass
, it will call toJSON
on the object and serialize the properties of the object. Similarly, when calling ThreeSerialization.Deserialize
on an object of MyClass
, it will call fromJSON
on the object and deserialize the properties of the object.
Extending classes
When extending classes that have toJSON
and fromJSON
methods(like three.js objects), you can call the super methods and then serialize the properties of the class.
class MyMaterial extends ThreeMaterial{
// ...
/**
* Serializes this material to JSON.
* @param meta - metadata for serialization
* @param _internal - Calls only super.toJSON, does internal three.js serialization and @serialize tags. Set it to true only if you know what you are doing. This is used in Serialization->serializer->material
*/
toJSON(meta?: SerializationMetaType, _internal = false): any {
if (_internal) return {
...super.toJSON(meta),
...ThreeSerialization.Serialize(this, meta, true), // this will serialize the properties of this class(like defined with @serialize and @serialize attribute)
}
return ThreeSerialization.Serialize(this, meta, false) // this will call toJSON again, but with baseOnly=true, that's why we set isThis to false.
}
/**
* Deserializes the material from JSON.
* Note: some properties that are not serialized in Material.toJSON when they are default values (like side, alphaTest, blending, maps), they wont be reverted back if not present in JSON
* If _internal = true, Textures should be loaded and in meta.textures before calling this method.
* @param data
* @param meta
* @param _internal
*/
fromJSON(data: any, meta?: SerializationMetaType, _internal = false): this | null {
if (_internal) {
ThreeSerialization.Deserialize(data, this, meta, true)
return this.setValues(data) // todo remove this and add @serialize decorator to properties
}
this.dispatchEvent({type: 'beforeDeserialize', data, meta, bubbleToObject: true, bubbleToParent: true})
return this
}
}