//this module is in charge of rendering basic objects like lines, points, and primitives
//it works over litegl (no need of scene)
//carefull, it is very slow
/**
* LS.Draw allows to render basic primitives, similar to the OpenGL Fixed pipeline.
* It reuses local meshes when possible to avoid fragmenting the VRAM.
* @class Draw
* @constructor
*/
var Draw = {
ready: false,
images: {},
image_last_id: 1,
onRequestFrame: null,
reset_stack_on_reset: true,
_rendercalls: 0,
/**
* Sets up everything (prepare meshes, shaders, and so)
* @method init
*/
init: function()
{
if(this.ready)
return;
if(!gl)
return;
this.color = new Float32Array(4);
this.color[3] = 1;
this.mvp_matrix = mat4.create();
this.temp_matrix = mat4.create();
this.point_size = 2;
this.line_width = 1;
this.stack = new Float32Array(16 * 32); //stack max size
this.model_matrix = new Float32Array(this.stack.buffer,0,16);
mat4.identity( this.model_matrix );
//matrices
this.camera = null;
this.camera_position = vec3.create();
this.view_matrix = mat4.create();
this.projection_matrix = mat4.create();
this.viewprojection_matrix = mat4.create();
this.camera_stack = []; //not used yet
this.uniforms = {
u_model: this.model_matrix,
u_viewprojection: this.viewprojection_matrix,
u_mvp: this.mvp_matrix,
u_color: this.color,
u_camera_position: this.camera_position,
u_point_size: this.point_size,
u_point_perspective: 0,
u_perspective: 1, //viewport.w * this._projection_matrix[5]
u_texture: 0
};
//temp containers
this._temp = vec3.create();
//Meshes
var vertices = [[-1,1,0],[1,1,0],[1,-1,0],[-1,-1,0]];
var coords = [[0,1],[1,1],[1,0],[0,0]];
this.quad_mesh = GL.Mesh.load({vertices:vertices, coords: coords});
var vertex_shader = '\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
#ifdef USE_COLOR\n\
attribute vec4 a_color;\n\
varying vec4 v_color;\n\
#endif\n\
#ifdef USE_TEXTURE\n\
attribute vec2 a_coord;\n\
varying vec2 v_coord;\n\
#endif\n\
#ifdef USE_SIZE\n\
attribute float a_extra;\n\
#endif\n\
#ifdef USE_INSTANCING\n\
attribute mat4 u_model;\n\
#else\n\
uniform mat4 u_model;\n\
#endif\n\
uniform mat4 u_viewprojection;\n\
uniform float u_point_size;\n\
uniform float u_perspective;\n\
uniform float u_point_perspective;\n\
float computePointSize(float radius, float w)\n\
{\n\
if(radius < 0.0)\n\
return -radius;\n\
return u_perspective * radius / w;\n\
}\n\
void main() {\n\
#ifdef USE_TEXTURE\n\
v_coord = a_coord;\n\
#endif\n\
#ifdef USE_COLOR\n\
v_color = a_color;\n\
#endif\n\
vec3 vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\
gl_Position = u_viewprojection * vec4(vertex,1.0);\n\
gl_PointSize = u_point_size;\n\
#ifdef USE_SIZE\n\
gl_PointSize = a_extra;\n\
#endif\n\
if(u_point_perspective != 0.0)\n\
gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\
}\
';
var pixel_shader = '\
precision mediump float;\n\
uniform vec4 u_color;\n\
#ifdef USE_COLOR\n\
varying vec4 v_color;\n\
#endif\n\
#ifdef USE_TEXTURE\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
#endif\n\
void main() {\n\
vec4 color = u_color;\n\
#ifdef USE_TEXTURE\n\
color *= texture2D(u_texture, v_coord);\n\
if(color.a < 0.1)\n\
discard;\n\
#endif\n\
#ifdef USE_POINTS\n\
float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\
if( dist > 0.45 )\n\
discard;\n\
#endif\n\
#ifdef USE_COLOR\n\
color *= v_color;\n\
#endif\n\
gl_FragColor = color;\n\
}\
';
//create shaders
this.shader = new Shader( vertex_shader, pixel_shader );
this.shader_instancing = new Shader(vertex_shader,pixel_shader,{"USE_INSTANCING":""});
this.shader_color = new Shader(vertex_shader,pixel_shader,{"USE_COLOR":""});
this.shader_color_instancing = new Shader(vertex_shader,pixel_shader,{"USE_COLOR":"","USE_INSTANCING":""});
this.shader_texture = new Shader(vertex_shader,pixel_shader,{"USE_TEXTURE":""});
this.shader_texture_instancing = new Shader(vertex_shader,pixel_shader,{"USE_TEXTURE":"","USE_INSTANCING":""});
this.shader_points = new Shader(vertex_shader,pixel_shader,{"USE_POINTS":""});
this.shader_points_color = new Shader(vertex_shader,pixel_shader,{"USE_COLOR":"","USE_POINTS":""});
this.shader_points_color_size = new Shader(vertex_shader,pixel_shader,{"USE_COLOR":"","USE_SIZE":"","USE_POINTS":""});
this.shader_image = new Shader('\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
uniform mat4 u_mvp;\n\
uniform float u_point_size;\n\
void main() {\n\
gl_PointSize = u_point_size;\n\
gl_Position = u_mvp * vec4(a_vertex,1.0);\n\
}\
','\
precision mediump float;\n\
uniform vec4 u_color;\n\
uniform sampler2D u_texture;\n\
void main() {\n\
vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\n\
if(tex.a < 0.01)\n\
discard;\n\
gl_FragColor = u_color * tex;\n\
}\
');
this.shader_points_color_texture_size = new Shader('\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec4 a_color;\n\
attribute float a_extra;\n\
uniform mat4 u_mvp;\n\
uniform float u_point_size;\n\
varying vec4 v_color;\n\
void main() {\n\
v_color = a_color;\n\
gl_PointSize = u_point_size * a_extra;\n\
gl_Position = u_mvp * vec4(a_vertex,1.0);\n\
}\
','\
precision mediump float;\n\
uniform vec4 u_color;\n\
varying vec4 v_color;\n\
uniform sampler2D u_texture;\n\
void main() {\n\
vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\n\
if(tex.a < 0.1)\n\
discard;\n\
vec4 color = u_color * v_color * tex;\n\
gl_FragColor = color;\n\
}\
');
this.shader_text2D = new Shader('\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec4 a_extra4;\n\
uniform mat4 u_mvp;\n\
uniform float u_point_size;\n\
void main() {\n\
gl_PointSize = u_point_size;\n\
gl_Position = u_mvp * vec4(a_vertex,1.0);\n\
}\
','\
precision mediump float;\n\
uniform vec4 u_color;\n\
uniform sampler2D u_texture;\n\
void main() {\n\
vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\n\
if(tex.a < 0.1)\n\
discard;\n\
vec4 color = u_color * tex;\n\
gl_FragColor = color;\n\
}\
');
//create shaders
var phong_vertex_code = "\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
#ifdef USE_INSTANCING\n\
attribute mat4 u_model;\n\
#else\n\
uniform mat4 u_model;\n\
#endif\n\
uniform mat4 u_viewprojection;\n\
void main() {\n\
v_pos = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\
v_normal = (u_model * vec4(a_normal,0.0)).xyz;\n\
gl_Position = u_viewprojection * vec4( v_pos, 1.0 );\n\
}\n";
var phong_pixel_shader = "\n\
precision mediump float;\n\
uniform vec3 u_ambient_color;\n\
uniform vec3 u_light_color;\n\
uniform vec3 u_light_dir;\n\
uniform vec4 u_color;\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
void main() {\n\
vec3 N = normalize(v_normal);\n\
float NdotL = max(0.0, dot(N,u_light_dir));\n\
gl_FragColor = u_color * vec4(u_ambient_color + u_light_color * NdotL, 1.0);\n\
}\n";
this.shader_phong = new Shader( phong_vertex_code, phong_pixel_shader);
this.shader_phong_instanced = new Shader( phong_vertex_code, phong_pixel_shader, { "USE_INSTANCING":"" } );
var phong_uniforms = {u_ambient_color:[0.1,0.1,0.1], u_light_color:[0.8,0.8,0.8], u_light_dir: [0,1,0] };
this.shader_phong.uniforms( phong_uniforms );
this.shader_phong_instanced.uniforms( phong_uniforms );
//create shaders
this.shader_depth = new Shader('\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
varying vec4 v_pos;\n\
uniform mat4 u_model;\n\
uniform mat4 u_mvp;\n\
void main() {\n\
v_pos = u_model * vec4(a_vertex,1.0);\n\
gl_Position = u_mvp * vec4(a_vertex,1.0);\n\
}\
','\
precision mediump float;\n\
varying vec4 v_pos;\n\
\n\
vec4 PackDepth32(float depth)\n\
{\n\
const vec4 bitSh = vec4( 256*256*256, 256*256, 256, 1);\n\
const vec4 bitMsk = vec4( 0, 1.0/256.0, 1.0/256.0, 1.0/256.0);\n\
vec4 comp;\n\
comp = depth * bitSh;\n\
comp = fract(comp);\n\
comp -= comp.xxyz * bitMsk;\n\
return comp;\n\
}\n\
void main() {\n\
float depth = (v_pos.z / v_pos.w) * 0.5 + 0.5;\n\
gl_FragColor = PackDepth32(depth);\n\
}\
');
this.ready = true;
},
/**
* A helper to create shaders when you only want to specify some basic shading
* @method createSurfaceShader
* @param {string} surface_function GLSL code like: "vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { return vec4(1.0); } ";
* @param {object} macros [optional] object containing the macros and value
* @param {object} uniforms [optional] object with name and type
* @return {GL.Shader} the resulting shader
*/
createSurfaceShader: function( surface_function, uniforms, macros )
{
//"vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { return vec4(1.0); } ";
if( surface_function.indexOf("surface_function") == -1 )
surface_function = "vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { " + surface_function + "\n } ";
if(uniforms)
{
if (uniforms.constructor === String)
surface_function = uniforms + ";\n" + surface_function;
else
for(var i in uniforms)
surface_function += "uniform " + uniforms[i] + " " + i + ";\n";
}
var vertex_shader = "\
precision mediump float;\n\
attribute vec3 a_vertex;\n\
attribute vec3 a_normal;\n\
attribute vec2 a_coord;\n\
varying vec2 v_coord;\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
uniform mat4 u_mvp;\n\
uniform mat4 u_model;\n\
void main() {\n\
v_coord = a_coord;\n\
v_pos = (u_model * vec4(a_vertex,1.0)).xyz;\n\
v_normal = (u_model * vec4(a_normal,0.0)).xyz;\n\
gl_Position = u_mvp * vec4(a_vertex,1.0);\n\
}\
";
var pixel_shader = "\
precision mediump float;\n\
varying vec2 v_coord;\n\
varying vec3 v_pos;\n\
varying vec3 v_normal;\n\
uniform vec4 u_color;\n\
uniform vec3 u_camera_position;\n\
uniform sampler2D u_texture;\n\
"+ surface_function +"\n\
void main() {\n\
gl_FragColor = surface_function(v_pos,v_normal,v_coord);\n\
}\
";
return new GL.Shader( vertex_shader, pixel_shader, macros );
},
/**
* clears the stack
* @method reset
*/
reset: function( reset_memory )
{
if(!this.ready)
this.init();
else
{
this.color.set([1,1,1,1]);
this.point_size = 2;
this.line_width = 1;
}
if( reset_memory )
this.images = {}; //clear images
if(this.reset_stack_on_reset)
{
this.model_matrix = new Float32Array(this.stack.buffer,0,16);
this.uniforms.u_model = this.model_matrix;
mat4.identity( this.model_matrix );
}
},
/**
* Sets the color used to paint primitives
* @method setColor
* @param {vec3|vec4} color
*/
setColor: function(color)
{
if( arguments.length >= 3 )
{
this.color[0] = arguments[0];
this.color[1] = arguments[1];
this.color[2] = arguments[2];
if( arguments.length == 4 )
this.color[3] = arguments[3];
}
else
for(var i = 0; i < color.length; i++)
this.color[i] = color[i];
},
/**
* Sets the alpha used to paint primitives
* @method setAlpha
* @param {number} alpha
*/
setAlpha: function(alpha)
{
this.color[3] = alpha;
},
/**
* Sets the point size
* @method setPointSize
* @param {number} v size of points
* @param {number} perspective [optional] if set to true, the points will be affected by perspective
*/
setPointSize: function(v, perspective)
{
this.point_size = v;
this.uniforms.u_point_size = v;
this.uniforms.u_point_perspective = perspective ? 1 : 0;
},
/**
* Sets the line width
* @method setLineWidth
* @param {number} v width in pixels
*/
setLineWidth: function(v)
{
if(gl.setLineWidth)
gl.setLineWidth(v);
else
gl.lineWidth(v);
this.line_width = v;
},
/**
* Sets the camera to use during the rendering, this is already done by LS.Renderer
* @method setCamera
* @param {LS.Camera} camera
*/
setCamera: function( camera )
{
this.camera = camera;
camera.updateMatrices();
vec3.copy( this.camera_position, camera.getEye() );
this.view_matrix.set( camera._view_matrix );
this.projection_matrix.set( camera._projection_matrix );
this.viewprojection_matrix.set( camera._viewprojection_matrix );
this.uniforms.u_perspective = gl.viewport_data[3] * this.projection_matrix[5];
},
/**
* Specifies the camera position (used to compute point size)
* @method setCameraPosition
* @param {vec3} center
*/
setCameraPosition: function(center)
{
vec3.copy( this.camera_position, center);
},
pushCamera: function()
{
this.camera_stack.push( mat4.create( this.viewprojection_matrix ) );
},
popCamera: function()
{
if(this.camera_stack.length == 0)
throw("too many pops");
this.viewprojection_matrix.set( this.camera_stack.pop() );
},
/**
* Specifies the camera view and projection matrices
* @method setViewProjectionMatrix
* @param {mat4} view
* @param {mat4} projection
* @param {mat4} vp viewprojection matrix [optional]
*/
setViewProjectionMatrix: function(view, projection, vp)
{
mat4.copy( this.view_matrix, view);
mat4.copy( this.projection_matrix, projection);
if(vp)
mat4.copy( this.viewprojection_matrix, vp);
else
mat4.multiply( this.viewprojection_matrix, view, vp);
},
/**
* Specifies the transformation matrix to apply to the mesh
* @method setMatrix
* @param {mat4} matrix
*/
setMatrix: function(matrix)
{
mat4.copy(this.model_matrix, matrix);
},
/**
* Multiplies the current matrix by a given one
* @method multMatrix
* @param {mat4} matrix
*/
multMatrix: function(matrix)
{
mat4.multiply(this.model_matrix, matrix, this.model_matrix);
},
/**
* Render lines given a set of points
* @method renderLines
* @param {Float32Array|Array} points
* @param {Float32Array|Array} colors [optional]
* @param {bool} strip [optional] if the lines are a line strip (one consecutive line)
* @param {bool} loop [optional] if strip, close loop
*/
renderLines: function(lines, colors, strip, loop)
{
if(!lines || !lines.length) return;
var vertices = null;
vertices = lines.constructor == Float32Array ? lines : this.linearize(lines);
if(colors)
colors = colors.constructor == Float32Array ? colors : this.linearize(colors);
if(colors && (colors.length/4) != (vertices.length/3))
colors = null;
var type = gl.LINES;
if(loop)
type = gl.LINE_LOOP;
else if(strip)
type = gl.LINE_STRIP;
var mesh = this.toGlobalMesh({vertices: vertices, colors: colors});
return this.renderMesh( mesh, type, colors ? this.shader_color : this.shader, undefined, 0, vertices.length / 3 );
},
/**
* Render points given a set of positions (and colors)
* @method renderPoints
* @param {Float32Array|Array} points
* @param {Float32Array|Array} colors [optional]
* @param {GL.Shader} shader [optional]
*/
renderPoints: function(points, colors, shader)
{
if(!points || !points.length)
return;
var vertices = null;
if(points.constructor == Float32Array)
vertices = points;
else if(points[0].length) //array of arrays
vertices = this.linearize(points);
else
vertices = new Float32Array(points);
if(colors && colors.constructor != Float32Array)
{
if(colors.constructor === Array && colors[0].constructor === Number)
colors = new Float32Array( colors );
else
colors = this.linearize(colors);
}
var mesh = this.toGlobalMesh({vertices: vertices, colors: colors});
if(!shader)
shader = colors ? this.shader_color : this.shader;
return this.renderMesh(mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );
},
/**
* Render round points given a set of positions (and colors)
* @method renderRoundPoints
* @param {Float32Array|Array} points
* @param {Float32Array|Array} colors [optional]
* @param {GL.Shader} shader [optional]
*/
renderRoundPoints: function(points, colors, shader)
{
if(!points || !points.length)
return;
var vertices = null;
if(points.constructor == Float32Array)
vertices = points;
else if(points[0].length) //array of arrays
vertices = this.linearize(points);
else
vertices = new Float32Array(points);
if(colors)
colors = colors.constructor == Float32Array ? colors : this.linearize(colors);
var mesh = this.toGlobalMesh({vertices: vertices, colors: colors});
if(!shader)
shader = colors ? this.shader_points_color : this.shader_points;
return this.renderMesh( mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );
},
/**
* Render points with color, size, and texture binded in 0
* @method renderPointsWithSize
* @param {Float32Array|Array} points
* @param {Float32Array|Array} colors [optional]
* @param {Float32Array|Array} sizes [optional]
* @param {GL.Texture} texture [optional]
* @param {GL.Shader} shader [optional]
*/
renderPointsWithSize: function(points, colors, sizes, texture, shader)
{
if(!points || !points.length) return;
var vertices = null;
if(points.constructor == Float32Array)
vertices = points;
else if(points[0].length) //array of arrays
vertices = this.linearize(points);
else
vertices = new Float32Array(points);
if(!colors)
throw("colors required in Draw.renderPointsWithSize");
colors = colors.constructor == Float32Array ? colors : this.linearize(colors);
if(!sizes)
throw("sizes required in Draw.renderPointsWithSize");
sizes = sizes.constructor == Float32Array ? sizes : this.linearize(sizes);
var mesh = this.toGlobalMesh({vertices: vertices, colors: colors, extra: sizes});
shader = shader || (texture ? this.shader_points_color_texture_size : this.shader_points_color_size);
return this.renderMesh(mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );
},
createRectangleMesh: function(width, height, in_z, use_global)
{
var vertices = new Float32Array(4 * 3);
if(in_z)
vertices.set([-width*0.5,0,height*0.5, width*0.5,0,height*0.5, width*0.5,0,-height*0.5, -width*0.5,0,-height*0.5]);
else
vertices.set([-width*0.5,height*0.5,0, width*0.5,height*0.5,0, width*0.5,-height*0.5,0, -width*0.5,-height*0.5,0]);
if(use_global)
return this.toGlobalMesh( {vertices: vertices} );
return GL.Mesh.load({vertices: vertices});
},
/**
* Render a wireframe rectangle of width x height
* @method renderRectangle
* @param {number} width
* @param {number} height
* @param {boolean} in_z [optional] if the plane is aligned with the z plane
*/
renderRectangle: function(width, height, in_z)
{
var mesh = this.createRectangleMesh(width, height, in_z, true);
return this.renderMesh( mesh, gl.LINE_LOOP, undefined, undefined, 0, this._global_mesh_last_size );
},
createCircleMesh: function(radius, segments, in_z, use_global)
{
segments = segments || 32;
var axis = [0,1,0];
var num_segments = segments || 100;
var R = quat.create();
var temp = this._temp;
var vertices = new Float32Array(num_segments * 3);
var offset = 2 * Math.PI / num_segments;
for(var i = 0; i < num_segments; i++)
{
temp[0] = Math.sin(offset * i) * radius;
if(in_z)
{
temp[1] = 0;
temp[2] = Math.cos(offset * i) * radius;
}
else
{
temp[2] = 0;
temp[1] = Math.cos(offset * i) * radius;
}
vertices.set(temp, i*3);
}
if(use_global)
return this.toGlobalMesh({vertices: vertices});
return GL.Mesh.load({vertices: vertices});
},
/**
* Renders a circle
* @method renderCircle
* @param {number} radius
* @param {number} segments
* @param {boolean} in_z [optional] if the circle is aligned with the z plane
* @param {boolean} filled [optional] renders the interior
*/
renderCircle: function(radius, segments, in_z, filled)
{
var mesh = this.createCircleMesh(radius, segments, in_z, true);
return this.renderMesh(mesh, filled ? gl.TRIANGLE_FAN : gl.LINE_LOOP, undefined, undefined, 0, this._global_mesh_last_size );
},
/**
* Render a filled circle
* @method renderSolidCircle
* @param {number} radius
* @param {number} segments
* @param {boolean} in_z [optional] if the circle is aligned with the z plane
*/
renderSolidCircle: function(radius, segments, in_z)
{
return this.renderCircle(radius, segments, in_z, true);
},
createWireSphereMesh: function(radius, segments, use_global )
{
var axis = [0,1,0];
segments = segments || 100;
var R = quat.create();
var temp = this._temp;
var vertices = new Float32Array( segments * 2 * 3 * 3);
var delta = 1.0 / segments * Math.PI * 2;
for(var i = 0; i < segments; i++)
{
temp.set([ Math.sin( i * delta) * radius, Math.cos( i * delta) * radius, 0]);
vertices.set(temp, i*18);
temp.set([Math.sin( (i+1) * delta) * radius, Math.cos( (i+1) * delta) * radius, 0]);
vertices.set(temp, i*18 + 3);
temp.set([ Math.sin( i * delta) * radius, 0, Math.cos( i * delta) * radius ]);
vertices.set(temp, i*18 + 6);
temp.set([Math.sin( (i+1) * delta) * radius, 0, Math.cos( (i+1) * delta) * radius ]);
vertices.set(temp, i*18 + 9);
temp.set([ 0, Math.sin( i * delta) * radius, Math.cos( i * delta) * radius ]);
vertices.set(temp, i*18 + 12);
temp.set([ 0, Math.sin( (i+1) * delta) * radius, Math.cos( (i+1) * delta) * radius ]);
vertices.set(temp, i*18 + 15);
}
if(use_global)
return this.toGlobalMesh({vertices: vertices});
return GL.Mesh.load({vertices: vertices});
},
/**
* Renders three circles to form a simple spherical shape
* @method renderWireSphere
* @param {number} radius
* @param {number} segments
*/
renderWireSphere: function(radius, segments)
{
var mesh = this.createWireSphereMesh( radius, segments, true );
return this.renderMesh( mesh, gl.LINES, undefined, undefined, 0, this._global_mesh_last_size );
},
/**
* Renders an sphere
* @method renderSolidSphere
* @param {number} radius
*/
renderSolidSphere: function(radius)
{
var mesh = this._sphere_mesh;
if(!this._sphere_mesh)
mesh = this._sphere_mesh = GL.Mesh.sphere({ size: 1 });
this.push();
this.scale( radius,radius,radius );
this.renderMesh( mesh, gl.TRIANGLES );
this.pop();
},
createWireBoxMesh: function( sizex, sizey, sizez, use_global )
{
sizex = sizex*0.5;
sizey = sizey*0.5;
sizez = sizez*0.5;
var vertices = new Float32Array([-sizex,sizey,sizez , -sizex,sizey,-sizez, sizex,sizey,-sizez, sizex,sizey,sizez,
-sizex,-sizey,sizez, -sizex,-sizey,-sizez, sizex,-sizey,-sizez, sizex,-sizey,sizez]);
var triangles = new Uint16Array([0,1, 0,4, 0,3, 1,2, 1,5, 2,3, 2,6, 3,7, 4,5, 4,7, 6,7, 5,6 ]);
if(use_global)
return this.toGlobalMesh( {vertices: vertices}, triangles );
return GL.Mesh.load({vertices: vertices, lines:triangles });
},
/**
* Renders a wire box (box made of lines, not filled)
* @method renderWireBox
* @param {number} sizex
* @param {number} sizey
* @param {number} sizez
*/
renderWireBox: function(sizex,sizey,sizez)
{
var mesh = this.createWireBoxMesh(sizex,sizey,sizez, true);
return this.renderMesh( mesh, gl.LINES, undefined, "indices", 0, this._global_mesh_last_size );
},
createSolidBoxMesh: function( sizex,sizey,sizez, use_global)
{
sizex = sizex*0.5;
sizey = sizey*0.5;
sizez = sizez*0.5;
//var vertices = [[-sizex,sizey,-sizez],[-sizex,-sizey,+sizez],[-sizex,sizey,sizez],[-sizex,sizey,-sizez],[-sizex,-sizey,-sizez],[-sizex,-sizey,+sizez],[sizex,sizey,-sizez],[sizex,sizey,sizez],[sizex,-sizey,+sizez],[sizex,sizey,-sizez],[sizex,-sizey,+sizez],[sizex,-sizey,-sizez],[-sizex,sizey,sizez],[sizex,-sizey,sizez],[sizex,sizey,sizez],[-sizex,sizey,sizez],[-sizex,-sizey,sizez],[sizex,-sizey,sizez],[-sizex,sizey,-sizez],[sizex,sizey,-sizez],[sizex,-sizey,-sizez],[-sizex,sizey,-sizez],[sizex,-sizey,-sizez],[-sizex,-sizey,-sizez],[-sizex,sizey,-sizez],[sizex,sizey,sizez],[sizex,sizey,-sizez],[-sizex,sizey,-sizez],[-sizex,sizey,sizez],[sizex,sizey,sizez],[-sizex,-sizey,-sizez],[sizex,-sizey,-sizez],[sizex,-sizey,sizez],[-sizex,-sizey,-sizez],[sizex,-sizey,sizez],[-sizex,-sizey,sizez]];
var vertices = [-sizex,sizey,-sizez,-sizex,-sizey,+sizez,-sizex,sizey,sizez,-sizex,sizey,-sizez,-sizex,-sizey,-sizez,-sizex,-sizey,+sizez,sizex,sizey,-sizez,sizex,sizey,sizez,sizex,-sizey,+sizez,sizex,sizey,-sizez,sizex,-sizey,+sizez,sizex,-sizey,-sizez,-sizex,sizey,sizez,sizex,-sizey,sizez,sizex,sizey,sizez,-sizex,sizey,sizez,-sizex,-sizey,sizez,sizex,-sizey,sizez,-sizex,sizey,-sizez,sizex,sizey,-sizez,sizex,-sizey,-sizez,-sizex,sizey,-sizez,sizex,-sizey,-sizez,-sizex,-sizey,-sizez,-sizex,sizey,-sizez,sizex,sizey,sizez,sizex,sizey,-sizez,-sizex,sizey,-sizez,-sizex,sizey,sizez,sizex,sizey,sizez,-sizex,-sizey,-sizez,sizex,-sizey,-sizez,sizex,-sizey,sizez,-sizex,-sizey,-sizez,sizex,-sizey,sizez,-sizex,-sizey,sizez];
if(use_global)
return this.toGlobalMesh( {vertices: vertices} );
return GL.Mesh.load({vertices: vertices });
},
/**
* Renders a solid box
* @method renderSolidBox
* @param {number} sizex
* @param {number} sizey
* @param {number} sizez
*/
renderSolidBox: function(sizex,sizey,sizez)
{
var mesh = this.createSolidBoxMesh(sizex,sizey,sizez, true);
return this.renderMesh( mesh, gl.TRIANGLES, undefined, undefined, 0, this._global_mesh_last_size );
},
/**
* Renders a wire cube of size size
* @method renderWireCube
* @param {number} size
*/
renderWireCube: function(size)
{
return this.renderWireBox(size,size,size);
},
/**
* Renders a solid cube of size size
* @method renderSolidCube
* @param {number} size
*/
renderSolidCube: function(size)
{
return this.renderSolidCube(size,size,size);
},
/**
* Renders a solid plane (could be textured or even with an specific shader)
* @method renderPlane
* @param {vec3} position
* @param {vec2} size
* @param {GL.Texture} texture
* @param {GL.Shader} shader
*/
renderPlane: function( position, size, texture, shader)
{
if(!position || !size)
throw("LS.Draw.renderPlane param missing");
this.push();
this.translate(position);
this.scale( size[0], size[1], 1 );
if(texture)
texture.bind(0);
if(!shader && texture)
shader = this.shader_texture;
this.renderMesh(this.quad_mesh, gl.TRIANGLE_FAN, shader );
if(texture)
texture.unbind(0);
this.pop();
},
createGridMesh: function(dist,num)
{
dist = dist || 20;
num = num || 10;
var vertices = new Float32Array( (num*2+1) * 4 * 3);
var pos = 0;
for(var i = -num; i <= num; i++)
{
vertices.set( [i*dist,0,dist*num], pos);
vertices.set( [i*dist,0,-dist*num],pos+3);
vertices.set( [dist*num,0,i*dist], pos+6);
vertices.set( [-dist*num,0,i*dist],pos+9);
pos += 3*4;
}
return GL.Mesh.load({vertices: vertices});
},
/**
* Renders a grid of lines
* @method renderGrid
* @param {number} dist distance between lines
* @param {number} num number of lines
*/
renderGrid: function(dist,num)
{
var mesh = this.createGridMesh(dist,num);
return this.renderMesh(mesh, gl.LINES);
},
createConeMesh: function(radius, height, segments, in_z, use_global )
{
var axis = [0,1,0];
segments = segments || 100;
var R = quat.create();
var temp = this._temp;
var vertices = new Float32Array( (segments+2) * 3);
vertices.set(in_z ? [0,0,height] : [0,height,0], 0);
for(var i = 0; i <= segments; i++)
{
quat.setAxisAngle(R,axis, 2 * Math.PI * (i/segments) );
vec3.transformQuat(temp, [0,0,radius], R );
if(in_z)
vec3.set(temp, temp[0],temp[2],temp[1] );
vertices.set(temp, i*3+3);
}
if(use_global)
return this.toGlobalMesh( {vertices: vertices} );
return GL.Mesh.load({vertices: vertices});
},
/**
* Renders a cone
* @method renderCone
* @param {number} radius
* @param {number} height
* @param {number} segments
* @param {boolean} in_z aligned with z axis
*/
renderCone: function(radius, height, segments, in_z)
{
var mesh = this.createConeMesh(radius, height, segments, in_z, true);
return this.renderMesh(mesh, gl.TRIANGLE_FAN, undefined, undefined, 0, this._global_mesh_last_size );
},
createCylinderMesh: function( radius, height, segments, in_z, use_global )
{
var axis = [0,1,0];
segments = segments || 100;
var R = quat.create();
var temp = this._temp;
var vertices = new Float32Array( (segments+1) * 3 * 2);
for(var i = 0; i <= segments; i++)
{
quat.setAxisAngle(R, axis, 2 * Math.PI * (i/segments) );
vec3.transformQuat(temp, [0,0,radius], R );
vertices.set(temp, i*3*2+3);
temp[1] = height;
vertices.set(temp, i*3*2);
}
if(use_global)
return this.toGlobalMesh( {vertices: vertices} );
return GL.Mesh.load({vertices: vertices});
},
/**
* Renders a cylinder
* @method renderCylinder
* @param {number} radius
* @param {number} height
* @param {number} segments
* @param {boolean} in_z aligned with z axis
*/
renderCylinder: function( radius, height, segments, in_z )
{
var mesh = this.createCylinderMesh(radius, height, segments, in_z, true);
return this.renderMesh( mesh, gl.TRIANGLE_STRIP, undefined, undefined, 0, this._global_mesh_last_size );
},
/**
* Renders an image
* @method renderImage
* @param {vec3} position
* @param {Image|Texture|String} image from an URL, or a texture
* @param {number} size [optional=10]
* @param {boolean} fixed_size [optional=false] (camera distance do not affect size)
*/
renderImage: function( position, image, size, fixed_size )
{
if(!position || !image)
throw("LS.Draw.renderImage param missing");
size = size || 10;
var texture = null;
if(typeof(image) == "string")
{
texture = this.images[image];
if(texture == null)
{
Draw.images[image] = 1; //loading
var img = new Image();
img.src = image;
img.onload = function()
{
var texture = GL.Texture.fromImage(this);
Draw.images[image] = texture;
if(Draw.onRequestFrame)
Draw.onRequestFrame();
return;
}
return;
}
else if(texture == 1)
return; //loading
}
else if(image.constructor == Image)
{
if(!image.texture)
image.texture = GL.Texture.fromImage( this );
texture = image.texture;
}
else if(image.constructor == Texture)
texture = image;
if(!texture)
return;
if(fixed_size)
{
this.setPointSize( size );
texture.bind(0);
this.renderPoints( position, null, this.shader_image );
}
else
{
this.push();
//this.lookAt(position, this.camera_position,[0,1,0]);
this.billboard(position);
this.scale(size,size,size);
texture.bind(0);
this.renderMesh(this.quad_mesh, gl.TRIANGLE_FAN, this.shader_texture );
this.pop();
}
},
/**
* Renders a given mesh applyting the stack transformations
* @method renderMesh
* @param {GL.Mesh} mesh
* @param {enum} primitive [optional=gl.TRIANGLES] GL.TRIANGLES, gl.LINES, gl.POINTS, ...
* @param {string} indices [optional="triangles"] the name of the buffer in the mesh with the indices
* @param {number} range_start [optional] in case of rendering a range, the start primitive
* @param {number} range_length [optional] in case of rendering a range, the number of primitives
*/
renderMesh: function( mesh, primitive, shader, indices, range_start, range_length )
{
if(!this.ready)
throw ("Draw.js not initialized, call Draw.init()");
if(!mesh)
throw ("LS.Draw.renderMesh mesh cannot be null");
if(!shader)
{
if(mesh === this._global_mesh && this._global_mesh_ignore_colors )
shader = this.shader;
else
shader = mesh.vertexBuffers["colors"] ? this.shader_color : this.shader;
}
mat4.multiply(this.mvp_matrix, this.viewprojection_matrix, this.model_matrix );
shader.uniforms( this.uniforms );
if( range_start === undefined )
shader.draw(mesh, primitive === undefined ? gl.TRIANGLES : primitive, indices );
else
shader.drawRange(mesh, primitive === undefined ? gl.TRIANGLES : primitive, range_start, range_length, indices );
//used for repeating render
this._last_mesh = mesh;
this._last_primitive = primitive;
this._last_shader = shader;
this._last_indices = indices;
this._last_range_start = range_start;
this._last_range_length = range_length;
this._rendercalls += 1;
this.last_mesh = mesh;
return mesh;
},
/**
* Renders several meshes in one draw call, keep in mind the shader and the browser should support instancing
* @method renderMeshesInstanced
* @param {GL.Mesh} mesh
* @param {Array} matrices an array containing all the matrices
* @param {enum} primitive [optional=gl.TRIANGLES] GL.TRIANGLES, gl.LINES, gl.POINTS, ...
* @param {string} indices [optional="triangles"] the name of the buffer in the mesh with the indices
*/
renderMeshesInstanced: (function(){
var tmp = { u_model: null };
var tmp_matrix = mat4.create();
return function( mesh, matrices, primitive, shader, indices )
{
if(!this.ready)
throw ("Draw.js not initialized, call Draw.init()");
if(!mesh)
throw ("LS.Draw.renderMeshesInstanced mesh cannot be null");
if( gl.webgl_version == 1 && !gl.extensions.ANGLE_instanced_arrays )
return null; //instancing not supported
if(!shader)
shader = mesh.vertexBuffers["colors"] ? this.shader_color_instancing : this.shader_instancing;
if( !shader.attributes.u_model )
throw("Shader does not support instancing, it must have a attribute u_model");
tmp.u_model = matrices;
//this hack is done so we dont have to multiply the global model for every matrix, the VP is in reality a MVP
tmp_matrix.set( this.viewprojection_matrix );
mat4.multiply( this.viewprojection_matrix, this.viewprojection_matrix, this.model_matrix );
shader.uniforms( this.uniforms );
shader.drawInstanced( mesh, primitive === undefined ? gl.TRIANGLES : primitive, indices, tmp );
this.viewprojection_matrix.set( tmp_matrix );
this._rendercalls += 1;
return mesh;
};
})(),
//used in some special cases
repeatLastRender: function()
{
this.renderMesh( this._last_mesh, this._last_primitive, this._last_shader, this._last_indices, this._last_range_start, this._last_range_length );
},
/**
* Renders a text in 3D, in the XY plane, using the current matrix position
* @method renderText
* @param {string} text
* @param {vec3} position [optional] 3D coordinate in relation to matrix
*/
renderText: function( text, position )
{
position = position || LS.ZEROS;
if(!Draw.font_atlas)
this.createFontAtlas();
var atlas = this.font_atlas;
var l = text.length;
var char_size = atlas.atlas.char_size;
var i_char_size = 1 / atlas.atlas.char_size;
var spacing = atlas.atlas.spacing;
var num_valid_chars = 0;
for(var i = 0; i < l; ++i)
if(atlas.atlas[ text.charCodeAt(i) ] != null)
num_valid_chars++;
var vertices = new Float32Array( num_valid_chars * 6 * 3);
var coords = new Float32Array( num_valid_chars * 6 * 2);
var pos = 0;
var x = 0, y = 0;
for(var i = 0; i < l; ++i)
{
var c = atlas.atlas[ text.charCodeAt(i) ];
if(!c)
{
if(text.charCodeAt(i) == 10)
{
x = 0;
y -= char_size;
}
else
x += char_size;
continue;
}
vertices.set( [x + position[0], y + position[1], position[2]], pos*6*3);
vertices.set( [x + position[0], y + position[1] + char_size, position[2]], pos*6*3+3);
vertices.set( [x + position[0] + char_size, y + position[1] + char_size, position[2]], pos*6*3+6);
vertices.set( [x + position[0] + char_size, y + position[1], position[2]], pos*6*3+9);
vertices.set( [x + position[0], y + position[1], position[2]], pos*6*3+12);
vertices.set( [x + position[0] + char_size, y + position[1] + char_size, position[2]], pos*6*3+15);
coords.set( [c[0], c[1]], pos*6*2);
coords.set( [c[0], c[3]], pos*6*2+2);
coords.set( [c[2], c[3]], pos*6*2+4);
coords.set( [c[2], c[1]], pos*6*2+6);
coords.set( [c[0], c[1]], pos*6*2+8);
coords.set( [c[2], c[3]], pos*6*2+10);
x+= spacing;
++pos;
}
var mesh = this.toGlobalMesh({vertices: vertices, coords: coords});
atlas.bind(0);
return this.renderMesh( mesh, gl.TRIANGLES, this.shader_texture, undefined, 0, vertices.length / 3 );
},
/*
renderText2D: function( text, position )
{
position = position || LS.ZEROS;
if(!Draw.font_atlas)
this.createFontAtlas();
var atlas = this.font_atlas;
var l = text.length;
var char_size = atlas.atlas.char_size;
var i_char_size = 1 / atlas.atlas.char_size;
var spacing = atlas.atlas.spacing;
var num_valid_chars = 0;
for(var i = 0; i < l; ++i)
if(atlas.atlas[ text.charCodeAt(i) ] != null)
num_valid_chars++;
var vertices = new Float32Array( num_valid_chars * 3 );
var extra4 = new Float32Array( num_valid_chars * 4 );
var pos = 0;
var x = 0, y = 0;
for(var i = 0; i < l; ++i)
{
var c = atlas.atlas[ text.charCodeAt(i) ];
if(!c)
{
if(text.charCodeAt(i) == 10) //breakline
{
x = 0;
y += char_size;
}
else
x += char_size;
continue;
}
vertices.set( position, i*3 );
extra4.set([ c[0], c[1], x,y );
x+= spacing;
++pos;
}
var mesh = this.toGlobalMesh({ vertices: vertices, extra4: extra4 });
this.setPointSize(20);
atlas.bind(0);
this.shader_text2D.uniforms({ u_char_offset: atlas.offset });
return this.renderMesh( mesh, gl.POINTS, this.shader_text2D, undefined, 0, vertices.length / 3 );
},
*/
createFontAtlas: function()
{
var canvas = createCanvas(512,512);
var fontsize = (canvas.width * 0.09)|0;
var char_size = (canvas.width * 0.1)|0;
//$("body").append(canvas);
var ctx = canvas.getContext("2d");
//ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "white";
ctx.font = fontsize + "px Courier New";
ctx.textAlign = "center";
var x = 0;
var y = 0;
var xoffset = 0.5, yoffset = fontsize * -0.3;
var atlas = { char_size: char_size, offset: char_size / canvas.width , spacing: char_size * 0.6 };
for(var i = 6; i < 100; i++)//valid characters
{
var character = String.fromCharCode(i+27);
atlas[i+27] = [x/canvas.width, 1-(y+char_size)/canvas.height, (x+char_size)/canvas.width, 1-(y)/canvas.height];
ctx.fillText( character,Math.floor(x+char_size*xoffset),Math.floor(y+char_size+yoffset),char_size);
x += char_size;
if((x + char_size) > canvas.width)
{
x = 0;
y += char_size;
}
}
this.font_atlas = GL.Texture.fromImage(canvas, {magFilter: gl.LINEAR, minFilter: gl.LINEAR_MIPMAP_LINEAR} );
gl.colorMask(true,true,true,false);
this.font_atlas.fill([1,1,1,0]);
gl.colorMask(true,true,true,true);
this.font_atlas.atlas = atlas;
},
linearize: function(array) //fairly optimized
{
if(!array.length)
return [];
if(array[0].constructor === Number) //array of numbers
return array.constructor === Float32Array ? array : new Float32Array(array);
//linearize
var n = array[0].length; //assuming all values have the same size!
var result = new Float32Array(array.length * n);
var l = array.length;
for(var i = 0; i < l; ++i)
result.set(array[i], i*n);
return result;
},
/**
* pushes the transform matrix into the stack to save the state
* @method push
*/
push: function()
{
if(this.model_matrix.byteOffset >= (this.stack.byteLength - 16*4))
throw("matrices stack overflow");
var old = this.model_matrix;
this.model_matrix = new Float32Array(this.stack.buffer,this.model_matrix.byteOffset + 16*4,16);
this.uniforms.u_model = this.model_matrix;
mat4.copy(this.model_matrix, old);
},
/**
* takes the matrix from the top position of the stack to restore the last saved state
* @method push
*/
pop: function()
{
if(this.model_matrix.byteOffset == 0)
throw("too many pops");
this.model_matrix = new Float32Array(this.stack.buffer,this.model_matrix.byteOffset - 16*4,16);
this.uniforms.u_model = this.model_matrix;
},
/**
* clears the transform matrix setting it to an identity
* @method identity
*/
identity: function()
{
mat4.identity(this.model_matrix);
},
/**
* changes the scale of the transform matrix. The parameters could be a vec3, a single number (then the scale is uniform in all axis) or three numbers
* @method scale
* @param {vec3|array|number} x could be an array of 3, one value (if no other values are specified then it is an uniform scaling)
* @param {number} y
* @param {number} z
*/
scale: function(x,y,z)
{
if(arguments.length == 3)
{
var temp = this._temp;
temp[0] = x; temp[1] = y; temp[2] = z;
mat4.scale(this.model_matrix,this.model_matrix,temp);
}
else if(x.length)//one argument: x is vec3
mat4.scale(this.model_matrix,this.model_matrix,x);
else //is number
{
var temp = this._temp;
temp[0] = temp[1] = temp[2] = x;
mat4.scale(this.model_matrix,this.model_matrix,temp);
}
},
/**
* applies a translation to the transform matrix
* @method translate
* @param {vec3|number} x could be an array of 3 or the x transform
* @param {number} y
* @param {number} z
*/
translate: function(x,y,z)
{
if(arguments.length == 3)
{
var temp = this._temp;
temp[0] = x; temp[1] = y; temp[2] = z;
mat4.translate(this.model_matrix,this.model_matrix,temp);
}
else //one argument: x -> vec3
mat4.translate(this.model_matrix,this.model_matrix,x);
},
/**
* applies a translation to the transform matrix
* @method rotate
* @param {number} angle in degrees
* @param {number|vec3} x could be the x component or the full axis
* @param {number} y
* @param {number} z
*/
rotate: function(angle, x,y,z)
{
if(arguments.length == 4)
{
var temp = this._temp;
temp[0] = x; temp[1] = y; temp[2] = z;
mat4.rotate(this.model_matrix, this.model_matrix, angle * DEG2RAD, [x,y,z]);
}
else //two arguments: x -> vec3
mat4.rotate(this.model_matrix, this.model_matrix, angle * DEG2RAD, x);
},
/**
* moves an object to a given position and forces it to look to another direction
* Warning: it doesnt changes the camera in any way, only the transform matrix
* @method lookAt
* @param {vec3} position
* @param {vec3} target
* @param {vec3} up
*/
lookAt: function(position, target, up)
{
mat4.lookAt( this.model_matrix, position, target, up );
mat4.invert( this.model_matrix, this.model_matrix );
},
billboard: function(position)
{
mat4.invert(this.model_matrix, this.view_matrix);
mat4.setTranslation(this.model_matrix, position);
},
fromTranslationFrontTop: function(position, front, top)
{
mat4.fromTranslationFrontTop(this.model_matrix, position, front, top);
},
/**
* projects a point from 3D space to 2D space (multiply by MVP)
* @method project
* @param {vec3} position
* @param {vec3} dest [optional]
* @return {vec3} the point in screen space (in normalized coordinates)
*/
project: function( position, dest )
{
dest = dest || vec3.create();
return mat4.multiplyVec3(dest, this.mvp_matrix, position);
},
getPhongShader: function( ambient_color, light_color, light_dir, instanced )
{
var shader = instanced ? this.shader_phong_instanced : this.shader_phong;
vec3.normalize( light_dir, light_dir );
shader.uniforms({ u_ambient_color: ambient_color, u_light_color: light_color, u_light_dir: light_dir });
return shader;
},
getDepthShader: function()
{
return this.shader_depth;
},
//reuses a global mesh to avoid fragmenting the VRAM
toGlobalMesh: function( buffers, indices )
{
if(!this._global_mesh)
{
//global mesh: to reuse memory and save fragmentation
this._global_mesh_max_vertices = 1024;
this._global_mesh = new GL.Mesh({
vertices: new Float32Array(this._global_mesh_max_vertices * 3),
normals: new Float32Array(this._global_mesh_max_vertices * 3),
coords: new Float32Array(this._global_mesh_max_vertices * 2),
colors: new Float32Array(this._global_mesh_max_vertices * 4),
extra: new Float32Array(this._global_mesh_max_vertices * 1)
},{
indices: new Uint16Array(this._global_mesh_max_vertices * 3)
}, { stream_type: gl.DYNAMIC_STREAM });
}
//take every stream and store it inside the mesh buffers
for(var i in buffers)
{
var mesh_buffer = this._global_mesh.getBuffer( i );
if(!mesh_buffer)
{
console.warn("Draw: global mesh lacks one buffer: " + i );
continue;
}
var buffer_data = buffers[i];
if(!buffer_data)
continue;
if(!buffer_data.buffer)
buffer_data = new Float32Array( buffer_data ); //force typed arrays
//some data would be lost here
if(buffer_data.length > mesh_buffer.data.length)
{
console.warn("Draw: data is too big, resizing" );
this.resizeGlobalMesh();
mesh_buffer = this._global_mesh.getBuffer( i );
buffer_data = buffer_data.subarray(0,mesh_buffer.data.length);
}
mesh_buffer.setData( buffer_data ); //set and upload
}
this._global_mesh_ignore_colors = !(buffers.colors);
if(indices)
{
var mesh_buffer = this._global_mesh.getIndexBuffer("indices");
mesh_buffer.setData( indices );
this._global_mesh_last_size = indices.length;
}
else
this._global_mesh_last_size = buffers["vertices"].length / 3;
return this._global_mesh;
},
resizeGlobalMesh: function()
{
if(!this._global_mesh)
throw("No global mesh to resize");
//global mesh: to reuse memory and save fragmentation
this._global_mesh_max_vertices = this._global_mesh_max_vertices * 2;
this._global_mesh.deleteBuffers();
this._global_mesh = new GL.Mesh({
vertices: new Float32Array(this._global_mesh_max_vertices * 3),
normals: new Float32Array(this._global_mesh_max_vertices * 3),
coords: new Float32Array(this._global_mesh_max_vertices * 2),
colors: new Float32Array(this._global_mesh_max_vertices * 4),
extra: new Float32Array(this._global_mesh_max_vertices * 1)
},{
indices: new Uint16Array(this._global_mesh_max_vertices * 3)
}, { stream_type: gl.DYNAMIC_STREAM });
}
};
if(typeof(LS) != "undefined")
LS.Draw = Draw;