function metaballs()
{
    this.start_time = 0;
    this.end_time = 0;

	  this.angularSpeed = 0.2; 
    this.lastTime = 0;
    this.cube;
    this.renderer, this.scene, this.camera;
    this.angleChange;
    this.current_material = "chrome";
    this.materials;

    this.createShaderMaterial = function(id, light, ambientLight) 
    {

      var shader = THREE.ShaderToon[id];

      var u = THREE.UniformsUtils.clone( shader.uniforms );

      var vs = shader.vertexShader;
      var fs = shader.fragmentShader;

      var material = new THREE.ShaderMaterial( { uniforms: u, vertexShader: vs, fragmentShader: fs } );

      material.uniforms.uDirLightPos.value = light.position;
      material.uniforms.uDirLightColor.value = light.color;

      material.uniforms.uAmbientLightColor.value = ambientLight.color;

      return material;

    }

    this.generateMaterials = function() 
    {

      // environment map

      var path = "assets/cube/";
      var format = '.jpg';
      var urls = [
          path + 'posx_1' + format, path + 'negx_1' + format,
          path + 'posy_1' + format, path + 'negy_1' + format,
          path + 'posz_1' + format, path + 'negz_1' + format
        ];

      var reflectionCube = THREE.ImageUtils.loadTextureCube( urls );
      reflectionCube.format = THREE.RGBFormat;

      var refractionCube = new THREE.Texture( reflectionCube.image, new THREE.CubeRefractionMapping() );
      reflectionCube.format = THREE.RGBFormat;

      var light = new THREE.DirectionalLight( 0xffffff );
      light.position.set( 0.5, 0.5, 1 );
      this.scene.add( light );

      var pointLight = new THREE.PointLight( 0xff3300 );
      pointLight.position.set( 0, 0, 100 );
      this.scene.add( pointLight );

      var ambientLight = new THREE.AmbientLight( 0x080808 );
      this.scene.add( ambientLight );

      var toonMaterial1 = this.createShaderMaterial( "toon1", light, ambientLight ),
      toonMaterial2 = this.createShaderMaterial( "toon2", light, ambientLight ),
      hatchingMaterial = this.createShaderMaterial( "hatching", light, ambientLight ),
      hatchingMaterial2 = this.createShaderMaterial( "hatching", light, ambientLight ),
      dottedMaterial = this.createShaderMaterial( "dotted", light, ambientLight ),
      dottedMaterial2 = this.createShaderMaterial( "dotted", light, ambientLight );

      hatchingMaterial2.uniforms.uBaseColor.value.setRGB( 0, 0, 0 );
      hatchingMaterial2.uniforms.uLineColor1.value.setHSL( 0, 0.8, 0.5 );
      hatchingMaterial2.uniforms.uLineColor2.value.setHSL( 0, 0.8, 0.5 );
      hatchingMaterial2.uniforms.uLineColor3.value.setHSL( 0, 0.8, 0.5 );
      hatchingMaterial2.uniforms.uLineColor4.value.setHSL( 0.1, 0.8, 0.5 );

      dottedMaterial2.uniforms.uBaseColor.value.setRGB( 0, 0, 0 );
      dottedMaterial2.uniforms.uLineColor1.value.setHSL( 0.05, 1.0, 0.5 );

      var texture = THREE.ImageUtils.loadTexture( "textures/UV_Grid_Sm.jpg" );
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;

      this.materials = {

      "chrome" :
      {
        m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: reflectionCube } ),
        h: 0, s: 0, l: 1
      },

      "liquid" :
      {
        m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: refractionCube, refractionRatio: 0.85 } ),
        h: 0, s: 0, l: 1
      },

      "shiny"  :
      {
        m: new THREE.MeshPhongMaterial( { color: 0x550000, specular: 0x440000, envMap: reflectionCube, combine: THREE.MixOperation, reflectivity: 0.3, metal: true } ),
        h: 0, s: 0.8, l: 0.2
      },

      "matte" :
      {
        m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x111111, shininess: 1 } ),
        h: 0, s: 0, l: 1
      },

      "flat" :
      {
        m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x111111, shininess: 1, shading: THREE.FlatShading } ),
        h: 0, s: 0, l: 1
      },

      "textured" :
      {
        m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 1, map: texture } ),
        h: 0, s: 0, l: 1
      },

      "colors" :
      {
        m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 2, vertexColors: THREE.VertexColors } ),
        h: 0, s: 0, l: 1
      },

      "plastic" :
      {
        m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x888888, ambient: 0x000000, shininess: 250 } ),
        h: 0.6, s: 0.8, l: 0.1
      },

      "toon1"  :
      {
        m: toonMaterial1,
        h: 0.2, s: 1, l: 0.75
      },

      "toon2" :
      {
        m: toonMaterial2,
        h: 0.4, s: 1, l: 0.75
      },

      "hatching" :
      {
        m: hatchingMaterial,
        h: 0.2, s: 1, l: 0.9
      },

      "hatching2" :
      {
        m: hatchingMaterial2,
        h: 0.0, s: 0.8, l: 0.5
      },

      "dotted" :
      {
        m: dottedMaterial,
        h: 0.2, s: 1, l: 0.9
      },

      "dotted2" :
      {
        m: dottedMaterial2,
        h: 0.1, s: 1, l: 0.5
      }

      };

      return materials;

    }    


    this.init = function(renderer, camera)
    {            
      
      this.renderer = renderer;

      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
      this.camera.position.set(-500, 500, 1500);

      this.scene = new THREE.Scene();

      this.materials = this.generateMaterials();
      this.current_material = "shiny";

      effect = new THREE.MarchingCubes(28, this.materials[this.current_material].m, true, true );
      effect.position.set( 0, 0, 0 );
      effect.scale.set( 700, 700, 700 );

      effect.enableUvs = false;
      effect.enableColors = false;

      scene.add( effect );

    };

    this.setupRenderer = function(){
    
      this.renderer.setClearColor( 0x000000, 0 );
      this.camera.position.z = 800;          
      this.renderer.autoClear = true;
      
    }


    this.render = function(demoFrame)
    {
    	  this.angleChange = this.angularSpeed * demoFrame * 2 * Math.PI / 1000;      
        
        this.renderer.render(this.scene, this.camera);
    }
}