Files
three.js/src/renderers/WebGLRenderer.js
T
alteredq dd7e1e84ee Removed redundant GL call in render target setup.
Also fixed RTT example - seems UV mapping for SphereGeometry is now rotated 180 degrees.
2011-11-13 20:10:31 +01:00

6007 lines
142 KiB
JavaScript

/**
* @author supereggbert / http://www.paulbrunt.co.uk/
* @author mrdoob / http://mrdoob.com/
* @author alteredq / http://alteredqualia.com/
* @author szimek / https://github.com/szimek/
*/
THREE.WebGLRenderer = function ( parameters ) {
// constructor parameters
var parameters = parameters || {},
_canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
_clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ),
_clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0,
_maxLights = parameters.maxLights !== undefined ? parameters.maxLights : 4;
// public properties
this.domElement = _canvas;
this.context = null;
// clearing
this.autoClear = true;
this.autoClearColor = true;
this.autoClearDepth = true;
this.autoClearStencil = true;
// scene graph
this.sortObjects = true;
this.autoUpdateObjects = true;
this.autoUpdateScene = true;
// physically based shading
this.gammaInput = false;
this.gammaOutput = false;
this.physicallyBasedShading = false;
// shadow map
this.shadowMapBias = 0.0039;
this.shadowMapDarkness = 0.5;
this.shadowMapWidth = 512;
this.shadowMapHeight = 512;
this.shadowCameraNear = 1;
this.shadowCameraFar = 5000;
this.shadowCameraFov = 50;
this.shadowMap = [];
this.shadowMapEnabled = false;
this.shadowMapAutoUpdate = true;
this.shadowMapSoft = true;
// morphs
this.maxMorphTargets = 8;
// info
this.info = {
memory: {
programs: 0,
geometries: 0,
textures: 0
},
render: {
calls: 0,
vertices: 0,
faces: 0
}
};
// internal properties
var _this = this,
_gl,
_programs = [],
// internal state cache
_currentProgram = null,
_currentFramebuffer = null,
_currentMaterialId = -1,
_currentGeometryGroupHash = null,
_geometryGroupCounter = 0,
// GL state cache
_oldDoubleSided = null,
_oldFlipSided = null,
_oldBlending = null,
_oldDepthTest = null,
_oldDepthWrite = null,
_oldPolygonOffset = null,
_oldPolygonOffsetFactor = null,
_oldPolygonOffsetUnits = null,
_oldLineWidth = null,
_viewportX = 0,
_viewportY = 0,
_viewportWidth = 0,
_viewportHeight = 0,
// frustum cache
_frustum = [
new THREE.Vector4(),
new THREE.Vector4(),
new THREE.Vector4(),
new THREE.Vector4(),
new THREE.Vector4(),
new THREE.Vector4()
],
// camera matrices cache
_projScreenMatrix = new THREE.Matrix4(),
_projectionMatrixArray = new Float32Array( 16 ),
_viewMatrixArray = new Float32Array( 16 ),
_vector3 = new THREE.Vector4(),
// light arrays cache
_lights = {
ambient: [ 0, 0, 0 ],
directional: { length: 0, colors: new Array(), positions: new Array() },
point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() }
};
// shadow maps
var _cameraLight, _shadowMatrix = [];
var _depthMaterial, _depthMaterialMorph;
// sprites
var _sprite = {};
var _spriteAttributesEnabled = false;
// initialize
_gl = initGL();
setDefaultGLState();
initSprites();
initShadowmaps();
this.context = _gl;
var _supportsVertexTextures = ( maxVertexTextures() > 0 );
// API
this.getContext = function () {
return _gl;
};
this.supportsVertexTextures = function () {
return _supportsVertexTextures;
};
this.setSize = function ( width, height ) {
_canvas.width = width;
_canvas.height = height;
this.setViewport( 0, 0, _canvas.width, _canvas.height );
};
this.setViewport = function ( x, y, width, height ) {
_viewportX = x;
_viewportY = y;
_viewportWidth = width;
_viewportHeight = height;
_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
};
this.setScissor = function ( x, y, width, height ) {
_gl.scissor( x, y, width, height );
};
this.enableScissorTest = function ( enable ) {
enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
};
// Clearing
this.setClearColorHex = function ( hex, alpha ) {
_clearColor.setHex( hex );
_clearAlpha = alpha;
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
this.setClearColor = function ( color, alpha ) {
_clearColor.copy( color );
_clearAlpha = alpha;
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
this.getClearColor = function () {
return _clearColor;
};
this.getClearAlpha = function () {
return _clearAlpha;
};
this.clear = function ( color, depth, stencil ) {
var bits = 0;
if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
_gl.clear( bits );
};
this.clearTarget = function ( renderTarget, color, depth, stencil ) {
setRenderTarget( renderTarget );
this.clear( color, depth, stencil );
};
// Deallocation
this.deallocateObject = function ( object ) {
if ( ! object.__webglInit ) return;
object.__webglInit = false;
delete object._modelViewMatrix;
delete object._normalMatrixArray;
delete object._modelViewMatrixArray;
delete object._objectMatrixArray;
if ( object instanceof THREE.Mesh ) {
for ( var g in object.geometry.geometryGroups ) {
deleteMeshBuffers( object.geometry.geometryGroups[ g ] );
}
} else if ( object instanceof THREE.Ribbon ) {
deleteRibbonBuffers( object.geometry );
} else if ( object instanceof THREE.Line ) {
deleteLineBuffers( object.geometry );
} else if ( object instanceof THREE.ParticleSystem ) {
deleteParticleBuffers( object.geometry );
}
};
this.deallocateTexture = function ( texture ) {
if ( ! texture.__webglInit ) return;
texture.__webglInit = false;
_gl.deleteTexture( texture.__webglTexture );
_this.info.memory.textures --;
};
// Rendering
this.updateShadowMap = function ( scene, camera ) {
renderShadowMap( scene, camera );
};
// Internal functions
// Buffer allocation
function createParticleBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
_this.info.geometries ++;
};
function createLineBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createRibbonBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createMeshBuffers ( geometryGroup ) {
geometryGroup.__webglVertexBuffer = _gl.createBuffer();
geometryGroup.__webglNormalBuffer = _gl.createBuffer();
geometryGroup.__webglTangentBuffer = _gl.createBuffer();
geometryGroup.__webglColorBuffer = _gl.createBuffer();
geometryGroup.__webglUVBuffer = _gl.createBuffer();
geometryGroup.__webglUV2Buffer = _gl.createBuffer();
geometryGroup.__webglSkinVertexABuffer = _gl.createBuffer();
geometryGroup.__webglSkinVertexBBuffer = _gl.createBuffer();
geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer();
geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer();
geometryGroup.__webglFaceBuffer = _gl.createBuffer();
geometryGroup.__webglLineBuffer = _gl.createBuffer();
if ( geometryGroup.numMorphTargets ) {
var m, ml;
geometryGroup.__webglMorphTargetsBuffers = [];
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
}
}
_this.info.memory.geometries ++;
};
// Buffer deallocation
function deleteParticleBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
_this.info.memory.geometries --;
};
function deleteLineBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
_this.info.memory.geometries --;
};
function deleteRibbonBuffers ( geometry ) {
_gl.deleteBuffer( geometry.__webglVertexBuffer );
_gl.deleteBuffer( geometry.__webglColorBuffer );
_this.info.memory.geometries --;
};
function deleteMeshBuffers ( geometryGroup ) {
_gl.deleteBuffer( geometryGroup.__webglVertexBuffer );
_gl.deleteBuffer( geometryGroup.__webglNormalBuffer );
_gl.deleteBuffer( geometryGroup.__webglTangentBuffer );
_gl.deleteBuffer( geometryGroup.__webglColorBuffer );
_gl.deleteBuffer( geometryGroup.__webglUVBuffer );
_gl.deleteBuffer( geometryGroup.__webglUV2Buffer );
_gl.deleteBuffer( geometryGroup.__webglSkinVertexABuffer );
_gl.deleteBuffer( geometryGroup.__webglSkinVertexBBuffer );
_gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer );
_gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer );
_gl.deleteBuffer( geometryGroup.__webglFaceBuffer );
_gl.deleteBuffer( geometryGroup.__webglLineBuffer );
if ( geometryGroup.numMorphTargets ) {
for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
}
}
if ( geometryGroup.__webglCustomAttributesList ) {
for ( var id in geometryGroup.__webglCustomAttributesList ) {
_gl.deleteBuffer( geometryGroup.__webglCustomAttributesList[ id ].buffer );
}
}
_this.info.memory.geometries --;
};
// Buffer initialization
function initCustomAttributes ( geometry, object ) {
var nvertices = geometry.vertices.length;
var material = object.material;
if ( material.attributes ) {
if ( geometry.__webglCustomAttributesList === undefined ) {
geometry.__webglCustomAttributesList = [];
}
for ( var a in material.attributes ) {
var attribute = material.attributes[ a ];
if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
attribute.__webglInitialized = true;
var size = 1; // "f" and "i"
if ( attribute.type === "v2" ) size = 2;
else if ( attribute.type === "v3" ) size = 3;
else if ( attribute.type === "v4" ) size = 4;
else if ( attribute.type === "c" ) size = 3;
attribute.size = size;
attribute.array = new Float32Array( nvertices * size );
attribute.buffer = _gl.createBuffer();
attribute.buffer.belongsToAttribute = a;
attribute.needsUpdate = true;
}
geometry.__webglCustomAttributesList.push( attribute );
}
}
};
function initParticleBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__sortArray = [];
geometry.__webglParticleCount = nvertices;
initCustomAttributes ( geometry, object );
};
function initLineBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__webglLineCount = nvertices;
initCustomAttributes ( geometry, object );
};
function initRibbonBuffers ( geometry ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__webglVertexCount = nvertices;
};
function initMeshBuffers ( geometryGroup, object ) {
var geometry = object.geometry,
faces3 = geometryGroup.faces3,
faces4 = geometryGroup.faces4,
nvertices = faces3.length * 3 + faces4.length * 4,
ntris = faces3.length * 1 + faces4.length * 2,
nlines = faces3.length * 3 + faces4.length * 4,
material = getBufferMaterial( object, geometryGroup ),
uvType = bufferGuessUVType( material ),
normalType = bufferGuessNormalType( material ),
vertexColorType = bufferGuessVertexColorType( material );
//console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
if ( normalType ) {
geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
}
if ( geometry.hasTangents ) {
geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
}
if ( vertexColorType ) {
geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
}
if ( uvType ) {
if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) {
geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
}
if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) {
geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
}
}
if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
geometryGroup.__skinVertexAArray = new Float32Array( nvertices * 4 );
geometryGroup.__skinVertexBArray = new Float32Array( nvertices * 4 );
geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
}
geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
if ( geometryGroup.numMorphTargets ) {
geometryGroup.__morphTargetsArrays = [];
for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
}
}
geometryGroup.__webglFaceCount = ntris * 3;
geometryGroup.__webglLineCount = nlines * 2;
// custom attributes
if ( material.attributes ) {
if ( geometryGroup.__webglCustomAttributesList === undefined ) {
geometryGroup.__webglCustomAttributesList = [];
}
for ( var a in material.attributes ) {
// Do a shallow copy of the attribute object so different geometryGroup chunks use different
// attribute buffers which are correctly indexed in the setMeshBuffers function
var originalAttribute = material.attributes[ a ];
var attribute = {};
for ( var property in originalAttribute ) {
attribute[ property ] = originalAttribute[ property ];
}
if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
attribute.__webglInitialized = true;
var size = 1; // "f" and "i"
if( attribute.type === "v2" ) size = 2;
else if( attribute.type === "v3" ) size = 3;
else if( attribute.type === "v4" ) size = 4;
else if( attribute.type === "c" ) size = 3;
attribute.size = size;
attribute.array = new Float32Array( nvertices * size );
attribute.buffer = _gl.createBuffer();
attribute.buffer.belongsToAttribute = a;
originalAttribute.needsUpdate = true;
attribute.__original = originalAttribute;
}
geometryGroup.__webglCustomAttributesList.push( attribute );
}
}
geometryGroup.__inittedArrays = true;
};
function getBufferMaterial( object, geometryGroup ) {
if ( object.material && ! ( object.material instanceof THREE.MeshFaceMaterial ) ) {
return object.material;
} else if ( geometryGroup.materialIndex >= 0 ) {
return object.geometry.materials[ geometryGroup.materialIndex ];
}
};
function materialNeedsSmoothNormals ( material ) {
return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
};
function bufferGuessNormalType ( material ) {
// only MeshBasicMaterial and MeshDepthMaterial don't need normals
if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
return false;
}
if ( materialNeedsSmoothNormals( material ) ) {
return THREE.SmoothShading;
} else {
return THREE.FlatShading;
}
};
function bufferGuessVertexColorType ( material ) {
if ( material.vertexColors ) {
return material.vertexColors;
}
return false;
};
function bufferGuessUVType ( material ) {
// material must use some texture to require uvs
if ( material.map || material.lightMap || material instanceof THREE.ShaderMaterial ) {
return true;
}
return false;
};
// Buffer setting
function setParticleBuffers ( geometry, hint, object ) {
var v, c, vertex, offset, index, color,
vertices = geometry.vertices,
vl = vertices.length,
colors = geometry.colors,
cl = colors.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
sortArray = geometry.__sortArray,
dirtyVertices = geometry.__dirtyVertices,
dirtyElements = geometry.__dirtyElements,
dirtyColors = geometry.__dirtyColors,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( object.sortParticles ) {
_projScreenMatrix.multiplySelf( object.matrixWorld );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ].position;
_vector3.copy( vertex );
_projScreenMatrix.multiplyVector3( _vector3 );
sortArray[ v ] = [ _vector3.z, v ];
}
sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ sortArray[v][1] ].position;
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
for ( c = 0; c < cl; c ++ ) {
offset = c * 3;
color = colors[ sortArray[c][1] ];
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
offset = 0;
cal = customAttribute.value.length;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
customAttribute.array[ ca ] = customAttribute.value[ index ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
index = sortArray[ ca ][ 1 ];
value = customAttribute.value[ index ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
}
}
} else {
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ].position;
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate &&
( customAttribute.boundTo === undefined ||
customAttribute.boundTo === "vertices") ) {
cal = customAttribute.value.length;
offset = 0;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
customAttribute.array[ ca ] = customAttribute.value[ ca ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
}
}
}
}
if ( dirtyVertices || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate || object.sortParticles ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
}
};
function setLineBuffers ( geometry, hint ) {
var v, c, vertex, offset, color,
vertices = geometry.vertices,
colors = geometry.colors,
vl = vertices.length,
cl = colors.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
dirtyVertices = geometry.__dirtyVertices,
dirtyColors = geometry.__dirtyColors,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ].position;
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( customAttribute.needsUpdate &&
( customAttribute.boundTo === undefined ||
customAttribute.boundTo === "vertices" ) ) {
offset = 0;
cal = customAttribute.value.length;
if ( customAttribute.size === 1 ) {
for ( ca = 0; ca < cal; ca ++ ) {
customAttribute.array[ ca ] = customAttribute.value[ ca ];
}
} else if ( customAttribute.size === 2 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
offset += 2;
}
} else if ( customAttribute.size === 3 ) {
if ( customAttribute.type === "c" ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.r;
customAttribute.array[ offset + 1 ] = value.g;
customAttribute.array[ offset + 2 ] = value.b;
offset += 3;
}
} else {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
offset += 3;
}
}
} else if ( customAttribute.size === 4 ) {
for ( ca = 0; ca < cal; ca ++ ) {
value = customAttribute.value[ ca ];
customAttribute.array[ offset ] = value.x;
customAttribute.array[ offset + 1 ] = value.y;
customAttribute.array[ offset + 2 ] = value.z;
customAttribute.array[ offset + 3 ] = value.w;
offset += 4;
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
}
};
function setRibbonBuffers ( geometry, hint ) {
var v, c, vertex, offset, color,
vertices = geometry.vertices,
colors = geometry.colors,
vl = vertices.length,
cl = colors.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
dirtyVertices = geometry.__dirtyVertices,
dirtyColors = geometry.__dirtyColors;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ].position;
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyColors ) {
for ( c = 0; c < cl; c ++ ) {
color = colors[ c ];
offset = c * 3;
colorArray[ offset ] = color.r;
colorArray[ offset + 1 ] = color.g;
colorArray[ offset + 2 ] = color.b;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
};
function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
if ( ! geometryGroup.__inittedArrays ) {
// console.log( object );
return;
}
var normalType = bufferGuessNormalType( material ),
vertexColorType = bufferGuessVertexColorType( material ),
uvType = bufferGuessUVType( material ),
needsSmoothNormals = ( normalType === THREE.SmoothShading );
var f, fl, fi, face,
vertexNormals, faceNormal, normal,
vertexColors, faceColor,
vertexTangents,
uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4,
c1, c2, c3, c4,
sw1, sw2, sw3, sw4,
si1, si2, si3, si4,
sa1, sa2, sa3, sa4,
sb1, sb2, sb3, sb4,
m, ml, i, il,
vn, uvi, uv2i,
vk, vkl, vka,
a,
vertexIndex = 0,
offset = 0,
offset_uv = 0,
offset_uv2 = 0,
offset_face = 0,
offset_normal = 0,
offset_tangent = 0,
offset_line = 0,
offset_color = 0,
offset_skin = 0,
offset_morphTarget = 0,
offset_custom = 0,
offset_customSrc = 0,
value,
vertexArray = geometryGroup.__vertexArray,
uvArray = geometryGroup.__uvArray,
uv2Array = geometryGroup.__uv2Array,
normalArray = geometryGroup.__normalArray,
tangentArray = geometryGroup.__tangentArray,
colorArray = geometryGroup.__colorArray,
skinVertexAArray = geometryGroup.__skinVertexAArray,
skinVertexBArray = geometryGroup.__skinVertexBArray,
skinIndexArray = geometryGroup.__skinIndexArray,
skinWeightArray = geometryGroup.__skinWeightArray,
morphTargetsArrays = geometryGroup.__morphTargetsArrays,
customAttributes = geometryGroup.__webglCustomAttributesList,
customAttribute,
faceArray = geometryGroup.__faceArray,
lineArray = geometryGroup.__lineArray,
geometry = object.geometry, // this is shared for all chunks
dirtyVertices = geometry.__dirtyVertices,
dirtyElements = geometry.__dirtyElements,
dirtyUvs = geometry.__dirtyUvs,
dirtyNormals = geometry.__dirtyNormals,
dirtyTangents = geometry.__dirtyTangents,
dirtyColors = geometry.__dirtyColors,
dirtyMorphTargets = geometry.__dirtyMorphTargets,
vertices = geometry.vertices,
chunk_faces3 = geometryGroup.faces3,
chunk_faces4 = geometryGroup.faces4,
obj_faces = geometry.faces,
obj_uvs = geometry.faceVertexUvs[ 0 ],
obj_uvs2 = geometry.faceVertexUvs[ 1 ],
obj_colors = geometry.colors,
obj_skinVerticesA = geometry.skinVerticesA,
obj_skinVerticesB = geometry.skinVerticesB,
obj_skinIndices = geometry.skinIndices,
obj_skinWeights = geometry.skinWeights,
morphTargets = geometry.morphTargets;
if ( dirtyVertices ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = vertices[ face.a ].position;
v2 = vertices[ face.b ].position;
v3 = vertices[ face.c ].position;
vertexArray[ offset ] = v1.x;
vertexArray[ offset + 1 ] = v1.y;
vertexArray[ offset + 2 ] = v1.z;
vertexArray[ offset + 3 ] = v2.x;
vertexArray[ offset + 4 ] = v2.y;
vertexArray[ offset + 5 ] = v2.z;
vertexArray[ offset + 6 ] = v3.x;
vertexArray[ offset + 7 ] = v3.y;
vertexArray[ offset + 8 ] = v3.z;
offset += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = vertices[ face.a ].position;
v2 = vertices[ face.b ].position;
v3 = vertices[ face.c ].position;
v4 = vertices[ face.d ].position;
vertexArray[ offset ] = v1.x;
vertexArray[ offset + 1 ] = v1.y;
vertexArray[ offset + 2 ] = v1.z;
vertexArray[ offset + 3 ] = v2.x;
vertexArray[ offset + 4 ] = v2.y;
vertexArray[ offset + 5 ] = v2.z;
vertexArray[ offset + 6 ] = v3.x;
vertexArray[ offset + 7 ] = v3.y;
vertexArray[ offset + 8 ] = v3.z;
vertexArray[ offset + 9 ] = v4.x;
vertexArray[ offset + 10 ] = v4.y;
vertexArray[ offset + 11 ] = v4.z;
offset += 12;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
}
if ( dirtyMorphTargets ) {
for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) {
offset_morphTarget = 0;
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = morphTargets[ vk ].vertices[ face.a ].position;
v2 = morphTargets[ vk ].vertices[ face.b ].position;
v3 = morphTargets[ vk ].vertices[ face.c ].position;
vka = morphTargetsArrays[ vk ];
vka[ offset_morphTarget ] = v1.x;
vka[ offset_morphTarget + 1 ] = v1.y;
vka[ offset_morphTarget + 2 ] = v1.z;
vka[ offset_morphTarget + 3 ] = v2.x;
vka[ offset_morphTarget + 4 ] = v2.y;
vka[ offset_morphTarget + 5 ] = v2.z;
vka[ offset_morphTarget + 6 ] = v3.x;
vka[ offset_morphTarget + 7 ] = v3.y;
vka[ offset_morphTarget + 8 ] = v3.z;
offset_morphTarget += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = morphTargets[ vk ].vertices[ face.a ].position;
v2 = morphTargets[ vk ].vertices[ face.b ].position;
v3 = morphTargets[ vk ].vertices[ face.c ].position;
v4 = morphTargets[ vk ].vertices[ face.d ].position;
vka = morphTargetsArrays[ vk ];
vka[ offset_morphTarget ] = v1.x;
vka[ offset_morphTarget + 1 ] = v1.y;
vka[ offset_morphTarget + 2 ] = v1.z;
vka[ offset_morphTarget + 3 ] = v2.x;
vka[ offset_morphTarget + 4 ] = v2.y;
vka[ offset_morphTarget + 5 ] = v2.z;
vka[ offset_morphTarget + 6 ] = v3.x;
vka[ offset_morphTarget + 7 ] = v3.y;
vka[ offset_morphTarget + 8 ] = v3.z;
vka[ offset_morphTarget + 9 ] = v4.x;
vka[ offset_morphTarget + 10 ] = v4.y;
vka[ offset_morphTarget + 11 ] = v4.z;
offset_morphTarget += 12;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] );
_gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint );
}
}
if ( obj_skinWeights.length ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
// weights
sw1 = obj_skinWeights[ face.a ];
sw2 = obj_skinWeights[ face.b ];
sw3 = obj_skinWeights[ face.c ];
skinWeightArray[ offset_skin ] = sw1.x;
skinWeightArray[ offset_skin + 1 ] = sw1.y;
skinWeightArray[ offset_skin + 2 ] = sw1.z;
skinWeightArray[ offset_skin + 3 ] = sw1.w;
skinWeightArray[ offset_skin + 4 ] = sw2.x;
skinWeightArray[ offset_skin + 5 ] = sw2.y;
skinWeightArray[ offset_skin + 6 ] = sw2.z;
skinWeightArray[ offset_skin + 7 ] = sw2.w;
skinWeightArray[ offset_skin + 8 ] = sw3.x;
skinWeightArray[ offset_skin + 9 ] = sw3.y;
skinWeightArray[ offset_skin + 10 ] = sw3.z;
skinWeightArray[ offset_skin + 11 ] = sw3.w;
// indices
si1 = obj_skinIndices[ face.a ];
si2 = obj_skinIndices[ face.b ];
si3 = obj_skinIndices[ face.c ];
skinIndexArray[ offset_skin ] = si1.x;
skinIndexArray[ offset_skin + 1 ] = si1.y;
skinIndexArray[ offset_skin + 2 ] = si1.z;
skinIndexArray[ offset_skin + 3 ] = si1.w;
skinIndexArray[ offset_skin + 4 ] = si2.x;
skinIndexArray[ offset_skin + 5 ] = si2.y;
skinIndexArray[ offset_skin + 6 ] = si2.z;
skinIndexArray[ offset_skin + 7 ] = si2.w;
skinIndexArray[ offset_skin + 8 ] = si3.x;
skinIndexArray[ offset_skin + 9 ] = si3.y;
skinIndexArray[ offset_skin + 10 ] = si3.z;
skinIndexArray[ offset_skin + 11 ] = si3.w;
// vertices A
sa1 = obj_skinVerticesA[ face.a ];
sa2 = obj_skinVerticesA[ face.b ];
sa3 = obj_skinVerticesA[ face.c ];
skinVertexAArray[ offset_skin ] = sa1.x;
skinVertexAArray[ offset_skin + 1 ] = sa1.y;
skinVertexAArray[ offset_skin + 2 ] = sa1.z;
skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader
skinVertexAArray[ offset_skin + 4 ] = sa2.x;
skinVertexAArray[ offset_skin + 5 ] = sa2.y;
skinVertexAArray[ offset_skin + 6 ] = sa2.z;
skinVertexAArray[ offset_skin + 7 ] = 1;
skinVertexAArray[ offset_skin + 8 ] = sa3.x;
skinVertexAArray[ offset_skin + 9 ] = sa3.y;
skinVertexAArray[ offset_skin + 10 ] = sa3.z;
skinVertexAArray[ offset_skin + 11 ] = 1;
// vertices B
sb1 = obj_skinVerticesB[ face.a ];
sb2 = obj_skinVerticesB[ face.b ];
sb3 = obj_skinVerticesB[ face.c ];
skinVertexBArray[ offset_skin ] = sb1.x;
skinVertexBArray[ offset_skin + 1 ] = sb1.y;
skinVertexBArray[ offset_skin + 2 ] = sb1.z;
skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader
skinVertexBArray[ offset_skin + 4 ] = sb2.x;
skinVertexBArray[ offset_skin + 5 ] = sb2.y;
skinVertexBArray[ offset_skin + 6 ] = sb2.z;
skinVertexBArray[ offset_skin + 7 ] = 1;
skinVertexBArray[ offset_skin + 8 ] = sb3.x;
skinVertexBArray[ offset_skin + 9 ] = sb3.y;
skinVertexBArray[ offset_skin + 10 ] = sb3.z;
skinVertexBArray[ offset_skin + 11 ] = 1;
offset_skin += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
// weights
sw1 = obj_skinWeights[ face.a ];
sw2 = obj_skinWeights[ face.b ];
sw3 = obj_skinWeights[ face.c ];
sw4 = obj_skinWeights[ face.d ];
skinWeightArray[ offset_skin ] = sw1.x;
skinWeightArray[ offset_skin + 1 ] = sw1.y;
skinWeightArray[ offset_skin + 2 ] = sw1.z;
skinWeightArray[ offset_skin + 3 ] = sw1.w;
skinWeightArray[ offset_skin + 4 ] = sw2.x;
skinWeightArray[ offset_skin + 5 ] = sw2.y;
skinWeightArray[ offset_skin + 6 ] = sw2.z;
skinWeightArray[ offset_skin + 7 ] = sw2.w;
skinWeightArray[ offset_skin + 8 ] = sw3.x;
skinWeightArray[ offset_skin + 9 ] = sw3.y;
skinWeightArray[ offset_skin + 10 ] = sw3.z;
skinWeightArray[ offset_skin + 11 ] = sw3.w;
skinWeightArray[ offset_skin + 12 ] = sw4.x;
skinWeightArray[ offset_skin + 13 ] = sw4.y;
skinWeightArray[ offset_skin + 14 ] = sw4.z;
skinWeightArray[ offset_skin + 15 ] = sw4.w;
// indices
si1 = obj_skinIndices[ face.a ];
si2 = obj_skinIndices[ face.b ];
si3 = obj_skinIndices[ face.c ];
si4 = obj_skinIndices[ face.d ];
skinIndexArray[ offset_skin ] = si1.x;
skinIndexArray[ offset_skin + 1 ] = si1.y;
skinIndexArray[ offset_skin + 2 ] = si1.z;
skinIndexArray[ offset_skin + 3 ] = si1.w;
skinIndexArray[ offset_skin + 4 ] = si2.x;
skinIndexArray[ offset_skin + 5 ] = si2.y;
skinIndexArray[ offset_skin + 6 ] = si2.z;
skinIndexArray[ offset_skin + 7 ] = si2.w;
skinIndexArray[ offset_skin + 8 ] = si3.x;
skinIndexArray[ offset_skin + 9 ] = si3.y;
skinIndexArray[ offset_skin + 10 ] = si3.z;
skinIndexArray[ offset_skin + 11 ] = si3.w;
skinIndexArray[ offset_skin + 12 ] = si4.x;
skinIndexArray[ offset_skin + 13 ] = si4.y;
skinIndexArray[ offset_skin + 14 ] = si4.z;
skinIndexArray[ offset_skin + 15 ] = si4.w;
// vertices A
sa1 = obj_skinVerticesA[ face.a ];
sa2 = obj_skinVerticesA[ face.b ];
sa3 = obj_skinVerticesA[ face.c ];
sa4 = obj_skinVerticesA[ face.d ];
skinVertexAArray[ offset_skin ] = sa1.x;
skinVertexAArray[ offset_skin + 1 ] = sa1.y;
skinVertexAArray[ offset_skin + 2 ] = sa1.z;
skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader
skinVertexAArray[ offset_skin + 4 ] = sa2.x;
skinVertexAArray[ offset_skin + 5 ] = sa2.y;
skinVertexAArray[ offset_skin + 6 ] = sa2.z;
skinVertexAArray[ offset_skin + 7 ] = 1;
skinVertexAArray[ offset_skin + 8 ] = sa3.x;
skinVertexAArray[ offset_skin + 9 ] = sa3.y;
skinVertexAArray[ offset_skin + 10 ] = sa3.z;
skinVertexAArray[ offset_skin + 11 ] = 1;
skinVertexAArray[ offset_skin + 12 ] = sa4.x;
skinVertexAArray[ offset_skin + 13 ] = sa4.y;
skinVertexAArray[ offset_skin + 14 ] = sa4.z;
skinVertexAArray[ offset_skin + 15 ] = 1;
// vertices B
sb1 = obj_skinVerticesB[ face.a ];
sb2 = obj_skinVerticesB[ face.b ];
sb3 = obj_skinVerticesB[ face.c ];
sb4 = obj_skinVerticesB[ face.d ];
skinVertexBArray[ offset_skin ] = sb1.x;
skinVertexBArray[ offset_skin + 1 ] = sb1.y;
skinVertexBArray[ offset_skin + 2 ] = sb1.z;
skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader
skinVertexBArray[ offset_skin + 4 ] = sb2.x;
skinVertexBArray[ offset_skin + 5 ] = sb2.y;
skinVertexBArray[ offset_skin + 6 ] = sb2.z;
skinVertexBArray[ offset_skin + 7 ] = 1;
skinVertexBArray[ offset_skin + 8 ] = sb3.x;
skinVertexBArray[ offset_skin + 9 ] = sb3.y;
skinVertexBArray[ offset_skin + 10 ] = sb3.z;
skinVertexBArray[ offset_skin + 11 ] = 1;
skinVertexBArray[ offset_skin + 12 ] = sb4.x;
skinVertexBArray[ offset_skin + 13 ] = sb4.y;
skinVertexBArray[ offset_skin + 14 ] = sb4.z;
skinVertexBArray[ offset_skin + 15 ] = 1;
offset_skin += 16;
}
if ( offset_skin > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinVertexAArray, hint );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinVertexBArray, hint );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint );
}
}
if ( dirtyColors && vertexColorType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexColors = face.vertexColors;
faceColor = face.color;
if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
c1 = vertexColors[ 0 ];
c2 = vertexColors[ 1 ];
c3 = vertexColors[ 2 ];
} else {
c1 = faceColor;
c2 = faceColor;
c3 = faceColor;
}
colorArray[ offset_color ] = c1.r;
colorArray[ offset_color + 1 ] = c1.g;
colorArray[ offset_color + 2 ] = c1.b;
colorArray[ offset_color + 3 ] = c2.r;
colorArray[ offset_color + 4 ] = c2.g;
colorArray[ offset_color + 5 ] = c2.b;
colorArray[ offset_color + 6 ] = c3.r;
colorArray[ offset_color + 7 ] = c3.g;
colorArray[ offset_color + 8 ] = c3.b;
offset_color += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexColors = face.vertexColors;
faceColor = face.color;
if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) {
c1 = vertexColors[ 0 ];
c2 = vertexColors[ 1 ];
c3 = vertexColors[ 2 ];
c4 = vertexColors[ 3 ];
} else {
c1 = faceColor;
c2 = faceColor;
c3 = faceColor;
c4 = faceColor;
}
colorArray[ offset_color ] = c1.r;
colorArray[ offset_color + 1 ] = c1.g;
colorArray[ offset_color + 2 ] = c1.b;
colorArray[ offset_color + 3 ] = c2.r;
colorArray[ offset_color + 4 ] = c2.g;
colorArray[ offset_color + 5 ] = c2.b;
colorArray[ offset_color + 6 ] = c3.r;
colorArray[ offset_color + 7 ] = c3.g;
colorArray[ offset_color + 8 ] = c3.b;
colorArray[ offset_color + 9 ] = c4.r;
colorArray[ offset_color + 10 ] = c4.g;
colorArray[ offset_color + 11 ] = c4.b;
offset_color += 12;
}
if ( offset_color > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
}
}
if ( dirtyTangents && geometry.hasTangents ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexTangents = face.vertexTangents;
t1 = vertexTangents[ 0 ];
t2 = vertexTangents[ 1 ];
t3 = vertexTangents[ 2 ];
tangentArray[ offset_tangent ] = t1.x;
tangentArray[ offset_tangent + 1 ] = t1.y;
tangentArray[ offset_tangent + 2 ] = t1.z;
tangentArray[ offset_tangent + 3 ] = t1.w;
tangentArray[ offset_tangent + 4 ] = t2.x;
tangentArray[ offset_tangent + 5 ] = t2.y;
tangentArray[ offset_tangent + 6 ] = t2.z;
tangentArray[ offset_tangent + 7 ] = t2.w;
tangentArray[ offset_tangent + 8 ] = t3.x;
tangentArray[ offset_tangent + 9 ] = t3.y;
tangentArray[ offset_tangent + 10 ] = t3.z;
tangentArray[ offset_tangent + 11 ] = t3.w;
offset_tangent += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexTangents = face.vertexTangents;
t1 = vertexTangents[ 0 ];
t2 = vertexTangents[ 1 ];
t3 = vertexTangents[ 2 ];
t4 = vertexTangents[ 3 ];
tangentArray[ offset_tangent ] = t1.x;
tangentArray[ offset_tangent + 1 ] = t1.y;
tangentArray[ offset_tangent + 2 ] = t1.z;
tangentArray[ offset_tangent + 3 ] = t1.w;
tangentArray[ offset_tangent + 4 ] = t2.x;
tangentArray[ offset_tangent + 5 ] = t2.y;
tangentArray[ offset_tangent + 6 ] = t2.z;
tangentArray[ offset_tangent + 7 ] = t2.w;
tangentArray[ offset_tangent + 8 ] = t3.x;
tangentArray[ offset_tangent + 9 ] = t3.y;
tangentArray[ offset_tangent + 10 ] = t3.z;
tangentArray[ offset_tangent + 11 ] = t3.w;
tangentArray[ offset_tangent + 12 ] = t4.x;
tangentArray[ offset_tangent + 13 ] = t4.y;
tangentArray[ offset_tangent + 14 ] = t4.z;
tangentArray[ offset_tangent + 15 ] = t4.w;
offset_tangent += 16;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint );
}
if ( dirtyNormals && normalType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
vertexNormals = face.vertexNormals;
faceNormal = face.normal;
if ( vertexNormals.length === 3 && needsSmoothNormals ) {
for ( i = 0; i < 3; i ++ ) {
vn = vertexNormals[ i ];
normalArray[ offset_normal ] = vn.x;
normalArray[ offset_normal + 1 ] = vn.y;
normalArray[ offset_normal + 2 ] = vn.z;
offset_normal += 3;
}
} else {
for ( i = 0; i < 3; i ++ ) {
normalArray[ offset_normal ] = faceNormal.x;
normalArray[ offset_normal + 1 ] = faceNormal.y;
normalArray[ offset_normal + 2 ] = faceNormal.z;
offset_normal += 3;
}
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
vertexNormals = face.vertexNormals;
faceNormal = face.normal;
if ( vertexNormals.length === 4 && needsSmoothNormals ) {
for ( i = 0; i < 4; i ++ ) {
vn = vertexNormals[ i ];
normalArray[ offset_normal ] = vn.x;
normalArray[ offset_normal + 1 ] = vn.y;
normalArray[ offset_normal + 2 ] = vn.z;
offset_normal += 3;
}
} else {
for ( i = 0; i < 4; i ++ ) {
normalArray[ offset_normal ] = faceNormal.x;
normalArray[ offset_normal + 1 ] = faceNormal.y;
normalArray[ offset_normal + 2 ] = faceNormal.z;
offset_normal += 3;
}
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
}
if ( dirtyUvs && obj_uvs && uvType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
face = obj_faces[ fi ];
uv = obj_uvs[ fi ];
if ( uv === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uvi = uv[ i ];
uvArray[ offset_uv ] = uvi.u;
uvArray[ offset_uv + 1 ] = uvi.v;
offset_uv += 2;
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
fi = chunk_faces4[ f ];
face = obj_faces[ fi ];
uv = obj_uvs[ fi ];
if ( uv === undefined ) continue;
for ( i = 0; i < 4; i ++ ) {
uvi = uv[ i ];
uvArray[ offset_uv ] = uvi.u;
uvArray[ offset_uv + 1 ] = uvi.v;
offset_uv += 2;
}
}
if ( offset_uv > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint );
}
}
if ( dirtyUvs && obj_uvs2 && uvType ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
face = obj_faces[ fi ];
uv2 = obj_uvs2[ fi ];
if ( uv2 === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uv2i = uv2[ i ];
uv2Array[ offset_uv2 ] = uv2i.u;
uv2Array[ offset_uv2 + 1 ] = uv2i.v;
offset_uv2 += 2;
}
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
fi = chunk_faces4[ f ];
face = obj_faces[ fi ];
uv2 = obj_uvs2[ fi ];
if ( uv2 === undefined ) continue;
for ( i = 0; i < 4; i ++ ) {
uv2i = uv2[ i ];
uv2Array[ offset_uv2 ] = uv2i.u;
uv2Array[ offset_uv2 + 1 ] = uv2i.v;
offset_uv2 += 2;
}
}
if ( offset_uv2 > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint );
}
}
if ( dirtyElements ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
faceArray[ offset_face ] = vertexIndex;
faceArray[ offset_face + 1 ] = vertexIndex + 1;
faceArray[ offset_face + 2 ] = vertexIndex + 2;
offset_face += 3;
lineArray[ offset_line ] = vertexIndex;
lineArray[ offset_line + 1 ] = vertexIndex + 1;
lineArray[ offset_line + 2 ] = vertexIndex;
lineArray[ offset_line + 3 ] = vertexIndex + 2;
lineArray[ offset_line + 4 ] = vertexIndex + 1;
lineArray[ offset_line + 5 ] = vertexIndex + 2;
offset_line += 6;
vertexIndex += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
faceArray[ offset_face ] = vertexIndex;
faceArray[ offset_face + 1 ] = vertexIndex + 1;
faceArray[ offset_face + 2 ] = vertexIndex + 3;
faceArray[ offset_face + 3 ] = vertexIndex + 1;
faceArray[ offset_face + 4 ] = vertexIndex + 2;
faceArray[ offset_face + 5 ] = vertexIndex + 3;
offset_face += 6;
lineArray[ offset_line ] = vertexIndex;
lineArray[ offset_line + 1 ] = vertexIndex + 1;
lineArray[ offset_line + 2 ] = vertexIndex;
lineArray[ offset_line + 3 ] = vertexIndex + 3;
lineArray[ offset_line + 4 ] = vertexIndex + 1;
lineArray[ offset_line + 5 ] = vertexIndex + 2;
lineArray[ offset_line + 6 ] = vertexIndex + 2;
lineArray[ offset_line + 7 ] = vertexIndex + 3;
offset_line += 8;
vertexIndex += 4;
}
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint );
}
if ( customAttributes ) {
for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
customAttribute = customAttributes[ i ];
if ( ! customAttribute.__original.needsUpdate ) continue;
offset_custom = 0;
offset_customSrc = 0;
if ( customAttribute.size === 1 ) {
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ];
customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
offset_custom += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ];
customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ];
offset_custom += 4;
}
} else if ( customAttribute.boundTo === "faces" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
customAttribute.array[ offset_custom ] = value;
customAttribute.array[ offset_custom + 1 ] = value;
customAttribute.array[ offset_custom + 2 ] = value;
offset_custom += 3;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces4[ f ] ];
customAttribute.array[ offset_custom ] = value;
customAttribute.array[ offset_custom + 1 ] = value;
customAttribute.array[ offset_custom + 2 ] = value;
customAttribute.array[ offset_custom + 3 ] = value;
offset_custom += 4;
}
}
} else if ( customAttribute.size === 2 ) {
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v2.x;
customAttribute.array[ offset_custom + 3 ] = v2.y;
customAttribute.array[ offset_custom + 4 ] = v3.x;
customAttribute.array[ offset_custom + 5 ] = v3.y;
offset_custom += 6;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
v4 = customAttribute.value[ face.d ];
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v2.x;
customAttribute.array[ offset_custom + 3 ] = v2.y;
customAttribute.array[ offset_custom + 4 ] = v3.x;
customAttribute.array[ offset_custom + 5 ] = v3.y;
customAttribute.array[ offset_custom + 6 ] = v4.x;
customAttribute.array[ offset_custom + 7 ] = v4.y;
offset_custom += 8;
}
} else if ( customAttribute.boundTo === "faces" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
v1 = value;
v2 = value;
v3 = value;
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v2.x;
customAttribute.array[ offset_custom + 3 ] = v2.y;
customAttribute.array[ offset_custom + 4 ] = v3.x;
customAttribute.array[ offset_custom + 5 ] = v3.y;
offset_custom += 6;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces4[ f ] ];
v1 = value;
v2 = value;
v3 = value;
v4 = value;
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v2.x;
customAttribute.array[ offset_custom + 3 ] = v2.y;
customAttribute.array[ offset_custom + 4 ] = v3.x;
customAttribute.array[ offset_custom + 5 ] = v3.y;
customAttribute.array[ offset_custom + 6 ] = v4.x;
customAttribute.array[ offset_custom + 7 ] = v4.y;
offset_custom += 8;
}
}
} else if ( customAttribute.size === 3 ) {
var pp;
if ( customAttribute.type === "c" ) {
pp = [ "r", "g", "b" ];
} else {
pp = [ "x", "y", "z" ];
}
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
offset_custom += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
v4 = customAttribute.value[ face.d ];
customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
offset_custom += 12;
}
} else if ( customAttribute.boundTo === "faces" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
v1 = value;
v2 = value;
v3 = value;
customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
offset_custom += 9;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces4[ f ] ];
v1 = value;
v2 = value;
v3 = value;
v4 = value;
customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ];
customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
offset_custom += 12;
}
}
} else if ( customAttribute.size === 4 ) {
if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v1.z;
customAttribute.array[ offset_custom + 3 ] = v1.w;
customAttribute.array[ offset_custom + 4 ] = v2.x;
customAttribute.array[ offset_custom + 5 ] = v2.y;
customAttribute.array[ offset_custom + 6 ] = v2.z;
customAttribute.array[ offset_custom + 7 ] = v2.w;
customAttribute.array[ offset_custom + 8 ] = v3.x;
customAttribute.array[ offset_custom + 9 ] = v3.y;
customAttribute.array[ offset_custom + 10 ] = v3.z;
customAttribute.array[ offset_custom + 11 ] = v3.w;
offset_custom += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces4[ f ] ];
v1 = customAttribute.value[ face.a ];
v2 = customAttribute.value[ face.b ];
v3 = customAttribute.value[ face.c ];
v4 = customAttribute.value[ face.d ];
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v1.z;
customAttribute.array[ offset_custom + 3 ] = v1.w;
customAttribute.array[ offset_custom + 4 ] = v2.x;
customAttribute.array[ offset_custom + 5 ] = v2.y;
customAttribute.array[ offset_custom + 6 ] = v2.z;
customAttribute.array[ offset_custom + 7 ] = v2.w;
customAttribute.array[ offset_custom + 8 ] = v3.x;
customAttribute.array[ offset_custom + 9 ] = v3.y;
customAttribute.array[ offset_custom + 10 ] = v3.z;
customAttribute.array[ offset_custom + 11 ] = v3.w;
customAttribute.array[ offset_custom + 12 ] = v4.x;
customAttribute.array[ offset_custom + 13 ] = v4.y;
customAttribute.array[ offset_custom + 14 ] = v4.z;
customAttribute.array[ offset_custom + 15 ] = v4.w;
offset_custom += 16;
}
} else if ( customAttribute.boundTo === "faces" ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
v1 = value;
v2 = value;
v3 = value;
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v1.z;
customAttribute.array[ offset_custom + 3 ] = v1.w;
customAttribute.array[ offset_custom + 4 ] = v2.x;
customAttribute.array[ offset_custom + 5 ] = v2.y;
customAttribute.array[ offset_custom + 6 ] = v2.z;
customAttribute.array[ offset_custom + 7 ] = v2.w;
customAttribute.array[ offset_custom + 8 ] = v3.x;
customAttribute.array[ offset_custom + 9 ] = v3.y;
customAttribute.array[ offset_custom + 10 ] = v3.z;
customAttribute.array[ offset_custom + 11 ] = v3.w;
offset_custom += 12;
}
for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces4[ f ] ];
v1 = value;
v2 = value;
v3 = value;
v4 = value;
customAttribute.array[ offset_custom ] = v1.x;
customAttribute.array[ offset_custom + 1 ] = v1.y;
customAttribute.array[ offset_custom + 2 ] = v1.z;
customAttribute.array[ offset_custom + 3 ] = v1.w;
customAttribute.array[ offset_custom + 4 ] = v2.x;
customAttribute.array[ offset_custom + 5 ] = v2.y;
customAttribute.array[ offset_custom + 6 ] = v2.z;
customAttribute.array[ offset_custom + 7 ] = v2.w;
customAttribute.array[ offset_custom + 8 ] = v3.x;
customAttribute.array[ offset_custom + 9 ] = v3.y;
customAttribute.array[ offset_custom + 10 ] = v3.z;
customAttribute.array[ offset_custom + 11 ] = v3.w;
customAttribute.array[ offset_custom + 12 ] = v4.x;
customAttribute.array[ offset_custom + 13 ] = v4.y;
customAttribute.array[ offset_custom + 14 ] = v4.z;
customAttribute.array[ offset_custom + 15 ] = v4.w;
offset_custom += 16;
}
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
}
}
if ( dispose ) {
delete geometryGroup.__inittedArrays;
delete geometryGroup.__colorArray;
delete geometryGroup.__normalArray;
delete geometryGroup.__tangentArray;
delete geometryGroup.__uvArray;
delete geometryGroup.__uv2Array;
delete geometryGroup.__faceArray;
delete geometryGroup.__vertexArray;
delete geometryGroup.__lineArray;
delete geometryGroup.__skinVertexAArray;
delete geometryGroup.__skinVertexBArray;
delete geometryGroup.__skinIndexArray;
delete geometryGroup.__skinWeightArray;
}
};
// Buffer rendering
function renderBufferImmediate ( object, program, shading ) {
if ( ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer();
if ( ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer();
if ( object.hasPos ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
_gl.enableVertexAttribArray( program.attributes.position );
_gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 );
}
if ( object.hasNormal ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer );
if ( shading === THREE.FlatShading ) {
var nx, ny, nz,
nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz,
normalArray,
i, il = object.count * 3;
for( i = 0; i < il; i += 9 ) {
normalArray = object.normalArray;
nax = normalArray[ i ];
nay = normalArray[ i + 1 ];
naz = normalArray[ i + 2 ];
nbx = normalArray[ i + 3 ];
nby = normalArray[ i + 4 ];
nbz = normalArray[ i + 5 ];
ncx = normalArray[ i + 6 ];
ncy = normalArray[ i + 7 ];
ncz = normalArray[ i + 8 ];
nx = ( nax + nbx + ncx ) / 3;
ny = ( nay + nby + ncy ) / 3;
nz = ( naz + nbz + ncz ) / 3;
normalArray[ i ] = nx;
normalArray[ i + 1 ] = ny;
normalArray[ i + 2 ] = nz;
normalArray[ i + 3 ] = nx;
normalArray[ i + 4 ] = ny;
normalArray[ i + 5 ] = nz;
normalArray[ i + 6 ] = nx;
normalArray[ i + 7 ] = ny;
normalArray[ i + 8 ] = nz;
}
}
_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
_gl.enableVertexAttribArray( program.attributes.normal );
_gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
}
_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
object.count = 0;
};
function renderBuffer ( camera, lights, fog, material, geometryGroup, object ) {
if ( material.opacity === 0 ) return;
var program, attributes, linewidth, primitives, a, attribute, i, il;
program = setProgram( camera, lights, fog, material, object );
attributes = program.attributes;
var updateBuffers = false,
wireframeBit = material.wireframe ? 1 : 0,
geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
if ( geometryGroupHash !== _currentGeometryGroupHash ) {
_currentGeometryGroupHash = geometryGroupHash;
updateBuffers = true;
}
// vertices
if ( !material.morphTargets && attributes.position >= 0 ) {
if ( updateBuffers ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
}
} else {
if ( object.morphTargetBase ) {
setupMorphTargets( material, geometryGroup, object );
}
}
if ( updateBuffers ) {
// custom attributes
// Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers
if ( geometryGroup.__webglCustomAttributesList ) {
for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
attribute = geometryGroup.__webglCustomAttributesList[ i ];
if( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer );
_gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 );
}
}
}
// colors
if ( attributes.color >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
}
// normals
if ( attributes.normal >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
}
// tangents
if ( attributes.tangent >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
}
// uvs
if ( attributes.uv >= 0 ) {
if ( geometryGroup.__webglUVBuffer ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
_gl.enableVertexAttribArray( attributes.uv );
} else {
_gl.disableVertexAttribArray( attributes.uv );
}
}
if ( attributes.uv2 >= 0 ) {
if ( geometryGroup.__webglUV2Buffer ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
_gl.enableVertexAttribArray( attributes.uv2 );
} else {
_gl.disableVertexAttribArray( attributes.uv2 );
}
}
if ( material.skinning &&
attributes.skinVertexA >= 0 && attributes.skinVertexB >= 0 &&
attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer );
_gl.vertexAttribPointer( attributes.skinVertexA, 4, _gl.FLOAT, false, 0, 0 );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer );
_gl.vertexAttribPointer( attributes.skinVertexB, 4, _gl.FLOAT, false, 0, 0 );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
_gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
_gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 );
}
}
// render mesh
if ( object instanceof THREE.Mesh ) {
// wireframe
if ( material.wireframe ) {
setLineWidth( material.wireframeLinewidth );
if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 );
// triangles
} else {
if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 );
}
_this.info.render.calls ++;
_this.info.render.vertices += geometryGroup.__webglFaceCount;
_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
// render lines
} else if ( object instanceof THREE.Line ) {
primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
setLineWidth( material.linewidth );
_gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount );
_this.info.render.calls ++;
// render particles
} else if ( object instanceof THREE.ParticleSystem ) {
_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
_this.info.render.calls ++;
// render ribbon
} else if ( object instanceof THREE.Ribbon ) {
_gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount );
_this.info.render.calls ++;
}
};
function setupMorphTargets ( material, geometryGroup, object ) {
// set base
var attributes = material.program.attributes;
if ( object.morphTargetBase !== - 1 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] );
_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
} else if ( attributes.position >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
}
if ( object.morphTargetForcedOrder.length ) {
// set forced order
var m = 0;
var order = object.morphTargetForcedOrder;
var influences = object.morphTargetInfluences;
while ( m < material.numSupportedMorphTargets && m < order.length ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
m ++;
}
} else {
// find most influencing
var used = [];
var candidateInfluence = - 1;
var candidate = 0;
var influences = object.morphTargetInfluences;
var i, il = influences.length;
var m = 0;
if ( object.morphTargetBase !== - 1 ) {
used[ object.morphTargetBase ] = true;
}
while ( m < material.numSupportedMorphTargets ) {
for ( i = 0; i < il; i ++ ) {
if ( !used[ i ] && influences[ i ] > candidateInfluence ) {
candidate = i;
candidateInfluence = influences[ candidate ];
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ candidate ] );
_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
object.__webglMorphTargetInfluences[ m ] = candidateInfluence;
used[ candidate ] = 1;
candidateInfluence = -1;
m ++;
}
}
// load updated influences uniform
if( material.program.uniforms.morphTargetInfluences !== null ) {
_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
}
}
// Frustum
function computeFrustum ( m ) {
_frustum[ 0 ].set( m.n41 - m.n11, m.n42 - m.n12, m.n43 - m.n13, m.n44 - m.n14 );
_frustum[ 1 ].set( m.n41 + m.n11, m.n42 + m.n12, m.n43 + m.n13, m.n44 + m.n14 );
_frustum[ 2 ].set( m.n41 + m.n21, m.n42 + m.n22, m.n43 + m.n23, m.n44 + m.n24 );
_frustum[ 3 ].set( m.n41 - m.n21, m.n42 - m.n22, m.n43 - m.n23, m.n44 - m.n24 );
_frustum[ 4 ].set( m.n41 - m.n31, m.n42 - m.n32, m.n43 - m.n33, m.n44 - m.n34 );
_frustum[ 5 ].set( m.n41 + m.n31, m.n42 + m.n32, m.n43 + m.n33, m.n44 + m.n34 );
var i, plane;
for ( i = 0; i < 6; i ++ ) {
plane = _frustum[ i ];
plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) );
}
};
function isInFrustum ( object ) {
var distance, matrix = object.matrixWorld,
radius = - object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) );
for ( var i = 0; i < 6; i ++ ) {
distance = _frustum[ i ].x * matrix.n14 + _frustum[ i ].y * matrix.n24 + _frustum[ i ].z * matrix.n34 + _frustum[ i ].w;
if ( distance <= radius ) return false;
}
return true;
};
function painterSort ( a, b ) {
return b.z - a.z;
};
// Rendering
this.render = function ( scene, camera, renderTarget, forceClear ) {
var i, il,
program, material,
webglObject, object,
renderList,
lights = scene.lights,
fog = scene.fog;
_currentMaterialId = -1;
if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
if ( this.shadowMapEnabled && this.shadowMapAutoUpdate ) renderShadowMap( scene, camera );
_this.info.render.calls = 0;
_this.info.render.vertices = 0;
_this.info.render.faces = 0;
if ( camera.parent === undefined ) {
console.warn( 'DEPRECATED: Camera hasn\'t been added to a Scene. Adding it...' );
scene.add( camera );
}
if ( this.autoUpdateScene ) scene.updateMatrixWorld();
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
camera.matrixWorldInverse.flattenToArray( _viewMatrixArray );
camera.projectionMatrix.flattenToArray( _projectionMatrixArray );
_projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse );
computeFrustum( _projScreenMatrix );
setRenderTarget( renderTarget );
if ( this.autoClear || forceClear ) {
this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
}
// set matrices for regular objects (frustum culled)
renderList = scene.__webglObjects;
for ( i = 0, il = renderList.length; i < il; i ++ ) {
webglObject = renderList[ i ];
object = webglObject.object;
webglObject.render = false;
if ( object.visible ) {
if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || isInFrustum( object ) ) {
object.matrixWorld.flattenToArray( object._objectMatrixArray );
setupMatrices( object, camera, true );
unrollBufferMaterial( webglObject );
webglObject.render = true;
if ( this.sortObjects ) {
if ( object.renderDepth ) {
webglObject.z = object.renderDepth;
} else {
_vector3.copy( object.position );
_projScreenMatrix.multiplyVector3( _vector3 );
webglObject.z = _vector3.z;
}
}
}
}
}
if ( this.sortObjects ) {
renderList.sort( painterSort );
}
// set matrices for immediate objects
renderList = scene.__webglObjectsImmediate;
for ( i = 0, il = renderList.length; i < il; i ++ ) {
webglObject = renderList[ i ];
object = webglObject.object;
if ( object.visible ) {
if( object.matrixAutoUpdate ) {
object.matrixWorld.flattenToArray( object._objectMatrixArray );
}
setupMatrices( object, camera, true );
unrollImmediateBufferMaterial( webglObject );
}
}
if ( scene.overrideMaterial ) {
setBlending( scene.overrideMaterial.blending );
setDepthTest( scene.overrideMaterial.depthTest );
setDepthWrite( scene.overrideMaterial.depthWrite );
setPolygonOffset( scene.overrideMaterial.polygonOffset, scene.overrideMaterial.polygonOffsetFactor, scene.overrideMaterial.polygonOffsetUnits );
renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, scene.overrideMaterial );
renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, scene.overrideMaterial );
} else {
// opaque pass (front-to-back order)
setBlending( THREE.NormalBlending );
renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false );
renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false );
// transparent pass (back-to-front order)
renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true );
renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true );
}
// render 2d
if ( scene.__webglSprites.length ) {
renderSprites( scene, camera );
}
// Generate mipmap if we're using any kind of mipmap filtering
if ( renderTarget && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
updateRenderTargetMipmap( renderTarget );
}
//_gl.finish();
};
function renderShadowMap ( scene, camera ) {
var i, il, j, jl,
shadowMap, shadowMatrix,
program, buffer, material,
webglObject, object, light,
shadowIndex = 0,
lights = scene.lights,
fog = null;
if ( ! _cameraLight ) {
_cameraLight = new THREE.PerspectiveCamera( _this.shadowCameraFov, _this.shadowMapWidth / _this.shadowMapHeight, _this.shadowCameraNear, _this.shadowCameraFar );
}
for ( i = 0, il = lights.length; i < il; i ++ ) {
light = lights[ i ];
if ( light.castShadow && light instanceof THREE.SpotLight ) {
_currentMaterialId = -1;
if ( ! _this.shadowMap[ shadowIndex ] ) {
var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat };
_this.shadowMap[ shadowIndex ] = new THREE.WebGLRenderTarget( _this.shadowMapWidth, _this.shadowMapHeight, pars );
_shadowMatrix[ shadowIndex ] = new THREE.Matrix4();
}
shadowMap = _this.shadowMap[ shadowIndex ];
shadowMatrix = _shadowMatrix[ shadowIndex ];
_cameraLight.position.copy( light.position );
_cameraLight.lookAt( light.target.position );
if ( _cameraLight.parent == null ) {
console.warn( "Camera is not on the Scene. Adding it..." );
scene.add( _cameraLight );
}
if ( this.autoUpdateScene ) scene.updateMatrixWorld();
_cameraLight.matrixWorldInverse.getInverse( _cameraLight.matrixWorld );
// compute shadow matrix
shadowMatrix.set( 0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0 );
shadowMatrix.multiplySelf( _cameraLight.projectionMatrix );
shadowMatrix.multiplySelf( _cameraLight.matrixWorldInverse );
// render shadow map
_cameraLight.matrixWorldInverse.flattenToArray( _viewMatrixArray );
_cameraLight.projectionMatrix.flattenToArray( _projectionMatrixArray );
_projScreenMatrix.multiply( _cameraLight.projectionMatrix, _cameraLight.matrixWorldInverse );
computeFrustum( _projScreenMatrix );
setRenderTarget( shadowMap );
// using arbitrary clear color in depth pass
// creates variance in shadows
_gl.clearColor( 1, 1, 1, 1 );
//_gl.clearColor( 0, 0, 0, 1 );
_this.clear();
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
// set matrices & frustum culling
jl = scene.__webglObjects.length;
for ( j = 0; j < jl; j ++ ) {
webglObject = scene.__webglObjects[ j ];
object = webglObject.object;
webglObject.render = false;
if ( object.visible && object.castShadow ) {
if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || isInFrustum( object ) ) {
object.matrixWorld.flattenToArray( object._objectMatrixArray );
setupMatrices( object, _cameraLight, false );
webglObject.render = true;
}
}
}
// render regular objects
setDepthTest( true );
setBlending( THREE.NormalBlending ); // maybe blending should be just disabled?
//_gl.cullFace( _gl.FRONT );
for ( j = 0; j < jl; j ++ ) {
webglObject = scene.__webglObjects[ j ];
if ( webglObject.render ) {
object = webglObject.object;
buffer = webglObject.buffer;
setObjectFaces( object );
if ( object.customDepthMaterial ) {
material = object.customDepthMaterial;
} else if ( object.geometry.morphTargets.length ) {
material = _depthMaterialMorph;
} else {
material = _depthMaterial;
}
renderBuffer( _cameraLight, lights, fog, material, buffer, object );
}
}
// set matrices and render immediate objects
jl = scene.__webglObjectsImmediate.length;
for ( j = 0; j < jl; j ++ ) {
webglObject = scene.__webglObjectsImmediate[ j ];
object = webglObject.object;
if ( object.visible && object.castShadow ) {
if( object.matrixAutoUpdate ) {
object.matrixWorld.flattenToArray( object._objectMatrixArray );
}
_currentGeometryGroupHash = -1;
setupMatrices( object, _cameraLight, false );
setObjectFaces( object );
program = setProgram( _cameraLight, lights, fog, _depthMaterial, object );
if ( object.immediateRenderCallback ) {
object.immediateRenderCallback( program, _gl, _frustum );
} else {
object.render( function( object ) { renderBufferImmediate( object, program, _depthMaterial.shading ); } );
}
}
}
//_gl.cullFace( _gl.BACK );
shadowIndex ++;
}
}
};
function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
var webglObject, object, buffer, material, start, end, delta;
if ( reverse ) {
start = renderList.length - 1;
end = -1;
delta = -1;
} else {
start = 0;
end = renderList.length;
delta = 1;
}
for ( var i = start; i !== end; i += delta ) {
webglObject = renderList[ i ];
if ( webglObject.render ) {
object = webglObject.object;
buffer = webglObject.buffer;
if ( overrideMaterial ) {
material = overrideMaterial;
} else {
material = webglObject[ materialType ];
if ( ! material ) continue;
if ( useBlending ) setBlending( material.blending );
setDepthTest( material.depthTest );
setDepthWrite( material.depthWrite );
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
}
setObjectFaces( object );
renderBuffer( camera, lights, fog, material, buffer, object );
}
}
};
function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
var webglObject, object, material, program;
for ( var i = 0, il = renderList.length; i < il; i ++ ) {
webglObject = renderList[ i ];
object = webglObject.object;
if ( object.visible ) {
_currentGeometryGroupHash = -1;
if ( overrideMaterial ) {
material = overrideMaterial;
} else {
material = webglObject[ materialType ];
if ( ! material ) continue;
if ( useBlending ) setBlending( material.blending );
setDepthTest( material.depthTest );
setDepthWrite( material.depthWrite );
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
}
setObjectFaces( object );
program = setProgram( camera, lights, fog, material, object );
if ( object.immediateRenderCallback ) {
object.immediateRenderCallback( program, _gl, _frustum );
} else {
object.render( function( object ) { renderBufferImmediate( object, program, material.shading ); } );
}
}
}
};
function unrollImmediateBufferMaterial ( globject ) {
var object = globject.object,
material = object.material;
if ( material.transparent ) {
globject.transparent = material;
globject.opaque = null;
} else {
globject.opaque = material;
globject.transparent = null;
}
};
function unrollBufferMaterial ( globject ) {
var object = globject.object,
buffer = globject.buffer,
material, materialIndex, meshMaterial;
meshMaterial = object.material;
if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
materialIndex = buffer.materialIndex;
if ( materialIndex >= 0 ) {
material = object.geometry.materials[ materialIndex ];
if ( material.transparent ) {
globject.transparent = material;
globject.opaque = null;
} else {
globject.opaque = material;
globject.transparent = null;
}
}
} else {
material = meshMaterial;
if ( material ) {
if ( material.transparent ) {
globject.transparent = material;
globject.opaque = null;
} else {
globject.opaque = material;
globject.transparent = null;
}
}
}
};
function renderSprites ( scene, camera ) {
var o, ol, object;
var attributes = _sprite.attributes;
var uniforms = _sprite.uniforms;
var invAspect = _viewportHeight / _viewportWidth;
var size, scale = [];
var screenPosition;
var halfViewportWidth = _viewportWidth * 0.5;
var halfViewportHeight = _viewportHeight * 0.5;
var mergeWith3D = true;
// setup gl
_gl.useProgram( _sprite.program );
_currentProgram = _sprite.program;
_oldBlending = -1;
_oldDepthTest = -1;
_currentGeometryGroupHash = -1;
if ( !_spriteAttributesEnabled ) {
_gl.enableVertexAttribArray( _sprite.attributes.position );
_gl.enableVertexAttribArray( _sprite.attributes.uv );
_spriteAttributesEnabled = true;
}
_gl.disable( _gl.CULL_FACE );
_gl.enable( _gl.BLEND );
_gl.depthMask( true );
_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
_gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 );
_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
_gl.uniformMatrix4fv( uniforms.projectionMatrix, false, _projectionMatrixArray );
_gl.activeTexture( _gl.TEXTURE0 );
_gl.uniform1i( uniforms.map, 0 );
// update positions and sort
for( o = 0, ol = scene.__webglSprites.length; o < ol; o ++ ) {
object = scene.__webglSprites[ o ];
if ( !object.visible || object.opacity === 0 ) continue;
if( !object.useScreenCoordinates ) {
object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray );
object.z = -object._modelViewMatrix.n34;
} else {
object.z = -object.position.z;
}
}
scene.__webglSprites.sort( painterSort );
// render all sprites
for ( o = 0, ol = scene.__webglSprites.length; o < ol; o ++ ) {
object = scene.__webglSprites[ o ];
if ( !object.visible || object.opacity === 0 ) continue;
if ( object.map && object.map.image && object.map.image.width ) {
if ( object.useScreenCoordinates ) {
_gl.uniform1i( uniforms.useScreenCoordinates, 1 );
_gl.uniform3f( uniforms.screenPosition, ( object.position.x - halfViewportWidth ) / halfViewportWidth,
( halfViewportHeight - object.position.y ) / halfViewportHeight,
Math.max( 0, Math.min( 1, object.position.z )));
} else {
_gl.uniform1i( uniforms.useScreenCoordinates, 0 );
_gl.uniform1i( uniforms.affectedByDistance, object.affectedByDistance ? 1 : 0 );
_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray );
}
size = object.map.image.width / ( object.scaleByViewport ? _viewportHeight : 1 );
scale[ 0 ] = size * invAspect * object.scale.x;
scale[ 1 ] = size * object.scale.y;
_gl.uniform2f( uniforms.uvScale, object.uvScale.x, object.uvScale.y );
_gl.uniform2f( uniforms.uvOffset, object.uvOffset.x, object.uvOffset.y );
_gl.uniform2f( uniforms.alignment, object.alignment.x, object.alignment.y );
_gl.uniform1f( uniforms.opacity, object.opacity );
_gl.uniform3f( uniforms.color, object.color.r, object.color.g, object.color.b );
_gl.uniform1f( uniforms.rotation, object.rotation );
_gl.uniform2fv( uniforms.scale, scale );
if ( object.mergeWith3D && !mergeWith3D ) {
_gl.enable( _gl.DEPTH_TEST );
mergeWith3D = true;
} else if ( !object.mergeWith3D && mergeWith3D ) {
_gl.disable( _gl.DEPTH_TEST );
mergeWith3D = false;
}
setBlending( object.blending );
setTexture( object.map, 0 );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
}
}
// restore gl
_gl.enable( _gl.CULL_FACE );
_gl.enable( _gl.DEPTH_TEST );
_gl.depthMask( _oldDepthWrite );
}
// Geometry splitting
function sortFacesByMaterial ( geometry ) {
var f, fl, face, materialIndex, vertices,
materialHash, groupHash,
hash_map = {};
var numMorphTargets = geometry.morphTargets.length;
geometry.geometryGroups = {};
for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
face = geometry.faces[ f ];
materialIndex = face.materialIndex;
materialHash = ( materialIndex !== undefined ) ? materialIndex : -1;
if ( hash_map[ materialHash ] === undefined ) {
hash_map[ materialHash ] = { 'hash': materialHash, 'counter': 0 };
}
groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter;
if ( geometry.geometryGroups[ groupHash ] === undefined ) {
geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets };
}
vertices = face instanceof THREE.Face3 ? 3 : 4;
if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
hash_map[ materialHash ].counter += 1;
groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter;
if ( geometry.geometryGroups[ groupHash ] === undefined ) {
geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets };
}
}
if ( face instanceof THREE.Face3 ) {
geometry.geometryGroups[ groupHash ].faces3.push( f );
} else {
geometry.geometryGroups[ groupHash ].faces4.push( f );
}
geometry.geometryGroups[ groupHash ].vertices += vertices;
}
geometry.geometryGroupsList = [];
for ( var g in geometry.geometryGroups ) {
geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
}
};
// Objects refresh
this.initWebGLObjects = function ( scene ) {
if ( !scene.__webglObjects ) {
scene.__webglObjects = [];
scene.__webglObjectsImmediate = [];
scene.__webglSprites = [];
}
while ( scene.__objectsAdded.length ) {
addObject( scene.__objectsAdded[ 0 ], scene );
scene.__objectsAdded.splice( 0, 1 );
}
while ( scene.__objectsRemoved.length ) {
removeObject( scene.__objectsRemoved[ 0 ], scene );
scene.__objectsRemoved.splice( 0, 1 );
}
// update must be called after objects adding / removal
for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
updateObject( scene.__webglObjects[ o ].object );
}
};
// Objects adding
function addObject ( object, scene ) {
var g, geometry, geometryGroup;
if ( ! object.__webglInit ) {
object.__webglInit = true;
object._modelViewMatrix = new THREE.Matrix4();
object._normalMatrixArray = new Float32Array( 9 );
object._modelViewMatrixArray = new Float32Array( 16 );
object._objectMatrixArray = new Float32Array( 16 );
object.matrixWorld.flattenToArray( object._objectMatrixArray );
if ( object instanceof THREE.Mesh ) {
geometry = object.geometry;
if ( geometry.geometryGroups === undefined ) {
sortFacesByMaterial( geometry );
}
// create separate VBOs per geometry chunk
for ( g in geometry.geometryGroups ) {
geometryGroup = geometry.geometryGroups[ g ];
// initialise VBO on the first access
if ( ! geometryGroup.__webglVertexBuffer ) {
createMeshBuffers( geometryGroup );
initMeshBuffers( geometryGroup, object );
geometry.__dirtyVertices = true;
geometry.__dirtyMorphTargets = true;
geometry.__dirtyElements = true;
geometry.__dirtyUvs = true;
geometry.__dirtyNormals = true;
geometry.__dirtyTangents = true;
geometry.__dirtyColors = true;
}
}
} else if ( object instanceof THREE.Ribbon ) {
geometry = object.geometry;
if( ! geometry.__webglVertexBuffer ) {
createRibbonBuffers( geometry );
initRibbonBuffers( geometry );
geometry.__dirtyVertices = true;
geometry.__dirtyColors = true;
}
} else if ( object instanceof THREE.Line ) {
geometry = object.geometry;
if( ! geometry.__webglVertexBuffer ) {
createLineBuffers( geometry );
initLineBuffers( geometry, object );
geometry.__dirtyVertices = true;
geometry.__dirtyColors = true;
}
} else if ( object instanceof THREE.ParticleSystem ) {
geometry = object.geometry;
if ( ! geometry.__webglVertexBuffer ) {
createParticleBuffers( geometry );
initParticleBuffers( geometry, object );
geometry.__dirtyVertices = true;
geometry.__dirtyColors = true;
}
}
}
if ( ! object.__webglActive ) {
if ( object instanceof THREE.Mesh ) {
geometry = object.geometry;
for ( g in geometry.geometryGroups ) {
geometryGroup = geometry.geometryGroups[ g ];
addBuffer( scene.__webglObjects, geometryGroup, object );
}
} else if ( object instanceof THREE.Ribbon ||
object instanceof THREE.Line ||
object instanceof THREE.ParticleSystem ) {
geometry = object.geometry;
addBuffer( scene.__webglObjects, geometry, object );
} else if ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes || object.immediateRenderCallback ) {
addBufferImmediate( scene.__webglObjectsImmediate, object );
} else if ( object instanceof THREE.Sprite ) {
scene.__webglSprites.push( object );
}
object.__webglActive = true;
}
};
function addBuffer ( objlist, buffer, object ) {
objlist.push(
{
buffer: buffer,
object: object,
opaque: null,
transparent: null
}
);
};
function addBufferImmediate ( objlist, object ) {
objlist.push(
{
object: object,
opaque: null,
transparent: null
}
);
};
// Objects updates
function updateObject ( object ) {
var geometry = object.geometry,
geometryGroup, customAttributesDirty, material;
if ( object instanceof THREE.Mesh ) {
// check all geometry groups
for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
geometryGroup = geometry.geometryGroupsList[ i ];
material = getBufferMaterial( object, geometryGroup );
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.__dirtyVertices || geometry.__dirtyMorphTargets || geometry.__dirtyElements ||
geometry.__dirtyUvs || geometry.__dirtyNormals ||
geometry.__dirtyColors || geometry.__dirtyTangents || customAttributesDirty ) {
setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material );
}
}
geometry.__dirtyVertices = false;
geometry.__dirtyMorphTargets = false;
geometry.__dirtyElements = false;
geometry.__dirtyUvs = false;
geometry.__dirtyNormals = false;
geometry.__dirtyColors = false;
geometry.__dirtyTangents = false;
material.attributes && clearCustomAttributes( material );
} else if ( object instanceof THREE.Ribbon ) {
if ( geometry.__dirtyVertices || geometry.__dirtyColors ) {
setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW );
}
geometry.__dirtyVertices = false;
geometry.__dirtyColors = false;
} else if ( object instanceof THREE.Line ) {
material = getBufferMaterial( object, geometryGroup );
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.__dirtyVertices || geometry.__dirtyColors || customAttributesDirty ) {
setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
}
geometry.__dirtyVertices = false;
geometry.__dirtyColors = false;
material.attributes && clearCustomAttributes( material );
} else if ( object instanceof THREE.ParticleSystem ) {
material = getBufferMaterial( object, geometryGroup );
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles || customAttributesDirty ) {
setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
}
geometry.__dirtyVertices = false;
geometry.__dirtyColors = false;
material.attributes && clearCustomAttributes( material );
}
};
// Objects updates - custom attributes check
function areCustomAttributesDirty ( material ) {
for ( var a in material.attributes ) {
if ( material.attributes[ a ].needsUpdate ) return true;
}
return false;
};
function clearCustomAttributes ( material ) {
for ( var a in material.attributes ) {
material.attributes[ a ].needsUpdate = false;
}
};
// Objects removal
function removeObject ( object, scene ) {
if ( object instanceof THREE.Mesh ||
object instanceof THREE.ParticleSystem ||
object instanceof THREE.Ribbon ||
object instanceof THREE.Line ) {
removeInstances( scene.__webglObjects, object );
} else if ( object instanceof THREE.Sprite ) {
removeInstancesDirect( scene.__webglSprites, object );
} else if ( object instanceof THREE.MarchingCubes || object.immediateRenderCallback ) {
removeInstances( scene.__webglObjectsImmediate, object );
}
object.__webglActive = false;
};
function removeInstances ( objlist, object ) {
for ( var o = objlist.length - 1; o >= 0; o -- ) {
if ( objlist[ o ].object === object ) {
objlist.splice( o, 1 );
}
}
};
function removeInstancesDirect ( objlist, object ) {
for ( var o = objlist.length - 1; o >= 0; o -- ) {
if ( objlist[ o ] === object ) {
objlist.splice( o, 1 );
}
}
};
// Materials
this.initMaterial = function ( material, lights, fog, object ) {
var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
if ( material instanceof THREE.MeshDepthMaterial ) {
shaderID = 'depth';
} else if ( material instanceof THREE.MeshNormalMaterial ) {
shaderID = 'normal';
} else if ( material instanceof THREE.MeshBasicMaterial ) {
shaderID = 'basic';
} else if ( material instanceof THREE.MeshLambertMaterial ) {
shaderID = 'lambert';
} else if ( material instanceof THREE.MeshPhongMaterial ) {
shaderID = 'phong';
} else if ( material instanceof THREE.LineBasicMaterial ) {
shaderID = 'basic';
} else if ( material instanceof THREE.ParticleBasicMaterial ) {
shaderID = 'particle_basic';
}
if ( shaderID ) {
setMaterialShaders( material, THREE.ShaderLib[ shaderID ] );
}
// heuristics to create shader parameters according to lights in the scene
// (not to blow over maxLights budget)
maxLightCount = allocateLights( lights );
maxShadows = allocateShadows( lights );
maxBones = allocateBones( object );
parameters = {
map: !!material.map, envMap: !!material.envMap, lightMap: !!material.lightMap,
vertexColors: material.vertexColors,
fog: fog, useFog: material.fog,
sizeAttenuation: material.sizeAttenuation,
skinning: material.skinning,
morphTargets: material.morphTargets,
maxMorphTargets: this.maxMorphTargets,
maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point,
maxBones: maxBones,
shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow,
shadowMapSoft: this.shadowMapSoft,
shadowMapWidth: this.shadowMapWidth,
shadowMapHeight: this.shadowMapHeight,
maxShadows: maxShadows,
alphaTest: material.alphaTest,
metal: material.metal,
perPixel: material.perPixel
};
material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, parameters );
var attributes = material.program.attributes;
if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position );
if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color );
if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal );
if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent );
if ( material.skinning &&
attributes.skinVertexA >=0 && attributes.skinVertexB >= 0 &&
attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
_gl.enableVertexAttribArray( attributes.skinVertexA );
_gl.enableVertexAttribArray( attributes.skinVertexB );
_gl.enableVertexAttribArray( attributes.skinIndex );
_gl.enableVertexAttribArray( attributes.skinWeight );
}
if ( material.attributes ) {
for ( a in material.attributes ) {
if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] );
}
}
if ( material.morphTargets ) {
material.numSupportedMorphTargets = 0;
var id, base = "morphTarget";
for ( i = 0; i < this.maxMorphTargets; i ++ ) {
id = base + i;
if ( attributes[ id ] >= 0 ) {
_gl.enableVertexAttribArray( attributes[ id ] );
material.numSupportedMorphTargets ++;
}
}
}
material.uniformsList = [];
for ( u in material.uniforms ) {
material.uniformsList.push( [ material.uniforms[ u ], u ] );
}
};
function setMaterialShaders ( material, shaders ) {
material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
material.vertexShader = shaders.vertexShader;
material.fragmentShader = shaders.fragmentShader;
};
function setProgram ( camera, lights, fog, material, object ) {
if ( ! material.program ) {
_this.initMaterial( material, lights, fog, object );
}
if ( material.morphTargets ) {
if ( ! object.__webglMorphTargetInfluences ) {
object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
for ( var i = 0, il = _this.maxMorphTargets; i < il; i ++ ) {
object.__webglMorphTargetInfluences[ i ] = 0;
}
}
}
var refreshMaterial = false;
var program = material.program,
p_uniforms = program.uniforms,
m_uniforms = material.uniforms;
if ( program !== _currentProgram ) {
_gl.useProgram( program );
_currentProgram = program;
refreshMaterial = true;
}
if ( material.id !== _currentMaterialId ) {
_currentMaterialId = material.id;
refreshMaterial = true;
}
if ( refreshMaterial ) {
_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, _projectionMatrixArray );
// refresh uniforms common to several materials
if ( fog && material.fog ) {
refreshUniformsFog( m_uniforms, fog );
}
if ( material instanceof THREE.MeshPhongMaterial ||
material instanceof THREE.MeshLambertMaterial ||
material.lights ) {
setupLights( program, lights );
refreshUniformsLights( m_uniforms, _lights );
}
if ( material instanceof THREE.MeshBasicMaterial ||
material instanceof THREE.MeshLambertMaterial ||
material instanceof THREE.MeshPhongMaterial ) {
refreshUniformsCommon( m_uniforms, material );
}
// refresh single material specific uniforms
if ( material instanceof THREE.LineBasicMaterial ) {
refreshUniformsLine( m_uniforms, material );
} else if ( material instanceof THREE.ParticleBasicMaterial ) {
refreshUniformsParticle( m_uniforms, material );
} else if ( material instanceof THREE.MeshPhongMaterial ) {
refreshUniformsPhong( m_uniforms, material );
} else if ( material instanceof THREE.MeshLambertMaterial ) {
refreshUniformsLambert( m_uniforms, material );
} else if ( material instanceof THREE.MeshDepthMaterial ) {
m_uniforms.mNear.value = camera.near;
m_uniforms.mFar.value = camera.far;
m_uniforms.opacity.value = material.opacity;
} else if ( material instanceof THREE.MeshNormalMaterial ) {
m_uniforms.opacity.value = material.opacity;
}
if ( object.receiveShadow && ! material._shadowPass ) {
refreshUniformsShadow( m_uniforms, material );
}
// load common uniforms
loadUniformsGeneric( program, material.uniformsList );
// load material specific uniforms
// (shader material also gets them for the sake of genericity)
if ( material instanceof THREE.ShaderMaterial ||
material instanceof THREE.MeshPhongMaterial ||
material.envMap ) {
if( p_uniforms.cameraPosition !== null ) {
_gl.uniform3f( p_uniforms.cameraPosition, camera.position.x, camera.position.y, camera.position.z );
}
}
if ( material instanceof THREE.MeshPhongMaterial ||
material instanceof THREE.MeshLambertMaterial ||
material instanceof THREE.ShaderMaterial ||
material.skinning ) {
if( p_uniforms.viewMatrix !== null ) {
_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, _viewMatrixArray );
}
}
if ( material.skinning ) {
loadUniformsSkinning( p_uniforms, object );
}
}
loadUniformsMatrices( p_uniforms, object );
if ( material instanceof THREE.ShaderMaterial ||
material.envMap ||
material.skinning ||
object.receiveShadow ) {
if ( p_uniforms.objectMatrix !== null ) {
_gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray );
}
}
return program;
};
// Uniforms (refresh uniforms objects)
function refreshUniformsCommon ( uniforms, material ) {
uniforms.opacity.value = material.opacity;
if ( _this.gammaInput ) {
uniforms.diffuse.value.copyGammaToLinear( material.color );
} else {
uniforms.diffuse.value = material.color;
}
uniforms.map.texture = material.map;
if ( material.map ) {
uniforms.offsetRepeat.value.set( material.map.offset.x, material.map.offset.y, material.map.repeat.x, material.map.repeat.y );
}
uniforms.lightMap.texture = material.lightMap;
uniforms.envMap.texture = material.envMap;
uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
if ( _this.gammaInput ) {
//uniforms.reflectivity.value = material.reflectivity * material.reflectivity;
uniforms.reflectivity.value = material.reflectivity;
} else {
uniforms.reflectivity.value = material.reflectivity;
}
uniforms.refractionRatio.value = material.refractionRatio;
uniforms.combine.value = material.combine;
uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
};
function refreshUniformsLine ( uniforms, material ) {
uniforms.diffuse.value = material.color;
uniforms.opacity.value = material.opacity;
};
function refreshUniformsParticle ( uniforms, material ) {
uniforms.psColor.value = material.color;
uniforms.opacity.value = material.opacity;
uniforms.size.value = material.size;
uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
uniforms.map.texture = material.map;
};
function refreshUniformsFog ( uniforms, fog ) {
uniforms.fogColor.value = fog.color;
if ( fog instanceof THREE.Fog ) {
uniforms.fogNear.value = fog.near;
uniforms.fogFar.value = fog.far;
} else if ( fog instanceof THREE.FogExp2 ) {
uniforms.fogDensity.value = fog.density;
}
};
function refreshUniformsPhong ( uniforms, material ) {
uniforms.shininess.value = material.shininess;
if ( _this.gammaInput ) {
uniforms.ambient.value.copyGammaToLinear( material.ambient );
uniforms.specular.value.copyGammaToLinear( material.specular );
} else {
uniforms.ambient.value = material.ambient;
uniforms.specular.value = material.specular;
}
};
function refreshUniformsLambert ( uniforms, material ) {
if ( _this.gammaInput ) {
uniforms.ambient.value.copyGammaToLinear( material.ambient );
} else {
uniforms.ambient.value = material.ambient;
}
};
function refreshUniformsLights ( uniforms, lights ) {
uniforms.ambientLightColor.value = lights.ambient;
uniforms.directionalLightColor.value = lights.directional.colors;
uniforms.directionalLightDirection.value = lights.directional.positions;
uniforms.pointLightColor.value = lights.point.colors;
uniforms.pointLightPosition.value = lights.point.positions;
uniforms.pointLightDistance.value = lights.point.distances;
};
function refreshUniformsShadow ( uniforms, material ) {
if ( uniforms.shadowMatrix ) {
for ( var i = 0; i < _shadowMatrix.length; i ++ ) {
uniforms.shadowMatrix.value[ i ] = _shadowMatrix[ i ];
uniforms.shadowMap.texture[ i ] = _this.shadowMap[ i ];
}
uniforms.shadowDarkness.value = _this.shadowMapDarkness;
uniforms.shadowBias.value = _this.shadowMapBias;
}
};
// Uniforms (load to GPU)
function loadUniformsSkinning ( uniforms, object ) {
_gl.uniformMatrix4fv( uniforms.cameraInverseMatrix, false, _viewMatrixArray );
_gl.uniformMatrix4fv( uniforms.boneGlobalMatrices, false, object.boneMatrices );
};
function loadUniformsMatrices ( uniforms, object ) {
_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray );
if ( uniforms.normalMatrix ) {
_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrixArray );
}
};
function loadUniformsGeneric ( program, uniforms ) {
var uniform, value, type, location, texture, i, il, j, jl, offset;
for( j = 0, jl = uniforms.length; j < jl; j ++ ) {
location = program.uniforms[ uniforms[ j ][ 1 ] ];
if ( !location ) continue;
uniform = uniforms[ j ][ 0 ];
type = uniform.type;
value = uniform.value;
// single integer
if( type === "i" ) {
_gl.uniform1i( location, value );
// single float
} else if( type === "f" ) {
_gl.uniform1f( location, value );
// single THREE.Vector2
} else if( type === "v2" ) {
_gl.uniform2f( location, value.x, value.y );
// single THREE.Vector3
} else if( type === "v3" ) {
_gl.uniform3f( location, value.x, value.y, value.z );
// single THREE.Vector4
} else if( type === "v4" ) {
_gl.uniform4f( location, value.x, value.y, value.z, value.w );
// single THREE.Color
} else if( type === "c" ) {
_gl.uniform3f( location, value.r, value.g, value.b );
// flat array of floats (JS or typed array)
} else if( type === "fv1" ) {
_gl.uniform1fv( location, value );
// flat array of floats with 3 x N size (JS or typed array)
} else if( type === "fv" ) {
_gl.uniform3fv( location, value );
// array of THREE.Vector3
} else if( type === "v3v" ) {
if ( ! uniform._array ) {
uniform._array = new Float32Array( 3 * value.length );
}
for ( i = 0, il = value.length; i < il; i ++ ) {
offset = i * 3;
uniform._array[ offset ] = value[ i ].x;
uniform._array[ offset + 1 ] = value[ i ].y;
uniform._array[ offset + 2 ] = value[ i ].z;
}
_gl.uniform3fv( location, uniform._array );
// single THREE.Matrix4
} else if( type === "m4" ) {
if ( ! uniform._array ) {
uniform._array = new Float32Array( 16 );
}
value.flattenToArray( uniform._array );
_gl.uniformMatrix4fv( location, false, uniform._array );
// array of THREE.Matrix4
} else if( type === "m4v" ) {
if ( ! uniform._array ) {
uniform._array = new Float32Array( 16 * value.length );
}
for ( i = 0, il = value.length; i < il; i ++ ) {
value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
}
_gl.uniformMatrix4fv( location, false, uniform._array );
// single THREE.Texture (2d or cube)
} else if( type === "t" ) {
_gl.uniform1i( location, value );
texture = uniform.texture;
if ( !texture ) continue;
if ( texture.image instanceof Array && texture.image.length === 6 ) {
setCubeTexture( texture, value );
} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
setCubeTextureDynamic( texture, value );
} else {
setTexture( texture, value );
}
// array of THREE.Texture (2d)
} else if( type === "tv" ) {
if ( ! uniform._array ) {
uniform._array = [];
for( i = 0, il = uniform.texture.length; i < il; i ++ ) {
uniform._array[ i ] = value + i;
}
}
_gl.uniform1iv( location, uniform._array );
for( i = 0, il = uniform.texture.length; i < il; i ++ ) {
texture = uniform.texture[ i ];
if ( !texture ) continue;
setTexture( texture, uniform._array[ i ] );
}
}
}
};
function setupMatrices ( object, camera, computeNormalMatrix ) {
object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray );
if ( computeNormalMatrix ) {
THREE.Matrix4.makeInvert3x3( object._modelViewMatrix ).transposeIntoArray( object._normalMatrixArray );
}
};
function setupLights ( program, lights ) {
var l, ll, light, n,
r = 0, g = 0, b = 0,
color, position, intensity, distance,
zlights = _lights,
dcolors = zlights.directional.colors,
dpositions = zlights.directional.positions,
pcolors = zlights.point.colors,
ppositions = zlights.point.positions,
pdistances = zlights.point.distances,
dlength = 0,
plength = 0,
doffset = 0,
poffset = 0;
for ( l = 0, ll = lights.length; l < ll; l ++ ) {
light = lights[ l ];
color = light.color;
position = light.position;
intensity = light.intensity;
distance = light.distance;
if ( light instanceof THREE.AmbientLight ) {
if ( _this.gammaInput ) {
r += color.r * color.r;
g += color.g * color.g;
b += color.b * color.b;
} else {
r += color.r;
g += color.g;
b += color.b;
}
} else if ( light instanceof THREE.DirectionalLight ) {
doffset = dlength * 3;
if ( _this.gammaInput ) {
dcolors[ doffset ] = color.r * color.r * intensity * intensity;
dcolors[ doffset + 1 ] = color.g * color.g * intensity * intensity;
dcolors[ doffset + 2 ] = color.b * color.b * intensity * intensity;
} else {
dcolors[ doffset ] = color.r * intensity;
dcolors[ doffset + 1 ] = color.g * intensity;
dcolors[ doffset + 2 ] = color.b * intensity;
}
dpositions[ doffset ] = position.x;
dpositions[ doffset + 1 ] = position.y;
dpositions[ doffset + 2 ] = position.z;
dlength += 1;
} else if ( light instanceof THREE.SpotLight ) { // hack, not a proper spotlight
doffset = dlength * 3;
if ( _this.gammaInput ) {
dcolors[ doffset ] = color.r * color.r * intensity * intensity;
dcolors[ doffset + 1 ] = color.g * color.g * intensity * intensity;
dcolors[ doffset + 2 ] = color.b * color.b * intensity * intensity;
} else {
dcolors[ doffset ] = color.r * intensity;
dcolors[ doffset + 1 ] = color.g * intensity;
dcolors[ doffset + 2 ] = color.b * intensity;
}
n = 1 / position.length();
dpositions[ doffset ] = position.x * n;
dpositions[ doffset + 1 ] = position.y * n;
dpositions[ doffset + 2 ] = position.z * n;
dlength += 1;
} else if( light instanceof THREE.PointLight ) {
poffset = plength * 3;
if ( _this.gammaInput ) {
pcolors[ poffset ] = color.r * color.r * intensity * intensity;
pcolors[ poffset + 1 ] = color.g * color.g * intensity * intensity;
pcolors[ poffset + 2 ] = color.b * color.b * intensity * intensity;
} else {
pcolors[ poffset ] = color.r * intensity;
pcolors[ poffset + 1 ] = color.g * intensity;
pcolors[ poffset + 2 ] = color.b * intensity;
}
ppositions[ poffset ] = position.x;
ppositions[ poffset + 1 ] = position.y;
ppositions[ poffset + 2 ] = position.z;
pdistances[ plength ] = distance;
plength += 1;
}
}
// null eventual remains from removed lights
// (this is to avoid if in shader)
for ( l = dlength * 3, ll = dcolors.length; l < ll; l ++ ) dcolors[ l ] = 0.0;
for ( l = plength * 3, ll = pcolors.length; l < ll; l ++ ) pcolors[ l ] = 0.0;
zlights.point.length = plength;
zlights.directional.length = dlength;
zlights.ambient[ 0 ] = r;
zlights.ambient[ 1 ] = g;
zlights.ambient[ 2 ] = b;
};
// GL state setting
this.setFaceCulling = function ( cullFace, frontFace ) {
if ( cullFace ) {
if ( !frontFace || frontFace === "ccw" ) {
_gl.frontFace( _gl.CCW );
} else {
_gl.frontFace( _gl.CW );
}
if( cullFace === "back" ) {
_gl.cullFace( _gl.BACK );
} else if( cullFace === "front" ) {
_gl.cullFace( _gl.FRONT );
} else {
_gl.cullFace( _gl.FRONT_AND_BACK );
}
_gl.enable( _gl.CULL_FACE );
} else {
_gl.disable( _gl.CULL_FACE );
}
};
function setLineWidth ( width ) {
if ( width !== _oldLineWidth ) {
_gl.lineWidth( width );
_oldLineWidth = width;
}
};
function setObjectFaces ( object ) {
if ( _oldDoubleSided !== object.doubleSided ) {
if( object.doubleSided ) {
_gl.disable( _gl.CULL_FACE );
} else {
_gl.enable( _gl.CULL_FACE );
}
_oldDoubleSided = object.doubleSided;
}
if ( _oldFlipSided !== object.flipSided ) {
if( object.flipSided ) {
_gl.frontFace( _gl.CW );
} else {
_gl.frontFace( _gl.CCW );
}
_oldFlipSided = object.flipSided;
}
};
function setDepthTest ( depthTest ) {
if ( _oldDepthTest !== depthTest ) {
if( depthTest ) {
_gl.enable( _gl.DEPTH_TEST );
} else {
_gl.disable( _gl.DEPTH_TEST );
}
_oldDepthTest = depthTest;
}
};
function setDepthWrite ( depthWrite ) {
if ( _oldDepthWrite !== depthWrite ) {
_gl.depthMask( depthWrite );
_oldDepthWrite = depthWrite;
}
};
function setPolygonOffset ( polygonoffset, factor, units ) {
if ( _oldPolygonOffset !== polygonoffset ) {
if ( polygonoffset ) {
_gl.enable( _gl.POLYGON_OFFSET_FILL );
} else {
_gl.disable( _gl.POLYGON_OFFSET_FILL );
}
_oldPolygonOffset = polygonoffset;
}
if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) {
_gl.polygonOffset( factor, units );
_oldPolygonOffsetFactor = factor;
_oldPolygonOffsetUnits = units;
}
};
function setBlending ( blending ) {
if ( blending !== _oldBlending ) {
switch ( blending ) {
case THREE.AdditiveBlending:
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
break;
case THREE.SubtractiveBlending:
// TODO: Find blendFuncSeparate() combination
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
break;
case THREE.MultiplyBlending:
// TODO: Find blendFuncSeparate() combination
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
break;
default:
_gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD );
_gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
break;
}
_oldBlending = blending;
}
};
// Shaders
function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, parameters ) {
var p, pl, program, code;
var chunks = [];
// Generate code
if ( shaderID ) {
chunks.push( shaderID );
} else {
chunks.push( fragmentShader );
chunks.push( vertexShader );
}
for ( p in parameters ) {
chunks.push( p );
chunks.push( parameters[ p ] );
}
code = chunks.join();
// Check if code has been already compiled
for ( p = 0, pl = _programs.length; p < pl; p ++ ) {
if ( _programs[ p ].code === code ) {
// console.log( "Code already compiled." /*: \n\n" + code*/ );
return _programs[ p ].program;
}
}
//console.log( "building new program " );
//
program = _gl.createProgram();
var prefix_vertex = [
_supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
_this.gammaInput ? "#define GAMMA_INPUT" : "",
_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
"#define MAX_SHADOWS " + parameters.maxShadows,
"#define MAX_BONES " + parameters.maxBones,
parameters.map ? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.vertexColors ? "#define USE_COLOR" : "",
parameters.skinning ? "#define USE_SKINNING" : "",
parameters.morphTargets ? "#define USE_MORPHTARGETS" : "",
parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "",
parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
"uniform mat4 objectMatrix;",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"uniform mat4 viewMatrix;",
"uniform mat3 normalMatrix;",
"uniform vec3 cameraPosition;",
"uniform mat4 cameraInverseMatrix;",
"attribute vec3 position;",
"attribute vec3 normal;",
"attribute vec2 uv;",
"attribute vec2 uv2;",
"#ifdef USE_COLOR",
"attribute vec3 color;",
"#endif",
"#ifdef USE_MORPHTARGETS",
"attribute vec3 morphTarget0;",
"attribute vec3 morphTarget1;",
"attribute vec3 morphTarget2;",
"attribute vec3 morphTarget3;",
"attribute vec3 morphTarget4;",
"attribute vec3 morphTarget5;",
"attribute vec3 morphTarget6;",
"attribute vec3 morphTarget7;",
"#endif",
"#ifdef USE_SKINNING",
"attribute vec4 skinVertexA;",
"attribute vec4 skinVertexB;",
"attribute vec4 skinIndex;",
"attribute vec4 skinWeight;",
"#endif",
""
].join("\n");
var prefix_fragment = [
"#ifdef GL_ES",
"precision " + _precision + " float;",
"#endif",
"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
"#define MAX_SHADOWS " + parameters.maxShadows,
parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
_this.gammaInput ? "#define GAMMA_INPUT" : "",
_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "",
( parameters.useFog && parameters.fog instanceof THREE.FogExp2 ) ? "#define FOG_EXP2" : "",
parameters.map ? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.vertexColors ? "#define USE_COLOR" : "",
parameters.metal ? "#define METAL" : "",
parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "",
parameters.shadowMapSoft ? "#define SHADOWMAP_WIDTH " + parameters.shadowMapWidth.toFixed( 1 ) : "",
parameters.shadowMapSoft ? "#define SHADOWMAP_HEIGHT " + parameters.shadowMapHeight.toFixed( 1 ) : "",
"uniform mat4 viewMatrix;",
"uniform vec3 cameraPosition;",
""
].join("\n");
_gl.attachShader( program, getShader( "fragment", prefix_fragment + fragmentShader ) );
_gl.attachShader( program, getShader( "vertex", prefix_vertex + vertexShader ) );
_gl.linkProgram( program );
if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
}
//console.log( prefix_fragment + fragmentShader );
//console.log( prefix_vertex + vertexShader );
program.uniforms = {};
program.attributes = {};
var identifiers, u, a, i;
// cache uniform locations
identifiers = [
'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'objectMatrix', 'cameraPosition',
'cameraInverseMatrix', 'boneGlobalMatrices', 'morphTargetInfluences'
];
for ( u in uniforms ) {
identifiers.push( u );
}
cacheUniformLocations( program, identifiers );
// cache attributes locations
identifiers = [
"position", "normal", "uv", "uv2", "tangent", "color",
"skinVertexA", "skinVertexB", "skinIndex", "skinWeight"
];
for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
identifiers.push( "morphTarget" + i );
}
for ( a in attributes ) {
identifiers.push( a );
}
cacheAttributeLocations( program, identifiers );
program.id = _programs.length;
_programs.push( { program: program, code: code } );
_this.info.memory.programs = _programs.length;
return program;
};
// Shader parameters cache
function cacheUniformLocations ( program, identifiers ) {
var i, l, id;
for( i = 0, l = identifiers.length; i < l; i++ ) {
id = identifiers[ i ];
program.uniforms[ id ] = _gl.getUniformLocation( program, id );
}
};
function cacheAttributeLocations ( program, identifiers ) {
var i, l, id;
for( i = 0, l = identifiers.length; i < l; i++ ) {
id = identifiers[ i ];
program.attributes[ id ] = _gl.getAttribLocation( program, id );
}
};
function getShader ( type, string ) {
var shader;
if ( type === "fragment" ) {
shader = _gl.createShader( _gl.FRAGMENT_SHADER );
} else if ( type === "vertex" ) {
shader = _gl.createShader( _gl.VERTEX_SHADER );
}
_gl.shaderSource( shader, string );
_gl.compileShader( shader );
if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
console.error( _gl.getShaderInfoLog( shader ) );
console.error( string );
return null;
}
return shader;
};
// Textures
function setTextureParameters ( textureType, texture, image ) {
if ( isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ) ) {
_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
return true;
} else {
_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
return false;
}
};
function setTexture ( texture, slot ) {
if ( texture.needsUpdate ) {
if ( ! texture.__webglInit ) {
texture.__webglInit = true;
texture.__webglTexture = _gl.createTexture();
_this.info.memory.textures ++;
}
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
var needsMipMaps = setTextureParameters( _gl.TEXTURE_2D, texture, texture.image );
if ( texture instanceof THREE.DataTexture ) {
_gl.texImage2D( _gl.TEXTURE_2D, 0, paramThreeToGL( texture.format ), texture.image.width, texture.image.height, 0, paramThreeToGL( texture.format ), _gl.UNSIGNED_BYTE, texture.image.data );
} else {
_gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image );
}
if ( needsMipMaps ) {
_gl.generateMipmap( _gl.TEXTURE_2D );
}
texture.needsUpdate = false;
if ( texture.onUpdated ) texture.onUpdated();
} else {
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
}
};
function setCubeTexture ( texture, slot ) {
if ( texture.image.length === 6 ) {
if ( texture.needsUpdate ) {
if ( ! texture.image.__webglTextureCube ) {
texture.image.__webglTextureCube = _gl.createTexture();
}
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
var needsMipMaps = setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, texture.image[ 0 ] );
for ( var i = 0; i < 6; i ++ ) {
_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image[ i ] );
}
if ( needsMipMaps ) {
_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
}
texture.needsUpdate = false;
} else {
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
}
}
};
function setCubeTextureDynamic ( texture, slot ) {
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture );
};
// Render targets
function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 );
};
function setupRenderBuffer ( renderbuffer, renderTarget ) {
_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
/* For some reason this is not working. Defaulting to RGBA4.
} else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
*/
} else if( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
} else {
_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
}
};
function setRenderTarget ( renderTarget ) {
var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
if ( renderTarget && ! renderTarget.__webglFramebuffer ) {
if( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
if( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
renderTarget.__webglTexture = _gl.createTexture();
// Setup texture, create render and frame buffers
if ( isCube ) {
renderTarget.__webglFramebuffer = [];
renderTarget.__webglRenderbuffer = [];
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, renderTarget );
for ( var i = 0; i < 6; i ++ ) {
renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer();
renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, paramThreeToGL( renderTarget.format ), renderTarget.width, renderTarget.height, 0, paramThreeToGL( renderTarget.format ), paramThreeToGL( renderTarget.type ), null );
setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget );
}
} else {
renderTarget.__webglFramebuffer = _gl.createFramebuffer();
renderTarget.__webglRenderbuffer = _gl.createRenderbuffer();
_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
setTextureParameters( _gl.TEXTURE_2D, renderTarget, renderTarget );
_gl.texImage2D( _gl.TEXTURE_2D, 0, paramThreeToGL( renderTarget.format ), renderTarget.width, renderTarget.height, 0, paramThreeToGL( renderTarget.format ), paramThreeToGL( renderTarget.type ), null );
setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget );
}
// Release everything
if ( isCube ) {
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
} else {
_gl.bindTexture( _gl.TEXTURE_2D, null );
}
_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
_gl.bindFramebuffer( _gl.FRAMEBUFFER, null);
}
var framebuffer, width, height, vx, vy;
if ( renderTarget ) {
if ( isCube ) {
framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ];
} else {
framebuffer = renderTarget.__webglFramebuffer;
}
width = renderTarget.width;
height = renderTarget.height;
vx = 0;
vy = 0;
} else {
framebuffer = null;
width = _viewportWidth;
height = _viewportHeight;
vx = _viewportX;
vy = _viewportY;
}
if ( framebuffer !== _currentFramebuffer ) {
_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
_gl.viewport( vx, vy, width, height );
_currentFramebuffer = framebuffer;
}
};
function updateRenderTargetMipmap ( renderTarget ) {
if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
} else {
_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
_gl.generateMipmap( _gl.TEXTURE_2D );
_gl.bindTexture( _gl.TEXTURE_2D, null );
}
};
// fallback filters for non-power-of-2 textures
function filterFallback ( f ) {
switch ( f ) {
case THREE.NearestFilter:
case THREE.NearestMipMapNearestFilter:
case THREE.NearestMipMapLinearFilter: return _gl.NEAREST; break;
case THREE.LinearFilter:
case THREE.LinearMipMapNearestFilter:
case THREE.LinearMipMapLinearFilter:
default:
return _gl.LINEAR; break;
}
};
function paramThreeToGL ( p ) {
switch ( p ) {
case THREE.RepeatWrapping: return _gl.REPEAT; break;
case THREE.ClampToEdgeWrapping: return _gl.CLAMP_TO_EDGE; break;
case THREE.MirroredRepeatWrapping: return _gl.MIRRORED_REPEAT; break;
case THREE.NearestFilter: return _gl.NEAREST; break;
case THREE.NearestMipMapNearestFilter: return _gl.NEAREST_MIPMAP_NEAREST; break;
case THREE.NearestMipMapLinearFilter: return _gl.NEAREST_MIPMAP_LINEAR; break;
case THREE.LinearFilter: return _gl.LINEAR; break;
case THREE.LinearMipMapNearestFilter: return _gl.LINEAR_MIPMAP_NEAREST; break;
case THREE.LinearMipMapLinearFilter: return _gl.LINEAR_MIPMAP_LINEAR; break;
case THREE.ByteType: return _gl.BYTE; break;
case THREE.UnsignedByteType: return _gl.UNSIGNED_BYTE; break;
case THREE.ShortType: return _gl.SHORT; break;
case THREE.UnsignedShortType: return _gl.UNSIGNED_SHORT; break;
case THREE.IntType: return _gl.INT; break;
case THREE.UnsignedShortType: return _gl.UNSIGNED_INT; break;
case THREE.FloatType: return _gl.FLOAT; break;
case THREE.AlphaFormat: return _gl.ALPHA; break;
case THREE.RGBFormat: return _gl.RGB; break;
case THREE.RGBAFormat: return _gl.RGBA; break;
case THREE.LuminanceFormat: return _gl.LUMINANCE; break;
case THREE.LuminanceAlphaFormat: return _gl.LUMINANCE_ALPHA; break;
}
return 0;
};
function isPowerOfTwo ( value ) {
return ( value & ( value - 1 ) ) === 0;
};
// Allocations
function allocateBones ( object ) {
// default for when object is not specified
// ( for example when prebuilding shader
// to be used with multiple objects )
//
// - leave some extra space for other uniforms
// - limit here is ANGLE's 254 max uniform vectors
// (up to 54 should be safe)
var maxBones = 50;
if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
maxBones = object.bones.length;
}
return maxBones;
};
function allocateLights ( lights ) {
var l, ll, light, dirLights, pointLights, maxDirLights, maxPointLights;
dirLights = pointLights = maxDirLights = maxPointLights = 0;
for ( l = 0, ll = lights.length; l < ll; l++ ) {
light = lights[ l ];
if ( light instanceof THREE.SpotLight ) dirLights ++; // hack, not a proper spotlight
if ( light instanceof THREE.DirectionalLight ) dirLights ++;
if ( light instanceof THREE.PointLight ) pointLights ++;
}
if ( ( pointLights + dirLights ) <= _maxLights ) {
maxDirLights = dirLights;
maxPointLights = pointLights;
} else {
maxDirLights = Math.ceil( _maxLights * dirLights / ( pointLights + dirLights ) );
maxPointLights = _maxLights - maxDirLights;
}
return { 'directional' : maxDirLights, 'point' : maxPointLights };
};
function allocateShadows ( lights ) {
var l, ll, light, maxShadows = 0;
for ( l = 0, ll = lights.length; l < ll; l++ ) {
light = lights[ l ];
if ( light instanceof THREE.SpotLight && light.castShadow ) maxShadows ++;
}
return maxShadows;
};
function maxVertexTextures () {
return _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
};
// Initialization
function initSprites () {
_sprite.vertices = new Float32Array( 8 + 8 );
_sprite.faces = new Uint16Array( 6 );
var i = 0;
_sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0
_sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 0
_sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1
_sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 1
_sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2
_sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 2
_sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3
_sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 3
i = 0;
_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2;
_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3;
_sprite.vertexBuffer = _gl.createBuffer();
_sprite.elementBuffer = _gl.createBuffer();
_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW );
_sprite.program = _gl.createProgram();
_gl.attachShader( _sprite.program, getShader( "fragment", THREE.ShaderLib.sprite.fragmentShader ) );
_gl.attachShader( _sprite.program, getShader( "vertex", THREE.ShaderLib.sprite.vertexShader ) );
_gl.linkProgram( _sprite.program );
_sprite.attributes = {};
_sprite.uniforms = {};
_sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" );
_sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" );
_sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" );
_sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" );
_sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" );
_sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" );
_sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" );
_sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" );
_sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" );
_sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" );
_sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" );
_sprite.uniforms.affectedByDistance = _gl.getUniformLocation( _sprite.program, "affectedByDistance" );
_sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" );
_sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" );
_sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" );
//_gl.enableVertexAttribArray( _sprite.attributes.position );
//_gl.enableVertexAttribArray( _sprite.attributes.uv );
};
function initShadowmaps () {
var depthShader = THREE.ShaderLib[ "depthRGBA" ];
var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
_depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
_depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
_depthMaterial._shadowPass = true;
_depthMaterialMorph._shadowPass = true;
}
function initGL () {
var gl;
try {
if ( ! ( gl = _canvas.getContext( 'experimental-webgl', { antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) {
throw 'Error creating WebGL context.';
}
console.log(
navigator.userAgent + " | " +
gl.getParameter( gl.VERSION ) + " | " +
gl.getParameter( gl.VENDOR ) + " | " +
gl.getParameter( gl.RENDERER ) + " | " +
gl.getParameter( gl.SHADING_LANGUAGE_VERSION )
);
} catch ( error ) {
console.error( error );
}
return gl;
};
function setDefaultGLState () {
_gl.clearColor( 0, 0, 0, 1 );
_gl.clearDepth( 1 );
_gl.clearStencil( 0 );
_gl.enable( _gl.DEPTH_TEST );
_gl.depthFunc( _gl.LEQUAL );
_gl.frontFace( _gl.CCW );
_gl.cullFace( _gl.BACK );
_gl.enable( _gl.CULL_FACE );
_gl.enable( _gl.BLEND );
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
};