basically what I have is a scene containing plane geometry and a number of meshes ( drawn or taken from GLB files). What would be the best way to save this scene and open it later in order to continue with work? I tried to do it with GLTFExporter but once the scene is imported it lost some information so it is clearly not the best way to achieve this. I’ve read that the best would be to export scene as JSON and import it later. If this is the case, could you please write how to do it? I’ve tried some examples from the internet but none of them worked. Many thanks!!
thank you for your answer!
However when I tried to export I got the json file (stringified) with this function:
function exportSceneToJSON(input){
control.detach();
scene.remove(control);
var result=scene.toJSON();
var output =JSON.stringify(result);
download(output, 'scene.json', 'application/json');
}
the file I got is attached.
Then i tried to load the file with this function:
function importObject(inp){
control.detach();
if (inp.files && inp.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var loader = new THREE.ObjectLoader();
loader.load(e.target.result,
function(json){
scene = JSON.parse( e.target.result );
render();
});
}
reader.readAsDataURL(inp.files[0]);
}
}
and I got:
three.js:41299 Uncaught TypeError: Cannot read property ‘type’ of undefined
at ObjectLoader.parseObject (three.js:41299)
at ObjectLoader.parse (three.js:40697)
at importObject (index.js:217)
at HTMLInputElement.onchange ((index):27)[testJSON.json|attachment]
Any idea what is wrong here?
(upload://6WbrPCF2qs8CwSfXiLvJmfGzXdo.json) (3.9 KB)
The usage does not look right. In the onLoad() callback, it’s not necessary to call JSON.parse(). Besides, I don’t think you have to use ObjectLoader.load() in this context. Try it similar to the editor:
You can also try saving it to glTF using the GLTFExporter. If you do this it’s reccomended not to embed textures as the browser will rewrite them usually with worse compression than you started out with.
function exportSceneToJSON(input){
control.detach();
scene.remove(control);
scene.updateMatrixWorld();
var result=scene.toJSON();
var output =JSON.stringify(result);
download(output, 'scene.json', 'application/json');
}
In the code you will also see the section ‘export to png’ where I was trying to export scene to png image. This works but the quality of image is pretty low - is there a way to improve it?
Okay, the problem in your app is related to such code:
var planeGeom = new THREE.PlaneGeometry( planedimensions, planedimensions );
planeGeom.rotateX( - Math.PI / 2 );
Although the API allows such a usage, it does not work when serializing/deserializing. three.js only saves the parameters of geometry generators in JSON and not their actual buffer attributes. Meaning if you transform the geometries via rotateX() or translate(), you don’t save these changes in JSON. Only the width and height of the plane (defined by planedimensions). Hence, use this approach:
var planeGeom = new THREE.PlaneBufferGeometry( planedimensions, planedimensions );
plane.rotation.x = Math.PI * - 0.5;
Meaning never transform the result of a geometry generator. Also use the BufferGeometry version whenever possible.
thanks a lot! It works much better now!
The scene and meshes are now loaded correctly.
However I still have a little issue when I try to make wall out of mouse clicks.
Everything works fine but new mesh has position of 0,0,0 which makes problem when I add transform controls to it as they show in the center of the scene and not above the object itself.
I’ve tried to set the position after mesh rotation but it would just move mesh away from the clicked place.
This is the function taking care of it:
function makeWalls(){
control.detach();
if(controlPoints.length > 1){
shape = new THREE.Shape();
shape.moveTo(controlPoints[0].x, -controlPoints[0].z);
for(var i = 1; i < controlPoints.length; i++){
shape.lineTo(controlPoints[i].x, -controlPoints[i].z);
}
shape.lineTo(controlPoints[0].x, -controlPoints[0].z);
var extrudeSettings = {
steps: 1,
depth: 40,
bevelEnabled: false
};
var extrudeGeom = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
console.log(extrudeGeom);
//extrudeGeom.rotateX(-Math.PI / 2);
var wall = new THREE.Mesh( extrudeGeom, [
new THREE.MeshLambertMaterial({color: 0xffffff, transparent: true, opacity: 1}),
new THREE.MeshLambertMaterial({color: 0xcfcfcf, transparent: true, opacity: 1})
] ) ;
wall.rotation.x = Math.PI * - 0.5;
/*wall.centroid = new THREE.Vector3();
for (var i = 0, l = controlPoints.length; i < l; i++) { wall.centroid.add(controlPoints[i].clone()); }
wall.centroid.divideScalar(controlPoints.length);
wall.centroid.z = 0;
var offset = wall.centroid.clone();
console.log(offset);
wall.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(-offset.x, -offset.y, -offset.z));
wall.position.copy(wall.centroid);
*/
allObjects.push(wall);
splineArray= [];
updatePositions();
scene.add(wall);
controlPoints = [];
clickCount = 0;
main_action = "";
//scene.remove(lineDrawMe);
}
else{
console.log('You need to click to create points first. Click on \'Draw Walls\' and make a shape.');
}
in commented lines you will see how problem was resolved while using rotateX function.
When using rotation.x it does not work.