Issue with filepath to load geometry file inside a function node

Dear team,

Currently trying to visualize a three.js sketch inside a dashboard. This works well thanks to the example : https://flows.nodered.org/flow/079151b53f63261911684acc015f9166

It works fluently and manage to bring in own Three js code with success. However when loading geometry that refers to a local filepath i get stuck. in the normal sketch (not embeded in node-red dashboard) it works. the result:

in node-red however the result:

the issue might be that the reference to the file is not correct, I have tried quite some options but none seems correct. Some help to steer me in the correct direction would really help.
The part of the code that has the file path ref:


  
  //Load meshes here
  const loader = new THREE.GLTFLoader();
  //loader.load('../buildings2.gltf', function(object){
 // loader.load('C:/Users/Admin/.node-red/meetpaal.gltf', function(object){
 //  loader.load(__dirname,'C:/Users/Admin/.node-red/lib/meetpaal.gltf', function(object){ 
  

Should i use a filenode or something as such, or is there a specific way node-red handles such ?
would be of great help to tackle what i am doing wrong.

i seem not to be able to upload geometry here, though its a gltf format.

the flow is added here:

[{"id":"f5ab3880.cdf7a8","type":"tab","label":"3d_setup","disabled":false,"info":""},{"id":"dd859cd1.bfff3","type":"http in","z":"f5ab3880.cdf7a8","name":"","url":"/cube","method":"get","upload":false,"swaggerDoc":"","x":160,"y":300,"wires":[["f0d248a7.5e40a8"]]},{"id":"7ef00cae.3e89a4","type":"http response","z":"f5ab3880.cdf7a8","name":"","statusCode":"","headers":{},"x":705,"y":300,"wires":[]},{"id":"f0d248a7.5e40a8","type":"template","z":"f5ab3880.cdf7a8","name":"three.js","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"en\" >\n<head>\n  <meta charset=\"UTF-8\">\n  <title>GLTFLoader - complete</title>\n  <link rel=\"stylesheet\" href=\"./style.css\">\n</head>\n<body>\n<!-- partial:index.partial.html -->\n\n<!-- partial -->\n  <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.min.js'></script>\n<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/2666677/OrbitControls.js'></script>\n<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/2666677/GLTFLoader.js'></script>\n<!--<script  src=\"meetpaal.js\"></script> -->\n\n<script>\nvar server = window.location.href.split(\"http://\")[1].split(\"/\")[0]\nconsole.log(\"Page location is \" + server)\nvar scene, camera, renderer, car;\n\n//var solar= mouseX/30;\n\ninit();\n\nfunction init(){\n  const assetPath = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/2666677/';\n  \n  scene = new THREE.Scene();\n  scene.background = new THREE.Color(0x00aaff);\n  \n  camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );\n  camera.position.set(0, 11, -116);\n\n  \n  \n  var pointColor = \"#ff5808\";\n  var directionalLight = new THREE.DirectionalLight();\n  directionalLight.position.set(-1, 35, 20);\n  directionalLight.castShadow = true;\n  directionalLight.shadowCameraNear = 2;\n  directionalLight.shadowCameraFar = 200;\n  directionalLight.shadowCameraLeft = -100;\n  directionalLight.shadowCameraRight = 100;\n  directionalLight.shadowCameraTop = 100;\n  directionalLight.shadowCameraBottom = -100;\n\n  directionalLight.distance = 1000;\n  directionalLight.intensity = 0.8;\n  directionalLight.shadowMapHeight = 1024;\n  directionalLight.shadowMapWidth = 1024;\n\n\n  scene.add(directionalLight);\n\n  \n\n\n \n\n  \n  renderer = new THREE.WebGLRenderer({antialias: true});\n  renderer.setSize( window.innerWidth, window.innerHeight );\n  renderer.shadowMap.enabled = true;\n  renderer.shadowMap.type = THREE.PCFShadowMap;\n  document.body.appendChild( renderer.domElement );\n  \n  const controls = new THREE.OrbitControls( camera, renderer.domElement );\n  controls.target.set(0,0,0);\n  controls.update();\n  \n\n  \n  \n  //Load meshes here\n  const loader = new THREE.GLTFLoader();\n  //loader.load('../buildings2.gltf', function(object){\n // loader.load('../Users/Admin/.node-red/meetpaal.gltf', function(object){\n  //  loader.load('http://127.0.0.1/user/admin/.node-red/lib/flows/meetpaal.gltf', function(object){ \n     //     loader.load(__dirname,'C:/Users/Admin/.node-red/lib/meetpaal.gltf', function(object){ \n        \n       // http://127.0.0.1/home/me/.node-red/public/Pictures/wall.jpg\n loader.load('C:/Users/Admin/OneDrive - Oosterhoff Group/work_projects_abt/QUAKE/urban_planning_webbased/three_js/learn/AirAd/meetpaal/meetpaal.gltf', function(object){\n      \n    //  C:\\Users\\Admin\\OneDrive - Oosterhoff Group\\work_projects_abt\\QUAKE\\urban_planning_webbased\\three_js\\learn\\AirAd\\meetpaal\n   \n\n  object.scene.traverse( function ( child ) {\n\n    if ( child.isMesh ) {\n\n     // console.log(child);\n\t\t // console.log(object);\n     console.log(child);\n      \n      child.material =  new THREE.MeshPhongMaterial({ \n         transparent : true ,\n         opacity : 1,\n         emissive: 0xffffff,\n         emissiveIntensity: 0.2,\n         color: Math.random()* 0xff00ff,\n         roughness: 0.6,\n         metalness: 0.1,\n              shading: THREE.FlatShading\n         });\n      \n         child.castShadow = true;\n         child.receiveShadow = true;\n      child.scale.set(10,10,10);\n      //child.position.x=-90;\n      //child.position.Z=-90;\n\n    }\n  //scene.add(child);\n  } );\n  \n  scene.add( object.scene );\n} );\n\n  var geo = new THREE.PlaneBufferGeometry(2000, 2000, 8, 8);\n  var mat = new THREE.MeshStandardMaterial({\n    emissive: 0x708090,\n    emissiveIntensity: 0.5,\n    side: THREE.DoubleSide,\n    color: 0xD3D3D3,\n    roughness: 0.9,\n    metalness: 0.7,\n    shading: THREE.FlatShading\n  });\n  var plane = new THREE.Mesh(geo, mat);\n  plane.receiveShadow = true;\n  plane.rotation.setFromVector3(new THREE.Vector3( Math.PI/2,Math.PI, 0 ));\n  //plane.translateZ(0.02);\n  \n  scene.add(plane);\n\n\n\n\n  \n  window.addEventListener( 'resize', resize, false);\n  \n  update();\n}\n\nfunction update(){\n  requestAnimationFrame( update );\n  renderer.render( scene, camera );\n  \n  \n}\n\nfunction resize(){\n  camera.aspect = window.innerWidth / window.innerHeight;\n  camera.updateProjectionMatrix();\n  renderer.setSize( window.innerWidth, window.innerHeight );\n}\n\n\n\n\n</script>\n\n\n\n</body>\n</html>","output":"str","x":370,"y":300,"wires":[["48e8c6b9.fccdb8"]]},{"id":"48e8c6b9.fccdb8","type":"change","z":"f5ab3880.cdf7a8","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/html","tot":"str"},{"t":"set","p":"headers.Access-Control-Allow-Origin","pt":"msg","to":"*","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":300,"wires":[["7ef00cae.3e89a4"]]},{"id":"6b29ba6.2352c44","type":"comment","z":"f5ab3880.cdf7a8","name":"Three.js example with websockets","info":"","x":240,"y":240,"wires":[]},{"id":"851809cf.e6f558","type":"ui_template","z":"f5ab3880.cdf7a8","group":"52c0f781.971f08","name":"P5.js display in iFrame","order":0,"width":"25","height":"16","format":"\n<iframe width=\"100%\" height=\"100%\" frameborder=\"0\" scrolling=\"no\"  src=\"http://127.0.0.1:1880/cube\"></iframe>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":820,"y":400,"wires":[[]]},{"id":"52c0f781.971f08","type":"ui_group","name":"3d view","tab":"200f07c6.f6ba98","order":1,"disp":true,"width":"25","collapse":true},{"id":"200f07c6.f6ba98","type":"ui_tab","name":"visual_3d","icon":"dashboard","disabled":false,"hidden":false}]

Have you checked the dashboard to see if there are any errors showing in the console?

1 Like

Hi zenofmud, checked the console, indeed it gives an error that is due to the issue:

Not being a Windows person, is it valid to have a folder name with spaces in it?

You need to provide resources via a http endpoint.

Add this flow this...

image

[{"id":"f31a598d.9fd2c8","type":"function","z":"f5ab3880.cdf7a8","name":"Set base path","func":"//restrict to c:\\temp\\\nvar basePath = \"c:\\\\temp\\\\models\\\\\";\nvar filename = msg.req.params.fn;\n\n\nif(filename.includes(\"..\\\\\")){\n    msg.payload = \"Illegal file path\";\n    msg.statusCode = 405;//not allowed\n    return [null, msg];//fire output 2\n} else if(filename.includes(\"../\")){\n    msg.payload = \"Illegal file path\";\n    msg.statusCode = 405;//not allowed\n    return [null, msg];//fire output 2\n} \n//TODO: add more checks\n\nmsg.filename = basePath + filename;\nreturn [msg, null];//fire output 1\n\n\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1320,"y":280,"wires":[["34dc99e5.495466"],["98261154.3006"]]},{"id":"98261154.3006","type":"http response","z":"f5ab3880.cdf7a8","name":"","statusCode":"","headers":{},"x":1670,"y":320,"wires":[]},{"id":"34dc99e5.495466","type":"file in","z":"f5ab3880.cdf7a8","name":"","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":1510,"y":260,"wires":[["98261154.3006"]]},{"id":"38d65d59.1d8aa2","type":"catch","z":"f5ab3880.cdf7a8","name":"","scope":null,"uncaught":false,"x":1140,"y":360,"wires":[["3b8014a.86ad8ec","5b18a8e7.fb8da8"]]},{"id":"3b8014a.86ad8ec","type":"function","z":"f5ab3880.cdf7a8","name":"Set 404","func":"msg.payload = msg.error;\nmsg.statusCode = 404;//resource not found\nreturn msg;","outputs":1,"noerr":0,"x":1520,"y":360,"wires":[["98261154.3006"]]},{"id":"5b18a8e7.fb8da8","type":"debug","z":"f5ab3880.cdf7a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1170,"y":400,"wires":[]},{"id":"5de7cbb4.fa21a4","type":"comment","z":"f5ab3880.cdf7a8","name":"Create http endpoint <nodered>/models/xxx  where xxx is the file name to download","info":"","x":1370,"y":220,"wires":[]},{"id":"67ecfa7f.3f0e24","type":"http in","z":"f5ab3880.cdf7a8","name":"","url":"/models/:fn","method":"get","upload":false,"swaggerDoc":"","x":1140,"y":280,"wires":[["f31a598d.9fd2c8"]]}]

This then permits you to access files like this...
loader.load('/models/duck/duck.gltf', function(object){
and
http://localhost:1880/models/duck/duck.gltf (assuming you had a duck.gltf file in c:\temp\models\ (this base path is set in the function)

1 Like

Steve-Mcl &zenofmud, it steers in the correct direction, will go through it, and confident this solves my wrongdoing. it helps so much to understand the node-red flow/pipeline in a correct way. Much appreciated taking the time to assist me.

update: worked :slight_smile:

Another common way of providing url access to files from within node-red is to uncomment this section of your settings.js file:

    // When httpAdminRoot is used to move the UI to a different root path, the
    // following property can be used to identify a directory of static content
    // that should be served at http://localhost:1880/.
    httpStatic: 'c:\\temp\\models',

But this only works if (as the note states) you also have given your admin and ui different root paths -- otherwise there could be conflicts between the internal urls and your local directory structure...

You can tell if you have this set up properly by looking at the output in the logs when restarting node-red... on my unix server, it looks like this:

8 Feb 15:55:22 - [info] Settings file  : /home/shr/.node-red/settings.js
8 Feb 15:55:22 - [info] HTTP Static    : /home/shr/.node-red/public
8 Feb 15:55:22 - [info] Context store  : 'default' [module=memory]
8 Feb 15:55:22 - [info] User directory : /home/shr/.node-red
8 Feb 15:55:22 - [warn] No active project : using default flows file
8 Feb 15:55:22 - [info] Flows file     : /home/shr/.node-red/flows_devshr.json
8 Feb 15:55:22 - [info] Server now running at http://127.0.0.1:1880/admin/

HTTP Static is the base folder for serving files to your app using http://localhost:1880/ ... tack on the relative path of your file under the models directory, and it should serve them.

1 Like

Shrickus, sounds as a very elegant way to tackle such a pipeline, am trying it out, much much appreciated

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.