text: "use strict";
  
  var CABLES=CABLES||{};
  CABLES.OPS=CABLES.OPS||{};
  
  var Ops=Ops || {};
  Ops.Gl=Ops.Gl || {};
  Ops.Math=Ops.Math || {};
  Ops.Anim=Ops.Anim || {};
  Ops.Trigger=Ops.Trigger || {};
  Ops.Gl.Matrix=Ops.Gl.Matrix || {};
  Ops.Gl.Shader=Ops.Gl.Shader || {};
  Ops.Gl.Meshes=Ops.Gl.Meshes || {};
  Ops.Gl.ShaderEffects=Ops.Gl.ShaderEffects || {};
  Ops.Gl.TextureEffects=Ops.Gl.TextureEffects || {};
  Ops.Gl.TextureEffects.Noise=Ops.Gl.TextureEffects.Noise || {};
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.MainLoop
  // 
  // **************************************************************
  
  Ops.Gl.MainLoop = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const fpsLimit=op.inValue("FPS Limit",0);
  const trigger=op.outTrigger("trigger");
  const width=op.outValue("width");
  const height=op.outValue("height");
  const reduceLoadingFPS=op.inValueBool("Reduce FPS loading");
  const clear=op.inValueBool("Clear",true);
  const fullscreen=op.inValueBool("Fullscreen Button",false);
  const active=op.inValueBool("Active",true);
  const hdpi=op.inValueBool("Hires Displays",false);
  
  hdpi.onChange=function()
  {
      if(hdpi.get()) op.patch.cgl.pixelDensity=window.devicePixelRatio;
          else op.patch.cgl.pixelDensity=1;
          
      op.patch.cgl.updateSize();
      if(CABLES.UI) gui.setLayout();
  };
  
  
  var cgl=op.patch.cgl;
  var rframes=0;
  var rframeStart=0;
  
  if(!op.patch.cgl) op.uiAttr( { 'error': 'No webgl cgl context' } );
  
  var identTranslate=vec3.create();
  vec3.set(identTranslate, 0,0,0);
  var identTranslateView=vec3.create();
  vec3.set(identTranslateView, 0,0,-2);
  
  fullscreen.onChange=updateFullscreenButton;
  setTimeout(updateFullscreenButton,100);
  var fsElement=null;
  
  function updateFullscreenButton()
  {
      function onMouseEnter()
      {
          if(fsElement)fsElement.style.display="block";
      }
  
      function onMouseLeave()
      {
          if(fsElement)fsElement.style.display="none";
      }
      
      op.patch.cgl.canvas.addEventListener('mouseleave', onMouseLeave);
      op.patch.cgl.canvas.addEventListener('mouseenter', onMouseEnter);
  
      if(fullscreen.get())
      {
          if(!fsElement) 
          {
              fsElement = document.createElement('div');
  
              var container = op.patch.cgl.canvas.parentElement;
              if(container)container.appendChild(fsElement);
      
              fsElement.addEventListener('mouseenter', onMouseEnter);
              fsElement.addEventListener('click', function(e)
              {
                  if(CABLES.UI && !e.shiftKey) gui.cycleRendererSize();
                      else
                      {
                          cgl.fullScreen();
                      }
              });
          }
  
          fsElement.style.padding="10px";
          fsElement.style.position="absolute";
          fsElement.style.right="5px";
          fsElement.style.top="5px";
          fsElement.style.width="20px";
          fsElement.style.height="20px";
          // fsElement.style.opacity="1.0";
          fsElement.style.cursor="pointer";
          fsElement.style['border-radius']="40px";
          fsElement.style.background="#444";
          fsElement.style["z-index"]="9999";
          fsElement.style.display="none";
          fsElement.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 490 490" style="width:20px;height:20px;" xml:space="preserve" width="512px" height="512px"><g><path d="M173.792,301.792L21.333,454.251v-80.917c0-5.891-4.776-10.667-10.667-10.667C4.776,362.667,0,367.442,0,373.333V480     c0,5.891,4.776,10.667,10.667,10.667h106.667c5.891,0,10.667-4.776,10.667-10.667s-4.776-10.667-10.667-10.667H36.416     l152.459-152.459c4.093-4.237,3.975-10.99-0.262-15.083C184.479,297.799,177.926,297.799,173.792,301.792z" fill="#FFFFFF"/><path d="M480,0H373.333c-5.891,0-10.667,4.776-10.667,10.667c0,5.891,4.776,10.667,10.667,10.667h80.917L301.792,173.792     c-4.237,4.093-4.354,10.845-0.262,15.083c4.093,4.237,10.845,4.354,15.083,0.262c0.089-0.086,0.176-0.173,0.262-0.262     L469.333,36.416v80.917c0,5.891,4.776,10.667,10.667,10.667s10.667-4.776,10.667-10.667V10.667C490.667,4.776,485.891,0,480,0z" fill="#FFFFFF"/><path d="M36.416,21.333h80.917c5.891,0,10.667-4.776,10.667-10.667C128,4.776,123.224,0,117.333,0H10.667     C4.776,0,0,4.776,0,10.667v106.667C0,123.224,4.776,128,10.667,128c5.891,0,10.667-4.776,10.667-10.667V36.416l152.459,152.459     c4.237,4.093,10.99,3.975,15.083-0.262c3.992-4.134,3.992-10.687,0-14.82L36.416,21.333z" fill="#FFFFFF"/><path d="M480,362.667c-5.891,0-10.667,4.776-10.667,10.667v80.917L316.875,301.792c-4.237-4.093-10.99-3.976-15.083,0.261     c-3.993,4.134-3.993,10.688,0,14.821l152.459,152.459h-80.917c-5.891,0-10.667,4.776-10.667,10.667s4.776,10.667,10.667,10.667     H480c5.891,0,10.667-4.776,10.667-10.667V373.333C490.667,367.442,485.891,362.667,480,362.667z" fill="#FFFFFF"/></g></svg>';
      }
      else
      {
          if(fsElement)
          {
              fsElement.style.display="none";
              fsElement.remove();
              fsElement=null;
          }
      }
  }
  
  
  fpsLimit.onChange=function()
  {
      op.patch.config.fpsLimit=fpsLimit.get()||0;
  };
  
  op.onDelete=function()
  {
      cgl.gl.clearColor(0,0,0,0);
      cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT);
  
      op.patch.removeOnAnimFrame(op);
  };
  
  
  op.patch.loading.setOnFinishedLoading(function(cb)
  {
      op.patch.config.fpsLimit=fpsLimit.get();
  });
  
  
  
  op.onAnimFrame=function(time)
  {
      if(!active.get())return;
      if(cgl.aborted || cgl.canvas.clientWidth===0 || cgl.canvas.clientHeight===0)return;
  
      if(op.patch.loading.getProgress()<1.0 && reduceLoadingFPS.get())
      {
          op.patch.config.fpsLimit=5;
      }
  
      if(cgl.canvasWidth==-1)
      {
          cgl.setCanvas(op.patch.config.glCanvasId);
          return;
      }
  
      if(cgl.canvasWidth!=width.get() || cgl.canvasHeight!=height.get())
      {
          // cgl.canvasWidth=cgl.canvas.clientWidth;
          width.set(cgl.canvasWidth);
          // cgl.canvasHeight=cgl.canvas.clientHeight;
          height.set(cgl.canvasHeight);
      }
  
      if(CABLES.now()-rframeStart>1000)
      {
          CGL.fpsReport=CGL.fpsReport||[];
          if(op.patch.loading.getProgress()>=1.0 && rframeStart!==0)CGL.fpsReport.push(rframes);
          rframes=0;
          rframeStart=CABLES.now();
      }
      CGL.MESH.lastShader=null;
      CGL.MESH.lastMesh=null;
  
      cgl.renderStart(cgl,identTranslate,identTranslateView);
  
      if(clear.get())
      {
          cgl.gl.clearColor(0,0,0,1);
          cgl.gl.clear(cgl.gl.COLOR_BUFFER_BIT | cgl.gl.DEPTH_BUFFER_BIT);
      }
  
      trigger.trigger();
  
  
      if(CGL.MESH.lastMesh)CGL.MESH.lastMesh.unBind();
  
  
      if(CGL.Texture.previewTexture)
      {
          if(!CGL.Texture.texturePreviewer) CGL.Texture.texturePreviewer=new CGL.Texture.texturePreview(cgl);
          CGL.Texture.texturePreviewer.render(CGL.Texture.previewTexture);
      }
      cgl.renderEnd(cgl);
      
      
      // cgl.printError('mainloop end');
      
      
  
      if(!cgl.frameStore.phong)cgl.frameStore.phong={};
      rframes++;
  };
  
  
  };
  
  Ops.Gl.MainLoop.prototype = new CABLES.Op();
  CABLES.OPS["b0472a1d-db16-4ba6-8787-f300fbdc77bb"]={f:Ops.Gl.MainLoop,objName:"Ops.Gl.MainLoop"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.Matrix.OrbitControls
  // 
  // **************************************************************
  
  Ops.Gl.Matrix.OrbitControls = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const render=op.inTrigger("render");
  const minDist=op.addInPort(new CABLES.Port(op,"min distance",CABLES.OP_PORT_TYPE_VALUE));
  const maxDist=op.addInPort(new CABLES.Port(op,"max distance",CABLES.OP_PORT_TYPE_VALUE));
  
  const minRotY=op.inValue("min rot y",0);
  const maxRotY=op.inValue("max rot y",0);
  
  // const minRotX=op.inValue("min rot x",0);
  // const maxRotX=op.inValue("max rot x",0);
  
  const initialRadius=op.inValue("initial radius",0);
  const initialAxis=op.addInPort(new CABLES.Port(op,"initial axis y",CABLES.OP_PORT_TYPE_VALUE,{display:'range'}));
  const initialX=op.addInPort(new CABLES.Port(op,"initial axis x",CABLES.OP_PORT_TYPE_VALUE,{display:'range'}));
  
  const mul=op.addInPort(new CABLES.Port(op,"mul",CABLES.OP_PORT_TYPE_VALUE));
  const smoothness=op.inValueSlider("Smoothness",1.0);
  const speedX=op.inValue("Speed X",1);
  const speedY=op.inValue("Speed Y",1);
  
  
  
  const active=op.inValueBool("Active",true);
  
  const allowPanning=op.inValueBool("Allow Panning",true);
  const allowZooming=op.inValueBool("Allow Zooming",true);
  const allowRotation=op.inValueBool("Allow Rotation",true);
  const restricted=op.inValueBool("restricted",true);
  const pointerLock=op.inValueBool("Pointerlock",false);
  
  const trigger=op.outTrigger("trigger");
  const outRadius=op.outValue("radius");
  const outYDeg=op.outValue("Rot Y");
  const outXDeg=op.outValue("Rot X");
  
  const inReset=op.inTriggerButton("Reset");
  
  op.setPortGroup("Initial Values",[initialAxis,initialX,initialRadius]);
  op.setPortGroup("Interaction",[mul,smoothness,speedX,speedY]);
  op.setPortGroup("Boundaries",[minRotY,maxRotY,minDist,maxDist]);
  
  
  
  mul.set(1);
  minDist.set(0.05);
  maxDist.set(99999);
  
  inReset.onTriggered=reset;
  
  const cgl=op.patch.cgl;
  var eye=vec3.create();
  var vUp=vec3.create();
  var vCenter=vec3.create();
  var viewMatrix=mat4.create();
  var vOffset=vec3.create();
  
  initialAxis.set(0.5);
  
  
  var mouseDown=false;
  var radius=5;
  outRadius.set(radius);
  
  var lastMouseX=0,lastMouseY=0;
  var percX=0,percY=0;
  
  
  vec3.set(vCenter, 0,0,0);
  vec3.set(vUp, 0,1,0);
  
  var tempEye=vec3.create();
  var finalEye=vec3.create();
  var tempCenter=vec3.create();
  var finalCenter=vec3.create();
  
  var px=0;
  var py=0;
  
  var divisor=1;
  var element=null;
  updateSmoothness();
  
  op.onDelete=unbind;
  
  var doLockPointer=false;
  
  pointerLock.onChange=function()
  {
      doLockPointer=pointerLock.get();
      console.log("doLockPointer",doLockPointer);
  };
  
  function reset()
  {
      px=px%(Math.PI*2);
      py=py%(Math.PI*2);
  
      percX=(initialX.get()*Math.PI*2);
      percY=(initialAxis.get()-0.5);
      radius=initialRadius.get();
      eye=circlePos( percY );
  }
  
  function updateSmoothness()
  {
      divisor=smoothness.get()*10+1.0;
  }
  
  smoothness.onChange=updateSmoothness;
  
  var initializing=true;
  
  function ip(val,goal)
  {
      if(initializing)return goal;
      return val+(goal-val)/divisor;
  }
  
  var lastPy=0;
  var lastPx=0;
  
  render.onTriggered=function()
  {
      cgl.pushViewMatrix();
  
      px=ip(px,percX);
      py=ip(py,percY);
  
      var degY=(py+0.5)*180;
  
  
      if(minRotY.get()!==0 && degY<minRotY.get())
      {
          degY=minRotY.get();
          py=lastPy;
      }
      else if(maxRotY.get()!==0 && degY>maxRotY.get())
      {
          degY=maxRotY.get();
          py=lastPy;
      }
      else
      {
          lastPy=py;
      }
  
  
      var degX=(px)*CGL.RAD2DEG;
  
      // if(minRotX.get()!==0 && degX<minRotX.get())
      // {
      //     degX=minRotX.get();
      //     px=lastPx;
      // }
      // else if(maxRotX.get()!==0 && degX>maxRotX.get())
      // {
      //     degX=maxRotX.get();
      //     px=lastPx;
      // }
      // else
      // {
      //     lastPx=px;
      // }
  
  
  
      outYDeg.set( degY );
      // outXDeg.set( (px)*180 );
      outXDeg.set( degX );
  
  
      circlePosi(eye, py );
  
      vec3.add(tempEye, eye, vOffset);
      vec3.add(tempCenter, vCenter, vOffset);
  
      finalEye[0]=ip(finalEye[0],tempEye[0]);
      finalEye[1]=ip(finalEye[1],tempEye[1]);
      finalEye[2]=ip(finalEye[2],tempEye[2]);
  
      finalCenter[0]=ip(finalCenter[0],tempCenter[0]);
      finalCenter[1]=ip(finalCenter[1],tempCenter[1]);
      finalCenter[2]=ip(finalCenter[2],tempCenter[2]);
  
      mat4.lookAt(viewMatrix, finalEye, finalCenter, vUp);
      mat4.rotate(viewMatrix, viewMatrix, px, vUp);
      mat4.multiply(cgl.vMatrix,cgl.vMatrix,viewMatrix);
  
      trigger.trigger();
      cgl.popViewMatrix();
      initializing=false;
  };
  
  function circlePosi(vec,perc)
  {
      const mmul=mul.get();
      if(radius<minDist.get()*mmul)radius=minDist.get()*mmul;
      if(radius>maxDist.get()*mmul)radius=maxDist.get()*mmul;
  
      outRadius.set(radius*mmul);
  
      var i=0,degInRad=0;
      // var vec=vec3.create();
      degInRad = 360*perc/2*CGL.DEG2RAD;
      vec3.set(vec,
          Math.cos(degInRad)*radius*mmul,
          Math.sin(degInRad)*radius*mmul,
          0);
      return vec;
  }
  
  
  function circlePos(perc)
  {
      const mmul=mul.get();
      if(radius<minDist.get()*mmul)radius=minDist.get()*mmul;
      if(radius>maxDist.get()*mmul)radius=maxDist.get()*mmul;
  
      outRadius.set(radius*mmul);
  
      var i=0,degInRad=0;
      var vec=vec3.create();
      degInRad = 360*perc/2*CGL.DEG2RAD;
      vec3.set(vec,
          Math.cos(degInRad)*radius*mmul,
          Math.sin(degInRad)*radius*mmul,
          0);
      return vec;
  }
  
  function onmousemove(event)
  {
      if(!mouseDown) return;
  
      var x = event.clientX;
      var y = event.clientY;
  
      var movementX=(x-lastMouseX);
      var movementY=(y-lastMouseY);
  
      if(doLockPointer)
      {
          movementX=event.movementX*mul.get();
          movementY=event.movementY*mul.get();
      }
  
      movementX*=speedX.get();
      movementY*=speedY.get();
  
      if(event.which==3 && allowPanning.get())
      {
          vOffset[2]+=movementX*0.01*mul.get();
          vOffset[1]+=movementY*0.01*mul.get();
      }
      else
      if(event.which==2 && allowZooming.get())
      {
          radius+=movementY*0.05;
          eye=circlePos(percY);
      }
      else
      {
          if(allowRotation.get())
          {
              percX+=movementX*0.003;
              percY+=movementY*0.002;
  
              if(restricted.get())
              {
                  if(percY>0.5)percY=0.5;
                  if(percY<-0.5)percY=-0.5;
              }
          }
      }
  
      lastMouseX=x;
      lastMouseY=y;
  }
  
  function onMouseDown(event)
  {
      lastMouseX = event.clientX;
      lastMouseY = event.clientY;
      mouseDown=true;
  
      if(doLockPointer)
      {
          var el=op.patch.cgl.canvas;
          el.requestPointerLock = el.requestPointerLock || el.mozRequestPointerLock || el.webkitRequestPointerLock;
          if(el.requestPointerLock) el.requestPointerLock();
          else console.log("no t found");
          // document.addEventListener("mousemove", onmousemove, false);
  
          document.addEventListener('pointerlockchange', lockChange, false);
          document.addEventListener('mozpointerlockchange', lockChange, false);
          document.addEventListener('webkitpointerlockchange', lockChange, false);
      }
  }
  
  function onMouseUp()
  {
      mouseDown=false;
      // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer';
  
      if(doLockPointer)
      {
          document.removeEventListener('pointerlockchange', lockChange, false);
          document.removeEventListener('mozpointerlockchange', lockChange, false);
          document.removeEventListener('webkitpointerlockchange', lockChange, false);
  
          if(document.exitPointerLock) document.exitPointerLock();
          document.removeEventListener("mousemove", pointerLock, false);
      }
  }
  
  function lockChange()
  {
      var el=op.patch.cgl.canvas;
  
      if (document.pointerLockElement === el || document.mozPointerLockElement === el || document.webkitPointerLockElement === el)
      {
          document.addEventListener("mousemove", onmousemove, false);
          console.log("listening...");
      }
  }
  
  function onMouseEnter(e)
  {
      // cgl.canvas.style.cursor='url(/ui/img/rotate.png),pointer';
  }
  
  initialRadius.onChange=function()
  {
      radius=initialRadius.get();
      reset();
  };
  
  initialX.onChange=function()
  {
      px=percX=(initialX.get()*Math.PI*2);
  };
  
  initialAxis.onChange=function()
  {
      py=percY=(initialAxis.get()-0.5);
      eye=circlePos(percY);
  };
  
  var onMouseWheel=function(event)
  {
      if(allowZooming.get())
      {
          var delta=CGL.getWheelSpeed(event)*0.06;
          radius+=(parseFloat(delta))*1.2;
  
          eye=circlePos(percY);
          event.preventDefault();
      }
  };
  
  var ontouchstart=function(event)
  {
      doLockPointer=false;
      if(event.touches && event.touches.length>0) onMouseDown(event.touches[0]);
  };
  
  var ontouchend=function(event)
  {
      doLockPointer=false;
      onMouseUp();
  };
  
  var ontouchmove=function(event)
  {
      doLockPointer=false;
      if(event.touches && event.touches.length>0) onmousemove(event.touches[0]);
  };
  
  active.onChange=function()
  {
      if(active.get())bind();
          else unbind();
  }
  
  
  function setElement(ele)
  {
      unbind();
      element=ele;
      bind();
  }
  
  function bind()
  {
      element.addEventListener('mousemove', onmousemove);
      element.addEventListener('mousedown', onMouseDown);
      element.addEventListener('mouseup', onMouseUp);
      element.addEventListener('mouseleave', onMouseUp);
      element.addEventListener('mouseenter', onMouseEnter);
      element.addEventListener('contextmenu', function(e){e.preventDefault();});
      element.addEventListener('wheel', onMouseWheel);
  
      element.addEventListener('touchmove', ontouchmove);
      element.addEventListener('touchstart', ontouchstart);
      element.addEventListener('touchend', ontouchend);
  }
  
  function unbind()
  {
      if(!element)return;
  
      element.removeEventListener('mousemove', onmousemove);
      element.removeEventListener('mousedown', onMouseDown);
      element.removeEventListener('mouseup', onMouseUp);
      element.removeEventListener('mouseleave', onMouseUp);
      element.removeEventListener('mouseenter', onMouseUp);
      element.removeEventListener('wheel', onMouseWheel);
  
      element.removeEventListener('touchmove', ontouchmove);
      element.removeEventListener('touchstart', ontouchstart);
      element.removeEventListener('touchend', ontouchend);
  }
  
  eye=circlePos(0);
  setElement(cgl.canvas);
  
  
  bind();
  
  initialX.set(0.25);
  initialRadius.set(0.05);
  
  
  };
  
  Ops.Gl.Matrix.OrbitControls.prototype = new CABLES.Op();
  CABLES.OPS["eaf4f7ce-08a3-4d1b-b9f4-ebc0b7b1cde1"]={f:Ops.Gl.Matrix.OrbitControls,objName:"Ops.Gl.Matrix.OrbitControls"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.Shader.BasicMaterial
  // 
  // **************************************************************
  
  Ops.Gl.Shader.BasicMaterial = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={shader_frag:"{{MODULES_HEAD}}\n\nIN vec2 texCoord;\n#ifdef HAS_TEXTURES\n    IN vec2 texCoordOrig;\n    #ifdef HAS_TEXTURE_DIFFUSE\n        UNI sampler2D tex;\n    #endif\n    #ifdef HAS_TEXTURE_OPACITY\n        UNI sampler2D texOpacity;\n   #endif\n#endif\nUNI float r;\nUNI float g;\nUNI float b;\nUNI float a;\n\nvoid main()\n{\n    {{MODULE_BEGIN_FRAG}}\n    vec4 col=vec4(r,g,b,a);\n\n    #ifdef HAS_TEXTURES\n        #ifdef HAS_TEXTURE_DIFFUSE\n\n           col=texture(tex,vec2(texCoord.x,(1.0-texCoord.y)));\n\n           #ifdef COLORIZE_TEXTURE\n               col.r*=r;\n               col.g*=g;\n               col.b*=b;\n           #endif\n        #endif\n\n        col.a*=a;\n        #ifdef HAS_TEXTURE_OPACITY\n            #ifdef TRANSFORMALPHATEXCOORDS\n                col.a*=texture(texOpacity,vec2(texCoordOrig.s,1.0-texCoordOrig.t)).g;\n            #endif\n            #ifndef TRANSFORMALPHATEXCOORDS\n                col.a*=texture(texOpacity,vec2(texCoord.s,1.0-texCoord.t)).g;\n            #endif\n       #endif\n\n    #endif\n\n    {{MODULE_COLOR}}\n\n    outColor = col;\n\n\n}\n",shader_vert:"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nIN vec3 attrVertNormal;\nIN vec2 attrTexCoord;\n\nOUT vec3 norm;\nOUT vec2 texCoord;\nOUT vec2 texCoordOrig;\n\nUNI mat4 projMatrix;\nUNI mat4 modelMatrix;\nUNI mat4 viewMatrix;\n\n#ifdef HAS_TEXTURES\n    #ifdef TEXTURE_REPEAT\n        UNI float diffuseRepeatX;\n        UNI float diffuseRepeatY;\n        UNI float texOffsetX;\n        UNI float texOffsetY;\n    #endif\n#endif\n\n\nvoid main()\n{\n    mat4 mMatrix=modelMatrix;\n    mat4 mvMatrix;\n    \n    texCoordOrig=attrTexCoord;\n    texCoord=attrTexCoord;\n    #ifdef HAS_TEXTURES\n        #ifdef TEXTURE_REPEAT\n            texCoord.x=texCoord.x*diffuseRepeatX+texOffsetX;\n            texCoord.y=texCoord.y*diffuseRepeatY+texOffsetY;\n        #endif\n    #endif\n\n    vec4 pos = vec4( vPosition, 1. );\n\n\n    #ifdef BILLBOARD\n       vec3 position=vPosition;\n       mvMatrix=viewMatrix*modelMatrix;\n\n       gl_Position = projMatrix * mvMatrix * vec4((\n           position.x * vec3(\n               mvMatrix[0][0],\n               mvMatrix[1][0],\n               mvMatrix[2][0] ) +\n           position.y * vec3(\n               mvMatrix[0][1],\n               mvMatrix[1][1],\n               mvMatrix[2][1]) ), 1.0);\n    #endif\n\n    {{MODULE_VERTEX_POSITION}}\n\n    #ifndef BILLBOARD\n        mvMatrix=viewMatrix * mMatrix;\n    #endif\n\n\n    #ifndef BILLBOARD\n        // gl_Position = projMatrix * viewMatrix * modelMatrix * pos;\n        gl_Position = projMatrix * mvMatrix * pos;\n    #endif\n}\n",};
  const render=op.inTrigger("render");
  const trigger=op.outTrigger("trigger");
  const shaderOut=op.addOutPort(new CABLES.Port(op,"shader",CABLES.OP_PORT_TYPE_OBJECT));
  
  shaderOut.ignoreValueSerialize=true;
  const cgl=op.patch.cgl;
  
  
  var shader=new CGL.Shader(cgl,'BasicMaterial');
  shader.setModules(['MODULE_VERTEX_POSITION','MODULE_COLOR','MODULE_BEGIN_FRAG']);
  shader.bindTextures=bindTextures;
  shader.setSource(attachments.shader_vert,attachments.shader_frag);
  shaderOut.set(shader);
  
  render.onTriggered=doRender;
  
  var textureOpacity=null;
  var textureOpacityUniform=null;
  
  
  function bindTextures()
  {
      if(diffuseTexture.get()) cgl.setTexture(0, diffuseTexture.get().tex);
      if(textureOpacity.get()) cgl.setTexture(1, textureOpacity.get().tex);
  }
  
  op.preRender=function()
  {
      shader.bind();
      doRender();
  };
  
  function doRender()
  {
      if(!shader)return;
  
      cgl.setShader(shader);
      shader.bindTextures();
  
      trigger.trigger();
  
      cgl.setPreviousShader();
  }
  
  
  {
      // rgba colors
      var r=op.addInPort(new CABLES.Port(op,"r",CABLES.OP_PORT_TYPE_VALUE,{ display:'range',colorPick:'true' }));
      var g=op.addInPort(new CABLES.Port(op,"g",CABLES.OP_PORT_TYPE_VALUE,{ display:'range'}));
      var b=op.addInPort(new CABLES.Port(op,"b",CABLES.OP_PORT_TYPE_VALUE,{ display:'range' }));
      var a=op.addInPort(new CABLES.Port(op,"a",CABLES.OP_PORT_TYPE_VALUE,{ display:'range'}));
  
      a.uniform=new CGL.Uniform(shader,'f','a',a);
      b.uniform=new CGL.Uniform(shader,'f','b',b);
      r.uniform=new CGL.Uniform(shader,'f','r',r);
      g.uniform=new CGL.Uniform(shader,'f','g',g);
  
  
      r.set(Math.random());
      g.set(Math.random());
      b.set(Math.random());
      a.set(1.0);
  
      op.setPortGroup('Color',[r,g,b,a]);
  
  }
  
  {
      // diffuse outTexture
  
  
      var diffuseTexture=op.inTexture("texture");
      var diffuseTextureUniform=null;
      shader.bindTextures=bindTextures;
  
      diffuseTexture.onChange=function()
      {
          if(diffuseTexture.get())
          {
              // if(diffuseTextureUniform!==null)return;
              // shader.addveUniform('texDiffuse');
              if(!shader.hasDefine('HAS_TEXTURE_DIFFUSE'))shader.define('HAS_TEXTURE_DIFFUSE');
              if(!diffuseTextureUniform)diffuseTextureUniform=new CGL.Uniform(shader,'t','texDiffuse',0);
              updateTexRepeat();
          }
          else
          {
              shader.removeUniform('texDiffuse');
              shader.removeDefine('HAS_TEXTURE_DIFFUSE');
              diffuseTextureUniform=null;
          }
      };
  
  
  
  }
  
  {
      // opacity texture
      textureOpacity=op.inTexture("textureOpacity");
  
      textureOpacity.onChange=function()
      {
          if(textureOpacity.get())
          {
              if(textureOpacityUniform!==null)return;
              shader.removeUniform('texOpacity');
              shader.define('HAS_TEXTURE_OPACITY');
              if(!textureOpacityUniform)textureOpacityUniform=new CGL.Uniform(shader,'t','texOpacity',1);
          }
          else
          {
              shader.removeUniform('texOpacity');
              shader.removeDefine('HAS_TEXTURE_OPACITY');
              textureOpacityUniform=null;
          }
      };
  
  }
  
  op.colorizeTexture=op.addInPort(new CABLES.Port(op,"colorizeTexture",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  op.colorizeTexture.set(false);
  op.colorizeTexture.onChange=function()
  {
      if(op.colorizeTexture.get()) shader.define('COLORIZE_TEXTURE');
          else shader.removeDefine('COLORIZE_TEXTURE');
  };
  
  
  op.doBillboard=op.addInPort(new CABLES.Port(op,"billboard",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  op.doBillboard.set(false);
  op.doBillboard.onChange=function()
  {
      if(op.doBillboard.get()) shader.define('BILLBOARD');
          else shader.removeDefine('BILLBOARD');
  };
  
  var texCoordAlpha=op.inValueBool("Opacity TexCoords Transform",false);
  
  texCoordAlpha.onChange=function()
  {
      if(texCoordAlpha.get()) shader.define('TRANSFORMALPHATEXCOORDS');
          else shader.removeDefine('TRANSFORMALPHATEXCOORDS');
  
  };
  
  var preMultipliedAlpha=op.addInPort(new CABLES.Port(op,"preMultiplied alpha",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  
  function updateTexRepeat()
  {
      if(!diffuseRepeatXUniform)
      {
          diffuseRepeatXUniform=new CGL.Uniform(shader,'f','diffuseRepeatX',diffuseRepeatX);
          diffuseRepeatYUniform=new CGL.Uniform(shader,'f','diffuseRepeatY',diffuseRepeatY);
          diffuseOffsetXUniform=new CGL.Uniform(shader,'f','texOffsetX',diffuseOffsetX);
          diffuseOffsetYUniform=new CGL.Uniform(shader,'f','texOffsetY',diffuseOffsetY);
      }
  
      diffuseRepeatXUniform.setValue(diffuseRepeatX.get());
      diffuseRepeatYUniform.setValue(diffuseRepeatY.get());
      diffuseOffsetXUniform.setValue(diffuseOffsetX.get());
      diffuseOffsetYUniform.setValue(diffuseOffsetY.get());
  }
  
  
  {
      // texture coords
  
      var diffuseRepeatX=op.addInPort(new CABLES.Port(op,"diffuseRepeatX",CABLES.OP_PORT_TYPE_VALUE));
      var diffuseRepeatY=op.addInPort(new CABLES.Port(op,"diffuseRepeatY",CABLES.OP_PORT_TYPE_VALUE));
      var diffuseOffsetX=op.addInPort(new CABLES.Port(op,"Tex Offset X",CABLES.OP_PORT_TYPE_VALUE));
      var diffuseOffsetY=op.addInPort(new CABLES.Port(op,"Tex Offset Y",CABLES.OP_PORT_TYPE_VALUE));
  
      op.setPortGroup('Transform Texture',[diffuseRepeatX,diffuseRepeatY,diffuseOffsetX,diffuseOffsetY]);
  
      diffuseRepeatX.onChange=updateTexRepeat;
      diffuseRepeatY.onChange=updateTexRepeat;
      diffuseOffsetY.onChange=updateTexRepeat;
      diffuseOffsetX.onChange=updateTexRepeat;
  
      var diffuseRepeatXUniform=null;
      var diffuseRepeatYUniform=null;
      var diffuseOffsetXUniform=null;
      var diffuseOffsetYUniform=null;
  
      shader.define('TEXTURE_REPEAT');
  
  
      diffuseOffsetX.set(0);
      diffuseOffsetY.set(0);
      diffuseRepeatX.set(1);
      diffuseRepeatY.set(1);
  }
  
  
  };
  
  Ops.Gl.Shader.BasicMaterial.prototype = new CABLES.Op();
  CABLES.OPS["85ae5cfa-5eca-4dd8-8b30-850ac34f7cd5"]={f:Ops.Gl.Shader.BasicMaterial,objName:"Ops.Gl.Shader.BasicMaterial"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.ShaderEffects.VertexDisplacementMap
  // 
  // **************************************************************
  
  Ops.Gl.ShaderEffects.VertexDisplacementMap = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  var cgl=op.patch.cgl;
  var id='mod'+Math.floor(Math.random()*10000);
  
  op.render=op.addInPort(new CABLES.Port(this,"render",CABLES.OP_PORT_TYPE_FUNCTION));
  op.trigger=op.addOutPort(new CABLES.Port(this,"trigger",CABLES.OP_PORT_TYPE_FUNCTION));
  
  var texture=this.addInPort(new CABLES.Port(this,"texture",CABLES.OP_PORT_TYPE_TEXTURE));
  
  var extrude=op.inValue("extrude",0.5);//addInPort(new CABLES.Port(this,"extrude",CABLES.OP_PORT_TYPE_VALUE));
  
  var flip=op.inValueBool("flip",true);
  
  var removeZero=op.addInPort(new CABLES.Port(this,"Ignore Zero Values",CABLES.OP_PORT_TYPE_VALUE,{display:'bool'}));
  
  var invert=op.addInPort(new CABLES.Port(this,"invert",CABLES.OP_PORT_TYPE_VALUE,{display:'bool'}));
  invert.set(false);
  var offsetX=op.addInPort(new CABLES.Port(this,"offset X",CABLES.OP_PORT_TYPE_VALUE));
  var offsetY=op.addInPort(new CABLES.Port(this,"offset Y",CABLES.OP_PORT_TYPE_VALUE));
  
  var colorize=op.addInPort(new CABLES.Port(this,"colorize",CABLES.OP_PORT_TYPE_VALUE,{display:'bool'}));
  var colorizeAdd=op.addInPort(new CABLES.Port(this,"colorize add",CABLES.OP_PORT_TYPE_VALUE,{display:'range'}));
  colorize.set(false);
  
  function updateColorize()
  {
      if(shader)
          if(colorize.get()) shader.define(id+'HEIGHTMAP_COLORIZE');
              else shader.removeDefine(id+'HEIGHTMAP_COLORIZE');
  }
  
  function updateInvert()
  {
      if(shader)
          if(invert.get()) shader.define(id+'HEIGHTMAP_INVERT');
              else shader.removeDefine(id+'HEIGHTMAP_INVERT');
  }
  
  colorize.onChange=updateColorize;
  invert.onChange=updateInvert;
  
  var meth=op.addInPort(new CABLES.Port(this,"mode",CABLES.OP_PORT_TYPE_VALUE,{display:'dropdown',
      values:['normal','mul xyz','add z','add y','mul y','sub z']}));
  
  
  removeZero.onChange=updateRemoveZero;
  
  function updateRemoveZero()
  {
      if(shader)
          if(removeZero.get()) shader.define(id+'DISPLACE_REMOVE_ZERO');
              else shader.removeDefine(id+'DISPLACE_REMOVE_ZERO');
  }
  
  var updateMethod=function()
  {
      if(shader)
      {
          if(flip.get()) shader.define(id+'FLIPY');
              else shader.removeDefine(id+'FLIPY');
  
          shader.removeDefine(id+'DISPLACE_METH_MULXYZ');
          shader.removeDefine(id+'DISPLACE_METH_ADDZ');
          shader.removeDefine(id+'DISPLACE_METH_ADDY');
          shader.removeDefine(id+'DISPLACE_METH_NORMAL');
  
          if(meth.get()=='mul xyz') shader.define(id+'DISPLACE_METH_MULXYZ');
          if(meth.get()=='add z') shader.define(id+'DISPLACE_METH_ADDZ');
          if(meth.get()=='add y') shader.define(id+'DISPLACE_METH_ADDY');
          if(meth.get()=='mul y') shader.define(id+'DISPLACE_METH_MULY');
          if(meth.get()=='normal') shader.define(id+'DISPLACE_METH_NORMAL');
  
          updateRemoveZero();
      }
  };
  
  flip.onChange=updateMethod;
  meth.onChange=updateMethod;
  meth.set('normal');
  
  var shader=null;
  var uniExtrude,uniTexture;
  
  
  var srcHeadVert=''
      .endl()+'UNI float {{mod}}_extrude;'
      .endl()+'UNI sampler2D {{mod}}_texture;'
      .endl()+'UNI float {{mod}}_offsetX;'
      .endl()+'UNI float {{mod}}_offsetY;'
  
      .endl()+'OUT float '+id+'displHeightMapColor;'
      .endl();
  
  var srcBodyVert=''
  
  
      .endl()+'vec2 {{mod}}tc=texCoord;'
      // .endl()+'vec2 {{mod}}tc=vec2(pos.x,pos.y);'
  
      .endl()+'#ifdef '+id+'FLIPY'
      .endl()+'    {{mod}}tc.y=1.0-{{mod}}tc.y;'
      .endl()+'#endif'
  
  
      .endl()+'float {{mod}}_texVal=texture2D( {{mod}}_texture, vec2({{mod}}tc.x+{{mod}}_offsetX,{{mod}}tc.y+{{mod}}_offsetY) ).b;'
  
      .endl()+'#ifdef '+id+'HEIGHTMAP_INVERT'
      .endl()+'{{mod}}_texVal=1.0-{{mod}}_texVal;'
      .endl()+'#endif'
  
      .endl()+'#ifdef '+id+'DISPLACE_METH_MULXYZ'
      .endl()+'   {{mod}}_texVal+=1.0;'
      .endl()+'   pos.xyz *= {{mod}}_texVal * {{mod}}_extrude;'
      // .endl()+'   norm=normalize(norm+normalize(pos.xyz+vec3({{mod}}_texVal)* {{mod}}_extrude));'
      .endl()+'#endif'
  
      .endl()+'#ifdef '+id+'DISPLACE_METH_ADDZ'
      .endl()+'   pos.z+=({{mod}}_texVal * {{mod}}_extrude);'
      .endl()+'#endif'
  
      .endl()+'#ifdef '+id+'DISPLACE_METH_ADDY'
      .endl()+'   pos.y+=({{mod}}_texVal * {{mod}}_extrude);'
      // .endl()+'norm=normalize(norm+normalize(vec3(0.0,0.0+({{mod}}_texVal ),0.0)));'
      .endl()+'#endif'
  
  
      .endl()+'#ifdef '+id+'DISPLACE_METH_MULY'
      .endl()+'   pos.y+=(({{mod}}_texVal-0.5) * {{mod}}_extrude);'
      .endl()+'#endif'
  
      .endl()+'#ifdef '+id+'DISPLACE_METH_NORMAL'
      .endl()+'   pos.xyz+=norm*{{mod}}_texVal*{{mod}}_extrude;'
      .endl()+'#endif'
  
  
      .endl()+''+id+'displHeightMapColor={{mod}}_texVal;'
  
      .endl();
  
  var srcHeadFrag=''
      .endl()+'UNI float {{mod}}_colorizeAdd;'
      .endl()+'IN float '+id+'displHeightMapColor;'
      .endl()+'UNI sampler2D {{mod}}_texture;'
      // .endl()+'IN vec3 vViewPosition;'
  
      .endl();
  
  var srcBodyFrag=''
  
      .endl()+'#ifdef '+id+'HEIGHTMAP_COLORIZE'
      .endl()+'   col.rgb*='+id+'displHeightMapColor*(1.0-{{mod}}_colorizeAdd);'
      .endl()+'   col+={{mod}}_colorizeAdd;'
      .endl()+'#endif'
  
      .endl()+'#ifdef '+id+'DISPLACE_REMOVE_ZERO'
      .endl()+'if('+id+'displHeightMapColor==0.0)discard;'
      .endl()+'#endif'
      .endl();
  
  
  var moduleFrag=null;
  var moduleVert=null;
  
  function removeModule()
  {
      if(shader && moduleFrag) shader.removeModule(moduleFrag);
      if(shader && moduleVert) shader.removeModule(moduleVert);
      shader=null;
  }
  
  
  var uniTexture=null;
  var uniTextureFrag=null;
  var uniExtrude=null;
  var uniOffsetX=null;
  var uniOffsetY=null;
  var uniColorizeAdd=null;
  
  op.render.onLinkChanged=removeModule;
  
  op.render.onTriggered=function()
  {
      if(!cgl.getShader())
      {
           op.trigger.trigger();
           return;
      }
  
  
      if(cgl.getShader()!=shader)
      {
          if(shader) removeModule();
  
  
          // console.log('re init shader module vertexdisplacement');
  
          shader=cgl.getShader();
  
  
          moduleVert=shader.addModule(
              {
                  title:op.objName,
                  name:'MODULE_VERTEX_POSITION',
                  srcHeadVert:srcHeadVert,
                  srcBodyVert:srcBodyVert
              });
  
          uniTexture=new CGL.Uniform(shader,'t',moduleVert.prefix+'_texture',0);
          uniExtrude=new CGL.Uniform(shader,'f',moduleVert.prefix+'_extrude',extrude);
          uniOffsetX=new CGL.Uniform(shader,'f',moduleVert.prefix+'_offsetX',offsetX);
          uniOffsetY=new CGL.Uniform(shader,'f',moduleVert.prefix+'_offsetY',offsetY);
  
          moduleFrag=shader.addModule(
              {
                  title:op.objName,
                  name:'MODULE_COLOR',
                  srcHeadFrag:srcHeadFrag,
                  srcBodyFrag:srcBodyFrag
              });
          uniTextureFrag=new CGL.Uniform(shader,'t',moduleFrag.prefix+'_texture',0);
          uniColorizeAdd=new CGL.Uniform(shader,'f',moduleFrag.prefix+'_colorizeAdd',colorizeAdd);
  
          updateMethod();
          updateInvert();
          updateColorize();
      }
  
  
      if(!shader)return;
      var texSlot=moduleVert.num+5;
  
      if(texture.get())
      {
          uniTexture.setValue(texSlot);
          uniTextureFrag.setValue(texSlot);
  
          cgl.setTexture(0+texSlot,texture.get().tex);
          // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, texture.get().tex);
      }
  
      op.trigger.trigger();
  };
  
  
  };
  
  Ops.Gl.ShaderEffects.VertexDisplacementMap.prototype = new CABLES.Op();
  CABLES.OPS["94ffa0de-2926-4d02-842e-a1ad6f5846c2"]={f:Ops.Gl.ShaderEffects.VertexDisplacementMap,objName:"Ops.Gl.ShaderEffects.VertexDisplacementMap"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Trigger.Sequence
  // 
  // **************************************************************
  
  Ops.Trigger.Sequence = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  
  const exe=op.inTrigger("exe");
  
  var exes=[];
  var triggers=[];
  
  var triggerAll=function()
  {
      for(var i=0;i<triggers.length;i++) triggers[i].trigger();
  };
  
  exe.onTriggered=triggerAll;
  
  var num=16;
  
  for(var i=0;i<num;i++)
  {
      triggers.push( op.addOutPort(new CABLES.Port(op,"trigger "+i,CABLES.OP_PORT_TYPE_FUNCTION)) );
      
      if(i<num-1)
      {
          var newExe=op.addInPort(new CABLES.Port(op,"exe "+i,CABLES.OP_PORT_TYPE_FUNCTION));
          newExe.onTriggered=triggerAll;
          exes.push( newExe );
      }
  }
  
  
  };
  
  Ops.Trigger.Sequence.prototype = new CABLES.Op();
  CABLES.OPS["641934f6-5143-4a6b-b592-08ab26e2cab0"]={f:Ops.Trigger.Sequence,objName:"Ops.Trigger.Sequence"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.TextureEffects.ImageCompose
  // 
  // **************************************************************
  
  Ops.Gl.TextureEffects.ImageCompose = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const render=op.inTrigger("render");
  const useVPSize=op.addInPort(new CABLES.Port(op,"use viewport size",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  const width=op.inValueInt("width");
  const height=op.inValueInt("height");
  
  const tfilter=op.inValueSelect("filter",['nearest','linear','mipmap'],"linear");
  const twrap=op.inValueSelect("wrap",['clamp to edge','repeat','mirrored repeat']);
  const fpTexture=op.inValueBool("HDR");
  
  const trigger=op.outTrigger("trigger");
  const texOut=op.outTexture("texture_out");
  
  const bgAlpha=op.inValueSlider("Background Alpha",1);
  const outRatio=op.outValue("Aspect Ratio");
  
  op.setPortGroup("Texture Size",[useVPSize,width,height]);
  op.setPortGroup("Texture Settings",[twrap,tfilter,fpTexture]);
  
  
  
  texOut.set(null);
  var cgl=op.patch.cgl;
  var effect=null;
  var tex=null;
  
  var w=8,h=8;
  var prevViewPort=[0,0,0,0];
  var reInitEffect=true;
  
  var bgFrag=''
      .endl()+'uniform float a;'
      .endl()+'void main()'
      .endl()+'{'
      .endl()+'   outColor= vec4(0.0,0.0,0.0,a);'
      .endl()+'}';
  var bgShader=new CGL.Shader(cgl,'imgcompose bg');
  bgShader.setSource(bgShader.getDefaultVertexShader(),bgFrag);
  var uniBgAlpha=new CGL.Uniform(bgShader,'f','a',bgAlpha);
  
  var selectedFilter=CGL.Texture.FILTER_LINEAR;
  var selectedWrap=CGL.Texture.WRAP_CLAMP_TO_EDGE;
  
  function initEffect()
  {
      if(effect)effect.delete();
      if(tex)tex.delete();
  
      effect=new CGL.TextureEffect(cgl,{"isFloatingPointTexture":fpTexture.get()});
  
      tex=new CGL.Texture(cgl,
          {
              "name":"image compose",
              "isFloatingPointTexture":fpTexture.get(),
              "filter":selectedFilter,
              "wrap":selectedWrap,
              "width": Math.ceil(width.get()),
              "height": Math.ceil(height.get()),
          });
  
      effect.setSourceTexture(tex);
      texOut.set(null);
      // texOut.set(effect.getCurrentSourceTexture());
  
      reInitEffect=false;
  
      // op.log("reinit effect");
      // tex.printInfo();
  }
  
  fpTexture.onChange=function()
  {
      reInitEffect=true;
  };
  
  function updateResolution()
  {
      if(!effect)initEffect();
  
      if(useVPSize.get())
      {
          w=cgl.getViewPort()[2];
          h=cgl.getViewPort()[3];
      }
      else
      {
          w=Math.ceil(width.get());
          h=Math.ceil(height.get());
      }
  
      if((w!=tex.width || h!= tex.height) && (w!==0 && h!==0))
      {
          height.set(h);
          width.set(w);
          tex.setSize(w,h);
          outRatio.set(w/h);
          effect.setSourceTexture(tex);
          texOut.set(null);
          texOut.set(tex);
      }
  
      if(texOut.get())
          if(!texOut.get().isPowerOfTwo() )
          {
              if(!op.uiAttribs.hint)
                  op.uiAttr(
                      {
                          hint:'texture dimensions not power of two! - texture filtering will not work.',
                          warning:null
                      });
          }
          else
          if(op.uiAttribs.hint)
          {
              op.uiAttr({hint:null,warning:null}); //todo only when needed...
          }
  
  }
  
  
  function updateSizePorts()
  {
      if(useVPSize.get())
      {
          width.setUiAttribs({greyout:true});
          height.setUiAttribs({greyout:true});
      }
      else
      {
          width.setUiAttribs({greyout:false});
          height.setUiAttribs({greyout:false});
      }
  }
  
  
  useVPSize.onChange=function()
  {
      updateSizePorts();
      if(useVPSize.get())
      {
          width.onChange=null;
          height.onChange=null;
      }
      else
      {
          width.onChange=updateResolution;
          height.onChange=updateResolution;
      }
      updateResolution();
  
  };
  
  
  op.preRender=function()
  {
      doRender();
      bgShader.bind();
  };
  
  
  var doRender=function()
  {
      if(!effect || reInitEffect)
      {
          initEffect();
      }
      var vp=cgl.getViewPort();
      prevViewPort[0]=vp[0];
      prevViewPort[1]=vp[1];
      prevViewPort[2]=vp[2];
      prevViewPort[3]=vp[3];
  
  
      cgl.gl.blendFunc(cgl.gl.SRC_ALPHA, cgl.gl.ONE_MINUS_SRC_ALPHA);
      // cgl.gl.blendFunc(cgl.gl.SRC_ALPHA,cgl.gl.ONE_MINUS_SRC_ALPHA);
  
  
  
      updateResolution();
  
      cgl.currentTextureEffect=effect;
      effect.setSourceTexture(tex);
  
      effect.startEffect();
  
      // render background color...
      cgl.setShader(bgShader);
      cgl.currentTextureEffect.bind();
      cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex );
      cgl.currentTextureEffect.finish();
      cgl.setPreviousShader();
  
      trigger.trigger();
  
      texOut.set(effect.getCurrentSourceTexture());
      // texOut.set(effect.getCurrentTargetTexture());
  
  
      // if(effect.getCurrentSourceTexture.filter==CGL.Texture.FILTER_MIPMAP)
      // {
      //         this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, effect.getCurrentSourceTexture.tex);
      //         effect.getCurrentSourceTexture.updateMipMap();
      //     // else
      //     // {
      //     //     this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, this._textureSource.tex);;
      //     //     this._textureSource.updateMipMap();
      //     // }
  
      //     this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, null);
      // }
  
      effect.endEffect();
  
      cgl.setViewPort(prevViewPort[0],prevViewPort[1],prevViewPort[2],prevViewPort[3]);
  
  
      cgl.gl.blendFunc(cgl.gl.SRC_ALPHA,cgl.gl.ONE_MINUS_SRC_ALPHA);
  
      cgl.currentTextureEffect=null;
  };
  
  
  function onWrapChange()
  {
      if(twrap.get()=='repeat') selectedWrap=CGL.Texture.WRAP_REPEAT;
      if(twrap.get()=='mirrored repeat') selectedWrap=CGL.Texture.WRAP_MIRRORED_REPEAT;
      if(twrap.get()=='clamp to edge') selectedWrap=CGL.Texture.WRAP_CLAMP_TO_EDGE;
  
      reInitEffect=true;
      updateResolution();
  }
  
  twrap.set('clamp to edge');
  twrap.onChange=onWrapChange;
  
  function onFilterChange()
  {
      if(tfilter.get()=='nearest') selectedFilter=CGL.Texture.FILTER_NEAREST;
      if(tfilter.get()=='linear')  selectedFilter=CGL.Texture.FILTER_LINEAR;
      if(tfilter.get()=='mipmap')  selectedFilter=CGL.Texture.FILTER_MIPMAP;
  
      reInitEffect=true;
      updateResolution();
      // effect.setSourceTexture(tex);
      // updateResolution();
  }
  
  tfilter.set('linear');
  tfilter.onChange=onFilterChange;
  
  useVPSize.set(true);
  render.onTriggered=doRender;
  
  width.set(640);
  height.set(360);
  updateSizePorts();
  
  };
  
  Ops.Gl.TextureEffects.ImageCompose.prototype = new CABLES.Op();
  CABLES.OPS["5c04608d-1e42-4e36-be00-1be4a81fc309"]={f:Ops.Gl.TextureEffects.ImageCompose,objName:"Ops.Gl.TextureEffects.ImageCompose"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Anim.Timer2
  // 
  // **************************************************************
  
  Ops.Anim.Timer2 = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const playPause=op.inValueBool("Play",true);
  const reset=op.inTriggerButton("Reset");
  const outTime=op.outValue("Time");
  const inSpeed=op.inValue("Speed",1);
  
  var timer=new CABLES.Timer();
  var lastTime=0;
  var time=0;
  
  playPause.onChange=setState;
  setState();
  
  function setState()
  {
      if(playPause.get())
      {
          timer.play();
          op.patch.addOnAnimFrame(op);
      }
      else
      {
          timer.pause();
          op.patch.removeOnAnimFrame(op);
      }
  }
  
  reset.onTriggered=function()
  {
      time=0;
      lastTime=0;
      timer.setTime(0);
      outTime.set(0);
  };
  
  op.onAnimFrame=function()
  {
      if(timer.isPlaying())
      {
          timer.update();
          if(lastTime===0)
          {
              lastTime=timer.get();
              return;
          }
  
          const t=timer.get()-lastTime;
          lastTime=timer.get();
          time+=t*inSpeed.get();
          if(time!=time)time=0;
          outTime.set(time);
      }
  };
  
  
  };
  
  Ops.Anim.Timer2.prototype = new CABLES.Op();
  CABLES.OPS["aac7f721-208f-411a-adb3-79adae2e471a"]={f:Ops.Anim.Timer2,objName:"Ops.Anim.Timer2"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.Meshes.LinesArray
  // 
  // **************************************************************
  
  Ops.Gl.Meshes.LinesArray = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  var render=op.inTrigger('render');
  var width=op.addInPort(new CABLES.Port(op,"width"),10);
  var height=op.addInPort(new CABLES.Port(op,"height"),1);
  var doLog=op.inValueBool("Logarithmic",false);
  var pivotX=op.inValueSelect("pivot x",["center","left","right"]);
  var pivotY=op.inValueSelect("pivot y",["center","top","bottom"]);
  
  var nColumns=op.addInPort(new CABLES.Port(op,"num columns"),10);
  var nRows=op.addInPort(new CABLES.Port(op,"num rows"),10);
  
  var axis=op.inValueSelect("axis",["xy","xz"]);
  var trigger=op.outTrigger('trigger');
  var outPointArrays=op.outArray("Point Arrays");
  
  var cgl=op.patch.cgl;
  axis.set('xy');
  pivotX.set('center');
  pivotY.set('center');
  
  var meshes=[];
  
  op.setPortGroup("Size",[width,height]);
  op.setPortGroup("Alignment",[pivotX,pivotY]);
  
  axis.onChange=
      pivotX.onChange=
      pivotY.onChange=
      width.onChange=
      height.onChange=
      nRows.onChange=
      nColumns.onChange=
      doLog.onChange=rebuildDelayed;
  
  
  
  rebuild();
  
  render.onTriggered=function()
  {
      for(var i=0;i<meshes.length;i++) meshes[i].render(cgl.getShader());
      trigger.trigger();
  };
  
  var delayRebuild=0;
  function rebuildDelayed()
  {
      clearTimeout(delayRebuild);
      delayRebuild=setTimeout(rebuild,60);
  }
  
  function rebuild()
  {
      var x=0;
      var y=0;
  
      if(pivotX.get()=='center') x=0;
      if(pivotX.get()=='right') x=-width.get()/2;
      if(pivotX.get()=='left') x=+width.get()/2;
  
      if(pivotY.get()=='center') y=0;
      if(pivotY.get()=='top') y=-height.get()/2;
      if(pivotY.get()=='bottom') y=+height.get()/2;
  
      var numRows=parseInt(nRows.get(),10);
      var numColumns=parseInt(nColumns.get(),10);
  
      var stepColumn=width.get()/numColumns;
      var stepRow=height.get()/numRows;
  
      var c,r;
      meshes.length=0;
  
      var vx,vy,vz;
      var verts=[];
      var tc=[];
      var indices=[];
      var count=0;
  
      function addMesh()
      {
          var geom=new CGL.Geometry();
          geom.vertices=verts;
          geom.texCoords=tc;
          geom.verticesIndices=indices;
  
          var mesh=new CGL.Mesh(cgl, geom, cgl.gl.LINES);
          mesh.setGeom(geom);
          meshes.push(mesh);
  
          verts.length=0;
          tc.length=0;
          indices.length=0;
          count=0;
          lvx=null;
      }
  
      var min=Math.log(1/numRows);
      var max=Math.log(1);
      // op.log(min,max);
  
      var lines=[];
  
      for(r=numRows;r>=0;r--)
      {
          // op.log(r/numRows);
          var lvx=null,lvy=null,lvz=null;
          var ltx=null,lty=null;
          var log=0;
          var doLoga=doLog.get();
  
          var linePoints=[];
          lines.push(linePoints);
  
  
          for(c=numColumns;c>=0;c--)
          {
              vx = c * stepColumn - width.get()  / 2 + x;
              if(doLoga)
                  vy=(Math.log((r/numRows) )/min)*height.get() - height.get() /2+y;
              else
                  vy = r * stepRow    - height.get() / 2 + y;
  
              var tx = c/numColumns;
              var ty = 1.0-r/numRows;
              if(doLoga) ty = (Math.log((r/numRows) )/min);
  
              vz=0.0;
  
              if(axis.get()=='xz')
              {
                  vz=vy;
                  vy= 0.0 ;
              }
              if(axis.get()=='xy') vz=0.0;
  
              if(lvx!==null)
              {
                  verts.push( lvx );
                  verts.push( lvy );
                  verts.push( lvz );
  
                  linePoints.push(lvx,lvy,lvz);
  
                  verts.push( vx );
                  verts.push( vy );
                  verts.push( vz );
  
                  tc.push( ltx );
                  tc.push( lty );
  
                  tc.push( tx );
                  tc.push( ty );
  
                  indices.push(count);
                  count++;
                  indices.push(count);
                  count++;
              }
  
              if(count>64000)
              {
                  addMesh();
              }
  
              ltx=tx;
              lty=ty;
  
              lvx=vx;
              lvy=vy;
              lvz=vz;
          }
      }
  
      outPointArrays.set(lines);
  
      addMesh();
  
      // op.log(meshes.length,' meshes');
  
  
  }
  
  
  };
  
  Ops.Gl.Meshes.LinesArray.prototype = new CABLES.Op();
  CABLES.OPS["a75265c2-957b-4719-9d03-7bbf00ace364"]={f:Ops.Gl.Meshes.LinesArray,objName:"Ops.Gl.Meshes.LinesArray"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.TextureEffects.Noise.FBMNoise
  // 
  // **************************************************************
  
  Ops.Gl.TextureEffects.Noise.FBMNoise = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={fbmnoise_frag:"UNI sampler2D tex;\nUNI float anim;\n\nUNI float scale;\nUNI float repeat;\n\nUNI float scrollX;\nUNI float scrollY;\n\nUNI float amount;\n\nUNI bool layer1;\nUNI bool layer2;\nUNI bool layer3;\nUNI bool layer4;\n\nUNI float aspect;\n\nIN vec2 texCoord;\n\n\n{{BLENDCODE}}\n\n// adapted from warp shader by inigo quilez/iq\n// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\n\n// See here for a tutorial on how to make this: http://www.iquilezles.org/www/articles/warp/warp.htm\n\nconst mat2 m = mat2( 0.80,  0.60, -0.60,  0.80 );\n\nfloat noise( in vec2 x )\n{\n\treturn sin(1.5*x.x)*sin(1.5*x.y);\n}\n\nfloat fbm4( vec2 p )\n{\n    float f = 0.0;\n    f += 0.5000*noise( p ); p = m*p*2.02;\n    f += 0.2500*noise( p ); p = m*p*2.03;\n    f += 0.1250*noise( p ); p = m*p*2.01;\n    f += 0.0625*noise( p );\n    return f/0.9375;\n}\n\nfloat fbm6( vec2 p )\n{\n    float f = 0.0;\n    f += 0.500000*(0.5+0.5*noise( p )); p = m*p*2.02;\n    f += 0.250000*(0.5+0.5*noise( p )); p = m*p*2.03;\n    f += 0.125000*(0.5+0.5*noise( p )); p = m*p*2.01;\n    f += 0.062500*(0.5+0.5*noise( p )); p = m*p*2.04;\n    f += 0.031250*(0.5+0.5*noise( p )); p = m*p*2.01;\n    f += 0.015625*(0.5+0.5*noise( p ));\n    return f/0.96875;\n}\n\nvoid main()\n{\n    // vec4 col=texture(tex,texCoord+2.0*fbm4(texCoord+2.0*fbm6(texCoord+anim)));\n\n    vec2 tc=texCoord;\n\t#ifdef DO_TILEABLE\n\t    tc=abs(texCoord-0.5);\n\t#endif\n\n\n    vec2 p=(tc-0.5)*scale;\n\n\n    p.y/=aspect;\n    vec2 q = vec2( fbm4( p + vec2(0.3+scrollX,0.20+scrollY) ),\n                   fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n    vec2 q2 = vec2( fbm4( p + vec2(2.0+scrollX,1.0+scrollY) ),\n                   fbm4( p + vec2(3.1+scrollX,1.3+scrollY) ) );\n\n    vec2 q3 = vec2( fbm4( p + vec2(9.0+scrollX,4.0+scrollY) ),\n                   fbm4( p + vec2(3.1+scrollX,4.3+scrollY) ) );\n\n\n\n    float v= fbm4( ( p + 4.0*q +anim*0.1)*repeat);\n    float v2= fbm4( (p + 4.0*q2 +anim*0.1)*repeat );\n\n    float v3= fbm6( (p + 4.0*q3 +anim*0.1)*repeat );\n    float v4= fbm6( (p + 4.0*q2 +anim*0.1)*repeat );\n\n\n    // vec4 col=vec4( vec3( fbm4(( texCoord + fbm6(texCoord*anim) ) )) ,1.0 );\n    // outColor = vec4(vec3(fbm6(texCoord)),1.0);\n\n\n    vec4 base=texture(tex,texCoord);\n\n    vec4 finalColor;\n    float colVal=0.0;\n    float numLayers=0.0;\n\n    if(layer1)\n    {\n        colVal+=v;\n        numLayers++;\n    }\n\n    if(layer2)\n    {\n        colVal+=v2;\n        numLayers++;\n    }\n\n    if(layer3)\n    {\n        colVal+=v3;\n        numLayers++;\n    }\n\n    if(layer4)\n    {\n        colVal+=v4;\n        numLayers++;\n    }\n\n\n    // finalColor=vec4( vec3(v3)*1.0,1.0);\n    // finalColor=vec4( vec3(v3+v2)/2.0,1.0);\n    // finalColor=vec4( vec3(v+v2+v4+v3)/4.0,1.0);\n\n    finalColor=vec4( vec3(colVal/numLayers),1.0);\n\n\n    finalColor = vec4( _blend( base.rgb, finalColor.rgb ) ,1.0);\n    finalColor = vec4( mix( finalColor.rgb, base.rgb ,1.0-base.a*amount),1.0);\n\n\n    outColor = finalColor;\n\n\n    // outColor=vec4( v2,v,v4,1.0);\n    // outColor=vec4( vec3(q.x+q.y),1.0);\n\n    // outColor=texture(tex,vec2(v,v2));\n}\n",};
  const
      render=op.inTrigger("render"),
      blendMode=CGL.TextureEffect.AddBlendSelect(op,"Blend Mode","normal"),
      amount=op.inValueSlider("Amount",1),
      trigger=op.outTrigger("trigger");
  
  const cgl=op.patch.cgl;
  const shader=new CGL.Shader(cgl);
  
  var srcFrag=attachments.fbmnoise_frag.replace('{{BLENDCODE}}',CGL.TextureEffect.getBlendCode());
  
  shader.setSource(shader.getDefaultVertexShader(),srcFrag );
  var textureUniform=new CGL.Uniform(shader,'t','tex',0);
  
  var uniScale=new CGL.Uniform(shader,'f','scale',op.inValue("scale",2));
  var uniAnim=new CGL.Uniform(shader,'f','anim',op.inValue("anim",0));
  var uniScrollX=new CGL.Uniform(shader,'f','scrollX',op.inValue("scrollX",9));
  var uniScrollY=new CGL.Uniform(shader,'f','scrollY',op.inValue("scrollY",0));
  var uniRepeat=new CGL.Uniform(shader,'f','repeat',op.inValue("repeat",1));
  var uniAspect=new CGL.Uniform(shader,'f','aspect',op.inValue("aspect",1));
  
  var uniLayer1=new CGL.Uniform(shader,'b','layer1',op.inValueBool("Layer 1",true));
  var uniLayer2=new CGL.Uniform(shader,'b','layer2',op.inValueBool("Layer 2",true));
  var uniLayer3=new CGL.Uniform(shader,'b','layer3',op.inValueBool("Layer 3",true));
  var uniLayer4=new CGL.Uniform(shader,'b','layer4',op.inValueBool("Layer 4",true));
  
  var amountUniform=new CGL.Uniform(shader,'f','amount',amount);
  
  var tile=op.inValueBool("Tileable",false);
  tile.onChange=updateTileable;
  function updateTileable()
  {
      if(tile.get())shader.define("DO_TILEABLE");
          else shader.removeDefine("DO_TILEABLE");
  }
  
  CGL.TextureEffect.setupBlending(op,shader,blendMode,amount);
  
  render.onTriggered=function()
  {
      if(!CGL.TextureEffect.checkOpInEffect(op)) return;
  
      cgl.setShader(shader);
      cgl.currentTextureEffect.bind();
  
      uniAspect.set( cgl.currentTextureEffect.getCurrentSourceTexture().width / cgl.currentTextureEffect.getCurrentSourceTexture().height );
  
      cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex );
  
      cgl.currentTextureEffect.finish();
      cgl.setPreviousShader();
  
      trigger.trigger();
  };
  
  
  };
  
  Ops.Gl.TextureEffects.Noise.FBMNoise.prototype = new CABLES.Op();
  CABLES.OPS["7073186c-b776-48c2-a01e-041df88ad88a"]={f:Ops.Gl.TextureEffects.Noise.FBMNoise,objName:"Ops.Gl.TextureEffects.Noise.FBMNoise"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Math.Multiply
  // 
  // **************************************************************
  
  Ops.Math.Multiply = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const number1=op.inValueFloat("number1");
  const number2=op.inValueFloat("number2");
  const result=op.outValue("result");
  
  number1.set(1);
  number2.set(2);
  
  number1.onChange=update;
  number2.onChange=update;
  
  function update()
  {
      const n1=number1.get();
      const n2=number2.get();
  
      if(isNaN(n1))n1=0;
      if(isNaN(n2))n2=0;
  
      result.set( n1*n2 );
  }
  
  
  
  };
  
  Ops.Math.Multiply.prototype = new CABLES.Op();
  CABLES.OPS["1bbdae06-fbb2-489b-9bcc-36c9d65bd441"]={f:Ops.Math.Multiply,objName:"Ops.Math.Multiply"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.TextureEffects.Noise.Noise
  // 
  // **************************************************************
  
  Ops.Gl.TextureEffects.Noise.Noise = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={noise_frag:"IN vec2 texCoord;\nUNI sampler2D tex;\nUNI float amount;\nUNI float time;\n\n{{BLENDCODE}}\n{{MODULES_HEAD}}\n\n{{CGL.RANDOM_TEX}}\n\nvoid main()\n{\n    vec4 rnd;\n\n    #ifndef RGB\n        float r=cgl_random(texCoord.xy+vec2(time));\n        rnd=vec4( r,r,r,1.0 );\n    #endif\n\n    #ifdef RGB\n        rnd=vec4(cgl_random3(texCoord.xy+vec2(time)),1.0);\n    #endif\n\n    vec4 base=texture(tex,texCoord);\n    vec4 col=vec4( _blend(base.rgb,rnd.rgb) ,1.0);\n\n    outColor=vec4( mix( col.rgb, base.rgb ,1.0-base.a*amount),1.0);\n}",};
  const
      render=op.inTrigger('Render'),
      blendMode=CGL.TextureEffect.AddBlendSelect(op,"Blend Mode","normal"),
      amount=op.inValueSlider("Amount",1),
      animated=op.inValueBool("Animated",true),
      inRGB=op.inValueBool("RGB",false),
      trigger=op.outTrigger("Next");
  
  const
      cgl=op.patch.cgl,
      shader=new CGL.Shader(cgl),
      amountUniform=new CGL.Uniform(shader,'f','amount',amount),
      timeUniform=new CGL.Uniform(shader,'f','time',1.0),
      textureUniform=new CGL.Uniform(shader,'t','tex',0),
      srcFrag=attachments.noise_frag.replace('{{BLENDCODE}}',CGL.TextureEffect.getBlendCode());
  
  shader.setSource(shader.getDefaultVertexShader(),srcFrag);
  
  CGL.TextureEffect.setupBlending(op,shader,blendMode,amount);
  
  op.toWorkPortsNeedToBeLinked(render);
  
  inRGB.onChange=function()
  {
      if(inRGB.get())shader.define("RGB");
      else shader.removeDefine("RGB");
  };
  
  shader.bindTextures=function()
  {
      cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex );
  };
  
  render.onTriggered=function()
  {
      if(!CGL.TextureEffect.checkOpInEffect(op)) return;
  
      if(animated.get()) timeUniform.setValue(op.patch.freeTimer.get()/1000%100);
          else timeUniform.setValue(0);
  
      cgl.setShader(shader);
      cgl.currentTextureEffect.bind();
  
      cgl.currentTextureEffect.finish();
      cgl.setPreviousShader();
  
      trigger.trigger();
  };
  
  
  
  };
  
  Ops.Gl.TextureEffects.Noise.Noise.prototype = new CABLES.Op();
  CABLES.OPS["81253441-cc73-42fa-b903-6d23806873d9"]={f:Ops.Gl.TextureEffects.Noise.Noise,objName:"Ops.Gl.TextureEffects.Noise.Noise"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.TextureEffects.ChromaticAberration
  // 
  // **************************************************************
  
  Ops.Gl.TextureEffects.ChromaticAberration = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={chromatic_frag:"\nIN vec2 texCoord;\nUNI sampler2D tex;\nUNI float pixel;\nUNI float onePixel;\nUNI float amount;\nUNI float lensDistort;\n\n#ifdef MASK\nUNI sampler2D texMask;\n#endif\n\n{{CGL.BLENDMODES}}\n\nvoid main()\n{\n   vec4 base=texture(tex,texCoord);\n   vec4 col=texture(tex,texCoord);\n\n   vec2 tc=texCoord;;\n   float pix = pixel;\n   if(lensDistort>0.0)\n   {\n       float dist = distance(texCoord, vec2(0.5,0.5));\n       tc-=0.5;\n       tc *=smoothstep(-0.9,1.0*lensDistort,1.0-dist);\n       tc+=0.5;\n   }\n\n    #ifdef MASK\n        vec4 m=texture(texMask,texCoord);\n        pix*=m.r*m.a;\n    #endif\n\n    #ifdef SMOOTH\n    #ifdef WEBGL2\n        float numSamples=round(pix/onePixel/4.0+1.0);\n        col.r=0.0;\n        col.b=0.0;\n\n        for(float off=0.0;off<numSamples;off++)\n        {\n            float diff=(pix/numSamples)*off;\n            col.r+=texture(tex,vec2(tc.x+diff,tc.y)).r/numSamples;\n            col.b+=texture(tex,vec2(tc.x-diff,tc.y)).b/numSamples;\n        }\n    #endif\n    #endif\n\n    #ifndef SMOOTH\n        col.r=texture(tex,vec2(tc.x+pix,tc.y)).r;\n        col.b=texture(tex,vec2(tc.x-pix,tc.y)).b;\n    #endif\n\n//   outColor = col;\n   outColor= cgl_blend(base,col,amount);\n\n}\n",};
  const
      render=op.inTrigger('render'),
      blendMode=CGL.TextureEffect.AddBlendSelect(op,"Blend Mode","normal"),
      amount=op.inValueSlider("Amount",1),
      pixel=op.inValue("Pixel",5),
      lensDistort=op.inValueSlider("Lens Distort",0),
      doSmooth=op.inValueBool("Smooth",false),
      textureMask=op.inTexture("Mask"),
      trigger=op.outTrigger('trigger');
  
  const cgl=op.patch.cgl;
  const shader=new CGL.Shader(cgl);
  
  CGL.TextureEffect.setupBlending(op,shader,blendMode,amount);
  
  shader.setSource(shader.getDefaultVertexShader(),attachments.chromatic_frag);
  const textureUniform=new CGL.Uniform(shader,'t','tex',0),
      uniPixel=new CGL.Uniform(shader,'f','pixel',0),
      uniOnePixel=new CGL.Uniform(shader,'f','onePixel',0),
      unitexMask=new CGL.Uniform(shader,'t','texMask',1),
      uniAmount=new CGL.Uniform(shader,'f','amount',amount),
      unilensDistort=new CGL.Uniform(shader,'f','lensDistort',lensDistort);
  
  doSmooth.onChange=function()
  {
      if(doSmooth.get())shader.define("SMOOTH");
          else shader.removeDefine("SMOOTH");
  };
  
  textureMask.onChange=function()
  {
      if(textureMask.get())shader.define("MASK");
          else shader.removeDefine("MASK");
  };
  
  render.onTriggered=function()
  {
      if(!CGL.TextureEffect.checkOpInEffect(op)) return;
  
      var texture=cgl.currentTextureEffect.getCurrentSourceTexture();
  
      uniPixel.setValue(pixel.get()*(1/texture.width));
      uniOnePixel.setValue(1/texture.width);
  
      cgl.setShader(shader);
      cgl.currentTextureEffect.bind();
  
      cgl.setTexture(0, texture.tex );
  
      if(textureMask.get()) cgl.setTexture(1, textureMask.get().tex );
  
      cgl.currentTextureEffect.finish();
      cgl.setPreviousShader();
  
      trigger.trigger();
  };
  
  
  };
  
  Ops.Gl.TextureEffects.ChromaticAberration.prototype = new CABLES.Op();
  CABLES.OPS["38ac43a1-1757-48f4-9450-29f07ac0d376"]={f:Ops.Gl.TextureEffects.ChromaticAberration,objName:"Ops.Gl.TextureEffects.ChromaticAberration"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.Meshes.FullscreenRectangle
  // 
  // **************************************************************
  
  Ops.Gl.Meshes.FullscreenRectangle = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={shader_frag:"\nUNI sampler2D tex;\nIN vec2 texCoord;\n\nvoid main()\n{\n   outColor= texture(tex,vec2(texCoord.x,(1.0-texCoord.y)));\n}\n",shader_vert:"{{MODULES_HEAD}}\n\nIN vec3 vPosition;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nOUT vec2 texCoord;\nIN vec2 attrTexCoord;\n\nvoid main()\n{\n   vec4 pos=vec4(vPosition,  1.0);\n\n   texCoord=attrTexCoord;\n\n   gl_Position = projMatrix * mvMatrix * pos;\n}\n",};
  const
      render=op.inTrigger('render'),
      centerInCanvas=op.inValueBool("Center in Canvas"),
      flipY=op.inValueBool("Flip Y"),
      inTexture=op.inTexture("Texture"),
      trigger=op.outTrigger('trigger');
  
  const cgl=op.patch.cgl;
  var mesh=null;
  var geom=new CGL.Geometry("fullscreen rectangle");
  var x=0,y=0,z=0,w=0,h=0;
  
  centerInCanvas.onChange=rebuild;
  flipY.onChange=rebuild;
  
  var shader=null;
  render.onTriggered=doRender;
  
  op.toWorkPortsNeedToBeLinked(render);
  
  inTexture.onChange=function()
  {
      var tex=inTexture.get();
      // shader=null;
      if(tex && !shader)
      {
          shader=new CGL.Shader(cgl,'fullscreenrectangle');
          shader.setModules(['MODULE_VERTEX_POSITION','MODULE_COLOR','MODULE_BEGIN_FRAG']);
  
          shader.setSource(attachments.shader_vert,attachments.shader_frag);
          shader.fullscreenRectUniform=new CGL.Uniform(shader,'t','tex',0);
      }
  
      if(!tex)
      {
          shader=null;
      }
  };
  
  op.preRender=function()
  {
      if(shader)shader.bind();
      if(mesh)mesh.render(shader);
      doRender();
  };
  
  function doRender()
  {
      if( cgl.getViewPort()[2]!=w || cgl.getViewPort()[3]!=h ) rebuild();
  
      cgl.pushPMatrix();
      mat4.identity(cgl.pMatrix);
      mat4.ortho(cgl.pMatrix, 0, w,h, 0, -10.0, 1000);
  
      cgl.pushModelMatrix();
      mat4.identity(cgl.mMatrix);
  
      cgl.pushViewMatrix();
      mat4.identity(cgl.vMatrix);
  
      if(centerInCanvas.get())
      {
          var x=0;
          var y=0;
          if(w<cgl.canvasWidth) x=(cgl.canvasWidth-w)/2;
          if(h<cgl.canvasHeight) y=(cgl.canvasHeight-h)/2;
  
          cgl.setViewPort(x,y,w,h);
      }
  
      if(shader)
      {
          if(inTexture.get())
          {
              cgl.setTexture(0,inTexture.get().tex);
              // cgl.gl.bindTexture(cgl.gl.TEXTURE_2D, inTexture.get().tex);
          }
  
          mesh.render(shader);
      }
      else
      {
          mesh.render(cgl.getShader());
      }
  
      cgl.gl.clear(cgl.gl.DEPTH_BUFFER_BIT);
  
      cgl.popPMatrix();
      cgl.popModelMatrix();
      cgl.popViewMatrix();
  
      trigger.trigger();
  }
  
  
  function rebuild()
  {
      const currentViewPort=cgl.getViewPort();
      if(currentViewPort[2]==w && currentViewPort[3]==h)return;
  
      var xx=0,xy=0;
  
      w=currentViewPort[2];
      h=currentViewPort[3];
  
      geom.vertices = new Float32Array([
           xx+w, xy+h,  0.0,
           xx,   xy+h,  0.0,
           xx+w, xy,    0.0,
           xx,   xy,    0.0
      ]);
  
      if(flipY.get())
          geom.setTexCoords( new Float32Array([
               1.0, 0.0,
               0.0, 0.0,
               1.0, 1.0,
               0.0, 1.0
          ]));
      else
          geom.setTexCoords(new Float32Array([
               1.0, 1.0,
               0.0, 1.0,
               1.0, 0.0,
               0.0, 0.0
          ]));
  
      geom.verticesIndices = new Float32Array([
          2, 1, 0,
          3, 1, 2
      ]);
  
  
      geom.vertexNormals=new Float32Array([
          0,0,1,
          0,0,1,
          0,0,1,
          0,0,1,
          ]);
      geom.tangents=new Float32Array([
          -1,0,0,
          -1,0,0,
          -1,0,0,
          -1,0,0]);
      geom.biTangents==new Float32Array([
          0,-1,0,
          0,-1,0,
          0,-1,0,
          0,-1,0]);
  
                  // norms.push(0,0,1);
                  // tangents.push(-1,0,0);
                  // biTangents.push(0,-1,0);
  
  
      if(!mesh) mesh=new CGL.Mesh(cgl,geom);
          else mesh.setGeom(geom);
  }
  
  
  };
  
  Ops.Gl.Meshes.FullscreenRectangle.prototype = new CABLES.Op();
  CABLES.OPS["255bd15b-cc91-4a12-9b4e-53c710cbb282"]={f:Ops.Gl.Meshes.FullscreenRectangle,objName:"Ops.Gl.Meshes.FullscreenRectangle"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.TextureEffects.DrawImage
  // 
  // **************************************************************
  
  Ops.Gl.TextureEffects.DrawImage = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={drawimage_frag:"#ifdef HAS_TEXTURES\n  IN vec2 texCoord;\n  UNI sampler2D tex;\n  UNI sampler2D image;\n#endif\n\nIN mat3 transform;\nUNI float rotate;\n{{BLENDCODE}}\n\n#ifdef HAS_TEXTUREALPHA\n   UNI sampler2D imageAlpha;\n#endif\n\nUNI float amount;\n\nvoid main()\n{\n   vec4 blendRGBA=vec4(0.0,0.0,0.0,1.0);\n   #ifdef HAS_TEXTURES\n       vec2 tc=texCoord;\n\n       #ifdef TEX_FLIP_X\n           tc.x=1.0-tc.x;\n       #endif\n       #ifdef TEX_FLIP_Y\n           tc.y=1.0-tc.y;\n       #endif\n\n       #ifdef TEX_TRANSFORM\n           vec3 coordinates=vec3(tc.x, tc.y,1.0);\n           tc=(transform * coordinates ).xy;\n       #endif\n\n       blendRGBA=texture(image,tc);\n\n       vec3 blend=blendRGBA.rgb;\n       vec4 baseRGBA=texture(tex,texCoord);\n       vec3 base=baseRGBA.rgb;\n\n       vec3 colNew=_blend(base,blend);\n\n       #ifdef REMOVE_ALPHA_SRC\n           blendRGBA.a=1.0;\n       #endif\n\n       #ifdef HAS_TEXTUREALPHA\n           vec4 colImgAlpha=texture(imageAlpha,texCoord);\n           float colImgAlphaAlpha=colImgAlpha.a;\n\n           #ifdef ALPHA_FROM_LUMINANCE\n               vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), colImgAlpha.rgb ));\n               colImgAlphaAlpha=(gray.r+gray.g+gray.b)/3.0;\n           #endif\n\n           blendRGBA.a=colImgAlphaAlpha*blendRGBA.a;\n           \n           #ifdef INVERT_ALPHA\n           blendRGBA.a=1.0-blendRGBA.a;\n           #endif\n       #endif\n\n\n   #endif\n\n   blendRGBA.rgb=mix( colNew, base ,1.0-blendRGBA.a*amount);\n   blendRGBA.a=1.0;\n\n\n   outColor= blendRGBA;\n}",drawimage_vert:"IN vec3 vPosition;\nIN vec2 attrTexCoord;\nIN vec3 attrVertNormal;\nOUT vec2 texCoord;\nOUT vec3 norm;\nUNI mat4 projMatrix;\nUNI mat4 mvMatrix;\n\nUNI float posX;\nUNI float posY;\nUNI float scale;\nUNI float rotate;\n\nOUT mat3 transform;\n\nvoid main()\n{\n    texCoord=attrTexCoord;\n    norm=attrVertNormal;\n\n    #ifdef TEX_TRANSFORM\n    vec3 coordinates=vec3(attrTexCoord.x, attrTexCoord.y,1.0);\n    float angle = radians( rotate );\n    vec2 scale= vec2(scale,scale);\n    vec2 translate= vec2(posX,posY);\n\n    transform = mat3(   scale.x * cos( angle ), scale.x * sin( angle ), 0.0,\n                        - scale.y * sin( angle ), scale.y * cos( angle ), 0.0,\n                        - 0.5 * scale.x * cos( angle ) + 0.5 * scale.y * sin( angle ) - 0.5 * translate.x*2.0 + 0.5,  - 0.5 * scale.x * sin( angle ) - 0.5 * scale.y * cos( angle ) - 0.5 * translate.y*2.0 + 0.5, 1.0);\n    #endif\n\n    gl_Position = projMatrix * mvMatrix * vec4(vPosition,  1.0);\n}",};
  var render=op.inTrigger('render');
  var amount=op.addInPort(new CABLES.Port(op,"amount",CABLES.OP_PORT_TYPE_VALUE,{ display:'range' }));
  var blendMode=CGL.TextureEffect.AddBlendSelect(op,"blendMode");
  
  var image=op.addInPort(new CABLES.Port(op,"image",CABLES.OP_PORT_TYPE_TEXTURE,{preview:true }));
  
  var imageAlpha=op.addInPort(new CABLES.Port(op,"imageAlpha",CABLES.OP_PORT_TYPE_TEXTURE,{preview:true }));
  var alphaSrc=op.inValueSelect("alphaSrc",['alpha channel','luminance']);
  var removeAlphaSrc=op.addInPort(new CABLES.Port(op,"removeAlphaSrc",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  
  var invAlphaChannel=op.addInPort(new CABLES.Port(op,"invert alpha channel",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  
  
  var trigger=op.outTrigger('trigger');
  
  op.toWorkPortsNeedToBeLinked(image);
  
  
  blendMode.set('normal');
  var cgl=op.patch.cgl;
  var shader=new CGL.Shader(cgl,'drawimage');
  
  amount.set(1.0);
  
  render.onTriggered=doRender;
  
  var srcFrag=attachments.drawimage_frag.replace('{{BLENDCODE}}',CGL.TextureEffect.getBlendCode());
  
  shader.setSource(attachments.drawimage_vert,srcFrag);
  var textureUniform=new CGL.Uniform(shader,'t','tex',0);
  var textureDisplaceUniform=new CGL.Uniform(shader,'t','image',1);
  var textureAlpha=new CGL.Uniform(shader,'t','imageAlpha',2);
  
  invAlphaChannel.onChange=function()
  {
      if(invAlphaChannel.get()) shader.define('INVERT_ALPHA');
          else shader.removeDefine('INVERT_ALPHA');
  };
  
  removeAlphaSrc.onChange=function()
  {
      if(removeAlphaSrc.get()) shader.define('REMOVE_ALPHA_SRC');
          else shader.removeDefine('REMOVE_ALPHA_SRC');
  };
  removeAlphaSrc.set(true);
  
  alphaSrc.onChange=function()
  {
      if(alphaSrc.get()=='luminance') shader.define('ALPHA_FROM_LUMINANCE');
          else shader.removeDefine('ALPHA_FROM_LUMINANCE');
  };
  
  alphaSrc.set("alpha channel");
  
  
  {
      //
      // texture flip
      //
      var flipX=op.addInPort(new CABLES.Port(op,"flip x",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
      var flipY=op.addInPort(new CABLES.Port(op,"flip y",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  
      flipX.onChange=function()
      {
          if(flipX.get()) shader.define('TEX_FLIP_X');
              else shader.removeDefine('TEX_FLIP_X');
      };
  
      flipY.onChange=function()
      {
          if(flipY.get()) shader.define('TEX_FLIP_Y');
              else shader.removeDefine('TEX_FLIP_Y');
      };
  }
  
  {
      //
      // texture transform
      //
      var scale=op.addInPort(new CABLES.Port(op,"scale",CABLES.OP_PORT_TYPE_VALUE,{ display:'range' }));
      var posX=op.addInPort(new CABLES.Port(op,"pos x",CABLES.OP_PORT_TYPE_VALUE, {}));
      var posY=op.addInPort(new CABLES.Port(op,"pos y",CABLES.OP_PORT_TYPE_VALUE, {}));
      var rotate=op.addInPort(new CABLES.Port(op,"rotate",CABLES.OP_PORT_TYPE_VALUE, {}));
  
      scale.set(1.0);
  
      var uniScale=new CGL.Uniform(shader,'f','scale',scale.get());
      var uniPosX=new CGL.Uniform(shader,'f','posX',posX.get());
      var uniPosY=new CGL.Uniform(shader,'f','posY',posY.get());
      var uniRotate=new CGL.Uniform(shader,'f','rotate',rotate.get());
  
      function updateTransform()
      {
          if(scale.get()!=1.0 || posX.get()!=0.0 || posY.get()!=0.0 || rotate.get()!=0.0 )
          {
              if(!shader.hasDefine('TEX_TRANSFORM')) shader.define('TEX_TRANSFORM');
              uniScale.setValue( parseFloat(scale.get()) );
              uniPosX.setValue( posX.get() );
              uniPosY.setValue( posY.get() );
              uniRotate.setValue( rotate.get() );
          }
          else
          {
              // shader.removeDefine('TEX_TRANSFORM');
          }
      }
  
      scale.onChange=updateTransform;
      posX.onChange=updateTransform;
      posY.onChange=updateTransform;
      rotate.onChange=updateTransform;
  }
  
  CGL.TextureEffect.setupBlending(op,shader,blendMode,amount);
  
  
  var amountUniform=new CGL.Uniform(shader,'f','amount',amount);
  var oldHadImageAlpha=false;
  
  function doRender()
  {
      if(!CGL.TextureEffect.checkOpInEffect(op)) return;
  
      if(imageAlpha.get() && !oldHadImageAlpha || !imageAlpha.get() && oldHadImageAlpha)
      {
          if(imageAlpha.get() && imageAlpha.get().tex)
          {
              shader.define('HAS_TEXTUREALPHA');
              oldHadImageAlpha=true;
          }
          else
          {
              shader.removeDefine('HAS_TEXTUREALPHA');
              oldHadImageAlpha=false;
          }
      }
  
      if(image.get() && image.get().tex && amount.get()>0.0)
      {
          cgl.setShader(shader);
          cgl.currentTextureEffect.bind();
  
          cgl.setTexture(0, cgl.currentTextureEffect.getCurrentSourceTexture().tex );
  
          if(image.get() && image.get().tex) cgl.setTexture(1, image.get().tex );
              else cgl.setTexture(1, null);
  
          if(imageAlpha.get() && imageAlpha.get().tex) cgl.setTexture(2, imageAlpha.get().tex );
              else cgl.setTexture(2,null);
  
          cgl.currentTextureEffect.finish();
          cgl.setPreviousShader();
      }
  
      trigger.trigger();
  }
  
  
  
  };
  
  Ops.Gl.TextureEffects.DrawImage.prototype = new CABLES.Op();
  CABLES.OPS["8248b866-9492-48c8-897d-3097c6fe6fe8"]={f:Ops.Gl.TextureEffects.DrawImage,objName:"Ops.Gl.TextureEffects.DrawImage"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Math.Max
  // 
  // **************************************************************
  
  Ops.Math.Max = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  const
      result=op.outValue("result"),
      value=op.inValueFloat("value"),
      max=op.inValueFloat("Maximum");
  
  max.onChange=exec;
  value.onChange=exec;
  
  value.set(1);
  max.set(1);
  
  function exec()
  {
      var v=Math.max(value.get(),max.get());
      if(v==v) result.set( v );
  }
  
  
  
  };
  
  Ops.Math.Max.prototype = new CABLES.Op();
  CABLES.OPS["07f0be49-c226-4029-8039-3b620145dc2a"]={f:Ops.Math.Max,objName:"Ops.Math.Max"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Math.Cosine
  // 
  // **************************************************************
  
  Ops.Math.Cosine = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  // input
  var value = op.inValue('Value');
  
  var phase = op.inValue('Phase', 0.0);
  var mul = op.inValue('Frequency', 1.0);
  var amplitude = op.inValue('Amplitude', 1.0);
  var invert = op.inValueBool("asine", false);
  
  // output
  var result = op.outValue('Result');
  
  var calculate = Math.cos;
  
  value.onChange=function()
  {
      result.set(
          amplitude.get() * calculate( ( value.get()*mul.get() ) + phase.get() )
      );
  };
  
  invert.onChange = function()
  {
      if(invert.get()) calculate = Math.acos;
      else calculate = Math.cos;
  }
  
  
  };
  
  Ops.Math.Cosine.prototype = new CABLES.Op();
  CABLES.OPS["b51166c4-e0a8-441a-b724-1531effdc52f"]={f:Ops.Math.Cosine,objName:"Ops.Math.Cosine"};
  
  
  
  
  // **************************************************************
  // 
  // Ops.Gl.Render2Texture
  // 
  // **************************************************************
  
  Ops.Gl.Render2Texture = function()
  {
  CABLES.Op.apply(this,arguments);
  const op=this;
  const attachments={};
  var cgl=op.patch.cgl;
  
  var render=op.inTrigger('render');
  var useVPSize=op.addInPort(new CABLES.Port(op,"use viewport size",CABLES.OP_PORT_TYPE_VALUE,{ display:'bool' }));
  
  var width=op.inValueInt("texture width");
  var height=op.inValueInt("texture height");
  var tfilter=op.addInPort(new CABLES.Port(op,"filter",CABLES.OP_PORT_TYPE_VALUE,{display:'dropdown',values:['nearest','linear','mipmap']}));
  
  var msaa=op.inValueSelect("MSAA",["none","2x","4x","8x"],"none");
  var trigger=op.outTrigger('trigger');
  // var tex=op.addOutPort(new CABLES.Port(op,"texture",CABLES.OP_PORT_TYPE_TEXTURE,{preview:true}));
  // var texDepth=op.addOutPort(new CABLES.Port(op,"textureDepth",CABLES.OP_PORT_TYPE_TEXTURE));
  
  var tex=op.outTexture("texture");
  var texDepth=op.outTexture("textureDepth");
  
  var fpTexture=op.inValueBool("HDR");
  var depth=op.inValueBool("Depth",true);
  var clear=op.inValueBool("Clear",true);
  
  var fb=null;
  
  width.set(512);
  height.set(512);
  useVPSize.set(true);
  tfilter.set('linear');
  var reInitFb=true;
  
  op.setPortGroup('Alignment',[useVPSize,width,height,tfilter]);
  
  
  // todo why does it only work when we render a mesh before>?>?????
  // only happens with matcap material with normal map....
  
  useVPSize.onChange=updateVpSize;
  function updateVpSize()
  {
      if(useVPSize.get())
      {
          width.setUiAttribs({greyout:true});
          height.setUiAttribs({greyout:true});
      }
      else
      {
          width.setUiAttribs({greyout:false});
          height.setUiAttribs({greyout:false});
      }
  }
  
  fpTexture.onChange=function()
  {
      reInitFb=true;
  };
  
  depth.onChange=function()
  {
      reInitFb=true;
  };
  
  clear.onChange=function()
  {
      reInitFb=true;
  };
  
  var onFilterChange=function()
  {
      reInitFb=true;
  };
  
  msaa.onChange=function()
  {
      reInitFb=true;
  };
  
  function doRender()
  {
      if(!fb || reInitFb)
      {
          if(fb) fb.delete();
          if(cgl.glVersion>=2)
          {
              var ms=true;
              var msSamples=4;
  
              if(msaa.get()=="none")
              {
                  msSamples=0;
                  ms=false;
              }
              if(msaa.get()=="2x")msSamples=2;
              if(msaa.get()=="4x")msSamples=4;
              if(msaa.get()=="8x")msSamples=8;
  
              fb=new CGL.Framebuffer2(cgl,8,8,
              {
                  isFloatingPointTexture:fpTexture.get(),
                  multisampling:ms,
                  depth:depth.get(),
                  multisamplingSamples:msSamples,
                  clear:clear.get()
              });
          }
          else
          {
              fb=new CGL.Framebuffer(cgl,8,8,{isFloatingPointTexture:fpTexture.get()});
          }
  
          if(tfilter.get()=='nearest') fb.setFilter(CGL.Texture.FILTER_NEAREST);
              else if(tfilter.get()=='linear') fb.setFilter(CGL.Texture.FILTER_LINEAR);
              else if(tfilter.get()=='mipmap') fb.setFilter(CGL.Texture.FILTER_MIPMAP);
  
  
          texDepth.set( fb.getTextureDepth() );
          reInitFb=false;
      }
  
      if(useVPSize.val)
      {
          width.set( cgl.getViewPort()[2] );
          height.set( cgl.getViewPort()[3] );
      }
  
      if(fb.getWidth()!=Math.ceil(width.get()) || fb.getHeight()!=Math.ceil(height.get()) )
      {
          fb.setSize(
              Math.max(1,Math.ceil(width.get())),
              Math.max(1,Math.ceil(height.get())) );
      }
  
      fb.renderStart(cgl);
  
      trigger.trigger();
      fb.renderEnd(cgl);
  
      cgl.resetViewPort();
  
      tex.set( fb.getTextureColor() );
  }
  
  
  render.onTriggered=doRender;
  
  
  tfilter.onValueChange(onFilterChange);
  updateVpSize();
  
  };
  
  Ops.Gl.Render2Texture.prototype = new CABLES.Op();
  CABLES.OPS["d01fa820-396c-4cb5-9d78-6b14762852af"]={f:Ops.Gl.Render2Texture,objName:"Ops.Gl.Render2Texture"};
  
  
  