Error with SSR Three.js Objects

Hello, I am switching to server side rendering my Three.js objects to save on excess computation.

I am currently running a a server doing data cron jobs and api’ing them out to the client. After installing Three.js on the node server, I ported over the old client side rendering code for my data objects. Then grouped that with some other data types, I send it to the front end.

However, now my problem is that when I pull the objects into the client I get an error that it is not a ThreeJs Object! I check and see that in the logger it differs from the objects created locally on the client. I don’t understand because the code I put on the server worked on the client previously. Perhaps I am missing something?

Here is the code running on the server.

// Cron every minute
cron.schedule(‘*/1 * * * *’, () => {

// Loop over API urls, make AJAX reqs, store in global
for (let i = 0; i < urlSuffix.length; i++) {
request(url+urlSuffix[i], function (error, response, body) {

  	if (response.statusCode == 200) {
  		const parsedBody = JSON.parse(body),
  			  quakeArr = new Array(parsedBody.metadata.count),
  			  geom = new THREE.Geometry(),
        		  cubeMat = new THREE.MeshBasicMaterial({ vertexColors: true });

  		// Data Parsing
  	    for (const feature of parsedBody.features) {
  	    		  // Quake Obj
  	        const magnitude = feature.properties.mag,
  	              location = feature.properties.place,
  	              time = feature.properties.time,
  	              coordinates = feature.geometry.coordinates,
  	              quake = {
  	                  "magnitude": magnitude,
  	                  "location": location,
  	                  "time": time,
  	                  "coordinates": coordinates
  	              },
  	              // Three Data Obj
  	              quakeVector = longLatToSphere(coordinates[0], coordinates[1], 600),
  	              rgb = colorData(magnitude),
  	              cubeColor = new THREE.Color(rgb),
  	              cubeHeight = magnitude > 0 ? (magnitude ** 3) * 1.75 : 0,
  	              cubeGeom = new THREE.BoxGeometry(3, 3, cubeHeight, 1, 1, 1),
              		  cube = new THREE.Mesh(cubeGeom, cubeMat);

              	// set position of cube on globe, point to center, merge together for one draw call
              for ( let j = 0; j < cubeGeom.faces.length; j ++ ) {
                  cubeGeom.faces[j].color = cubeColor;
              }

              cube.position.set(quakeVector.x, quakeVector.y, quakeVector.z);
              cube.lookAt(new THREE.Vector3(0,0,0));
              cube.updateMatrix();
              geom.merge(cube.geometry, cube.matrix);

  	        quakeArr.push(quake);
  	    }

  	    // Data Sorting by Highest Magnitude
  	    quakeArr.sort((a, b) => b.magnitude - a.magnitude);
  	    // Store in Global
  		quakes[i] = quakeArr;
  		threeData[i] = geom;
  	} else {
  		console.log(response);
  		console.log("error: "+response);
  		console.log(this);
  	}

  })

}

})

It is stored in a larger object as a value and then sent to the client. I then store those into Redux and then the visualization takes them from Redux and into a scene loader array, iterates and render.

Here is the schema/keys of the objects I get back from the server

{
data: {vertices: Array(504), normals: Array(426), colors: Array(18), uvs: Array(1), faces: Array(3276)}
metadata: {version: 4.5, type: “Geometry”, generator: “Geometry.toJSON”}
type: “Geometry”
uuid: “9A6BB7E5-B134-497A-ABD7-302D42E1FAD4”
}

1 Like

Most likely it will be sent to the client as json, therefore as a serialized object. So you’d need to convert it back to a threejs object with the object loader:
https://threejs.org/docs/#api/en/loaders/ObjectLoader

1 Like

Hmm so I went to implement the object loader but it looks like it requires a file to work from. Is there a way to load JSON from a variable in memory without editing the standard loader file?

At the bottom of the example in the linked page, there is a way to work with a json object directly:

// Alternatively, to parse a previously loaded JSON structure
var object = loader.parse( a_json_object );
scene.add( object );

Does that help you?

1 Like

This doesn’t seem to work for me. When I try to parse it straight with the loader then I get an error. When i try to parse it after I JSON.parse it then I also get an error.

loader.parse without JSON.parse before

obj loader parsed

OBJLoader.js:414 Uncaught Error: THREE.OBJLoader: Unexpected line: "{"metadata":{"version":4.5,"type":"Geometry","generator":"Geometry.toJSON"},"uuid":"45BA4531-AF9C-46F9-AB7F-551D404284C2","type":"Geometry","data":{"vertices":[-253.72886863413882,377.0505462864755,391.12638200637485,-254.06594580633598,377.55138696472005,391.6496707449355,-254.74729809664012,374.7132161229377,392.70742252974094,-255.08437526883728,375.21405680118227,393.2307112683016,-251.54389622397878,377.55138696472005,393.27425250776923,-251.20681905178162,377.0505462864755,392.75096376920857,-252.56232568648008,375.21405680118227,394.8552930311353,-252.22524851428292,374.7132161229377,394.33200429257465,-253.8250244342498,377.13808325572404,390.90513403710816,-254.20323077661746,377.6999508207422,391.491718090364,-254.84445493817964,374.80122910134276,392.4862330564008,-255.2226612805473,375.3630966663609,393.0728171096566,-251.68188081229232,377.6999508207422,393.11738544791217,-251.30367446992466,377.13808325572404,392.53080139465635,-252.70131131622216,375.3630966663609,394.6984844672048,-252.3231049738545,374.80122910134276,394.111900413949,-221.3862957497906,345.3972769280417,415.4910366456231,-233.54445011061603,364.377610457773,438.4853246825743,-222.21288626910749,342.97388330510375,417.0543381078111,-234.3710406299329,361.95421683483505,440.04862614476224,-230.8923573799623,364.377610457773,439.8876100130309,-218.73420301913688,345.3972769280417,416.89332197607973,-231.7189478992792,361.95421683483505,441.45091147521885,-219.56079353845377,342.97388330510375,418.4566234382677,-222.96474213086472,349.4926766949184,421.476297299868,-229.58879461137113,359.882481099213,434.09379444402003,-223.78626616322134,347.0685528382245,423.0411365221743,-230.41031864372775,357.4583572425191,435.6586336663263,-226.93258933459418,359.882481099213,435.4882741059808,-220.30853685408778,349.4926766949184,422.8707769618288,-227.7541133669508,357.4583572425191,437.0531133282871,-221.1300608864444,347.0685528382245,424.4356161841351,-508.0926340969651,198.34483889923865,232.44258945642605,-520.2668771249058,203.06504218804963,238.05164328718965,-508.9976484541264,195.51515934257083,232.85955782442076,-521.171891482067,200.2353626313818,238.46861165518436,-519.011516121845,203.06504218804963,240.776357722134,-506.83727309390434,198.34483889923865,235.1673038913704,-519.9165304790063,200.2353626313818,241.19332609012872,-507.74228745106564,195.51515934257083,235.58427225936512,-221.4200975562854,338.29613197017017,441.74333473786027,-222.27757937876774,339.60715741081844,443.467093199403,-222.17215712397874,335.8164609539931,443.2551674197971,-223.02963894646106,337.1274863946414,444.97892588133976,-219.59156253097623,339.60715741081844,444.8032494377899,-218.7340807084939,338.29613197017017,443.0794909762472,-220.34362209866956,337.1274863946414,446.3150821197267,-219.48614027618723,335.8164609539931,444.59132365818397,-224.15292986473023,352.9993803329295,428.8786788979242,-224.9079096223807,354.18915916095256,430.3341771788696,-224.964197700737,350.57127313148254,430.4426929376765,-225.71917745838746,351.7610519595056,431.89819121862195,-222.24485312586617,354.18915916095256,431.7155280019308,-221.4898733682157,352.9993803329295,430.26002972098536,-223.05612096187295,351.7610519595056,433.2795420416831,-222.30114120422246,350.57127313148254,431.8240437607377,-224.38162476494833,349.30876993112486,429.84338367676486,-226.17716816266017,352.1058392146166,433.3091637866271,-225.1854623934278,346.8702349614538,431.39496102537873,-226.98100579113964,349.6673042449455,434.86074113524097,-223.51342488570836,352.1058392146166,434.6891897843873,-221.71788148799652,349.30876993112486,431.22340967452504,-224.31726251418783,349.6673042449455,436.24076713300116,-222.521719116476,346.8702349614538,432.7749870231389,-224.97524688785103,350.20287295629214,430.96460755099775,-225.61020227150223,351.191909509324,432.19013647574684,-225.77909777899663,347.7643024044278,432.51612210406665,-226.41405316264783,348.7533389574596,433.74165102881574,-222.946491109132,351.191909509324,433.5702244597045,-222.3115357254808,350.20287295629214,432.3446955349554,-223.7503420002776,348.7533389574596,435.1217390127734,-223.1153866166264,347.7643024044278,433.8962100880243,-224.79354796410018,348.7089752581558,430.7798241992161,-226.23265026104085,350.9428072936244,433.55850752435856,-225.5951475744988,346.26728259404086,432.3275887157418,-227.03424987143944,348.50111462950946,435.1062720408843,-223.56872234782014,350.9428072936244,434.93817707652084,-222.1296200508795,348.7089752581558,432.15949375137836,-224.37032195821877,348.50111462950946,436.48594159304656,-222.9312196612781,346.26728259404086,433.7072582679041,-222.91276276047807,533.2446067646291,132.7161992935562,-228.18440063724387,545.8182825185447,135.89621692426851,-225.2198610504757,531.925353531371,134.10791348362113,-230.4914989272415,544.4990292852866,137.28793111433345,-226.6348141008771,545.8182825185447,138.46502629172085,-221.36317622

OBJLoader.js:414) at XMLHttpRequest.t.onreadystatechange (index.js:101)

loader.parse after JSON.parse

{metadata: {…}, uuid: "1E8F2F88-305C-433C-A725-D0DBB1BF8784", type: "Geometry", data: {…}}data: {vertices: Array(768), normals: Array(618), colors: Array(23), uvs: Array(1), faces: Array(4992)}colors: (23) [1893120, 1762560, 2350080, 1697280, 2219520, 3394560, 2154240, 2611200, 1175040, 2872320, 2023680, 2480640, 1566720, 1109760, 1827840, 5614080, 1305600, 3198720, 2676480, 2545920, 2937600, 1240320, 2807040]faces: (4992) [122, 0, 2, 1, 0, 0, 1, 2, 0, 0, 0, 0, 0, 122, 2, 3, 1, 0, 1, 3, 2, 0, 0, 0, 0, 0, 122, 4, 6, 5, 1, 0, 1, 2, 1, 1, 1, 1, 0, 122, 6, 7, 5, 1, 1, 3, 2, 1, 1, 1, 1, 0, 122, 4, 5, 1, 2, 0, 1, 2, 2, 2, 2, 2, 0, 122, 5, 0, 1, 2, 1, 3, 2, 2, 2, 2, 2, 0, 122, 7, 6, 2, 3, 0, 1, 2, 3, 3, 3, 3, 0, 122, 6, 3, 2, 3, 1, 3, 2, 3, …]normals: (618) [-0.895338949263834, 2.775557561562892e-17, -0.44538541279563026, 0.895338949263834, -2.775557561562892e-17, 0.44538541279563026, 0.2506865225644415, 0.8265570053923591, -0.5039442273122726, -0.2506865225644415, -0.8265570053923591, 0.5039442273122726, 0.36813643304579585, -0.5628530153040097, -0.7400486807146559, -0.36813643304579585, 0.5628530153040097, 0.7400486807146559, -0.8876854988381758, -2.7755575615628914e-17, -0.4604502743537236, -0.8876854988381757, -2.7755575615628914e-17, -0.46045027435372354, 0.8876854988381758, 2.7755575615628914e-17, 0.4604502743537236, 0.8876854988381757, 2.7755575615628914e-17, 0.46045027435372354, 0.2704226120022505, 0.8093690671489893, -0.5213380132507656, -0.2704226120022505, -0.8093690671489893, 0.5213380132507656, 0.3726742090221693, -0.587300360243696, -0.7184651841163394, -0.3726742090221693, 0.587300360243696, 0.7184651841163394, -0.887914425650601, -2.775557561562892e-17, -0.46000866592007095, 0.887914425650601, 2.775557561562892e-17, 0.46000866592007095, 0.267945876159819, 0.8128449898903648, -0.5171924495379646, -0.267945876159819, -0.8128449898903648, 0.5171924495379646, 0.3739157393992803, -0.5824800618133918, -0.7217367923414718, -0.3739157393992803, 0.5824800618133918, 0.7217367923414718, -0.8879037207900708, 0, -0.4600293279858885, 0.8879037207900708, 0, 0.4600293279858885, 0.26795029704853784, 0.8128568506214409, -0.5171715176896278, -0.26795029704853784, -0.8128568506214409, 0.5171715176896278, 0.37393799074010714, -0.5824635099281265, -0.721738622136476, -0.37393799074010714, 0.5824635099281265, 0.721738622136476, -0.8879759710735624, 2.7755575615628914e-17, -0.45988985072076133, 0.8879759710735624, -2.7755575615628914e-17, 0.45988985072076133, 0.26719987013287244, 0.8138975547049798, -0.515921505508571, -0.26719987013287244, -0.8138975547049798, 0.515921505508571, 0.3743032249352658, -0.5810084082397211, -0.7227214714935523, -0.3743032249352658, 0.5810084082397211, 0.7227214714935523, -0.5165288454555907, 1.9428902930940235e-16, -0.8562697891507762, 0.5165288454555907, …]uvs: [Array(8)]vertices: (768) [-221.4200975562854, 338.29613197017017, 441.74333473786027, -222.27757937876774, 339.60715741081844, 443.467093199403, -222.17215712397874, 335.8164609539931, 443.2551674197971, -223.02963894646106, 337.1274863946414, 444.97892588133976, -219.59156253097623, 339.60715741081844, 444.8032494377899, -218.7340807084939, 338.29613197017017, 443.0794909762472, -220.34362209866956, 337.1274863946414, 446.3150821197267, -219.48614027618723, 335.8164609539931, 444.59132365818397, -224.15292986473023, 352.9993803329295, 428.8786788979242, -224.9079096223807, 354.18915916095256, 430.3341771788696, -224.964197700737, 350.57127313148254, 430.4426929376765, -225.71917745838746, 351.7610519595056, 431.89819121862195, -222.24485312586617, 354.18915916095256, 431.7155280019308, -221.4898733682157, 352.9993803329295, 430.26002972098536, -223.05612096187295, 351.7610519595056, 433.2795420416831, -222.30114120422246, 350.57127313148254, 431.8240437607377, -224.38162476494833, 349.30876993112486, 429.84338367676486, -226.17716816266017, 352.1058392146166, 433.3091637866271, -225.1854623934278, 346.8702349614538, 431.39496102537873, -226.98100579113964, 349.6673042449455, 434.86074113524097, -223.51342488570836, 352.1058392146166, 434.6891897843873, -221.71788148799652, 349.30876993112486, 431.22340967452504, -224.31726251418783, 349.6673042449455, 436.24076713300116, -222.521719116476, 346.8702349614538, 432.7749870231389, -224.97524688785103, 350.20287295629214, 430.96460755099775, -225.61020227150223, 351.191909509324, 432.19013647574684, -225.77909777899663, 347.7643024044278, 432.51612210406665, -226.41405316264783, 348.7533389574596, 433.74165102881574, -222.946491109132, 351.191909509324, 433.5702244597045, -222.3115357254808, 350.20287295629214, 432.3446955349554, -223.7503420002776, 348.7533389574596, 435.1217390127734, -223.1153866166264, 347.7643024044278, 433.8962100880243, -224.79354796410018, 348.7089752581558, 430.7798241992161, -226.23265026104085, …]__proto__: Objectmetadata: {version: 4.5, type: "Geometry", generator: "Geometry.toJSON"}type: "Geometry"uuid: "1E8F2F88-305C-433C-A725-D0DBB1BF8784"__proto__: Object

index.js:99 obj loader parsed

OBJLoader.js:276 Uncaught TypeError: o.indexOf is not a function
    at o.parse (OBJLoader.js:276)
    at XMLHttpRequest.t.onreadystatechange (index.js:101)

I’d really need to see the code to help properly, as this seems really dependent on how your server and client communicate. But as a guess, it seems that your server is not serializing the object using threejs .toJSON(). It may simply consider them as a default object, and therefore miss threejs properties.

Are you able to share your code on a git repo or jsfiddle?

I was unaware JSON.stringify and JSON.parse removed properties.

Here is the gist for the CRON Three server. https://gist.github.com/mpaccione/19b7f13ec8600bc37d2927b3ac696d84

The data gets sent together with some standard JSON as one big JSON object and sent to the front end. The data then gets passed to redux state and rendered.

1 Like

Ah, you’re displaying earthquakes? I love this type of projects, so useful.

So what I’d try is on line 61 and 71 to send threeData.toJSON() instead of just threeData. Let me know if that fixes the loader on the client side.

And as a side note, I was confused about line 122 geom.merge(cube.geometry, cube.matrix) and wasn’t sure what that was doing. So checking the docs, I see .merge ( bufferGeometry : BufferGeometry, offset : Integer ), showing the second param should be an integer, not a matrix. Maybe you’re doing the right thing, I can’t tell, but that could be something to pay attention to.

Also, it’s not that JSON.stringify and JSON.parse remove properties if the object is standard, but it will loose the fact that it’s a threejs object, as well as methods attached to it. So using .toJSON() and then the ObjectLoader is necessary. (And there are probably other reasons I’m not aware of)

xhr.onreadystatechange = function(){
            if (this.readyState == 4 && this.status == 200) {
                // const res = JSON.parse(this.responseText);
                const res = new OBJLoader().parse(this.responseText);

and the resulting error…

Uncaught Error: THREE.OBJLoader: Unexpected line: "{"0":{"metadata":{"version":4.5,"type":"Geometry","generator":"Geometry.toJSON"},"uuid":"9D04F728-DCB9-44B1-A089-045C1CAB9D2E","type":"Geometry","data":{"vertices":[...

EDIT

Okay, I seemed to have missed an important part when I copy pasted the code from clientside to serverside.

That was combining the geometry and the mesh. A pretty big oversight from just missing a line or two on a copypasta job.

// create a new mesh, containing all the other meshes.
const combined = new THREE.Mesh(geom, cubeMat);
combined.name = `data${i}`;

Even after fixing this I can’t get it to go. I’m rather close to giving up at this point and just scrapping the whole project since the performance on clientside rendering has too many calculations to be feasible for phones.

Don’t give up so quickly. Coding always gets hard at some point, but it’s really rewarding when you go past that.
And as a last resort, you should really share the full repo, so it would be easier to help. At the moment, I’m doing a lot of guessing.

But I assume you have tried this?

xhr.onreadystatechange = function(){
    if (this.readyState == 4 && this.status == 200) {
        const jsonResponse = JSON.parse(this.responseText);
        const loader = new THREE.OBJLoader();
        const object = loader.parse(jsonResponse);
    }

You may have, since the line is commented in your snippet, but just making sure. And it may depend if your xhr gives you the response in a string or as a json object. I never use xhr directly, so can’t tell (I use the library axios, which makes requests more intuitive)

I am using XHR because fetch doesn’t have a progress API afaik, which is why everyone uses indeterminate loading bars. Axios is built on fetch so I can’t use it.

This is the error below I receive when double parsing like you suggested. It truly shouldn’t be this hard to have basic three js 3D Objects made on server side and sent to the client. I’ve been held up re-releasing this app to the play store for almost a month because of this.

What is your github I will invite you and chip you some $ if you get it going? At this rate though I think it’s a lost cause. I think I might be better off just fcking server side rendering off altogether and running it in a web worker so the apk doesn’t hang when parsing the dataset.

react-dom.production.min.js:4408 TypeError: r.indexOf is not a function
    at r.parse (OBJLoader.js:276)
    at t.e.addData (index.js:449)
    at t.value (index.js:180)
    at Ga (react-dom.production.min.js:4978)
    at Va (react-dom.production.min.js:5123)
    at react-dom.production.min.js:5975
    at Object.t.unstable_runWithPriority (scheduler.production.min.js:274)
    at Os (react-dom.production.min.js:5974)
    at Ps (react-dom.production.min.js:5958)
    at As (react-dom.production.min.js:5925)
ha @ react-dom.production.min.js:4408
n.callback @ react-dom.production.min.js:4773
ia @ react-dom.production.min.js:4271
ra @ react-dom.production.min.js:4257
Ga @ react-dom.production.min.js:4999
Va @ react-dom.production.min.js:5123
(anonymous) @ react-dom.production.min.js:5975
t.unstable_runWithPriority @ scheduler.production.min.js:274
Os @ react-dom.production.min.js:5974
Ps @ react-dom.production.min.js:5958
As @ react-dom.production.min.js:5925
Es @ react-dom.production.min.js:5860
(anonymous) @ react-dom.production.min.js:5761
Promise.then (async)
(anonymous) @ react-dom.production.min.js:4747
ba @ react-dom.production.min.js:4745
ja @ react-dom.production.min.js:4910
Va @ react-dom.production.min.js:5104
(anonymous) @ react-dom.production.min.js:5975
t.unstable_runWithPriority @ scheduler.production.min.js:274
Os @ react-dom.production.min.js:5974
Ps @ react-dom.production.min.js:5958
As @ react-dom.production.min.js:5925
Es @ react-dom.production.min.js:5860
Ka @ react-dom.production.min.js:5787
enqueueSetState @ react-dom.production.min.js:2790
x.setState @ react.production.min.js:72
u.onStateChange @ connectAdvanced.js:205
v @ redux.js:213
e @ VM17:1
(anonymous) @ middleware.js:40
dispatch @ VM17:1
onLocationChanged @ ConnectedRouter.js:193
a @ ConnectedRouter.js:129
r @ history.js:162
(anonymous) @ history.js:180
notifyListeners @ history.js:179
A @ history.js:300
(anonymous) @ history.js:381
confirmTransitionTo @ history.js:152
push @ history.js:362
(anonymous) @ index.js:50
setTimeout (async)
value @ index.js:50
Ga @ react-dom.production.min.js:4980
Va @ react-dom.production.min.js:5123
(anonymous) @ react-dom.production.min.js:5975
t.unstable_runWithPriority @ scheduler.production.min.js:274
Os @ react-dom.production.min.js:5974
Ps @ react-dom.production.min.js:5958
As @ react-dom.production.min.js:5925
Es @ react-dom.production.min.js:5860
Ka @ react-dom.production.min.js:5787
enqueueSetState @ react-dom.production.min.js:2790
x.setState @ react.production.min.js:72
u.onStateChange @ connectAdvanced.js:205
notify @ Subscription.js:23
t.notifyNestedSubs @ Subscription.js:62
u.onStateChange @ connectAdvanced.js:202
notify @ Subscription.js:23
t.notifyNestedSubs @ Subscription.js:62
u.onStateChange @ connectAdvanced.js:202
v @ redux.js:213
e @ VM17:1
(anonymous) @ middleware.js:40
dispatch @ VM17:1
setProgressComplete @ index.js:21
t.onprogress @ index.js:87
XMLHttpRequest.send (async)
value @ index.js:146
onClick @ index.js:175
(anonymous) @ react-dom.production.min.js:49
h @ react-dom.production.min.js:69
(anonymous) @ react-dom.production.min.js:73
S @ react-dom.production.min.js:140
A @ react-dom.production.min.js:169
T @ react-dom.production.min.js:158
O @ react-dom.production.min.js:232
En @ react-dom.production.min.js:1718
Is @ react-dom.production.min.js:5990
Ne @ react-dom.production.min.js:660
An @ react-dom.production.min.js:1760
(anonymous) @ react-dom.production.min.js:6017
t.unstable_runWithPriority @ scheduler.production.min.js:274
Ds @ react-dom.production.min.js:6016
Rn @ react-dom.production.min.js:1737
OBJLoader.js:276 Uncaught (in promise) TypeError: r.indexOf is not a function
    at r.parse (OBJLoader.js:276)
    at t.e.addData (index.js:449)
    at t.value (index.js:180)
    at Ga (react-dom.production.min.js:4978)
    at Va (react-dom.production.min.js:5123)
    at react-dom.production.min.js:5975
    at Object.t.unstable_runWithPriority (scheduler.production.min.js:274)
    at Os (react-dom.production.min.js:5974)
    at Ps (react-dom.production.min.js:5958)
    at As (react-dom.production.min.js:5925)

Cool, let’s try. My github https://github.com/polygonjs.

https://github.com/mpaccione/usgs_viz/invitations

Okay here is how this works…

A Node CRON server pulls USGS geoJSON data sets. It then parses and organizes the data. It stores the data in local memory and sends a combined JSON response to the client. [0] is the earthquake data and [1] are the rendered Three.js objects. (index.js)

A loader then makes an XHR request in the client and parses the response and stores it in Redux state. (client/components/splashLoader/index.js)

Another component begins rendering the Three js scene (client/components/viz/index.js) Look specifically to the addData function. If you search for it you will see what I am doing. I am then trying your 2nd stage OBJLoader parsing in that function. Please note I have written my own loader check that ensures it will not try to render all the 3D objects and textures until all the data is present.

Process:

In the main repo you will need to:

  1. node index.js (This will create a CRON server polling USGS Datasets)
  2. Open new shell and cd into client directory
  3. npm run start

You need to have the CRON server running over a minute before trying the client okay

There may be some service worker errors but they are non blocking and will be addressed after I get SSR working. Not sure whether I’m going to go with IndexedDB or finangle just a SW for caching the SSR objects.

ahh, it should be THREE.ObjectLoader, not OBJLoader. The former is to load threejs objects converted with .toJSON() and the latter to load .obj files. But I agree the names are confusing. I was paying attention to that in that thread, but still missed it.

After changing that, I now get the error
THREE.ObjectLoader: You have to import LegacyJSONLoader in order load geometry data of type "Geometry".
so I assume you’ll be able to carry on with this? Personally, I’d switch to BufferGeometry instead of Geometry to be sure it’s future proof.

So yes, it’s just another case of a simple typo bringing some insanity.

And thank you very much for offering $, but it only took a few minutes. How about instead you have a look at my startup https://polygonjs.com/ and tell me if that seems interesting to you for future 3D web projects? It’s designed to simplify the creation of interactive scenes (and therefore to preserve your peace of mind). I’m always happy to hear positive or negative feedback.

1 Like

Holy shit, naming -.-" .

Yeah, I’ve used the OBJLoader in the past to load in 3D Models so I was confused. I will definitely take another crack at it. I’m dying to get this app bundled and shipped out so I can move on, wrap up my other projects, and snag an engineering job here in SF.

As far as your 3D Startup Venture, I find the GLSL Shader Generator and the MapBox Integration the most interesting. I know you are still developing this so there’s not much I can say to help except one thing. If people are anything like me they suck at GUI’s. I might be out of the bell curve on that as a programmer but for a lot of interfaces it’s easier for me to read the code to get the understanding. When you go to market… screencap tutorials so people know where things are and how they work. You have some already in the docs section… it may be better for marketing to feature videos moreso than the demo or to actually make a guided interactive tutorial demo. I think that would go far because I tried the demo right quick and was like mmm okay what do I do a bit.

I wish you good luck in developing your own software, that is most exciting my friend :slight_smile:

Excellent, thank you very much for taking a look and giving this feedback, it’s worth gold.

And yes, I agree that the current demo could do with more hand holding. Or just be a video, as you say. I also agree that GUIs and code are approached differently, so I have a few things planned to help on that part.

Yeah a video would probably be the best bet during early development. Then a more finished interactive as you’ve neared a full completed release.

In regards to the JSONLoader… I pulled in the old deprecated one. It appears the format it expects is different than the format mine is in.

Same issue as this essentially: https://stackoverflow.com/questions/34929928/threejs-jsonloader-cannot-read-property-length-of-undefined

I edited the loader to see if I could make it work. I changed this part of the parsing function from…

faces = json.faces,
vertices = json.vertices,
normals = json.normals,
colors = json.colors,

to

faces = json.geometries["0"].data.faces,
vertices = json.geometries["0"].data.vertices,
normals = json.geometries["0"].data.normals,
colors = json.geometries["0"].data.colors,

thought I might get lucky and have it run through but I get an error at:

Uncaught TypeError: Cannot read property 'x' of undefined
    at Vector3.subVectors (0.chunk.js:67937)

How do people send their JSON objects normally from the backend to the front end? I feel like I’m doing it in a non standard way since it’s been like pushing a square peg through a round hole.

ok, I think I’ve got it working. At least I see the globe with some spikes on it.

And it doesn’t look like I have write permission to your repo, so here is what I’ve done:

In short, I’ve replaced Geometry by BufferGeometry. I think that’s really the way to go with Geometry being deprecated in the near future. Using anything with the word Legacy in the name makes me run away.

  • commented out geom = new THREE.Geometry(),

  • add a list of cubes before the for loop const cubes = []

  • made the cube a BufferBoxGeometry: cubeGeom = new THREE.BoxBufferGeometry(3, 3, cubeHeight, 1, 1, 1);

  • convert to a BufferGeometry (as opposed to a BufferBox): cubeGeom = cubeGeom.clone() (so needs to be declared with let, not const). I think that’s step is required because of the merging below that works better for BufferGeometry, but not BufferBox (or any non raw BufferGeometry)

  • I’ve had to comment out the for loop setting faces color: for ( let j = 0; j < cubeGeom.faces.length; j ++ ) {, as you’ll have to do it on the geometry.attributes['color'] attribute directly

  • replace .merge(cube.geometry, cube.matrix); with cube.geometry.applyMatrix(cube.matrix)

  • add to the cubes list: cubes.push(cube)

  • and after the for loop, merge all cubes together: geom = BufferGeometryUtils.mergeBufferGeometries(cubes.map(c=>c.geometry))
    (For that you need to include BufferGeometryUtils from https://github.com/mrdoob/three.js/blob/d1f94e361abe6b8bff27fb90c0b947d6e24b86bf/examples/jsm/utils/BufferGeometryUtils.js - I’m not familiar with require, so in my test I’ve copied it into index.js after removing the import statements)

  • profit!

Also, I haven’t heard of anyone doing SSR for threejs, but using workers seems more frequent. I suspect using toJSON and ObjectLoader is the main way to go, but if there is another one, I’m certainly curious to hear suggestions too.

Okay, so I’ve gone an promised the USGS that I’ll be sending them a link to the app. I had to email them about their earthquake data feeds api’s breaking with 400 codes. They changed the entire public limit because of my complaints xD

I really have to deliver this now haha. I can’t seem to get it to go. I read through your writeup and seemingly did eveyrthing. Although I don’t quite understand your bit about cloning the cubeGeom? A new one get’s made for every quake anyways because the cubeHeight changes.

The objects appear to be sent to the front end as BufferGeometry. I copy pasted in the BufferUtility Code. Yes, you are right in that Three doesn’t do well with requires. I think in the docs some may work with requires but in my experience this has been bogus as browserify and babel don’t seem to work well with them. Webpack imports have always worked for me. No webpack on this server though. So I edited the loader by adding THREE in front of the instances it creates.

Anyways, in using the ObjectLoader to parse them I receive the error: (Please note, I have imported the LegacyJSONLoader on the clientside)

TypeError: Cannot read property 'type' of undefined
    ObjectLoader.parseObject

C:/wamp64/www/usgs_viz/react/client/node_modules/three/build/three.module.js:26841

26838 |   return materials[name];  
26839 | }  
26840 | > 
26841 | switch (data.type) {        | ^ 
26842 |   case 'Scene':  
26843 |     object = new Scene();  
26844 | 

Here is the updated CRON server code.

// Cron every minute
cron.schedule('*/1 * * * *', () => {

	console.log("CRON JOB - "+new Date().getMinutes());

	// Loop over API urls, make AJAX reqs, store in global
	for (let i = 0; i < urlSuffix.length; i++) {
		request(url+urlSuffix[i], function (error, response, body) {	

			if (response.statusCode == 200) {
				const parsedBody = JSON.parse(body),
					  quakeArr   = new Array(parsedBody.metadata.count),
					  // geom       = new THREE.Geometry(),
            		  cubes 	 = [],
            		  cubeMat    = new THREE.MeshBasicMaterial({ vertexColors: true });

				// Data Parsing
			    for (const feature of parsedBody.features) {
			    		  // Quake Obj
			        const magnitude   = feature.properties.mag,
			              location    = feature.properties.place,
			              time        = feature.properties.time,
			              coordinates = feature.geometry.coordinates,
			              quake       = {
			                               "magnitude":   magnitude,
			                               "location":    location,
			                               "time":        time,
			                               "coordinates": coordinates
			                            },
			              // Three Data Obj
			              quakeVector = longLatToSphere(coordinates[0], coordinates[1], 600),
			              rgb         = colorData(magnitude),
			              cubeColor   = new THREE.Color(rgb),
			              cubeHeight  = magnitude > 0 ? (magnitude ** 3) * 1.75 : 0;
			        	  cubeGeom    = new THREE.BoxBufferGeometry(3, 3, cubeHeight, 1, 1, 1),
			        	  // cubeGeom 	  = cubeGeom.clone()
                  		  cube        = new THREE.Mesh(cubeGeom, cubeMat);

                  	// set position of cube on globe, point to center, merge together for one draw call
		            // for ( let j = 0; j < cubeGeom.faces.length; j ++ ) {
		            //     cubeGeom.faces[j].color = cubeColor;
		            // }

		            cube.position.set(quakeVector.x, quakeVector.y, quakeVector.z);
		            cube.lookAt(new THREE.Vector3(0,0,0));
		            cube.updateMatrix();
		            cube.geometry.applyMatrix(cube.matrix);

			        quakeArr.push(quake);
			        cubes.push(cube);
			    }

			    // create a new mesh, containing all the other meshes.
		        // const combined = new THREE.Mesh(geom, cubeMat);
		        const combined = BufferGeometryUtils.mergeBufferGeometries(cubes.map(c=>c.geometry));
		        combined.name = `data${i}`;

			    // Data Sorting by Highest Magnitude
			    quakeArr.sort((a, b) => b.magnitude - a.magnitude);
			    // Store in Global
				quakes[i] = quakeArr;
				threeData[i] = combined.toJSON();

			} else {
				console.log(response);
				console.log("error: "+response);
				console.log(this);
			}

		})
	}

})

I think the use case for SSR Three.js is pretty damn big. Sure, you can throw code on a web worker but if you need some CPU intensive 3D tasks it’s gonna suck on phones and tablets. Overall this has been like pulling teeth of a process and I’m not even there yet!

I hope Mr. Doob, West Langley, and Mugen consider SSR for Three.js more so in the future.

After checking the ObjectLoader code, that error seems to mean that one element in the json.geometries array is null. So there’s something fishy in the json that’s generated.

It may also be easier if I could do a pull request to your repo, but in the mean time, I’ve just sent you a DM with a link to a zip containing the repo with the changes I made. Hopefully that’s only the ones I explained. I’ve also added a screenshot of the final result I see (For some reason I still have to click ‘Enter Vizualizer’ about 10x though before seeing the globe, as if the router rejects the path change. Not sure about that part)

1 Like