UNPKG

@bitowl/three-instanced-mesh

Version:

Scene graph level abstraction for three.js InstancedBufferGeometry

766 lines (524 loc) 19.1 kB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ require('./index')(THREE) },{"./index":2}],2:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ module.exports = function (THREE) { const differentSignature = parseInt(THREE.REVISION) >= 96 //monkeypatch shaders require('./monkey-patch.js')(THREE); //depth mat var DEPTH_MATERIAL = new THREE.MeshDepthMaterial(); DEPTH_MATERIAL.depthPacking = THREE.RGBADepthPacking; DEPTH_MATERIAL.clipping = true; DEPTH_MATERIAL.defines = { INSTANCE_TRANSFORM: '' }; //distance mat var DISTANCE_SHADER = THREE.ShaderLib["distanceRGBA"], DISTANCE_UNIFORMS = THREE.UniformsUtils.clone(DISTANCE_SHADER.uniforms), DISTANCE_DEFINES = { 'USE_SHADOWMAP': '', 'INSTANCE_TRANSFORM': '' }, DISTANCE_MATERIAL = new THREE.ShaderMaterial({ defines: DISTANCE_DEFINES, uniforms: DISTANCE_UNIFORMS, vertexShader: DISTANCE_SHADER.vertexShader, fragmentShader: DISTANCE_SHADER.fragmentShader, clipping: true }) ; //main class THREE.ThreeInstancedMesh = function ( bufferGeometry, material, numInstances, dynamic, colors, uniformScale ) { debugger; THREE.Mesh.call(this, (new THREE.InstancedBufferGeometry()).copy(bufferGeometry)); //hacky for now this._dynamic = !!dynamic; //TODO: set a bit mask for different attributes? this._uniformScale = !!uniformScale; this._colors = !!colors; this.numInstances = numInstances; this._setAttributes(); /** * use the setter to decorate this material * this is in lieu of changing the renderer * WebGLRenderer injects stuff like this */ this.material = material.clone(); this.frustumCulled = false; //you can uncheck this if you generate your own bounding info //make it work with depth effects this.customDepthMaterial = DEPTH_MATERIAL; this.customDistanceMaterial = DISTANCE_MATERIAL; } THREE.ThreeInstancedMesh.prototype = Object.create(THREE.Mesh.prototype); THREE.ThreeInstancedMesh.constructor = THREE.ThreeInstancedMesh; //this is kinda gnarly, done in order to avoid setting these defines in the WebGLRenderer (it manages most if not all of the define flags) Object.defineProperties(THREE.ThreeInstancedMesh.prototype, { 'material': { set: function (m) { /** * whenever a material is set, decorate it, * if a material used with regular geometry is passed, * it will mutate it which is bad mkay * * either flag Material with these instance properties: * * "i want to create a RED PLASTIC material that will * be INSTANCED and i know it will be used on clones * that are known to be UNIFORMly scaled" * (also figure out where dynamic fits here) * * or check here if the material has INSTANCE_TRANSFORM * define set, if not, clone, document that it breaks reference * or do a shallow copy or something * * or something else? */ m = m.clone(); if (m.defines) { m.defines.INSTANCE_TRANSFORM = ''; if (this._uniformScale) m.defines.INSTANCE_UNIFORM = ''; //an optimization, should avoid doing an expensive matrix inverse in the shader else delete m.defines['INSTANCE_UNIFORM']; if (this._colors) m.defines.INSTANCE_COLOR = ''; else delete m.defines['INSTANCE_COLOR']; } else { m.defines = { INSTANCE_TRANSFORM: '' }; if (this._uniformScale) m.defines.INSTANCE_UNIFORM = ''; if (this._colors) m.defines.INSTANCE_COLOR = ''; } this._material = m; }, get: function () { return this._material; } }, //force new attributes to be created when set? 'numInstances': { set: function (v) { this._numInstances = v; //reset buffers this._setAttributes(); }, get: function () { return this._numInstances; } }, //do some auto-magic when BufferGeometry is set //TODO: account for Geometry, or change this approach completely 'geometry': { set: function (g) { //if its not already instanced attach buffers if (!!g.attributes.instancePosition) { this._geometry = new THREE.InstancedBufferGeometry(); this._setAttributes(); } else this._geometry = g; }, get: function () { return this._geometry; } } }); THREE.ThreeInstancedMesh.prototype.setPositionAt = function (index, position) { this.geometry.attributes.instancePosition.setXYZ(index, position.x, position.y, position.z); }; THREE.ThreeInstancedMesh.prototype.setQuaternionAt = function (index, quat) { this.geometry.attributes.instanceQuaternion.setXYZW(index, quat.x, quat.y, quat.z, quat.w); }; THREE.ThreeInstancedMesh.prototype.setScaleAt = function (index, scale) { this.geometry.attributes.instanceScale.setXYZ(index, scale.x, scale.y, scale.z); }; THREE.ThreeInstancedMesh.prototype.setColorAt = function (index, color) { if (!this._colors) { console.warn('THREE.ThreeInstancedMesh: color not enabled'); return; } this.geometry.attributes.instanceColor.setXYZ( index, Math.floor(color.r * 255), Math.floor(color.g * 255), Math.floor(color.b * 255) ); }; THREE.ThreeInstancedMesh.prototype.getPositionAt = function (index, position) { var arr = this.geometry.attributes.instancePosition.array; index *= 3; return position ? position.set(arr[index++], arr[index++], arr[index]) : new THREE.Vector3(arr[index++], arr[index++], arr[index]) ; }; THREE.ThreeInstancedMesh.prototype.getQuaternionAt = function (index, quat) { var arr = this.geometry.attributes.instanceQuaternion.array; index = index << 2; return quat ? quat.set(arr[index++], arr[index++], arr[index++], arr[index]) : new THREE.Quaternion(arr[index++], arr[index++], arr[index++], arr[index]) ; }; THREE.ThreeInstancedMesh.prototype.getScaleAt = function (index, scale) { var arr = this.geometry.attributes.instanceScale.array; index *= 3; return scale ? scale.set(arr[index++], arr[index++], arr[index]) : new THREE.Vector3(arr[index++], arr[index++], arr[index]) ; }; THREE.ThreeInstancedMesh.prototype.getColorAt = (function () { var inv255 = 1 / 255; return function (index, color) { if (!this._colors) { console.warn('THREE.ThreeInstancedMesh: color not enabled'); return false; } var arr = this.geometry.attributes.instanceColor.array; index *= 3; return color ? color.setRGB(arr[index++] * inv255, arr[index++] * inv255, arr[index] * inv255) : new THREE.Vector3(arr[index++], arr[index++], arr[index]).multiplyScalar(inv255) ; }; })() THREE.ThreeInstancedMesh.prototype.needsUpdate = function (attribute) { switch (attribute) { case 'position': this.geometry.attributes.instancePosition.needsUpdate = true; break; case 'quaternion': this.geometry.attributes.instanceQuaternion.needsUpdate = true; break; case 'scale': this.geometry.attributes.instanceScale.needsUpdate = true; break; case 'colors': this.geometry.attributes.instanceColor.needsUpdate = true; break; default: this.geometry.attributes.instancePosition.needsUpdate = true; this.geometry.attributes.instanceQuaternion.needsUpdate = true; this.geometry.attributes.instanceScale.needsUpdate = true; if (this._colors) { this.geometry.attributes.instanceColor.needsUpdate = true; } break; } }; THREE.ThreeInstancedMesh.prototype._setAttributes = function () { var normalized = true var meshPerAttribute = 1 var vec4Size = 4 var vec3Size = 3 var attributes = { instancePosition: [ new Float32Array(this.numInstances * vec3Size), vec3Size, !normalized, meshPerAttribute, ], instanceQuaternion: [ new Float32Array(this.numInstances * vec4Size), vec4Size, !normalized, meshPerAttribute, ], instanceScale: [ new Float32Array(this.numInstances * vec3Size), vec3Size, !normalized, meshPerAttribute, ] } if (this._colors) { attributes.instanceColor = [ new Uint8Array(this.numInstances * vec3Size), vec3Size, normalized, meshPerAttribute, ] } Object.keys(attributes).forEach(name => { const a = attributes[name] let attribute if (differentSignature) { attribute = new THREE.InstancedBufferAttribute(...a) } else { attribute = new THREE.InstancedBufferAttribute(a[0], a[1], a[3]) attribute.normalized = a[2] } attribute.setUsage(this._dynamic ? THREE.DynamicDrawUsage : THREE.StaticDrawUsage) this.geometry.setAttribute(name, attribute) }) }; return THREE.ThreeInstancedMesh; }; },{"./monkey-patch.js":3}],3:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ module.exports = function (THREE) { if (THREE.monkeyPatchApplied) return THREE; require('./monkey-patch/index.js')(THREE); THREE.monkeyPatchApplied = true; return THREE; } },{"./monkey-patch/index.js":9}],4:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ // transform vertices with the transform matrix module.exports = [ "#ifndef INSTANCE_TRANSFORM", "vec3 transformed = vec3( position );", "#else", "#ifndef INSTANCE_MATRIX", "mat4 _instanceMatrix = getInstanceMatrix();", "#define INSTANCE_MATRIX", "#endif", "vec3 transformed = ( _instanceMatrix * vec4( position , 1. )).xyz;", "#endif", ].join("\n") },{}],5:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ // multiply the color with per instance color if enabled module.exports = [ '#ifdef USE_COLOR', 'diffuseColor.rgb *= vColor;', '#endif', '#if defined(INSTANCE_COLOR)', 'diffuseColor.rgb *= vInstanceColor;', '#endif' ].join("\n") },{}],6:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ // add fragment varying if feature enabled module.exports = [ "#ifdef USE_COLOR", "varying vec3 vColor;", "#endif", "#if defined( INSTANCE_COLOR )", "varying vec3 vInstanceColor;", "#endif" ].join("\n") },{}],7:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ // read per instance color from attribute, pass to varying module.exports = [ "#ifdef USE_COLOR", "vColor.xyz = color.xyz;", "#endif", "#if defined( INSTANCE_COLOR ) && defined( INSTANCE_TRANSFORM )", "vInstanceColor = instanceColor;", "#endif", ].join("\n") },{}],8:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ module.exports = [ "#ifdef FLIP_SIDED", "objectNormal = -objectNormal;", "#endif", "#ifndef INSTANCE_TRANSFORM", "vec3 transformedNormal = normalMatrix * objectNormal;", "#else", "#ifndef INSTANCE_MATRIX ", "mat4 _instanceMatrix = getInstanceMatrix();", "#define INSTANCE_MATRIX", "#endif", "#ifndef INSTANCE_UNIFORM", "vec3 transformedNormal = transposeMat3( inverse( mat3( modelViewMatrix * _instanceMatrix ) ) ) * objectNormal ;", "#else", "vec3 transformedNormal = ( modelViewMatrix * _instanceMatrix * vec4( objectNormal , 0.0 ) ).xyz;", "#endif", "#endif" ].join("\n"); },{}],9:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ module.exports = function( THREE ){ //patches these methods and shader chunks with the required logic THREE.ShaderChunk[ 'begin_vertex' ] = require('./begin_vertex.glsl.js'); THREE.ShaderChunk[ 'color_fragment' ] = require('./color_fragment.glsl.js'); THREE.ShaderChunk[ 'color_pars_fragment' ] = require('./color_pars_fragment.glsl.js'); THREE.ShaderChunk[ 'color_vertex' ] = require('./color_vertex.glsl.js'); THREE.ShaderChunk[ 'defaultnormal_vertex' ] = require('./defaultnormal_vertex.glsl.js'); THREE.ShaderChunk[ 'uv_pars_vertex' ] = require('./uv_pars_vertex.glsl.js'); // patches the matcap shaders THREE.ShaderLib.matcap = { uniforms: THREE.UniformsUtils.merge([ THREE.UniformsLib.common, THREE.UniformsLib.bumpmap, THREE.UniformsLib.normalmap, THREE.UniformsLib.displacementmap, THREE.UniformsLib.fog, { matcap: { value: null } } ]), vertexShader: require('./meshmatcap_vert.glsl.js'), fragmentShader: require('./meshmatcap_frag.glsl.js') }; } },{"./begin_vertex.glsl.js":4,"./color_fragment.glsl.js":5,"./color_pars_fragment.glsl.js":6,"./color_vertex.glsl.js":7,"./defaultnormal_vertex.glsl.js":8,"./meshmatcap_frag.glsl.js":10,"./meshmatcap_vert.glsl.js":11,"./uv_pars_vertex.glsl.js":12}],10:[function(require,module,exports){ // copy of https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshmatcap_frag.glsl.js module.exports = [ "#define MATCAP", "uniform vec3 diffuse;", "uniform float opacity;", "uniform sampler2D matcap;", "varying vec3 vViewPosition;", "#ifndef FLAT_SHADED", " varying vec3 vNormal;", "#endif", "#include <common>", "#include <color_pars_fragment>", "#include <uv_pars_fragment>", "#include <map_pars_fragment>", "#include <alphamap_pars_fragment>", "#include <fog_pars_fragment>", "#include <bumpmap_pars_fragment>", "#include <normalmap_pars_fragment>", "#include <logdepthbuf_pars_fragment>", "#include <clipping_planes_pars_fragment>", "void main() {", " #include <clipping_planes_fragment>", " vec4 diffuseColor = vec4( diffuse, opacity );", " #include <logdepthbuf_fragment>", " #include <map_fragment>", " #include <color_fragment>", " #include <alphamap_fragment>", " #include <alphatest_fragment>", " #include <normal_fragment_begin>", " #include <normal_fragment_maps>", " vec3 viewDir = normalize( vViewPosition );", " vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );", " vec3 y = cross( viewDir, x );", " vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks", " #ifdef USE_MATCAP", " vec4 matcapColor = texture2D( matcap, uv );", " matcapColor = matcapTexelToLinear( matcapColor );", " #else", " vec4 matcapColor = vec4( 1.0 );", " #endif", " vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;", " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", " #include <premultiplied_alpha_fragment>", " #include <tonemapping_fragment>", " #include <encodings_fragment>", " #include <fog_fragment>", "}"].join("\n"); },{}],11:[function(require,module,exports){ // copy of https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshmatcap_vert.glsl.js module.exports = [ "#define MATCAP", "", "varying vec3 vViewPosition;", "", "#ifndef FLAT_SHADED", "", " varying vec3 vNormal;", "", "#endif", "", "#include <common>", "#include <uv_pars_vertex>", "#include <displacementmap_pars_vertex>", "#include <fog_pars_vertex>", "#include <morphtarget_pars_vertex>", "#include <skinning_pars_vertex>", "", "#include <logdepthbuf_pars_vertex>", "#include <clipping_planes_pars_vertex>", "", "void main() {", "", " #include <uv_vertex>", " #include <color_vertex>", " ", " #include <beginnormal_vertex>", " #include <morphnormal_vertex>", " #include <skinbase_vertex>", " #include <skinnormal_vertex>", " #include <defaultnormal_vertex>", "", " #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED", "", " vNormal = normalize( transformedNormal );", "", " #endif", "", " #include <begin_vertex>", " #include <morphtarget_vertex>", " #include <skinning_vertex>", " #include <displacementmap_vertex>", " #include <project_vertex>", "", " #include <logdepthbuf_vertex>", " #include <clipping_planes_vertex>", " #include <fog_vertex>", "", " vViewPosition = - mvPosition.xyz;", "", "}"].join("\n"); },{}],12:[function(require,module,exports){ /************************** * Dusan Bosnjak @pailhead **************************/ module.exports = [ "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )", "varying vec2 vUv;", "uniform mat3 uvTransform;", "#endif", "#ifdef INSTANCE_TRANSFORM", "mat3 inverse(mat3 m) {", "float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];", "float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];", "float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];", "float b01 = a22 * a11 - a12 * a21;", "float b11 = -a22 * a10 + a12 * a20;", "float b21 = a21 * a10 - a11 * a20;", "float det = a00 * b01 + a01 * b11 + a02 * b21;", "return mat3(b01, (-a22 * a01 + a02 * a21), ( a12 * a01 - a02 * a11),", "b11, ( a22 * a00 - a02 * a20), (-a12 * a00 + a02 * a10),", "b21, (-a21 * a00 + a01 * a20), ( a11 * a00 - a01 * a10)) / det;", "}", //for dynamic, avoid computing the matrices on the cpu "attribute vec3 instancePosition;", "attribute vec4 instanceQuaternion;", "attribute vec3 instanceScale;", "#if defined( INSTANCE_COLOR )", "attribute vec3 instanceColor;", "varying vec3 vInstanceColor;", "#endif", "mat4 getInstanceMatrix(){", "vec4 q = instanceQuaternion;", "vec3 s = instanceScale;", "vec3 v = instancePosition;", "vec3 q2 = q.xyz + q.xyz;", "vec3 a = q.xxx * q2.xyz;", "vec3 b = q.yyz * q2.yzz;", "vec3 c = q.www * q2.xyz;", "vec3 r0 = vec3( 1.0 - (b.x + b.z) , a.y + c.z , a.z - c.y ) * s.xxx;", "vec3 r1 = vec3( a.y - c.z , 1.0 - (a.x + b.z) , b.y + c.x ) * s.yyy;", "vec3 r2 = vec3( a.z + c.y , b.y - c.x , 1.0 - (a.x + b.x) ) * s.zzz;", "return mat4(", "r0 , 0.0,", "r1 , 0.0,", "r2 , 0.0,", "v , 1.0", ");", "}", "#endif" ].join("\n"); },{}]},{},[1]);