Skip to content

Instantly share code, notes, and snippets.

@gidili
Created July 28, 2015 10:14
Show Gist options
  • Save gidili/61515feabf9dbb2d5dc3 to your computer and use it in GitHub Desktop.
Save gidili/61515feabf9dbb2d5dc3 to your computer and use it in GitHub Desktop.
define(function(require) {
THREE.WebGLRenderer = function ( parameters ) {
console.log('THREE.WormWebGLRenderer');
parameters = parameters || {};
var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
_context = parameters.context !== undefined ? parameters.context : null,
_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
_alpha = parameters.alpha !== undefined ? parameters.alpha : false,
_depth = parameters.depth !== undefined ? parameters.depth : true,
_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
_logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false,
_clearColor = new THREE.Color( 0x000000 ),
_clearAlpha = 0;
var lights = [];
var _webglObjects = {};
var _webglObjectsImmediate = [];
var opaqueObjects = [];
var transparentObjects = [];
var sprites = [];
var lensFlares = [];
// public properties
this.domElement = _canvas;
this.context = null;
this.devicePixelRatio = parameters.devicePixelRatio !== undefined
? parameters.devicePixelRatio
: self.devicePixelRatio !== undefined
? self.devicePixelRatio
: 1;
// clearing
this.autoClear = true;
this.autoClearColor = true;
this.autoClearDepth = true;
this.autoClearStencil = true;
// scene graph
this.sortObjects = true;
// physically based shading
this.gammaInput = false;
this.gammaOutput = false;
// shadow map
this.shadowMapEnabled = false;
this.shadowMapType = THREE.PCFShadowMap;
this.shadowMapCullFace = THREE.CullFaceFront;
this.shadowMapDebug = false;
this.shadowMapCascade = false;
// morphs
this.maxMorphTargets = 8;
this.maxMorphNormals = 4;
// flags
this.autoScaleCubemaps = true;
// info
this.info = {
memory: {
programs: 0,
geometries: 0,
textures: 0
},
render: {
calls: 0,
vertices: 0,
faces: 0,
points: 0
}
};
// internal properties
var _this = this,
_programs = [],
// internal state cache
_currentProgram = null,
_currentFramebuffer = null,
_currentMaterialId = - 1,
_currentGeometryGroupHash = - 1,
_currentCamera = null,
_usedTextureUnits = 0,
// GL state cache
_oldDoubleSided = - 1,
_oldFlipSided = - 1,
_oldBlending = - 1,
_oldBlendEquation = - 1,
_oldBlendSrc = - 1,
_oldBlendDst = - 1,
_oldDepthTest = - 1,
_oldDepthWrite = - 1,
_oldPolygonOffset = null,
_oldPolygonOffsetFactor = null,
_oldPolygonOffsetUnits = null,
_oldLineWidth = null,
_viewportX = 0,
_viewportY = 0,
_viewportWidth = _canvas.width,
_viewportHeight = _canvas.height,
_currentWidth = 0,
_currentHeight = 0,
_newAttributes = new Uint8Array( 16 ),
_enabledAttributes = new Uint8Array( 16 ),
// frustum
_frustum = new THREE.Frustum(),
// camera matrices cache
_projScreenMatrix = new THREE.Matrix4(),
_projScreenMatrixPS = new THREE.Matrix4(),
_vector3 = new THREE.Vector3(),
// light arrays cache
_direction = new THREE.Vector3(),
_lightsNeedUpdate = true,
_lights = {
ambient: [ 0, 0, 0 ],
directional: { length: 0, colors:[], positions: [] },
point: { length: 0, colors: [], positions: [], distances: [] },
spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] },
hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
};
var timestep;
var currentMatrix = new Array();
this.setTimestep = function setTimestep(ts) {
timestep = ts;
}
this.setCurrentMatrix = function setCurrentMatrix(m) {
currentMatrix = m;
}
// initialize
var _gl;
try {
var attributes = {
alpha: _alpha,
depth: _depth,
stencil: _stencil,
antialias: _antialias,
premultipliedAlpha: _premultipliedAlpha,
preserveDrawingBuffer: _preserveDrawingBuffer
};
_gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
if ( _gl === null ) {
if ( _canvas.getContext( 'webgl') !== null ) {
throw 'Error creating WebGL context with your selected attributes.';
} else {
throw 'Error creating WebGL context.';
}
}
} catch ( error ) {
console.error( error );
}
if ( _gl.getShaderPrecisionFormat === undefined ) {
_gl.getShaderPrecisionFormat = function () {
return {
'rangeMin': 1,
'rangeMax': 1,
'precision': 1
};
}
}
var extensions = new THREE.WebGLExtensions( _gl );
extensions.get( 'OES_texture_float' );
extensions.get( 'OES_texture_float_linear' );
extensions.get( 'OES_standard_derivatives' );
if ( _logarithmicDepthBuffer ) {
extensions.get( 'EXT_frag_depth' );
}
//
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.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
}
setDefaultGLState();
this.context = _gl;
// GPU capabilities
var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS );
var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE );
var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE );
var _supportsVertexTextures = _maxVertexTextures > 0;
var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' );
//
var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT );
var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT );
var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT );
var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT );
var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT );
var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT );
var getCompressedTextureFormats = ( function () {
var array;
return function () {
if ( array !== undefined ) {
return array;
}
array = [];
if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) {
var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS );
for ( var i = 0; i < formats.length; i ++ ){
array.push( formats[ i ] );
}
}
return array;
};
} )();
// clamp precision to maximum available
var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0;
var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0;
if ( _precision === 'highp' && ! highpAvailable ) {
if ( mediumpAvailable ) {
_precision = 'mediump';
console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' );
} else {
_precision = 'lowp';
console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' );
}
}
if ( _precision === 'mediump' && ! mediumpAvailable ) {
_precision = 'lowp';
console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' );
}
// Plugins
var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate );
var spritePlugin = new THREE.SpritePlugin( this, sprites );
var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );
// API
this.getContext = function () {
return _gl;
};
this.supportsVertexTextures = function () {
return _supportsVertexTextures;
};
this.supportsFloatTextures = function () {
return extensions.get( 'OES_texture_float' );
};
this.supportsStandardDerivatives = function () {
return extensions.get( 'OES_standard_derivatives' );
};
this.supportsCompressedTextureS3TC = function () {
return extensions.get( 'WEBGL_compressed_texture_s3tc' );
};
this.supportsCompressedTexturePVRTC = function () {
return extensions.get( 'WEBGL_compressed_texture_pvrtc' );
};
this.supportsBlendMinMax = function () {
return extensions.get( 'EXT_blend_minmax' );
};
this.getMaxAnisotropy = ( function () {
var value;
return function () {
if ( value !== undefined ) {
return value;
}
var extension = extensions.get( 'EXT_texture_filter_anisotropic' );
value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
return value;
}
} )();
this.getPrecision = function () {
return _precision;
};
this.setSize = function ( width, height, updateStyle ) {
_canvas.width = width * this.devicePixelRatio;
_canvas.height = height * this.devicePixelRatio;
if ( updateStyle !== false ) {
_canvas.style.width = width + 'px';
_canvas.style.height = height + 'px';
}
this.setViewport( 0, 0, width, height );
};
this.setViewport = function ( x, y, width, height ) {
_viewportX = x * this.devicePixelRatio;
_viewportY = y * this.devicePixelRatio;
_viewportWidth = width * this.devicePixelRatio;
_viewportHeight = height * this.devicePixelRatio;
_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
};
this.setScissor = function ( x, y, width, height ) {
_gl.scissor(
x * this.devicePixelRatio,
y * this.devicePixelRatio,
width * this.devicePixelRatio,
height * this.devicePixelRatio
);
};
this.enableScissorTest = function ( enable ) {
enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
};
// Clearing
this.setClearColor = function ( color, alpha ) {
_clearColor.set( color );
_clearAlpha = alpha !== undefined ? alpha : 1;
_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
};
this.setClearColorHex = function ( hex, alpha ) {
console.warn( 'THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' );
this.setClearColor( hex, alpha );
};
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.clearColor = function () {
_gl.clear( _gl.COLOR_BUFFER_BIT );
};
this.clearDepth = function () {
_gl.clear( _gl.DEPTH_BUFFER_BIT );
};
this.clearStencil = function () {
_gl.clear( _gl.STENCIL_BUFFER_BIT );
};
this.clearTarget = function ( renderTarget, color, depth, stencil ) {
this.setRenderTarget( renderTarget );
this.clear( color, depth, stencil );
};
// Reset
this.resetGLState = function () {
_currentProgram = null;
_currentCamera = null;
_oldBlending = - 1;
_oldDepthTest = - 1;
_oldDepthWrite = - 1;
_oldDoubleSided = - 1;
_oldFlipSided = - 1;
_currentGeometryGroupHash = - 1;
_currentMaterialId = - 1;
_lightsNeedUpdate = true;
};
// Buffer allocation
function createParticleBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
_this.info.memory.geometries ++;
};
function createLineBuffers ( geometry ) {
geometry.__webglVertexBuffer = _gl.createBuffer();
geometry.__webglColorBuffer = _gl.createBuffer();
geometry.__webglLineDistanceBuffer = _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.__webglSkinIndicesBuffer = _gl.createBuffer();
geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer();
geometryGroup.__webglFaceBuffer = _gl.createBuffer();
geometryGroup.__webglLineBuffer = _gl.createBuffer();
var m, ml;
if ( geometryGroup.numMorphTargets ) {
geometryGroup.__webglMorphTargetsBuffers = [];
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
}
}
if ( geometryGroup.numMorphNormals ) {
geometryGroup.__webglMorphNormalsBuffers = [];
for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
}
}
_this.info.memory.geometries ++;
};
// Events
var onObjectRemoved = function ( event ) {
var object = event.target;
object.traverse( function ( child ) {
child.removeEventListener( 'remove', onObjectRemoved );
removeObject( child );
} );
};
var onGeometryDispose = function ( event ) {
var geometry = event.target;
geometry.removeEventListener( 'dispose', onGeometryDispose );
deallocateGeometry( geometry );
};
var onTextureDispose = function ( event ) {
var texture = event.target;
texture.removeEventListener( 'dispose', onTextureDispose );
deallocateTexture( texture );
_this.info.memory.textures --;
};
var onRenderTargetDispose = function ( event ) {
var renderTarget = event.target;
renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
deallocateRenderTarget( renderTarget );
_this.info.memory.textures --;
};
var onMaterialDispose = function ( event ) {
var material = event.target;
material.removeEventListener( 'dispose', onMaterialDispose );
deallocateMaterial( material );
};
// Buffer deallocation
var deleteBuffers = function ( geometry ) {
var buffers = [
'__webglVertexBuffer',
'__webglNormalBuffer',
'__webglTangentBuffer',
'__webglColorBuffer',
'__webglUVBuffer',
'__webglUV2Buffer',
'__webglSkinIndicesBuffer',
'__webglSkinWeightsBuffer',
'__webglFaceBuffer',
'__webglLineBuffer',
'__webglLineDistanceBuffer'
];
for ( var i = 0, l = buffers.length; i < l; i ++ ) {
var name = buffers[ i ];
if ( geometry[ name ] !== undefined ) {
_gl.deleteBuffer( geometry[ name ] );
delete geometry[ name ];
}
}
// custom attributes
if ( geometry.__webglCustomAttributesList !== undefined ) {
for ( var name in geometry.__webglCustomAttributesList ) {
_gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer );
}
delete geometry.__webglCustomAttributesList;
}
_this.info.memory.geometries --;
};
var deallocateGeometry = function ( geometry ) {
delete geometry.__webglInit;
if ( geometry instanceof THREE.BufferGeometry ) {
for ( var name in geometry.attributes ) {
var attribute = geometry.attributes[ name ];
if ( attribute.buffer !== undefined ) {
_gl.deleteBuffer( attribute.buffer );
delete attribute.buffer;
}
}
_this.info.memory.geometries --;
} else {
var geometryGroupsList = geometryGroups[ geometry.id ];
if ( geometryGroupsList !== undefined ) {
for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) {
var geometryGroup = geometryGroupsList[ i ];
if ( geometryGroup.numMorphTargets !== undefined ) {
for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
}
delete geometryGroup.__webglMorphTargetsBuffers;
}
if ( geometryGroup.numMorphNormals !== undefined ) {
for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
}
delete geometryGroup.__webglMorphNormalsBuffers;
}
deleteBuffers( geometryGroup );
}
delete geometryGroups[ geometry.id ];
} else {
deleteBuffers( geometry );
}
}
// TOFIX: Workaround for deleted geometry being currently bound
_currentGeometryGroupHash = - 1;
};
var deallocateTexture = function ( texture ) {
if ( texture.image && texture.image.__webglTextureCube ) {
// cube texture
_gl.deleteTexture( texture.image.__webglTextureCube );
delete texture.image.__webglTextureCube;
} else {
// 2D texture
if ( texture.__webglInit === undefined ) return;
_gl.deleteTexture( texture.__webglTexture );
delete texture.__webglTexture;
delete texture.__webglInit;
}
};
var deallocateRenderTarget = function ( renderTarget ) {
if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return;
_gl.deleteTexture( renderTarget.__webglTexture );
delete renderTarget.__webglTexture;
if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
for ( var i = 0; i < 6; i ++ ) {
_gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] );
_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] );
}
} else {
_gl.deleteFramebuffer( renderTarget.__webglFramebuffer );
_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer );
}
delete renderTarget.__webglFramebuffer;
delete renderTarget.__webglRenderbuffer;
};
var deallocateMaterial = function ( material ) {
var program = material.program.program;
if ( program === undefined ) return;
material.program = undefined;
// only deallocate GL program if this was the last use of shared program
// assumed there is only single copy of any program in the _programs list
// (that's how it's constructed)
var i, il, programInfo;
var deleteProgram = false;
for ( i = 0, il = _programs.length; i < il; i ++ ) {
programInfo = _programs[ i ];
if ( programInfo.program === program ) {
programInfo.usedTimes --;
if ( programInfo.usedTimes === 0 ) {
deleteProgram = true;
}
break;
}
}
if ( deleteProgram === true ) {
// avoid using array.splice, this is costlier than creating new array from scratch
var newPrograms = [];
for ( i = 0, il = _programs.length; i < il; i ++ ) {
programInfo = _programs[ i ];
if ( programInfo.program !== program ) {
newPrograms.push( programInfo );
}
}
_programs = newPrograms;
_gl.deleteProgram( program );
_this.info.memory.programs --;
}
};
// Buffer initialization
function initCustomAttributes ( object ) {
var geometry = object.geometry;
var material = object.material;
var nvertices = geometry.vertices.length;
if ( material.attributes ) {
if ( geometry.__webglCustomAttributesList === undefined ) {
geometry.__webglCustomAttributesList = [];
}
for ( var name in material.attributes ) {
var attribute = material.attributes[ name ];
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 = name;
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( object );
};
function initLineBuffers ( geometry, object ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__colorArray = new Float32Array( nvertices * 3 );
geometry.__lineDistanceArray = new Float32Array( nvertices * 1 );
geometry.__webglLineCount = nvertices;
initCustomAttributes( object );
};
function initMeshBuffers ( geometryGroup, object ) {
var geometry = object.geometry,
faces3 = geometryGroup.faces3,
nvertices = faces3.length * 3,
ntris = faces3.length * 1,
nlines = faces3.length * 3,
material = getBufferMaterial( object, geometryGroup );
geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
if ( geometry.faceVertexUvs.length > 1 ) {
geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
}
if ( geometry.hasTangents ) {
geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
}
if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
}
var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3
geometryGroup.__typeArray = UintArray;
geometryGroup.__faceArray = new UintArray( ntris * 3 );
geometryGroup.__lineArray = new UintArray( nlines * 2 );
var m, ml;
if ( geometryGroup.numMorphTargets ) {
geometryGroup.__morphTargetsArrays = [];
for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
}
}
if ( geometryGroup.numMorphNormals ) {
geometryGroup.__morphNormalsArrays = [];
for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
geometryGroup.__morphNormalsArrays.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 name 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[ name ];
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 = name;
originalAttribute.needsUpdate = true;
attribute.__original = originalAttribute;
}
geometryGroup.__webglCustomAttributesList.push( attribute );
}
}
geometryGroup.__inittedArrays = true;
};
function getBufferMaterial( object, geometryGroup ) {
return object.material instanceof THREE.MeshFaceMaterial
? object.material.materials[ geometryGroup.materialIndex ]
: object.material;
};
function materialNeedsSmoothNormals ( material ) {
return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
};
// 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.verticesNeedUpdate,
dirtyElements = geometry.elementsNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( object.sortParticles ) {
_projScreenMatrixPS.copy( _projScreenMatrix );
_projScreenMatrixPS.multiply( object.matrixWorld );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
_vector3.copy( vertex );
_vector3.applyProjection( _projScreenMatrixPS );
sortArray[ v ] = [ _vector3.z, v ];
}
sortArray.sort( numericalSort );
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ sortArray[ v ][ 1 ] ];
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 ];
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, d, vertex, offset, color,
vertices = geometry.vertices,
colors = geometry.colors,
lineDistances = geometry.lineDistances,
vl = vertices.length,
cl = colors.length,
dl = lineDistances.length,
vertexArray = geometry.__vertexArray,
colorArray = geometry.__colorArray,
lineDistanceArray = geometry.__lineDistanceArray,
dirtyVertices = geometry.verticesNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
dirtyLineDistances = geometry.lineDistancesNeedUpdate,
customAttributes = geometry.__webglCustomAttributesList,
i, il,
a, ca, cal, value,
customAttribute;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v ++ ) {
vertex = vertices[ v ];
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 ( dirtyLineDistances ) {
for ( d = 0; d < dl; d ++ ) {
lineDistanceArray[ d ] = lineDistances[ d ];
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, 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 setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
if ( ! geometryGroup.__inittedArrays ) {
return;
}
var needsSmoothNormals = materialNeedsSmoothNormals( material );
var f, fl, fi, face,
vertexNormals, faceNormal, normal,
vertexColors, faceColor,
vertexTangents,
uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4,
c1, c2, c3,
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,
nka, chf, faceVertexNormals,
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,
skinIndexArray = geometryGroup.__skinIndexArray,
skinWeightArray = geometryGroup.__skinWeightArray,
morphTargetsArrays = geometryGroup.__morphTargetsArrays,
morphNormalsArrays = geometryGroup.__morphNormalsArrays,
customAttributes = geometryGroup.__webglCustomAttributesList,
customAttribute,
faceArray = geometryGroup.__faceArray,
lineArray = geometryGroup.__lineArray,
geometry = object.geometry, // this is shared for all chunks
dirtyVertices = geometry.verticesNeedUpdate,
dirtyElements = geometry.elementsNeedUpdate,
dirtyUvs = geometry.uvsNeedUpdate,
dirtyNormals = geometry.normalsNeedUpdate,
dirtyTangents = geometry.tangentsNeedUpdate,
dirtyColors = geometry.colorsNeedUpdate,
dirtyMorphTargets = geometry.morphTargetsNeedUpdate,
vertices = geometry.vertices,
chunk_faces3 = geometryGroup.faces3,
obj_faces = geometry.faces,
obj_uvs = geometry.faceVertexUvs[ 0 ],
obj_uvs2 = geometry.faceVertexUvs[ 1 ],
obj_colors = geometry.colors,
obj_skinIndices = geometry.skinIndices,
obj_skinWeights = geometry.skinWeights,
morphTargets = geometry.morphTargets,
morphNormals = geometry.morphNormals;
if ( dirtyVertices ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
face = obj_faces[ chunk_faces3[ f ] ];
v1 = vertices[ face.a ];
v2 = vertices[ face.b ];
v3 = vertices[ face.c ];
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;
}
_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 ++ ) {
chf = chunk_faces3[ f ];
face = obj_faces[ chf ];
// morph positions
v1 = morphTargets[ vk ].vertices[ face.a ];
v2 = morphTargets[ vk ].vertices[ face.b ];
v3 = morphTargets[ vk ].vertices[ face.c ];
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;
// morph normals
if ( material.morphNormals ) {
if ( needsSmoothNormals ) {
faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
n1 = faceVertexNormals.a;
n2 = faceVertexNormals.b;
n3 = faceVertexNormals.c;
} else {
n1 = morphNormals[ vk ].faceNormals[ chf ];
n2 = n1;
n3 = n1;
}
nka = morphNormalsArrays[ vk ];
nka[ offset_morphTarget ] = n1.x;
nka[ offset_morphTarget + 1 ] = n1.y;
nka[ offset_morphTarget + 2 ] = n1.z;
nka[ offset_morphTarget + 3 ] = n2.x;
nka[ offset_morphTarget + 4 ] = n2.y;
nka[ offset_morphTarget + 5 ] = n2.z;
nka[ offset_morphTarget + 6 ] = n3.x;
nka[ offset_morphTarget + 7 ] = n3.y;
nka[ offset_morphTarget + 8 ] = n3.z;
}
//
offset_morphTarget += 9;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] );
_gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint );
if ( material.morphNormals ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] );
_gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ 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;
offset_skin += 12;
}
if ( offset_skin > 0 ) {
_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 ) {
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 && material.vertexColors === 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;
}
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;
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint );
}
if ( dirtyNormals ) {
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;
}
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
}
if ( dirtyUvs && obj_uvs ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
uv = obj_uvs[ fi ];
if ( uv === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uvi = uv[ i ];
uvArray[ offset_uv ] = uvi.x;
uvArray[ offset_uv + 1 ] = uvi.y;
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 ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
fi = chunk_faces3[ f ];
uv2 = obj_uvs2[ fi ];
if ( uv2 === undefined ) continue;
for ( i = 0; i < 3; i ++ ) {
uv2i = uv2[ i ];
uv2Array[ offset_uv2 ] = uv2i.x;
uv2Array[ offset_uv2 + 1 ] = uv2i.y;
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 ++ ) {
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;
}
_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;
}
} 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;
}
}
} 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;
}
} 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;
}
}
} 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;
}
} 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;
}
} else if ( customAttribute.boundTo === 'faceVertices' ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
v1 = value[ 0 ];
v2 = value[ 1 ];
v3 = value[ 2 ];
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;
}
}
} 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;
}
} 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;
}
} else if ( customAttribute.boundTo === 'faceVertices' ) {
for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
value = customAttribute.value[ chunk_faces3[ f ] ];
v1 = value[ 0 ];
v2 = value[ 1 ];
v3 = value[ 2 ];
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;
}
}
}
_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.__skinIndexArray;
delete geometryGroup.__skinWeightArray;
}
};
function setDirectBuffers( geometry ) {
var attributes = geometry.attributes;
var attributesKeys = geometry.attributesKeys;
for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) {
var key = attributesKeys[ i ];
var attribute = attributes[ key ];
if ( attribute.buffer === undefined ) {
attribute.buffer = _gl.createBuffer();
attribute.needsUpdate = true;
}
if ( attribute.needsUpdate === true ) {
var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER;
_gl.bindBuffer( bufferType, attribute.buffer );
_gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW );
attribute.needsUpdate = false;
}
}
}
// Buffer rendering
this.renderBufferImmediate = function ( object, program, material ) {
initAttributes();
if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer();
if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer();
if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer();
if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer();
if ( object.hasPositions ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
enableAttribute( program.attributes.position );
_gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 );
}
if ( object.hasNormals ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer );
if ( material.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 );
enableAttribute( program.attributes.normal );
_gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
}
if ( object.hasUvs && material.map ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
enableAttribute( program.attributes.uv );
_gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
}
if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
enableAttribute( program.attributes.color );
_gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 );
}
disableUnusedAttributes();
_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
object.count = 0;
};
function setupVertexAttributes( material, program, geometry, startIndex ) {
var geometryAttributes = geometry.attributes;
var programAttributes = program.attributes;
var programAttributesKeys = program.attributesKeys;
for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) {
var key = programAttributesKeys[ i ];
var programAttribute = programAttributes[ key ];
if ( programAttribute >= 0 ) {
var geometryAttribute = geometryAttributes[ key ];
if ( geometryAttribute !== undefined ) {
var size = geometryAttribute.itemSize;
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer );
enableAttribute( programAttribute );
_gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
} else if ( material.defaultAttributeValues !== undefined ) {
if ( material.defaultAttributeValues[ key ].length === 2 ) {
_gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] );
} else if ( material.defaultAttributeValues[ key ].length === 3 ) {
_gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] );
}
}
}
}
disableUnusedAttributes();
}
this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
if ( material.visible === false ) return;
console.log("renderBufferDirect");
var program = setProgram( camera, lights, fog, material, object );
var updateBuffers = false,
wireframeBit = material.wireframe ? 1 : 0,
geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
if ( geometryHash !== _currentGeometryGroupHash ) {
_currentGeometryGroupHash = geometryHash;
updateBuffers = true;
}
if ( updateBuffers ) {
initAttributes();
}
// render mesh
if ( object instanceof THREE.Mesh ) {
var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES;
var index = geometry.attributes.index;
if ( index ) {
// indexed triangles
var type, size;
if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {
type = _gl.UNSIGNED_INT;
size = 4;
} else {
type = _gl.UNSIGNED_SHORT;
size = 2;
}
var offsets = geometry.offsets;
if ( offsets.length === 0 ) {
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, 0 );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
}
_gl.drawElements( mode, index.array.length, type, 0 );
_this.info.render.calls ++;
_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
_this.info.render.faces += index.array.length / 3;
} else {
// if there is more than 1 chunk
// must set attribute pointers to use new offsets for each chunk
// even if geometry and materials didn't change
updateBuffers = true;
for ( var i = 0, il = offsets.length; i < il; i ++ ) {
var startIndex = offsets[ i ].index;
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, startIndex );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
}
// render indexed triangles
_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size );
_this.info.render.calls ++;
_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
_this.info.render.faces += offsets[ i ].count / 3;
}
}
} else {
// non-indexed triangles
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, 0 );
}
var position = geometry.attributes[ 'position' ];
// render non-indexed triangles
_gl.drawArrays( mode, 0, position.array.length / 3 );
_this.info.render.calls ++;
_this.info.render.vertices += position.array.length / 3;
_this.info.render.faces += position.array.length / 9;
}
} else if ( object instanceof THREE.PointCloud ) {
// render particles
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, 0 );
}
var position = geometry.attributes.position;
// render particles
_gl.drawArrays( _gl.POINTS, 0, position.array.length / 3 );
_this.info.render.calls ++;
_this.info.render.points += position.array.length / 3;
} else if ( object instanceof THREE.Line ) {
var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
setLineWidth( material.linewidth );
var index = geometry.attributes.index;
if ( index ) {
// indexed lines
var type, size;
if ( index.array instanceof Uint32Array ) {
type = _gl.UNSIGNED_INT;
size = 4;
} else {
type = _gl.UNSIGNED_SHORT;
size = 2;
}
var offsets = geometry.offsets;
if ( offsets.length === 0 ) {
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, 0 );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
}
_gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array
_this.info.render.calls ++;
_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
} else {
// if there is more than 1 chunk
// must set attribute pointers to use new offsets for each chunk
// even if geometry and materials didn't change
if ( offsets.length > 1 ) updateBuffers = true;
for ( var i = 0, il = offsets.length; i < il; i ++ ) {
var startIndex = offsets[ i ].index;
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, startIndex );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
}
// render indexed lines
_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array
_this.info.render.calls ++;
_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
}
}
} else {
// non-indexed lines
if ( updateBuffers ) {
setupVertexAttributes( material, program, geometry, 0 );
}
var position = geometry.attributes.position;
_gl.drawArrays( mode, 0, position.array.length / 3 );
_this.info.render.calls ++;
_this.info.render.points += position.array.length / 3;
}
}
};
this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
if ( material.visible === false ) return;
//console.log("renderBuffer");
var program = setProgram( camera, lights, fog, material, object );
var 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;
}
if ( updateBuffers ) {
initAttributes();
}
// vertices
if ( ! material.morphTargets && attributes.position >= 0 ) {
if ( updateBuffers ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
enableAttribute( attributes.position );
_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 ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
var attribute = geometryGroup.__webglCustomAttributesList[ i ];
if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer );
enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] );
_gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 );
}
}
}
// colors
if ( attributes.color >= 0 ) {
if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
enableAttribute( attributes.color );
_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
} else if ( material.defaultAttributeValues !== undefined ) {
_gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color );
}
}
// normals
if ( attributes.normal >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
enableAttribute( attributes.normal );
_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
}
// tangents
if ( attributes.tangent >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
enableAttribute( attributes.tangent );
_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
}
// uvs
if ( attributes.uv >= 0 ) {
if ( object.geometry.faceVertexUvs[ 0 ] ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
enableAttribute( attributes.uv );
_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
} else if ( material.defaultAttributeValues !== undefined ) {
_gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv );
}
}
if ( attributes.uv2 >= 0 ) {
if ( object.geometry.faceVertexUvs[ 1 ] ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
enableAttribute( attributes.uv2 );
_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
} else if ( material.defaultAttributeValues !== undefined ) {
_gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 );
}
}
if ( material.skinning &&
attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
enableAttribute( attributes.skinIndex );
_gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 );
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
enableAttribute( attributes.skinWeight );
_gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 );
}
// line distances
if ( attributes.lineDistance >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer );
enableAttribute( attributes.lineDistance );
_gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 );
}
}
disableUnusedAttributes();
// render mesh
if ( object instanceof THREE.Mesh ) {
var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT;
// wireframe
if ( material.wireframe ) {
setLineWidth( material.wireframeLinewidth );
if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 );
// triangles
} else {
if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 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 ) {
var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
setLineWidth( material.linewidth );
_gl.drawArrays( mode, 0, geometryGroup.__webglLineCount );
_this.info.render.calls ++;
// render particles
} else if ( object instanceof THREE.PointCloud ) {
_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
_this.info.render.calls ++;
_this.info.render.points += geometryGroup.__webglParticleCount;
}
};
function initAttributes() {
for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) {
_newAttributes[ i ] = 0;
}
}
function enableAttribute( attribute ) {
_newAttributes[ attribute ] = 1;
if ( _enabledAttributes[ attribute ] === 0 ) {
_gl.enableVertexAttribArray( attribute );
_enabledAttributes[ attribute ] = 1;
}
}
function disableUnusedAttributes() {
for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) {
if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) {
_gl.disableVertexAttribArray( i );
_enabledAttributes[ i ] = 0;
}
}
}
function setupMorphTargets ( material, geometryGroup, object ) {
// set base
var attributes = material.program.attributes;
if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] );
enableAttribute( attributes.position );
_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
} else if ( attributes.position >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
enableAttribute( attributes.position );
_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 ) {
if ( attributes[ 'morphTarget' + m ] >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
enableAttribute( attributes[ 'morphTarget' + m ] );
_gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 );
}
if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] );
enableAttribute( attributes[ 'morphNormal' + m ] );
_gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 );
}
object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
m ++;
}
} else {
// find the most influencing
var influence, activeInfluenceIndices = [];
var influences = object.morphTargetInfluences;
var i, il = influences.length;
for ( i = 0; i < il; i ++ ) {
influence = influences[ i ];
if ( influence > 0 ) {
activeInfluenceIndices.push( [ influence, i ] );
}
}
if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
activeInfluenceIndices.sort( numericalSort );
activeInfluenceIndices.length = material.numSupportedMorphTargets;
} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
activeInfluenceIndices.sort( numericalSort );
} else if ( activeInfluenceIndices.length === 0 ) {
activeInfluenceIndices.push( [ 0, 0 ] );
};
var influenceIndex, m = 0;
while ( m < material.numSupportedMorphTargets ) {
if ( activeInfluenceIndices[ m ] ) {
influenceIndex = activeInfluenceIndices[ m ][ 1 ];
if ( attributes[ 'morphTarget' + m ] >= 0 ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
enableAttribute( attributes[ 'morphTarget' + m ] );
_gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 );
}
if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) {
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] );
enableAttribute( attributes[ 'morphNormal' + m ] );
_gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 );
}
object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
} else {
/*
_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
if ( material.morphNormals ) {
_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
}
*/
object.__webglMorphTargetInfluences[ m ] = 0;
}
m ++;
}
}
// load updated influences uniform
if ( material.program.uniforms.morphTargetInfluences !== null ) {
_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
}
}
// Sorting
function painterSortStable ( a, b ) {
if ( a.material.id !== b.material.id ) {
return b.material.id - a.material.id;
} else if ( a.z !== b.z ) {
return b.z - a.z;
} else {
return a.id - b.id;
}
}
function reversePainterSortStable ( a, b ) {
if ( a.z !== b.z ) {
return a.z - b.z;
} else {
return a.id - b.id;
}
}
function numericalSort ( a, b ) {
return b[ 0 ] - a[ 0 ];
}
// Rendering
this.render = function ( scene, camera, renderTarget, forceClear ) {
if ( camera instanceof THREE.Camera === false ) {
console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
return;
}
var fog = scene.fog;
// reset caching for this frame
_currentGeometryGroupHash = - 1;
_currentMaterialId = - 1;
_currentCamera = null;
_lightsNeedUpdate = true;
// update scene graph
if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
// update camera matrices and frustum
if ( camera.parent === undefined ) camera.updateMatrixWorld();
// update Skeleton objects
scene.traverse( function ( object ) {
if ( object instanceof THREE.SkinnedMesh ) {
object.skeleton.update();
}
} );
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
_frustum.setFromMatrix( _projScreenMatrix );
lights.length = 0;
opaqueObjects.length = 0;
transparentObjects.length = 0;
sprites.length = 0;
lensFlares.length = 0;
projectObject( scene, scene );
if ( _this.sortObjects === true ) {
opaqueObjects.sort( painterSortStable );
transparentObjects.sort( reversePainterSortStable );
}
// custom render plugins (pre pass)
shadowMapPlugin.render( scene, camera );
//
_this.info.render.calls = 0;
_this.info.render.vertices = 0;
_this.info.render.faces = 0;
_this.info.render.points = 0;
this.setRenderTarget( renderTarget );
if ( this.autoClear || forceClear ) {
this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
}
// set matrices for immediate objects
for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) {
var webglObject = _webglObjectsImmediate[ i ];
var object = webglObject.object;
if ( object.visible ) {
setupMatrices( object, camera );
unrollImmediateBufferMaterial( webglObject );
}
}
if ( scene.overrideMaterial ) {
var material = scene.overrideMaterial;
this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
this.setDepthTest( material.depthTest );
this.setDepthWrite( material.depthWrite );
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
renderObjects( opaqueObjects, camera, lights, fog, true, material );
renderObjects( transparentObjects, camera, lights, fog, true, material );
renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, false, material );
} else {
var material = null;
// opaque pass (front-to-back order)
this.setBlending( THREE.NoBlending );
renderObjects( opaqueObjects, camera, lights, fog, false, material );
renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, false, material );
// transparent pass (back-to-front order)
renderObjects( transparentObjects, camera, lights, fog, true, material );
renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, true, material );
}
// custom render plugins (post pass)
spritePlugin.render( scene, camera );
lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
// Generate mipmap if we're using any kind of mipmap filtering
if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
updateRenderTargetMipmap( renderTarget );
}
// Ensure depth buffer writing is enabled so it can be cleared on next render
this.setDepthTest( true );
this.setDepthWrite( true );
// _gl.finish();
};
function projectObject( scene, object ) {
if ( object.visible === false ) return;
if ( object instanceof THREE.Scene || object instanceof THREE.Group ) {
// skip
} else {
initObject( object, scene );
if ( object instanceof THREE.Light ) {
lights.push( object );
} else if ( object instanceof THREE.Sprite ) {
sprites.push( object );
} else if ( object instanceof THREE.LensFlare ) {
lensFlares.push( object );
} else {
var webglObjects = _webglObjects[ object.id ];
if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
updateObject( object, scene );
for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
var webglObject = webglObjects[i];
unrollBufferMaterial( webglObject );
webglObject.render = true;
if ( _this.sortObjects === true ) {
if ( object.renderDepth !== null ) {
webglObject.z = object.renderDepth;
} else {
_vector3.setFromMatrixPosition( object.matrixWorld );
_vector3.applyProjection( _projScreenMatrix );
webglObject.z = _vector3.z;
}
}
}
}
}
}
for ( var i = 0, l = object.children.length; i < l; i ++ ) {
projectObject( scene, object.children[ i ] );
}
}
function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) {
var material;
//console.log("renderObjects");
for ( var i = renderList.length - 1; i !== - 1; i -- ) {
var webglObject = renderList[ i ];
var object = webglObject.object;
var buffer = webglObject.buffer;
setupMatrices( object, camera );
if ( overrideMaterial ) {
material = overrideMaterial;
} else {
material = webglObject.material;
if ( ! material ) continue;
if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
_this.setDepthTest( material.depthTest );
_this.setDepthWrite( material.depthWrite );
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
}
_this.setMaterialFaces( material );
if ( buffer instanceof THREE.BufferGeometry ) {
_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
} else {
_this.renderBuffer( camera, lights, fog, material, buffer, object );
}
}
}
function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
var material;
for ( var i = 0, il = renderList.length; i < il; i ++ ) {
var webglObject = renderList[ i ];
var object = webglObject.object;
if ( object.visible ) {
if ( overrideMaterial ) {
material = overrideMaterial;
} else {
material = webglObject[ materialType ];
if ( ! material ) continue;
if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
_this.setDepthTest( material.depthTest );
_this.setDepthWrite( material.depthWrite );
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
}
_this.renderImmediateObject( camera, lights, fog, material, object );
}
}
}
this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
console.log("renderImmediateObject");
var program = setProgram( camera, lights, fog, material, object );
_currentGeometryGroupHash = - 1;
_this.setMaterialFaces( material );
if ( object.immediateRenderCallback ) {
object.immediateRenderCallback( program, _gl, _frustum );
} else {
object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } );
}
};
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;
var buffer = globject.buffer;
var geometry = object.geometry;
var material = object.material;
if ( material instanceof THREE.MeshFaceMaterial ) {
var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex;
material = material.materials[ materialIndex ];
globject.material = material;
if ( material.transparent ) {
transparentObjects.push( globject );
} else {
opaqueObjects.push( globject );
}
} else if ( material ) {
globject.material = material;
if ( material.transparent ) {
transparentObjects.push( globject );
} else {
opaqueObjects.push( globject );
}
}
}
function initObject( object, scene ) {
if ( object.__webglInit === undefined ) {
object.__webglInit = true;
object._modelViewMatrix = new THREE.Matrix4();
object._normalMatrix = new THREE.Matrix3();
object.addEventListener( 'removed', onObjectRemoved );
}
var geometry = object.geometry;
if ( geometry === undefined ) {
// ImmediateRenderObject
} else if ( geometry.__webglInit === undefined ) {
geometry.__webglInit = true;
geometry.addEventListener( 'dispose', onGeometryDispose );
if ( geometry instanceof THREE.BufferGeometry ) {
//
} else if ( object instanceof THREE.Mesh ) {
initGeometryGroups( scene, object, geometry );
} else if ( object instanceof THREE.Line ) {
if ( geometry.__webglVertexBuffer === undefined ) {
createLineBuffers( geometry );
initLineBuffers( geometry, object );
geometry.verticesNeedUpdate = true;
geometry.colorsNeedUpdate = true;
geometry.lineDistancesNeedUpdate = true;
}
} else if ( object instanceof THREE.PointCloud ) {
if ( geometry.__webglVertexBuffer === undefined ) {
createParticleBuffers( geometry );
initParticleBuffers( geometry, object );
geometry.verticesNeedUpdate = true;
geometry.colorsNeedUpdate = true;
}
}
}
if ( object.__webglActive === undefined) {
object.__webglActive = true;
if ( object instanceof THREE.Mesh ) {
if ( geometry instanceof THREE.BufferGeometry ) {
addBuffer( _webglObjects, geometry, object );
} else if ( geometry instanceof THREE.Geometry ) {
var geometryGroupsList = geometryGroups[ geometry.id ];
for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) {
addBuffer( _webglObjects, geometryGroupsList[ i ], object );
}
}
} else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) {
addBuffer( _webglObjects, geometry, object );
} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
addBufferImmediate( _webglObjectsImmediate, object );
}
}
}
// Geometry splitting
var geometryGroups = {};
var geometryGroupCounter = 0;
function makeGroups( geometry, usesFaceMaterial ) {
var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535;
var groupHash, hash_map = {};
var numMorphTargets = geometry.morphTargets.length;
var numMorphNormals = geometry.morphNormals.length;
var group;
var groups = {};
var groupsList = [];
for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
var face = geometry.faces[ f ];
var materialIndex = usesFaceMaterial ? face.materialIndex : 0;
if ( ! ( materialIndex in hash_map ) ) {
hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 };
}
groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
if ( ! ( groupHash in groups ) ) {
group = {
id: geometryGroupCounter ++,
faces3: [],
materialIndex: materialIndex,
vertices: 0,
numMorphTargets: numMorphTargets,
numMorphNormals: numMorphNormals
};
groups[ groupHash ] = group;
groupsList.push( group );
}
if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) {
hash_map[ materialIndex ].counter += 1;
groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
if ( ! ( groupHash in groups ) ) {
group = {
id: geometryGroupCounter ++,
faces3: [],
materialIndex: materialIndex,
vertices: 0,
numMorphTargets: numMorphTargets,
numMorphNormals: numMorphNormals
};
groups[ groupHash ] = group;
groupsList.push( group );
}
}
groups[ groupHash ].faces3.push( f );
groups[ groupHash ].vertices += 3;
}
return groupsList;
}
function initGeometryGroups( scene, object, geometry ) {
var material = object.material, addBuffers = false;
if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) {
delete _webglObjects[ object.id ];
geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial );
geometry.groupsNeedUpdate = false;
}
var geometryGroupsList = geometryGroups[ geometry.id ];
// create separate VBOs per geometry chunk
for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) {
var geometryGroup = geometryGroupsList[ i ];
// initialise VBO on the first access
if ( geometryGroup.__webglVertexBuffer === undefined ) {
createMeshBuffers( geometryGroup );
initMeshBuffers( geometryGroup, object );
geometry.verticesNeedUpdate = true;
geometry.morphTargetsNeedUpdate = true;
geometry.elementsNeedUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.normalsNeedUpdate = true;
geometry.tangentsNeedUpdate = true;
geometry.colorsNeedUpdate = true;
addBuffers = true;
} else {
addBuffers = false;
}
if ( addBuffers || object.__webglActive === undefined ) {
addBuffer( _webglObjects, geometryGroup, object );
}
}
object.__webglActive = true;
}
function addBuffer( objlist, buffer, object ) {
var id = object.id;
objlist[id] = objlist[id] || [];
objlist[id].push(
{
id: id,
buffer: buffer,
object: object,
material: null,
z: 0
}
);
};
function addBufferImmediate( objlist, object ) {
objlist.push(
{
id: null,
object: object,
opaque: null,
transparent: null,
z: 0
}
);
};
// Objects updates
function updateObject( object, scene ) {
var geometry = object.geometry, customAttributesDirty, material;
if ( geometry instanceof THREE.BufferGeometry ) {
setDirectBuffers( geometry );
} else if ( object instanceof THREE.Mesh ) {
// check all geometry groups
if ( geometry.groupsNeedUpdate === true ) {
initGeometryGroups( scene, object, geometry );
}
var geometryGroupsList = geometryGroups[ geometry.id ];
for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) {
var geometryGroup = geometryGroupsList[ i ];
material = getBufferMaterial( object, geometryGroup );
if ( geometry.groupsNeedUpdate === true ) {
initMeshBuffers( geometryGroup, object );
}
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate ||
geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) {
setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material );
}
}
geometry.verticesNeedUpdate = false;
geometry.morphTargetsNeedUpdate = false;
geometry.elementsNeedUpdate = false;
geometry.uvsNeedUpdate = false;
geometry.normalsNeedUpdate = false;
geometry.colorsNeedUpdate = false;
geometry.tangentsNeedUpdate = false;
material.attributes && clearCustomAttributes( material );
} else if ( object instanceof THREE.Line ) {
material = getBufferMaterial( object, geometry );
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
}
geometry.verticesNeedUpdate = false;
geometry.colorsNeedUpdate = false;
geometry.lineDistancesNeedUpdate = false;
material.attributes && clearCustomAttributes( material );
} else if ( object instanceof THREE.PointCloud ) {
material = getBufferMaterial( object, geometry );
customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) {
setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
}
geometry.verticesNeedUpdate = false;
geometry.colorsNeedUpdate = false;
material.attributes && clearCustomAttributes( material );
}
}
// Objects updates - custom attributes check
function areCustomAttributesDirty( material ) {
for ( var name in material.attributes ) {
if ( material.attributes[ name ].needsUpdate ) return true;
}
return false;
}
function clearCustomAttributes( material ) {
for ( var name in material.attributes ) {
material.attributes[ name ].needsUpdate = false;
}
}
// Objects removal
function removeObject( object ) {
if ( object instanceof THREE.Mesh ||
object instanceof THREE.PointCloud ||
object instanceof THREE.Line ) {
delete _webglObjects[ object.id ];
} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
removeInstances( _webglObjectsImmediate, object );
}
delete object.__webglInit;
delete object._modelViewMatrix;
delete object._normalMatrix;
delete object.__webglActive;
}
function removeInstances( objlist, object ) {
for ( var o = objlist.length - 1; o >= 0; o -- ) {
if ( objlist[ o ].object === object ) {
objlist.splice( o, 1 );
}
}
}
// Materials
function initMaterial( material, lights, fog, object ) {
material.addEventListener( 'dispose', onMaterialDispose );
var 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.LineDashedMaterial ) {
shaderID = 'dashed';
} else if ( material instanceof THREE.PointCloudMaterial ) {
shaderID = 'particle_basic';
}
if ( shaderID ) {
var shader = THREE.ShaderLib[ shaderID ];
material.__webglShader = {
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
}
} else {
material.__webglShader = {
uniforms: material.uniforms,
vertexShader: material.vertexShader,
fragmentShader: material.fragmentShader
}
}
// heuristics to create shader parameters according to lights in the scene
// (not to blow over maxLights budget)
var maxLightCount = allocateLights( lights );
var maxShadows = allocateShadows( lights );
var maxBones = allocateBones( object );
var parameters = {
precision: _precision,
supportsVertexTextures: _supportsVertexTextures,
map: !! material.map,
envMap: !! material.envMap,
lightMap: !! material.lightMap,
bumpMap: !! material.bumpMap,
normalMap: !! material.normalMap,
specularMap: !! material.specularMap,
alphaMap: !! material.alphaMap,
vertexColors: material.vertexColors,
fog: fog,
useFog: material.fog,
fogExp: fog instanceof THREE.FogExp2,
sizeAttenuation: material.sizeAttenuation,
logarithmicDepthBuffer: _logarithmicDepthBuffer,
skinning: material.skinning,
maxBones: maxBones,
useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture,
morphTargets: material.morphTargets,
morphNormals: material.morphNormals,
maxMorphTargets: _this.maxMorphTargets,
maxMorphNormals: _this.maxMorphNormals,
maxDirLights: maxLightCount.directional,
maxPointLights: maxLightCount.point,
maxSpotLights: maxLightCount.spot,
maxHemiLights: maxLightCount.hemi,
maxShadows: maxShadows,
shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0,
shadowMapType: _this.shadowMapType,
shadowMapDebug: _this.shadowMapDebug,
shadowMapCascade: _this.shadowMapCascade,
alphaTest: material.alphaTest,
metal: material.metal,
wrapAround: material.wrapAround,
doubleSided: material.side === THREE.DoubleSide,
flipSided: material.side === THREE.BackSide
};
// Generate code
var chunks = [];
if ( shaderID ) {
chunks.push( shaderID );
} else {
chunks.push( material.fragmentShader );
chunks.push( material.vertexShader );
}
if ( material.defines !== undefined ) {
for ( var name in material.defines ) {
chunks.push( name );
chunks.push( material.defines[ name ] );
}
}
for ( var name in parameters ) {
chunks.push( name );
chunks.push( parameters[ name ] );
}
var code = chunks.join();
var program;
// Check if code has been already compiled
for ( var p = 0, pl = _programs.length; p < pl; p ++ ) {
var programInfo = _programs[ p ];
if ( programInfo.code === code ) {
program = programInfo;
program.usedTimes ++;
break;
}
}
if ( program === undefined ) {
program = new THREE.WebGLProgram( _this, code, material, parameters );
_programs.push( program );
_this.info.memory.programs = _programs.length;
}
material.program = program;
var attributes = program.attributes;
if ( material.morphTargets ) {
material.numSupportedMorphTargets = 0;
var id, base = 'morphTarget';
for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
id = base + i;
if ( attributes[ id ] >= 0 ) {
material.numSupportedMorphTargets ++;
}
}
}
if ( material.morphNormals ) {
material.numSupportedMorphNormals = 0;
var id, base = 'morphNormal';
for ( i = 0; i < _this.maxMorphNormals; i ++ ) {
id = base + i;
if ( attributes[ id ] >= 0 ) {
material.numSupportedMorphNormals ++;
}
}
}
material.uniformsList = [];
for ( var u in material.__webglShader.uniforms ) {
var location = material.program.uniforms[ u ];
if ( location ) {
material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] );
}
}
}
function setProgram( camera, lights, fog, material, object ) {
_usedTextureUnits = 0;
if ( material.needsUpdate ) {
if ( material.program ) deallocateMaterial( material );
initMaterial( material, lights, fog, object );
material.needsUpdate = false;
}
if ( material.morphTargets ) {
if ( ! object.__webglMorphTargetInfluences ) {
object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
}
}
var refreshProgram = false;
var refreshMaterial = false;
var refreshLights = false;
var program = material.program,
p_uniforms = program.uniforms,
m_uniforms = material.__webglShader.uniforms;
if ( program.id !== _currentProgram ) {
_gl.useProgram( program.program );
_currentProgram = program.id;
refreshProgram = true;
refreshMaterial = true;
refreshLights = true;
}
if ( material.id !== _currentMaterialId ) {
if ( _currentMaterialId === -1 ) refreshLights = true;
_currentMaterialId = material.id;
refreshMaterial = true;
}
if ( refreshProgram || camera !== _currentCamera ) {
_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
if ( _logarithmicDepthBuffer ) {
_gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
}
if ( camera !== _currentCamera ) _currentCamera = camera;
// 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 ) {
_vector3.setFromMatrixPosition( camera.matrixWorld );
_gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.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, camera.matrixWorldInverse.elements );
}
}
}
// skinning uniforms must be set even if material didn't change
// auto-setting of texture unit for bone texture must go before other textures
// not sure why, but otherwise weird things happen
if ( material.skinning ) {
if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) {
_gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );
}
if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) {
_gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );
}
if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) {
if ( p_uniforms.boneTexture !== null ) {
var textureUnit = getTextureUnit();
_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
_this.setTexture( object.skeleton.boneTexture, textureUnit );
}
if ( p_uniforms.boneTextureWidth !== null ) {
_gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );
}
if ( p_uniforms.boneTextureHeight !== null ) {
_gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );
}
} else if ( object.skeleton && object.skeleton.boneMatrices ) {
//console.log(object.skeleton);
if ( p_uniforms.boneGlobalMatrices !== null ) {
var modelMatrix = new Array();
for (var n = 0; n < 16; n++){
modelMatrix[n] = object.skeleton.boneMatrices[n];
}
var newMatrix = new Array();
if (currentMatrix.length == 0){
newMatrix = modelMatrix;
} else {
var mModel = new THREE.Matrix4();
mModel.set(modelMatrix[0], modelMatrix[4], modelMatrix[8], modelMatrix[12],
modelMatrix[1], modelMatrix[5], modelMatrix[9], modelMatrix[13],
modelMatrix[2], modelMatrix[6], modelMatrix[10], modelMatrix[14],
modelMatrix[3], modelMatrix[7], modelMatrix[11], modelMatrix[15]);
for (var m = 0; m < (currentMatrix.length / 16); m++){
var mCurrent = new THREE.Matrix4();
mCurrent.set(currentMatrix[m * 16 + 0], currentMatrix[m * 16 + 4], currentMatrix[m * 16 + 8], currentMatrix[m * 16 + 12],
currentMatrix[m * 16 + 1], currentMatrix[m * 16 + 5], currentMatrix[m * 16 + 9], currentMatrix[m * 16 + 13],
currentMatrix[m * 16 + 2], currentMatrix[m * 16 + 6], currentMatrix[m * 16 + 10], currentMatrix[m * 16 + 14],
currentMatrix[m * 16 + 3], currentMatrix[m * 16 + 7], currentMatrix[m * 16 + 11], currentMatrix[m * 16 + 15]);
var mNew = new THREE.Matrix4();
mNew.multiplyMatrices(mModel, mCurrent);
for (var n = 0; n < 16; n++){
newMatrix.push(mNew.elements[n]);
}
}
}
_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, newMatrix );
//_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );
}
}
}
if ( refreshMaterial ) {
// 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 ) {
if ( _lightsNeedUpdate ) {
refreshLights = true;
setupLights( lights );
_lightsNeedUpdate = false;
}
if ( refreshLights ) {
refreshUniformsLights( m_uniforms, _lights );
markUniformsLightsNeedsUpdate( m_uniforms, true );
} else {
markUniformsLightsNeedsUpdate( m_uniforms, false );
}
}
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.LineDashedMaterial ) {
refreshUniformsLine( m_uniforms, material );
refreshUniformsDash( m_uniforms, material );
} else if ( material instanceof THREE.PointCloudMaterial ) {
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, lights );
}
// load common uniforms
loadUniformsGeneric( material.uniformsList );
}
loadUniformsMatrices( p_uniforms, object );
if ( p_uniforms.modelMatrix !== null ) {
_gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
}
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.value = material.map;
uniforms.lightMap.value = material.lightMap;
uniforms.specularMap.value = material.specularMap;
uniforms.alphaMap.value = material.alphaMap;
if ( material.bumpMap ) {
uniforms.bumpMap.value = material.bumpMap;
uniforms.bumpScale.value = material.bumpScale;
}
if ( material.normalMap ) {
uniforms.normalMap.value = material.normalMap;
uniforms.normalScale.value.copy( material.normalScale );
}
// uv repeat and offset setting priorities
// 1. color map
// 2. specular map
// 3. normal map
// 4. bump map
// 5. alpha map
var uvScaleMap;
if ( material.map ) {
uvScaleMap = material.map;
} else if ( material.specularMap ) {
uvScaleMap = material.specularMap;
} else if ( material.normalMap ) {
uvScaleMap = material.normalMap;
} else if ( material.bumpMap ) {
uvScaleMap = material.bumpMap;
} else if ( material.alphaMap ) {
uvScaleMap = material.alphaMap;
}
if ( uvScaleMap !== undefined ) {
var offset = uvScaleMap.offset;
var repeat = uvScaleMap.repeat;
uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
}
uniforms.envMap.value = 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;
if(uniforms.combine) if(uniforms.combine.value) uniforms.combine.value = material.combine;
if(uniforms.useRefract) if(uniforms.useRefract.value) 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 refreshUniformsDash ( uniforms, material ) {
uniforms.dashSize.value = material.dashSize;
uniforms.totalSize.value = material.dashSize + material.gapSize;
uniforms.scale.value = material.scale;
}
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.value = 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.emissive.value.copyGammaToLinear( material.emissive );
uniforms.specular.value.copyGammaToLinear( material.specular );
} else {
uniforms.ambient.value = material.ambient;
uniforms.emissive.value = material.emissive;
uniforms.specular.value = material.specular;
}
if ( material.wrapAround ) {
uniforms.wrapRGB.value.copy( material.wrapRGB );
}
}
function refreshUniformsLambert ( uniforms, material ) {
if ( _this.gammaInput ) {
uniforms.ambient.value.copyGammaToLinear( material.ambient );
uniforms.emissive.value.copyGammaToLinear( material.emissive );
} else {
if(uniforms.ambient) uniforms.ambient.value = material.ambient;
if(uniforms.emissive) uniforms.emissive.value = material.emissive;
}
if ( material.wrapAround ) {
uniforms.wrapRGB.value.copy( material.wrapRGB );
}
}
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;
uniforms.spotLightColor.value = lights.spot.colors;
uniforms.spotLightPosition.value = lights.spot.positions;
uniforms.spotLightDistance.value = lights.spot.distances;
uniforms.spotLightDirection.value = lights.spot.directions;
uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
uniforms.spotLightExponent.value = lights.spot.exponents;
uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
uniforms.hemisphereLightDirection.value = lights.hemi.positions;
}
// If uniforms are marked as clean, they don't need to be loaded to the GPU.
function markUniformsLightsNeedsUpdate ( uniforms, boolean ) {
uniforms.ambientLightColor.needsUpdate = boolean;
uniforms.directionalLightColor.needsUpdate = boolean;
uniforms.directionalLightDirection.needsUpdate = boolean;
uniforms.pointLightColor.needsUpdate = boolean;
uniforms.pointLightPosition.needsUpdate = boolean;
uniforms.pointLightDistance.needsUpdate = boolean;
uniforms.spotLightColor.needsUpdate = boolean;
uniforms.spotLightPosition.needsUpdate = boolean;
uniforms.spotLightDistance.needsUpdate = boolean;
uniforms.spotLightDirection.needsUpdate = boolean;
uniforms.spotLightAngleCos.needsUpdate = boolean;
uniforms.spotLightExponent.needsUpdate = boolean;
uniforms.hemisphereLightSkyColor.needsUpdate = boolean;
uniforms.hemisphereLightGroundColor.needsUpdate = boolean;
uniforms.hemisphereLightDirection.needsUpdate = boolean;
}
function refreshUniformsShadow ( uniforms, lights ) {
if ( uniforms.shadowMatrix ) {
var j = 0;
for ( var i = 0, il = lights.length; i < il; i ++ ) {
var light = lights[ i ];
if ( ! light.castShadow ) continue;
if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) {
uniforms.shadowMap.value[ j ] = light.shadowMap;
uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
uniforms.shadowBias.value[ j ] = light.shadowBias;
j ++;
}
}
}
}
// Uniforms (load to GPU)
function loadUniformsMatrices ( uniforms, object ) {
_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements );
if ( uniforms.normalMatrix ) {
_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements );
}
}
function getTextureUnit() {
var textureUnit = _usedTextureUnits;
if ( textureUnit >= _maxTextures ) {
console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures );
}
_usedTextureUnits += 1;
return textureUnit;
}
function loadUniformsGeneric ( uniforms ) {
var texture, textureUnit, offset;
for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {
var uniform = uniforms[ j ][ 0 ];
// needsUpdate property is not added to all uniforms.
if ( uniform.needsUpdate === false ) continue;
var type = uniform.type;
var value = uniform.value;
var location = uniforms[ j ][ 1 ];
switch ( type ) {
case '1i':
_gl.uniform1i( location, value );
break;
case '1f':
_gl.uniform1f( location, value );
break;
case '2f':
_gl.uniform2f( location, value[ 0 ], value[ 1 ] );
break;
case '3f':
_gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] );
break;
case '4f':
_gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] );
break;
case '1iv':
_gl.uniform1iv( location, value );
break;
case '3iv':
_gl.uniform3iv( location, value );
break;
case '1fv':
_gl.uniform1fv( location, value );
break;
case '2fv':
_gl.uniform2fv( location, value );
break;
case '3fv':
_gl.uniform3fv( location, value );
break;
case '4fv':
_gl.uniform4fv( location, value );
break;
case 'Matrix3fv':
_gl.uniformMatrix3fv( location, false, value );
break;
case 'Matrix4fv':
_gl.uniformMatrix4fv( location, false, value );
break;
//
case 'i':
// single integer
_gl.uniform1i( location, value );
break;
case 'f':
// single float
_gl.uniform1f( location, value );
break;
case 'v2':
// single THREE.Vector2
_gl.uniform2f( location, value.x, value.y );
break;
case 'v3':
// single THREE.Vector3
_gl.uniform3f( location, value.x, value.y, value.z );
break;
case 'v4':
// single THREE.Vector4
_gl.uniform4f( location, value.x, value.y, value.z, value.w );
break;
case 'c':
// single THREE.Color
_gl.uniform3f( location, value.r, value.g, value.b );
break;
case 'iv1':
// flat array of integers (JS or typed array)
_gl.uniform1iv( location, value );
break;
case 'iv':
// flat array of integers with 3 x N size (JS or typed array)
_gl.uniform3iv( location, value );
break;
case 'fv1':
// flat array of floats (JS or typed array)
_gl.uniform1fv( location, value );
break;
case 'fv':
// flat array of floats with 3 x N size (JS or typed array)
_gl.uniform3fv( location, value );
break;
case 'v2v':
// array of THREE.Vector2
if ( uniform._array === undefined ) {
uniform._array = new Float32Array( 2 * value.length );
}
for ( var i = 0, il = value.length; i < il; i ++ ) {
offset = i * 2;
uniform._array[ offset ] = value[ i ].x;
uniform._array[ offset + 1 ] = value[ i ].y;
}
_gl.uniform2fv( location, uniform._array );
break;
case 'v3v':
// array of THREE.Vector3
if ( uniform._array === undefined ) {
uniform._array = new Float32Array( 3 * value.length );
}
for ( var 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 );
break;
case 'v4v':
// array of THREE.Vector4
if ( uniform._array === undefined ) {
uniform._array = new Float32Array( 4 * value.length );
}
for ( var i = 0, il = value.length; i < il; i ++ ) {
offset = i * 4;
uniform._array[ offset ] = value[ i ].x;
uniform._array[ offset + 1 ] = value[ i ].y;
uniform._array[ offset + 2 ] = value[ i ].z;
uniform._array[ offset + 3 ] = value[ i ].w;
}
_gl.uniform4fv( location, uniform._array );
break;
case 'm3':
// single THREE.Matrix3
_gl.uniformMatrix3fv( location, false, value.elements );
break;
case 'm3v':
// array of THREE.Matrix3
if ( uniform._array === undefined ) {
uniform._array = new Float32Array( 9 * value.length );
}
for ( var i = 0, il = value.length; i < il; i ++ ) {
value[ i ].flattenToArrayOffset( uniform._array, i * 9 );
}
_gl.uniformMatrix3fv( location, false, uniform._array );
break;
case 'm4':
// single THREE.Matrix4
_gl.uniformMatrix4fv( location, false, value.elements );
break;
case 'm4v':
// array of THREE.Matrix4
if ( uniform._array === undefined ) {
uniform._array = new Float32Array( 16 * value.length );
}
for ( var i = 0, il = value.length; i < il; i ++ ) {
value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
}
_gl.uniformMatrix4fv( location, false, uniform._array );
break;
case 't':
// single THREE.Texture (2d or cube)
texture = value;
textureUnit = getTextureUnit();
_gl.uniform1i( location, textureUnit );
if ( ! texture ) continue;
if ( texture instanceof THREE.CubeTexture ||
( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/
setCubeTexture( texture, textureUnit );
} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
setCubeTextureDynamic( texture, textureUnit );
} else {
_this.setTexture( texture, textureUnit );
}
break;
case 'tv':
// array of THREE.Texture (2d)
if ( uniform._array === undefined ) {
uniform._array = [];
}
for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
uniform._array[ i ] = getTextureUnit();
}
_gl.uniform1iv( location, uniform._array );
for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {
texture = uniform.value[ i ];
textureUnit = uniform._array[ i ];
if ( ! texture ) continue;
_this.setTexture( texture, textureUnit );
}
break;
default:
console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );
}
}
}
function setupMatrices ( object, camera ) {
object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
object._normalMatrix.getNormalMatrix( object._modelViewMatrix );
}
//
function setColorGamma( array, offset, color, intensitySq ) {
array[ offset ] = color.r * color.r * intensitySq;
array[ offset + 1 ] = color.g * color.g * intensitySq;
array[ offset + 2 ] = color.b * color.b * intensitySq;
}
function setColorLinear( array, offset, color, intensity ) {
array[ offset ] = color.r * intensity;
array[ offset + 1 ] = color.g * intensity;
array[ offset + 2 ] = color.b * intensity;
}
function setupLights ( lights ) {
var l, ll, light, n,
r = 0, g = 0, b = 0,
color, skyColor, groundColor,
intensity, intensitySq,
position,
distance,
zlights = _lights,
dirColors = zlights.directional.colors,
dirPositions = zlights.directional.positions,
pointColors = zlights.point.colors,
pointPositions = zlights.point.positions,
pointDistances = zlights.point.distances,
spotColors = zlights.spot.colors,
spotPositions = zlights.spot.positions,
spotDistances = zlights.spot.distances,
spotDirections = zlights.spot.directions,
spotAnglesCos = zlights.spot.anglesCos,
spotExponents = zlights.spot.exponents,
hemiSkyColors = zlights.hemi.skyColors,
hemiGroundColors = zlights.hemi.groundColors,
hemiPositions = zlights.hemi.positions,
dirLength = 0,
pointLength = 0,
spotLength = 0,
hemiLength = 0,
dirCount = 0,
pointCount = 0,
spotCount = 0,
hemiCount = 0,
dirOffset = 0,
pointOffset = 0,
spotOffset = 0,
hemiOffset = 0;
for ( l = 0, ll = lights.length; l < ll; l ++ ) {
light = lights[ l ];
if ( light.onlyShadow ) continue;
color = light.color;
intensity = light.intensity;
distance = light.distance;
if ( light instanceof THREE.AmbientLight ) {
if ( ! light.visible ) continue;
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 ) {
dirCount += 1;
if ( ! light.visible ) continue;
_direction.setFromMatrixPosition( light.matrixWorld );
_vector3.setFromMatrixPosition( light.target.matrixWorld );
_direction.sub( _vector3 );
_direction.normalize();
dirOffset = dirLength * 3;
dirPositions[ dirOffset ] = _direction.x;
dirPositions[ dirOffset + 1 ] = _direction.y;
dirPositions[ dirOffset + 2 ] = _direction.z;
if ( _this.gammaInput ) {
setColorGamma( dirColors, dirOffset, color, intensity * intensity );
} else {
setColorLinear( dirColors, dirOffset, color, intensity );
}
dirLength += 1;
} else if ( light instanceof THREE.PointLight ) {
pointCount += 1;
if ( ! light.visible ) continue;
pointOffset = pointLength * 3;
if ( _this.gammaInput ) {
setColorGamma( pointColors, pointOffset, color, intensity * intensity );
} else {
setColorLinear( pointColors, pointOffset, color, intensity );
}
_vector3.setFromMatrixPosition( light.matrixWorld );
pointPositions[ pointOffset ] = _vector3.x;
pointPositions[ pointOffset + 1 ] = _vector3.y;
pointPositions[ pointOffset + 2 ] = _vector3.z;
pointDistances[ pointLength ] = distance;
pointLength += 1;
} else if ( light instanceof THREE.SpotLight ) {
spotCount += 1;
if ( ! light.visible ) continue;
spotOffset = spotLength * 3;
if ( _this.gammaInput ) {
setColorGamma( spotColors, spotOffset, color, intensity * intensity );
} else {
setColorLinear( spotColors, spotOffset, color, intensity );
}
_direction.setFromMatrixPosition( light.matrixWorld );
spotPositions[ spotOffset ] = _direction.x;
spotPositions[ spotOffset + 1 ] = _direction.y;
spotPositions[ spotOffset + 2 ] = _direction.z;
spotDistances[ spotLength ] = distance;
_vector3.setFromMatrixPosition( light.target.matrixWorld );
_direction.sub( _vector3 );
_direction.normalize();
spotDirections[ spotOffset ] = _direction.x;
spotDirections[ spotOffset + 1 ] = _direction.y;
spotDirections[ spotOffset + 2 ] = _direction.z;
spotAnglesCos[ spotLength ] = Math.cos( light.angle );
spotExponents[ spotLength ] = light.exponent;
spotLength += 1;
} else if ( light instanceof THREE.HemisphereLight ) {
hemiCount += 1;
if ( ! light.visible ) continue;
_direction.setFromMatrixPosition( light.matrixWorld );
_direction.normalize();
hemiOffset = hemiLength * 3;
hemiPositions[ hemiOffset ] = _direction.x;
hemiPositions[ hemiOffset + 1 ] = _direction.y;
hemiPositions[ hemiOffset + 2 ] = _direction.z;
skyColor = light.color;
groundColor = light.groundColor;
if ( _this.gammaInput ) {
intensitySq = intensity * intensity;
setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq );
setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq );
} else {
setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
}
hemiLength += 1;
}
}
// null eventual remains from removed lights
// (this is to avoid if in shader)
for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
zlights.directional.length = dirLength;
zlights.point.length = pointLength;
zlights.spot.length = spotLength;
zlights.hemi.length = hemiLength;
zlights.ambient[ 0 ] = r;
zlights.ambient[ 1 ] = g;
zlights.ambient[ 2 ] = b;
}
// GL state setting
this.setFaceCulling = function ( cullFace, frontFaceDirection ) {
if ( cullFace === THREE.CullFaceNone ) {
_gl.disable( _gl.CULL_FACE );
} else {
if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
_gl.frontFace( _gl.CW );
} else {
_gl.frontFace( _gl.CCW );
}
if ( cullFace === THREE.CullFaceBack ) {
_gl.cullFace( _gl.BACK );
} else if ( cullFace === THREE.CullFaceFront ) {
_gl.cullFace( _gl.FRONT );
} else {
_gl.cullFace( _gl.FRONT_AND_BACK );
}
_gl.enable( _gl.CULL_FACE );
}
};
this.setMaterialFaces = function ( material ) {
var doubleSided = material.side === THREE.DoubleSide;
var flipSided = material.side === THREE.BackSide;
if ( _oldDoubleSided !== doubleSided ) {
if ( doubleSided ) {
_gl.disable( _gl.CULL_FACE );
} else {
_gl.enable( _gl.CULL_FACE );
}
_oldDoubleSided = doubleSided;
}
if ( _oldFlipSided !== flipSided ) {
if ( flipSided ) {
_gl.frontFace( _gl.CW );
} else {
_gl.frontFace( _gl.CCW );
}
_oldFlipSided = flipSided;
}
};
this.setDepthTest = function ( depthTest ) {
if ( _oldDepthTest !== depthTest ) {
if ( depthTest ) {
_gl.enable( _gl.DEPTH_TEST );
} else {
_gl.disable( _gl.DEPTH_TEST );
}
_oldDepthTest = depthTest;
}
};
this.setDepthWrite = function ( depthWrite ) {
if ( _oldDepthWrite !== depthWrite ) {
_gl.depthMask( depthWrite );
_oldDepthWrite = depthWrite;
}
};
function setLineWidth ( width ) {
if ( width !== _oldLineWidth ) {
_gl.lineWidth( width );
_oldLineWidth = width;
}
}
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;
}
}
this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
if ( blending !== _oldBlending ) {
if ( blending === THREE.NoBlending ) {
_gl.disable( _gl.BLEND );
} else if ( blending === THREE.AdditiveBlending ) {
_gl.enable( _gl.BLEND );
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
} else if ( blending === THREE.SubtractiveBlending ) {
// TODO: Find blendFuncSeparate() combination
_gl.enable( _gl.BLEND );
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
} else if ( blending === THREE.MultiplyBlending ) {
// TODO: Find blendFuncSeparate() combination
_gl.enable( _gl.BLEND );
_gl.blendEquation( _gl.FUNC_ADD );
_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
} else if ( blending === THREE.CustomBlending ) {
_gl.enable( _gl.BLEND );
} else {
_gl.enable( _gl.BLEND );
_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 );
}
_oldBlending = blending;
}
if ( blending === THREE.CustomBlending ) {
if ( blendEquation !== _oldBlendEquation ) {
_gl.blendEquation( paramThreeToGL( blendEquation ) );
_oldBlendEquation = blendEquation;
}
if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) {
_gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) );
_oldBlendSrc = blendSrc;
_oldBlendDst = blendDst;
}
} else {
_oldBlendEquation = null;
_oldBlendSrc = null;
_oldBlendDst = null;
}
};
// Textures
function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
var extension;
if ( isImagePowerOfTwo ) {
_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 ) );
} 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 ) );
}
extension = extensions.get( 'EXT_texture_filter_anisotropic' );
if ( extension && texture.type !== THREE.FloatType ) {
if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
_gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) );
texture.__oldAnisotropy = texture.anisotropy;
}
}
}
this.uploadTexture = function ( texture ) {
if ( texture.__webglInit === undefined ) {
texture.__webglInit = true;
texture.addEventListener( 'dispose', onTextureDispose );
texture.__webglTexture = _gl.createTexture();
_this.info.memory.textures ++;
}
_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
texture.image = clampToMaxSize( texture.image, _maxTextureSize );
var image = texture.image,
isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
glFormat = paramThreeToGL( texture.format ),
glType = paramThreeToGL( texture.type );
setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
var mipmap, mipmaps = texture.mipmaps;
if ( texture instanceof THREE.DataTexture ) {
// use manually created mipmaps if available
// if there are no manual mipmaps
// set 0 level mipmap and then use GL to generate other mipmap levels
if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
mipmap = mipmaps[ i ];
_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
}
texture.generateMipmaps = false;
} else {
_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
}
} else if ( texture instanceof THREE.CompressedTexture ) {
for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
mipmap = mipmaps[ i ];
if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) {
_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
} else {
console.warn( "Attempt to load unsupported compressed texture format" );
}
} else {
_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
}
}
} else { // regular Texture (image, video, canvas)
// use manually created mipmaps if available
// if there are no manual mipmaps
// set 0 level mipmap and then use GL to generate other mipmap levels
if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
mipmap = mipmaps[ i ];
_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
}
texture.generateMipmaps = false;
} else {
_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
}
}
if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
texture.needsUpdate = false;
if ( texture.onUpdate ) texture.onUpdate();
};
this.setTexture = function ( texture, slot ) {
_gl.activeTexture( _gl.TEXTURE0 + slot );
if ( texture.needsUpdate ) {
_this.uploadTexture( texture );
} else {
_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
}
};
function clampToMaxSize ( image, maxSize ) {
if ( image.width > maxSize || image.height > maxSize ) {
// Warning: Scaling through the canvas will only work with images that use
// premultiplied alpha.
var scale = maxSize / Math.max( image.width, image.height );
var canvas = document.createElement( 'canvas' );
canvas.width = Math.floor( image.width * scale );
canvas.height = Math.floor( image.height * scale );
var context = canvas.getContext( '2d' );
context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );
console.log( 'THREE.WebGLRenderer:', image, 'is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height + '.' );
return canvas;
}
return image;
}
function setCubeTexture ( texture, slot ) {
if ( texture.image.length === 6 ) {
if ( texture.needsUpdate ) {
if ( ! texture.image.__webglTextureCube ) {
texture.addEventListener( 'dispose', onTextureDispose );
texture.image.__webglTextureCube = _gl.createTexture();
_this.info.memory.textures ++;
}
_gl.activeTexture( _gl.TEXTURE0 + slot );
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
var isCompressed = texture instanceof THREE.CompressedTexture;
var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture;
var cubeImage = [];
for ( var i = 0; i < 6; i ++ ) {
if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) {
cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
} else {
cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
}
}
var image = cubeImage[ 0 ],
isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
glFormat = paramThreeToGL( texture.format ),
glType = paramThreeToGL( texture.type );
setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
for ( var i = 0; i < 6; i ++ ) {
if ( ! isCompressed ) {
if ( isDataTexture ) {
_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
} else {
_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
}
} else {
var mipmap, mipmaps = cubeImage[ i ].mipmaps;
for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
mipmap = mipmaps[ j ];
if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {
if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) {
_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
} else {
console.warn( "Attempt to load unsupported compressed texture format" );
}
} else {
_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
}
}
}
}
if ( texture.generateMipmaps && isImagePowerOfTwo ) {
_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
}
texture.needsUpdate = false;
if ( texture.onUpdate ) texture.onUpdate();
} 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 );
}
}
this.setRenderTarget = function ( renderTarget ) {
var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) {
if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
renderTarget.__webglTexture = _gl.createTexture();
_this.info.memory.textures ++;
// Setup texture, create render and frame buffers
var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ),
glFormat = paramThreeToGL( renderTarget.format ),
glType = paramThreeToGL( renderTarget.type );
if ( isCube ) {
renderTarget.__webglFramebuffer = [];
renderTarget.__webglRenderbuffer = [];
_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo );
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, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget );
}
if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
} else {
renderTarget.__webglFramebuffer = _gl.createFramebuffer();
if ( renderTarget.shareDepthFrom ) {
renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;
} else {
renderTarget.__webglRenderbuffer = _gl.createRenderbuffer();
}
_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo );
_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
if ( renderTarget.shareDepthFrom ) {
if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
}
} else {
setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget );
}
if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
}
// 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;
}
_currentWidth = width;
_currentHeight = height;
};
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 ) {
if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
return _gl.NEAREST;
}
return _gl.LINEAR;
}
// Map three.js constants to WebGL constants
function paramThreeToGL ( p ) {
var extension;
if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
if ( p === THREE.NearestFilter ) return _gl.NEAREST;
if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
if ( p === THREE.LinearFilter ) return _gl.LINEAR;
if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
if ( p === THREE.ByteType ) return _gl.BYTE;
if ( p === THREE.ShortType ) return _gl.SHORT;
if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
if ( p === THREE.IntType ) return _gl.INT;
if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
if ( p === THREE.FloatType ) return _gl.FLOAT;
if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
if ( p === THREE.RGBFormat ) return _gl.RGB;
if ( p === THREE.RGBAFormat ) return _gl.RGBA;
if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
if ( p === THREE.ZeroFactor ) return _gl.ZERO;
if ( p === THREE.OneFactor ) return _gl.ONE;
if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
if ( extension !== null ) {
if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
if ( extension !== null ) {
if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
}
extension = extensions.get( 'EXT_blend_minmax' );
if ( extension !== null ) {
if ( p === THREE.MinEquation ) return extension.MIN_EXT;
if ( p === THREE.MaxEquation ) return extension.MAX_EXT;
}
return 0;
}
// Allocations
function allocateBones ( object ) {
if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {
return 1024;
} else {
// 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 nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS );
var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
var maxBones = nVertexMatrices;
if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
maxBones = Math.min( object.skeleton.bones.length, maxBones );
if ( maxBones < object.skeleton.bones.length ) {
console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );
}
}
return maxBones;
}
}
function allocateLights( lights ) {
var dirLights = 0;
var pointLights = 0;
var spotLights = 0;
var hemiLights = 0;
for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
var light = lights[ l ];
if ( light.onlyShadow || light.visible === false ) continue;
if ( light instanceof THREE.DirectionalLight ) dirLights ++;
if ( light instanceof THREE.PointLight ) pointLights ++;
if ( light instanceof THREE.SpotLight ) spotLights ++;
if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
}
return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights };
}
function allocateShadows( lights ) {
var maxShadows = 0;
for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
var light = lights[ l ];
if ( ! light.castShadow ) continue;
if ( light instanceof THREE.SpotLight ) maxShadows ++;
if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++;
}
return maxShadows;
}
// DEPRECATED
this.initMaterial = function () {
console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
};
this.addPrePlugin = function () {
console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
};
this.addPostPlugin = function () {
console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
};
this.updateShadowMap = function () {
console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
};
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment