Error: material.onBeforeCompile undefined (webpack+three.js+ShaderParticleEngine)

Hello,
im trying to get the ShaderParticleEngine running, in a project using npm/webpack.
Doing so, I get an Error TypeError: material.onBeforeCompile is undefined. This happens after I add the particle mesh to the scene (scene.add( particleGroup.mesh );).

I’ve tested several things to track down the Error, however throwing three.js r107 at the SPE Examples, using the <script src="">-Syntax works. So maybe Im doing something wrong somewhere, and someone has already figured out how to bundle this.

Code is on gh in this spetest-branch, Im not aware how to provide this more easily:


The init file (init.js):

Summary
import * as THREE from 'three';
import { SPE } from "shader-particle-engine/build/SPE";
import Controls from './classes/controls';
import Renderer from './classes/renderer';
import Camera from './classes/camera';

export default function () {

    var emitter, particleGroup;

    const clock = new THREE.Clock();
    let delta = 0;

    const scene = new THREE.Scene();
    const renderer = new Renderer(document.body);
    const camera = new Camera(renderer.threeRenderer);
    const controls = new Controls(camera.threeCamera, renderer.threeRenderer.domElement);

    var gridHelper = new THREE.GridHelper( 10, 10 );
    scene.add( gridHelper );

    // SPE Default Example "clouds"
    // Create particle group and emitter
    var loader = new THREE.TextureLoader().load("./img/cloud.png", (texture) => {
        particleGroup = new SPE.Group({
            texture: {
                // value: THREE.ImageUtils.loadTexture('./img/cloud.png')
                value: texture
            },
            blending: THREE.NormalBlending,
            maxParticleCount: 750
            // fog: true
        });
    
        emitter = new SPE.Emitter({
            particleCount: 750,
            maxAge: {
                value: 3,
            },
            position: {
                value: new THREE.Vector3( 0, 0, 0 ),
                spread: new THREE.Vector3( 100, 30, 100 )
            },
            velocity: {
                value: new THREE.Vector3( 0, 0, 30 )
            },
            wiggle: {
                spread: 10
            },
            size: {
                value: 75,
                spread: 50
            },
            opacity: {
                value: [ 0, 1, 0 ]
            },
            color: {
                value: new THREE.Color( 1, 1, 1 ),
                spread: new THREE.Color( 0.1, 0.1, 0.1 )
            },
            angle: {
                value: [ 0, Math.PI * 0.125 ]
            }
        });
    
        particleGroup.addEmitter( emitter );
        scene.add( particleGroup.mesh );
        // particleGroup.mesh.frustumCulled = false;
    
    });

    function update(delta) {
        if( particleGroup ) {
            particleGroup.tick( delta );
        }
        controls.threeControls.update();
    }

    const animate = function animate() {
        requestAnimationFrame(animate);
        delta = clock.getDelta();
        update(delta);
        renderer.render(scene, camera.threeCamera);
    };
    animate();
}

I think webpack or something optimizes the empty function defined for the onBeforeCompile method away. THREE always calls these empty functions just like onBeforeRender etc, it’s a design issue.

If that’s the case you either could

1 Like

Thank you for your reply Fyrestar, Ive tried your second and third suggestion without success:

Added an empty onBeforeCompile via
particleGroup.mesh.material.onBeforeCompile = function() {};

Result: no Error in the console, but no Particles on the screen either :frowning:

I patched the Renderer just to make sure (without the empty onBeforeCompile). The Error is thrown from this line though: three.js/WebGLPrograms.js at 162d99e9c7dfcff2d948c8e9385afe9ae7f70b84 · mrdoob/three.js · GitHub
I prepend there, its thrown on the next line somewhere else- It feels like its the wrong end to look for.
I have updated the example code on gh and in the opening post.

If someone is using SPE+threejs with a bundler and got it going i would be happy to have a look.

Update: SPE is using his own copy of three.js in his node_modules folder. By replacing this module with the latest release of three.js the onBeforeCompile-Error in the console is gone.

No particles are rendered though, so Im still looking to fix this.

I was about to say you should check your own code after it didnt threw an error anymore. You might also set frustumCulled to false on the particlegroup or whatever represents the mesh.

Yeah Ive tried disabling the culling (particleGroup.mesh.frustumCulled = false;) without success. My code is only a boilerplate with OrbitControls and a grid helper. Im trying to stay close to one of SPEs official cloud examples which is working.

Ive opened an issue in the authors project on github, where @titansoftime did a PR not long ago. Maybe he is still using this engine and has some insight?

I’m using it actually too, try to debug it deeper and check if it’s even rendered at all, some value that might make it invisible etc. Are there any other warnings in the console?

Hmm, not sure I’ll be of any help here as I don’t use any kind of js bundling, just old fashioned js script src.

I don’t use any kind of js bundling

:pleading_face:

Thanks for giving me hope by reassuring it should work.
I debugged deeper as @Fyrestar said and it seems to me I cant use SPE with webpacks shimming. Or atleast I need to learn some more shenanigans.
IMO this should work:

{
    test: require.resolve('shader-particle-engine'),
    use: ['imports-loader?THREE=three', 'exports-loader?SPE']
}

Which got me to the point where everything is compiling and running but no particles.

I went with throwing my THREE Instance into SPE by editing the source file:
module.exports = function(THREE) { [spe.min.js content] return SPE;}
And in my module:
let SPE = spe.default(THREE);

So this should be improved, but is still solved in some way for now, thanks! :slight_smile: