Cannot read property 'id' of null when trying to serialize mesh and importing again as babylon file

Good day everyone. I’m currently facing a problem with saving a meshes as a babylon file.

Right now, I have done the following:

  • I have a model of a lion with panels, the panels are detachable from the lion’s body and can be designed with flowers (which are really planes with materials/ diffuse texture set on it). What I do is I clone the flower on the book when it is clicked using mesh.clone(cloneName) method. See image below:

  • What I do is after placement of flower on the panel, the user can choose to save his current progress. There is no problem with I place unique flowers on each panel like the image below:

With the above sample, I encounter no problem when saving it and importing it to the scene on reload.

  • I only encounter the error when I design a panel with duplicated of one type of flower clicked like in the image below:

This is how I save the designed panel / serialize the meshes:
function save_designed_panels(filename) {
designCamera.detachControl(canvas);
var serializedMesh;

//the mapping of the panel:flowers will be saved here
let savedFlowersList = {};

//mapping of the serialized mesh will be saved here
var meshes_to_save = {                       
    "materials": [],                        
    "geometries":    {    
        "boxes":[],                                            
        "spheres":[],                                            
        "cylinders":[],                                            
        "toruses":[],                                            
        "grounds":[],                                            
        "planes":[],                                            
        "torusKnots":[],                                            
        "vertexData":[]                        
    },                        
    "meshes": []            
};  

if(objectUrl) {
    window.URL.revokeObjectURL(objectUrl);
}

//for each flower mesh placed in each panel taken from the flowerspanelsmap, serialize each mesh
for (const [panel,val] of flowersPanelsMap.entries()) {
    if(val.size>0){
        serializedMesh = BABYLON.SceneSerializer.SerializeMesh(panel,false,true);    
        for (let i=0;i<serializedMesh["meshes"].length;i++) { 
            meshes_to_save["materials"].push(serializedMesh["materials"][i]);            
            meshes_to_save["geometries"]["vertexData"].push(serializedMesh["geometries"]["vertexData"][i]);   
            meshes_to_save["meshes"].push(serializedMesh["meshes"][i]);  
        } 
    }
}

//for each flower mesh placed in each panel taken from the flowerspanelsmap, serialize each mesh
//the modified_panels_list contains the panels:flowers mapping taken from the database, only update the flowers in the db with updated flowers
for (const [panel,val] of flowersPanelsMap.entries()) {
    if(modified_panels_list.has(panel.name)){
        let temp = [];
       
            for(const a of val.values()){
                temp.push(a.name);
            }
      
        if(temp.length == 0) temp = "0";
        
       savedFlowersList[panel.name] = temp;
    }
}

//convert the meshes to blob format and save as babylon file
var strMesh = JSON.stringify(meshes_to_save);

var blob = new Blob ( [ strMesh ], { type : "octet/stream" } );
if (filename.toLowerCase().lastIndexOf(".babylon") !== filename.length - 8 || filename.length < 9){
    filename += ".babylon";
}

//post to backend to save the user's progress
if(blob){
    $.ajax({
        type: "POST",
        url:urlDesignPanel,
        data:{
            uid: userId,
            designPath:userId+"_designedPanel.babylon",
            babylonFile:strMesh,
            flowersList:savedFlowersList,
            _token:token
        },
        success: function(result){
            Swal.fire({
                width: '10vw',
                padding: '3em',
                background: 'rgba(8, 64, 147, 0.6) url(front/images3D/designScene/trevorSaved.png)',
                title: '\n\n\n\nYour progress has been saved.',
                showConfirmButton: false,
                position: 'top-end',
                timer: 3000,
                customClass: {
                  popup: 'trevor-popup-class',
                }
              });
        
            designCamera.attachControl(canvas,true);
            savedFlowersList = {};
            is_design_saved = true;
        },
        error: function(result){
            Swal.fire({
                width: '10vw',
                padding: '3em',
                background: 'rgba(8, 64, 147, 0.6) url(front/images3D/designScene/trevorSaved.png) ',
                title: '\n\n\n\nOops...something went wrong. Your progress was not saved.',
                showConfirmButton: false,
                position: 'top-end',
                timer: 3000,
                customClass: {
                    title: 'error-title-class',
                    popup: 'trevor-popup-class',
                },
            });
        }
    });
}     

}

And this is how I import the panels on page access:

function load_saved_game(){

Promise.all([
    BABYLON.SceneLoader.ImportMeshAsync(null, "storage/saveState/designPanel/", load_filename, designScene).then(function (result) {
      
        result.meshes.forEach(function(m){
            if(mbayePanelsMap.has(m.name)){
                m.setParent(mbayeDesign_object);
                //mbayePanelsMap is a fresh mapping of the panels:flowers loaded from the database when the page is loaded
                if(mbayePanelsMap.get(m.name) !== null)  mbayePanelsMap.get(m.name).dispose();
                m.isPickable = true;
                m.material.emissiveColor = new BABYLON.Color3(0,0,0);
                mbayePanelsMap.set(m.name, m);
               
               
                let children = set_panel_children(m._children);
                flowersPanelsMap.set(m, children);

            }
        });
     
    }),
    
]).then(() => {
    isProgressLoaded = true;
});

}

And this is the error I am getting when I load the page when I have previously designed a panel with flowers of the same type.

I am not sure why I am getting this error and I also tried loading the exported babylon file in sandbox and it throws me error that id is null. Please guide me where I am doing wrong. Thank you.

Page load error:
image

Sandbox error:

For reference, this is how I clone the black flower from the book. onPointerDown, I call this function:
function create_black_flower_copy(theFlower){
//create a clone of the flower clicked
let theFlowerCopy = theFlower.clone(theFlower.name);
theFlowerCopy.material = theFlower.material.clone(theFlower.name+flowerCtr+“Matl”);
theFlowerCopy.material.id = theFlowerCopy.material.name;

//i have also tried createInstance but it behaves differently 
// let theFlowerCopy = theFlower.createInstance(theFlower.name + flowerCtr);
// theFlowerCopy.setParent(theFlowerCopy.parent)
flowerCtr++;
let flowerHasParent = false;
theFlowerCopy.id = theFlowerCopy.id + "_"+theFlowerCopy.uniqueId;

;

//if the active camera is panel camera or focus camera
if(designScene.activeCamera === panelCamera ){
    if(theFlower.parent === bookLeftPages) flowerHasParent = true;
}else{
    if(theFlower.parent === bookLeftPages) flowerHasParent = true;
}

//if the clone has a parent, set position x and y depending if the camera is panel or focus
if(flowerHasParent){                        //if focus camera
    theFlowerCopy.position.y += 50;
    theFlowerCopy.position.x -= 50;
    theFlowerCopy.rotation = new BABYLON.Vector3(BABYLON.Tools.ToRadians(155),BABYLON.Tools.ToRadians(0),BABYLON.Tools.ToRadians(0));
    theFlowerCopy.setParent(null);
    
}else{          
    theFlowerCopy.position.y += 2.5;
    theFlowerCopy.position.x += 0.1;
}
  
//increment the current flower selection count
flowerSelectionCount++;
if(theCurrentFlower.obj) theCurrentFlower.obj.showBoundingBox = false;

//attach the position gizmo to the flower clone
designGizmoManager.positionGizmoEnabled = true;
designGizmoManager.attachToMesh(theFlowerCopy);


//set the clone as the current flower and show bounding box
theCurrentFlower.obj = theFlowerCopy;
theCurrentFlower.obj.showBoundingBox = true;
//add the flower to the selected flowers map
selectedFlowersMap.set(theFlowerCopy.uniqueId, theFlowerCopy);


set_gizmo_style("#positionGizmo");
$("#gizmoToolsDiv").css('display','flex');
designGizmoManager.positionGizmoEnabled = true;
designGizmoManager.attachToMesh(theFlowerCopy);

}

Hi!

Pretty hard to follow, would you be able to create a minimal reproduction of the issue? preferably in the playground? This way we can understand where the error is coming from.

I found out where the problem is. When I was cloning the plane multiple times, the geometry id for the duplicates are not generated so before cloning, I set the plane property to makeGeometryUnique() and now the meshes are serializing properly. The error ‘Cannot read property ‘id’ of null’ is because it is looking for the verticesData of the duplicates and it was not found because initially, one geometry id is assigned to all duplicates/clones of the plane. After adding the method makeGeometryUnique(), the problem is resolved. Thank you.

1 Like