Search code examples
javascriptbrowserify

why is my variable in a nested function with array.forEach use undefined in Javascript


I am sorry if the answer here is obvious, I am mostly a c/c++ guy and javascript is relatively new to me.

I am using THREE, bundling with browserify. In one module, I am creating an array of mesh objects that I need to access in a nested function using forEach(...), but I keep getting an error, that the corresponding object is not defined. I am suspecting that the problem is related to me actually defining the array in a separate callback function that is defined in parallel with the nested function using the forEach(). I have been trying to "declare" the variable globally, before I assign it to the array in the callback function, but that doesn't work. In c++ I would define an uninstantiated object globally (or in a class header) and then instantiate it in some function later, while accessing it in yet another function after that. Below is the relevant code and the variable that keeps coming back undefined is called "mesh". Note that I am not even sure why setPallete(...) and update(...) are implemented as nested function (a strange concept to me)? In another incarnation of this code, I implement them as "standalone" (e.g. not nested) functions and things are working, but in that code I am not using browserify to bundle, because I have just one, monolythic script. So maybe this has soemthing to do with that.

Thank you for your help.

let mesh;   //try global definition -- no dice
 
module.exports = function (app) {

    const totalMeshes = 40;
    const subdivisions = 300;
    const numSides = 8;
    const openEnded = false;
    const geometry = createGeometry(numSides, subdivisions, openEnded);
    const container = new THREE.Object3D();
    const lines = newArray(totalMeshes);
 
    ShaderLoader("scripts/Grp3D/lib/shaders/tube.vert", "scripts/Grp3D/lib/shaders/tube.frag", function (vertex, fragment) {
      const baseMaterial = new THREE.RawShaderMaterial({
            vertexShader: vertex,
            fragmentShader: fragment,
            side: THREE.FrontSide,
            extensions: {
              deriviatives: true
           },
           defines: {
              lengthSegments: subdivisions.toFixed(1),
              ROBUST: false,
              ROBUST_NORMALS: false, 
              FLAT_SHADED: true
           },
           uniforms: {
             thickness: { type: 'f', value: 1 },
             time: { type: 'f', value: 0 },
             color: { type: 'c', value: new THREE.Color('#303030') },
             animateRadius: { type: 'f', value: 0 },
             animateStrength: { type: 'f', value: 0 },
             index: { type: 'f', value: 0 },
             totalMeshes: { type: 'f', value: totalMeshes },
             radialSegments: { type: 'f', value: numSides }
           }
        });
    
        lines.map((_, i) => {
           const t = totalMeshes <= 1 ? 0 : i / (totalMeshes - 1);
           const material = baseMaterial.clone();
           material.uniforms = THREE.UniformsUtils.clone(material.uniforms);
           material.uniforms.index.value = t;
           material.uniforms.thickness.value = randomFloat(0.005, 0.0075);
           mesh = new THREE.Mesh(geometry, material);
           mesh.frustumCulled = false; // to avoid ThreeJS errors
           return mesh;
        });
    
        lines.forEach(tmesh => container.add(tmesh));
    });

    return {
        object3d: container,
        update,
        setPalette
    };
    
    // animate in a new color palette
    function setPalette (palette) {
        tweenr.cancel();
        lines.forEach((mesh, i) => {
           const uniforms = mesh.material.uniforms;
           uniforms.color.value.set(palette);
        
           const delay = i * 0.004;
           uniforms.animateRadius.value = 0;
           uniforms.animateStrength.value = 1;
           tweenr.to(uniforms.animateRadius, { value: 1, duration: 0.5, delay, ease: 'epxoOut' });
           tweenr.to(uniforms.animateStrength, { value: 0, duration: 1, delay, ease: 'expoInOut' });
        });
    }
    
    function update (dt) {
       dt = dt / 1000;
       lines.forEach(mesh => {  //undefined when the code runs
            mesh.material.uniforms.time.value += dt;
       });
    }
};


Solution

  • Few problems here:

    wrong const lines = newArray(totalMeshes); correct const lines= []; //In Js its not necessary to have a predefined length.

    You need to put data in above lines array as well.