@bitowl/three-instanced-mesh
Version:
Scene graph level abstraction for three.js InstancedBufferGeometry
766 lines (524 loc) • 19.1 kB
JavaScript
(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]);