"use strict" function bufferID_to_assetID( id ) { if( id===0 ) return '4dXGR8'; if( id===1 ) return 'XsXGR8'; if( id===2 ) return '4sXGR8'; if( id===3 ) return 'XdfGR8'; return 'none'; } function assetID_to_bufferID( id ) { if( id==='4dXGR8' ) return 0; if( id==='XsXGR8' ) return 1; if( id==='4sXGR8' ) return 2; if( id==='XdfGR8' ) return 3; return -1; } function assetID_to_cubemapBuferID( id ) { if( id==='4dX3Rr' ) return 0; return -1; } function cubamepBufferID_to_assetID( id ) { if( id===0 ) return '4dX3Rr'; return 'none'; } function EffectPass( renderer, is20, isLowEnd, hasShaderTextureLOD, callback, obj, forceMuted, forcePaused, outputGainNode, copyProgram, id, effect ) { this.mID = id; this.mInputs = [null, null, null, null ]; this.mOutputs = [null, null, null, null ]; this.mSource = null; this.mGainNode = outputGainNode; this.mSoundShaderCompiled = false; this.mEffect = effect; this.mRenderer = renderer; this.mProgramCopy = copyProgram; this.mCompilationTime = 0; this.mType = "none"; this.mName = "none"; this.mFrame = 0; this.mShaderTextureLOD = hasShaderTextureLOD; this.mIs20 = is20; this.mIsLowEnd = isLowEnd; this.mTextureCallbackFun = callback; this.mTextureCallbackObj = obj; this.mForceMuted = forceMuted; this.mForcePaused = forcePaused; } EffectPass.prototype.MakeHeader_Image = function() { let header = ""; header += "#define HW_PERFORMANCE " + ((this.mIsLowEnd===true)?"0":"1") + "\n"; header += "uniform vec3 iResolution;\n" + "uniform float iTime;\n" + "uniform float iChannelTime[4];\n" + "uniform vec4 iMouse;\n" + "uniform vec4 iDate;\n" + "uniform float iSampleRate;\n" + "uniform vec3 iChannelResolution[4];\n" + "uniform int iFrame;\n" + "uniform float iTimeDelta;\n" + "uniform float iFrameRate;\n"; for( let i=0; i0 || n2>0 || n3>0 ) { let vsSourceVR; if( this.mIs20 ) vsSourceVR = "layout(location = 0) in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; else vsSourceVR = "attribute in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; let fsSourceVR = this.mHeader; for (let i = 0; i < commonShaderCodes.length; i++) { fsSourceVR += commonShaderCodes[i]; } fsSourceVR += shaderCode; fsSourceVR += this.mImagePassFooterVR; let res = this.mRenderer.CreateShader(vsSource, fsSourceVR, preventCache); if( res.mResult == false ) { return res.mInfo; } if( this.mProgramVR != null ) this.mRenderer.DestroyShader( this.mProgramVR ); this.mSupportsVR = true; this.mProgramVR = res; } */ } EffectPass.prototype.NewShader_Cubemap = function( shaderCode, commonShaderCodes ) { let vsSource = null; if( this.mIs20 ) vsSource = "layout(location = 0) in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; else vsSource = "attribute vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; let fsSource = this.mHeader; for (let i = 0; i < commonShaderCodes.length; i++) { fsSource += commonShaderCodes[i]+'\n'; } this.mHeaderLength = fsSource.split(/\r\n|\r|\n/).length; fsSource += shaderCode; return [vsSource, fsSource]; } EffectPass.prototype.NewShader_Common = function (shaderCode ) { let vsSource = null; if (this.mIs20) vsSource = "layout(location = 0) in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; else vsSource = "attribute vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; let fsSource = this.mHeader + shaderCode; return [vsSource, fsSource]; } EffectPass.prototype.NewShader = function ( commonSourceCodes, preventCache, onResolve) { if( this.mRenderer===null ) return; let vs_fs = null; if( this.mType==="sound" ) vs_fs = this.NewShader_Sound( this.mSource, commonSourceCodes ); else if( this.mType==="image" ) vs_fs = this.NewShader_Image( this.mSource, commonSourceCodes ); else if( this.mType==="buffer" ) vs_fs = this.NewShader_Image( this.mSource, commonSourceCodes ); else if( this.mType==="common" ) vs_fs = this.NewShader_Common( this.mSource, ); else if( this.mType==="cubemap") vs_fs = this.NewShader_Cubemap( this.mSource, commonSourceCodes ); else { console.log("ERROR 3: \"" + this.mType + "\""); return; } let me = this; this.mRenderer.CreateShader(vs_fs[0], vs_fs[1], preventCache, false, function (worked, info) { if (worked === true) { if (me.mType === "sound") { me.mSoundShaderCompiled = true; } me.mCompilationTime = info.mTime; me.mError = false; me.mErrorStr = "No Errors"; if (me.mProgram !== null) me.mRenderer.DestroyShader(me.mProgram); me.mTranslatedSource = me.mRenderer.GetTranslatedShaderSource(info); me.mProgram = info; } else { me.mError = true; me.mErrorStr = info.mErrorStr; } onResolve(); }); } EffectPass.prototype.DestroyInput = function( id ) { if( this.mInputs[id]===null ) return; if( this.mInputs[id].mInfo.mType==="texture" ) { if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } if( this.mInputs[id].mInfo.mType==="volume" ) { if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="webcam" ) { this.mInputs[id].video.pause(); this.mInputs[id].video.src = ""; if( this.mInputs[id].video.srcObject!==null ) { let tracks = this.mInputs[id].video.srcObject.getVideoTracks(); if( tracks ) tracks[0].stop(); } this.mInputs[id].video = null; if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="video" ) { this.mInputs[id].video.pause(); this.mInputs[id].video = null; if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="music" || this.mInputs[id].mInfo.mType==="musicstream") { this.mInputs[id].audio.pause(); this.mInputs[id].audio.mSound.mFreqData = null; this.mInputs[id].audio.mSound.mWaveData = null; this.mInputs[id].audio = null; if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="cubemap" ) { if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="keyboard" ) { //if( this.mInputs[id].globject != null ) // this.mRenderer.DestroyTexture(this.mInputs[id].globject); } else if( this.mInputs[id].mInfo.mType==="mic" ) { this.mInputs[id].mic = null; if( this.mInputs[id].globject !== null ) this.mRenderer.DestroyTexture(this.mInputs[id].globject); } this.mInputs[id] = null; } EffectPass.prototype.TooglePauseInput = function( wa, id ) { var me = this; let inp = this.mInputs[id]; if( inp===null ) { } else if( inp.mInfo.mType==="texture" ) { } else if( inp.mInfo.mType==="volume" ) { } else if( inp.mInfo.mType==="video" ) { if( inp.video.mPaused ) { inp.video.play(); inp.video.mPaused = false; } else { inp.video.pause(); inp.video.mPaused = true; } return inp.video.mPaused; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream") { wa.resume() if( inp.audio.mPaused ) { if( inp.loaded ) { inp.audio.play(); } inp.audio.mPaused = false; } else { inp.audio.pause(); inp.audio.mPaused = true; } return inp.audio.mPaused; } return null; } EffectPass.prototype.StopInput = function( id ) { let inp = this.mInputs[id]; if( inp===null ) { } else if( inp.mInfo.mType==="texture" ) { } else if( inp.mInfo.mType==="volume" ) { } else if( inp.mInfo.mType==="video" ) { if( inp.video.mPaused === false ) { inp.video.pause(); inp.video.mPaused = true; } return inp.video.mPaused; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream" ) { if( inp.audio.mPaused === false ) { inp.audio.pause(); inp.audio.mPaused = true; } return inp.audio.mPaused; } return null; } EffectPass.prototype.ResumeInput = function( id ) { let inp = this.mInputs[id]; if( inp===null ) { } else if( inp.mInfo.mType==="texture" ) { } else if( inp.mInfo.mType==="volume" ) { } else if( inp.mInfo.mType==="video" ) { if( inp.video.mPaused ) { inp.video.play(); inp.video.mPaused = false; } return inp.video.mPaused; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream" ) { if( inp.audio.mPaused ) { inp.audio.play(); inp.audio.mPaused = false; } return inp.audio.mPaused; } return null; } EffectPass.prototype.RewindInput = function( wa, id ) { var me = this; let inp = this.mInputs[id]; if( inp==null ) { } else if( inp.mInfo.mType==="texture" ) { } else if( inp.mInfo.mType==="volume" ) { } else if( inp.mInfo.mType==="video" ) { if( inp.loaded ) { inp.video.currentTime = 0; } } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream") { wa.resume() if( inp.loaded ) { inp.audio.currentTime = 0; } } } EffectPass.prototype.MuteInput = function( wa, id ) { let inp = this.mInputs[id]; if( inp===null ) return; if( inp.mInfo.mType==="video" ) { inp.video.muted = true; inp.video.mMuted = true; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream") { if (wa !== null) inp.audio.mSound.mGain.gain.value = 0.0; inp.audio.mMuted = true; } } EffectPass.prototype.UnMuteInput = function( wa, id ) { let inp = this.mInputs[id]; if( inp===null ) return; if( inp.mInfo.mType==="video" ) { inp.video.muted = false; inp.video.mMuted = false; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream") { if (wa !== null) inp.audio.mSound.mGain.gain.value = 1.0; inp.audio.mMuted = false; } } EffectPass.prototype.ToggleMuteInput = function( wa, id ) { var me = this; let inp = this.mInputs[id]; if( inp===null ) return null; if( inp.mInfo.mType==="video" ) { if( inp.video.mMuted ) this.UnMuteInput(wa,id); else this.MuteInput(wa,id); return inp.video.mMuted; } else if( inp.mInfo.mType==="music" || inp.mInfo.mType==="musicstream") { if( inp.audio.mMuted ) this.UnMuteInput(wa,id); else this.MuteInput(wa,id); return inp.audio.mMuted; } return null; } EffectPass.prototype.UpdateInputs = function( wa, forceUpdate, keyboard ) { for (let i=0; imXres || yres>mYres ) { let texture = mRenderer.CreateTexture(mRenderer.TEXTYPE.T2D, xres, yres, mRenderer.TEXFMT.C4F32, mRenderer.FILTER.NONE, mRenderer.TEXWRP.CLAMP, null); let target = mRenderer.CreateRenderTarget( texture, null, null, null, null, false); if( mXres!==0 ) { mRenderer.DestroyTexture(mTexture); mRenderer.DestroyRenderTarget(mTarget); } mTexture = texture; mTarget = target; mXres = xres; mYres = yres; } }; me.GetProgram = function() { return mCubemapToEquirectProgram; }; me.GetTarget = function() { return mTarget; }; return me; }; //============================================================================================================ function Effect(vr, ac, canvas, callback, obj, forceMuted, forcePaused, resizeCallback, crashCallback ) { let xres = canvas.width; let yres = canvas.height; let me = this; this.mCanvas = canvas; this.mCreated = false; this.mRenderer = null; this.mAudioContext = ac; this.mGLContext = null; this.mWebVR = vr; this.mRenderingStereo = false; this.mXres = xres; this.mYres = yres; this.mForceMuted = forceMuted; if( ac===null ) this.mForceMuted = true; this.mForcePaused = forcePaused; this.mGainNode = null; this.mPasses = []; this.mFrame = 0; this.mTextureCallbackFun = callback; this.mTextureCallbackObj = obj; this.mMaxBuffers = 4; this.mMaxCubeBuffers = 1; this.mMaxPasses = this.mMaxBuffers + 1 + 1 + 1 + 1; // some day decouple passes from buffers (4 buffers + common + Imagen + sound + cubemap) this.mBuffers = []; this.mCubeBuffers = []; this.mScreenshotSytem = null; this.mCompilationTime = 0; this.mIsLowEnd = piIsMobile(); this.mGLContext = piCreateGlContext(canvas, false, false, true, false); // need preserve-buffe to true in order to capture screenshots if (this.mGLContext === null) { return; } canvas.addEventListener("webglcontextlost", function (event) { event.preventDefault(); crashCallback(); }, false); this.mRenderer = piRenderer(); if (!this.mRenderer.Initialize(this.mGLContext)) return; this.mScreenshotSytem = Screenshots(); if (!this.mScreenshotSytem.Initialize(this.mRenderer)) return; var caps = this.mRenderer.GetCaps(); this.mIs20 = caps.mIsGL20; this.mShaderTextureLOD = caps.mShaderTextureLOD; //------------- if( ac!==null ) { this.mGainNode = ac.createGain(); if( !forceMuted ) { this.mGainNode.connect( ac.destination); } if (this.mForceMuted ) this.mGainNode.gain.value = 0.0; else this.mGainNode.gain.value = 1.0; } //------------- let vsSourceC, fsSourceC; if( this.mIs20 ) { vsSourceC = "layout(location = 0) in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; fsSourceC = "uniform vec4 v; uniform sampler2D t; out vec4 outColor; void main() { outColor = textureLod(t, gl_FragCoord.xy / v.zw, 0.0); }"; } else { vsSourceC = "attribute vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; fsSourceC = "uniform vec4 v; uniform sampler2D t; void main() { gl_FragColor = texture2D(t, gl_FragCoord.xy / v.zw, -100.0); }"; } this.mRenderer.CreateShader(vsSourceC, fsSourceC, false, true, function(worked, info) { if (worked === false) console.log("Failed to compile shader to copy buffers : " + info.mErrorStr); else me.mProgramCopy = info; }); let vsSourceD, fsSourceD; if( this.mIs20 ) { vsSourceD = "layout(location = 0) in vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; fsSourceD = "uniform vec4 v; uniform sampler2D t; out vec4 outColor; void main() { vec2 uv = gl_FragCoord.xy / v.zw; outColor = texture(t, vec2(uv.x,1.0-uv.y)); }"; } else { vsSourceD = "attribute vec2 pos; void main() { gl_Position = vec4(pos.xy,0.0,1.0); }"; fsSourceD = "uniform vec4 v; uniform sampler2D t; void main() { vec2 uv = gl_FragCoord.xy / v.zw; gl_FragColor = texture2D(t, vec2(uv.x,1.0-uv.y)); }"; } this.mRenderer.CreateShader(vsSourceD, fsSourceD, false, true, function (worked, info) { if (worked === false) console.log("Failed to compile shader to downscale buffers : " + info.mErrorStr); else me.mProgramDownscale = info; }); // set all buffers and cubemaps to null for( let i=0; ithis.mMaxPasses ) { console.log("Corrupted Shader - " + numPasses); return false; } this.mPasses = []; for (let j = 0; j < numPasses; j++) { let rpass = jobj.renderpass[j]; // skip sound passes if in thumbnail mode if( this.mForceMuted && rpass.type === "sound" ) continue; let wpass = new EffectPass(this.mRenderer, this.mIs20, this.mIsLowEnd, this.mShaderTextureLOD, this.mTextureCallbackFun, this.mTextureCallbackObj, this.mForceMuted, this.mForcePaused, this.mGainNode, this.mProgramDownscale, j, this); wpass.Create(rpass.type, this.mAudioContext); let numInputs = rpass.inputs.length; for (let i = 0; i < 4; i++) { wpass.NewTexture(this.mAudioContext, i, null, null, null); } for (let i = 0; i < numInputs; i++) { let lid = rpass.inputs[i].channel; let styp = rpass.inputs[i].type; let sid = rpass.inputs[i].id; let ssrc = rpass.inputs[i].filepath; let psrc = rpass.inputs[i].previewfilepath; let samp = rpass.inputs[i].sampler; wpass.NewTexture(this.mAudioContext, lid, { mType: styp, mID: sid, mSrc: ssrc, mSampler: samp, mPreviewSrc: psrc }, this.mBuffers, this.mCubeBuffers, this.mKeyboard); } for (let i = 0; i < 4; i++) { wpass.SetOutputs(i, null); } let numOutputs = rpass.outputs.length; for (let i = 0; i < numOutputs; i++) { let outputID = rpass.outputs[i].id; let outputCH = rpass.outputs[i].channel; wpass.SetOutputs(outputCH, outputID); } // create some hardcoded names. This should come from the DB let rpassName = ""; if (rpass.type === "common" ) rpassName = "Common"; if (rpass.type === "sound" ) rpassName = "Sound"; if (rpass.type === "image" ) rpassName = "Image"; if (rpass.type === "buffer") rpassName = "Buffer " + String.fromCharCode(65 + assetID_to_bufferID(wpass.mOutputs[0])); if (rpass.type === "cubemap") rpassName = "Cube A";// " + String.fromCharCode(65 + assetID_to_bufferID(this.mPasses[j].mOutputs[0])); wpass.SetName(rpassName); wpass.SetCode(rpass.code); this.mPasses.push(wpass); } return true; } Effect.prototype.CompileSome = function ( passes, preventCache, onResolve ) { let me = this; let to = (new Date()).getTime(); let allPromisses = []; for (let j = 0; j < passes.length; j++) { allPromisses.push(new Promise(function (resolve, reject) { me.NewShader(passes[j], preventCache, function () { resolve(1); }); })); } // aggregated callback when all passes have been compiled Promise.all(allPromisses).then(function (values) { let totalError = false; for (let j = 0; j < me.mPasses.length; j++) { if (me.mPasses[j].mError) { totalError = true; break; } } me.mCompilationTime = (new Date()).getTime() - to; onResolve(!totalError); }).catch(console.log); } Effect.prototype.Compile = function (preventCache, onResolve ) { let me = this; let to = (new Date()).getTime(); let allPromisses = []; let numPasses = this.mPasses.length; for (let j = 0; j < numPasses; j++) { allPromisses.push(new Promise(function (resolve, reject) { me.NewShader(j, preventCache, function () { resolve(1); }); })); } // aggregated callback when all passes have been compiled Promise.all(allPromisses).then(function (values) { let totalError = false; for (let j = 0; j < numPasses; j++) { if (me.mPasses[j].mError) { totalError = true; break; } } me.mCompilationTime = (new Date()).getTime() - to; onResolve(!totalError); }).catch(console.log); } Effect.prototype.GetCompilationTime = function( id ) { return this.mPasses[id].GetCompilationTime()/1000.0; } Effect.prototype.GetTotalCompilationTime = function() { return this.mCompilationTime/1000.0; } Effect.prototype.DestroyPass = function( id ) { this.mPasses[id].Destroy( this.mAudioContext ); this.mPasses.splice(id, 1); } Effect.prototype.AddPass = function( passType, passName, onResolve ) { let shaderStr = null; if( passType==="sound" ) shaderStr = "vec2 mainSound( int samp, float time )\n{\n // A 440 Hz wave that attenuates quickly overt time\n return vec2( sin(6.2831*440.0*time)*exp(-3.0*time) );\n}"; if( passType==="buffer" ) shaderStr = "void mainImage( out vec4 fragColor, in vec2 fragCoord )\n{\n fragColor = vec4(0.0,0.0,1.0,1.0);\n}"; if( passType==="common" ) shaderStr = "vec4 someFunction( vec4 a, float b )\n{\n return a+b;\n}"; if( passType==="cubemap" ) shaderStr = "void mainCubemap( out vec4 fragColor, in vec2 fragCoord, in vec3 rayOri, in vec3 rayDir )\n{\n // Ray direction as color\n vec3 col = 0.5 + 0.5*rayDir;\n\n // Output to cubemap\n fragColor = vec4(col,1.0);\n}"; let id = this.GetNumPasses(); this.mPasses[id] = new EffectPass( this.mRenderer, this.mIs20, this.mIsLowEnd, this.mShaderTextureLOD, this.mTextureCallbackFun, this.mTextureCallbackObj, this.mForceMuted, this.mForcePaused, this.mGainNode, this.mProgramDownscale, id, this ); this.mPasses[id].Create( passType, this.mAudioContext ); this.mPasses[id].SetName( passName ); this.mPasses[id].SetCode( shaderStr ); this.NewShader(id, false, function () { onResolve(); }); return { mId : id, mShader : shaderStr }; } // this should be removed once we have MultiPass 2.0 and passes render to arbitrary buffers Effect.prototype.IsBufferPassUsed = function( bufferID ) { for (let j=0; j 0 || n2 > 0) flagVR = true; } return { mFlagVR: flagVR, mFlagWebcam: flagWebcam, mFlagSoundInput: flagSoundInput, mFlagSoundOutput: flagSoundOutput, mFlagKeyboard: flagKeyboard, mFlagMultipass: flagMultipass, mFlagMusicStream: flagMusicStream }; }