playcanvas / playcanvas/engine

Add toJSON for the assets

Open
#1,053 3 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
JavaScript
Stars
16.8k
Forks
2k
Avg merge
4h 32m
Merged PRs (30d)
222

Description

It is good for all assets to have toJSON method, so that we can save and init the asset by data easily.

Here is my StandardMaterial.prototype.toJSON implementation:

import constants from './constants';

pc.StandardMaterial.prototype.toJSON = (function() {

    var fields = [
        'alphaTest', 'alphaToCoverage',
        'ambient', 'ambientTint',
        'aoMap', 'aoMapChannel', 'aoMapOffset', 'aoMapTiling',
        'aoMapUv', 'aoMapVertexColor',
        'blendType',
        'bumpiness',
        'conserveEnergy',
        'cubeMap', 'cubeMapProjection', 'cubeMapProjectionBox',
        'cull',
        'depthTest', 'depthWrite', 'diffuse',
        'diffuseMap', 'diffuseMapChannel', 'diffuseMapOffset', 'diffuseMapTiling',
        'diffuseMapTint', 'diffuseMapUv', 'diffuseMapVertexColor',
        'emissive', 'emissiveIntensity', 'emissiveMap',
        'emissiveMapChannel', 'emissiveMapOffset', 'emissiveMapTiling',
        'emissiveMapTint', 'emissiveMapUv', 'emissiveMapVertexColor',
        'fresnelModel',
        'glossMap', 'glossMapChannel', 'glossMapOffset', 'glossMapTiling',
        'glossMapUv', 'glossMapVertexColor',
        'heightMap', 'heightMapChannel', 'heightMapFactor',
        'heightMapOffset', 'heightMapTiling', 'heightMapVertexColor', 'heightMapUv',
        'lightMap', 'lightMapChannel', 'lightMapOffset',
        'lightMapTiling', 'lightMapUv', 'lightMapVertexColor',
        'metalness', 'metalnessMap', 'metalnessMapChannel',
        'metalnessMapOffset', 'metalnessMapTiling',
        'metalnessMapUv', 'metalnessMapVertexColor',
        'name',
        'normalMap', 'normalMapOffset', 'normalMapTiling',
        'normalMapUv', 'normalMapVertexColor',
        'occludeSpecular', 'occludeSpecularIntensity',
        'opacity', 'opacityMap', 'opacityMapChannel',
        'opacityMapOffset', 'opacityMapTiling',
        'opacityMapUv', 'opacityMapVertexColor',
        'reflectivity', 'refraction', 'refractionIndex',
        'shadingModel',
        'shadowSampleType',
        'shininess',
        'specular', 'specularAntialias', 'specularMap',
        'specularMapChannel', 'specularMapOffset', 'specularMapTiling',
        'specularMapTint', 'specularMapUv', 'specularMapVertexColor', 'sphereMap',
        'useFog', 'useGammaTonemap', 'useLighting',
        'useMetalness', 'useSkybox',
    ];

    var defaultMaterial = toJSON.call(pc.ModelHandler.DEFAULT_MATERIAL);

    function toJSON(options) {
        var result = {};
        var self = this;

        if (typeof options === 'undefined') {
            options = {};
        }

        fields.forEach(function(field) {
            // texture
            if (constants.MATERIAL_TEXTURE_FIELDS2[field]) {

                if (self[field] !== null) {
                    result[field] = self[field].name;
                } else {
                    result[field] = null;
                }

            // color or vector
            } else if (constants.MATERIAL_ARRAY_FIELDS2[field]) {

                result[field] = Array.from(self[field].data);

            } else if (constants.MATERIAL_OBJECT_FIELDS2[field]) {

                result[field] = self[field] ? self[field].toJSON() : null;

            // primitive
            } else {

                // convert bumpiness to bumpMapFactor
                if (field === 'bumpiness') {
                    result['bumpMapFactor'] = self[field];
                } else {
                    result[field] = self[field];
                }
            }
        });

        if (options.diff) {
            fields.forEach(function(field) {

                if (field === 'bumpiness') {
                    field = 'bumpMapFactor';
                }

                var value1 = defaultMaterial[field];
                var value2 = result[field];

                // test two array is equal or not
                if (constants.MATERIAL_ARRAY_FIELDS2[field]) {
                    value1 = value1.toString();
                    value2 = value2.toString();
                }

                if (constants.MATERIAL_OBJECT_FIELDS2[field]) {
                    value1 = JSON.stringify(value1);
                    value2 = JSON.stringify(value2);
                }

                if (value1 === value2) {
                    delete result[field];
                }
            });
        }

        return result;
    }

    return toJSON;
})();

And the constants:

var MATERIAL_TEXTURE_FIELDS = [
    'aoMap', 'diffuseMap', 'glossMap', 'metalnessMap',
    'specularMap', 'emissiveMap', 'opacityMap', 'lightMap',
    'normalMap', 'heightMap', 'sphereMap', 'cubeMap'
];

var MATERIAL_TEXTURE_FIELDS2 = MATERIAL_TEXTURE_FIELDS.reduce(function(prev, next) {
    prev[next] = true;
    return prev;
}, {});

var MATERIAL_COLOR_FIELDS = [
    'diffuse', 'specular', 'ambient', 'emissive'
];

var MATERIAL_VECTOR_FIELDS = [
    // vec2
    'diffuseMapOffset', 'diffuseMapTiling',
    'opacityMapOffset', 'opacityMapTiling',
    'aoMapOffset', 'aoMapTiling',
    'specularMapOffset', 'specularMapTiling',
    'metalnessMapOffset', 'metalnessMapTiling',
    'glossMapOffset', 'glossMapTiling',
    'heightMapOffset', 'heightMapTiling',
    'emissiveMapOffset', 'emissiveMapTiling',
    'normalMapOffset', 'normalMapTiling',
    'lightMapOffset', 'lightMapTiling',
];

var MATERIAL_OBJECT_FIELDS = [
    'cubeMapProjectionBox'
];
var MATERIAL_OBJECT_FIELDS2 = MATERIAL_OBJECT_FIELDS.reduce(function(prev, next) {
    prev[next] = true;
    return prev;
}, {});

var MATERIAL_ARRAY_FIELDS = MATERIAL_COLOR_FIELDS.concat(MATERIAL_VECTOR_FIELDS);
var MATERIAL_ARRAY_FIELDS2 = MATERIAL_ARRAY_FIELDS.reduce(function(prev, next) {
    prev[next] = true;
    return prev;
}, {});

export default {
    MATERIAL_TEXTURE_FIELDS: MATERIAL_TEXTURE_FIELDS,
    MATERIAL_TEXTURE_FIELDS2: MATERIAL_TEXTURE_FIELDS2,
    MATERIAL_ARRAY_FIELDS: MATERIAL_ARRAY_FIELDS,
    MATERIAL_ARRAY_FIELDS2: MATERIAL_ARRAY_FIELDS2,
    MATERIAL_OBJECT_FIELDS: MATERIAL_OBJECT_FIELDS,
    MATERIAL_OBJECT_FIELDS2: MATERIAL_OBJECT_FIELDS2,
};

We can call it by:

var mat = new pc.StandardMaterial();
mat.diffuse = new pc.Color(1,0,0,1);

mat.toJSON() // return the full json
/**
 * { diffuse: [1, 0, 0, 1], diffuseMap: null, blendType: 0 ... }
 */


mat.toJSON({ diff: true }); // return the json different from mat.toJSON()
/**
 * { diffuse: [1, 0, 0, 1] }
 */

We can add the same interface to other type of assets. 😃

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating StandardMaterial.prototype.toJSON, the referenced constants, and pc.ModelHandler.DEFAULT_MATERIAL. Review how the proposed serialization distinguishes textures, arrays, objects, and primitive fields, then identify the other asset types that should expose the same interface. Done means assets can be serialized and initialized from JSON, including the proposed full and diff forms, with coverage for the supported asset types.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
computer-graphics
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.