/* <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> */

var RDGE = RDGE || {};

// runtime globals
RDGE.rdgeConstants = (function () {
    return {
        // clear flags
        colorBuffer: 0x00004000,
        depthBuffer: 0x00000100,
        stencilBuffer: 0x00000400,

        // primitive types
        POINTS: 0,
        LINES: 1,
        LINE_LOOP: 2,
        LINE_STRIP: 3,
        TRIANGLES: 4,
        TRIANGLE_STRIP: 5,
        TRIANGLE_FAN: 6,

        // primitive data types
        BYTE: 0x1400,
        UNSIGNED_BYTE: 0x1401,
        SHORT: 0x1402,
        UNSIGNED_SHORT: 0x1403,
        INT: 0x1404,
        UNSIGNED_INT: 0x1405,
        FLOAT: 0x1406,

        // pre-defined vertex element type
        VS_ELEMENT_FLOAT4: 4,
        VS_ELEMENT_POS: 3,
        VS_ELEMENT_NORM: 3,
        VS_ELEMENT_FLOAT3: 3,
        VS_ELEMENT_FLOAT2: 2,
        VS_ELEMENT_UV: 2,
        VS_ELEMENT_FLOAT: 1,
        MAX_ELEM_TYPES: 7,

        // GL Definition of buffer types
        BUFFER_STATIC: 0x88E0,
        BUFFER_DYNAMIC: 0x88E4,
        BUFFER_STREAM: 0x88E8,

        // render constants
        MAX_MATERIAL_LIGHTS: 4,

        //	Material categories determine sorting materials support the following categories
        categoryEnumeration:
        {
            'BACKGROUND': 0,
            'OPAQUE': 1,
            'TRANSPARENT': 2,
            'ADDITIVE': 3,
            'TRANSLUCENT': 4,
            'FOREGROUND': 5,
            'MAX_CAT': 6
        },

        // Node types supported by the scene graph
        nodeType:
        {
            'TRNODE': 0,
            'MESHNODE': 1,
            'MATNODE': 2,
            'LIGHTNODE': 3
        }
    };
})();

RDGE._renderer = function (canvas) {
    /*
    *	Initialize the context associated with this canvas
    */
    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 RDGE.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 = RDGE.globals.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 = RDGE.globals.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_" + RDGE.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
*/
RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
// currently not used in Ninja
// RDGE.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
*/
RDGE.rdgePrimitiveDefinition = function () {
    /*
    *	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 = RDGE.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 = RDGE.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 = RDGE.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;
                }
            }
        }
    };
};


// generate an id for the renderer to map a render buffer to primitive
RDGE._renderer.prototype._rendererID = 0;
RDGE._renderer.prototype.getBufferID = function () {
    return RDGE._renderer.prototype._rendererID++;
};
/*
* @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
*/
RDGE._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
*/
RDGE._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
*/
RDGE._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
*/
RDGE._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
*/
RDGE._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;
};

RDGE._renderer.prototype.updateBuffer = function (dstBuffer, srcBuffer, bufferUsage, vertexOffset) {
    this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, dstBuffer);
    if (bufferUsage === RDGE.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
*/
RDGE._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 = this.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
*/
RDGE._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
*/
RDGE._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
*/
RDGE._renderer.prototype.getPrimitiveBuffer = function (prim, bufferIndex) {
    return this.buffers[prim.buffersID][bufferIndex];
};

RDGE._renderer.prototype.drawPrimitive = function (prim, program, attribs) {
    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
*/
RDGE._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 = RDGE.globals.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
*/
RDGE._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
*/
RDGE._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
*/
RDGE._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]);
    }
};

// currently not used in Ninja
/*
RDGE.renderDebug = function (maxLines) {
    this.renderer = RDGE.globals.engine.getContext().renderer;
    this.ctx = RDGE.globals.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 RDGE.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();
};

RDGE.renderDebug.prototype.hide = function () {
    this.hidden = true;
};

RDGE.renderDebug.prototype.show = function () {
    this.hidden = false;
};

RDGE.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]);
};

RDGE.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);
};

RDGE.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);
};

RDGE.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;
    }
};

RDGE.renderDebug.prototype.flush = function () {
    var drawCalls = 0;
    var activeCam = this.renderer.cameraManager().getActiveCamera();

    RDGE.mat4.inplace_copy(this.ctx.projectionMatrix, activeCam.proj);
    RDGE.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;
            }
        }
    }
};
*/