/* <copyright> This file contains proprietary software owned by Motorola Mobility, Inc.<br/> No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. </copyright> */ g_renderStats = {}; g_renderStats.numDrawCalls = new stat("rendering", "numDrawCalls", 0, null, false); g_renderStats.numTriangles = new stat("rendering", "numTriangles", 0, null, false); g_renderStats.numVerts = new stat("rendering", "numVerts", 0, null, false); g_renderStats.numPasses = new stat("rendering", "numPasses", 0, null, false); g_renderStats.reset = function() { g_renderStats.numTriangles.value = 0; g_renderStats.numDrawCalls.value = 0; g_renderStats.numVerts.value = 0; g_renderStats.numPasses.value = 0; }; rdgeConstants = {}; /* * clear flags */ rdgeConstants.colorBuffer = 0x00004000; rdgeConstants.depthBuffer = 0x00000100; rdgeConstants.stencilBuffer = 0x00000400; /* * buffer types */ rdgeConstants.BUFFER_STATIC = 0; rdgeConstants.BUFFER_DYNAMIC = 1; rdgeConstants.BUFFER_STREAM = 2; /* * primitive types */ rdgeConstants.POINTS = 0; rdgeConstants.LINES = 1; rdgeConstants.LINE_LOOP = 2; rdgeConstants.LINE_STRIP = 3; rdgeConstants.TRIANGLES = 4; rdgeConstants.TRIANGLE_STRIP = 5; rdgeConstants.TRIANGLE_FAN = 6; /* * primitive data types */ rdgeConstants.BYTE = 0x1400; rdgeConstants.UNSIGNED_BYTE = 0x1401; rdgeConstants.SHORT = 0x1402; rdgeConstants.UNSIGNED_SHORT = 0x1403; rdgeConstants.INT = 0x1404; rdgeConstants.UNSIGNED_INT = 0x1405; rdgeConstants.FLOAT = 0x1406; /* * pre-defined vertex element type */ rdgeConstants.VS_ELEMENT_FLOAT4 = 4; rdgeConstants.VS_ELEMENT_POS = 3; rdgeConstants.VS_ELEMENT_NORM = 3; rdgeConstants.VS_ELEMENT_FLOAT3 = 3; rdgeConstants.VS_ELEMENT_FLOAT2 = 2; rdgeConstants.VS_ELEMENT_UV = 2; rdgeConstants.VS_ELEMENT_FLOAT = 1; rdgeConstants.MAX_ELEM_TYPES = 7; // GL Definition of buffer types rdgeConstants.BUFFER_STATIC = 0x88E0; rdgeConstants.BUFFER_DYNAMIC = 0x88E4; rdgeConstants.BUFFER_STREAM = 0x88E8; // render constants rdgeConstants.MAX_MATERIAL_LIGHTS = 4; /* * Material categories determine sorting * materials support the following categories */ rdgeConstants.categoryEnumeration = { 'BACKGROUND' : 0, 'OPAQUE' : 1, 'TRANSPARENT' : 2, 'ADDITIVE' : 3, 'TRANSLUCENT' : 4, 'FOREGROUND' : 5, 'MAX_CAT' : 6 }; /* * Node types supported by the scene graph */ rdgeConstants.nodeType = { 'TRNODE' : 0, 'MESHNODE' : 1, 'MATNODE' : 2, 'LIGHTNODE' : 3 }; // generate an id for the renderer to map a render buffer to primitive rdgeId = 0; function getBufferID() { return rdgeId++; } _renderer = function(canvas) { /* * Initialize the context associated with this canvas */ this.ctx; try { this.ctx = canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }); // true, true, false, true, true); if (!this.ctx) this.ctx = canvas.getContext("webgl", { preserveDrawingBuffer: true }); if (!this.ctx) this.ctx = canvas.getContext("webkit-3d", { preserveDrawingBuffer: true }); if (!this.ctx) this.ctx = canvas.getContext("moz-webgl", { preserveDrawingBuffer: true }); } catch (err) { } if (!this.ctx) { window.console.log("Could not create GL context"); return null; } // set viewport for the first time this.ctx.viewport(0, 0, canvas.width, canvas.height); // Add a console output to the renderer this.console = ("console" in window) ? window.console : { log: function() { } }; /* * Set the default clear color */ this.ctx.clearColor(1, 0, 0, 1); /* * the clear color of this renderer */ this.clearColor = [1, 0, 0, 1]; /* * The clear flags clear color and depth buffers by default */ this.clearFlags = this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT /* * clear flags */ this.colorBuffer = this.ctx.COLOR_BUFFER_BIT; this.depthBuffer = this.ctx.DEPTH_BUFFER_BIT; this.stencilBuffer = this.ctx.STENCIL_BUFFER_BIT; /* * buffer types */ this.BUFFER_STATIC = 0; this.BUFFER_DYNAMIC = 1; this.BUFFER_STREAM = 2; /* * primitive types */ this.POINTS = 0; this.LINES = 1; this.LINE_LOOP = 2; this.LINE_STRIP = 3; this.TRIANGLES = 4; this.TRIANGLE_STRIP = 5; this.TRIANGLE_FAN = 6; /* * primitive data types */ this.BYTE = 0x1400; this.UNSIGNED_BYTE = 0x1401; this.SHORT = 0x1402; this.UNSIGNED_SHORT = 0x1403; this.INT = 0x1404; this.UNSIGNED_INT = 0x1405; this.FLOAT = 0x1406; /* * pre-defined vertex element type */ this.VS_ELEMENT_FLOAT4 = 4; this.VS_ELEMENT_POS = 3; this.VS_ELEMENT_NORM = 3; this.VS_ELEMENT_FLOAT3 = 3; this.VS_ELEMENT_FLOAT2 = 2; this.VS_ELEMENT_UV = 2; this.VS_ELEMENT_FLOAT = 1; this.MAX_ELEM_TYPES = 7; // GL Definition of buffer types this.BUFFER_STATIC = 0x88E0; this.BUFFER_DYNAMIC = 0x88E4; this.BUFFER_STREAM = 0x88E8; // render constants this.MAX_MATERIAL_LIGHTS = 4; // max system textures this.usedTextureUnits = 5; /* * the renderers current viewport */ this.vpX = 0; this.vpY = 0; this.vpWidth = canvas.width; this.vpHeight = canvas.height; /* * the camera manager - contains the camera stack for this render context */ this.cameraMan = new cameraManager(); /* * a list of device buffers that are owned by this render context */ this.buffers = []; /* * State wrappers */ this.cullBackFace = function() { this.ctx.cullFace(this.ctx.Back); } this.cullFrontFace = function() { this.ctx.cullFace(this.ctx.FRONT); } this.disableCulling = function() { this.ctx.disable(this.ctx.CULL_FACE); } this.enableCulling = function() { this.ctx.enable(this.ctx.CULL_FACE); } this.enablePolyOffsetFill = function() { this.ctx.enable(this.ctx.POLYGON_OFFSET_FILL); } this.disablePolyOffsetFill = function() { this.ctx.enable(this.ctx.POLYGON_OFFSET_FILL); } this.enablePointSprites = function() { // this.ctx.enable(0x8642); } this.disablePointSprites = function() { // this.ctx.enable(0x8642); } this.setClearColor = function(color) { this.clearColor = color.slice(); this.ctx.clearColor(color[0], color[1], color[2], color[3]); } /* * flags that specify how to clear the scene, can be OR'ed together */ this.setClearFlags = function(flags) { this.clearFlags = flags; } /* * called by the system to clear the video buffer according to pre-set flags */ this._clear = function() { this.ctx.clear(this.clearFlags); } /* * clears the video buffer with flags provided */ this.clear = function(flags) { this.ctx.clear(flags); } /* * flush the video buffer */ this.flush = function() { this.ctx.flush(); } /* * Sets the current viewport */ this.setViewPort = function(x, y, width, height) { this.vpX = x; this.vpY = y; this.vpWidth = width; this.vpHeight = height; this.ctx.viewport(this.vpX, this.vpY, this.vpWidth, this.vpHeight); } /* * access the camera manager associated with the renderer */ this.cameraManager = function() { return this.cameraMan; } /* * Sets of texture maps owned by the renderer */ this.textureMap = []; this.rttMap = []; /* * gets the texture by name or creates the texture requested * @param name - the name of the texture to try and get * @param wrap - optional "CLAMP or "REPEAT", default is clamp * @param mips - optional true/false value to create mipmaps, the default is true */ this.getTextureByName = function(name, wrap, mips) { var ext = name.split('.')[1]; if (!ext) ext = ".png"; else ext = ""; var tex = this.textureMap[name]; if (tex === undefined) { // load the texture name = g_Engine.remapAssetFolder( name ); tex = this.createTexture(name + ext, wrap, mips); this.textureMap[name] = tex; tex.lookUpName = name; tex.previouslyReferenced = false; } else { //console.log( "texture already loaded: " + name ); tex.previouslyReferenced = true; } return tex; }; /* * creates a texture from the given URL * @param url - the resource location * @param wrap - optional "CLAMP or "REPEAT", default is clamp * @param mips - optional true/false value to create mipmaps, the default is true */ this.unloadedTextureCount = 0; _texparams = function(wrap, mips) { this.wrap = wrap, this.mips = mips }; this.createTexture = function(url, wrap, mips) { var texture = this.ctx.createTexture(); this.unloadedTextureCount++; if (wrap === undefined) wrap = "CLAMP"; if (mips === undefined) mips = true; if (texture) { texture.image = new Image(); texture.image.src = url; texture.image.context = g_Engine.getContext(); texture.texparams = new _texparams(wrap, mips); texture.image.onload = function() { var stateMan = this.context.ctxStateManager; stateMan.RDGEInitState.loadTexture(texture); this.context.renderer.unloadedTextureCount--; //console.log( "loaded texture: " + texture.lookUpName + ",to: " + this.context.renderer._world._worldCount + ", textures remaining to load: " + this.context.renderer.unloadedTextureCount ); if (texture.callback) texture.callback( texture ); if (this.context.renderer.unloadedTextureCount < 0) console.log( "more textures loaded then created..." ); }; texture.image.onerror = function() { this.context.renderer.unloadedTextureCount--; if (texture.callback) texture.callback( texture ); //console.log( "Error loading texture: " + texture.image.src ); if (this.context.renderer.unloadedTextureCount < 0) console.log( "more textures loaded then created..." ); } } return texture; } /* * commits a texture to video memory * @param - the texture object created by a call to create texture */ this.commitTexture = function(texture) { this.ctx.bindTexture(this.ctx.TEXTURE_2D, texture); this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, texture.image); if (texture.texparams.mips) this.ctx.generateMipmap(this.ctx.TEXTURE_2D); this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.LINEAR); this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, texture.texparams.mips ? this.ctx.LINEAR_MIPMAP_LINEAR : this.ctx.LINEAR); this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, texture.texparams.wrap === "REPEAT" ? this.ctx.REPEAT : this.ctx.CLAMP_TO_EDGE); this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, texture.texparams.wrap === "REPEAT" ? this.ctx.REPEAT : this.ctx.CLAMP_TO_EDGE); this.ctx.bindTexture(this.ctx.TEXTURE_2D, null); } this.verify = function(label) { var error = this.ctx.getError(); if (error != 0) { window.console.log("GLError ( " + label + ") : " + error); } } this.createRenderTargetTexture = function(lookUpName, width, height, generateMips) { var ctx = this.ctx; // create framebuffer var renderTargetFrameBuffer = ctx.createFramebuffer(); ctx.bindFramebuffer(ctx.FRAMEBUFFER, renderTargetFrameBuffer); // setup parameters (width, hight, filtering) renderTargetFrameBuffer.width = width; renderTargetFrameBuffer.height = height; // create the texture var renderTarget = ctx.createTexture(); ctx.bindTexture(ctx.TEXTURE_2D, renderTarget); try { // Do it the way the spec requires ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, renderTargetFrameBuffer.width, renderTargetFrameBuffer.height, 0, ctx.RGBA, ctx.UNSIGNED_BYTE, null); } catch (exception) { // Workaround for what appears to be a Minefield bug. var textureStorage = new WebctxUnsignedByteArray(renderTargetFrameBuffer.width * renderTargetFrameBuffer.height * 4); ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, renderTargetFrameBuffer.width, renderTargetFrameBuffer.height, 0, ctx.RGBA, ctx.UNSIGNED_BYTE, textureStorage); } ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, generateMips ? ctx.LINEAR_MIPMAP_NEAREST : ctx.LINEAR); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); if (generateMips) { ctx.generateMipmap(ctx.TEXTURE_2D); } // set frame buffer storage and texture var renderBuffer = ctx.createRenderbuffer(); ctx.bindRenderbuffer(ctx.RENDERBUFFER, renderBuffer); ctx.renderbufferStorage(ctx.RENDERBUFFER, ctx.DEPTH_COMPONENT16, renderTargetFrameBuffer.width, renderTargetFrameBuffer.height); // bind var error = ctx.getError(ctx.bindFramebuffer(ctx.FRAMEBUFFER, renderTargetFrameBuffer)); error = ctx.getError(ctx.bindRenderbuffer(ctx.RENDERBUFFER, renderBuffer)); error = ctx.getError(ctx.renderbufferStorage(ctx.RENDERBUFFER, ctx.DEPTH_COMPONENT16, renderTargetFrameBuffer.width, renderTargetFrameBuffer.height)); ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); // bind texture handle and renderBuffer to frame buffer error = ctx.getError(ctx.framebufferTexture2D(ctx.FRAMEBUFFER, ctx.COLOR_ATTACHMENT0, ctx.TEXTURE_2D, renderTarget, 0)); error = ctx.getError(ctx.framebufferRenderbuffer(ctx.FRAMEBUFFER, ctx.DEPTH_ATTACHMENT, ctx.RENDERBUFFER, renderBuffer)); ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); /* var status=ctx.checkFramebufferStatus(ctx.FRAMEBUFFER); switch(status) { case ctx.FRAMEBUFFER_COMPLETE: break; case ctx.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: ctx.console.log("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); return null; case ctx.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: ctx.console.log("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); return null; case ctx.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: ctx.console.log("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); return null; case ctx.FRAMEBUFFER_UNSUPPORTED: ctx.console.log("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED"); return null; default: ctx.console.log("Incomplete framebuffer: "+status); return null; } */ // unbind ctx.bindTexture(ctx.TEXTURE_2D, null); ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); renderTarget.id = "RT_" + nodeIdGen.getId(); renderTarget.frameBuffer = renderTargetFrameBuffer; if (this.textureMap[lookUpName]) window.console.log("Notification: render target: " + lookUpName + " has overwritten an existing render target"); // add to lookup map this.textureMap[lookUpName] = renderTarget; return renderTarget; } /* * The default shader setup */ this.defaultShaderDefintion = { 'shaders': { 'defaultVShader': "assets/shaders/test_vshader.glsl", 'defaultFShader': "assets/shaders/test_fshader.glsl" }, 'techniques': { 'defaultTechnique': [{ 'vshader': 'defaultVShader', 'fshader': 'defaultFShader', // attributes 'attributes': { 'vert': { 'type': 'vec3' }, 'normal': { 'type': 'vec3' }, 'texcoord': { 'type': 'vec2' } }, // parameters 'params': { }, // render states 'states': { 'depthEnable': true, 'blendEnable': false, 'culling': true, 'cullFace': "BACK" } }] } }; }; /* * Shader definitions */ /* * The default shader setup */ rdgeDefaultShaderDefintion = { 'shaders': { //'defaultVShader':"assets/shaders/test_vshader.glsl", //'defaultFShader':"assets/shaders/test_fshader.glsl" 'defaultVShader':"assets/shaders/Basic.vert.glsl", 'defaultFShader':"assets/shaders/Basic.frag.glsl" }, 'techniques': { 'defaultTechnique':[{ 'vshader' : 'defaultVShader', 'fshader' : 'defaultFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'normal' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' : { }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK" } }]}}; /* * The default depth map shader techniques */ rdgeDepthMapShaderDef = { 'shaders': { 'depthMapVShader':"assets/shaders/depthMap_vshader.glsl", 'depthMapFShader':"assets/shaders/depthMap_fshader.glsl" }, 'techniques': { 'shadowDepthMap': [{ 'vshader' : 'depthMapVShader', 'fshader' : 'depthMapFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'normal' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' :{ }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK"} }], 'depthMap': [{ 'vshader' : 'depthMapVShader', 'fshader' : 'depthMapFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'normal' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' :{ }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK"} }] }}; /* * capture normals in view space */ rdgeViewSpaceNormalsShader = { 'shaders': { 'normalsVShader':"assets/shaders/norm_depth_vshader.glsl", 'normalsFShader':"assets/shaders/norm_depth_fshader.glsl" }, 'techniques': { 'depthMapWNormal': [{ 'vshader' : 'normalsVShader', 'fshader' : 'normalsFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'normal' : { 'type' : 'vec3' }}, // parameters 'params' :{ }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK"} }] }}; /* * basic screen squad shader definition */ rdgeScreenQuadShaderDef = { 'shaders': { // Texture coordinates are setup so that the full texture // is mapped completely onto the screen 'screenQuadVShader': "\ attribute vec3 a_pos;\ attribute vec2 a_uv;\ uniform float u_inv_viewport_width;\ uniform float u_inv_viewport_height;\ varying vec2 vTexcoord;\ void main()\ {\ gl_Position = vec4(a_pos.xy, 0.0, 1.0);\ \ vTexcoord.x = 0.5 * (1.0 + a_pos.x + u_inv_viewport_width);\ vTexcoord.y = 0.5 * (1.0 - a_pos.y + u_inv_viewport_height);\ }", 'screenQuadFShader': "\ precision highp float;\ uniform sampler2D u_mainRT;\ uniform sampler2D u_glowFinal;\ uniform sampler2D u_ssaoRT;\ uniform sampler2D u_shadowMap;\ varying vec2 vTexcoord;\ void main()\ {\ vec2 tex = vec2(vTexcoord.x, 1.0 - vTexcoord.y);\ vec4 glowTexel = texture2D(u_glowFinal, tex);\ vec4 ssaoTexel = texture2D(u_ssaoRT, tex);\ vec4 smapCoef = texture2D(u_shadowMap, tex);\ ssaoTexel.a = 0.0;\ vec4 texel = texture2D(u_mainRT, tex);\ gl_FragColor = vec4(texel.r*((1.0 - glowTexel.r)*smapCoef.r), texel.g*((1.0 - glowTexel.g)*smapCoef.g), texel.b*((1.0 - glowTexel.b)*smapCoef.b), texel.a) + glowTexel - ssaoTexel;\ }" }, 'techniques': { // rendering control 'screenQuad':[{ 'vshader' : 'screenQuadVShader', 'fshader' : 'screenQuadFShader', // attributes 'attributes' :{ 'a_pos' : { 'type' : 'vec3' }, 'a_uv' : { 'type' : 'vec2' } }, 'params' : { 'u_mainRT' : {'type' : "tex2d"}, 'u_glowFinal' : {'type' : "tex2d", 'data':"assets/images/black"}, 'u_ssaoRT' : {'type' : "tex2d", 'data':"assets/images/black"}, 'u_shadowMap' : {'type' : "tex2d", 'data':"assets/images/white"}}, // render states 'states' : { 'blendEnabled' : true, 'srcBlend' : 'SRC_ALPHA', 'dstcBlend' : 'ONE_MINUS_SRC_ALPHA' } }] } } /* * creates the glow map */ rdgeGlowMapShader = { 'shaders': { 'createGlowVShader':"assets/shaders/glowMap_vshader.glsl", 'createGlowFShader':"assets/shaders/glowMap_fshader.glsl" }, 'techniques': { 'createGlowMap':[ { 'vshader' : 'createGlowVShader', 'fshader' : 'createGlowFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'normal' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' : { }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK" } } ]} } /* * Gaussian blur shader */ rdgeGaussianBlurShader = { 'shaders': { 'blurVShader':"assets/shaders/separableBlur_vshader.glsl", 'blurFShader':"assets/shaders/separableBlur_fshader.glsl" }, 'techniques': { 'gaussianBlur':[ { 'vshader' : 'blurVShader', 'fshader' : 'blurFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' : { 'vCoeffs' : {'type' :'vec3', 'data': [ 5.0 / 16.0, 6.0 / 16.0, 5.0 / 16.0 ]}, 'vOffset' : {'type' : 'vec2', 'data': [0.00617, 0.00617]}, 'u_weight' : {'type' : 'float', 'data': [1.0]}, 'sTexture': {'type' : "tex2d"} }, // render states 'states' : { 'culling' : false } } ] }} /* * Screen space ambient occlusion shader */ rdgeSSAOShader = { 'shaders': { 'blurVShader':"assets/shaders/ssao_vshader.glsl", 'blurFShader':"assets/shaders/ssaohr_fshader.glsl" }, 'techniques': { 'ssao':[ { 'vshader' : 'blurVShader', 'fshader' : 'blurFShader', // attributes 'attributes' : { 'vert' : { 'type' : 'vec3' }, 'texcoord' : { 'type' : 'vec2' } }, // parameters 'params' : { 'u_normalsRT' : {'type' : "tex2d"}, 'u_depthMap' : {'type' : "tex2d"}, 'sRandMap' : {'type' : "tex2d", 'data':"assets/images/random_normal.png", 'wrap':"REPEAT", 'mips':false}, 'u_cameraFTR' : {'type' :'vec3'}, 'u_artVals' : {'type' :'vec4', 'data':[0.36, 0.75, 0.60, 0.05]}, // sample radius, intensity, distScale, bias 'u_randMapSize' : {'type' :'float', 'data':[64.0]}, 'u_screenSize' : {'type' :'vec2', 'data':[1024, 1024]} }, // render states 'states' : { 'culling' : false } } ] }} /* * Shadow map generation */ rdgeShadowMapShader = { 'shaders': { 'shadowMapVShader':"assets/shaders/shadowMap_vshader.glsl", 'shadowMapFShader':"assets/shaders/shadowMap_fshader.glsl" }, 'techniques': { 'shadowMap':[ { 'vshader' : 'shadowMapVShader', 'fshader' : 'shadowMapFShader', // attributes 'attributes' : { 'a_pos' : { 'type' : 'vec3' }, 'a_uv' : { 'type' : 'vec2' } }, // parameters 'params' : { 'u_lightSize' : { 'type' : 'float', 'data' : [7.93]}, // color is inverted, alpha represents intensity and is not inverted 'u_shadowColor' : { 'type' : 'vec4', 'data' : [0.922, 0.7373, 0.4824, 0.5]} }, // render states 'states' : { 'depthEnable' : true, 'blendEnable' : false, 'culling' : true, 'cullFace' : "BACK" } } ] }} /* * Noise blur filter */ rdgeNoiseBlurShader = { 'shaders': { 'blurVShader':"assets/shaders/noiseBlur_vshader.glsl", 'blurFShader':"assets/shaders/noiseBlur_fshader.glsl" }, 'techniques': { 'blur':[ { 'vshader' : 'blurVShader', 'fshader' : 'blurFShader', // attributes 'attributes' : { 'a_pos' : { 'type' : 'vec3' }, 'a_uv' : { 'type' : 'vec2' } }, // parameters 'params' : { 'u_blurSourceMap': {'type' : "tex2d"} }, // render states 'states' : { 'culling' : false } } ] }} /* * defines a primitive using java script native types */ function rdgePrimitiveDefinition() { /* * The type of primitive * supported types are * renderer.POINTS * renderer.LINES * renderer.LINE_LOOP * renderer.LINE_STRIP * renderer.TRIANGLES * renderer.TRIANGLE_STRIP * renderer.TRIANGLE_FAN */ this.type = rdgeConstants.TRIANGLE_STRIP; /* * Define vertex elements size in bytes and order the element appears in the stream * Predefined size types: * renderer.VS_ELEMENT_FLOAT4 * renderer.VS_ELEMENT_FLOAT3 * renderer.VS_ELEMENT_FLOAT2 * renderer.VS_ELEMENT_FLOAT * renderer.VS_ELEMENT_UINT16 * renderer.VS_ELEMENT_UINT8 */ this.vertexDefinition= { // usage example: two ways of declaring a float 3 stream //"vert" :{ 'type':renderer.VS_ELEMENT_POS, 'bufferIndex':indexIntoBufferStream, 'usage': renderer.BUFFER_STATIC } //"a_pos" :{ 'type':renderer.VS_ELEMENT_POS, 'bufferIndex':indexIntoBufferStream, 'usage': renderer.BUFFER_STATIC } }; /* * Arrays of buffer data listed in the order given by the vertex definition */ this.bufferStreams= [ ]; /* * An array indicating the the stream usage listed in the order given by the vertex definition * Valid usage values: renderer.BUFFER_STATIC The data store contents will be specified once by the application, and used many times * renderer.BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * renderer.BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times */ this.streamUsage= [ ]; /* * indicates the the stream usage of the index buffer * Valid usage values: renderer.BUFFER_STATIC The data store contents will be specified once by the application, and used many times * renderer.BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * renderer.BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times */ this.indexUsage = rdgeConstants.BUFFER_STREAM; /* * a references to an array of indices */ this.indexBuffer= [ ]; /* * the number of sets of geometry in this primitive */ this.setCount = 0; /* * Offset of a primitive in an index buffer * if the primitive represents a set each set array has a corresponding index offset */ this.indexOffsets = [ ]; ////////////////////////////////////////////////////////////// // OUTPUT VALUES // ////////////////////////////////////////////////////////////// /* * the number of coordinates's that make this primitive * auto populated with a call to renderer.createPrimitive */ this.posCount = 0; /* * the number of triangle that make up this primitive * auto populated with a call to renderer.createPrimitive */ this.triCount = 0; /* * the number of indices in the primitive * auto populated with a call to renderer.createPrimitive */ this.indexCount = 0; /* * size of an index value in bytes */ this.indexElementSize = rdgeConstants.UNSIGNED_SHORT; /* * bufferHanldes are created when passing a primitive to renderer.createPrimitive * An array of handles to buffers in vram, listed in the order given by the vertex definition */ this.bufferHandles = [ ]; /* * An index into the buffers table for the render context that created the buffer */ this.buffersID = -1; /* * Handle to index buffer object */ this.indexHandle = null; /* * ------------------- Double buffer Setup----------------------------- */ /* * user flag used to create a double buffered primitive * Double buffer flag - when double buffered a .front() and .back() buffer * are available as well as a call .flip() to flip the buffers */ this.useDoubleBuffer = false; /* * The double buffer offset tells the renderer where in its 'bufferID' array for this primitive the 'doubled' buffers begin */ this.doubleBufferOffset = 0; /* * Keeps track of which buffer is the front buffer or active buffer */ this.frontBufferIndex = 0; /* * Helper function for retrieving the buffer for editing - successfully calling this makes the buffer dirty and will trigger a buffer swap * @param bufIndex - buffer stream index * @return buffer stream requested, or null if invalid index is given */ this.update = function( bufIndex ) { if(!this.bufferStreams[bufIndex]) return null; this.bufferStreams[bufIndex].dirty = true; return this.bufferStreams[bufIndex]; } /* * Flips the front and back buffers */ this.flip = function( renderer ) { if(this.useDoubleBuffer === true) { // if a back buffer is dirty update it for(var i = 0, len = this.bufferStreams.length; i < len; ++i) { if(this.bufferStreams[i].dirty) { this.bufferStreams[i].dirty = false; renderer.updateBuffer(renderer.buffers[this.buffersID][this.frontBufferIndex*this.doubleBufferOffset + i], this.bufferStreams[i], this.streamUsage[i]); this.frontBufferIndex = 1 - this.frontBufferIndex; } } } } } /* * @param bufferSizeOrData: an array of indices, or the size in bytes to preallocate * @param bufferUsage: BUFFER_STATIC The data store contents will be specified once by the application, and used many times * BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times * @return an unsigned short index buffer object */ _renderer.prototype.createIndexBufferUINT16 = function( bufferSizeOrData, bufferUsage ) { var bufferObject = this.ctx.createBuffer(); bufferObject.type = bufferSizeOrData.type; this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, bufferObject); this.ctx.bufferData(this.ctx.ELEMENT_ARRAY_BUFFER, (typeof k == "number") ? bufferSizeOrData : new Uint16Array(bufferSizeOrData), bufferUsage); return bufferObject; } /* * @param bufferSizeOrData: an array of indices, or the size in bytes to preallocate * @param bufferUsage: BUFFER_STATIC The data store contents will be specified once by the application, and used many times * BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times * @return an unsigned byte index buffer object */ _renderer.prototype.createIndexBufferUINT8 = function( bufferSizeOrData, bufferUsage ) { var bufferObject = this.ctx.createBuffer(); bufferObject.type = bufferSizeOrData.type; this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, bufferObject); this.ctx.bufferData(this.ctx.ELEMENT_ARRAY_BUFFER, (typeof k == "number") ? bufferSizeOrData : new Uint8Array(bufferSizeOrData), bufferUsage); return bufferObject; } /* * @param bufferSizeOrData: a buffer of data the represents a stream in a vertex, or the size in bytes to preallocate * @param bufferUsage: BUFFER_STATIC The data store contents will be specified once by the application, and used many times * BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times * @return an unsigned byte index buffer object */ _renderer.prototype.createBufferFLOAT32 = function( bufferSizeOrData, bufferUsage ) { var bufferObject = this.ctx.createBuffer(); bufferObject.type = bufferSizeOrData.type; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, bufferObject); this.ctx.bufferData(this.ctx.ARRAY_BUFFER, (typeof k == "number") ? bufferSizeOrData : new Float32Array(bufferSizeOrData), bufferUsage); return bufferObject; } /* * @param bufferSizeOrData: a buffer of data the represents a stream in a vertex, or the size in bytes to preallocate * @param bufferUsage: BUFFER_STATIC The data store contents will be specified once by the application, and used many times * BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times * @return an unsigned byte index buffer object */ _renderer.prototype.createBufferINT32 = function(bufferSizeOrData,bufferUsage) { var bufferObject=this.ctx.createBuffer(); bufferObject.type=bufferSizeOrData.type; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, bufferObject); this.ctx.bufferData(this.ctx.ARRAY_BUFFER, (typeof k == "number") ? bufferSizeOrData : new Int32Array(bufferSizeOrData), bufferUsage); return bufferObject; } /* * @param bufferSizeOrData: a buffer of data the represents a stream in a vertex, or the size in bytes to preallocate * @param bufferUsage: BUFFER_STATIC The data store contents will be specified once by the application, and used many times * BUFFER_DYNAMIC The data store contents will be respecified repeatedly by the application, and used many times * BUFFER_STREAM The data store contents will be specified once by the application, and used at most a few times * @return an unsigned byte index buffer object */ _renderer.prototype.createBufferINT16 = function(bufferSizeOrData,bufferUsage) { var bufferObject=this.ctx.createBuffer(); bufferObject.type=bufferSizeOrData.type; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, bufferObject); this.ctx.bufferData(this.ctx.ARRAY_BUFFER, (typeof k == "number") ? bufferSizeOrData : new Int16Array(bufferSizeOrData), bufferUsage); return bufferObject; } _renderer.prototype.updateBuffer = function( dstBuffer, srcBuffer, bufferUsage, vertexOffset ) { this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, dstBuffer); if(bufferUsage === rdgeConstants.BUFFER_DYNAMIC) { // use bufferSubData this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, vertexOffset || 0, new Float32Array(srcBuffer)); } else { // re-create the buffer this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(srcBuffer), bufferUsage); } } /* * @param primitiveDef defines a primitive using java script types, creates the video ram objects and attaches them to the primitive passed in */ _renderer.prototype.createPrimitive = function( primitiveDef ) { if(!primitiveDef.built) { // store the buffer handle with the renderer as it is context specific, creating an ID to look up the buffer at render time primitiveDef.buffersID = getBufferID(); primitiveDef.built = true; } else if(this.buffers[primitiveDef.buffersID]) { // already created return; } // this mapping in the array holds an array of buffers this.buffers[primitiveDef.buffersID] = []; this.buffers[primitiveDef.buffersID].ctxId = this.id; // set up buffers this.updatePrimitive(primitiveDef); } /* * @param primitiveDef defines a primitive using java script types, creates the video ram objects and attaches them to the primitive passed in */ _renderer.prototype.updatePrimitive = function( prim ) { if(!prim.built) { this.createPrimitive(prim); return; } var bufIdxVisited = []; for(var e in prim.vertexDefinition) { var vert_element = prim.vertexDefinition[e]; if(bufIdxVisited.indexOf(vert_element.bufferIndex) > -1) continue; bufIdxVisited.push(vert_element.bufferIndex); vert_element.debugName = e + " buffer"; // flag the positional data if(vert_element.type == this.VS_ELEMENT_POS) { prim.posCount = prim.bufferStreams[vert_element.bufferIndex].length; // save a reference to the coordinates for later prim.positions = prim.bufferStreams[vert_element.bufferIndex]; } // if we have not already created the buffer - do so now if(this.buffers[prim.buffersID][vert_element.bufferIndex] == undefined) { prim.bufferStreams[vert_element.bufferIndex].type = e + " PrimaryBuffer"; if(prim.forceVertexCount) { this.buffers[prim.buffersID][vert_element.bufferIndex] = this.createBufferFLOAT32( 4 * prim.forceVertexCount, vert_element.bufferUsage ); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, new Float32Array(prim.bufferStreams[vert_element.bufferIndex]), vert_element.bufferUsage ); } else { this.buffers[prim.buffersID][vert_element.bufferIndex] = this.createBufferFLOAT32( prim.bufferStreams[vert_element.bufferIndex], vert_element.bufferUsage ); } } else { this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers[prim.buffersID][vert_element.bufferIndex]); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, new Float32Array(prim.bufferStreams[vert_element.bufferIndex]), vert_element.bufferUsage ); } // double up the buffer if(prim.useDoubleBuffer === true) { prim.doubleBufferOffset = prim.bufferStreams.length // debug data prim.bufferStreams[vert_element.bufferIndex].type = e + " SecondaryBuffer"; // store double buffer at the doubleBuffer offset if(this.buffers[prim.buffersID][prim.doubleBufferOffset + vert_element.bufferIndex] == undefined) { if(prim.forceVertexCount) { this.buffers[prim.buffersID][prim.doubleBufferOffset + vert_element.bufferIndex] = this.createBufferFLOAT32( 4 * prim.prim.forceVertexCount, vert_element.bufferUsage ); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, new Float32Array(prim.bufferStreams[vert_element.bufferIndex]), vert_element.bufferUsage ); } else { this.buffers[prim.buffersID][prim.doubleBufferOffset + vert_element.bufferIndex] = this.createBufferFLOAT32( prim.bufferStreams[vert_element.bufferIndex], vert_element.bufferUsage ); } } else { this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers[prim.buffersID][prim.doubleBufferOffset + vert_element.bufferIndex]); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, new Float32Array(prim.bufferStreams[vert_element.bufferIndex]), vert_element.bufferUsage ); } } } if(prim.indexBuffer.length > 0) { var indexBufLength = prim.indexBuffer.length; prim.indexBuffer.debugName="index Buffer"; if(this.buffers[prim.buffersID].indexHandle == undefined) { if(prim.forceIndexCount) { this.buffers[prim.buffersID].indexHandle = this.createIndexBufferUINT16( 2 * prim.forceIndexCount, prim.indexUsage ); this.ctx.bufferSubData(this.ctx.ELEMENT_ARRAY_BUFFER, 0, new Float32Array(prim.indexBuffer), prim.indexUsage ); } else { this.buffers[prim.buffersID].indexHandle = this.createIndexBufferUINT16(prim.indexBuffer,prim.indexUsage); } } else { this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, this.buffers[prim.buffersID].indexHandle); this.ctx.bufferSubData(this.ctx.ELEMENT_ARRAY_BUFFER, 0, new Float32Array(prim.indexBuffer), prim.indexUsage ); } prim.indexCount=indexBufLength; prim.triCount=indexBufLength/3; } else { prim.triCount=prim.posCount/3; } } /* * @param prim the primitive to delete from the GL context */ _renderer.prototype.deletePrimitive = function( prim ) { var buffers = this.buffers[prim.buffersID]; if (buffers) { var self = this; buffers.forEach(function(thisBuffer) { self.ctx.deleteBuffer(thisBuffer); }); delete this.buffers[prim.buffersID]; } } /* * @param prim the primitive for which to retrieve the GL VBO handle * @param bufferIndex which buffer name to retrieve */ _renderer.prototype.getPrimitiveBuffer = function( prim, bufferIndex ) { return this.buffers[prim.buffersID][bufferIndex]; } _renderer.prototype.drawPrimitive = function( prim, program, attribs ) { g_renderStats.numDrawCalls.value++; g_renderStats.numTriangles.value+=Math.floor(prim.triCount); g_renderStats.numVerts.value+=Math.floor(prim.coordCount/3); if(prim.indexCount) { this.drawIndexedPrimitive( prim, program, attribs ) } else { this.drawNonIndexedPrimitive( prim, program, attribs ) } if(prim.useDoubleBuffer === true) { // after drawing flip the buffer prim.flip(this); } } /* * Draws a single primitive using indices * @param: prim a single primitive */ _renderer.prototype.drawIndexedPrimitive = function(prim, program, attribs) { var bufferIndex = 0; var loc = 0; var buffersId = prim.buffersID; var name = ""; var attrCount = attribs.length var attrIdx = 0; var dbOffset = prim.frontBufferIndex * prim.doubleBufferOffset; var ctx = g_Engine.getContext(); for (; attrIdx < attrCount; ++attrIdx) { loc = attribs[attrIdx].loc; name = attribs[attrIdx].name; if (!prim.vertexDefinition[name]) continue; bufferIndex = prim.vertexDefinition[name].bufferIndex; this.ctx.enableVertexAttribArray(loc); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers[buffersId][dbOffset + bufferIndex]); this.ctx.vertexAttribPointer(loc, prim.vertexDefinition[name].type, this.FLOAT, false, 0, 0); } this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, this.buffers[buffersId].indexHandle); this.ctx.drawElements(prim.type, prim.indexCount, prim.indexElementSize, 0); for (attrIdx = 0; attrIdx < attrCount; ++attrIdx) { this.ctx.disableVertexAttribArray(attribs[attrIdx].loc); } } /* * Draws a single primitive using indices in wireframe mode * @param: prim a single primitive */ _renderer.prototype.drawIndexedPrimitiveWireFrame = function( prim, program, attribs ) { var bufferIndex = 0; var loc = 0; var buffersId = prim.buffersID; var name =""; var attrCount = attribs.length var attrIdx = 0; var dbOffset = prim.frontBufferIndex*prim.doubleBufferOffset; for(; attrIdx < attrCount; ++attrIdx) { loc = attribs[attrIdx].loc; name = attribs[attrIdx].name; if(!prim.vertexDefinition[name]) continue; bufferIndex = prim.vertexDefinition[name].bufferIndex; this.ctx.enableVertexAttribArray(loc); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers[buffersId][dbOffset + bufferIndex]); this.ctx.vertexAttribPointer(loc, prim.vertexDefinition[name].type, this.FLOAT ,false, 0, 0); } this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, this.buffers[buffersId].indexHandle); this.ctx.drawElements( this.LINE_LOOP, prim.indexCount, prim.indexElementSize, 0); for(attrIdx = 0; attrIdx < attrCount; ++attrIdx) { this.ctx.disableVertexAttribArray(attribs[attrIdx].loc); } } /* * Draws a single primitive (non-indexed primitive) * @param: prim a single primitive */ _renderer.prototype.drawNonIndexedPrimitive = function( prim, program, attribs ) { var bufferIndex = 0; var loc = 0; var buffersId = prim.buffersID; var name =""; var attrCount = attribs.length var attrIdx = 0; var dbOffset = prim.frontBufferIndex*prim.doubleBufferOffset; for(; attrIdx < attrCount; ++attrIdx) { loc = attribs[attrIdx].loc; name = attribs[attrIdx].name; bufferIndex = prim.vertexDefinition[name].bufferIndex; if(!prim.vertexDefinition[name]) continue; this.ctx.enableVertexAttribArray(loc); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers[buffersId][dbOffset + bufferIndex]); this.ctx.vertexAttribPointer(loc, prim.vertexDefinition[name].type, this.FLOAT ,false, 0, 0); } this.ctx.drawArrays( prim.type, 0, prim.triCount); for(attrIdx = 0; attrIdx < attrCount; ++attrIdx) { this.ctx.disableVertexAttribArray(attribs[attrIdx].loc); } } /* * Not yet supported but stubbed out here */ _renderer.prototype.drawIndexedPrimitiveSet = function( prim ) { window.alert("drawIndexedPrimitiveSet is not implemented"); for(var i =0; i < prim.setCount; ++i) { this.ctx.drawElements(prim.type[i], mesh.numIndices[i], prim.indexElementSize[i], prim.indexOffsets[i]); } } renderDebug = function(maxLines) { this.renderer = g_Engine.getContext().renderer; this.ctx = g_Engine.getContext().renderer.ctx; this.hidden = false; this.maxLines = maxLines; this.lineBuffer = []; this.posBufferData = new Float32Array(3 * this.maxLines); this.colorBufferData = new Float32Array(4 * this.maxLines); this.posBufferObject = renderer.createBufferFLOAT32(this.posBufferData, this.renderer.BUFFER_DYNAMIC); this.colorBufferObject = renderer.createBufferFLOAT32(this.colorBufferData, this.renderer.BUFFER_DYNAMIC); this.shader = new jshader(); this.shader.def = { 'shaders': { 'defaultVShader': "\ uniform mat4 u_mvMatrix;\ uniform mat4 u_projMatrix;\ attribute vec3 a_pos;\ attribute vec4 a_color;\ varying vec4 v_color;\ void main() {\ gl_Position = u_projMatrix * u_mvMatrix * vec4(a_pos,1.0);\ v_color = a_color;\ }", 'defaultFShader': "\ precision mediump float;\ varying vec4 v_color;\ void main() {\ gl_FragColor = v_color;\ gl_FragColor.a = 1.0;\ }" }, 'techniques': { 'defaultTechnique': [{ 'vshader' : 'defaultVShader', 'fshader' : 'defaultFShader', 'attributes' : { 'a_pos' : { 'type' : 'vec3' }, 'a_color' : { 'type' : 'vec4' } }, 'params' : { 'u_projMat' : { 'type' : 'mat4' }, 'u_viewMat' : { 'type' : 'mat4' } } }] } } this.shader.init(); } renderDebug.prototype.hide = function() { this.hidden = true; } renderDebug.prototype.show = function() { this.hidden = false; } renderDebug.prototype.line = function(p0, p1, c0, c1 ) { if( this.hidden ) { return; } if( c0 == undefined ) { c0 = [1.0,1.0,1.0,1.0]; } if( c1 == undefined ) { c1 = c0; } this.lineBuffer.push( [ p0, p1, c0, c1 ] ); } renderDebug.prototype.box = function(min, max, c) { this.line([min[0],min[1],min[2]], [max[0],min[1],min[2]], c, c); this.line([max[0],min[1],min[2]], [max[0],max[1],min[2]], c, c); this.line([max[0],max[1],min[2]], [min[0],max[1],min[2]], c, c); this.line([min[0],max[1],min[2]], [min[0],min[1],min[2]], c, c); this.line([min[0],min[1],max[2]], [max[0],min[1],max[2]], c, c); this.line([max[0],min[1],max[2]], [max[0],max[1],max[2]], c, c); this.line([max[0],max[1],max[2]], [min[0],max[1],max[2]], c, c); this.line([min[0],max[1],max[2]], [min[0],min[1],max[2]], c, c); this.line([max[0],min[1],min[2]], [max[0],min[1],max[2]], c, c); this.line([max[0],max[1],min[2]], [max[0],max[1],max[2]], c, c); this.line([min[0],max[1],min[2]], [min[0],max[1],max[2]], c, c); this.line([min[0],min[1],min[2]], [min[0],min[1],max[2]], c, c); } renderDebug.prototype.frustum = function(p, c) { this.line(p[0], p[1], c, c); this.line(p[1], p[2], c, c); this.line(p[2], p[3], c, c); this.line(p[3], p[4], c, c); this.line(p[4], p[5], c, c); this.line(p[5], p[6], c, c); this.line(p[6], p[7], c, c); } renderDebug.prototype.sphere = function(p, r, c) { var rho = 0.0; var rho_step = Math.PI / 8.0; var phi = 0.0; var phi_step = 2.0 * Math.PI / 8.0; while( rho < Math.PI ) { var srho = Math.sin( rho ); var crho = Math.cos( rho ); while( phi < 2.0 * Math.PI ) { var sphi = Math.sin(phi); var cphi = Math.cos(phi); var x0 = p[0] + cphi * srho * r; var y0 = p[1] + crho * r; var z0 = p[2] + sphi * srho * r; phi += phi_step; sphi = Math.sin(phi); cphi = Math.cos(phi); var x1 = p[0] + cphi * srho * r; var y1 = p[1] + crho * r; var z1 = p[2] + sphi * srho * r; phi += phi_step; this.line([x0,y0,z0], [x1,y1,z1], c); } rho += rho_step; } } renderDebug.prototype.flush = function() { var drawCalls = 0; var activeCam = this.renderer.cameraManager().getActiveCamera(); mat4.inplace_copy(this.ctx.projectionMatrix, activeCam.proj); mat4.inplace_copy(this.ctx.mvMatrix, activeCam.view); while( this.lineBuffer.length > 0 ) { var count = Math.min( this.lineBuffer.length, this.maxLines ); var index = 0; while( count > 0 ) { var line = this.lineBuffer.shift(); var i6 = index * 6; var i8 = index * 8; this.posBufferData[i6+0] = line[0][0]; this.posBufferData[i6+1] = line[0][1]; this.posBufferData[i6+2] = line[0][2]; this.posBufferData[i6+3] = line[1][0]; this.posBufferData[i6+4] = line[1][1]; this.posBufferData[i6+5] = line[1][2]; this.colorBufferData[i8+0] = line[2][0]; this.colorBufferData[i8+1] = line[2][1]; this.colorBufferData[i8+2] = line[2][2]; this.colorBufferData[i8+3] = line[2][3]; this.colorBufferData[i8+4] = line[3][0]; this.colorBufferData[i8+5] = line[3][1]; this.colorBufferData[i8+6] = line[3][2]; this.colorBufferData[i8+7] = line[3][3]; count--; index++; if( count <= 0 ) { this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.posBufferObject); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, null); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, this.posBufferData); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.colorBufferObject); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, null); this.ctx.bufferSubData(this.ctx.ARRAY_BUFFER, 0, this.colorBufferData); this.ctx.disable(this.ctx.DEPTH_TEST); this.shader.begin(); this.shader.beginPass(0); this.ctx.enableVertexAttribArray(0); this.ctx.enableVertexAttribArray(1); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.posBufferObject); this.ctx.vertexAttribPointer(0, 3, this.ctx.FLOAT, false, 0, 0); this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.colorBufferObject); this.ctx.vertexAttribPointer(1, 4, this.ctx.FLOAT, false, 0, 0); this.ctx.drawArrays(this.ctx.LINES, 0, index * 2); this.shader.endPass(); this.shader.end(); this.ctx.enable(this.ctx.DEPTH_TEST); drawCalls++; this.ctx.finish(); this.ctx.flush(); break; } } } }