Loading serialized material from database

Hi,

Skip to the short version at the bottom please.

the user sets up his material using NME and then saves his work to database. This happens in vue frontend, so I loop over the meshes, and store them in JSON parsed array with material.serialize() function. This gets to laravel backend, where the array of materials is stored in a long text column type in json format. Well, to be specific, it’s not an array, it’s an object keyed by mesh id. And value is the serialize() result.

Then in she show view, the meshes and materials are loaded. For each mesh, I look up it’s material from the mentioneded materials object. I get this by calling const dbMats = JSON.parse(materials).

Now I want to assign material back to its mesh, so I would do this.meshes[key].material = BABYLON.NodeMaterial.Parse(dbMats[key], this.scene) in a loop.

However, when I do this, i get an error: Uncaught (in promise) Build of NodeMaterial failed: input vector from block WorldPos[TransformBlock] is not connected and is not optional.. So I open up the NME and I see that most of the blocks are not connected.

I decided to debug this first by looking up the blocks inputs. There is only a single block Rotate2D, so I started with this one. In database, I can see that it has 2 inputs, both with correct targetBlockId, as it should be. This is also true, when I console log the received json on the frontend. But when I open it up in the NME and I see the blocks are not connected and I hit the save button and open this json file, the block has only one input there. Well, still rather two, but the input one doesn’t have the targetBlockId set at all, even tho the VectorMerger blocks exists there, not connected to anything.

One more thing I thought about was that using JSON.parse() on backend, doesn’t only parse the top level, but the serialized material too. So I tried doing this too

for (let key in materials) {
    const jsonAgain = JSON.stringify(materials[key])
    console.log(jsonAgain)
    this.meshes[key].material = BABYLON.NodeMaterial.Parse(jsonAgain, this.scene)
}

But it would result in other error

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')
    at t2.parseSerializedObject (nodeMaterial.ts:1926:35)
    at t2.Parse (nodeMaterial.ts:2046:22)
    at VueComponent.setupModel (TdModelView.vue:109:1)
    at async VueComponent.setup (TdModelView.vue:81:1)

Short version

In this repo you can see commits starting before upload.

  1. before upload is downloaded from the nme using save button.
  2. from db is copied straight from phpmyadmin column. it’s gotten to db by using material.serialize()
  3. console log json parsed received from db is result of
const materials = JSON.parse(dbMaterials)
console.log(materials[key])
  1. saved from nme is after opening the nme is the show view and pressing save button

First three are working just fine. After 3rd point, this code happens

this.meshes[key].material = BABYLON.NodeMaterial.Parse(materials[key], this.scene)

And I get the WorldPos[TransformBlock] is not connected error. So I open the NME, see only a few blocks connected and download the 4th point version.

Where does it break up? Could it be the blocks ids changing? Is it something else?

Thank you.

Step 2 is already wrong. If you look at the json that is produced, you will see that there are missing spaces in the name of some entries of the vector merger / splitter blocks:

“before upload” file:

file “from db” :

As you can see, some of the name / inputName fields have a space at the end in the first case, but not in the second.

This is not a problem with material.serialize(), which generates the correct code (with the trailing space) in :

So, it seems there’s a problem when you store the file in the DB, or when you retrieve it.

2 Likes

Holy molly, how could I’ve missed that. I solved it by calling JSON.stringify() on the result of serialize() and then stored it to the db. Then I JSON.parse() both object/array of materials and individual materials before babylon parsing.

Store:

this.meshes.forEach(m => data.materials[m.id] = JSON.stringify(m.material.serialize()))

Get:

const mats = JSON.parse(dbMats)
for (let key in mats) {
    this.meshes[key].material = BABYLON.NodeMaterial.Parse(JSON.parse(mats[key]), this.scene)
}

Thank you very much for pointing this out.