Demo.prototype.addEffectExplosion = function (particleTexture, particleModel, startTime, duration, maxDist, amount, particleScale, posX, posY, posZ, xStartDim, yStartDim, zStartDim, xDim = 0.1, yDim =0.1, zDim = 0.0, xOffset, yOffset, zOffset, blendingMode = "CustomBlending", parentId = "scene")
{

  let particles = new Array(amount);
  for (let i = 0; i < particles.length; i++) {

    let xStart = Utils.random() * xStartDim - xStartDim*.5;
    let yStart = Utils.random() * yStartDim - yStartDim*.5;
    let zStart = Utils.random() * zStartDim - zStartDim*.5;
    let xStartDir = (xStart != 0) ? xStart/Math.abs(xStart) : 0;
    let yStartDir = (yStart != 0) ? yStart/Math.abs(yStart) : 0;
    let zStartDir = (zStart != 0) ? zStart/Math.abs(zStart) : 0;
    let xDir = Utils.random() * (xDim+xStartDir);
    let yDir = Utils.random() * (yDim+yStartDir);
    let zDir = Utils.random() * (zDim+zStartDir);
    xStart+=posX;
    yStart+=posY;
    zStart+=posZ;

    let vectorLength = Math.sqrt(xDir*xDir+yDir*yDir+zDir*zDir);

    xDir = vectorLength*xStartDir*maxDist + (xDir/vectorLength) * (Utils.random() * maxDist * 2 - maxDist) + xOffset + xStart;
    yDir = vectorLength*yStartDir*maxDist + (yDir/vectorLength) * (Utils.random() * maxDist * 2 - maxDist) + yOffset + yStart;
    zDir = vectorLength*zStartDir*maxDist + (zDir/vectorLength) * (Utils.random() * maxDist * 2 - maxDist) + zOffset + zStart;

    
    particles[i] = {
      "x1": xStart,
      "y1": yStart,
      "z1": zStart,
      "x2": xDir,
      "y2": yDir,
      "z2": zDir,
      "startAngle": Utils.random() * 360,
      "speedAngle": (Utils.random()*4-2)*(250),
    };
  }

  let normalBlend =  {
    "blending": blendingMode,
    "transparent":true,
    "depthWrite":blendingMode == false,
  };

  let customBlend = {
    "transparent":true,
    "depthWrite":false,
    "blending": 'CustomBlending',
    "blendEquation":'MaxEquation',
    "blendSrc":'SrcColorFactor',
    "blendDst":'SrcColorFactor'
  };

  let explosionMaterial = {};
  if(blendingMode == "CustomBlending")
  {
    explosionMaterial = customBlend;
  }
  else
  {
    explosionMaterial = normalBlend;
  }


  let explosionAnimation = {
    "parent": (parentId==="scene") ? undefined:parentId,
    "start": startTime, "duration": duration,
    "image": particleTexture,
    textureProperties: [{},{minFilter: 'NearestMipmapNearestFilter', magFilter: 'LinearFilter'}],
    "perspective": "3d",
    "billboard": true,
    "color":[{
      "r":1.0,
      "g":1.0,
      "b":1.0,
      "a":1.0
    }],
    "scale":[{"uniform3d":1.0}],
    "material":explosionMaterial,
    "instancer": {
      "count": particles.length,
      "runInstanceFunction": (properties) => {

        const i = properties.index;
        //const count = properties.count;
        //const time = properties.time;
        let object = properties.object;
        let color = properties.color;
        let angle = properties.angle;

        const scale = particleScale;
        object.scale.x = scale;
        object.scale.y = scale;
        object.scale.z = scale;
        
        const particle = particles[i];

        const now = getSceneTimeFromStart();

        angle.degreesZ = particle.startAngle+now*(particle.speedAngle);

        const percent = (now-startTime)/duration;

        const mix = Math.log((percent*7.0+1.0))/3;
        object.position.x = Utils.mix(particle.x1, particle.x2, mix);
        object.position.y = Utils.mix(particle.y1, particle.y2, mix);
        object.position.z = Utils.mix(particle.z1, particle.z2, mix);
        color.a = 1.0-percent*percent;
        color.r = color.a;
        color.g = color.a;
        color.b = color.a;
        }
      }
    }

  this.loader.addAnimation(explosionAnimation);
}

Demo.prototype.addEffectWaterSplash = function (startTime, durationTime, amountOfParticles, particleSize, texture, xPos,yPos,zPos, xSpread = 1.0, ySpread = 1.0 ,interval = 0.,angle)
{
  const deg2rad = 0.01745329251;
  let particles = new Array(amountOfParticles);
  for (let i = 0; i < particles.length; i++) {
    particles[i] = {
      "x0": ()=>Sync.get('Sub:X')+xPos*Math.cos(Sync.get('Sub:Rot')*deg2rad),
      "y0": ()=>Sync.get('Sub:Y')+Utils.random()*.1-.05+yPos+xPos*Math.sin(Sync.get('Sub:Rot')*deg2rad),
      "z0": ()=>Sync.get('Sub:Z')+zPos,
      "x1": 10*-7.5,
      "y1": 2*(Utils.random()*ySpread*2-ySpread),
      "z1": 2*(Utils.random()*xSpread*2-xSpread),
      "enabled": false
    };
  }

  let prevIntervalTime = 0;
  let particleIndex = 0;
  this.loader.addAnimation({
    "start":startTime, "duration":durationTime,
    "image": texture,
    textureProperties: [{},{minFilter: 'NearestMipmapNearestFilter', magFilter: 'LinearFilter'}],
    //"angle":[{"degreesY":180*directionFlip}],
    "perspective": "3d",
    "billboard": true,
    "additive": true,
    "material":{
      "blending": 'AdditiveBlending',
      "transparent":true,
      "depthWrite":false,

    },
    "scale":[{"uniform3d":.1}],
    "angle":[{degreesZ:angle}],
    "runPreFunction": (animation)=>{
      if(getSceneTimeFromStart() > prevIntervalTime + interval)
      {
        prevIntervalTime = getSceneTimeFromStart();
        particleIndex++;
        if(particleIndex >= particles.length) 
          particleIndex = 0;

        particles[particleIndex].enabled = true;
      }
    },
    "instancer": {
      "count": particles.length,
      "runInstanceFunction": (properties) => {

        const i = properties.index;

        if (i == particleIndex)
        {
          particles[i].x0 = 10*(Sync.get('Sub:X')+xPos*Math.cos(Sync.get('Sub:Rot')*deg2rad));
          particles[i].y0 = 10*(Sync.get('Sub:Y')+Utils.random()*.1-.05+yPos+xPos*Math.sin(-Sync.get('Sub:Rot')*deg2rad));
          particles[i].z0 = 10*(Sync.get('Sub:Z')+zPos);
        }

        if(particles[i].enabled == false)
        {
          let object = properties.object;
          object.scale.x = 0;
          object.scale.y = 0;
          object.scale.z = 0;   
          return;
        }

        const time = properties.time;
        let object = properties.object;
        let color = properties.color;
        
        const scale = particleSize;
        object.scale.x = scale;
        object.scale.y = scale;
        object.scale.z = scale;   

        particles[i].x0+=particles[i].x1*getDeltaTime();
        particles[i].y0+=particles[i].y1*getDeltaTime();
        particles[i].z0+=particles[i].z1*getDeltaTime();

        object.position.x = particles[i].x0;        
        object.position.y = particles[i].y0;
        object.position.z = particles[i].z0;


      }
  }
  });
}