9

I'm trying to offload some Geometry loading and processing into a web worker. To send it back to the main thread, the Geometry instance needs to be serialized, and it seems that Geometry.prototype.toJSON() was meant for exactly this type of thing.

But I can't figure out how to turn that object back into a Geometry instance in the main thread. How is the toJSON() output supposed to be used?

PS: I've seen this related question, but it seems dated. toJSON() wasn't in the API yet. The accepted answer is a bit convoluted, and requires me to still do some raw work in the main thread.

Community
  • 1
  • 1
mhelvens
  • 4,225
  • 4
  • 31
  • 55
  • You should look around the loader classes in the documentation: http://threejs.org/docs/#Reference/Loaders/ObjectLoader – nemesv Feb 28 '15 at 16:28
  • 1
    @nemesv: I've looked, and looked. Those classes cannot be used to turn `toJSON()` output back into a `Geometry`. In theory, I could try to comprehend all of their source-code, and write a full solution from scratch, but that would take a lot of time. Mostly, I can't believe that a serialization method `toJSON()` exists, without a way to deserialize. :-/ – mhelvens Mar 01 '15 at 09:02
  • See if this helps: http://stackoverflow.com/questions/27992147/three-js-include-mesh-data-in-code/27996338#27996338 – WestLangley Mar 01 '15 at 16:55
  • @WestLangley: Indeed, that may be the best I can do. That is, download the JSON file in the worker thread, then turn it into geometry in the main thread. Still, it doesn't use `toJSON()` at all. That's something, I guess, but then I don't understand the point of that method. – mhelvens Mar 01 '15 at 19:19

3 Answers3

6

If I understand correctly the issue is:

  • You have a file which you want to load as a geometry (obj, stl, etc.).
  • You want to load this file in a WebWorker.
  • You then want to send the geometry back to the main script.
  • So you're thinking about sending the file back to the main thread as JSON, since sending objects is not supported.
  • Then you would convert the json to a geometry on the main thread.

The problem with this is that converting from a JSON string to a geometry is another loading operation (which is why there's JSONLoader), so at that point you may as well have just done the loading on the main thread.

The approach I've used is to load the file into flat arrays of vertices and normals, then I send those back to the main thread to add to a BufferGeometry. You can also use transferable objects to gain some more speed.

// worker.js

var vertices = new Float32Array( faces * 3 * 3 );
var normals = new Float32Array( faces * 3 * 3 );   

// Load your file into the arrays somehow.

var message = {
    status:'complete',
    vertices: vertices,
    normals: normals
};

postMessage(message, [message.vertices.buffer, message.normals.buffer]);

// app.js

onmessage = function (event) {

    var vertices = event.data.vertices;
    var normals = event.data.normals;

    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );

    var material = new THREE.MeshPhongMaterial();

    var mesh = new THREE.Mesh( geometry, material );

    // Do something with it.

};
DylanVann
  • 483
  • 5
  • 9
  • I used a worker thread because the geometry still needed to be processed / transformed, which would noticeably stall the main thread. Your solution looks reasonable on first sight, but it's been too long since I was working on the problem. I cannot really judge right now. (In any case, it doesn't really answer the question as posed.) – mhelvens Dec 31 '15 at 20:49
  • The question asked is "Where is geometry.fromJSON()?" but it seems like what you really wanted to know was how to pass geometry from a WebWorker to the main script without blocking/stalling. The technique I explained does that, although instead of loading you were doing processing. Creating the BufferGeometry from the transferred typed arrays is nearly instant. I found this question looking for how to do this. – DylanVann Dec 31 '15 at 22:08
  • Sure, it's a useful answer (I +1'd it). But it only works if vertices and normals are the only pieces of data in a geometry, which isn't necessarily the case (like for animated 3d models). Yeah, we could add that too, but ideally there should be serialization / deserialization facilities that handle all possible models, so we don't have to code and maintain this ourselves. – mhelvens Jan 02 '16 at 11:15
  • I see your point. I've been thinking about it. With the current implementation of WebWorkers we're pretty limited in what we can send back to the main thread. The solution I've come up with that would work for all file types is to load the specific file type in a worker, convert the object to json, then use a non-blocking (using setTimeOut) version of JSONLoader with a callback to convert to a three.js object. We don't have a non-blocking version of JSONLoader at the moment though. – DylanVann Jan 04 '16 at 06:22
5

You can use JSONLoader unserialize geometry like so:

var geometry = new THREE.Geometry();
var serializedGeometry = geometry.toJSON();
var jsonLoader = new THREE.JSONLoader();

var result = jsonLoader.parse(serializedGeometry.data);

var unserializedGeometry = result.geometry;
adam187
  • 3,193
  • 21
  • 15
3

Why don't you just use the JSONLoader?

myloader = new THREE.JSONLoader()
myloader.load("path/to/json", function(geometry,material){
    mesh = new THREE.Mesh(geometry,material)
    scene.add(mesh)
})

or loading a JSON file the same way

Arman
  • 344
  • 3
  • 12
  • I *am* using the JSONLoader. But I'm doing it in a worker thread, and am now looking for a way to communicate the resulting geometry (after modification) back to the main thread. – mhelvens Mar 01 '15 at 08:57