添加关照、全局等高线、修改图层问题
This commit is contained in:
215
static/sdk/three/jsm/renderers/CSS2DRenderer.js
Normal file
215
static/sdk/three/jsm/renderers/CSS2DRenderer.js
Normal file
@ -0,0 +1,215 @@
|
||||
import {
|
||||
Matrix4,
|
||||
Object3D,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
class CSS2DObject extends Object3D {
|
||||
|
||||
constructor( element = document.createElement( 'div' ) ) {
|
||||
|
||||
super();
|
||||
|
||||
this.isCSS2DObject = true;
|
||||
|
||||
this.element = element;
|
||||
|
||||
this.element.style.position = 'absolute';
|
||||
this.element.style.userSelect = 'none';
|
||||
|
||||
this.element.setAttribute( 'draggable', false );
|
||||
|
||||
this.center = new Vector2( 0.5, 0.5 ); // ( 0, 0 ) is the lower left; ( 1, 1 ) is the top right
|
||||
|
||||
this.addEventListener( 'removed', function () {
|
||||
|
||||
this.traverse( function ( object ) {
|
||||
|
||||
if ( object.element instanceof Element && object.element.parentNode !== null ) {
|
||||
|
||||
object.element.parentNode.removeChild( object.element );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.element = source.element.cloneNode( true );
|
||||
|
||||
this.center = source.center;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const _vector = new Vector3();
|
||||
const _viewMatrix = new Matrix4();
|
||||
const _viewProjectionMatrix = new Matrix4();
|
||||
const _a = new Vector3();
|
||||
const _b = new Vector3();
|
||||
|
||||
class CSS2DRenderer {
|
||||
|
||||
constructor( parameters = {} ) {
|
||||
|
||||
const _this = this;
|
||||
|
||||
let _width, _height;
|
||||
let _widthHalf, _heightHalf;
|
||||
|
||||
const cache = {
|
||||
objects: new WeakMap()
|
||||
};
|
||||
|
||||
const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
|
||||
|
||||
domElement.style.overflow = 'hidden';
|
||||
|
||||
this.domElement = domElement;
|
||||
|
||||
this.getSize = function () {
|
||||
|
||||
return {
|
||||
width: _width,
|
||||
height: _height
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
_viewMatrix.copy( camera.matrixWorldInverse );
|
||||
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
|
||||
|
||||
renderObject( scene, scene, camera );
|
||||
zOrder( scene );
|
||||
|
||||
};
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
_widthHalf = _width / 2;
|
||||
_heightHalf = _height / 2;
|
||||
|
||||
domElement.style.width = width + 'px';
|
||||
domElement.style.height = height + 'px';
|
||||
|
||||
};
|
||||
|
||||
function renderObject( object, scene, camera ) {
|
||||
|
||||
if ( object.isCSS2DObject ) {
|
||||
|
||||
_vector.setFromMatrixPosition( object.matrixWorld );
|
||||
_vector.applyMatrix4( _viewProjectionMatrix );
|
||||
|
||||
const visible = ( object.visible === true ) && ( _vector.z >= - 1 && _vector.z <= 1 ) && ( object.layers.test( camera.layers ) === true );
|
||||
object.element.style.display = ( visible === true ) ? '' : 'none';
|
||||
|
||||
if ( visible === true ) {
|
||||
|
||||
object.onBeforeRender( _this, scene, camera );
|
||||
|
||||
const element = object.element;
|
||||
|
||||
element.style.transform = 'translate(' + ( - 100 * object.center.x ) + '%,' + ( - 100 * object.center.y ) + '%)' + 'translate(' + ( _vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - _vector.y * _heightHalf + _heightHalf ) + 'px)';
|
||||
|
||||
if ( element.parentNode !== domElement ) {
|
||||
|
||||
domElement.appendChild( element );
|
||||
|
||||
}
|
||||
|
||||
object.onAfterRender( _this, scene, camera );
|
||||
|
||||
}
|
||||
|
||||
const objectData = {
|
||||
distanceToCameraSquared: getDistanceToSquared( camera, object )
|
||||
};
|
||||
|
||||
cache.objects.set( object, objectData );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0, l = object.children.length; i < l; i ++ ) {
|
||||
|
||||
renderObject( object.children[ i ], scene, camera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getDistanceToSquared( object1, object2 ) {
|
||||
|
||||
_a.setFromMatrixPosition( object1.matrixWorld );
|
||||
_b.setFromMatrixPosition( object2.matrixWorld );
|
||||
|
||||
return _a.distanceToSquared( _b );
|
||||
|
||||
}
|
||||
|
||||
function filterAndFlatten( scene ) {
|
||||
|
||||
const result = [];
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
if ( object.isCSS2DObject ) result.push( object );
|
||||
|
||||
} );
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function zOrder( scene ) {
|
||||
|
||||
const sorted = filterAndFlatten( scene ).sort( function ( a, b ) {
|
||||
|
||||
if ( a.renderOrder !== b.renderOrder ) {
|
||||
|
||||
return b.renderOrder - a.renderOrder;
|
||||
|
||||
}
|
||||
|
||||
const distanceA = cache.objects.get( a ).distanceToCameraSquared;
|
||||
const distanceB = cache.objects.get( b ).distanceToCameraSquared;
|
||||
|
||||
return distanceA - distanceB;
|
||||
|
||||
} );
|
||||
|
||||
const zMax = sorted.length;
|
||||
|
||||
for ( let i = 0, l = sorted.length; i < l; i ++ ) {
|
||||
|
||||
sorted[ i ].element.style.zIndex = zMax - i;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CSS2DObject, CSS2DRenderer };
|
329
static/sdk/three/jsm/renderers/CSS3DRenderer.js
Normal file
329
static/sdk/three/jsm/renderers/CSS3DRenderer.js
Normal file
@ -0,0 +1,329 @@
|
||||
import {
|
||||
Matrix4,
|
||||
Object3D,
|
||||
Quaternion,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
|
||||
*/
|
||||
|
||||
const _position = new Vector3();
|
||||
const _quaternion = new Quaternion();
|
||||
const _scale = new Vector3();
|
||||
|
||||
class CSS3DObject extends Object3D {
|
||||
|
||||
constructor( element = document.createElement( 'div' ) ) {
|
||||
|
||||
super();
|
||||
|
||||
this.isCSS3DObject = true;
|
||||
|
||||
this.element = element;
|
||||
this.element.style.position = 'absolute';
|
||||
this.element.style.pointerEvents = 'auto';
|
||||
this.element.style.userSelect = 'none';
|
||||
|
||||
this.element.setAttribute( 'draggable', false );
|
||||
|
||||
this.addEventListener( 'removed', function () {
|
||||
|
||||
this.traverse( function ( object ) {
|
||||
|
||||
if ( object.element instanceof Element && object.element.parentNode !== null ) {
|
||||
|
||||
object.element.parentNode.removeChild( object.element );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.element = source.element.cloneNode( true );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CSS3DSprite extends CSS3DObject {
|
||||
|
||||
constructor( element ) {
|
||||
|
||||
super( element );
|
||||
|
||||
this.isCSS3DSprite = true;
|
||||
|
||||
this.rotation2D = 0;
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.rotation2D = source.rotation2D;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const _matrix = new Matrix4();
|
||||
const _matrix2 = new Matrix4();
|
||||
|
||||
class CSS3DRenderer {
|
||||
|
||||
constructor( parameters = {} ) {
|
||||
|
||||
const _this = this;
|
||||
|
||||
let _width, _height;
|
||||
let _widthHalf, _heightHalf;
|
||||
|
||||
const cache = {
|
||||
camera: { style: '' },
|
||||
objects: new WeakMap()
|
||||
};
|
||||
|
||||
const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
|
||||
|
||||
domElement.style.overflow = 'hidden';
|
||||
|
||||
this.domElement = domElement;
|
||||
|
||||
const viewElement = document.createElement( 'div' );
|
||||
viewElement.style.transformOrigin = '0 0';
|
||||
viewElement.style.pointerEvents = 'none';
|
||||
domElement.appendChild( viewElement );
|
||||
|
||||
const cameraElement = document.createElement( 'div' );
|
||||
|
||||
cameraElement.style.transformStyle = 'preserve-3d';
|
||||
|
||||
viewElement.appendChild( cameraElement );
|
||||
|
||||
this.getSize = function () {
|
||||
|
||||
return {
|
||||
width: _width,
|
||||
height: _height
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
const fov = camera.projectionMatrix.elements[ 5 ] * _heightHalf;
|
||||
|
||||
if ( camera.view && camera.view.enabled ) {
|
||||
|
||||
// view offset
|
||||
viewElement.style.transform = `translate( ${ - camera.view.offsetX * ( _width / camera.view.width ) }px, ${ - camera.view.offsetY * ( _height / camera.view.height ) }px )`;
|
||||
|
||||
// view fullWidth and fullHeight, view width and height
|
||||
viewElement.style.transform += `scale( ${ camera.view.fullWidth / camera.view.width }, ${ camera.view.fullHeight / camera.view.height } )`;
|
||||
|
||||
} else {
|
||||
|
||||
viewElement.style.transform = '';
|
||||
|
||||
}
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
let tx, ty;
|
||||
|
||||
if ( camera.isOrthographicCamera ) {
|
||||
|
||||
tx = - ( camera.right + camera.left ) / 2;
|
||||
ty = ( camera.top + camera.bottom ) / 2;
|
||||
|
||||
}
|
||||
|
||||
const scaleByViewOffset = camera.view && camera.view.enabled ? camera.view.height / camera.view.fullHeight : 1;
|
||||
const cameraCSSMatrix = camera.isOrthographicCamera ?
|
||||
`scale( ${ scaleByViewOffset } )` + 'scale(' + fov + ')' + 'translate(' + epsilon( tx ) + 'px,' + epsilon( ty ) + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ) :
|
||||
`scale( ${ scaleByViewOffset } )` + 'translateZ(' + fov + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse );
|
||||
const perspective = camera.isPerspectiveCamera ? 'perspective(' + fov + 'px) ' : '';
|
||||
|
||||
const style = perspective + cameraCSSMatrix +
|
||||
'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)';
|
||||
|
||||
if ( cache.camera.style !== style ) {
|
||||
|
||||
cameraElement.style.transform = style;
|
||||
|
||||
cache.camera.style = style;
|
||||
|
||||
}
|
||||
|
||||
renderObject( scene, scene, camera, cameraCSSMatrix );
|
||||
|
||||
};
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
_widthHalf = _width / 2;
|
||||
_heightHalf = _height / 2;
|
||||
|
||||
domElement.style.width = width + 'px';
|
||||
domElement.style.height = height + 'px';
|
||||
|
||||
viewElement.style.width = width + 'px';
|
||||
viewElement.style.height = height + 'px';
|
||||
|
||||
cameraElement.style.width = width + 'px';
|
||||
cameraElement.style.height = height + 'px';
|
||||
|
||||
};
|
||||
|
||||
function epsilon( value ) {
|
||||
|
||||
return Math.abs( value ) < 1e-10 ? 0 : value;
|
||||
|
||||
}
|
||||
|
||||
function getCameraCSSMatrix( matrix ) {
|
||||
|
||||
const elements = matrix.elements;
|
||||
|
||||
return 'matrix3d(' +
|
||||
epsilon( elements[ 0 ] ) + ',' +
|
||||
epsilon( - elements[ 1 ] ) + ',' +
|
||||
epsilon( elements[ 2 ] ) + ',' +
|
||||
epsilon( elements[ 3 ] ) + ',' +
|
||||
epsilon( elements[ 4 ] ) + ',' +
|
||||
epsilon( - elements[ 5 ] ) + ',' +
|
||||
epsilon( elements[ 6 ] ) + ',' +
|
||||
epsilon( elements[ 7 ] ) + ',' +
|
||||
epsilon( elements[ 8 ] ) + ',' +
|
||||
epsilon( - elements[ 9 ] ) + ',' +
|
||||
epsilon( elements[ 10 ] ) + ',' +
|
||||
epsilon( elements[ 11 ] ) + ',' +
|
||||
epsilon( elements[ 12 ] ) + ',' +
|
||||
epsilon( - elements[ 13 ] ) + ',' +
|
||||
epsilon( elements[ 14 ] ) + ',' +
|
||||
epsilon( elements[ 15 ] ) +
|
||||
')';
|
||||
|
||||
}
|
||||
|
||||
function getObjectCSSMatrix( matrix ) {
|
||||
|
||||
const elements = matrix.elements;
|
||||
const matrix3d = 'matrix3d(' +
|
||||
epsilon( elements[ 0 ] ) + ',' +
|
||||
epsilon( elements[ 1 ] ) + ',' +
|
||||
epsilon( elements[ 2 ] ) + ',' +
|
||||
epsilon( elements[ 3 ] ) + ',' +
|
||||
epsilon( - elements[ 4 ] ) + ',' +
|
||||
epsilon( - elements[ 5 ] ) + ',' +
|
||||
epsilon( - elements[ 6 ] ) + ',' +
|
||||
epsilon( - elements[ 7 ] ) + ',' +
|
||||
epsilon( elements[ 8 ] ) + ',' +
|
||||
epsilon( elements[ 9 ] ) + ',' +
|
||||
epsilon( elements[ 10 ] ) + ',' +
|
||||
epsilon( elements[ 11 ] ) + ',' +
|
||||
epsilon( elements[ 12 ] ) + ',' +
|
||||
epsilon( elements[ 13 ] ) + ',' +
|
||||
epsilon( elements[ 14 ] ) + ',' +
|
||||
epsilon( elements[ 15 ] ) +
|
||||
')';
|
||||
|
||||
return 'translate(-50%,-50%)' + matrix3d;
|
||||
|
||||
}
|
||||
|
||||
function renderObject( object, scene, camera, cameraCSSMatrix ) {
|
||||
|
||||
if ( object.isCSS3DObject ) {
|
||||
|
||||
const visible = ( object.visible === true ) && ( object.layers.test( camera.layers ) === true );
|
||||
object.element.style.display = ( visible === true ) ? '' : 'none';
|
||||
|
||||
if ( visible === true ) {
|
||||
|
||||
object.onBeforeRender( _this, scene, camera );
|
||||
|
||||
let style;
|
||||
|
||||
if ( object.isCSS3DSprite ) {
|
||||
|
||||
// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/
|
||||
|
||||
_matrix.copy( camera.matrixWorldInverse );
|
||||
_matrix.transpose();
|
||||
|
||||
if ( object.rotation2D !== 0 ) _matrix.multiply( _matrix2.makeRotationZ( object.rotation2D ) );
|
||||
|
||||
object.matrixWorld.decompose( _position, _quaternion, _scale );
|
||||
_matrix.setPosition( _position );
|
||||
_matrix.scale( _scale );
|
||||
|
||||
_matrix.elements[ 3 ] = 0;
|
||||
_matrix.elements[ 7 ] = 0;
|
||||
_matrix.elements[ 11 ] = 0;
|
||||
_matrix.elements[ 15 ] = 1;
|
||||
|
||||
style = getObjectCSSMatrix( _matrix );
|
||||
|
||||
} else {
|
||||
|
||||
style = getObjectCSSMatrix( object.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
const element = object.element;
|
||||
const cachedObject = cache.objects.get( object );
|
||||
|
||||
if ( cachedObject === undefined || cachedObject.style !== style ) {
|
||||
|
||||
element.style.transform = style;
|
||||
|
||||
const objectData = { style: style };
|
||||
cache.objects.set( object, objectData );
|
||||
|
||||
}
|
||||
|
||||
if ( element.parentNode !== cameraElement ) {
|
||||
|
||||
cameraElement.appendChild( element );
|
||||
|
||||
}
|
||||
|
||||
object.onAfterRender( _this, scene, camera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0, l = object.children.length; i < l; i ++ ) {
|
||||
|
||||
renderObject( object.children[ i ], scene, camera, cameraCSSMatrix );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CSS3DObject, CSS3DSprite, CSS3DRenderer };
|
918
static/sdk/three/jsm/renderers/Projector.js
Normal file
918
static/sdk/three/jsm/renderers/Projector.js
Normal file
@ -0,0 +1,918 @@
|
||||
import {
|
||||
Box3,
|
||||
Color,
|
||||
DoubleSide,
|
||||
Frustum,
|
||||
Matrix3,
|
||||
Matrix4,
|
||||
Vector2,
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
|
||||
class RenderableObject {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.id = 0;
|
||||
|
||||
this.object = null;
|
||||
this.z = 0;
|
||||
this.renderOrder = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class RenderableFace {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.id = 0;
|
||||
|
||||
this.v1 = new RenderableVertex();
|
||||
this.v2 = new RenderableVertex();
|
||||
this.v3 = new RenderableVertex();
|
||||
|
||||
this.normalModel = new Vector3();
|
||||
|
||||
this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ];
|
||||
this.vertexNormalsLength = 0;
|
||||
|
||||
this.color = new Color();
|
||||
this.material = null;
|
||||
this.uvs = [ new Vector2(), new Vector2(), new Vector2() ];
|
||||
|
||||
this.z = 0;
|
||||
this.renderOrder = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class RenderableVertex {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.position = new Vector3();
|
||||
this.positionWorld = new Vector3();
|
||||
this.positionScreen = new Vector4();
|
||||
|
||||
this.visible = true;
|
||||
|
||||
}
|
||||
|
||||
copy( vertex ) {
|
||||
|
||||
this.positionWorld.copy( vertex.positionWorld );
|
||||
this.positionScreen.copy( vertex.positionScreen );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class RenderableLine {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.id = 0;
|
||||
|
||||
this.v1 = new RenderableVertex();
|
||||
this.v2 = new RenderableVertex();
|
||||
|
||||
this.vertexColors = [ new Color(), new Color() ];
|
||||
this.material = null;
|
||||
|
||||
this.z = 0;
|
||||
this.renderOrder = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class RenderableSprite {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.id = 0;
|
||||
|
||||
this.object = null;
|
||||
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
|
||||
this.rotation = 0;
|
||||
this.scale = new Vector2();
|
||||
|
||||
this.material = null;
|
||||
this.renderOrder = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
class Projector {
|
||||
|
||||
constructor() {
|
||||
|
||||
let _object, _objectCount, _objectPoolLength = 0,
|
||||
_vertex, _vertexCount, _vertexPoolLength = 0,
|
||||
_face, _faceCount, _facePoolLength = 0,
|
||||
_line, _lineCount, _linePoolLength = 0,
|
||||
_sprite, _spriteCount, _spritePoolLength = 0,
|
||||
_modelMatrix;
|
||||
|
||||
const
|
||||
|
||||
_renderData = { objects: [], lights: [], elements: [] },
|
||||
|
||||
_vector3 = new Vector3(),
|
||||
_vector4 = new Vector4(),
|
||||
|
||||
_clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ),
|
||||
_boundingBox = new Box3(),
|
||||
_points3 = new Array( 3 ),
|
||||
|
||||
_viewMatrix = new Matrix4(),
|
||||
_viewProjectionMatrix = new Matrix4(),
|
||||
|
||||
_modelViewProjectionMatrix = new Matrix4(),
|
||||
|
||||
_frustum = new Frustum(),
|
||||
|
||||
_objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [];
|
||||
|
||||
//
|
||||
|
||||
function RenderList() {
|
||||
|
||||
const normals = [];
|
||||
const colors = [];
|
||||
const uvs = [];
|
||||
|
||||
let object = null;
|
||||
|
||||
const normalMatrix = new Matrix3();
|
||||
|
||||
function setObject( value ) {
|
||||
|
||||
object = value;
|
||||
|
||||
normalMatrix.getNormalMatrix( object.matrixWorld );
|
||||
|
||||
normals.length = 0;
|
||||
colors.length = 0;
|
||||
uvs.length = 0;
|
||||
|
||||
}
|
||||
|
||||
function projectVertex( vertex ) {
|
||||
|
||||
const position = vertex.position;
|
||||
const positionWorld = vertex.positionWorld;
|
||||
const positionScreen = vertex.positionScreen;
|
||||
|
||||
positionWorld.copy( position ).applyMatrix4( _modelMatrix );
|
||||
positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix );
|
||||
|
||||
const invW = 1 / positionScreen.w;
|
||||
|
||||
positionScreen.x *= invW;
|
||||
positionScreen.y *= invW;
|
||||
positionScreen.z *= invW;
|
||||
|
||||
vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 &&
|
||||
positionScreen.y >= - 1 && positionScreen.y <= 1 &&
|
||||
positionScreen.z >= - 1 && positionScreen.z <= 1;
|
||||
|
||||
}
|
||||
|
||||
function pushVertex( x, y, z ) {
|
||||
|
||||
_vertex = getNextVertexInPool();
|
||||
_vertex.position.set( x, y, z );
|
||||
|
||||
projectVertex( _vertex );
|
||||
|
||||
}
|
||||
|
||||
function pushNormal( x, y, z ) {
|
||||
|
||||
normals.push( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
function pushColor( r, g, b ) {
|
||||
|
||||
colors.push( r, g, b );
|
||||
|
||||
}
|
||||
|
||||
function pushUv( x, y ) {
|
||||
|
||||
uvs.push( x, y );
|
||||
|
||||
}
|
||||
|
||||
function checkTriangleVisibility( v1, v2, v3 ) {
|
||||
|
||||
if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true;
|
||||
|
||||
_points3[ 0 ] = v1.positionScreen;
|
||||
_points3[ 1 ] = v2.positionScreen;
|
||||
_points3[ 2 ] = v3.positionScreen;
|
||||
|
||||
return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) );
|
||||
|
||||
}
|
||||
|
||||
function checkBackfaceCulling( v1, v2, v3 ) {
|
||||
|
||||
return ( ( v3.positionScreen.x - v1.positionScreen.x ) *
|
||||
( v2.positionScreen.y - v1.positionScreen.y ) -
|
||||
( v3.positionScreen.y - v1.positionScreen.y ) *
|
||||
( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
|
||||
|
||||
}
|
||||
|
||||
function pushLine( a, b ) {
|
||||
|
||||
const v1 = _vertexPool[ a ];
|
||||
const v2 = _vertexPool[ b ];
|
||||
|
||||
// Clip
|
||||
|
||||
v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix );
|
||||
v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix );
|
||||
|
||||
if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) {
|
||||
|
||||
// Perform the perspective divide
|
||||
v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w );
|
||||
v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w );
|
||||
|
||||
_line = getNextLineInPool();
|
||||
_line.id = object.id;
|
||||
_line.v1.copy( v1 );
|
||||
_line.v2.copy( v2 );
|
||||
_line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z );
|
||||
_line.renderOrder = object.renderOrder;
|
||||
|
||||
_line.material = object.material;
|
||||
|
||||
if ( object.material.vertexColors ) {
|
||||
|
||||
_line.vertexColors[ 0 ].fromArray( colors, a * 3 );
|
||||
_line.vertexColors[ 1 ].fromArray( colors, b * 3 );
|
||||
|
||||
}
|
||||
|
||||
_renderData.elements.push( _line );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function pushTriangle( a, b, c, material ) {
|
||||
|
||||
const v1 = _vertexPool[ a ];
|
||||
const v2 = _vertexPool[ b ];
|
||||
const v3 = _vertexPool[ c ];
|
||||
|
||||
if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return;
|
||||
|
||||
if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) {
|
||||
|
||||
_face = getNextFaceInPool();
|
||||
|
||||
_face.id = object.id;
|
||||
_face.v1.copy( v1 );
|
||||
_face.v2.copy( v2 );
|
||||
_face.v3.copy( v3 );
|
||||
_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
|
||||
_face.renderOrder = object.renderOrder;
|
||||
|
||||
// face normal
|
||||
_vector3.subVectors( v3.position, v2.position );
|
||||
_vector4.subVectors( v1.position, v2.position );
|
||||
_vector3.cross( _vector4 );
|
||||
_face.normalModel.copy( _vector3 );
|
||||
_face.normalModel.applyMatrix3( normalMatrix ).normalize();
|
||||
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const normal = _face.vertexNormalsModel[ i ];
|
||||
normal.fromArray( normals, arguments[ i ] * 3 );
|
||||
normal.applyMatrix3( normalMatrix ).normalize();
|
||||
|
||||
const uv = _face.uvs[ i ];
|
||||
uv.fromArray( uvs, arguments[ i ] * 2 );
|
||||
|
||||
}
|
||||
|
||||
_face.vertexNormalsLength = 3;
|
||||
|
||||
_face.material = material;
|
||||
|
||||
if ( material.vertexColors ) {
|
||||
|
||||
_face.color.fromArray( colors, a * 3 );
|
||||
|
||||
}
|
||||
|
||||
_renderData.elements.push( _face );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
setObject: setObject,
|
||||
projectVertex: projectVertex,
|
||||
checkTriangleVisibility: checkTriangleVisibility,
|
||||
checkBackfaceCulling: checkBackfaceCulling,
|
||||
pushVertex: pushVertex,
|
||||
pushNormal: pushNormal,
|
||||
pushColor: pushColor,
|
||||
pushUv: pushUv,
|
||||
pushLine: pushLine,
|
||||
pushTriangle: pushTriangle
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const renderList = new RenderList();
|
||||
|
||||
function projectObject( object ) {
|
||||
|
||||
if ( object.visible === false ) return;
|
||||
|
||||
if ( object.isLight ) {
|
||||
|
||||
_renderData.lights.push( object );
|
||||
|
||||
} else if ( object.isMesh || object.isLine || object.isPoints ) {
|
||||
|
||||
if ( object.material.visible === false ) return;
|
||||
if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return;
|
||||
|
||||
addObject( object );
|
||||
|
||||
} else if ( object.isSprite ) {
|
||||
|
||||
if ( object.material.visible === false ) return;
|
||||
if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return;
|
||||
|
||||
addObject( object );
|
||||
|
||||
}
|
||||
|
||||
const children = object.children;
|
||||
|
||||
for ( let i = 0, l = children.length; i < l; i ++ ) {
|
||||
|
||||
projectObject( children[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function addObject( object ) {
|
||||
|
||||
_object = getNextObjectInPool();
|
||||
_object.id = object.id;
|
||||
_object.object = object;
|
||||
|
||||
_vector3.setFromMatrixPosition( object.matrixWorld );
|
||||
_vector3.applyMatrix4( _viewProjectionMatrix );
|
||||
_object.z = _vector3.z;
|
||||
_object.renderOrder = object.renderOrder;
|
||||
|
||||
_renderData.objects.push( _object );
|
||||
|
||||
}
|
||||
|
||||
this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
|
||||
|
||||
_faceCount = 0;
|
||||
_lineCount = 0;
|
||||
_spriteCount = 0;
|
||||
|
||||
_renderData.elements.length = 0;
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
_viewMatrix.copy( camera.matrixWorldInverse );
|
||||
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
|
||||
|
||||
_frustum.setFromProjectionMatrix( _viewProjectionMatrix );
|
||||
|
||||
//
|
||||
|
||||
_objectCount = 0;
|
||||
|
||||
_renderData.objects.length = 0;
|
||||
_renderData.lights.length = 0;
|
||||
|
||||
projectObject( scene );
|
||||
|
||||
if ( sortObjects === true ) {
|
||||
|
||||
_renderData.objects.sort( painterSort );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const objects = _renderData.objects;
|
||||
|
||||
for ( let o = 0, ol = objects.length; o < ol; o ++ ) {
|
||||
|
||||
const object = objects[ o ].object;
|
||||
const geometry = object.geometry;
|
||||
|
||||
renderList.setObject( object );
|
||||
|
||||
_modelMatrix = object.matrixWorld;
|
||||
|
||||
_vertexCount = 0;
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
let material = object.material;
|
||||
|
||||
const isMultiMaterial = Array.isArray( material );
|
||||
|
||||
const attributes = geometry.attributes;
|
||||
const groups = geometry.groups;
|
||||
|
||||
if ( attributes.position === undefined ) continue;
|
||||
|
||||
const positions = attributes.position.array;
|
||||
|
||||
for ( let i = 0, l = positions.length; i < l; i += 3 ) {
|
||||
|
||||
let x = positions[ i ];
|
||||
let y = positions[ i + 1 ];
|
||||
let z = positions[ i + 2 ];
|
||||
|
||||
const morphTargets = geometry.morphAttributes.position;
|
||||
|
||||
if ( morphTargets !== undefined ) {
|
||||
|
||||
const morphTargetsRelative = geometry.morphTargetsRelative;
|
||||
const morphInfluences = object.morphTargetInfluences;
|
||||
|
||||
for ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) {
|
||||
|
||||
const influence = morphInfluences[ t ];
|
||||
|
||||
if ( influence === 0 ) continue;
|
||||
|
||||
const target = morphTargets[ t ];
|
||||
|
||||
if ( morphTargetsRelative ) {
|
||||
|
||||
x += target.getX( i / 3 ) * influence;
|
||||
y += target.getY( i / 3 ) * influence;
|
||||
z += target.getZ( i / 3 ) * influence;
|
||||
|
||||
} else {
|
||||
|
||||
x += ( target.getX( i / 3 ) - positions[ i ] ) * influence;
|
||||
y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence;
|
||||
z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderList.pushVertex( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
if ( attributes.normal !== undefined ) {
|
||||
|
||||
const normals = attributes.normal.array;
|
||||
|
||||
for ( let i = 0, l = normals.length; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( attributes.color !== undefined ) {
|
||||
|
||||
const colors = attributes.color.array;
|
||||
|
||||
for ( let i = 0, l = colors.length; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( attributes.uv !== undefined ) {
|
||||
|
||||
const uvs = attributes.uv.array;
|
||||
|
||||
for ( let i = 0, l = uvs.length; i < l; i += 2 ) {
|
||||
|
||||
renderList.pushUv( uvs[ i ], uvs[ i + 1 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.index !== null ) {
|
||||
|
||||
const indices = geometry.index.array;
|
||||
|
||||
if ( groups.length > 0 ) {
|
||||
|
||||
for ( let g = 0; g < groups.length; g ++ ) {
|
||||
|
||||
const group = groups[ g ];
|
||||
|
||||
material = isMultiMaterial === true
|
||||
? object.material[ group.materialIndex ]
|
||||
: object.material;
|
||||
|
||||
if ( material === undefined ) continue;
|
||||
|
||||
for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = indices.length; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( groups.length > 0 ) {
|
||||
|
||||
for ( let g = 0; g < groups.length; g ++ ) {
|
||||
|
||||
const group = groups[ g ];
|
||||
|
||||
material = isMultiMaterial === true
|
||||
? object.material[ group.materialIndex ]
|
||||
: object.material;
|
||||
|
||||
if ( material === undefined ) continue;
|
||||
|
||||
for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushTriangle( i, i + 1, i + 2, material );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = positions.length / 3; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushTriangle( i, i + 1, i + 2, material );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isLine ) {
|
||||
|
||||
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
|
||||
|
||||
const attributes = geometry.attributes;
|
||||
|
||||
if ( attributes.position !== undefined ) {
|
||||
|
||||
const positions = attributes.position.array;
|
||||
|
||||
for ( let i = 0, l = positions.length; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );
|
||||
|
||||
}
|
||||
|
||||
if ( attributes.color !== undefined ) {
|
||||
|
||||
const colors = attributes.color.array;
|
||||
|
||||
for ( let i = 0, l = colors.length; i < l; i += 3 ) {
|
||||
|
||||
renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.index !== null ) {
|
||||
|
||||
const indices = geometry.index.array;
|
||||
|
||||
for ( let i = 0, l = indices.length; i < l; i += 2 ) {
|
||||
|
||||
renderList.pushLine( indices[ i ], indices[ i + 1 ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const step = object.isLineSegments ? 2 : 1;
|
||||
|
||||
for ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
|
||||
|
||||
renderList.pushLine( i, i + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isPoints ) {
|
||||
|
||||
_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
|
||||
|
||||
const attributes = geometry.attributes;
|
||||
|
||||
if ( attributes.position !== undefined ) {
|
||||
|
||||
const positions = attributes.position.array;
|
||||
|
||||
for ( let i = 0, l = positions.length; i < l; i += 3 ) {
|
||||
|
||||
_vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 );
|
||||
_vector4.applyMatrix4( _modelViewProjectionMatrix );
|
||||
|
||||
pushPoint( _vector4, object, camera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isSprite ) {
|
||||
|
||||
object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
|
||||
_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 );
|
||||
_vector4.applyMatrix4( _viewProjectionMatrix );
|
||||
|
||||
pushPoint( _vector4, object, camera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( sortElements === true ) {
|
||||
|
||||
_renderData.elements.sort( painterSort );
|
||||
|
||||
}
|
||||
|
||||
return _renderData;
|
||||
|
||||
};
|
||||
|
||||
function pushPoint( _vector4, object, camera ) {
|
||||
|
||||
const invW = 1 / _vector4.w;
|
||||
|
||||
_vector4.z *= invW;
|
||||
|
||||
if ( _vector4.z >= - 1 && _vector4.z <= 1 ) {
|
||||
|
||||
_sprite = getNextSpriteInPool();
|
||||
_sprite.id = object.id;
|
||||
_sprite.x = _vector4.x * invW;
|
||||
_sprite.y = _vector4.y * invW;
|
||||
_sprite.z = _vector4.z;
|
||||
_sprite.renderOrder = object.renderOrder;
|
||||
_sprite.object = object;
|
||||
|
||||
_sprite.rotation = object.rotation;
|
||||
|
||||
_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) );
|
||||
_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) );
|
||||
|
||||
_sprite.material = object.material;
|
||||
|
||||
_renderData.elements.push( _sprite );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Pools
|
||||
|
||||
function getNextObjectInPool() {
|
||||
|
||||
if ( _objectCount === _objectPoolLength ) {
|
||||
|
||||
const object = new RenderableObject();
|
||||
_objectPool.push( object );
|
||||
_objectPoolLength ++;
|
||||
_objectCount ++;
|
||||
return object;
|
||||
|
||||
}
|
||||
|
||||
return _objectPool[ _objectCount ++ ];
|
||||
|
||||
}
|
||||
|
||||
function getNextVertexInPool() {
|
||||
|
||||
if ( _vertexCount === _vertexPoolLength ) {
|
||||
|
||||
const vertex = new RenderableVertex();
|
||||
_vertexPool.push( vertex );
|
||||
_vertexPoolLength ++;
|
||||
_vertexCount ++;
|
||||
return vertex;
|
||||
|
||||
}
|
||||
|
||||
return _vertexPool[ _vertexCount ++ ];
|
||||
|
||||
}
|
||||
|
||||
function getNextFaceInPool() {
|
||||
|
||||
if ( _faceCount === _facePoolLength ) {
|
||||
|
||||
const face = new RenderableFace();
|
||||
_facePool.push( face );
|
||||
_facePoolLength ++;
|
||||
_faceCount ++;
|
||||
return face;
|
||||
|
||||
}
|
||||
|
||||
return _facePool[ _faceCount ++ ];
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getNextLineInPool() {
|
||||
|
||||
if ( _lineCount === _linePoolLength ) {
|
||||
|
||||
const line = new RenderableLine();
|
||||
_linePool.push( line );
|
||||
_linePoolLength ++;
|
||||
_lineCount ++;
|
||||
return line;
|
||||
|
||||
}
|
||||
|
||||
return _linePool[ _lineCount ++ ];
|
||||
|
||||
}
|
||||
|
||||
function getNextSpriteInPool() {
|
||||
|
||||
if ( _spriteCount === _spritePoolLength ) {
|
||||
|
||||
const sprite = new RenderableSprite();
|
||||
_spritePool.push( sprite );
|
||||
_spritePoolLength ++;
|
||||
_spriteCount ++;
|
||||
return sprite;
|
||||
|
||||
}
|
||||
|
||||
return _spritePool[ _spriteCount ++ ];
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function painterSort( a, b ) {
|
||||
|
||||
if ( a.renderOrder !== b.renderOrder ) {
|
||||
|
||||
return a.renderOrder - b.renderOrder;
|
||||
|
||||
} else if ( a.z !== b.z ) {
|
||||
|
||||
return b.z - a.z;
|
||||
|
||||
} else if ( a.id !== b.id ) {
|
||||
|
||||
return a.id - b.id;
|
||||
|
||||
} else {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function clipLine( s1, s2 ) {
|
||||
|
||||
let alpha1 = 0, alpha2 = 1;
|
||||
|
||||
// Calculate the boundary coordinate of each vertex for the near and far clip planes,
|
||||
// Z = -1 and Z = +1, respectively.
|
||||
|
||||
const bc1near = s1.z + s1.w,
|
||||
bc2near = s2.z + s2.w,
|
||||
bc1far = - s1.z + s1.w,
|
||||
bc2far = - s2.z + s2.w;
|
||||
|
||||
if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
|
||||
|
||||
// Both vertices lie entirely within all clip planes.
|
||||
return true;
|
||||
|
||||
} else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) {
|
||||
|
||||
// Both vertices lie entirely outside one of the clip planes.
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
// The line segment spans at least one clip plane.
|
||||
|
||||
if ( bc1near < 0 ) {
|
||||
|
||||
// v1 lies outside the near plane, v2 inside
|
||||
alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
|
||||
|
||||
} else if ( bc2near < 0 ) {
|
||||
|
||||
// v2 lies outside the near plane, v1 inside
|
||||
alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
|
||||
|
||||
}
|
||||
|
||||
if ( bc1far < 0 ) {
|
||||
|
||||
// v1 lies outside the far plane, v2 inside
|
||||
alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
|
||||
|
||||
} else if ( bc2far < 0 ) {
|
||||
|
||||
// v2 lies outside the far plane, v2 inside
|
||||
alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
|
||||
|
||||
}
|
||||
|
||||
if ( alpha2 < alpha1 ) {
|
||||
|
||||
// The line segment spans two boundaries, but is outside both of them.
|
||||
// (This can't happen when we're only clipping against just near/far but good
|
||||
// to leave the check here for future usage if other clip planes are added.)
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
// Update the s1 and s2 vertices to match the clipped line segment.
|
||||
s1.lerp( s2, alpha1 );
|
||||
s2.lerp( s1, 1 - alpha2 );
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector };
|
556
static/sdk/three/jsm/renderers/SVGRenderer.js
Normal file
556
static/sdk/three/jsm/renderers/SVGRenderer.js
Normal file
@ -0,0 +1,556 @@
|
||||
import {
|
||||
Box2,
|
||||
Camera,
|
||||
Color,
|
||||
Matrix3,
|
||||
Matrix4,
|
||||
Object3D,
|
||||
SRGBColorSpace,
|
||||
Vector3
|
||||
} from 'three';
|
||||
import { Projector } from '../renderers/Projector.js';
|
||||
import { RenderableFace } from '../renderers/Projector.js';
|
||||
import { RenderableLine } from '../renderers/Projector.js';
|
||||
import { RenderableSprite } from '../renderers/Projector.js';
|
||||
|
||||
class SVGObject extends Object3D {
|
||||
|
||||
constructor( node ) {
|
||||
|
||||
super();
|
||||
|
||||
this.isSVGObject = true;
|
||||
|
||||
this.node = node;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SVGRenderer {
|
||||
|
||||
constructor() {
|
||||
|
||||
let _renderData, _elements, _lights,
|
||||
_svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf,
|
||||
|
||||
_v1, _v2, _v3,
|
||||
|
||||
_svgNode,
|
||||
_pathCount = 0,
|
||||
|
||||
_precision = null,
|
||||
_quality = 1,
|
||||
|
||||
_currentPath, _currentStyle;
|
||||
|
||||
const _this = this,
|
||||
_clipBox = new Box2(),
|
||||
_elemBox = new Box2(),
|
||||
|
||||
_color = new Color(),
|
||||
_diffuseColor = new Color(),
|
||||
_ambientLight = new Color(),
|
||||
_directionalLights = new Color(),
|
||||
_pointLights = new Color(),
|
||||
_clearColor = new Color(),
|
||||
|
||||
_vector3 = new Vector3(), // Needed for PointLight
|
||||
_centroid = new Vector3(),
|
||||
_normal = new Vector3(),
|
||||
_normalViewMatrix = new Matrix3(),
|
||||
|
||||
_viewMatrix = new Matrix4(),
|
||||
_viewProjectionMatrix = new Matrix4(),
|
||||
|
||||
_svgPathPool = [],
|
||||
|
||||
_projector = new Projector(),
|
||||
_svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
|
||||
|
||||
this.domElement = _svg;
|
||||
|
||||
this.autoClear = true;
|
||||
this.sortObjects = true;
|
||||
this.sortElements = true;
|
||||
|
||||
this.overdraw = 0.5;
|
||||
|
||||
this.outputColorSpace = SRGBColorSpace;
|
||||
|
||||
this.info = {
|
||||
|
||||
render: {
|
||||
|
||||
vertices: 0,
|
||||
faces: 0
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.setQuality = function ( quality ) {
|
||||
|
||||
switch ( quality ) {
|
||||
|
||||
case 'high': _quality = 1; break;
|
||||
case 'low': _quality = 0; break;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.setClearColor = function ( color ) {
|
||||
|
||||
_clearColor.set( color );
|
||||
|
||||
};
|
||||
|
||||
this.setPixelRatio = function () {};
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
_svgWidth = width; _svgHeight = height;
|
||||
_svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2;
|
||||
|
||||
_svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight );
|
||||
_svg.setAttribute( 'width', _svgWidth );
|
||||
_svg.setAttribute( 'height', _svgHeight );
|
||||
|
||||
_clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf );
|
||||
_clipBox.max.set( _svgWidthHalf, _svgHeightHalf );
|
||||
|
||||
};
|
||||
|
||||
this.getSize = function () {
|
||||
|
||||
return {
|
||||
width: _svgWidth,
|
||||
height: _svgHeight
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
this.setPrecision = function ( precision ) {
|
||||
|
||||
_precision = precision;
|
||||
|
||||
};
|
||||
|
||||
function removeChildNodes() {
|
||||
|
||||
_pathCount = 0;
|
||||
|
||||
while ( _svg.childNodes.length > 0 ) {
|
||||
|
||||
_svg.removeChild( _svg.childNodes[ 0 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function convert( c ) {
|
||||
|
||||
return _precision !== null ? c.toFixed( _precision ) : c;
|
||||
|
||||
}
|
||||
|
||||
this.clear = function () {
|
||||
|
||||
removeChildNodes();
|
||||
_svg.style.backgroundColor = _clearColor.getStyle( _this.outputColorSpace );
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( camera instanceof Camera === false ) {
|
||||
|
||||
console.error( 'THREE.SVGRenderer.render: camera is not an instance of Camera.' );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const background = scene.background;
|
||||
|
||||
if ( background && background.isColor ) {
|
||||
|
||||
removeChildNodes();
|
||||
_svg.style.backgroundColor = background.getStyle( _this.outputColorSpace );
|
||||
|
||||
} else if ( this.autoClear === true ) {
|
||||
|
||||
this.clear();
|
||||
|
||||
}
|
||||
|
||||
_this.info.render.vertices = 0;
|
||||
_this.info.render.faces = 0;
|
||||
|
||||
_viewMatrix.copy( camera.matrixWorldInverse );
|
||||
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
|
||||
|
||||
_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
|
||||
_elements = _renderData.elements;
|
||||
_lights = _renderData.lights;
|
||||
|
||||
_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse );
|
||||
|
||||
calculateLights( _lights );
|
||||
|
||||
// reset accumulated path
|
||||
|
||||
_currentPath = '';
|
||||
_currentStyle = '';
|
||||
|
||||
for ( let e = 0, el = _elements.length; e < el; e ++ ) {
|
||||
|
||||
const element = _elements[ e ];
|
||||
const material = element.material;
|
||||
|
||||
if ( material === undefined || material.opacity === 0 ) continue;
|
||||
|
||||
_elemBox.makeEmpty();
|
||||
|
||||
if ( element instanceof RenderableSprite ) {
|
||||
|
||||
_v1 = element;
|
||||
_v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf;
|
||||
|
||||
renderSprite( _v1, element, material );
|
||||
|
||||
} else if ( element instanceof RenderableLine ) {
|
||||
|
||||
_v1 = element.v1; _v2 = element.v2;
|
||||
|
||||
_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
|
||||
_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
|
||||
|
||||
_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
|
||||
|
||||
if ( _clipBox.intersectsBox( _elemBox ) === true ) {
|
||||
|
||||
renderLine( _v1, _v2, material );
|
||||
|
||||
}
|
||||
|
||||
} else if ( element instanceof RenderableFace ) {
|
||||
|
||||
_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
|
||||
|
||||
if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue;
|
||||
if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue;
|
||||
if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue;
|
||||
|
||||
_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
|
||||
_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
|
||||
_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;
|
||||
|
||||
if ( this.overdraw > 0 ) {
|
||||
|
||||
expand( _v1.positionScreen, _v2.positionScreen, this.overdraw );
|
||||
expand( _v2.positionScreen, _v3.positionScreen, this.overdraw );
|
||||
expand( _v3.positionScreen, _v1.positionScreen, this.overdraw );
|
||||
|
||||
}
|
||||
|
||||
_elemBox.setFromPoints( [
|
||||
_v1.positionScreen,
|
||||
_v2.positionScreen,
|
||||
_v3.positionScreen
|
||||
] );
|
||||
|
||||
if ( _clipBox.intersectsBox( _elemBox ) === true ) {
|
||||
|
||||
renderFace3( _v1, _v2, _v3, element, material );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flushPath(); // just to flush last svg:path
|
||||
|
||||
scene.traverseVisible( function ( object ) {
|
||||
|
||||
if ( object.isSVGObject ) {
|
||||
|
||||
_vector3.setFromMatrixPosition( object.matrixWorld );
|
||||
_vector3.applyMatrix4( _viewProjectionMatrix );
|
||||
|
||||
if ( _vector3.z < - 1 || _vector3.z > 1 ) return;
|
||||
|
||||
const x = _vector3.x * _svgWidthHalf;
|
||||
const y = - _vector3.y * _svgHeightHalf;
|
||||
|
||||
const node = object.node;
|
||||
node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' );
|
||||
|
||||
_svg.appendChild( node );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
function calculateLights( lights ) {
|
||||
|
||||
_ambientLight.setRGB( 0, 0, 0 );
|
||||
_directionalLights.setRGB( 0, 0, 0 );
|
||||
_pointLights.setRGB( 0, 0, 0 );
|
||||
|
||||
for ( let l = 0, ll = lights.length; l < ll; l ++ ) {
|
||||
|
||||
const light = lights[ l ];
|
||||
const lightColor = light.color;
|
||||
|
||||
if ( light.isAmbientLight ) {
|
||||
|
||||
_ambientLight.r += lightColor.r;
|
||||
_ambientLight.g += lightColor.g;
|
||||
_ambientLight.b += lightColor.b;
|
||||
|
||||
} else if ( light.isDirectionalLight ) {
|
||||
|
||||
_directionalLights.r += lightColor.r;
|
||||
_directionalLights.g += lightColor.g;
|
||||
_directionalLights.b += lightColor.b;
|
||||
|
||||
} else if ( light.isPointLight ) {
|
||||
|
||||
_pointLights.r += lightColor.r;
|
||||
_pointLights.g += lightColor.g;
|
||||
_pointLights.b += lightColor.b;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function calculateLight( lights, position, normal, color ) {
|
||||
|
||||
for ( let l = 0, ll = lights.length; l < ll; l ++ ) {
|
||||
|
||||
const light = lights[ l ];
|
||||
const lightColor = light.color;
|
||||
|
||||
if ( light.isDirectionalLight ) {
|
||||
|
||||
const lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize();
|
||||
|
||||
let amount = normal.dot( lightPosition );
|
||||
|
||||
if ( amount <= 0 ) continue;
|
||||
|
||||
amount *= light.intensity;
|
||||
|
||||
color.r += lightColor.r * amount;
|
||||
color.g += lightColor.g * amount;
|
||||
color.b += lightColor.b * amount;
|
||||
|
||||
} else if ( light.isPointLight ) {
|
||||
|
||||
const lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld );
|
||||
|
||||
let amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
|
||||
|
||||
if ( amount <= 0 ) continue;
|
||||
|
||||
amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
|
||||
|
||||
if ( amount == 0 ) continue;
|
||||
|
||||
amount *= light.intensity;
|
||||
|
||||
color.r += lightColor.r * amount;
|
||||
color.g += lightColor.g * amount;
|
||||
color.b += lightColor.b * amount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function renderSprite( v1, element, material ) {
|
||||
|
||||
let scaleX = element.scale.x * _svgWidthHalf;
|
||||
let scaleY = element.scale.y * _svgHeightHalf;
|
||||
|
||||
if ( material.isPointsMaterial ) {
|
||||
|
||||
scaleX *= material.size;
|
||||
scaleY *= material.size;
|
||||
|
||||
}
|
||||
|
||||
const path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert( - scaleX ) + 'z';
|
||||
let style = '';
|
||||
|
||||
if ( material.isSpriteMaterial || material.isPointsMaterial ) {
|
||||
|
||||
style = 'fill:' + material.color.getStyle( _this.outputColorSpace ) + ';fill-opacity:' + material.opacity;
|
||||
|
||||
}
|
||||
|
||||
addPath( style, path );
|
||||
|
||||
}
|
||||
|
||||
function renderLine( v1, v2, material ) {
|
||||
|
||||
const path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y );
|
||||
|
||||
if ( material.isLineBasicMaterial ) {
|
||||
|
||||
let style = 'fill:none;stroke:' + material.color.getStyle( _this.outputColorSpace ) + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap;
|
||||
|
||||
if ( material.isLineDashedMaterial ) {
|
||||
|
||||
style = style + ';stroke-dasharray:' + material.dashSize + ',' + material.gapSize;
|
||||
|
||||
}
|
||||
|
||||
addPath( style, path );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function renderFace3( v1, v2, v3, element, material ) {
|
||||
|
||||
_this.info.render.vertices += 3;
|
||||
_this.info.render.faces ++;
|
||||
|
||||
const path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z';
|
||||
let style = '';
|
||||
|
||||
if ( material.isMeshBasicMaterial ) {
|
||||
|
||||
_color.copy( material.color );
|
||||
|
||||
if ( material.vertexColors ) {
|
||||
|
||||
_color.multiply( element.color );
|
||||
|
||||
}
|
||||
|
||||
} else if ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) {
|
||||
|
||||
_diffuseColor.copy( material.color );
|
||||
|
||||
if ( material.vertexColors ) {
|
||||
|
||||
_diffuseColor.multiply( element.color );
|
||||
|
||||
}
|
||||
|
||||
_color.copy( _ambientLight );
|
||||
|
||||
_centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 );
|
||||
|
||||
calculateLight( _lights, _centroid, element.normalModel, _color );
|
||||
|
||||
_color.multiply( _diffuseColor ).add( material.emissive );
|
||||
|
||||
} else if ( material.isMeshNormalMaterial ) {
|
||||
|
||||
_normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ).normalize();
|
||||
|
||||
_color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
|
||||
|
||||
}
|
||||
|
||||
if ( material.wireframe ) {
|
||||
|
||||
style = 'fill:none;stroke:' + _color.getStyle( _this.outputColorSpace ) + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin;
|
||||
|
||||
} else {
|
||||
|
||||
style = 'fill:' + _color.getStyle( _this.outputColorSpace ) + ';fill-opacity:' + material.opacity;
|
||||
|
||||
}
|
||||
|
||||
addPath( style, path );
|
||||
|
||||
}
|
||||
|
||||
// Hide anti-alias gaps
|
||||
|
||||
function expand( v1, v2, pixels ) {
|
||||
|
||||
let x = v2.x - v1.x, y = v2.y - v1.y;
|
||||
const det = x * x + y * y;
|
||||
|
||||
if ( det === 0 ) return;
|
||||
|
||||
const idet = pixels / Math.sqrt( det );
|
||||
|
||||
x *= idet; y *= idet;
|
||||
|
||||
v2.x += x; v2.y += y;
|
||||
v1.x -= x; v1.y -= y;
|
||||
|
||||
}
|
||||
|
||||
function addPath( style, path ) {
|
||||
|
||||
if ( _currentStyle === style ) {
|
||||
|
||||
_currentPath += path;
|
||||
|
||||
} else {
|
||||
|
||||
flushPath();
|
||||
|
||||
_currentStyle = style;
|
||||
_currentPath = path;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function flushPath() {
|
||||
|
||||
if ( _currentPath ) {
|
||||
|
||||
_svgNode = getPathNode( _pathCount ++ );
|
||||
_svgNode.setAttribute( 'd', _currentPath );
|
||||
_svgNode.setAttribute( 'style', _currentStyle );
|
||||
_svg.appendChild( _svgNode );
|
||||
|
||||
}
|
||||
|
||||
_currentPath = '';
|
||||
_currentStyle = '';
|
||||
|
||||
}
|
||||
|
||||
function getPathNode( id ) {
|
||||
|
||||
if ( _svgPathPool[ id ] == null ) {
|
||||
|
||||
_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
|
||||
|
||||
if ( _quality == 0 ) {
|
||||
|
||||
_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
|
||||
|
||||
}
|
||||
|
||||
return _svgPathPool[ id ];
|
||||
|
||||
}
|
||||
|
||||
return _svgPathPool[ id ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SVGObject, SVGRenderer };
|
50
static/sdk/three/jsm/renderers/common/Animation.js
Normal file
50
static/sdk/three/jsm/renderers/common/Animation.js
Normal file
@ -0,0 +1,50 @@
|
||||
class Animation {
|
||||
|
||||
constructor( nodes, info ) {
|
||||
|
||||
this.nodes = nodes;
|
||||
this.info = info;
|
||||
|
||||
this.animationLoop = null;
|
||||
this.requestId = null;
|
||||
|
||||
this._init();
|
||||
|
||||
}
|
||||
|
||||
_init() {
|
||||
|
||||
const update = ( time, frame ) => {
|
||||
|
||||
this.requestId = self.requestAnimationFrame( update );
|
||||
|
||||
if ( this.info.autoReset === true ) this.info.reset();
|
||||
|
||||
this.nodes.nodeFrame.update();
|
||||
|
||||
this.info.frame = this.nodes.nodeFrame.frameId;
|
||||
|
||||
if ( this.animationLoop !== null ) this.animationLoop( time, frame );
|
||||
|
||||
};
|
||||
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
self.cancelAnimationFrame( this.requestId );
|
||||
this.requestId = null;
|
||||
|
||||
}
|
||||
|
||||
setAnimationLoop( callback ) {
|
||||
|
||||
this.animationLoop = callback;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Animation;
|
75
static/sdk/three/jsm/renderers/common/Attributes.js
Normal file
75
static/sdk/three/jsm/renderers/common/Attributes.js
Normal file
@ -0,0 +1,75 @@
|
||||
import DataMap from './DataMap.js';
|
||||
import { AttributeType } from './Constants.js';
|
||||
import { DynamicDrawUsage } from 'three';
|
||||
|
||||
class Attributes extends DataMap {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
super();
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
delete( attribute ) {
|
||||
|
||||
const attributeData = super.delete( attribute );
|
||||
|
||||
if ( attributeData !== undefined ) {
|
||||
|
||||
this.backend.destroyAttribute( attribute );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update( attribute, type ) {
|
||||
|
||||
const data = this.get( attribute );
|
||||
|
||||
if ( data.version === undefined ) {
|
||||
|
||||
if ( type === AttributeType.VERTEX ) {
|
||||
|
||||
this.backend.createAttribute( attribute );
|
||||
|
||||
} else if ( type === AttributeType.INDEX ) {
|
||||
|
||||
this.backend.createIndexAttribute( attribute );
|
||||
|
||||
} else if ( type === AttributeType.STORAGE ) {
|
||||
|
||||
this.backend.createStorageAttribute( attribute );
|
||||
|
||||
}
|
||||
|
||||
data.version = this._getBufferAttribute( attribute ).version;
|
||||
|
||||
} else {
|
||||
|
||||
const bufferAttribute = this._getBufferAttribute( attribute );
|
||||
|
||||
if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) {
|
||||
|
||||
this.backend.updateAttribute( attribute );
|
||||
|
||||
data.version = bufferAttribute.version;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_getBufferAttribute( attribute ) {
|
||||
|
||||
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
|
||||
|
||||
return attribute;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Attributes;
|
195
static/sdk/three/jsm/renderers/common/Backend.js
Normal file
195
static/sdk/three/jsm/renderers/common/Backend.js
Normal file
@ -0,0 +1,195 @@
|
||||
let vector2 = null;
|
||||
let vector4 = null;
|
||||
let color4 = null;
|
||||
|
||||
import Color4 from './Color4.js';
|
||||
import { Vector2, Vector4, REVISION, createCanvasElement } from 'three';
|
||||
|
||||
class Backend {
|
||||
|
||||
constructor( parameters = {} ) {
|
||||
|
||||
this.parameters = Object.assign( {}, parameters );
|
||||
this.data = new WeakMap();
|
||||
this.renderer = null;
|
||||
this.domElement = null;
|
||||
|
||||
}
|
||||
|
||||
async init( renderer ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
}
|
||||
|
||||
// render context
|
||||
|
||||
begin( renderContext ) { }
|
||||
|
||||
finish( renderContext ) { }
|
||||
|
||||
// render object
|
||||
|
||||
draw( renderObject, info ) { }
|
||||
|
||||
// program
|
||||
|
||||
createProgram( program ) { }
|
||||
|
||||
destroyProgram( program ) { }
|
||||
|
||||
// bindings
|
||||
|
||||
createBindings( renderObject ) { }
|
||||
|
||||
updateBindings( renderObject ) { }
|
||||
|
||||
// pipeline
|
||||
|
||||
createRenderPipeline( renderObject ) { }
|
||||
|
||||
createComputePipeline( computeNode, pipeline ) { }
|
||||
|
||||
destroyPipeline( pipeline ) { }
|
||||
|
||||
// cache key
|
||||
|
||||
needsRenderUpdate( renderObject ) { } // return Boolean ( fast test )
|
||||
|
||||
getRenderCacheKey( renderObject ) { } // return String
|
||||
|
||||
// node builder
|
||||
|
||||
createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT)
|
||||
|
||||
// textures
|
||||
|
||||
createSampler( texture ) { }
|
||||
|
||||
createDefaultTexture( texture ) { }
|
||||
|
||||
createTexture( texture ) { }
|
||||
|
||||
copyTextureToBuffer( texture, x, y, width, height ) {}
|
||||
|
||||
// attributes
|
||||
|
||||
createAttribute( attribute ) { }
|
||||
|
||||
createIndexAttribute( attribute ) { }
|
||||
|
||||
updateAttribute( attribute ) { }
|
||||
|
||||
destroyAttribute( attribute ) { }
|
||||
|
||||
// canvas
|
||||
|
||||
getContext() { }
|
||||
|
||||
updateSize() { }
|
||||
|
||||
// utils
|
||||
|
||||
resolveTimestampAsync( renderContext, type ) { }
|
||||
|
||||
hasFeatureAsync( name ) { } // return Boolean
|
||||
|
||||
hasFeature( name ) { } // return Boolean
|
||||
|
||||
getInstanceCount( renderObject ) {
|
||||
|
||||
const { object, geometry } = renderObject;
|
||||
|
||||
return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 );
|
||||
|
||||
}
|
||||
|
||||
getDrawingBufferSize() {
|
||||
|
||||
vector2 = vector2 || new Vector2();
|
||||
|
||||
return this.renderer.getDrawingBufferSize( vector2 );
|
||||
|
||||
}
|
||||
|
||||
getScissor() {
|
||||
|
||||
vector4 = vector4 || new Vector4();
|
||||
|
||||
return this.renderer.getScissor( vector4 );
|
||||
|
||||
}
|
||||
|
||||
setScissorTest( boolean ) { }
|
||||
|
||||
getClearColor() {
|
||||
|
||||
const renderer = this.renderer;
|
||||
|
||||
color4 = color4 || new Color4();
|
||||
|
||||
renderer.getClearColor( color4 );
|
||||
|
||||
color4.getRGB( color4, this.renderer.currentColorSpace );
|
||||
|
||||
return color4;
|
||||
|
||||
}
|
||||
|
||||
getDomElement() {
|
||||
|
||||
let domElement = this.domElement;
|
||||
|
||||
if ( domElement === null ) {
|
||||
|
||||
domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : createCanvasElement();
|
||||
|
||||
// OffscreenCanvas does not have setAttribute, see #22811
|
||||
if ( 'setAttribute' in domElement ) domElement.setAttribute( 'data-engine', `three.js r${REVISION} webgpu` );
|
||||
|
||||
this.domElement = domElement;
|
||||
|
||||
}
|
||||
|
||||
return domElement;
|
||||
|
||||
}
|
||||
|
||||
// resource properties
|
||||
|
||||
set( object, value ) {
|
||||
|
||||
this.data.set( object, value );
|
||||
|
||||
}
|
||||
|
||||
get( object ) {
|
||||
|
||||
let map = this.data.get( object );
|
||||
|
||||
if ( map === undefined ) {
|
||||
|
||||
map = {};
|
||||
this.data.set( object, map );
|
||||
|
||||
}
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
has( object ) {
|
||||
|
||||
return this.data.has( object );
|
||||
|
||||
}
|
||||
|
||||
delete( object ) {
|
||||
|
||||
this.data.delete( object );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Backend;
|
134
static/sdk/three/jsm/renderers/common/Background.js
Normal file
134
static/sdk/three/jsm/renderers/common/Background.js
Normal file
@ -0,0 +1,134 @@
|
||||
import DataMap from './DataMap.js';
|
||||
import Color4 from './Color4.js';
|
||||
import { Mesh, SphereGeometry, BackSide, LinearSRGBColorSpace } from 'three';
|
||||
import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
|
||||
|
||||
const _clearColor = new Color4();
|
||||
|
||||
class Background extends DataMap {
|
||||
|
||||
constructor( renderer, nodes ) {
|
||||
|
||||
super();
|
||||
|
||||
this.renderer = renderer;
|
||||
this.nodes = nodes;
|
||||
|
||||
}
|
||||
|
||||
update( scene, renderList, renderContext ) {
|
||||
|
||||
const renderer = this.renderer;
|
||||
const background = this.nodes.getBackgroundNode( scene ) || scene.background;
|
||||
|
||||
let forceClear = false;
|
||||
|
||||
if ( background === null ) {
|
||||
|
||||
// no background settings, use clear color configuration from the renderer
|
||||
|
||||
renderer._clearColor.getRGB( _clearColor, LinearSRGBColorSpace );
|
||||
_clearColor.a = renderer._clearColor.a;
|
||||
|
||||
} else if ( background.isColor === true ) {
|
||||
|
||||
// background is an opaque color
|
||||
|
||||
background.getRGB( _clearColor, LinearSRGBColorSpace );
|
||||
_clearColor.a = 1;
|
||||
|
||||
forceClear = true;
|
||||
|
||||
} else if ( background.isNode === true ) {
|
||||
|
||||
const sceneData = this.get( scene );
|
||||
const backgroundNode = background;
|
||||
|
||||
_clearColor.copy( renderer._clearColor );
|
||||
|
||||
let backgroundMesh = sceneData.backgroundMesh;
|
||||
|
||||
if ( backgroundMesh === undefined ) {
|
||||
|
||||
const backgroundMeshNode = context( vec4( backgroundNode ).mul( backgroundIntensity ), {
|
||||
// @TODO: Add Texture2D support using node context
|
||||
getUV: () => normalWorld,
|
||||
getTextureLevel: () => backgroundBlurriness
|
||||
} );
|
||||
|
||||
let viewProj = modelViewProjection();
|
||||
viewProj = viewProj.setZ( viewProj.w );
|
||||
|
||||
const nodeMaterial = new NodeMaterial();
|
||||
nodeMaterial.side = BackSide;
|
||||
nodeMaterial.depthTest = false;
|
||||
nodeMaterial.depthWrite = false;
|
||||
nodeMaterial.fog = false;
|
||||
nodeMaterial.vertexNode = viewProj;
|
||||
nodeMaterial.fragmentNode = backgroundMeshNode;
|
||||
|
||||
sceneData.backgroundMeshNode = backgroundMeshNode;
|
||||
sceneData.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
|
||||
backgroundMesh.frustumCulled = false;
|
||||
|
||||
backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) {
|
||||
|
||||
this.matrixWorld.copyPosition( camera.matrixWorld );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const backgroundCacheKey = backgroundNode.getCacheKey();
|
||||
|
||||
if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
|
||||
|
||||
sceneData.backgroundMeshNode.node = vec4( backgroundNode ).mul( backgroundIntensity );
|
||||
|
||||
backgroundMesh.material.needsUpdate = true;
|
||||
|
||||
sceneData.backgroundCacheKey = backgroundCacheKey;
|
||||
|
||||
}
|
||||
|
||||
renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'THREE.Renderer: Unsupported background configuration.', background );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if ( renderer.autoClear === true || forceClear === true ) {
|
||||
|
||||
_clearColor.multiplyScalar( _clearColor.a );
|
||||
|
||||
const clearColorValue = renderContext.clearColorValue;
|
||||
|
||||
clearColorValue.r = _clearColor.r;
|
||||
clearColorValue.g = _clearColor.g;
|
||||
clearColorValue.b = _clearColor.b;
|
||||
clearColorValue.a = _clearColor.a;
|
||||
|
||||
renderContext.depthClearValue = renderer._clearDepth;
|
||||
renderContext.stencilClearValue = renderer._clearStencil;
|
||||
|
||||
renderContext.clearColor = renderer.autoClearColor === true;
|
||||
renderContext.clearDepth = renderer.autoClearDepth === true;
|
||||
renderContext.clearStencil = renderer.autoClearStencil === true;
|
||||
|
||||
} else {
|
||||
|
||||
renderContext.clearColor = false;
|
||||
renderContext.clearDepth = false;
|
||||
renderContext.clearStencil = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Background;
|
25
static/sdk/three/jsm/renderers/common/Binding.js
Normal file
25
static/sdk/three/jsm/renderers/common/Binding.js
Normal file
@ -0,0 +1,25 @@
|
||||
class Binding {
|
||||
|
||||
constructor( name = '' ) {
|
||||
|
||||
this.name = name;
|
||||
|
||||
this.visibility = 0;
|
||||
|
||||
}
|
||||
|
||||
setVisibility( visibility ) {
|
||||
|
||||
this.visibility |= visibility;
|
||||
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
return Object.assign( new this.constructor(), this );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Binding;
|
173
static/sdk/three/jsm/renderers/common/Bindings.js
Normal file
173
static/sdk/three/jsm/renderers/common/Bindings.js
Normal file
@ -0,0 +1,173 @@
|
||||
import DataMap from './DataMap.js';
|
||||
import { AttributeType } from './Constants.js';
|
||||
|
||||
class Bindings extends DataMap {
|
||||
|
||||
constructor( backend, nodes, textures, attributes, pipelines, info ) {
|
||||
|
||||
super();
|
||||
|
||||
this.backend = backend;
|
||||
this.textures = textures;
|
||||
this.pipelines = pipelines;
|
||||
this.attributes = attributes;
|
||||
this.nodes = nodes;
|
||||
this.info = info;
|
||||
|
||||
this.pipelines.bindings = this; // assign bindings to pipelines
|
||||
|
||||
}
|
||||
|
||||
getForRender( renderObject ) {
|
||||
|
||||
const bindings = renderObject.getBindings();
|
||||
|
||||
const data = this.get( renderObject );
|
||||
|
||||
if ( data.bindings !== bindings ) {
|
||||
|
||||
// each object defines an array of bindings (ubos, textures, samplers etc.)
|
||||
|
||||
data.bindings = bindings;
|
||||
|
||||
this._init( bindings );
|
||||
|
||||
this.backend.createBindings( bindings );
|
||||
|
||||
}
|
||||
|
||||
return data.bindings;
|
||||
|
||||
}
|
||||
|
||||
getForCompute( computeNode ) {
|
||||
|
||||
const data = this.get( computeNode );
|
||||
|
||||
if ( data.bindings === undefined ) {
|
||||
|
||||
const nodeBuilderState = this.nodes.getForCompute( computeNode );
|
||||
|
||||
const bindings = nodeBuilderState.bindings;
|
||||
|
||||
data.bindings = bindings;
|
||||
|
||||
this._init( bindings );
|
||||
|
||||
this.backend.createBindings( bindings );
|
||||
|
||||
}
|
||||
|
||||
return data.bindings;
|
||||
|
||||
}
|
||||
|
||||
updateForCompute( computeNode ) {
|
||||
|
||||
this._update( computeNode, this.getForCompute( computeNode ) );
|
||||
|
||||
}
|
||||
|
||||
updateForRender( renderObject ) {
|
||||
|
||||
this._update( renderObject, this.getForRender( renderObject ) );
|
||||
|
||||
}
|
||||
|
||||
_init( bindings ) {
|
||||
|
||||
for ( const binding of bindings ) {
|
||||
|
||||
if ( binding.isSampledTexture ) {
|
||||
|
||||
this.textures.updateTexture( binding.texture );
|
||||
|
||||
} else if ( binding.isStorageBuffer ) {
|
||||
|
||||
const attribute = binding.attribute;
|
||||
|
||||
this.attributes.update( attribute, AttributeType.STORAGE );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_update( object, bindings ) {
|
||||
|
||||
const { backend } = this;
|
||||
|
||||
let needsBindingsUpdate = false;
|
||||
|
||||
// iterate over all bindings and check if buffer updates or a new binding group is required
|
||||
|
||||
for ( const binding of bindings ) {
|
||||
|
||||
if ( binding.isNodeUniformsGroup ) {
|
||||
|
||||
const updated = this.nodes.updateGroup( binding );
|
||||
|
||||
if ( ! updated ) continue;
|
||||
|
||||
}
|
||||
|
||||
if ( binding.isUniformBuffer ) {
|
||||
|
||||
const updated = binding.update();
|
||||
|
||||
if ( updated ) {
|
||||
|
||||
backend.updateBinding( binding );
|
||||
|
||||
}
|
||||
|
||||
} else if ( binding.isSampledTexture ) {
|
||||
|
||||
const texture = binding.texture;
|
||||
|
||||
if ( binding.needsBindingsUpdate ) needsBindingsUpdate = true;
|
||||
|
||||
const updated = binding.update();
|
||||
|
||||
if ( updated ) {
|
||||
|
||||
this.textures.updateTexture( binding.texture );
|
||||
|
||||
}
|
||||
|
||||
if ( texture.isStorageTexture === true ) {
|
||||
|
||||
const textureData = this.get( texture );
|
||||
|
||||
if ( binding.store === true ) {
|
||||
|
||||
textureData.needsMipmap = true;
|
||||
|
||||
} else if ( texture.generateMipmaps === true && this.textures.needsMipmaps( texture ) && textureData.needsMipmap === true ) {
|
||||
|
||||
this.backend.generateMipmaps( texture );
|
||||
|
||||
textureData.needsMipmap = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( needsBindingsUpdate === true ) {
|
||||
|
||||
const pipeline = this.pipelines.getForRender( object );
|
||||
|
||||
this.backend.updateBindings( bindings, pipeline );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Bindings;
|
38
static/sdk/three/jsm/renderers/common/Buffer.js
Normal file
38
static/sdk/three/jsm/renderers/common/Buffer.js
Normal file
@ -0,0 +1,38 @@
|
||||
import Binding from './Binding.js';
|
||||
import { getFloatLength } from './BufferUtils.js';
|
||||
|
||||
class Buffer extends Binding {
|
||||
|
||||
constructor( name, buffer = null ) {
|
||||
|
||||
super( name );
|
||||
|
||||
this.isBuffer = true;
|
||||
|
||||
this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT;
|
||||
|
||||
this._buffer = buffer;
|
||||
|
||||
}
|
||||
|
||||
get byteLength() {
|
||||
|
||||
return getFloatLength( this._buffer.byteLength );
|
||||
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
|
||||
return this._buffer;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Buffer;
|
33
static/sdk/three/jsm/renderers/common/BufferUtils.js
Normal file
33
static/sdk/three/jsm/renderers/common/BufferUtils.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { GPU_CHUNK_BYTES } from './Constants.js';
|
||||
|
||||
function getFloatLength( floatLength ) {
|
||||
|
||||
// ensure chunk size alignment (STD140 layout)
|
||||
|
||||
return floatLength + ( ( GPU_CHUNK_BYTES - ( floatLength % GPU_CHUNK_BYTES ) ) % GPU_CHUNK_BYTES );
|
||||
|
||||
}
|
||||
|
||||
function getVectorLength( count, vectorLength = 4 ) {
|
||||
|
||||
const strideLength = getStrideLength( vectorLength );
|
||||
|
||||
const floatLength = strideLength * count;
|
||||
|
||||
return getFloatLength( floatLength );
|
||||
|
||||
}
|
||||
|
||||
function getStrideLength( vectorLength ) {
|
||||
|
||||
const strideLength = 4;
|
||||
|
||||
return vectorLength + ( ( strideLength - ( vectorLength % strideLength ) ) % strideLength );
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
getFloatLength,
|
||||
getVectorLength,
|
||||
getStrideLength
|
||||
};
|
89
static/sdk/three/jsm/renderers/common/ChainMap.js
Normal file
89
static/sdk/three/jsm/renderers/common/ChainMap.js
Normal file
@ -0,0 +1,89 @@
|
||||
export default class ChainMap {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.weakMap = new WeakMap();
|
||||
|
||||
}
|
||||
|
||||
get( keys ) {
|
||||
|
||||
if ( Array.isArray( keys ) ) {
|
||||
|
||||
let map = this.weakMap;
|
||||
|
||||
for ( let i = 0; i < keys.length; i ++ ) {
|
||||
|
||||
map = map.get( keys[ i ] );
|
||||
|
||||
if ( map === undefined ) return undefined;
|
||||
|
||||
}
|
||||
|
||||
return map.get( keys[ keys.length - 1 ] );
|
||||
|
||||
} else {
|
||||
|
||||
return super.get( keys );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
set( keys, value ) {
|
||||
|
||||
if ( Array.isArray( keys ) ) {
|
||||
|
||||
let map = this.weakMap;
|
||||
|
||||
for ( let i = 0; i < keys.length; i ++ ) {
|
||||
|
||||
const key = keys[ i ];
|
||||
|
||||
if ( map.has( key ) === false ) map.set( key, new WeakMap() );
|
||||
|
||||
map = map.get( key );
|
||||
|
||||
}
|
||||
|
||||
return map.set( keys[ keys.length - 1 ], value );
|
||||
|
||||
} else {
|
||||
|
||||
return super.set( keys, value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete( keys ) {
|
||||
|
||||
if ( Array.isArray( keys ) ) {
|
||||
|
||||
let map = this.weakMap;
|
||||
|
||||
for ( let i = 0; i < keys.length; i ++ ) {
|
||||
|
||||
map = map.get( keys[ i ] );
|
||||
|
||||
if ( map === undefined ) return false;
|
||||
|
||||
}
|
||||
|
||||
return map.delete( keys[ keys.length - 1 ] );
|
||||
|
||||
} else {
|
||||
|
||||
return super.delete( keys );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.weakMap.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
165
static/sdk/three/jsm/renderers/common/ClippingContext.js
Normal file
165
static/sdk/three/jsm/renderers/common/ClippingContext.js
Normal file
@ -0,0 +1,165 @@
|
||||
import { Matrix3, Plane, Vector4 } from 'three';
|
||||
|
||||
const _plane = new Plane();
|
||||
const _viewNormalMatrix = new Matrix3();
|
||||
|
||||
let _clippingContextVersion = 0;
|
||||
|
||||
class ClippingContext {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.version = ++ _clippingContextVersion;
|
||||
|
||||
this.globalClippingCount = 0;
|
||||
|
||||
this.localClippingCount = 0;
|
||||
this.localClippingEnabled = false;
|
||||
this.localClipIntersection = false;
|
||||
|
||||
this.planes = [];
|
||||
|
||||
this.parentVersion = 0;
|
||||
|
||||
}
|
||||
|
||||
projectPlanes( source, offset ) {
|
||||
|
||||
const l = source.length;
|
||||
const planes = this.planes;
|
||||
|
||||
for ( let i = 0; i < l; i ++ ) {
|
||||
|
||||
_plane.copy( source[ i ] ).applyMatrix4( this.viewMatrix, _viewNormalMatrix );
|
||||
|
||||
const v = planes[ offset + i ];
|
||||
const normal = _plane.normal;
|
||||
|
||||
v.x = - normal.x;
|
||||
v.y = - normal.y;
|
||||
v.z = - normal.z;
|
||||
v.w = _plane.constant;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateGlobal( renderer, camera ) {
|
||||
|
||||
const rendererClippingPlanes = renderer.clippingPlanes;
|
||||
this.viewMatrix = camera.matrixWorldInverse;
|
||||
|
||||
_viewNormalMatrix.getNormalMatrix( this.viewMatrix );
|
||||
|
||||
let update = false;
|
||||
|
||||
if ( Array.isArray( rendererClippingPlanes ) && rendererClippingPlanes.length !== 0 ) {
|
||||
|
||||
const l = rendererClippingPlanes.length;
|
||||
|
||||
if ( l !== this.globalClippingCount ) {
|
||||
|
||||
const planes = [];
|
||||
|
||||
for ( let i = 0; i < l; i ++ ) {
|
||||
|
||||
planes.push( new Vector4() );
|
||||
|
||||
}
|
||||
|
||||
this.globalClippingCount = l;
|
||||
this.planes = planes;
|
||||
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
this.projectPlanes( rendererClippingPlanes, 0 );
|
||||
|
||||
} else if ( this.globalClippingCount !== 0 ) {
|
||||
|
||||
this.globalClippingCount = 0;
|
||||
this.planes = [];
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
if ( renderer.localClippingEnabled !== this.localClippingEnabled ) {
|
||||
|
||||
this.localClippingEnabled = renderer.localClippingEnabled;
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
if ( update ) this.version = _clippingContextVersion ++;
|
||||
|
||||
}
|
||||
|
||||
update( parent, material ) {
|
||||
|
||||
let update = false;
|
||||
|
||||
if ( this !== parent && parent.version !== this.parentVersion ) {
|
||||
|
||||
this.globalClippingCount = material.isShadowNodeMaterial ? 0 : parent.globalClippingCount;
|
||||
this.localClippingEnabled = parent.localClippingEnabled;
|
||||
this.planes = Array.from( parent.planes );
|
||||
this.parentVersion = parent.version;
|
||||
this.viewMatrix = parent.viewMatrix;
|
||||
|
||||
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
if ( this.localClippingEnabled ) {
|
||||
|
||||
const localClippingPlanes = material.clippingPlanes;
|
||||
|
||||
if ( ( Array.isArray( localClippingPlanes ) && localClippingPlanes.length !== 0 ) ) {
|
||||
|
||||
const l = localClippingPlanes.length;
|
||||
const planes = this.planes;
|
||||
const offset = this.globalClippingCount;
|
||||
|
||||
if ( update || l !== this.localClippingCount ) {
|
||||
|
||||
planes.length = offset + l;
|
||||
|
||||
for ( let i = 0; i < l; i ++ ) {
|
||||
|
||||
planes[ offset + i ] = new Vector4();
|
||||
|
||||
}
|
||||
|
||||
this.localClippingCount = l;
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
this.projectPlanes( localClippingPlanes, offset );
|
||||
|
||||
|
||||
} else if ( this.localClippingCount !== 0 ) {
|
||||
|
||||
this.localClippingCount = 0;
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
if ( this.localClipIntersection !== material.clipIntersection ) {
|
||||
|
||||
this.localClipIntersection = material.clipIntersection;
|
||||
update = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( update ) this.version = _clippingContextVersion ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ClippingContext;
|
37
static/sdk/three/jsm/renderers/common/Color4.js
Normal file
37
static/sdk/three/jsm/renderers/common/Color4.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { Color } from 'three';
|
||||
|
||||
class Color4 extends Color {
|
||||
|
||||
constructor( r, g, b, a = 1 ) {
|
||||
|
||||
super( r, g, b );
|
||||
|
||||
this.a = a;
|
||||
|
||||
}
|
||||
|
||||
set( r, g, b, a = 1 ) {
|
||||
|
||||
this.a = a;
|
||||
|
||||
return super.set( r, g, b );
|
||||
|
||||
}
|
||||
|
||||
copy( color ) {
|
||||
|
||||
if ( color.a !== undefined ) this.a = color.a;
|
||||
|
||||
return super.copy( color );
|
||||
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
return new this.constructor( this.r, this.g, this.b, this.a );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Color4;
|
17
static/sdk/three/jsm/renderers/common/ComputePipeline.js
Normal file
17
static/sdk/three/jsm/renderers/common/ComputePipeline.js
Normal file
@ -0,0 +1,17 @@
|
||||
import Pipeline from './Pipeline.js';
|
||||
|
||||
class ComputePipeline extends Pipeline {
|
||||
|
||||
constructor( cacheKey, computeProgram ) {
|
||||
|
||||
super( cacheKey );
|
||||
|
||||
this.computeProgram = computeProgram;
|
||||
|
||||
this.isComputePipeline = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ComputePipeline;
|
14
static/sdk/three/jsm/renderers/common/Constants.js
Normal file
14
static/sdk/three/jsm/renderers/common/Constants.js
Normal file
@ -0,0 +1,14 @@
|
||||
export const AttributeType = {
|
||||
VERTEX: 1,
|
||||
INDEX: 2,
|
||||
STORAGE: 4
|
||||
};
|
||||
|
||||
// size of a chunk in bytes (STD140 layout)
|
||||
|
||||
export const GPU_CHUNK_BYTES = 16;
|
||||
|
||||
// @TODO: Move to src/constants.js
|
||||
|
||||
export const BlendColorFactor = 211;
|
||||
export const OneMinusBlendColorFactor = 212;
|
65
static/sdk/three/jsm/renderers/common/CubeRenderTarget.js
Normal file
65
static/sdk/three/jsm/renderers/common/CubeRenderTarget.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { WebGLCubeRenderTarget, Scene, CubeCamera, BoxGeometry, Mesh, BackSide, NoBlending, LinearFilter, LinearMipmapLinearFilter } from 'three';
|
||||
import { equirectUV } from '../../nodes/utils/EquirectUVNode.js';
|
||||
import { texture as TSL_Texture } from '../../nodes/accessors/TextureNode.js';
|
||||
import { positionWorldDirection } from '../../nodes/accessors/PositionNode.js';
|
||||
import { createNodeMaterialFromType } from '../../nodes/materials/NodeMaterial.js';
|
||||
|
||||
// @TODO: Consider rename WebGLCubeRenderTarget to just CubeRenderTarget
|
||||
|
||||
class CubeRenderTarget extends WebGLCubeRenderTarget {
|
||||
|
||||
constructor( size = 1, options = {} ) {
|
||||
|
||||
super( size, options );
|
||||
|
||||
this.isCubeRenderTarget = true;
|
||||
|
||||
}
|
||||
|
||||
fromEquirectangularTexture( renderer, texture ) {
|
||||
|
||||
const currentMinFilter = texture.minFilter;
|
||||
const currentGenerateMipmaps = texture.generateMipmaps;
|
||||
|
||||
texture.generateMipmaps = true;
|
||||
|
||||
this.texture.type = texture.type;
|
||||
this.texture.colorSpace = texture.colorSpace;
|
||||
|
||||
this.texture.generateMipmaps = texture.generateMipmaps;
|
||||
this.texture.minFilter = texture.minFilter;
|
||||
this.texture.magFilter = texture.magFilter;
|
||||
|
||||
const geometry = new BoxGeometry( 5, 5, 5 );
|
||||
|
||||
const uvNode = equirectUV( positionWorldDirection );
|
||||
|
||||
const material = createNodeMaterialFromType( 'MeshBasicNodeMaterial' );
|
||||
material.colorNode = TSL_Texture( texture, uvNode, 0 );
|
||||
material.side = BackSide;
|
||||
material.blending = NoBlending;
|
||||
|
||||
const mesh = new Mesh( geometry, material );
|
||||
|
||||
const scene = new Scene();
|
||||
scene.add( mesh );
|
||||
|
||||
// Avoid blurred poles
|
||||
if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;
|
||||
|
||||
const camera = new CubeCamera( 1, 10, this );
|
||||
camera.update( renderer, scene );
|
||||
|
||||
texture.minFilter = currentMinFilter;
|
||||
texture.currentGenerateMipmaps = currentGenerateMipmaps;
|
||||
|
||||
mesh.geometry.dispose();
|
||||
mesh.material.dispose();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CubeRenderTarget;
|
54
static/sdk/three/jsm/renderers/common/DataMap.js
Normal file
54
static/sdk/three/jsm/renderers/common/DataMap.js
Normal file
@ -0,0 +1,54 @@
|
||||
class DataMap {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.data = new WeakMap();
|
||||
|
||||
}
|
||||
|
||||
get( object ) {
|
||||
|
||||
let map = this.data.get( object );
|
||||
|
||||
if ( map === undefined ) {
|
||||
|
||||
map = {};
|
||||
this.data.set( object, map );
|
||||
|
||||
}
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
delete( object ) {
|
||||
|
||||
let map;
|
||||
|
||||
if ( this.data.has( object ) ) {
|
||||
|
||||
map = this.data.get( object );
|
||||
|
||||
this.data.delete( object );
|
||||
|
||||
}
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
has( object ) {
|
||||
|
||||
return this.data.has( object );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.data = new WeakMap();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DataMap;
|
215
static/sdk/three/jsm/renderers/common/Geometries.js
Normal file
215
static/sdk/three/jsm/renderers/common/Geometries.js
Normal file
@ -0,0 +1,215 @@
|
||||
import DataMap from './DataMap.js';
|
||||
import { AttributeType } from './Constants.js';
|
||||
import { Uint32BufferAttribute, Uint16BufferAttribute } from 'three';
|
||||
|
||||
function arrayNeedsUint32( array ) {
|
||||
|
||||
// assumes larger values usually on last
|
||||
|
||||
for ( let i = array.length - 1; i >= 0; -- i ) {
|
||||
|
||||
if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function getWireframeVersion( geometry ) {
|
||||
|
||||
return ( geometry.index !== null ) ? geometry.index.version : geometry.attributes.position.version;
|
||||
|
||||
}
|
||||
|
||||
function getWireframeIndex( geometry ) {
|
||||
|
||||
const indices = [];
|
||||
|
||||
const geometryIndex = geometry.index;
|
||||
const geometryPosition = geometry.attributes.position;
|
||||
|
||||
if ( geometryIndex !== null ) {
|
||||
|
||||
const array = geometryIndex.array;
|
||||
|
||||
for ( let i = 0, l = array.length; i < l; i += 3 ) {
|
||||
|
||||
const a = array[ i + 0 ];
|
||||
const b = array[ i + 1 ];
|
||||
const c = array[ i + 2 ];
|
||||
|
||||
indices.push( a, b, b, c, c, a );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const array = geometryPosition.array;
|
||||
|
||||
for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
|
||||
|
||||
const a = i + 0;
|
||||
const b = i + 1;
|
||||
const c = i + 2;
|
||||
|
||||
indices.push( a, b, b, c, c, a );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
|
||||
attribute.version = getWireframeVersion( geometry );
|
||||
|
||||
return attribute;
|
||||
|
||||
}
|
||||
|
||||
class Geometries extends DataMap {
|
||||
|
||||
constructor( attributes, info ) {
|
||||
|
||||
super();
|
||||
|
||||
this.attributes = attributes;
|
||||
this.info = info;
|
||||
|
||||
this.wireframes = new WeakMap();
|
||||
this.attributeCall = new WeakMap();
|
||||
|
||||
}
|
||||
|
||||
has( renderObject ) {
|
||||
|
||||
const geometry = renderObject.geometry;
|
||||
|
||||
return super.has( geometry ) && this.get( geometry ).initialized === true;
|
||||
|
||||
}
|
||||
|
||||
updateForRender( renderObject ) {
|
||||
|
||||
if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
|
||||
|
||||
this.updateAttributes( renderObject );
|
||||
|
||||
}
|
||||
|
||||
initGeometry( renderObject ) {
|
||||
|
||||
const geometry = renderObject.geometry;
|
||||
const geometryData = this.get( geometry );
|
||||
|
||||
geometryData.initialized = true;
|
||||
|
||||
this.info.memory.geometries ++;
|
||||
|
||||
const onDispose = () => {
|
||||
|
||||
this.info.memory.geometries --;
|
||||
|
||||
const index = geometry.index;
|
||||
const geometryAttributes = renderObject.getAttributes();
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
this.attributes.delete( index );
|
||||
|
||||
}
|
||||
|
||||
for ( const geometryAttribute of geometryAttributes ) {
|
||||
|
||||
this.attributes.delete( geometryAttribute );
|
||||
|
||||
}
|
||||
|
||||
const wireframeAttribute = this.wireframes.get( geometry );
|
||||
|
||||
if ( wireframeAttribute !== undefined ) {
|
||||
|
||||
this.attributes.delete( wireframeAttribute );
|
||||
|
||||
}
|
||||
|
||||
geometry.removeEventListener( 'dispose', onDispose );
|
||||
|
||||
};
|
||||
|
||||
geometry.addEventListener( 'dispose', onDispose );
|
||||
|
||||
}
|
||||
|
||||
updateAttributes( renderObject ) {
|
||||
|
||||
const attributes = renderObject.getAttributes();
|
||||
|
||||
for ( const attribute of attributes ) {
|
||||
|
||||
this.updateAttribute( attribute, AttributeType.VERTEX );
|
||||
|
||||
}
|
||||
|
||||
const index = this.getIndex( renderObject );
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
this.updateAttribute( index, AttributeType.INDEX );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateAttribute( attribute, type ) {
|
||||
|
||||
const callId = this.info.render.calls;
|
||||
|
||||
if ( this.attributeCall.get( attribute ) !== callId ) {
|
||||
|
||||
this.attributes.update( attribute, type );
|
||||
|
||||
this.attributeCall.set( attribute, callId );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getIndex( renderObject ) {
|
||||
|
||||
const { geometry, material } = renderObject;
|
||||
|
||||
let index = geometry.index;
|
||||
|
||||
if ( material.wireframe === true ) {
|
||||
|
||||
const wireframes = this.wireframes;
|
||||
|
||||
let wireframeAttribute = wireframes.get( geometry );
|
||||
|
||||
if ( wireframeAttribute === undefined ) {
|
||||
|
||||
wireframeAttribute = getWireframeIndex( geometry );
|
||||
|
||||
wireframes.set( geometry, wireframeAttribute );
|
||||
|
||||
} else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) {
|
||||
|
||||
this.attributes.delete( wireframeAttribute );
|
||||
|
||||
wireframeAttribute = getWireframeIndex( geometry );
|
||||
|
||||
wireframes.set( geometry, wireframeAttribute );
|
||||
|
||||
}
|
||||
|
||||
index = wireframeAttribute;
|
||||
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Geometries;
|
99
static/sdk/three/jsm/renderers/common/Info.js
Normal file
99
static/sdk/three/jsm/renderers/common/Info.js
Normal file
@ -0,0 +1,99 @@
|
||||
class Info {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.autoReset = true;
|
||||
|
||||
this.frame = 0;
|
||||
this.calls = 0;
|
||||
|
||||
this.render = {
|
||||
calls: 0,
|
||||
drawCalls: 0,
|
||||
triangles: 0,
|
||||
points: 0,
|
||||
lines: 0,
|
||||
timestamp: 0
|
||||
};
|
||||
|
||||
this.compute = {
|
||||
calls: 0,
|
||||
computeCalls: 0,
|
||||
timestamp: 0
|
||||
};
|
||||
|
||||
this.memory = {
|
||||
geometries: 0,
|
||||
textures: 0
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
update( object, count, instanceCount ) {
|
||||
|
||||
this.render.drawCalls ++;
|
||||
|
||||
if ( object.isMesh || object.isSprite ) {
|
||||
|
||||
this.render.triangles += instanceCount * ( count / 3 );
|
||||
|
||||
} else if ( object.isPoints ) {
|
||||
|
||||
this.render.points += instanceCount * count;
|
||||
|
||||
} else if ( object.isLineSegments ) {
|
||||
|
||||
this.render.lines += instanceCount * ( count / 2 );
|
||||
|
||||
} else if ( object.isLine ) {
|
||||
|
||||
this.render.lines += instanceCount * ( count - 1 );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'THREE.WebGPUInfo: Unknown object type.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateTimestamp( type, time ) {
|
||||
|
||||
this[ type ].timestamp += time;
|
||||
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.render.drawCalls = 0;
|
||||
this.compute.computeCalls = 0;
|
||||
|
||||
this.render.triangles = 0;
|
||||
this.render.points = 0;
|
||||
this.render.lines = 0;
|
||||
|
||||
this.render.timestamp = 0;
|
||||
this.compute.timestamp = 0;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.reset();
|
||||
|
||||
this.calls = 0;
|
||||
|
||||
this.render.calls = 0;
|
||||
this.compute.calls = 0;
|
||||
|
||||
this.render.timestamp = 0;
|
||||
this.compute.timestamp = 0;
|
||||
this.memory.geometries = 0;
|
||||
this.memory.textures = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Info;
|
13
static/sdk/three/jsm/renderers/common/Pipeline.js
Normal file
13
static/sdk/three/jsm/renderers/common/Pipeline.js
Normal file
@ -0,0 +1,13 @@
|
||||
class Pipeline {
|
||||
|
||||
constructor( cacheKey ) {
|
||||
|
||||
this.cacheKey = cacheKey;
|
||||
|
||||
this.usedTimes = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Pipeline;
|
322
static/sdk/three/jsm/renderers/common/Pipelines.js
Normal file
322
static/sdk/three/jsm/renderers/common/Pipelines.js
Normal file
@ -0,0 +1,322 @@
|
||||
import DataMap from './DataMap.js';
|
||||
import RenderPipeline from './RenderPipeline.js';
|
||||
import ComputePipeline from './ComputePipeline.js';
|
||||
import ProgrammableStage from './ProgrammableStage.js';
|
||||
|
||||
class Pipelines extends DataMap {
|
||||
|
||||
constructor( backend, nodes ) {
|
||||
|
||||
super();
|
||||
|
||||
this.backend = backend;
|
||||
this.nodes = nodes;
|
||||
|
||||
this.bindings = null; // set by the bindings
|
||||
|
||||
this.caches = new Map();
|
||||
this.programs = {
|
||||
vertex: new Map(),
|
||||
fragment: new Map(),
|
||||
compute: new Map()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
getForCompute( computeNode, bindings ) {
|
||||
|
||||
const { backend } = this;
|
||||
|
||||
const data = this.get( computeNode );
|
||||
|
||||
if ( this._needsComputeUpdate( computeNode ) ) {
|
||||
|
||||
const previousPipeline = data.pipeline;
|
||||
|
||||
if ( previousPipeline ) {
|
||||
|
||||
previousPipeline.usedTimes --;
|
||||
previousPipeline.computeProgram.usedTimes --;
|
||||
|
||||
}
|
||||
|
||||
// get shader
|
||||
|
||||
const nodeBuilderState = this.nodes.getForCompute( computeNode );
|
||||
|
||||
// programmable stage
|
||||
|
||||
let stageCompute = this.programs.compute.get( nodeBuilderState.computeShader );
|
||||
|
||||
if ( stageCompute === undefined ) {
|
||||
|
||||
if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram );
|
||||
|
||||
stageCompute = new ProgrammableStage( nodeBuilderState.computeShader, 'compute', nodeBuilderState.transforms, nodeBuilderState.nodeAttributes );
|
||||
this.programs.compute.set( nodeBuilderState.computeShader, stageCompute );
|
||||
|
||||
backend.createProgram( stageCompute );
|
||||
|
||||
}
|
||||
|
||||
// determine compute pipeline
|
||||
|
||||
const cacheKey = this._getComputeCacheKey( computeNode, stageCompute );
|
||||
|
||||
let pipeline = this.caches.get( cacheKey );
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( computeNode );
|
||||
|
||||
pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings );
|
||||
|
||||
}
|
||||
|
||||
// keep track of all used times
|
||||
|
||||
pipeline.usedTimes ++;
|
||||
stageCompute.usedTimes ++;
|
||||
|
||||
//
|
||||
|
||||
data.version = computeNode.version;
|
||||
data.pipeline = pipeline;
|
||||
|
||||
}
|
||||
|
||||
return data.pipeline;
|
||||
|
||||
}
|
||||
|
||||
getForRender( renderObject, promises = null ) {
|
||||
|
||||
const { backend } = this;
|
||||
|
||||
const data = this.get( renderObject );
|
||||
|
||||
if ( this._needsRenderUpdate( renderObject ) ) {
|
||||
|
||||
const previousPipeline = data.pipeline;
|
||||
|
||||
if ( previousPipeline ) {
|
||||
|
||||
previousPipeline.usedTimes --;
|
||||
previousPipeline.vertexProgram.usedTimes --;
|
||||
previousPipeline.fragmentProgram.usedTimes --;
|
||||
|
||||
}
|
||||
|
||||
// get shader
|
||||
|
||||
const nodeBuilderState = renderObject.getNodeBuilderState();
|
||||
|
||||
// programmable stages
|
||||
|
||||
let stageVertex = this.programs.vertex.get( nodeBuilderState.vertexShader );
|
||||
|
||||
if ( stageVertex === undefined ) {
|
||||
|
||||
if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram );
|
||||
|
||||
stageVertex = new ProgrammableStage( nodeBuilderState.vertexShader, 'vertex' );
|
||||
this.programs.vertex.set( nodeBuilderState.vertexShader, stageVertex );
|
||||
|
||||
backend.createProgram( stageVertex );
|
||||
|
||||
}
|
||||
|
||||
let stageFragment = this.programs.fragment.get( nodeBuilderState.fragmentShader );
|
||||
|
||||
if ( stageFragment === undefined ) {
|
||||
|
||||
if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram );
|
||||
|
||||
stageFragment = new ProgrammableStage( nodeBuilderState.fragmentShader, 'fragment' );
|
||||
this.programs.fragment.set( nodeBuilderState.fragmentShader, stageFragment );
|
||||
|
||||
backend.createProgram( stageFragment );
|
||||
|
||||
}
|
||||
|
||||
// determine render pipeline
|
||||
|
||||
const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
|
||||
|
||||
let pipeline = this.caches.get( cacheKey );
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline );
|
||||
|
||||
pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises );
|
||||
|
||||
} else {
|
||||
|
||||
renderObject.pipeline = pipeline;
|
||||
|
||||
}
|
||||
|
||||
// keep track of all used times
|
||||
|
||||
pipeline.usedTimes ++;
|
||||
stageVertex.usedTimes ++;
|
||||
stageFragment.usedTimes ++;
|
||||
|
||||
//
|
||||
|
||||
data.pipeline = pipeline;
|
||||
|
||||
}
|
||||
|
||||
return data.pipeline;
|
||||
|
||||
}
|
||||
|
||||
delete( object ) {
|
||||
|
||||
const pipeline = this.get( object ).pipeline;
|
||||
|
||||
if ( pipeline ) {
|
||||
|
||||
// pipeline
|
||||
|
||||
pipeline.usedTimes --;
|
||||
|
||||
if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline );
|
||||
|
||||
// programs
|
||||
|
||||
if ( pipeline.isComputePipeline ) {
|
||||
|
||||
pipeline.computeProgram.usedTimes --;
|
||||
|
||||
if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram );
|
||||
|
||||
} else {
|
||||
|
||||
pipeline.fragmentProgram.usedTimes --;
|
||||
pipeline.vertexProgram.usedTimes --;
|
||||
|
||||
if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram );
|
||||
if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
super.delete( object );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
super.dispose();
|
||||
|
||||
this.caches = new Map();
|
||||
this.programs = {
|
||||
vertex: new Map(),
|
||||
fragment: new Map(),
|
||||
compute: new Map()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
updateForRender( renderObject ) {
|
||||
|
||||
this.getForRender( renderObject );
|
||||
|
||||
}
|
||||
|
||||
_getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) {
|
||||
|
||||
// check for existing pipeline
|
||||
|
||||
cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute );
|
||||
|
||||
let pipeline = this.caches.get( cacheKey );
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
pipeline = new ComputePipeline( cacheKey, stageCompute );
|
||||
|
||||
this.caches.set( cacheKey, pipeline );
|
||||
|
||||
this.backend.createComputePipeline( pipeline, bindings );
|
||||
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
|
||||
}
|
||||
|
||||
_getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ) {
|
||||
|
||||
// check for existing pipeline
|
||||
|
||||
cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
|
||||
|
||||
let pipeline = this.caches.get( cacheKey );
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment );
|
||||
|
||||
this.caches.set( cacheKey, pipeline );
|
||||
|
||||
renderObject.pipeline = pipeline;
|
||||
|
||||
this.backend.createRenderPipeline( renderObject, promises );
|
||||
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
|
||||
}
|
||||
|
||||
_getComputeCacheKey( computeNode, stageCompute ) {
|
||||
|
||||
return computeNode.id + ',' + stageCompute.id;
|
||||
|
||||
}
|
||||
|
||||
_getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
|
||||
|
||||
return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey( renderObject );
|
||||
|
||||
}
|
||||
|
||||
_releasePipeline( pipeline ) {
|
||||
|
||||
this.caches.delete( pipeline.cacheKey );
|
||||
|
||||
}
|
||||
|
||||
_releaseProgram( program ) {
|
||||
|
||||
const code = program.code;
|
||||
const stage = program.stage;
|
||||
|
||||
this.programs[ stage ].delete( code );
|
||||
|
||||
}
|
||||
|
||||
_needsComputeUpdate( computeNode ) {
|
||||
|
||||
const data = this.get( computeNode );
|
||||
|
||||
return data.pipeline === undefined || data.version !== computeNode.version;
|
||||
|
||||
}
|
||||
|
||||
_needsRenderUpdate( renderObject ) {
|
||||
|
||||
const data = this.get( renderObject );
|
||||
|
||||
return data.pipeline === undefined || this.backend.needsRenderUpdate( renderObject );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Pipelines;
|
33
static/sdk/three/jsm/renderers/common/PostProcessing.js
Normal file
33
static/sdk/three/jsm/renderers/common/PostProcessing.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { vec4, NodeMaterial } from '../../nodes/Nodes.js';
|
||||
import QuadMesh from '../../objects/QuadMesh.js';
|
||||
|
||||
const quadMesh = new QuadMesh( new NodeMaterial() );
|
||||
|
||||
class PostProcessing {
|
||||
|
||||
constructor( renderer, outputNode = vec4( 0, 0, 1, 1 ) ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
this.outputNode = outputNode;
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
quadMesh.material.fragmentNode = this.outputNode;
|
||||
|
||||
quadMesh.render( this.renderer );
|
||||
|
||||
}
|
||||
|
||||
renderAsync() {
|
||||
|
||||
quadMesh.material.fragmentNode = this.outputNode;
|
||||
|
||||
return quadMesh.renderAsync( this.renderer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PostProcessing;
|
20
static/sdk/three/jsm/renderers/common/ProgrammableStage.js
Normal file
20
static/sdk/three/jsm/renderers/common/ProgrammableStage.js
Normal file
@ -0,0 +1,20 @@
|
||||
let _id = 0;
|
||||
|
||||
class ProgrammableStage {
|
||||
|
||||
constructor( code, type, transforms = null, attributes = null ) {
|
||||
|
||||
this.id = _id ++;
|
||||
|
||||
this.code = code;
|
||||
this.stage = type;
|
||||
this.transforms = transforms;
|
||||
this.attributes = attributes;
|
||||
|
||||
this.usedTimes = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ProgrammableStage;
|
43
static/sdk/three/jsm/renderers/common/RenderContext.js
Normal file
43
static/sdk/three/jsm/renderers/common/RenderContext.js
Normal file
@ -0,0 +1,43 @@
|
||||
import { Vector4 } from 'three';
|
||||
|
||||
let id = 0;
|
||||
|
||||
class RenderContext {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.id = id ++;
|
||||
|
||||
this.color = true;
|
||||
this.clearColor = true;
|
||||
this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 };
|
||||
|
||||
this.depth = true;
|
||||
this.clearDepth = true;
|
||||
this.clearDepthValue = 1;
|
||||
|
||||
this.stencil = false;
|
||||
this.clearStencil = true;
|
||||
this.clearStencilValue = 1;
|
||||
|
||||
this.viewport = false;
|
||||
this.viewportValue = new Vector4();
|
||||
|
||||
this.scissor = false;
|
||||
this.scissorValue = new Vector4();
|
||||
|
||||
this.textures = null;
|
||||
this.depthTexture = null;
|
||||
this.activeCubeFace = 0;
|
||||
this.sampleCount = 1;
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
this.isRenderContext = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderContext;
|
63
static/sdk/three/jsm/renderers/common/RenderContexts.js
Normal file
63
static/sdk/three/jsm/renderers/common/RenderContexts.js
Normal file
@ -0,0 +1,63 @@
|
||||
import ChainMap from './ChainMap.js';
|
||||
import RenderContext from './RenderContext.js';
|
||||
|
||||
class RenderContexts {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.chainMaps = {};
|
||||
|
||||
}
|
||||
|
||||
get( scene, camera, renderTarget = null ) {
|
||||
|
||||
const chainKey = [ scene, camera ];
|
||||
|
||||
let attachmentState;
|
||||
|
||||
if ( renderTarget === null ) {
|
||||
|
||||
attachmentState = 'default';
|
||||
|
||||
} else {
|
||||
|
||||
const format = renderTarget.texture.format;
|
||||
const count = renderTarget.count;
|
||||
|
||||
attachmentState = `${ count }:${ format }:${ renderTarget.samples }:${ renderTarget.depthBuffer }:${ renderTarget.stencilBuffer }`;
|
||||
|
||||
}
|
||||
|
||||
const chainMap = this.getChainMap( attachmentState );
|
||||
|
||||
let renderState = chainMap.get( chainKey );
|
||||
|
||||
if ( renderState === undefined ) {
|
||||
|
||||
renderState = new RenderContext();
|
||||
|
||||
chainMap.set( chainKey, renderState );
|
||||
|
||||
}
|
||||
|
||||
if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
|
||||
|
||||
return renderState;
|
||||
|
||||
}
|
||||
|
||||
getChainMap( attachmentState ) {
|
||||
|
||||
return this.chainMaps[ attachmentState ] || ( this.chainMaps[ attachmentState ] = new ChainMap() );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.chainMaps = {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderContexts;
|
186
static/sdk/three/jsm/renderers/common/RenderList.js
Normal file
186
static/sdk/three/jsm/renderers/common/RenderList.js
Normal file
@ -0,0 +1,186 @@
|
||||
import { LightsNode } from '../../nodes/Nodes.js';
|
||||
|
||||
function painterSortStable( a, b ) {
|
||||
|
||||
if ( a.groupOrder !== b.groupOrder ) {
|
||||
|
||||
return a.groupOrder - b.groupOrder;
|
||||
|
||||
} else if ( a.renderOrder !== b.renderOrder ) {
|
||||
|
||||
return a.renderOrder - b.renderOrder;
|
||||
|
||||
} else if ( a.material.id !== b.material.id ) {
|
||||
|
||||
return a.material.id - b.material.id;
|
||||
|
||||
} else if ( a.z !== b.z ) {
|
||||
|
||||
return a.z - b.z;
|
||||
|
||||
} else {
|
||||
|
||||
return a.id - b.id;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function reversePainterSortStable( a, b ) {
|
||||
|
||||
if ( a.groupOrder !== b.groupOrder ) {
|
||||
|
||||
return a.groupOrder - b.groupOrder;
|
||||
|
||||
} else if ( a.renderOrder !== b.renderOrder ) {
|
||||
|
||||
return a.renderOrder - b.renderOrder;
|
||||
|
||||
} else if ( a.z !== b.z ) {
|
||||
|
||||
return b.z - a.z;
|
||||
|
||||
} else {
|
||||
|
||||
return a.id - b.id;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RenderList {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.renderItems = [];
|
||||
this.renderItemsIndex = 0;
|
||||
|
||||
this.opaque = [];
|
||||
this.transparent = [];
|
||||
|
||||
this.lightsNode = new LightsNode( [] );
|
||||
this.lightsArray = [];
|
||||
|
||||
this.occlusionQueryCount = 0;
|
||||
|
||||
}
|
||||
|
||||
begin() {
|
||||
|
||||
this.renderItemsIndex = 0;
|
||||
|
||||
this.opaque.length = 0;
|
||||
this.transparent.length = 0;
|
||||
this.lightsArray.length = 0;
|
||||
|
||||
this.occlusionQueryCount = 0;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
getNextRenderItem( object, geometry, material, groupOrder, z, group ) {
|
||||
|
||||
let renderItem = this.renderItems[ this.renderItemsIndex ];
|
||||
|
||||
if ( renderItem === undefined ) {
|
||||
|
||||
renderItem = {
|
||||
id: object.id,
|
||||
object: object,
|
||||
geometry: geometry,
|
||||
material: material,
|
||||
groupOrder: groupOrder,
|
||||
renderOrder: object.renderOrder,
|
||||
z: z,
|
||||
group: group
|
||||
};
|
||||
|
||||
this.renderItems[ this.renderItemsIndex ] = renderItem;
|
||||
|
||||
} else {
|
||||
|
||||
renderItem.id = object.id;
|
||||
renderItem.object = object;
|
||||
renderItem.geometry = geometry;
|
||||
renderItem.material = material;
|
||||
renderItem.groupOrder = groupOrder;
|
||||
renderItem.renderOrder = object.renderOrder;
|
||||
renderItem.z = z;
|
||||
renderItem.group = group;
|
||||
|
||||
}
|
||||
|
||||
this.renderItemsIndex ++;
|
||||
|
||||
return renderItem;
|
||||
|
||||
}
|
||||
|
||||
push( object, geometry, material, groupOrder, z, group ) {
|
||||
|
||||
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group );
|
||||
|
||||
if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
|
||||
|
||||
( material.transparent === true || material.transmission > 0 ? this.transparent : this.opaque ).push( renderItem );
|
||||
|
||||
}
|
||||
|
||||
unshift( object, geometry, material, groupOrder, z, group ) {
|
||||
|
||||
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group );
|
||||
|
||||
( material.transparent === true ? this.transparent : this.opaque ).unshift( renderItem );
|
||||
|
||||
}
|
||||
|
||||
pushLight( light ) {
|
||||
|
||||
this.lightsArray.push( light );
|
||||
|
||||
}
|
||||
|
||||
getLightsNode() {
|
||||
|
||||
return this.lightsNode.fromLights( this.lightsArray );
|
||||
|
||||
}
|
||||
|
||||
sort( customOpaqueSort, customTransparentSort ) {
|
||||
|
||||
if ( this.opaque.length > 1 ) this.opaque.sort( customOpaqueSort || painterSortStable );
|
||||
if ( this.transparent.length > 1 ) this.transparent.sort( customTransparentSort || reversePainterSortStable );
|
||||
|
||||
}
|
||||
|
||||
finish() {
|
||||
|
||||
// update lights
|
||||
|
||||
this.lightsNode.fromLights( this.lightsArray );
|
||||
|
||||
// Clear references from inactive renderItems in the list
|
||||
|
||||
for ( let i = this.renderItemsIndex, il = this.renderItems.length; i < il; i ++ ) {
|
||||
|
||||
const renderItem = this.renderItems[ i ];
|
||||
|
||||
if ( renderItem.id === null ) break;
|
||||
|
||||
renderItem.id = null;
|
||||
renderItem.object = null;
|
||||
renderItem.geometry = null;
|
||||
renderItem.material = null;
|
||||
renderItem.groupOrder = null;
|
||||
renderItem.renderOrder = null;
|
||||
renderItem.z = null;
|
||||
renderItem.group = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderList;
|
38
static/sdk/three/jsm/renderers/common/RenderLists.js
Normal file
38
static/sdk/three/jsm/renderers/common/RenderLists.js
Normal file
@ -0,0 +1,38 @@
|
||||
import ChainMap from './ChainMap.js';
|
||||
import RenderList from './RenderList.js';
|
||||
|
||||
class RenderLists {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.lists = new ChainMap();
|
||||
|
||||
}
|
||||
|
||||
get( scene, camera ) {
|
||||
|
||||
const lists = this.lists;
|
||||
const keys = [ scene, camera ];
|
||||
|
||||
let list = lists.get( keys );
|
||||
|
||||
if ( list === undefined ) {
|
||||
|
||||
list = new RenderList();
|
||||
lists.set( keys, list );
|
||||
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.lists = new ChainMap();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderLists;
|
263
static/sdk/three/jsm/renderers/common/RenderObject.js
Normal file
263
static/sdk/three/jsm/renderers/common/RenderObject.js
Normal file
@ -0,0 +1,263 @@
|
||||
import ClippingContext from './ClippingContext.js';
|
||||
|
||||
let id = 0;
|
||||
|
||||
function getKeys( obj ) {
|
||||
|
||||
const keys = Object.keys( obj );
|
||||
|
||||
let proto = Object.getPrototypeOf( obj );
|
||||
|
||||
while ( proto ) {
|
||||
|
||||
const descriptors = Object.getOwnPropertyDescriptors( proto );
|
||||
|
||||
for ( const key in descriptors ) {
|
||||
|
||||
if ( descriptors[ key ] !== undefined ) {
|
||||
|
||||
const descriptor = descriptors[ key ];
|
||||
|
||||
if ( descriptor && typeof descriptor.get === 'function' ) {
|
||||
|
||||
keys.push( key );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
proto = Object.getPrototypeOf( proto );
|
||||
|
||||
}
|
||||
|
||||
return keys;
|
||||
|
||||
}
|
||||
|
||||
export default class RenderObject {
|
||||
|
||||
constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext ) {
|
||||
|
||||
this._nodes = nodes;
|
||||
this._geometries = geometries;
|
||||
|
||||
this.id = id ++;
|
||||
|
||||
this.renderer = renderer;
|
||||
this.object = object;
|
||||
this.material = material;
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
this.lightsNode = lightsNode;
|
||||
this.context = renderContext;
|
||||
|
||||
this.geometry = object.geometry;
|
||||
this.version = material.version;
|
||||
|
||||
this.drawRange = null;
|
||||
|
||||
this.attributes = null;
|
||||
this.pipeline = null;
|
||||
this.vertexBuffers = null;
|
||||
|
||||
this.updateClipping( renderContext.clippingContext );
|
||||
|
||||
this.clippingContextVersion = this.clippingContext.version;
|
||||
|
||||
this.initialNodesCacheKey = this.getNodesCacheKey();
|
||||
this.initialCacheKey = this.getCacheKey();
|
||||
|
||||
this._nodeBuilderState = null;
|
||||
this._bindings = null;
|
||||
|
||||
this.onDispose = null;
|
||||
|
||||
this.isRenderObject = true;
|
||||
|
||||
this.onMaterialDispose = () => {
|
||||
|
||||
this.dispose();
|
||||
|
||||
};
|
||||
|
||||
this.material.addEventListener( 'dispose', this.onMaterialDispose );
|
||||
|
||||
}
|
||||
|
||||
updateClipping( parent ) {
|
||||
|
||||
const material = this.material;
|
||||
|
||||
let clippingContext = this.clippingContext;
|
||||
|
||||
if ( Array.isArray( material.clippingPlanes ) ) {
|
||||
|
||||
if ( clippingContext === parent || ! clippingContext ) {
|
||||
|
||||
clippingContext = new ClippingContext();
|
||||
this.clippingContext = clippingContext;
|
||||
|
||||
}
|
||||
|
||||
clippingContext.update( parent, material );
|
||||
|
||||
} else if ( this.clippingContext !== parent ) {
|
||||
|
||||
this.clippingContext = parent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get clippingNeedsUpdate() {
|
||||
|
||||
if ( this.clippingContext.version === this.clippingContextVersion ) return false;
|
||||
|
||||
this.clippingContextVersion = this.clippingContext.version;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
getNodeBuilderState() {
|
||||
|
||||
return this._nodeBuilderState || ( this._nodeBuilderState = this._nodes.getForRender( this ) );
|
||||
|
||||
}
|
||||
|
||||
getBindings() {
|
||||
|
||||
return this._bindings || ( this._bindings = this.getNodeBuilderState().createBindings() );
|
||||
|
||||
}
|
||||
|
||||
getIndex() {
|
||||
|
||||
return this._geometries.getIndex( this );
|
||||
|
||||
}
|
||||
|
||||
getChainArray() {
|
||||
|
||||
return [ this.object, this.material, this.context, this.lightsNode ];
|
||||
|
||||
}
|
||||
|
||||
getAttributes() {
|
||||
|
||||
if ( this.attributes !== null ) return this.attributes;
|
||||
|
||||
const nodeAttributes = this.getNodeBuilderState().nodeAttributes;
|
||||
const geometry = this.geometry;
|
||||
|
||||
const attributes = [];
|
||||
const vertexBuffers = new Set();
|
||||
|
||||
for ( const nodeAttribute of nodeAttributes ) {
|
||||
|
||||
const attribute = nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name );
|
||||
|
||||
if ( attribute === undefined ) continue;
|
||||
|
||||
attributes.push( attribute );
|
||||
|
||||
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
||||
vertexBuffers.add( bufferAttribute );
|
||||
|
||||
}
|
||||
|
||||
this.attributes = attributes;
|
||||
this.vertexBuffers = Array.from( vertexBuffers.values() );
|
||||
|
||||
return attributes;
|
||||
|
||||
}
|
||||
|
||||
getVertexBuffers() {
|
||||
|
||||
if ( this.vertexBuffers === null ) this.getAttributes();
|
||||
|
||||
return this.vertexBuffers;
|
||||
|
||||
}
|
||||
|
||||
getMaterialCacheKey() {
|
||||
|
||||
const { object, material } = this;
|
||||
|
||||
let cacheKey = material.customProgramCacheKey();
|
||||
|
||||
for ( const property of getKeys( material ) ) {
|
||||
|
||||
if ( /^(is[A-Z]|_)|^(visible|version|uuid|name|opacity|userData)$/.test( property ) ) continue;
|
||||
|
||||
let value = material[ property ];
|
||||
|
||||
if ( value !== null ) {
|
||||
|
||||
const type = typeof value;
|
||||
|
||||
if ( type === 'number' ) value = value !== 0 ? '1' : '0'; // Convert to on/off, important for clearcoat, transmission, etc
|
||||
else if ( type === 'object' ) value = '{}';
|
||||
|
||||
}
|
||||
|
||||
cacheKey += /*property + ':' +*/ value + ',';
|
||||
|
||||
}
|
||||
|
||||
cacheKey += this.clippingContextVersion + ',';
|
||||
|
||||
if ( object.skeleton ) {
|
||||
|
||||
cacheKey += object.skeleton.bones.length + ',';
|
||||
|
||||
}
|
||||
|
||||
if ( object.morphTargetInfluences ) {
|
||||
|
||||
cacheKey += object.morphTargetInfluences.length + ',';
|
||||
|
||||
}
|
||||
|
||||
if ( object.isBatchedMesh ) {
|
||||
|
||||
cacheKey += object._matricesTexture.uuid + ',';
|
||||
|
||||
}
|
||||
|
||||
return cacheKey;
|
||||
|
||||
}
|
||||
|
||||
get needsUpdate() {
|
||||
|
||||
return this.initialNodesCacheKey !== this.getNodesCacheKey() || this.clippingNeedsUpdate;
|
||||
|
||||
}
|
||||
|
||||
getNodesCacheKey() {
|
||||
|
||||
// Environment Nodes Cache Key
|
||||
|
||||
return this._nodes.getCacheKey( this.scene, this.lightsNode );
|
||||
|
||||
}
|
||||
|
||||
getCacheKey() {
|
||||
|
||||
return this.getMaterialCacheKey() + ',' + this.getNodesCacheKey();
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.material.removeEventListener( 'dispose', this.onMaterialDispose );
|
||||
|
||||
this.onDispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
93
static/sdk/three/jsm/renderers/common/RenderObjects.js
Normal file
93
static/sdk/three/jsm/renderers/common/RenderObjects.js
Normal file
@ -0,0 +1,93 @@
|
||||
import ChainMap from './ChainMap.js';
|
||||
import RenderObject from './RenderObject.js';
|
||||
|
||||
class RenderObjects {
|
||||
|
||||
constructor( renderer, nodes, geometries, pipelines, bindings, info ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
this.nodes = nodes;
|
||||
this.geometries = geometries;
|
||||
this.pipelines = pipelines;
|
||||
this.bindings = bindings;
|
||||
this.info = info;
|
||||
|
||||
this.chainMaps = {};
|
||||
|
||||
}
|
||||
|
||||
get( object, material, scene, camera, lightsNode, renderContext, passId ) {
|
||||
|
||||
const chainMap = this.getChainMap( passId );
|
||||
const chainArray = [ object, material, renderContext, lightsNode ];
|
||||
|
||||
let renderObject = chainMap.get( chainArray );
|
||||
|
||||
if ( renderObject === undefined ) {
|
||||
|
||||
renderObject = this.createRenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode, renderContext, passId );
|
||||
|
||||
chainMap.set( chainArray, renderObject );
|
||||
|
||||
} else {
|
||||
|
||||
renderObject.updateClipping( renderContext.clippingContext );
|
||||
|
||||
if ( renderObject.version !== material.version || renderObject.needsUpdate ) {
|
||||
|
||||
if ( renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
|
||||
|
||||
renderObject.dispose();
|
||||
|
||||
renderObject = this.get( object, material, scene, camera, lightsNode, renderContext, passId );
|
||||
|
||||
} else {
|
||||
|
||||
renderObject.version = material.version;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return renderObject;
|
||||
|
||||
}
|
||||
|
||||
getChainMap( passId = 'default' ) {
|
||||
|
||||
return this.chainMaps[ passId ] || ( this.chainMaps[ passId ] = new ChainMap() );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.chainMaps = {};
|
||||
|
||||
}
|
||||
|
||||
createRenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, passId ) {
|
||||
|
||||
const chainMap = this.getChainMap( passId );
|
||||
|
||||
const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext );
|
||||
|
||||
renderObject.onDispose = () => {
|
||||
|
||||
this.pipelines.delete( renderObject );
|
||||
this.bindings.delete( renderObject );
|
||||
this.nodes.delete( renderObject );
|
||||
|
||||
chainMap.delete( renderObject.getChainArray() );
|
||||
|
||||
};
|
||||
|
||||
return renderObject;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default RenderObjects;
|
16
static/sdk/three/jsm/renderers/common/RenderPipeline.js
Normal file
16
static/sdk/three/jsm/renderers/common/RenderPipeline.js
Normal file
@ -0,0 +1,16 @@
|
||||
import Pipeline from './Pipeline.js';
|
||||
|
||||
class RenderPipeline extends Pipeline {
|
||||
|
||||
constructor( cacheKey, vertexProgram, fragmentProgram ) {
|
||||
|
||||
super( cacheKey );
|
||||
|
||||
this.vertexProgram = vertexProgram;
|
||||
this.fragmentProgram = fragmentProgram;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderPipeline;
|
1436
static/sdk/three/jsm/renderers/common/Renderer.js
Normal file
1436
static/sdk/three/jsm/renderers/common/Renderer.js
Normal file
File diff suppressed because it is too large
Load Diff
83
static/sdk/three/jsm/renderers/common/SampledTexture.js
Normal file
83
static/sdk/three/jsm/renderers/common/SampledTexture.js
Normal file
@ -0,0 +1,83 @@
|
||||
import Binding from './Binding.js';
|
||||
|
||||
let id = 0;
|
||||
|
||||
class SampledTexture extends Binding {
|
||||
|
||||
constructor( name, texture ) {
|
||||
|
||||
super( name );
|
||||
|
||||
this.id = id ++;
|
||||
|
||||
this.texture = texture;
|
||||
this.version = texture ? texture.version : 0;
|
||||
this.store = false;
|
||||
|
||||
this.isSampledTexture = true;
|
||||
|
||||
}
|
||||
|
||||
get needsBindingsUpdate() {
|
||||
|
||||
const { texture, version } = this;
|
||||
|
||||
return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( add it just to External Textures like PNG,JPG )
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const { texture, version } = this;
|
||||
|
||||
if ( version !== texture.version ) {
|
||||
|
||||
this.version = texture.version;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SampledArrayTexture extends SampledTexture {
|
||||
|
||||
constructor( name, texture ) {
|
||||
|
||||
super( name, texture );
|
||||
|
||||
this.isSampledArrayTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Sampled3DTexture extends SampledTexture {
|
||||
|
||||
constructor( name, texture ) {
|
||||
|
||||
super( name, texture );
|
||||
|
||||
this.isSampled3DTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SampledCubeTexture extends SampledTexture {
|
||||
|
||||
constructor( name, texture ) {
|
||||
|
||||
super( name, texture );
|
||||
|
||||
this.isSampledCubeTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SampledTexture, SampledArrayTexture, Sampled3DTexture, SampledCubeTexture };
|
18
static/sdk/three/jsm/renderers/common/Sampler.js
Normal file
18
static/sdk/three/jsm/renderers/common/Sampler.js
Normal file
@ -0,0 +1,18 @@
|
||||
import Binding from './Binding.js';
|
||||
|
||||
class Sampler extends Binding {
|
||||
|
||||
constructor( name, texture ) {
|
||||
|
||||
super( name );
|
||||
|
||||
this.texture = texture;
|
||||
this.version = texture ? texture.version : 0;
|
||||
|
||||
this.isSampler = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Sampler;
|
17
static/sdk/three/jsm/renderers/common/StorageBuffer.js
Normal file
17
static/sdk/three/jsm/renderers/common/StorageBuffer.js
Normal file
@ -0,0 +1,17 @@
|
||||
import Buffer from './Buffer.js';
|
||||
|
||||
class StorageBuffer extends Buffer {
|
||||
|
||||
constructor( name, attribute ) {
|
||||
|
||||
super( name, attribute ? attribute.array : null );
|
||||
|
||||
this.attribute = attribute;
|
||||
|
||||
this.isStorageBuffer = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StorageBuffer;
|
@ -0,0 +1,17 @@
|
||||
import { BufferAttribute } from 'three';
|
||||
|
||||
class StorageBufferAttribute extends BufferAttribute {
|
||||
|
||||
constructor( array, itemSize, typeClass = Float32Array ) {
|
||||
|
||||
if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize );
|
||||
|
||||
super( array, itemSize );
|
||||
|
||||
this.isStorageBufferAttribute = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StorageBufferAttribute;
|
@ -0,0 +1,17 @@
|
||||
import { InstancedBufferAttribute } from 'three';
|
||||
|
||||
class StorageInstancedBufferAttribute extends InstancedBufferAttribute {
|
||||
|
||||
constructor( array, itemSize, typeClass = Float32Array ) {
|
||||
|
||||
if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize );
|
||||
|
||||
super( array, itemSize );
|
||||
|
||||
this.isStorageInstancedBufferAttribute = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StorageInstancedBufferAttribute;
|
20
static/sdk/three/jsm/renderers/common/StorageTexture.js
Normal file
20
static/sdk/three/jsm/renderers/common/StorageTexture.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { Texture, LinearFilter } from 'three';
|
||||
|
||||
class StorageTexture extends Texture {
|
||||
|
||||
constructor( width = 1, height = 1 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.image = { width, height };
|
||||
|
||||
this.magFilter = LinearFilter;
|
||||
this.minFilter = LinearFilter;
|
||||
|
||||
this.isStorageTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default StorageTexture;
|
344
static/sdk/three/jsm/renderers/common/Textures.js
Normal file
344
static/sdk/three/jsm/renderers/common/Textures.js
Normal file
@ -0,0 +1,344 @@
|
||||
import DataMap from './DataMap.js';
|
||||
|
||||
import { Vector3, DepthTexture, DepthStencilFormat, DepthFormat, UnsignedIntType, UnsignedInt248Type, LinearFilter, NearestFilter, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeReflectionMapping, CubeRefractionMapping, UnsignedByteType } from 'three';
|
||||
|
||||
const _size = new Vector3();
|
||||
|
||||
class Textures extends DataMap {
|
||||
|
||||
constructor( renderer, backend, info ) {
|
||||
|
||||
super();
|
||||
|
||||
this.renderer = renderer;
|
||||
this.backend = backend;
|
||||
this.info = info;
|
||||
|
||||
}
|
||||
|
||||
updateRenderTarget( renderTarget, activeMipmapLevel = 0 ) {
|
||||
|
||||
const renderTargetData = this.get( renderTarget );
|
||||
|
||||
const sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
|
||||
const depthTextureMips = renderTargetData.depthTextureMips || ( renderTargetData.depthTextureMips = {} );
|
||||
|
||||
const texture = renderTarget.texture;
|
||||
const textures = renderTarget.textures;
|
||||
|
||||
const size = this.getSize( texture );
|
||||
|
||||
const mipWidth = size.width >> activeMipmapLevel;
|
||||
const mipHeight = size.height >> activeMipmapLevel;
|
||||
|
||||
let depthTexture = renderTarget.depthTexture || depthTextureMips[ activeMipmapLevel ];
|
||||
let textureNeedsUpdate = false;
|
||||
|
||||
if ( depthTexture === undefined ) {
|
||||
|
||||
depthTexture = new DepthTexture();
|
||||
depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat;
|
||||
depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType; // FloatType
|
||||
depthTexture.image.width = mipWidth;
|
||||
depthTexture.image.height = mipHeight;
|
||||
|
||||
depthTextureMips[ activeMipmapLevel ] = depthTexture;
|
||||
|
||||
}
|
||||
|
||||
if ( renderTargetData.width !== size.width || size.height !== renderTargetData.height ) {
|
||||
|
||||
textureNeedsUpdate = true;
|
||||
depthTexture.needsUpdate = true;
|
||||
|
||||
depthTexture.image.width = mipWidth;
|
||||
depthTexture.image.height = mipHeight;
|
||||
|
||||
}
|
||||
|
||||
renderTargetData.width = size.width;
|
||||
renderTargetData.height = size.height;
|
||||
renderTargetData.textures = textures;
|
||||
renderTargetData.depthTexture = depthTexture;
|
||||
renderTargetData.depth = renderTarget.depthBuffer;
|
||||
renderTargetData.stencil = renderTarget.stencilBuffer;
|
||||
renderTargetData.renderTarget = renderTarget;
|
||||
|
||||
if ( renderTargetData.sampleCount !== sampleCount ) {
|
||||
|
||||
textureNeedsUpdate = true;
|
||||
depthTexture.needsUpdate = true;
|
||||
|
||||
renderTargetData.sampleCount = sampleCount;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const options = { sampleCount };
|
||||
|
||||
for ( let i = 0; i < textures.length; i ++ ) {
|
||||
|
||||
const texture = textures[ i ];
|
||||
|
||||
if ( textureNeedsUpdate ) texture.needsUpdate = true;
|
||||
|
||||
this.updateTexture( texture, options );
|
||||
|
||||
}
|
||||
|
||||
this.updateTexture( depthTexture, options );
|
||||
|
||||
// dispose handler
|
||||
|
||||
if ( renderTargetData.initialized !== true ) {
|
||||
|
||||
renderTargetData.initialized = true;
|
||||
|
||||
// dispose
|
||||
|
||||
const onDispose = () => {
|
||||
|
||||
renderTarget.removeEventListener( 'dispose', onDispose );
|
||||
|
||||
if ( textures !== undefined ) {
|
||||
|
||||
for ( let i = 0; i < textures.length; i ++ ) {
|
||||
|
||||
this._destroyTexture( textures[ i ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this._destroyTexture( texture );
|
||||
|
||||
}
|
||||
|
||||
this._destroyTexture( depthTexture );
|
||||
|
||||
};
|
||||
|
||||
renderTarget.addEventListener( 'dispose', onDispose );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateTexture( texture, options = {} ) {
|
||||
|
||||
const textureData = this.get( texture );
|
||||
if ( textureData.initialized === true && textureData.version === texture.version ) return;
|
||||
|
||||
const isRenderTarget = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture;
|
||||
const backend = this.backend;
|
||||
|
||||
if ( isRenderTarget && textureData.initialized === true ) {
|
||||
|
||||
// it's an update
|
||||
|
||||
backend.destroySampler( texture );
|
||||
backend.destroyTexture( texture );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if ( texture.isFramebufferTexture ) {
|
||||
|
||||
const renderer = this.renderer;
|
||||
const renderTarget = renderer.getRenderTarget();
|
||||
|
||||
if ( renderTarget ) {
|
||||
|
||||
texture.type = renderTarget.texture.type;
|
||||
|
||||
} else {
|
||||
|
||||
texture.type = UnsignedByteType;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const { width, height, depth } = this.getSize( texture );
|
||||
|
||||
options.width = width;
|
||||
options.height = height;
|
||||
options.depth = depth;
|
||||
options.needsMipmaps = this.needsMipmaps( texture );
|
||||
options.levels = options.needsMipmaps ? this.getMipLevels( texture, width, height ) : 1;
|
||||
|
||||
//
|
||||
|
||||
if ( isRenderTarget || texture.isStorageTexture === true ) {
|
||||
|
||||
backend.createSampler( texture );
|
||||
backend.createTexture( texture, options );
|
||||
|
||||
} else {
|
||||
|
||||
const needsCreate = textureData.initialized !== true;
|
||||
|
||||
if ( needsCreate ) backend.createSampler( texture );
|
||||
|
||||
if ( texture.version > 0 ) {
|
||||
|
||||
const image = texture.image;
|
||||
|
||||
if ( image === undefined ) {
|
||||
|
||||
console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' );
|
||||
|
||||
} else if ( image.complete === false ) {
|
||||
|
||||
console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' );
|
||||
|
||||
} else {
|
||||
|
||||
if ( texture.images ) {
|
||||
|
||||
const images = [];
|
||||
|
||||
for ( const image of texture.images ) {
|
||||
|
||||
images.push( image );
|
||||
|
||||
}
|
||||
|
||||
options.images = images;
|
||||
|
||||
} else {
|
||||
|
||||
options.image = image;
|
||||
|
||||
}
|
||||
|
||||
if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) {
|
||||
|
||||
backend.createTexture( texture, options );
|
||||
|
||||
textureData.isDefaultTexture = false;
|
||||
|
||||
}
|
||||
|
||||
if ( texture.source.dataReady === true ) backend.updateTexture( texture, options );
|
||||
|
||||
if ( options.needsMipmaps && texture.mipmaps.length === 0 ) backend.generateMipmaps( texture );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// async update
|
||||
|
||||
backend.createDefaultTexture( texture );
|
||||
|
||||
textureData.isDefaultTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// dispose handler
|
||||
|
||||
if ( textureData.initialized !== true ) {
|
||||
|
||||
textureData.initialized = true;
|
||||
|
||||
//
|
||||
|
||||
this.info.memory.textures ++;
|
||||
|
||||
// dispose
|
||||
|
||||
const onDispose = () => {
|
||||
|
||||
texture.removeEventListener( 'dispose', onDispose );
|
||||
|
||||
this._destroyTexture( texture );
|
||||
|
||||
this.info.memory.textures --;
|
||||
|
||||
};
|
||||
|
||||
texture.addEventListener( 'dispose', onDispose );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
textureData.version = texture.version;
|
||||
|
||||
}
|
||||
|
||||
getSize( texture, target = _size ) {
|
||||
|
||||
let image = texture.images ? texture.images[ 0 ] : texture.image;
|
||||
|
||||
if ( image ) {
|
||||
|
||||
if ( image.image !== undefined ) image = image.image;
|
||||
|
||||
target.width = image.width;
|
||||
target.height = image.height;
|
||||
target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 );
|
||||
|
||||
} else {
|
||||
|
||||
target.width = target.height = target.depth = 1;
|
||||
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
getMipLevels( texture, width, height ) {
|
||||
|
||||
let mipLevelCount;
|
||||
|
||||
if ( texture.isCompressedTexture ) {
|
||||
|
||||
mipLevelCount = texture.mipmaps.length;
|
||||
|
||||
} else {
|
||||
|
||||
mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
|
||||
|
||||
}
|
||||
|
||||
return mipLevelCount;
|
||||
|
||||
}
|
||||
|
||||
needsMipmaps( texture ) {
|
||||
|
||||
if ( this.isEnvironmentTexture( texture ) ) return true;
|
||||
|
||||
return ( texture.isCompressedTexture === true ) || ( ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter ) );
|
||||
|
||||
}
|
||||
|
||||
isEnvironmentTexture( texture ) {
|
||||
|
||||
const mapping = texture.mapping;
|
||||
|
||||
return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );
|
||||
|
||||
}
|
||||
|
||||
_destroyTexture( texture ) {
|
||||
|
||||
this.backend.destroySampler( texture );
|
||||
this.backend.destroyTexture( texture );
|
||||
|
||||
this.delete( texture );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Textures;
|
140
static/sdk/three/jsm/renderers/common/Uniform.js
Normal file
140
static/sdk/three/jsm/renderers/common/Uniform.js
Normal file
@ -0,0 +1,140 @@
|
||||
import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three';
|
||||
|
||||
class Uniform {
|
||||
|
||||
constructor( name, value = null ) {
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
|
||||
this.boundary = 0; // used to build the uniform buffer according to the STD140 layout
|
||||
this.itemSize = 0;
|
||||
|
||||
this.offset = 0; // this property is set by WebGPUUniformsGroup and marks the start position in the uniform buffer
|
||||
|
||||
}
|
||||
|
||||
setValue( value ) {
|
||||
|
||||
this.value = value;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FloatUniform extends Uniform {
|
||||
|
||||
constructor( name, value = 0 ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isFloatUniform = true;
|
||||
|
||||
this.boundary = 4;
|
||||
this.itemSize = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector2Uniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Vector2() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isVector2Uniform = true;
|
||||
|
||||
this.boundary = 8;
|
||||
this.itemSize = 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector3Uniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Vector3() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isVector3Uniform = true;
|
||||
|
||||
this.boundary = 16;
|
||||
this.itemSize = 3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector4Uniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Vector4() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isVector4Uniform = true;
|
||||
|
||||
this.boundary = 16;
|
||||
this.itemSize = 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ColorUniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Color() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isColorUniform = true;
|
||||
|
||||
this.boundary = 16;
|
||||
this.itemSize = 3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Matrix3Uniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Matrix3() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isMatrix3Uniform = true;
|
||||
|
||||
this.boundary = 48;
|
||||
this.itemSize = 12;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Matrix4Uniform extends Uniform {
|
||||
|
||||
constructor( name, value = new Matrix4() ) {
|
||||
|
||||
super( name, value );
|
||||
|
||||
this.isMatrix4Uniform = true;
|
||||
|
||||
this.boundary = 64;
|
||||
this.itemSize = 16;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
FloatUniform,
|
||||
Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform,
|
||||
Matrix3Uniform, Matrix4Uniform
|
||||
};
|
15
static/sdk/three/jsm/renderers/common/UniformBuffer.js
Normal file
15
static/sdk/three/jsm/renderers/common/UniformBuffer.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Buffer from './Buffer.js';
|
||||
|
||||
class UniformBuffer extends Buffer {
|
||||
|
||||
constructor( name, buffer = null ) {
|
||||
|
||||
super( name, buffer );
|
||||
|
||||
this.isUniformBuffer = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default UniformBuffer;
|
301
static/sdk/three/jsm/renderers/common/UniformsGroup.js
Normal file
301
static/sdk/three/jsm/renderers/common/UniformsGroup.js
Normal file
@ -0,0 +1,301 @@
|
||||
import UniformBuffer from './UniformBuffer.js';
|
||||
import { GPU_CHUNK_BYTES } from './Constants.js';
|
||||
|
||||
class UniformsGroup extends UniformBuffer {
|
||||
|
||||
constructor( name ) {
|
||||
|
||||
super( name );
|
||||
|
||||
this.isUniformsGroup = true;
|
||||
|
||||
// the order of uniforms in this array must match the order of uniforms in the shader
|
||||
|
||||
this.uniforms = [];
|
||||
|
||||
}
|
||||
|
||||
addUniform( uniform ) {
|
||||
|
||||
this.uniforms.push( uniform );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
removeUniform( uniform ) {
|
||||
|
||||
const index = this.uniforms.indexOf( uniform );
|
||||
|
||||
if ( index !== - 1 ) {
|
||||
|
||||
this.uniforms.splice( index, 1 );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
|
||||
let buffer = this._buffer;
|
||||
|
||||
if ( buffer === null ) {
|
||||
|
||||
const byteLength = this.byteLength;
|
||||
|
||||
buffer = new Float32Array( new ArrayBuffer( byteLength ) );
|
||||
|
||||
this._buffer = buffer;
|
||||
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
||||
}
|
||||
|
||||
get byteLength() {
|
||||
|
||||
let offset = 0; // global buffer offset in bytes
|
||||
|
||||
for ( let i = 0, l = this.uniforms.length; i < l; i ++ ) {
|
||||
|
||||
const uniform = this.uniforms[ i ];
|
||||
|
||||
const { boundary, itemSize } = uniform;
|
||||
|
||||
// offset within a single chunk in bytes
|
||||
|
||||
const chunkOffset = offset % GPU_CHUNK_BYTES;
|
||||
const remainingSizeInChunk = GPU_CHUNK_BYTES - chunkOffset;
|
||||
|
||||
// conformance tests
|
||||
|
||||
if ( chunkOffset !== 0 && ( remainingSizeInChunk - boundary ) < 0 ) {
|
||||
|
||||
// check for chunk overflow
|
||||
|
||||
offset += ( GPU_CHUNK_BYTES - chunkOffset );
|
||||
|
||||
} else if ( chunkOffset % boundary !== 0 ) {
|
||||
|
||||
// check for correct alignment
|
||||
|
||||
offset += ( chunkOffset % boundary );
|
||||
|
||||
}
|
||||
|
||||
uniform.offset = ( offset / this.bytesPerElement );
|
||||
|
||||
offset += ( itemSize * this.bytesPerElement );
|
||||
|
||||
}
|
||||
|
||||
return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
let updated = false;
|
||||
|
||||
for ( const uniform of this.uniforms ) {
|
||||
|
||||
if ( this.updateByType( uniform ) === true ) {
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateByType( uniform ) {
|
||||
|
||||
if ( uniform.isFloatUniform ) return this.updateNumber( uniform );
|
||||
if ( uniform.isVector2Uniform ) return this.updateVector2( uniform );
|
||||
if ( uniform.isVector3Uniform ) return this.updateVector3( uniform );
|
||||
if ( uniform.isVector4Uniform ) return this.updateVector4( uniform );
|
||||
if ( uniform.isColorUniform ) return this.updateColor( uniform );
|
||||
if ( uniform.isMatrix3Uniform ) return this.updateMatrix3( uniform );
|
||||
if ( uniform.isMatrix4Uniform ) return this.updateMatrix4( uniform );
|
||||
|
||||
console.error( 'THREE.WebGPUUniformsGroup: Unsupported uniform type.', uniform );
|
||||
|
||||
}
|
||||
|
||||
updateNumber( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const v = uniform.getValue();
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset ] !== v ) {
|
||||
|
||||
a[ offset ] = v;
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateVector2( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const v = uniform.getValue();
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y ) {
|
||||
|
||||
a[ offset + 0 ] = v.x;
|
||||
a[ offset + 1 ] = v.y;
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateVector3( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const v = uniform.getValue();
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z ) {
|
||||
|
||||
a[ offset + 0 ] = v.x;
|
||||
a[ offset + 1 ] = v.y;
|
||||
a[ offset + 2 ] = v.z;
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateVector4( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const v = uniform.getValue();
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z || a[ offset + 4 ] !== v.w ) {
|
||||
|
||||
a[ offset + 0 ] = v.x;
|
||||
a[ offset + 1 ] = v.y;
|
||||
a[ offset + 2 ] = v.z;
|
||||
a[ offset + 3 ] = v.w;
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateColor( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const c = uniform.getValue();
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset + 0 ] !== c.r || a[ offset + 1 ] !== c.g || a[ offset + 2 ] !== c.b ) {
|
||||
|
||||
a[ offset + 0 ] = c.r;
|
||||
a[ offset + 1 ] = c.g;
|
||||
a[ offset + 2 ] = c.b;
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateMatrix3( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const e = uniform.getValue().elements;
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( a[ offset + 0 ] !== e[ 0 ] || a[ offset + 1 ] !== e[ 1 ] || a[ offset + 2 ] !== e[ 2 ] ||
|
||||
a[ offset + 4 ] !== e[ 3 ] || a[ offset + 5 ] !== e[ 4 ] || a[ offset + 6 ] !== e[ 5 ] ||
|
||||
a[ offset + 8 ] !== e[ 6 ] || a[ offset + 9 ] !== e[ 7 ] || a[ offset + 10 ] !== e[ 8 ] ) {
|
||||
|
||||
a[ offset + 0 ] = e[ 0 ];
|
||||
a[ offset + 1 ] = e[ 1 ];
|
||||
a[ offset + 2 ] = e[ 2 ];
|
||||
a[ offset + 4 ] = e[ 3 ];
|
||||
a[ offset + 5 ] = e[ 4 ];
|
||||
a[ offset + 6 ] = e[ 5 ];
|
||||
a[ offset + 8 ] = e[ 6 ];
|
||||
a[ offset + 9 ] = e[ 7 ];
|
||||
a[ offset + 10 ] = e[ 8 ];
|
||||
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
updateMatrix4( uniform ) {
|
||||
|
||||
let updated = false;
|
||||
|
||||
const a = this.buffer;
|
||||
const e = uniform.getValue().elements;
|
||||
const offset = uniform.offset;
|
||||
|
||||
if ( arraysEqual( a, e, offset ) === false ) {
|
||||
|
||||
a.set( e, offset );
|
||||
updated = true;
|
||||
|
||||
}
|
||||
|
||||
return updated;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function arraysEqual( a, b, offset ) {
|
||||
|
||||
for ( let i = 0, l = b.length; i < l; i ++ ) {
|
||||
|
||||
if ( a[ offset + i ] !== b[ i ] ) return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
export default UniformsGroup;
|
773
static/sdk/three/jsm/renderers/common/extras/PMREMGenerator.js
Normal file
773
static/sdk/three/jsm/renderers/common/extras/PMREMGenerator.js
Normal file
@ -0,0 +1,773 @@
|
||||
import NodeMaterial from '../../../nodes/materials/NodeMaterial.js';
|
||||
import { getDirection, blur } from '../../../nodes/pmrem/PMREMUtils.js';
|
||||
import { equirectUV } from '../../../nodes/utils/EquirectUVNode.js';
|
||||
import { uniform } from '../../../nodes/core/UniformNode.js';
|
||||
import { uniforms } from '../../../nodes/accessors/UniformsNode.js';
|
||||
import { texture } from '../../../nodes/accessors/TextureNode.js';
|
||||
import { cubeTexture } from '../../../nodes/accessors/CubeTextureNode.js';
|
||||
import { float, vec3 } from '../../../nodes/shadernode/ShaderNode.js';
|
||||
import { uv } from '../../../nodes/accessors/UVNode.js';
|
||||
import { attribute } from '../../../nodes/core/AttributeNode.js';
|
||||
import {
|
||||
OrthographicCamera,
|
||||
Color,
|
||||
Vector3,
|
||||
BufferGeometry,
|
||||
BufferAttribute,
|
||||
RenderTarget,
|
||||
Mesh,
|
||||
CubeReflectionMapping,
|
||||
CubeRefractionMapping,
|
||||
CubeUVReflectionMapping,
|
||||
LinearFilter,
|
||||
NoBlending,
|
||||
RGBAFormat,
|
||||
HalfFloatType,
|
||||
BackSide,
|
||||
LinearSRGBColorSpace,
|
||||
PerspectiveCamera,
|
||||
MeshBasicMaterial,
|
||||
BoxGeometry
|
||||
} from 'three';
|
||||
|
||||
const LOD_MIN = 4;
|
||||
|
||||
// The standard deviations (radians) associated with the extra mips. These are
|
||||
// chosen to approximate a Trowbridge-Reitz distribution function times the
|
||||
// geometric shadowing function. These sigma values squared must match the
|
||||
// variance #defines in cube_uv_reflection_fragment.glsl.js.
|
||||
const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
|
||||
|
||||
// The maximum length of the blur for loop. Smaller sigmas will use fewer
|
||||
// samples and exit early, but not recompile the shader.
|
||||
const MAX_SAMPLES = 20;
|
||||
|
||||
const _flatCamera = /*@__PURE__*/ new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
const _cubeCamera = /*@__PURE__*/ new PerspectiveCamera( 90, 1 );
|
||||
const _clearColor = /*@__PURE__*/ new Color();
|
||||
let _oldTarget = null;
|
||||
let _oldActiveCubeFace = 0;
|
||||
let _oldActiveMipmapLevel = 0;
|
||||
|
||||
// Golden Ratio
|
||||
const PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
|
||||
const INV_PHI = 1 / PHI;
|
||||
|
||||
// Vertices of a dodecahedron (except the opposites, which represent the
|
||||
// same axis), used as axis directions evenly spread on a sphere.
|
||||
const _axisDirections = [
|
||||
/*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ),
|
||||
/*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),
|
||||
/*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),
|
||||
/*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),
|
||||
/*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),
|
||||
/*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),
|
||||
/*@__PURE__*/ new Vector3( - 1, 1, - 1 ),
|
||||
/*@__PURE__*/ new Vector3( 1, 1, - 1 ),
|
||||
/*@__PURE__*/ new Vector3( - 1, 1, 1 ),
|
||||
/*@__PURE__*/ new Vector3( 1, 1, 1 )
|
||||
];
|
||||
|
||||
//
|
||||
|
||||
// WebGPU Face indices
|
||||
const _faceLib = [
|
||||
3, 1, 5,
|
||||
0, 4, 2
|
||||
];
|
||||
|
||||
const direction = getDirection( uv(), attribute( 'faceIndex' ) ).normalize();
|
||||
const outputDirection = vec3( direction.x, direction.y.negate(), direction.z );
|
||||
|
||||
/**
|
||||
* This class generates a Prefiltered, Mipmapped Radiance Environment Map
|
||||
* (PMREM) from a cubeMap environment texture. This allows different levels of
|
||||
* blur to be quickly accessed based on material roughness. It is packed into a
|
||||
* special CubeUV format that allows us to perform custom interpolation so that
|
||||
* we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
|
||||
* chain, it only goes down to the LOD_MIN level (above), and then creates extra
|
||||
* even more filtered 'mips' at the same LOD_MIN resolution, associated with
|
||||
* higher roughness levels. In this way we maintain resolution to smoothly
|
||||
* interpolate diffuse lighting while limiting sampling computation.
|
||||
*
|
||||
* Paper: Fast, Accurate Image-Based Lighting
|
||||
* https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view
|
||||
*/
|
||||
|
||||
class PMREMGenerator {
|
||||
|
||||
constructor( renderer ) {
|
||||
|
||||
this._renderer = renderer;
|
||||
this._pingPongRenderTarget = null;
|
||||
|
||||
this._lodMax = 0;
|
||||
this._cubeSize = 0;
|
||||
this._lodPlanes = [];
|
||||
this._sizeLods = [];
|
||||
this._sigmas = [];
|
||||
this._lodMeshes = [];
|
||||
|
||||
this._blurMaterial = null;
|
||||
this._cubemapMaterial = null;
|
||||
this._equirectMaterial = null;
|
||||
this._backgroundBox = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a PMREM from a supplied Scene, which can be faster than using an
|
||||
* image if networking bandwidth is low. Optional sigma specifies a blur radius
|
||||
* in radians to be applied to the scene before PMREM generation. Optional near
|
||||
* and far planes ensure the scene is rendered in its entirety (the cubeCamera
|
||||
* is placed at the origin).
|
||||
*/
|
||||
fromScene( scene, sigma = 0, near = 0.1, far = 100 ) {
|
||||
|
||||
_oldTarget = this._renderer.getRenderTarget();
|
||||
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
|
||||
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
|
||||
|
||||
this._setSize( 256 );
|
||||
|
||||
const cubeUVRenderTarget = this._allocateTargets();
|
||||
cubeUVRenderTarget.depthBuffer = true;
|
||||
|
||||
this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
|
||||
|
||||
if ( sigma > 0 ) {
|
||||
|
||||
this._blur( cubeUVRenderTarget, 0, 0, sigma );
|
||||
|
||||
}
|
||||
|
||||
this._applyPMREM( cubeUVRenderTarget );
|
||||
|
||||
this._cleanup( cubeUVRenderTarget );
|
||||
|
||||
return cubeUVRenderTarget;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a PMREM from an equirectangular texture, which can be either LDR
|
||||
* or HDR. The ideal input image size is 1k (1024 x 512),
|
||||
* as this matches best with the 256 x 256 cubemap output.
|
||||
*/
|
||||
fromEquirectangular( equirectangular, renderTarget = null ) {
|
||||
|
||||
return this._fromTexture( equirectangular, renderTarget );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a PMREM from an cubemap texture, which can be either LDR
|
||||
* or HDR. The ideal input cube size is 256 x 256,
|
||||
* as this matches best with the 256 x 256 cubemap output.
|
||||
*/
|
||||
fromCubemap( cubemap, renderTarget = null ) {
|
||||
|
||||
return this._fromTexture( cubemap, renderTarget );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during
|
||||
* your texture's network fetch for increased concurrency.
|
||||
*/
|
||||
compileCubemapShader() {
|
||||
|
||||
if ( this._cubemapMaterial === null ) {
|
||||
|
||||
this._cubemapMaterial = _getCubemapMaterial();
|
||||
this._compileMaterial( this._cubemapMaterial );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during
|
||||
* your texture's network fetch for increased concurrency.
|
||||
*/
|
||||
compileEquirectangularShader() {
|
||||
|
||||
if ( this._equirectMaterial === null ) {
|
||||
|
||||
this._equirectMaterial = _getEquirectMaterial();
|
||||
this._compileMaterial( this._equirectMaterial );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
|
||||
* so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
|
||||
* one of them will cause any others to also become unusable.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this._dispose();
|
||||
|
||||
if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();
|
||||
if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();
|
||||
if ( this._backgroundBox !== null ) {
|
||||
|
||||
this._backgroundBox.geometry.dispose();
|
||||
this._backgroundBox.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// private interface
|
||||
|
||||
_setSize( cubeSize ) {
|
||||
|
||||
this._lodMax = Math.floor( Math.log2( cubeSize ) );
|
||||
this._cubeSize = Math.pow( 2, this._lodMax );
|
||||
|
||||
}
|
||||
|
||||
_dispose() {
|
||||
|
||||
if ( this._blurMaterial !== null ) this._blurMaterial.dispose();
|
||||
|
||||
if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
|
||||
|
||||
for ( let i = 0; i < this._lodPlanes.length; i ++ ) {
|
||||
|
||||
this._lodPlanes[ i ].dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_cleanup( outputTarget ) {
|
||||
|
||||
this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );
|
||||
outputTarget.scissorTest = false;
|
||||
_setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );
|
||||
|
||||
}
|
||||
|
||||
_fromTexture( texture, renderTarget ) {
|
||||
|
||||
if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {
|
||||
|
||||
this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );
|
||||
|
||||
} else { // Equirectangular
|
||||
|
||||
this._setSize( texture.image.width / 4 );
|
||||
|
||||
}
|
||||
|
||||
_oldTarget = this._renderer.getRenderTarget();
|
||||
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
|
||||
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
|
||||
|
||||
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
|
||||
this._textureToCubeUV( texture, cubeUVRenderTarget );
|
||||
this._applyPMREM( cubeUVRenderTarget );
|
||||
this._cleanup( cubeUVRenderTarget );
|
||||
|
||||
return cubeUVRenderTarget;
|
||||
|
||||
}
|
||||
|
||||
_allocateTargets() {
|
||||
|
||||
const width = 3 * Math.max( this._cubeSize, 16 * 7 );
|
||||
const height = 4 * this._cubeSize;
|
||||
|
||||
const params = {
|
||||
magFilter: LinearFilter,
|
||||
minFilter: LinearFilter,
|
||||
generateMipmaps: false,
|
||||
type: HalfFloatType,
|
||||
format: RGBAFormat,
|
||||
colorSpace: LinearSRGBColorSpace,
|
||||
//depthBuffer: false
|
||||
};
|
||||
|
||||
const cubeUVRenderTarget = _createRenderTarget( width, height, params );
|
||||
|
||||
if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {
|
||||
|
||||
if ( this._pingPongRenderTarget !== null ) {
|
||||
|
||||
this._dispose();
|
||||
|
||||
}
|
||||
|
||||
this._pingPongRenderTarget = _createRenderTarget( width, height, params );
|
||||
|
||||
const { _lodMax } = this;
|
||||
( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas, lodMeshes: this._lodMeshes } = _createPlanes( _lodMax ) );
|
||||
|
||||
this._blurMaterial = _getBlurShader( _lodMax, width, height );
|
||||
|
||||
}
|
||||
|
||||
return cubeUVRenderTarget;
|
||||
|
||||
}
|
||||
|
||||
_compileMaterial( material ) {
|
||||
|
||||
const tmpMesh = this._lodMeshes[ 0 ];
|
||||
tmpMesh.material = material;
|
||||
|
||||
this._renderer.compile( tmpMesh, _flatCamera );
|
||||
|
||||
}
|
||||
|
||||
_sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
|
||||
|
||||
const cubeCamera = _cubeCamera;
|
||||
cubeCamera.near = near;
|
||||
cubeCamera.far = far;
|
||||
|
||||
// px, py, pz, nx, ny, nz
|
||||
const upSign = [ - 1, 1, - 1, - 1, - 1, - 1 ];
|
||||
const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];
|
||||
|
||||
const renderer = this._renderer;
|
||||
|
||||
const originalAutoClear = renderer.autoClear;
|
||||
|
||||
renderer.getClearColor( _clearColor );
|
||||
|
||||
renderer.autoClear = false;
|
||||
|
||||
let backgroundBox = this._backgroundBox;
|
||||
|
||||
if ( backgroundBox === null ) {
|
||||
|
||||
const backgroundMaterial = new MeshBasicMaterial( {
|
||||
name: 'PMREM.Background',
|
||||
side: BackSide,
|
||||
depthWrite: false,
|
||||
depthTest: false
|
||||
} );
|
||||
|
||||
backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial );
|
||||
|
||||
}
|
||||
|
||||
let useSolidColor = false;
|
||||
const background = scene.background;
|
||||
|
||||
if ( background ) {
|
||||
|
||||
if ( background.isColor ) {
|
||||
|
||||
backgroundBox.material.color.copy( background );
|
||||
scene.background = null;
|
||||
useSolidColor = true;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
backgroundBox.material.color.copy( _clearColor );
|
||||
useSolidColor = true;
|
||||
|
||||
}
|
||||
|
||||
renderer.setRenderTarget( cubeUVRenderTarget );
|
||||
|
||||
renderer.clear();
|
||||
|
||||
if ( useSolidColor ) {
|
||||
|
||||
renderer.render( backgroundBox, cubeCamera );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < 6; i ++ ) {
|
||||
|
||||
const col = i % 3;
|
||||
|
||||
if ( col === 0 ) {
|
||||
|
||||
cubeCamera.up.set( 0, upSign[ i ], 0 );
|
||||
cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
|
||||
|
||||
} else if ( col === 1 ) {
|
||||
|
||||
cubeCamera.up.set( 0, 0, upSign[ i ] );
|
||||
cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
|
||||
|
||||
} else {
|
||||
|
||||
cubeCamera.up.set( 0, upSign[ i ], 0 );
|
||||
cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
|
||||
|
||||
}
|
||||
|
||||
const size = this._cubeSize;
|
||||
|
||||
_setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );
|
||||
|
||||
renderer.render( scene, cubeCamera );
|
||||
|
||||
}
|
||||
|
||||
renderer.autoClear = originalAutoClear;
|
||||
scene.background = background;
|
||||
|
||||
}
|
||||
|
||||
_textureToCubeUV( texture, cubeUVRenderTarget ) {
|
||||
|
||||
const renderer = this._renderer;
|
||||
|
||||
const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );
|
||||
|
||||
if ( isCubeTexture ) {
|
||||
|
||||
if ( this._cubemapMaterial === null ) {
|
||||
|
||||
this._cubemapMaterial = _getCubemapMaterial( texture );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( this._equirectMaterial === null ) {
|
||||
|
||||
this._equirectMaterial = _getEquirectMaterial( texture );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;
|
||||
material.fragmentNode.value = texture;
|
||||
|
||||
const mesh = this._lodMeshes[ 0 ];
|
||||
mesh.material = material;
|
||||
|
||||
const size = this._cubeSize;
|
||||
|
||||
_setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );
|
||||
|
||||
renderer.setRenderTarget( cubeUVRenderTarget );
|
||||
renderer.render( mesh, _flatCamera );
|
||||
|
||||
}
|
||||
|
||||
_applyPMREM( cubeUVRenderTarget ) {
|
||||
|
||||
const renderer = this._renderer;
|
||||
const autoClear = renderer.autoClear;
|
||||
renderer.autoClear = false;
|
||||
const n = this._lodPlanes.length;
|
||||
|
||||
for ( let i = 1; i < n; i ++ ) {
|
||||
|
||||
const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );
|
||||
|
||||
const poleAxis = _axisDirections[ ( n - i - 1 ) % _axisDirections.length ];
|
||||
|
||||
this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
|
||||
|
||||
}
|
||||
|
||||
renderer.autoClear = autoClear;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a two-pass Gaussian blur for a cubemap. Normally this is done
|
||||
* vertically and horizontally, but this breaks down on a cube. Here we apply
|
||||
* the blur latitudinally (around the poles), and then longitudinally (towards
|
||||
* the poles) to approximate the orthogonally-separable blur. It is least
|
||||
* accurate at the poles, but still does a decent job.
|
||||
*/
|
||||
_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
|
||||
|
||||
const pingPongRenderTarget = this._pingPongRenderTarget;
|
||||
|
||||
this._halfBlur(
|
||||
cubeUVRenderTarget,
|
||||
pingPongRenderTarget,
|
||||
lodIn,
|
||||
lodOut,
|
||||
sigma,
|
||||
'latitudinal',
|
||||
poleAxis );
|
||||
|
||||
this._halfBlur(
|
||||
pingPongRenderTarget,
|
||||
cubeUVRenderTarget,
|
||||
lodOut,
|
||||
lodOut,
|
||||
sigma,
|
||||
'longitudinal',
|
||||
poleAxis );
|
||||
|
||||
}
|
||||
|
||||
_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
|
||||
|
||||
const renderer = this._renderer;
|
||||
const blurMaterial = this._blurMaterial;
|
||||
|
||||
if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
|
||||
|
||||
console.error( 'blur direction must be either latitudinal or longitudinal!' );
|
||||
|
||||
}
|
||||
|
||||
// Number of standard deviations at which to cut off the discrete approximation.
|
||||
const STANDARD_DEVIATIONS = 3;
|
||||
|
||||
const blurMesh = this._lodMeshes[ lodOut ];
|
||||
blurMesh.material = blurMaterial;
|
||||
|
||||
const blurUniforms = blurMaterial.uniforms;
|
||||
|
||||
const pixels = this._sizeLods[ lodIn ] - 1;
|
||||
const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
|
||||
const sigmaPixels = sigmaRadians / radiansPerPixel;
|
||||
const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
|
||||
|
||||
if ( samples > MAX_SAMPLES ) {
|
||||
|
||||
console.warn( `sigmaRadians, ${
|
||||
sigmaRadians}, is too large and will clip, as it requested ${
|
||||
samples} samples when the maximum is set to ${MAX_SAMPLES}` );
|
||||
|
||||
}
|
||||
|
||||
const weights = [];
|
||||
let sum = 0;
|
||||
|
||||
for ( let i = 0; i < MAX_SAMPLES; ++ i ) {
|
||||
|
||||
const x = i / sigmaPixels;
|
||||
const weight = Math.exp( - x * x / 2 );
|
||||
weights.push( weight );
|
||||
|
||||
if ( i === 0 ) {
|
||||
|
||||
sum += weight;
|
||||
|
||||
} else if ( i < samples ) {
|
||||
|
||||
sum += 2 * weight;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < weights.length; i ++ ) {
|
||||
|
||||
weights[ i ] = weights[ i ] / sum;
|
||||
|
||||
}
|
||||
|
||||
targetIn.texture.frame = ( targetIn.texture.frame || 0 ) + 1;
|
||||
|
||||
blurUniforms.envMap.value = targetIn.texture;
|
||||
blurUniforms.samples.value = samples;
|
||||
blurUniforms.weights.array = weights;
|
||||
blurUniforms.latitudinal.value = direction === 'latitudinal' ? 1 : 0;
|
||||
|
||||
if ( poleAxis ) {
|
||||
|
||||
blurUniforms.poleAxis.value = poleAxis;
|
||||
|
||||
}
|
||||
|
||||
const { _lodMax } = this;
|
||||
blurUniforms.dTheta.value = radiansPerPixel;
|
||||
blurUniforms.mipInt.value = _lodMax - lodIn;
|
||||
|
||||
const outputSize = this._sizeLods[ lodOut ];
|
||||
const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );
|
||||
const y = 4 * ( this._cubeSize - outputSize );
|
||||
|
||||
_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
|
||||
renderer.setRenderTarget( targetOut );
|
||||
renderer.render( blurMesh, _flatCamera );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _createPlanes( lodMax ) {
|
||||
|
||||
const lodPlanes = [];
|
||||
const sizeLods = [];
|
||||
const sigmas = [];
|
||||
const lodMeshes = [];
|
||||
|
||||
let lod = lodMax;
|
||||
|
||||
const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
|
||||
|
||||
for ( let i = 0; i < totalLods; i ++ ) {
|
||||
|
||||
const sizeLod = Math.pow( 2, lod );
|
||||
sizeLods.push( sizeLod );
|
||||
let sigma = 1.0 / sizeLod;
|
||||
|
||||
if ( i > lodMax - LOD_MIN ) {
|
||||
|
||||
sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];
|
||||
|
||||
} else if ( i === 0 ) {
|
||||
|
||||
sigma = 0;
|
||||
|
||||
}
|
||||
|
||||
sigmas.push( sigma );
|
||||
|
||||
const texelSize = 1.0 / ( sizeLod - 2 );
|
||||
const min = - texelSize;
|
||||
const max = 1 + texelSize;
|
||||
const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
|
||||
|
||||
const cubeFaces = 6;
|
||||
const vertices = 6;
|
||||
const positionSize = 3;
|
||||
const uvSize = 2;
|
||||
const faceIndexSize = 1;
|
||||
|
||||
const position = new Float32Array( positionSize * vertices * cubeFaces );
|
||||
const uv = new Float32Array( uvSize * vertices * cubeFaces );
|
||||
const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
|
||||
|
||||
for ( let face = 0; face < cubeFaces; face ++ ) {
|
||||
|
||||
const x = ( face % 3 ) * 2 / 3 - 1;
|
||||
const y = face > 2 ? 0 : - 1;
|
||||
const coordinates = [
|
||||
x, y, 0,
|
||||
x + 2 / 3, y, 0,
|
||||
x + 2 / 3, y + 1, 0,
|
||||
x, y, 0,
|
||||
x + 2 / 3, y + 1, 0,
|
||||
x, y + 1, 0
|
||||
];
|
||||
|
||||
const faceIdx = _faceLib[ face ];
|
||||
position.set( coordinates, positionSize * vertices * faceIdx );
|
||||
uv.set( uv1, uvSize * vertices * faceIdx );
|
||||
const fill = [ faceIdx, faceIdx, faceIdx, faceIdx, faceIdx, faceIdx ];
|
||||
faceIndex.set( fill, faceIndexSize * vertices * faceIdx );
|
||||
|
||||
}
|
||||
|
||||
const planes = new BufferGeometry();
|
||||
planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
|
||||
planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
|
||||
planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
|
||||
lodPlanes.push( planes );
|
||||
lodMeshes.push( new Mesh( planes, null ) );
|
||||
|
||||
if ( lod > LOD_MIN ) {
|
||||
|
||||
lod --;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return { lodPlanes, sizeLods, sigmas, lodMeshes };
|
||||
|
||||
}
|
||||
|
||||
function _createRenderTarget( width, height, params ) {
|
||||
|
||||
const cubeUVRenderTarget = new RenderTarget( width, height, params );
|
||||
cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
|
||||
cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
|
||||
cubeUVRenderTarget.texture.isPMREMTexture = true;
|
||||
cubeUVRenderTarget.scissorTest = true;
|
||||
return cubeUVRenderTarget;
|
||||
|
||||
}
|
||||
|
||||
function _setViewport( target, x, y, width, height ) {
|
||||
|
||||
const viewY = target.height - height - y;
|
||||
|
||||
target.viewport.set( x, viewY, width, height );
|
||||
target.scissor.set( x, viewY, width, height );
|
||||
|
||||
}
|
||||
|
||||
function _getMaterial() {
|
||||
|
||||
const material = new NodeMaterial();
|
||||
material.depthTest = false;
|
||||
material.depthWrite = false;
|
||||
material.blending = NoBlending;
|
||||
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
function _getBlurShader( lodMax, width, height ) {
|
||||
|
||||
const weights = uniforms( new Array( MAX_SAMPLES ).fill( 0 ) );
|
||||
const poleAxis = uniform( new Vector3( 0, 1, 0 ) );
|
||||
const dTheta = uniform( 0 );
|
||||
const n = float( MAX_SAMPLES );
|
||||
const latitudinal = uniform( 0 ); // false, bool
|
||||
const samples = uniform( 1 ); // int
|
||||
const envMap = texture( null );
|
||||
const mipInt = uniform( 0 ); // int
|
||||
const CUBEUV_TEXEL_WIDTH = float( 1 / width );
|
||||
const CUBEUV_TEXEL_HEIGHT = float( 1 / height );
|
||||
const CUBEUV_MAX_MIP = float( lodMax );
|
||||
|
||||
const materialUniforms = {
|
||||
n,
|
||||
latitudinal,
|
||||
weights,
|
||||
poleAxis,
|
||||
outputDirection,
|
||||
dTheta,
|
||||
samples,
|
||||
envMap,
|
||||
mipInt,
|
||||
CUBEUV_TEXEL_WIDTH,
|
||||
CUBEUV_TEXEL_HEIGHT,
|
||||
CUBEUV_MAX_MIP
|
||||
};
|
||||
|
||||
const material = _getMaterial();
|
||||
material.uniforms = materialUniforms; // TODO: Move to outside of the material
|
||||
material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( 1 ) } );
|
||||
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
function _getCubemapMaterial( envTexture ) {
|
||||
|
||||
const material = _getMaterial();
|
||||
material.fragmentNode = cubeTexture( envTexture, outputDirection );
|
||||
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
function _getEquirectMaterial( envTexture ) {
|
||||
|
||||
const material = _getMaterial();
|
||||
material.fragmentNode = texture( envTexture, equirectUV( outputDirection ), 0 );
|
||||
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
export default PMREMGenerator;
|
@ -0,0 +1,44 @@
|
||||
class NodeBuilderState {
|
||||
|
||||
constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, transforms = [] ) {
|
||||
|
||||
this.vertexShader = vertexShader;
|
||||
this.fragmentShader = fragmentShader;
|
||||
this.computeShader = computeShader;
|
||||
this.transforms = transforms;
|
||||
|
||||
this.nodeAttributes = nodeAttributes;
|
||||
this.bindings = bindings;
|
||||
|
||||
this.updateNodes = updateNodes;
|
||||
this.updateBeforeNodes = updateBeforeNodes;
|
||||
|
||||
this.usedTimes = 0;
|
||||
|
||||
}
|
||||
|
||||
createBindings() {
|
||||
|
||||
const bindingsArray = [];
|
||||
|
||||
for ( const instanceBinding of this.bindings ) {
|
||||
|
||||
let binding = instanceBinding;
|
||||
|
||||
if ( instanceBinding.shared !== true ) {
|
||||
|
||||
binding = instanceBinding.clone();
|
||||
|
||||
}
|
||||
|
||||
bindingsArray.push( binding );
|
||||
|
||||
}
|
||||
|
||||
return bindingsArray;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NodeBuilderState;
|
@ -0,0 +1,49 @@
|
||||
import { SampledTexture } from '../SampledTexture.js';
|
||||
|
||||
class NodeSampledTexture extends SampledTexture {
|
||||
|
||||
constructor( name, textureNode ) {
|
||||
|
||||
super( name, textureNode ? textureNode.value : null );
|
||||
|
||||
this.textureNode = textureNode;
|
||||
|
||||
}
|
||||
|
||||
get needsBindingsUpdate() {
|
||||
|
||||
return this.textureNode.value !== this.texture || super.needsBindingsUpdate;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const { textureNode } = this;
|
||||
|
||||
if ( this.texture !== textureNode.value ) {
|
||||
|
||||
this.texture = textureNode.value;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return super.update();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NodeSampledCubeTexture extends NodeSampledTexture {
|
||||
|
||||
constructor( name, textureNode ) {
|
||||
|
||||
super( name, textureNode );
|
||||
|
||||
this.isSampledCubeTexture = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { NodeSampledTexture, NodeSampledCubeTexture };
|
15
static/sdk/three/jsm/renderers/common/nodes/NodeSampler.js
Normal file
15
static/sdk/three/jsm/renderers/common/nodes/NodeSampler.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Sampler from '../Sampler.js';
|
||||
|
||||
class NodeSampler extends Sampler {
|
||||
|
||||
constructor( name, textureNode ) {
|
||||
|
||||
super( name, textureNode ? textureNode.value : null );
|
||||
|
||||
this.textureNode = textureNode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NodeSampler;
|
@ -0,0 +1,23 @@
|
||||
import StorageBuffer from '../StorageBuffer.js';
|
||||
|
||||
let _id = 0;
|
||||
|
||||
class NodeStorageBuffer extends StorageBuffer {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NodeStorageBuffer;
|
135
static/sdk/three/jsm/renderers/common/nodes/NodeUniform.js
Normal file
135
static/sdk/three/jsm/renderers/common/nodes/NodeUniform.js
Normal file
@ -0,0 +1,135 @@
|
||||
import {
|
||||
FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform,
|
||||
ColorUniform, Matrix3Uniform, Matrix4Uniform
|
||||
} from '../Uniform.js';
|
||||
|
||||
class FloatNodeUniform extends FloatUniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector2NodeUniform extends Vector2Uniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector3NodeUniform extends Vector3Uniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vector4NodeUniform extends Vector4Uniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ColorNodeUniform extends ColorUniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Matrix3NodeUniform extends Matrix3Uniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Matrix4NodeUniform extends Matrix4Uniform {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( nodeUniform.name, nodeUniform.value );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
|
||||
ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
import UniformBuffer from '../UniformBuffer.js';
|
||||
|
||||
let _id = 0;
|
||||
|
||||
class NodeUniformBuffer extends UniformBuffer {
|
||||
|
||||
constructor( nodeUniform ) {
|
||||
|
||||
super( 'UniformBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
|
||||
|
||||
this.nodeUniform = nodeUniform;
|
||||
|
||||
}
|
||||
|
||||
get buffer() {
|
||||
|
||||
return this.nodeUniform.value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NodeUniformBuffer;
|
@ -0,0 +1,44 @@
|
||||
import UniformsGroup from '../UniformsGroup.js';
|
||||
|
||||
let id = 0;
|
||||
|
||||
class NodeUniformsGroup extends UniformsGroup {
|
||||
|
||||
constructor( name, groupNode ) {
|
||||
|
||||
super( name );
|
||||
|
||||
this.id = id ++;
|
||||
this.groupNode = groupNode;
|
||||
|
||||
this.isNodeUniformsGroup = true;
|
||||
|
||||
}
|
||||
|
||||
get shared() {
|
||||
|
||||
return this.groupNode.shared;
|
||||
|
||||
}
|
||||
|
||||
getNodes() {
|
||||
|
||||
const nodes = [];
|
||||
|
||||
for ( const uniform of this.uniforms ) {
|
||||
|
||||
const node = uniform.nodeUniform.node;
|
||||
|
||||
if ( ! node ) throw new Error( 'NodeUniformsGroup: Uniform has no node.' );
|
||||
|
||||
nodes.push( node );
|
||||
|
||||
}
|
||||
|
||||
return nodes;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default NodeUniformsGroup;
|
467
static/sdk/three/jsm/renderers/common/nodes/Nodes.js
Normal file
467
static/sdk/three/jsm/renderers/common/nodes/Nodes.js
Normal file
@ -0,0 +1,467 @@
|
||||
import DataMap from '../DataMap.js';
|
||||
import ChainMap from '../ChainMap.js';
|
||||
import NodeBuilderState from './NodeBuilderState.js';
|
||||
import { EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping, SRGBColorSpace } from 'three';
|
||||
import { NodeFrame, vec4, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, viewportBottomLeft, normalWorld, pmremTexture, viewportTopLeft } from '../../../nodes/Nodes.js';
|
||||
|
||||
class Nodes extends DataMap {
|
||||
|
||||
constructor( renderer, backend ) {
|
||||
|
||||
super();
|
||||
|
||||
this.renderer = renderer;
|
||||
this.backend = backend;
|
||||
this.nodeFrame = new NodeFrame();
|
||||
this.nodeBuilderCache = new Map();
|
||||
this.callHashCache = new ChainMap();
|
||||
this.groupsData = new ChainMap();
|
||||
|
||||
}
|
||||
|
||||
updateGroup( nodeUniformsGroup ) {
|
||||
|
||||
const groupNode = nodeUniformsGroup.groupNode;
|
||||
const name = groupNode.name;
|
||||
|
||||
// objectGroup is every updated
|
||||
|
||||
if ( name === objectGroup.name ) return true;
|
||||
|
||||
// renderGroup is updated once per render/compute call
|
||||
|
||||
if ( name === renderGroup.name ) {
|
||||
|
||||
const uniformsGroupData = this.get( nodeUniformsGroup );
|
||||
const renderId = this.nodeFrame.renderId;
|
||||
|
||||
if ( uniformsGroupData.renderId !== renderId ) {
|
||||
|
||||
uniformsGroupData.renderId = renderId;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// frameGroup is updated once per frame
|
||||
|
||||
if ( name === frameGroup.name ) {
|
||||
|
||||
const uniformsGroupData = this.get( nodeUniformsGroup );
|
||||
const frameId = this.nodeFrame.frameId;
|
||||
|
||||
if ( uniformsGroupData.frameId !== frameId ) {
|
||||
|
||||
uniformsGroupData.frameId = frameId;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// other groups are updated just when groupNode.needsUpdate is true
|
||||
|
||||
const groupChain = [ groupNode, nodeUniformsGroup ];
|
||||
|
||||
let groupData = this.groupsData.get( groupChain );
|
||||
if ( groupData === undefined ) this.groupsData.set( groupChain, groupData = {} );
|
||||
|
||||
if ( groupData.version !== groupNode.version ) {
|
||||
|
||||
groupData.version = groupNode.version;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
getForRenderCacheKey( renderObject ) {
|
||||
|
||||
return renderObject.initialCacheKey;
|
||||
|
||||
}
|
||||
|
||||
getForRender( renderObject ) {
|
||||
|
||||
const renderObjectData = this.get( renderObject );
|
||||
|
||||
let nodeBuilderState = renderObjectData.nodeBuilderState;
|
||||
|
||||
if ( nodeBuilderState === undefined ) {
|
||||
|
||||
const { nodeBuilderCache } = this;
|
||||
|
||||
const cacheKey = this.getForRenderCacheKey( renderObject );
|
||||
|
||||
nodeBuilderState = nodeBuilderCache.get( cacheKey );
|
||||
|
||||
if ( nodeBuilderState === undefined ) {
|
||||
|
||||
const nodeBuilder = this.backend.createNodeBuilder( renderObject.object, this.renderer, renderObject.scene );
|
||||
nodeBuilder.material = renderObject.material;
|
||||
nodeBuilder.context.material = renderObject.material;
|
||||
nodeBuilder.lightsNode = renderObject.lightsNode;
|
||||
nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene );
|
||||
nodeBuilder.fogNode = this.getFogNode( renderObject.scene );
|
||||
nodeBuilder.clippingContext = renderObject.clippingContext;
|
||||
nodeBuilder.build();
|
||||
|
||||
nodeBuilderState = this._createNodeBuilderState( nodeBuilder );
|
||||
|
||||
nodeBuilderCache.set( cacheKey, nodeBuilderState );
|
||||
|
||||
}
|
||||
|
||||
nodeBuilderState.usedTimes ++;
|
||||
|
||||
renderObjectData.nodeBuilderState = nodeBuilderState;
|
||||
|
||||
}
|
||||
|
||||
return nodeBuilderState;
|
||||
|
||||
}
|
||||
|
||||
delete( object ) {
|
||||
|
||||
if ( object.isRenderObject ) {
|
||||
|
||||
const nodeBuilderState = this.get( object ).nodeBuilderState;
|
||||
nodeBuilderState.usedTimes --;
|
||||
|
||||
if ( nodeBuilderState.usedTimes === 0 ) {
|
||||
|
||||
this.nodeBuilderCache.delete( this.getForRenderCacheKey( object ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return super.delete( object );
|
||||
|
||||
}
|
||||
|
||||
getForCompute( computeNode ) {
|
||||
|
||||
const computeData = this.get( computeNode );
|
||||
|
||||
let nodeBuilderState = computeData.nodeBuilderState;
|
||||
|
||||
if ( nodeBuilderState === undefined ) {
|
||||
|
||||
const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer );
|
||||
nodeBuilder.build();
|
||||
|
||||
nodeBuilderState = this._createNodeBuilderState( nodeBuilder );
|
||||
|
||||
computeData.nodeBuilderState = nodeBuilderState;
|
||||
|
||||
}
|
||||
|
||||
return nodeBuilderState;
|
||||
|
||||
}
|
||||
|
||||
_createNodeBuilderState( nodeBuilder ) {
|
||||
|
||||
return new NodeBuilderState(
|
||||
nodeBuilder.vertexShader,
|
||||
nodeBuilder.fragmentShader,
|
||||
nodeBuilder.computeShader,
|
||||
nodeBuilder.getAttributesArray(),
|
||||
nodeBuilder.getBindings(),
|
||||
nodeBuilder.updateNodes,
|
||||
nodeBuilder.updateBeforeNodes,
|
||||
nodeBuilder.transforms
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
getEnvironmentNode( scene ) {
|
||||
|
||||
return scene.environmentNode || this.get( scene ).environmentNode || null;
|
||||
|
||||
}
|
||||
|
||||
getBackgroundNode( scene ) {
|
||||
|
||||
return scene.backgroundNode || this.get( scene ).backgroundNode || null;
|
||||
|
||||
}
|
||||
|
||||
getFogNode( scene ) {
|
||||
|
||||
return scene.fogNode || this.get( scene ).fogNode || null;
|
||||
|
||||
}
|
||||
|
||||
getCacheKey( scene, lightsNode ) {
|
||||
|
||||
const chain = [ scene, lightsNode ];
|
||||
const callId = this.renderer.info.calls;
|
||||
|
||||
let cacheKeyData = this.callHashCache.get( chain );
|
||||
|
||||
if ( cacheKeyData === undefined || cacheKeyData.callId !== callId ) {
|
||||
|
||||
const environmentNode = this.getEnvironmentNode( scene );
|
||||
const fogNode = this.getFogNode( scene );
|
||||
|
||||
const cacheKey = [];
|
||||
|
||||
if ( lightsNode ) cacheKey.push( lightsNode.getCacheKey() );
|
||||
if ( environmentNode ) cacheKey.push( environmentNode.getCacheKey() );
|
||||
if ( fogNode ) cacheKey.push( fogNode.getCacheKey() );
|
||||
|
||||
cacheKeyData = {
|
||||
callId,
|
||||
cacheKey: cacheKey.join( ',' )
|
||||
};
|
||||
|
||||
this.callHashCache.set( chain, cacheKeyData );
|
||||
|
||||
}
|
||||
|
||||
return cacheKeyData.cacheKey;
|
||||
|
||||
}
|
||||
|
||||
updateScene( scene ) {
|
||||
|
||||
this.updateEnvironment( scene );
|
||||
this.updateFog( scene );
|
||||
this.updateBackground( scene );
|
||||
|
||||
}
|
||||
|
||||
get isToneMappingState() {
|
||||
|
||||
return this.renderer.getRenderTarget() ? false : true;
|
||||
|
||||
}
|
||||
|
||||
updateBackground( scene ) {
|
||||
|
||||
const sceneData = this.get( scene );
|
||||
const background = scene.background;
|
||||
|
||||
if ( background ) {
|
||||
|
||||
if ( sceneData.background !== background ) {
|
||||
|
||||
let backgroundNode = null;
|
||||
|
||||
if ( background.isCubeTexture === true || ( background.mapping === EquirectangularReflectionMapping || background.mapping === EquirectangularRefractionMapping ) ) {
|
||||
|
||||
backgroundNode = pmremTexture( background, normalWorld );
|
||||
|
||||
} else if ( background.isTexture === true ) {
|
||||
|
||||
backgroundNode = texture( background, viewportBottomLeft ).setUpdateMatrix( true );
|
||||
|
||||
} else if ( background.isColor !== true ) {
|
||||
|
||||
console.error( 'WebGPUNodes: Unsupported background configuration.', background );
|
||||
|
||||
}
|
||||
|
||||
sceneData.backgroundNode = backgroundNode;
|
||||
sceneData.background = background;
|
||||
|
||||
}
|
||||
|
||||
} else if ( sceneData.backgroundNode ) {
|
||||
|
||||
delete sceneData.backgroundNode;
|
||||
delete sceneData.background;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateFog( scene ) {
|
||||
|
||||
const sceneData = this.get( scene );
|
||||
const fog = scene.fog;
|
||||
|
||||
if ( fog ) {
|
||||
|
||||
if ( sceneData.fog !== fog ) {
|
||||
|
||||
let fogNode = null;
|
||||
|
||||
if ( fog.isFogExp2 ) {
|
||||
|
||||
fogNode = densityFog( reference( 'color', 'color', fog ), reference( 'density', 'float', fog ) );
|
||||
|
||||
} else if ( fog.isFog ) {
|
||||
|
||||
fogNode = rangeFog( reference( 'color', 'color', fog ), reference( 'near', 'float', fog ), reference( 'far', 'float', fog ) );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'WebGPUNodes: Unsupported fog configuration.', fog );
|
||||
|
||||
}
|
||||
|
||||
sceneData.fogNode = fogNode;
|
||||
sceneData.fog = fog;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
delete sceneData.fogNode;
|
||||
delete sceneData.fog;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateEnvironment( scene ) {
|
||||
|
||||
const sceneData = this.get( scene );
|
||||
const environment = scene.environment;
|
||||
|
||||
if ( environment ) {
|
||||
|
||||
if ( sceneData.environment !== environment ) {
|
||||
|
||||
let environmentNode = null;
|
||||
|
||||
if ( environment.isCubeTexture === true ) {
|
||||
|
||||
environmentNode = cubeTexture( environment );
|
||||
|
||||
} else if ( environment.isTexture === true ) {
|
||||
|
||||
environmentNode = texture( environment );
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'Nodes: Unsupported environment configuration.', environment );
|
||||
|
||||
}
|
||||
|
||||
sceneData.environmentNode = environmentNode;
|
||||
sceneData.environment = environment;
|
||||
|
||||
}
|
||||
|
||||
} else if ( sceneData.environmentNode ) {
|
||||
|
||||
delete sceneData.environmentNode;
|
||||
delete sceneData.environment;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) {
|
||||
|
||||
const nodeFrame = this.nodeFrame;
|
||||
nodeFrame.renderer = renderer;
|
||||
nodeFrame.scene = scene;
|
||||
nodeFrame.object = object;
|
||||
nodeFrame.camera = camera;
|
||||
nodeFrame.material = material;
|
||||
|
||||
return nodeFrame;
|
||||
|
||||
}
|
||||
|
||||
getNodeFrameForRender( renderObject ) {
|
||||
|
||||
return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material );
|
||||
|
||||
}
|
||||
|
||||
getOutputNode( outputTexture ) {
|
||||
|
||||
let output = texture( outputTexture, viewportTopLeft );
|
||||
|
||||
if ( this.isToneMappingState ) {
|
||||
|
||||
if ( this.renderer.toneMappingNode ) {
|
||||
|
||||
output = vec4( this.renderer.toneMappingNode.context( { color: output.rgb } ), output.a );
|
||||
|
||||
} else if ( this.renderer.toneMapping !== NoToneMapping ) {
|
||||
|
||||
output = output.toneMapping( this.renderer.toneMapping );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( this.renderer.currentColorSpace === SRGBColorSpace ) {
|
||||
|
||||
output = output.linearToColorSpace( this.renderer.currentColorSpace );
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
updateBefore( renderObject ) {
|
||||
|
||||
const nodeFrame = this.getNodeFrameForRender( renderObject );
|
||||
const nodeBuilder = renderObject.getNodeBuilderState();
|
||||
|
||||
for ( const node of nodeBuilder.updateBeforeNodes ) {
|
||||
|
||||
nodeFrame.updateBeforeNode( node );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateForCompute( computeNode ) {
|
||||
|
||||
const nodeFrame = this.getNodeFrame();
|
||||
const nodeBuilder = this.getForCompute( computeNode );
|
||||
|
||||
for ( const node of nodeBuilder.updateNodes ) {
|
||||
|
||||
nodeFrame.updateNode( node );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateForRender( renderObject ) {
|
||||
|
||||
const nodeFrame = this.getNodeFrameForRender( renderObject );
|
||||
const nodeBuilder = renderObject.getNodeBuilderState();
|
||||
|
||||
for ( const node of nodeBuilder.updateNodes ) {
|
||||
|
||||
nodeFrame.updateNode( node );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
super.dispose();
|
||||
|
||||
this.nodeFrame = new NodeFrame();
|
||||
this.nodeBuilderCache = new Map();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Nodes;
|
1492
static/sdk/three/jsm/renderers/webgl/WebGLBackend.js
Normal file
1492
static/sdk/three/jsm/renderers/webgl/WebGLBackend.js
Normal file
File diff suppressed because it is too large
Load Diff
145
static/sdk/three/jsm/renderers/webgl/WebGLBufferRenderer.js
Normal file
145
static/sdk/three/jsm/renderers/webgl/WebGLBufferRenderer.js
Normal file
@ -0,0 +1,145 @@
|
||||
class WebGLBufferRenderer {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.gl = backend.gl;
|
||||
this.extensions = backend.extensions;
|
||||
this.info = backend.renderer.info;
|
||||
this.mode = null;
|
||||
this.index = 0;
|
||||
this.type = null;
|
||||
this.object = null;
|
||||
|
||||
}
|
||||
|
||||
render( start, count ) {
|
||||
|
||||
const { gl, mode, object, type, info, index } = this;
|
||||
|
||||
if ( index !== 0 ) {
|
||||
|
||||
gl.drawElements( mode, count, type, start );
|
||||
|
||||
} else {
|
||||
|
||||
gl.drawArrays( mode, start, count );
|
||||
|
||||
}
|
||||
|
||||
info.update( object, count, mode, 1 );
|
||||
|
||||
}
|
||||
|
||||
renderInstances( start, count, primcount ) {
|
||||
|
||||
const { gl, mode, type, index, object, info } = this;
|
||||
|
||||
if ( primcount === 0 ) return;
|
||||
|
||||
if ( index !== 0 ) {
|
||||
|
||||
gl.drawElementsInstanced( mode, count, type, start, primcount );
|
||||
|
||||
} else {
|
||||
|
||||
gl.drawArraysInstanced( mode, start, count, primcount );
|
||||
|
||||
}
|
||||
|
||||
info.update( object, count, mode, primcount );
|
||||
|
||||
}
|
||||
|
||||
renderMultiDraw( starts, counts, drawCount ) {
|
||||
|
||||
const { extensions, mode, object, info } = this;
|
||||
|
||||
if ( drawCount === 0 ) return;
|
||||
|
||||
const extension = extensions.get( 'WEBGL_multi_draw' );
|
||||
|
||||
if ( extension === null ) {
|
||||
|
||||
for ( let i = 0; i < drawCount; i ++ ) {
|
||||
|
||||
this.render( starts[ i ], counts[ i ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.index !== 0 ) {
|
||||
|
||||
extension.multiDrawElementsWEBGL( mode, counts, 0, this.type, starts, 0, drawCount );
|
||||
|
||||
} else {
|
||||
|
||||
extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );
|
||||
|
||||
}
|
||||
|
||||
let elementCount = 0;
|
||||
for ( let i = 0; i < drawCount; i ++ ) {
|
||||
|
||||
elementCount += counts[ i ];
|
||||
|
||||
}
|
||||
|
||||
info.update( object, elementCount, mode, 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderMultiDrawInstances( starts, counts, drawCount, primcount ) {
|
||||
|
||||
const { extensions, mode, object, info } = this;
|
||||
|
||||
if ( drawCount === 0 ) return;
|
||||
|
||||
const extension = extensions.get( 'WEBGL_multi_draw' );
|
||||
|
||||
if ( extension === null ) {
|
||||
|
||||
for ( let i = 0; i < drawCount; i ++ ) {
|
||||
|
||||
this.renderInstances( starts[ i ], counts[ i ], primcount[ i ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.index !== 0 ) {
|
||||
|
||||
extension.multiDrawElementsInstancedWEBGL( mode, counts, 0, this.type, starts, 0, primcount, 0, drawCount );
|
||||
|
||||
} else {
|
||||
|
||||
extension.multiDrawArraysInstancedWEBGL( mode, starts, 0, counts, 0, primcount, 0, drawCount );
|
||||
|
||||
}
|
||||
|
||||
let elementCount = 0;
|
||||
|
||||
for ( let i = 0; i < drawCount; i ++ ) {
|
||||
|
||||
elementCount += counts[ i ];
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < primcount.length; i ++ ) {
|
||||
|
||||
info.update( object, elementCount, mode, primcount[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
|
||||
export { WebGLBufferRenderer };
|
810
static/sdk/three/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
Normal file
810
static/sdk/three/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
Normal file
@ -0,0 +1,810 @@
|
||||
import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js';
|
||||
|
||||
import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js';
|
||||
import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js';
|
||||
|
||||
import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js';
|
||||
|
||||
import { RedFormat, RGFormat, IntType, DataTexture, RGBFormat, RGBAFormat, FloatType } from 'three';
|
||||
|
||||
const glslMethods = {
|
||||
[ MathNode.ATAN2 ]: 'atan',
|
||||
textureDimensions: 'textureSize',
|
||||
equals: 'equal'
|
||||
};
|
||||
|
||||
const precisionLib = {
|
||||
low: 'lowp',
|
||||
medium: 'mediump',
|
||||
high: 'highp'
|
||||
};
|
||||
|
||||
const supports = {
|
||||
instance: true,
|
||||
swizzleAssign: true
|
||||
};
|
||||
|
||||
const defaultPrecisions = `
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
precision mediump sampler2DArray;
|
||||
precision lowp sampler2DShadow;
|
||||
`;
|
||||
|
||||
class GLSLNodeBuilder extends NodeBuilder {
|
||||
|
||||
constructor( object, renderer, scene = null ) {
|
||||
|
||||
super( object, renderer, new GLSLNodeParser(), scene );
|
||||
|
||||
this.uniformGroups = {};
|
||||
this.transforms = [];
|
||||
|
||||
}
|
||||
|
||||
getMethod( method ) {
|
||||
|
||||
return glslMethods[ method ] || method;
|
||||
|
||||
}
|
||||
|
||||
getPropertyName( node, shaderStage ) {
|
||||
|
||||
if ( node.isOutputStructVar ) return '';
|
||||
|
||||
return super.getPropertyName( node, shaderStage );
|
||||
|
||||
}
|
||||
|
||||
buildFunctionCode( shaderNode ) {
|
||||
|
||||
const layout = shaderNode.layout;
|
||||
const flowData = this.flowShaderNode( shaderNode );
|
||||
|
||||
const parameters = [];
|
||||
|
||||
for ( const input of layout.inputs ) {
|
||||
|
||||
parameters.push( this.getType( input.type ) + ' ' + input.name );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) {
|
||||
|
||||
${ flowData.vars }
|
||||
|
||||
${ flowData.code }
|
||||
return ${ flowData.result };
|
||||
|
||||
}`;
|
||||
|
||||
//
|
||||
|
||||
return code;
|
||||
|
||||
}
|
||||
|
||||
setupPBO( storageBufferNode ) {
|
||||
|
||||
const attribute = storageBufferNode.value;
|
||||
|
||||
if ( attribute.pbo === undefined ) {
|
||||
|
||||
const originalArray = attribute.array;
|
||||
const numElements = attribute.count * attribute.itemSize;
|
||||
|
||||
const { itemSize } = attribute;
|
||||
let format = RedFormat;
|
||||
|
||||
if ( itemSize === 2 ) {
|
||||
|
||||
format = RGFormat;
|
||||
|
||||
} else if ( itemSize === 3 ) {
|
||||
|
||||
format = RGBFormat;
|
||||
|
||||
} else if ( itemSize === 4 ) {
|
||||
|
||||
format = RGBAFormat;
|
||||
|
||||
}
|
||||
|
||||
const width = Math.pow( 2, Math.ceil( Math.log2( Math.sqrt( numElements / itemSize ) ) ) );
|
||||
let height = Math.ceil( ( numElements / itemSize ) / width );
|
||||
if ( width * height * itemSize < numElements ) height ++; // Ensure enough space
|
||||
|
||||
const newSize = width * height * itemSize;
|
||||
|
||||
const newArray = new Float32Array( newSize );
|
||||
|
||||
newArray.set( originalArray, 0 );
|
||||
|
||||
attribute.array = newArray;
|
||||
|
||||
const pboTexture = new DataTexture( attribute.array, width, height, format, FloatType );
|
||||
pboTexture.needsUpdate = true;
|
||||
pboTexture.isPBOTexture = true;
|
||||
|
||||
const pbo = new UniformNode( pboTexture );
|
||||
pbo.setPrecision( 'high' );
|
||||
|
||||
attribute.pboNode = pbo;
|
||||
attribute.pbo = pbo.value;
|
||||
|
||||
this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generatePBO( storageArrayElementNode ) {
|
||||
|
||||
const { node, indexNode } = storageArrayElementNode;
|
||||
const attribute = node.value;
|
||||
|
||||
if ( this.renderer.backend.has( attribute ) ) {
|
||||
|
||||
const attributeData = this.renderer.backend.get( attribute );
|
||||
attributeData.pbo = attribute.pbo;
|
||||
|
||||
}
|
||||
|
||||
|
||||
const nodeUniform = this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label );
|
||||
const textureName = this.getPropertyName( nodeUniform );
|
||||
|
||||
indexNode.increaseUsage( this ); // force cache generate to be used as index in x,y
|
||||
const indexSnippet = indexNode.build( this, 'uint' );
|
||||
|
||||
const elementNodeData = this.getDataFromNode( storageArrayElementNode );
|
||||
|
||||
let propertyName = elementNodeData.propertyName;
|
||||
|
||||
if ( propertyName === undefined ) {
|
||||
|
||||
// property element
|
||||
|
||||
const nodeVar = this.getVarFromNode( storageArrayElementNode );
|
||||
|
||||
propertyName = this.getPropertyName( nodeVar );
|
||||
|
||||
// property size
|
||||
|
||||
const bufferNodeData = this.getDataFromNode( node );
|
||||
|
||||
let propertySizeName = bufferNodeData.propertySizeName;
|
||||
|
||||
if ( propertySizeName === undefined ) {
|
||||
|
||||
propertySizeName = propertyName + 'Size';
|
||||
|
||||
this.getVarFromNode( node, propertySizeName, 'uint' );
|
||||
|
||||
this.addLineFlowCode( `${ propertySizeName } = uint( textureSize( ${ textureName }, 0 ).x )` );
|
||||
|
||||
bufferNodeData.propertySizeName = propertySizeName;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const { itemSize } = attribute;
|
||||
|
||||
const channel = '.' + vectorComponents.join( '' ).slice( 0, itemSize );
|
||||
const uvSnippet = `ivec2(${indexSnippet} % ${ propertySizeName }, ${indexSnippet} / ${ propertySizeName })`;
|
||||
|
||||
const snippet = this.generateTextureLoad( null, textureName, uvSnippet, null, '0' );
|
||||
|
||||
//
|
||||
|
||||
this.addLineFlowCode( `${ propertyName } = ${ snippet + channel }` );
|
||||
|
||||
elementNodeData.propertyName = propertyName;
|
||||
|
||||
}
|
||||
|
||||
return propertyName;
|
||||
|
||||
}
|
||||
|
||||
generateTextureLoad( texture, textureProperty, uvIndexSnippet, depthSnippet, levelSnippet = '0' ) {
|
||||
|
||||
if ( depthSnippet ) {
|
||||
|
||||
return `texelFetch( ${ textureProperty }, ivec3( ${ uvIndexSnippet }, ${ depthSnippet } ), ${ levelSnippet } )`;
|
||||
|
||||
} else {
|
||||
|
||||
return `texelFetch( ${ textureProperty }, ${ uvIndexSnippet }, ${ levelSnippet } )`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateTexture( texture, textureProperty, uvSnippet, depthSnippet ) {
|
||||
|
||||
if ( texture.isDepthTexture ) {
|
||||
|
||||
return `texture( ${ textureProperty }, ${ uvSnippet } ).x`;
|
||||
|
||||
} else {
|
||||
|
||||
if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`;
|
||||
|
||||
return `texture( ${ textureProperty }, ${ uvSnippet } )`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet ) {
|
||||
|
||||
return `textureLod( ${ textureProperty }, ${ uvSnippet }, ${ levelSnippet } )`;
|
||||
|
||||
}
|
||||
|
||||
generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet ) {
|
||||
|
||||
return `textureGrad( ${ textureProperty }, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`;
|
||||
|
||||
}
|
||||
|
||||
generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) {
|
||||
|
||||
if ( shaderStage === 'fragment' ) {
|
||||
|
||||
return `texture( ${ textureProperty }, vec3( ${ uvSnippet }, ${ compareSnippet } ) )`;
|
||||
|
||||
} else {
|
||||
|
||||
console.error( `WebGPURenderer: THREE.DepthTexture.compareFunction() does not support ${ shaderStage } shader.` );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getVars( shaderStage ) {
|
||||
|
||||
const snippets = [];
|
||||
|
||||
const vars = this.vars[ shaderStage ];
|
||||
|
||||
if ( vars !== undefined ) {
|
||||
|
||||
for ( const variable of vars ) {
|
||||
|
||||
if ( variable.isOutputStructVar ) continue;
|
||||
|
||||
snippets.push( `${ this.getVar( variable.type, variable.name ) };` );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return snippets.join( '\n\t' );
|
||||
|
||||
}
|
||||
|
||||
getUniforms( shaderStage ) {
|
||||
|
||||
const uniforms = this.uniforms[ shaderStage ];
|
||||
|
||||
const bindingSnippets = [];
|
||||
const uniformGroups = {};
|
||||
|
||||
for ( const uniform of uniforms ) {
|
||||
|
||||
let snippet = null;
|
||||
let group = false;
|
||||
|
||||
if ( uniform.type === 'texture' ) {
|
||||
|
||||
const texture = uniform.node.value;
|
||||
|
||||
if ( texture.compareFunction ) {
|
||||
|
||||
snippet = `sampler2DShadow ${ uniform.name };`;
|
||||
|
||||
} else if ( texture.isDataArrayTexture === true ) {
|
||||
|
||||
snippet = `sampler2DArray ${ uniform.name };`;
|
||||
|
||||
} else {
|
||||
|
||||
snippet = `sampler2D ${ uniform.name };`;
|
||||
|
||||
}
|
||||
|
||||
} else if ( uniform.type === 'cubeTexture' ) {
|
||||
|
||||
snippet = `samplerCube ${ uniform.name };`;
|
||||
|
||||
} else if ( uniform.type === 'buffer' ) {
|
||||
|
||||
const bufferNode = uniform.node;
|
||||
const bufferType = this.getType( bufferNode.bufferType );
|
||||
const bufferCount = bufferNode.bufferCount;
|
||||
|
||||
const bufferCountSnippet = bufferCount > 0 ? bufferCount : '';
|
||||
snippet = `${bufferNode.name} {\n\t${ bufferType } ${ uniform.name }[${ bufferCountSnippet }];\n};\n`;
|
||||
|
||||
} else {
|
||||
|
||||
const vectorType = this.getVectorType( uniform.type );
|
||||
|
||||
snippet = `${vectorType} ${uniform.name};`;
|
||||
|
||||
group = true;
|
||||
|
||||
}
|
||||
|
||||
const precision = uniform.node.precision;
|
||||
|
||||
if ( precision !== null ) {
|
||||
|
||||
snippet = precisionLib[ precision ] + ' ' + snippet;
|
||||
|
||||
}
|
||||
|
||||
if ( group ) {
|
||||
|
||||
snippet = '\t' + snippet;
|
||||
|
||||
const groupName = uniform.groupNode.name;
|
||||
const groupSnippets = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = [] );
|
||||
|
||||
groupSnippets.push( snippet );
|
||||
|
||||
} else {
|
||||
|
||||
snippet = 'uniform ' + snippet;
|
||||
|
||||
bindingSnippets.push( snippet );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let output = '';
|
||||
|
||||
for ( const name in uniformGroups ) {
|
||||
|
||||
const groupSnippets = uniformGroups[ name ];
|
||||
|
||||
output += this._getGLSLUniformStruct( shaderStage + '_' + name, groupSnippets.join( '\n' ) ) + '\n';
|
||||
|
||||
}
|
||||
|
||||
output += bindingSnippets.join( '\n' );
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
getTypeFromAttribute( attribute ) {
|
||||
|
||||
let nodeType = super.getTypeFromAttribute( attribute );
|
||||
|
||||
if ( /^[iu]/.test( nodeType ) && attribute.gpuType !== IntType ) {
|
||||
|
||||
let dataAttribute = attribute;
|
||||
|
||||
if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data;
|
||||
|
||||
const array = dataAttribute.array;
|
||||
|
||||
if ( ( array instanceof Uint32Array || array instanceof Int32Array || array instanceof Uint16Array || array instanceof Int16Array ) === false ) {
|
||||
|
||||
nodeType = nodeType.slice( 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nodeType;
|
||||
|
||||
}
|
||||
|
||||
getAttributes( shaderStage ) {
|
||||
|
||||
let snippet = '';
|
||||
|
||||
if ( shaderStage === 'vertex' || shaderStage === 'compute' ) {
|
||||
|
||||
const attributes = this.getAttributesArray();
|
||||
|
||||
let location = 0;
|
||||
|
||||
for ( const attribute of attributes ) {
|
||||
|
||||
snippet += `layout( location = ${ location ++ } ) in ${ attribute.type } ${ attribute.name };\n`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return snippet;
|
||||
|
||||
}
|
||||
|
||||
getStructMembers( struct ) {
|
||||
|
||||
const snippets = [];
|
||||
const members = struct.getMemberTypes();
|
||||
|
||||
for ( let i = 0; i < members.length; i ++ ) {
|
||||
|
||||
const member = members[ i ];
|
||||
snippets.push( `layout( location = ${i} ) out ${ member} m${i};` );
|
||||
|
||||
}
|
||||
|
||||
return snippets.join( '\n' );
|
||||
|
||||
}
|
||||
|
||||
getStructs( shaderStage ) {
|
||||
|
||||
const snippets = [];
|
||||
const structs = this.structs[ shaderStage ];
|
||||
|
||||
if ( structs.length === 0 ) {
|
||||
|
||||
return 'layout( location = 0 ) out vec4 fragColor;\n';
|
||||
|
||||
}
|
||||
|
||||
for ( let index = 0, length = structs.length; index < length; index ++ ) {
|
||||
|
||||
const struct = structs[ index ];
|
||||
|
||||
let snippet = '\n';
|
||||
snippet += this.getStructMembers( struct );
|
||||
snippet += '\n';
|
||||
|
||||
snippets.push( snippet );
|
||||
|
||||
}
|
||||
|
||||
return snippets.join( '\n\n' );
|
||||
|
||||
}
|
||||
|
||||
getVaryings( shaderStage ) {
|
||||
|
||||
let snippet = '';
|
||||
|
||||
const varyings = this.varyings;
|
||||
|
||||
if ( shaderStage === 'vertex' || shaderStage === 'compute' ) {
|
||||
|
||||
for ( const varying of varyings ) {
|
||||
|
||||
if ( shaderStage === 'compute' ) varying.needsInterpolation = true;
|
||||
const type = varying.type;
|
||||
const flat = type === 'int' || type === 'uint' ? 'flat ' : '';
|
||||
|
||||
snippet += `${flat}${varying.needsInterpolation ? 'out' : '/*out*/'} ${type} ${varying.name};\n`;
|
||||
|
||||
}
|
||||
|
||||
} else if ( shaderStage === 'fragment' ) {
|
||||
|
||||
for ( const varying of varyings ) {
|
||||
|
||||
if ( varying.needsInterpolation ) {
|
||||
|
||||
const type = varying.type;
|
||||
const flat = type === 'int' || type === 'uint' ? 'flat ' : '';
|
||||
|
||||
snippet += `${flat}in ${type} ${varying.name};\n`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return snippet;
|
||||
|
||||
}
|
||||
|
||||
getVertexIndex() {
|
||||
|
||||
return 'uint( gl_VertexID )';
|
||||
|
||||
}
|
||||
|
||||
getInstanceIndex() {
|
||||
|
||||
return 'uint( gl_InstanceID )';
|
||||
|
||||
}
|
||||
|
||||
getFrontFacing() {
|
||||
|
||||
return 'gl_FrontFacing';
|
||||
|
||||
}
|
||||
|
||||
getFragCoord() {
|
||||
|
||||
return 'gl_FragCoord';
|
||||
|
||||
}
|
||||
|
||||
getFragDepth() {
|
||||
|
||||
return 'gl_FragDepth';
|
||||
|
||||
}
|
||||
|
||||
isAvailable( name ) {
|
||||
|
||||
return supports[ name ] === true;
|
||||
|
||||
}
|
||||
|
||||
isFlipY() {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
registerTransform( varyingName, attributeNode ) {
|
||||
|
||||
this.transforms.push( { varyingName, attributeNode } );
|
||||
|
||||
}
|
||||
|
||||
getTransforms( /* shaderStage */ ) {
|
||||
|
||||
const transforms = this.transforms;
|
||||
|
||||
let snippet = '';
|
||||
|
||||
for ( let i = 0; i < transforms.length; i ++ ) {
|
||||
|
||||
const transform = transforms[ i ];
|
||||
|
||||
const attributeName = this.getPropertyName( transform.attributeNode );
|
||||
|
||||
snippet += `${ transform.varyingName } = ${ attributeName };\n\t`;
|
||||
|
||||
}
|
||||
|
||||
return snippet;
|
||||
|
||||
}
|
||||
|
||||
_getGLSLUniformStruct( name, vars ) {
|
||||
|
||||
return `
|
||||
layout( std140 ) uniform ${name} {
|
||||
${vars}
|
||||
};`;
|
||||
|
||||
}
|
||||
|
||||
_getGLSLVertexCode( shaderData ) {
|
||||
|
||||
return `#version 300 es
|
||||
|
||||
${ this.getSignature() }
|
||||
|
||||
// precision
|
||||
${ defaultPrecisions }
|
||||
|
||||
// uniforms
|
||||
${shaderData.uniforms}
|
||||
|
||||
// varyings
|
||||
${shaderData.varyings}
|
||||
|
||||
// attributes
|
||||
${shaderData.attributes}
|
||||
|
||||
// codes
|
||||
${shaderData.codes}
|
||||
|
||||
void main() {
|
||||
|
||||
// vars
|
||||
${shaderData.vars}
|
||||
|
||||
// transforms
|
||||
${shaderData.transforms}
|
||||
|
||||
// flow
|
||||
${shaderData.flow}
|
||||
|
||||
gl_PointSize = 1.0;
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
_getGLSLFragmentCode( shaderData ) {
|
||||
|
||||
return `#version 300 es
|
||||
|
||||
${ this.getSignature() }
|
||||
|
||||
// precision
|
||||
${ defaultPrecisions }
|
||||
|
||||
// uniforms
|
||||
${shaderData.uniforms}
|
||||
|
||||
// varyings
|
||||
${shaderData.varyings}
|
||||
|
||||
// codes
|
||||
${shaderData.codes}
|
||||
|
||||
${shaderData.structs}
|
||||
|
||||
void main() {
|
||||
|
||||
// vars
|
||||
${shaderData.vars}
|
||||
|
||||
// flow
|
||||
${shaderData.flow}
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
buildCode() {
|
||||
|
||||
const shadersData = this.material !== null ? { fragment: {}, vertex: {} } : { compute: {} };
|
||||
|
||||
for ( const shaderStage in shadersData ) {
|
||||
|
||||
let flow = '// code\n\n';
|
||||
flow += this.flowCode[ shaderStage ];
|
||||
|
||||
const flowNodes = this.flowNodes[ shaderStage ];
|
||||
const mainNode = flowNodes[ flowNodes.length - 1 ];
|
||||
|
||||
for ( const node of flowNodes ) {
|
||||
|
||||
const flowSlotData = this.getFlowData( node/*, shaderStage*/ );
|
||||
const slotName = node.name;
|
||||
|
||||
if ( slotName ) {
|
||||
|
||||
if ( flow.length > 0 ) flow += '\n';
|
||||
|
||||
flow += `\t// flow -> ${ slotName }\n\t`;
|
||||
|
||||
}
|
||||
|
||||
flow += `${ flowSlotData.code }\n\t`;
|
||||
|
||||
if ( node === mainNode && shaderStage !== 'compute' ) {
|
||||
|
||||
flow += '// result\n\t';
|
||||
|
||||
if ( shaderStage === 'vertex' ) {
|
||||
|
||||
flow += 'gl_Position = ';
|
||||
flow += `${ flowSlotData.result };`;
|
||||
|
||||
} else if ( shaderStage === 'fragment' ) {
|
||||
|
||||
if ( ! node.outputNode.isOutputStructNode ) {
|
||||
|
||||
flow += 'fragColor = ';
|
||||
flow += `${ flowSlotData.result };`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const stageData = shadersData[ shaderStage ];
|
||||
|
||||
stageData.uniforms = this.getUniforms( shaderStage );
|
||||
stageData.attributes = this.getAttributes( shaderStage );
|
||||
stageData.varyings = this.getVaryings( shaderStage );
|
||||
stageData.vars = this.getVars( shaderStage );
|
||||
stageData.structs = this.getStructs( shaderStage );
|
||||
stageData.codes = this.getCodes( shaderStage );
|
||||
stageData.transforms = this.getTransforms( shaderStage );
|
||||
stageData.flow = flow;
|
||||
|
||||
}
|
||||
|
||||
if ( this.material !== null ) {
|
||||
|
||||
this.vertexShader = this._getGLSLVertexCode( shadersData.vertex );
|
||||
this.fragmentShader = this._getGLSLFragmentCode( shadersData.fragment );
|
||||
|
||||
} else {
|
||||
|
||||
this.computeShader = this._getGLSLVertexCode( shadersData.compute );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getUniformFromNode( node, type, shaderStage, name = null ) {
|
||||
|
||||
const uniformNode = super.getUniformFromNode( node, type, shaderStage, name );
|
||||
const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );
|
||||
|
||||
let uniformGPU = nodeData.uniformGPU;
|
||||
|
||||
if ( uniformGPU === undefined ) {
|
||||
|
||||
if ( type === 'texture' ) {
|
||||
|
||||
uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node );
|
||||
|
||||
this.bindings[ shaderStage ].push( uniformGPU );
|
||||
|
||||
} else if ( type === 'cubeTexture' ) {
|
||||
|
||||
uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node );
|
||||
|
||||
this.bindings[ shaderStage ].push( uniformGPU );
|
||||
|
||||
} else if ( type === 'buffer' ) {
|
||||
|
||||
node.name = `NodeBuffer_${ node.id }`;
|
||||
uniformNode.name = `buffer${ node.id }`;
|
||||
|
||||
const buffer = new NodeUniformBuffer( node );
|
||||
buffer.name = node.name;
|
||||
|
||||
this.bindings[ shaderStage ].push( buffer );
|
||||
|
||||
uniformGPU = buffer;
|
||||
|
||||
} else {
|
||||
|
||||
const group = node.groupNode;
|
||||
const groupName = group.name;
|
||||
|
||||
const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} );
|
||||
|
||||
let uniformsGroup = uniformsStage[ groupName ];
|
||||
|
||||
if ( uniformsGroup === undefined ) {
|
||||
|
||||
uniformsGroup = new NodeUniformsGroup( shaderStage + '_' + groupName, group );
|
||||
//uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );
|
||||
|
||||
uniformsStage[ groupName ] = uniformsGroup;
|
||||
|
||||
this.bindings[ shaderStage ].push( uniformsGroup );
|
||||
|
||||
}
|
||||
|
||||
uniformGPU = this.getNodeUniform( uniformNode, type );
|
||||
|
||||
uniformsGroup.addUniform( uniformGPU );
|
||||
|
||||
}
|
||||
|
||||
nodeData.uniformGPU = uniformGPU;
|
||||
|
||||
}
|
||||
|
||||
return uniformNode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GLSLNodeBuilder;
|
@ -0,0 +1,258 @@
|
||||
import { IntType } from 'three';
|
||||
|
||||
let _id = 0;
|
||||
|
||||
class DualAttributeData {
|
||||
|
||||
constructor( attributeData, dualBuffer ) {
|
||||
|
||||
this.buffers = [ attributeData.bufferGPU, dualBuffer ];
|
||||
this.type = attributeData.type;
|
||||
this.bufferType = attributeData.bufferType;
|
||||
this.pbo = attributeData.pbo;
|
||||
this.byteLength = attributeData.byteLength;
|
||||
this.bytesPerElement = attributeData.BYTES_PER_ELEMENT;
|
||||
this.version = attributeData.version;
|
||||
this.isInteger = attributeData.isInteger;
|
||||
this.activeBufferIndex = 0;
|
||||
this.baseId = attributeData.id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
get id() {
|
||||
|
||||
return `${ this.baseId }|${ this.activeBufferIndex }`;
|
||||
|
||||
}
|
||||
|
||||
get bufferGPU() {
|
||||
|
||||
return this.buffers[ this.activeBufferIndex ];
|
||||
|
||||
}
|
||||
|
||||
get transformBuffer() {
|
||||
|
||||
return this.buffers[ this.activeBufferIndex ^ 1 ];
|
||||
|
||||
}
|
||||
|
||||
switchBuffers() {
|
||||
|
||||
this.activeBufferIndex ^= 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebGLAttributeUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
createAttribute( attribute, bufferType ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const { gl } = backend;
|
||||
|
||||
const array = attribute.array;
|
||||
const usage = attribute.usage || gl.STATIC_DRAW;
|
||||
|
||||
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
||||
const bufferData = backend.get( bufferAttribute );
|
||||
|
||||
let bufferGPU = bufferData.bufferGPU;
|
||||
|
||||
if ( bufferGPU === undefined ) {
|
||||
|
||||
bufferGPU = this._createBuffer( gl, bufferType, array, usage );
|
||||
|
||||
bufferData.bufferGPU = bufferGPU;
|
||||
bufferData.bufferType = bufferType;
|
||||
bufferData.version = bufferAttribute.version;
|
||||
|
||||
}
|
||||
|
||||
//attribute.onUploadCallback();
|
||||
|
||||
let type;
|
||||
|
||||
if ( array instanceof Float32Array ) {
|
||||
|
||||
type = gl.FLOAT;
|
||||
|
||||
} else if ( array instanceof Uint16Array ) {
|
||||
|
||||
if ( attribute.isFloat16BufferAttribute ) {
|
||||
|
||||
type = gl.HALF_FLOAT;
|
||||
|
||||
} else {
|
||||
|
||||
type = gl.UNSIGNED_SHORT;
|
||||
|
||||
}
|
||||
|
||||
} else if ( array instanceof Int16Array ) {
|
||||
|
||||
type = gl.SHORT;
|
||||
|
||||
} else if ( array instanceof Uint32Array ) {
|
||||
|
||||
type = gl.UNSIGNED_INT;
|
||||
|
||||
} else if ( array instanceof Int32Array ) {
|
||||
|
||||
type = gl.INT;
|
||||
|
||||
} else if ( array instanceof Int8Array ) {
|
||||
|
||||
type = gl.BYTE;
|
||||
|
||||
} else if ( array instanceof Uint8Array ) {
|
||||
|
||||
type = gl.UNSIGNED_BYTE;
|
||||
|
||||
} else if ( array instanceof Uint8ClampedArray ) {
|
||||
|
||||
type = gl.UNSIGNED_BYTE;
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.WebGLBackend: Unsupported buffer data format: ' + array );
|
||||
|
||||
}
|
||||
|
||||
let attributeData = {
|
||||
bufferGPU,
|
||||
bufferType,
|
||||
type,
|
||||
byteLength: array.byteLength,
|
||||
bytesPerElement: array.BYTES_PER_ELEMENT,
|
||||
version: attribute.version,
|
||||
pbo: attribute.pbo,
|
||||
isInteger: type === gl.INT || type === gl.UNSIGNED_INT || type === gl.UNSIGNED_SHORT || attribute.gpuType === IntType,
|
||||
id: _id ++
|
||||
};
|
||||
|
||||
if ( attribute.isStorageBufferAttribute || attribute.isStorageInstancedBufferAttribute ) {
|
||||
|
||||
// create buffer for tranform feedback use
|
||||
const bufferGPUDual = this._createBuffer( gl, bufferType, array, usage );
|
||||
attributeData = new DualAttributeData( attributeData, bufferGPUDual );
|
||||
|
||||
}
|
||||
|
||||
backend.set( attribute, attributeData );
|
||||
|
||||
}
|
||||
|
||||
updateAttribute( attribute ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const { gl } = backend;
|
||||
|
||||
const array = attribute.array;
|
||||
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
||||
const bufferData = backend.get( bufferAttribute );
|
||||
const bufferType = bufferData.bufferType;
|
||||
const updateRanges = attribute.isInterleavedBufferAttribute ? attribute.data.updateRanges : attribute.updateRanges;
|
||||
|
||||
gl.bindBuffer( bufferType, bufferData.bufferGPU );
|
||||
|
||||
if ( updateRanges.length === 0 ) {
|
||||
|
||||
// Not using update ranges
|
||||
|
||||
gl.bufferSubData( bufferType, 0, array );
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {
|
||||
|
||||
const range = updateRanges[ i ];
|
||||
gl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT,
|
||||
array, range.start, range.count );
|
||||
|
||||
}
|
||||
|
||||
bufferAttribute.clearUpdateRanges();
|
||||
|
||||
}
|
||||
|
||||
gl.bindBuffer( bufferType, null );
|
||||
|
||||
bufferData.version = bufferAttribute.version;
|
||||
|
||||
}
|
||||
|
||||
destroyAttribute( attribute ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const { gl } = backend;
|
||||
|
||||
if ( attribute.isInterleavedBufferAttribute ) {
|
||||
|
||||
backend.delete( attribute.data );
|
||||
|
||||
}
|
||||
|
||||
const attributeData = backend.get( attribute );
|
||||
|
||||
gl.deleteBuffer( attributeData.bufferGPU );
|
||||
|
||||
backend.delete( attribute );
|
||||
|
||||
}
|
||||
|
||||
async getArrayBufferAsync( attribute ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const { gl } = backend;
|
||||
|
||||
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
|
||||
const { bufferGPU } = backend.get( bufferAttribute );
|
||||
|
||||
const array = attribute.array;
|
||||
const byteLength = array.byteLength;
|
||||
|
||||
gl.bindBuffer( gl.COPY_READ_BUFFER, bufferGPU );
|
||||
|
||||
const writeBuffer = gl.createBuffer();
|
||||
|
||||
gl.bindBuffer( gl.COPY_WRITE_BUFFER, writeBuffer );
|
||||
gl.bufferData( gl.COPY_WRITE_BUFFER, byteLength, gl.STREAM_READ );
|
||||
|
||||
gl.copyBufferSubData( gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, byteLength );
|
||||
|
||||
await backend.utils._clientWaitAsync();
|
||||
|
||||
const dstBuffer = new attribute.array.constructor( array.length );
|
||||
|
||||
gl.getBufferSubData( gl.COPY_WRITE_BUFFER, 0, dstBuffer );
|
||||
|
||||
gl.deleteBuffer( writeBuffer );
|
||||
|
||||
return dstBuffer.buffer;
|
||||
|
||||
}
|
||||
|
||||
_createBuffer( gl, bufferType, array, usage ) {
|
||||
|
||||
const bufferGPU = gl.createBuffer();
|
||||
|
||||
gl.bindBuffer( bufferType, bufferGPU );
|
||||
gl.bufferData( bufferType, array, usage );
|
||||
gl.bindBuffer( bufferType, null );
|
||||
|
||||
return bufferGPU;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLAttributeUtils;
|
@ -0,0 +1,36 @@
|
||||
class WebGLCapabilities {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
this.maxAnisotropy = null;
|
||||
|
||||
}
|
||||
|
||||
getMaxAnisotropy() {
|
||||
|
||||
if ( this.maxAnisotropy !== null ) return this.maxAnisotropy;
|
||||
|
||||
const gl = this.backend.gl;
|
||||
const extensions = this.backend.extensions;
|
||||
|
||||
if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
|
||||
|
||||
const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
|
||||
|
||||
this.maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
|
||||
|
||||
} else {
|
||||
|
||||
this.maxAnisotropy = 0;
|
||||
|
||||
}
|
||||
|
||||
return this.maxAnisotropy;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLCapabilities;
|
12
static/sdk/three/jsm/renderers/webgl/utils/WebGLConstants.js
Normal file
12
static/sdk/three/jsm/renderers/webgl/utils/WebGLConstants.js
Normal file
@ -0,0 +1,12 @@
|
||||
export const GLFeatureName = {
|
||||
|
||||
'WEBGL_compressed_texture_astc': 'texture-compression-astc',
|
||||
'WEBGL_compressed_texture_etc': 'texture-compression-etc2',
|
||||
'WEBGL_compressed_texture_etc1': 'texture-compression-etc1',
|
||||
'WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc',
|
||||
'WEBKIT_WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc',
|
||||
'WEBGL_compressed_texture_s3tc': 'texture-compression-bc',
|
||||
'EXT_texture_compression_bptc': 'texture-compression-bptc',
|
||||
'EXT_disjoint_timer_query_webgl2': 'timestamp-query',
|
||||
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
class WebGLExtensions {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
this.gl = this.backend.gl;
|
||||
this.availableExtensions = this.gl.getSupportedExtensions();
|
||||
|
||||
this.extensions = {};
|
||||
|
||||
}
|
||||
|
||||
get( name ) {
|
||||
|
||||
let extension = this.extensions[ name ];
|
||||
|
||||
if ( extension === undefined ) {
|
||||
|
||||
extension = this.gl.getExtension( name );
|
||||
|
||||
}
|
||||
|
||||
return extension;
|
||||
|
||||
}
|
||||
|
||||
has( name ) {
|
||||
|
||||
return this.availableExtensions.includes( name );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLExtensions;
|
738
static/sdk/three/jsm/renderers/webgl/utils/WebGLState.js
Normal file
738
static/sdk/three/jsm/renderers/webgl/utils/WebGLState.js
Normal file
@ -0,0 +1,738 @@
|
||||
import {
|
||||
CullFaceNone, CullFaceBack, CullFaceFront, DoubleSide, BackSide,
|
||||
NormalBlending, NoBlending, CustomBlending, AddEquation,
|
||||
AdditiveBlending, SubtractiveBlending, MultiplyBlending, SubtractEquation, ReverseSubtractEquation,
|
||||
ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor,
|
||||
OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor,
|
||||
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth
|
||||
} from 'three';
|
||||
|
||||
let initialized = false, equationToGL, factorToGL;
|
||||
|
||||
class WebGLState {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
this.gl = this.backend.gl;
|
||||
|
||||
this.enabled = {};
|
||||
this.currentFlipSided = null;
|
||||
this.currentCullFace = null;
|
||||
this.currentProgram = null;
|
||||
this.currentBlendingEnabled = false;
|
||||
this.currentBlending = null;
|
||||
this.currentBlendSrc = null;
|
||||
this.currentBlendDst = null;
|
||||
this.currentBlendSrcAlpha = null;
|
||||
this.currentBlendDstAlpha = null;
|
||||
this.currentPremultipledAlpha = null;
|
||||
this.currentPolygonOffsetFactor = null;
|
||||
this.currentPolygonOffsetUnits = null;
|
||||
this.currentColorMask = null;
|
||||
this.currentDepthFunc = null;
|
||||
this.currentDepthMask = null;
|
||||
this.currentStencilFunc = null;
|
||||
this.currentStencilRef = null;
|
||||
this.currentStencilFuncMask = null;
|
||||
this.currentStencilFail = null;
|
||||
this.currentStencilZFail = null;
|
||||
this.currentStencilZPass = null;
|
||||
this.currentStencilMask = null;
|
||||
this.currentLineWidth = null;
|
||||
|
||||
this.currentBoundFramebuffers = {};
|
||||
this.currentDrawbuffers = new WeakMap();
|
||||
|
||||
this.maxTextures = this.gl.getParameter( this.gl.MAX_TEXTURE_IMAGE_UNITS );
|
||||
this.currentTextureSlot = null;
|
||||
this.currentBoundTextures = {};
|
||||
|
||||
if ( initialized === false ) {
|
||||
|
||||
this._init( this.gl );
|
||||
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_init( gl ) {
|
||||
|
||||
// Store only WebGL constants here.
|
||||
|
||||
equationToGL = {
|
||||
[ AddEquation ]: gl.FUNC_ADD,
|
||||
[ SubtractEquation ]: gl.FUNC_SUBTRACT,
|
||||
[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT
|
||||
};
|
||||
|
||||
factorToGL = {
|
||||
[ ZeroFactor ]: gl.ZERO,
|
||||
[ OneFactor ]: gl.ONE,
|
||||
[ SrcColorFactor ]: gl.SRC_COLOR,
|
||||
[ SrcAlphaFactor ]: gl.SRC_ALPHA,
|
||||
[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,
|
||||
[ DstColorFactor ]: gl.DST_COLOR,
|
||||
[ DstAlphaFactor ]: gl.DST_ALPHA,
|
||||
[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,
|
||||
[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,
|
||||
[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,
|
||||
[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
enable( id ) {
|
||||
|
||||
const { enabled } = this;
|
||||
|
||||
if ( enabled[ id ] !== true ) {
|
||||
|
||||
this.gl.enable( id );
|
||||
enabled[ id ] = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disable( id ) {
|
||||
|
||||
const { enabled } = this;
|
||||
|
||||
if ( enabled[ id ] !== false ) {
|
||||
|
||||
this.gl.disable( id );
|
||||
enabled[ id ] = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setFlipSided( flipSided ) {
|
||||
|
||||
if ( this.currentFlipSided !== flipSided ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( flipSided ) {
|
||||
|
||||
gl.frontFace( gl.CW );
|
||||
|
||||
} else {
|
||||
|
||||
gl.frontFace( gl.CCW );
|
||||
|
||||
}
|
||||
|
||||
this.currentFlipSided = flipSided;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setCullFace( cullFace ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( cullFace !== CullFaceNone ) {
|
||||
|
||||
this.enable( gl.CULL_FACE );
|
||||
|
||||
if ( cullFace !== this.currentCullFace ) {
|
||||
|
||||
if ( cullFace === CullFaceBack ) {
|
||||
|
||||
gl.cullFace( gl.BACK );
|
||||
|
||||
} else if ( cullFace === CullFaceFront ) {
|
||||
|
||||
gl.cullFace( gl.FRONT );
|
||||
|
||||
} else {
|
||||
|
||||
gl.cullFace( gl.FRONT_AND_BACK );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this.disable( gl.CULL_FACE );
|
||||
|
||||
}
|
||||
|
||||
this.currentCullFace = cullFace;
|
||||
|
||||
}
|
||||
|
||||
setLineWidth( width ) {
|
||||
|
||||
const { currentLineWidth, gl } = this;
|
||||
|
||||
if ( width !== currentLineWidth ) {
|
||||
|
||||
gl.lineWidth( width );
|
||||
|
||||
this.currentLineWidth = width;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( blending === NoBlending ) {
|
||||
|
||||
if ( this.currentBlendingEnabled === true ) {
|
||||
|
||||
this.disable( gl.BLEND );
|
||||
this.currentBlendingEnabled = false;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ( this.currentBlendingEnabled === false ) {
|
||||
|
||||
this.enable( gl.BLEND );
|
||||
this.currentBlendingEnabled = true;
|
||||
|
||||
}
|
||||
|
||||
if ( blending !== CustomBlending ) {
|
||||
|
||||
if ( blending !== this.currentBlending || premultipliedAlpha !== this.currentPremultipledAlpha ) {
|
||||
|
||||
if ( this.currentBlendEquation !== AddEquation || this.currentBlendEquationAlpha !== AddEquation ) {
|
||||
|
||||
gl.blendEquation( gl.FUNC_ADD );
|
||||
|
||||
this.currentBlendEquation = AddEquation;
|
||||
this.currentBlendEquationAlpha = AddEquation;
|
||||
|
||||
}
|
||||
|
||||
if ( premultipliedAlpha ) {
|
||||
|
||||
switch ( blending ) {
|
||||
|
||||
case NormalBlending:
|
||||
gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
|
||||
break;
|
||||
|
||||
case AdditiveBlending:
|
||||
gl.blendFunc( gl.ONE, gl.ONE );
|
||||
break;
|
||||
|
||||
case SubtractiveBlending:
|
||||
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
|
||||
break;
|
||||
|
||||
case MultiplyBlending:
|
||||
gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
switch ( blending ) {
|
||||
|
||||
case NormalBlending:
|
||||
gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
|
||||
break;
|
||||
|
||||
case AdditiveBlending:
|
||||
gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
|
||||
break;
|
||||
|
||||
case SubtractiveBlending:
|
||||
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
|
||||
break;
|
||||
|
||||
case MultiplyBlending:
|
||||
gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.currentBlendSrc = null;
|
||||
this.currentBlendDst = null;
|
||||
this.currentBlendSrcAlpha = null;
|
||||
this.currentBlendDstAlpha = null;
|
||||
|
||||
this.currentBlending = blending;
|
||||
this.currentPremultipledAlpha = premultipliedAlpha;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// custom blending
|
||||
|
||||
blendEquationAlpha = blendEquationAlpha || blendEquation;
|
||||
blendSrcAlpha = blendSrcAlpha || blendSrc;
|
||||
blendDstAlpha = blendDstAlpha || blendDst;
|
||||
|
||||
if ( blendEquation !== this.currentBlendEquation || blendEquationAlpha !== this.currentBlendEquationAlpha ) {
|
||||
|
||||
gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
|
||||
|
||||
this.currentBlendEquation = blendEquation;
|
||||
this.currentBlendEquationAlpha = blendEquationAlpha;
|
||||
|
||||
}
|
||||
|
||||
if ( blendSrc !== this.currentBlendSrc || blendDst !== this.currentBlendDst || blendSrcAlpha !== this.currentBlendSrcAlpha || blendDstAlpha !== this.currentBlendDstAlpha ) {
|
||||
|
||||
gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
|
||||
|
||||
this.currentBlendSrc = blendSrc;
|
||||
this.currentBlendDst = blendDst;
|
||||
this.currentBlendSrcAlpha = blendSrcAlpha;
|
||||
this.currentBlendDstAlpha = blendDstAlpha;
|
||||
|
||||
}
|
||||
|
||||
this.currentBlending = blending;
|
||||
this.currentPremultipledAlpha = false;
|
||||
|
||||
}
|
||||
|
||||
setColorMask( colorMask ) {
|
||||
|
||||
if ( this.currentColorMask !== colorMask ) {
|
||||
|
||||
this.gl.colorMask( colorMask, colorMask, colorMask, colorMask );
|
||||
this.currentColorMask = colorMask;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setDepthTest( depthTest ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( depthTest ) {
|
||||
|
||||
this.enable( gl.DEPTH_TEST );
|
||||
|
||||
} else {
|
||||
|
||||
this.disable( gl.DEPTH_TEST );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setDepthMask( depthMask ) {
|
||||
|
||||
if ( this.currentDepthMask !== depthMask ) {
|
||||
|
||||
this.gl.depthMask( depthMask );
|
||||
this.currentDepthMask = depthMask;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setDepthFunc( depthFunc ) {
|
||||
|
||||
if ( this.currentDepthFunc !== depthFunc ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
switch ( depthFunc ) {
|
||||
|
||||
case NeverDepth:
|
||||
|
||||
gl.depthFunc( gl.NEVER );
|
||||
break;
|
||||
|
||||
case AlwaysDepth:
|
||||
|
||||
gl.depthFunc( gl.ALWAYS );
|
||||
break;
|
||||
|
||||
case LessDepth:
|
||||
|
||||
gl.depthFunc( gl.LESS );
|
||||
break;
|
||||
|
||||
case LessEqualDepth:
|
||||
|
||||
gl.depthFunc( gl.LEQUAL );
|
||||
break;
|
||||
|
||||
case EqualDepth:
|
||||
|
||||
gl.depthFunc( gl.EQUAL );
|
||||
break;
|
||||
|
||||
case GreaterEqualDepth:
|
||||
|
||||
gl.depthFunc( gl.GEQUAL );
|
||||
break;
|
||||
|
||||
case GreaterDepth:
|
||||
|
||||
gl.depthFunc( gl.GREATER );
|
||||
break;
|
||||
|
||||
case NotEqualDepth:
|
||||
|
||||
gl.depthFunc( gl.NOTEQUAL );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
gl.depthFunc( gl.LEQUAL );
|
||||
|
||||
}
|
||||
|
||||
this.currentDepthFunc = depthFunc;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setStencilTest( stencilTest ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( stencilTest ) {
|
||||
|
||||
this.enable( gl.STENCIL_TEST );
|
||||
|
||||
} else {
|
||||
|
||||
this.disable( gl.STENCIL_TEST );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setStencilMask( stencilMask ) {
|
||||
|
||||
if ( this.currentStencilMask !== stencilMask ) {
|
||||
|
||||
this.gl.stencilMask( stencilMask );
|
||||
this.currentStencilMask = stencilMask;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setStencilFunc( stencilFunc, stencilRef, stencilMask ) {
|
||||
|
||||
if ( this.currentStencilFunc !== stencilFunc ||
|
||||
this.currentStencilRef !== stencilRef ||
|
||||
this.currentStencilFuncMask !== stencilMask ) {
|
||||
|
||||
this.gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
|
||||
|
||||
this.currentStencilFunc = stencilFunc;
|
||||
this.currentStencilRef = stencilRef;
|
||||
this.currentStencilFuncMask = stencilMask;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setStencilOp( stencilFail, stencilZFail, stencilZPass ) {
|
||||
|
||||
if ( this.currentStencilFail !== stencilFail ||
|
||||
this.currentStencilZFail !== stencilZFail ||
|
||||
this.currentStencilZPass !== stencilZPass ) {
|
||||
|
||||
this.gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
|
||||
|
||||
this.currentStencilFail = stencilFail;
|
||||
this.currentStencilZFail = stencilZFail;
|
||||
this.currentStencilZPass = stencilZPass;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setMaterial( material, frontFaceCW ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
material.side === DoubleSide
|
||||
? this.disable( gl.CULL_FACE )
|
||||
: this.enable( gl.CULL_FACE );
|
||||
|
||||
let flipSided = ( material.side === BackSide );
|
||||
if ( frontFaceCW ) flipSided = ! flipSided;
|
||||
|
||||
this.setFlipSided( flipSided );
|
||||
|
||||
( material.blending === NormalBlending && material.transparent === false )
|
||||
? this.setBlending( NoBlending )
|
||||
: this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
|
||||
|
||||
this.setDepthFunc( material.depthFunc );
|
||||
this.setDepthTest( material.depthTest );
|
||||
this.setDepthMask( material.depthWrite );
|
||||
this.setColorMask( material.colorWrite );
|
||||
|
||||
const stencilWrite = material.stencilWrite;
|
||||
this.setStencilTest( stencilWrite );
|
||||
if ( stencilWrite ) {
|
||||
|
||||
this.setStencilMask( material.stencilWriteMask );
|
||||
this.setStencilFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
|
||||
this.setStencilOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
|
||||
|
||||
}
|
||||
|
||||
this.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
|
||||
|
||||
material.alphaToCoverage === true
|
||||
? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
|
||||
: this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
|
||||
|
||||
}
|
||||
|
||||
setPolygonOffset( polygonOffset, factor, units ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( polygonOffset ) {
|
||||
|
||||
this.enable( gl.POLYGON_OFFSET_FILL );
|
||||
|
||||
if ( this.currentPolygonOffsetFactor !== factor || this.currentPolygonOffsetUnits !== units ) {
|
||||
|
||||
gl.polygonOffset( factor, units );
|
||||
|
||||
this.currentPolygonOffsetFactor = factor;
|
||||
this.currentPolygonOffsetUnits = units;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this.disable( gl.POLYGON_OFFSET_FILL );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
useProgram( program ) {
|
||||
|
||||
if ( this.currentProgram !== program ) {
|
||||
|
||||
this.gl.useProgram( program );
|
||||
|
||||
this.currentProgram = program;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// framebuffer
|
||||
|
||||
|
||||
bindFramebuffer( target, framebuffer ) {
|
||||
|
||||
const { gl, currentBoundFramebuffers } = this;
|
||||
|
||||
if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
|
||||
|
||||
gl.bindFramebuffer( target, framebuffer );
|
||||
|
||||
currentBoundFramebuffers[ target ] = framebuffer;
|
||||
|
||||
// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
|
||||
|
||||
if ( target === gl.DRAW_FRAMEBUFFER ) {
|
||||
|
||||
currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;
|
||||
|
||||
}
|
||||
|
||||
if ( target === gl.FRAMEBUFFER ) {
|
||||
|
||||
currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
drawBuffers( renderContext, framebuffer ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
let drawBuffers = [];
|
||||
|
||||
let needsUpdate = false;
|
||||
|
||||
if ( renderContext.textures !== null ) {
|
||||
|
||||
drawBuffers = this.currentDrawbuffers.get( framebuffer );
|
||||
|
||||
if ( drawBuffers === undefined ) {
|
||||
|
||||
drawBuffers = [];
|
||||
this.currentDrawbuffers.set( framebuffer, drawBuffers );
|
||||
|
||||
}
|
||||
|
||||
|
||||
const textures = renderContext.textures;
|
||||
|
||||
if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {
|
||||
|
||||
for ( let i = 0, il = textures.length; i < il; i ++ ) {
|
||||
|
||||
drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;
|
||||
|
||||
}
|
||||
|
||||
drawBuffers.length = textures.length;
|
||||
|
||||
needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
if ( drawBuffers[ 0 ] !== gl.BACK ) {
|
||||
|
||||
drawBuffers[ 0 ] = gl.BACK;
|
||||
|
||||
needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( needsUpdate ) {
|
||||
|
||||
gl.drawBuffers( drawBuffers );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// texture
|
||||
|
||||
activeTexture( webglSlot ) {
|
||||
|
||||
const { gl, currentTextureSlot, maxTextures } = this;
|
||||
|
||||
if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
|
||||
|
||||
if ( currentTextureSlot !== webglSlot ) {
|
||||
|
||||
gl.activeTexture( webglSlot );
|
||||
this.currentTextureSlot = webglSlot;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bindTexture( webglType, webglTexture, webglSlot ) {
|
||||
|
||||
const { gl, currentTextureSlot, currentBoundTextures, maxTextures } = this;
|
||||
|
||||
if ( webglSlot === undefined ) {
|
||||
|
||||
if ( currentTextureSlot === null ) {
|
||||
|
||||
webglSlot = gl.TEXTURE0 + maxTextures - 1;
|
||||
|
||||
} else {
|
||||
|
||||
webglSlot = currentTextureSlot;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let boundTexture = currentBoundTextures[ webglSlot ];
|
||||
|
||||
if ( boundTexture === undefined ) {
|
||||
|
||||
boundTexture = { type: undefined, texture: undefined };
|
||||
currentBoundTextures[ webglSlot ] = boundTexture;
|
||||
|
||||
}
|
||||
|
||||
if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
|
||||
|
||||
if ( currentTextureSlot !== webglSlot ) {
|
||||
|
||||
gl.activeTexture( webglSlot );
|
||||
this.currentTextureSlot = webglSlot;
|
||||
|
||||
}
|
||||
|
||||
gl.bindTexture( webglType, webglTexture );
|
||||
|
||||
boundTexture.type = webglType;
|
||||
boundTexture.texture = webglTexture;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
unbindTexture() {
|
||||
|
||||
const { gl, currentTextureSlot, currentBoundTextures } = this;
|
||||
|
||||
const boundTexture = currentBoundTextures[ currentTextureSlot ];
|
||||
|
||||
if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
|
||||
|
||||
gl.bindTexture( boundTexture.type, null );
|
||||
|
||||
boundTexture.type = undefined;
|
||||
boundTexture.texture = undefined;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLState;
|
751
static/sdk/three/jsm/renderers/webgl/utils/WebGLTextureUtils.js
Normal file
751
static/sdk/three/jsm/renderers/webgl/utils/WebGLTextureUtils.js
Normal file
@ -0,0 +1,751 @@
|
||||
import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, FloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, SRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare } from 'three';
|
||||
|
||||
let initialized = false, wrappingToGL, filterToGL, compareToGL;
|
||||
|
||||
class WebGLTextureUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
this.gl = backend.gl;
|
||||
this.extensions = backend.extensions;
|
||||
this.defaultTextures = {};
|
||||
|
||||
if ( initialized === false ) {
|
||||
|
||||
this._init( this.gl );
|
||||
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_init( gl ) {
|
||||
|
||||
// Store only WebGL constants here.
|
||||
|
||||
wrappingToGL = {
|
||||
[ RepeatWrapping ]: gl.REPEAT,
|
||||
[ ClampToEdgeWrapping ]: gl.CLAMP_TO_EDGE,
|
||||
[ MirroredRepeatWrapping ]: gl.MIRRORED_REPEAT
|
||||
};
|
||||
|
||||
filterToGL = {
|
||||
[ NearestFilter ]: gl.NEAREST,
|
||||
[ NearestMipmapNearestFilter ]: gl.NEAREST_MIPMAP_NEAREST,
|
||||
[ NearestMipmapLinearFilter ]: gl.NEAREST_MIPMAP_LINEAR,
|
||||
|
||||
[ LinearFilter ]: gl.LINEAR,
|
||||
[ LinearMipmapNearestFilter ]: gl.LINEAR_MIPMAP_NEAREST,
|
||||
[ LinearMipmapLinearFilter ]: gl.LINEAR_MIPMAP_LINEAR
|
||||
};
|
||||
|
||||
compareToGL = {
|
||||
[ NeverCompare ]: gl.NEVER,
|
||||
[ AlwaysCompare ]: gl.ALWAYS,
|
||||
[ LessCompare ]: gl.LESS,
|
||||
[ LessEqualCompare ]: gl.LEQUAL,
|
||||
[ EqualCompare ]: gl.EQUAL,
|
||||
[ GreaterEqualCompare ]: gl.GEQUAL,
|
||||
[ GreaterCompare ]: gl.GREATER,
|
||||
[ NotEqualCompare ]: gl.NOTEQUAL
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
filterFallback( f ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) {
|
||||
|
||||
return gl.NEAREST;
|
||||
|
||||
}
|
||||
|
||||
return gl.LINEAR;
|
||||
|
||||
}
|
||||
|
||||
getGLTextureType( texture ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
let glTextureType;
|
||||
|
||||
if ( texture.isCubeTexture === true ) {
|
||||
|
||||
glTextureType = gl.TEXTURE_CUBE_MAP;
|
||||
|
||||
} else if ( texture.isDataArrayTexture === true ) {
|
||||
|
||||
glTextureType = gl.TEXTURE_2D_ARRAY;
|
||||
|
||||
} else {
|
||||
|
||||
glTextureType = gl.TEXTURE_2D;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return glTextureType;
|
||||
|
||||
}
|
||||
|
||||
getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) {
|
||||
|
||||
const { gl, extensions } = this;
|
||||
|
||||
if ( internalFormatName !== null ) {
|
||||
|
||||
if ( gl[ internalFormatName ] !== undefined ) return gl[ internalFormatName ];
|
||||
|
||||
console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' );
|
||||
|
||||
}
|
||||
|
||||
let internalFormat = glFormat;
|
||||
|
||||
if ( glFormat === gl.RED ) {
|
||||
|
||||
if ( glType === gl.FLOAT ) internalFormat = gl.R32F;
|
||||
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.R16F;
|
||||
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.RED_INTEGER ) {
|
||||
|
||||
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8UI;
|
||||
if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.R16UI;
|
||||
if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.R32UI;
|
||||
if ( glType === gl.BYTE ) internalFormat = gl.R8I;
|
||||
if ( glType === gl.SHORT ) internalFormat = gl.R16I;
|
||||
if ( glType === gl.INT ) internalFormat = gl.R32I;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.RG ) {
|
||||
|
||||
if ( glType === gl.FLOAT ) internalFormat = gl.RG32F;
|
||||
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RG16F;
|
||||
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RG8;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.RGB ) {
|
||||
|
||||
if ( glType === gl.FLOAT ) internalFormat = gl.RGB32F;
|
||||
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGB16F;
|
||||
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGB8;
|
||||
if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) internalFormat = gl.RGB565;
|
||||
if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1;
|
||||
if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGB4;
|
||||
if ( glType === gl.UNSIGNED_INT_5_9_9_9_REV ) internalFormat = gl.RGB9_E5;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.RGBA ) {
|
||||
|
||||
if ( glType === gl.FLOAT ) internalFormat = gl.RGBA32F;
|
||||
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGBA16F;
|
||||
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? gl.SRGB8_ALPHA8 : gl.RGBA8;
|
||||
if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGBA4;
|
||||
if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.DEPTH_COMPONENT ) {
|
||||
|
||||
if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.DEPTH24_STENCIL8;
|
||||
if ( glType === gl.FLOAT ) internalFormat = gl.DEPTH_COMPONENT32F;
|
||||
|
||||
}
|
||||
|
||||
if ( glFormat === gl.DEPTH_STENCIL ) {
|
||||
|
||||
if ( glType === gl.UNSIGNED_INT_24_8 ) internalFormat = gl.DEPTH24_STENCIL8;
|
||||
|
||||
}
|
||||
|
||||
if ( internalFormat === gl.R16F || internalFormat === gl.R32F ||
|
||||
internalFormat === gl.RG16F || internalFormat === gl.RG32F ||
|
||||
internalFormat === gl.RGBA16F || internalFormat === gl.RGBA32F ) {
|
||||
|
||||
extensions.get( 'EXT_color_buffer_float' );
|
||||
|
||||
}
|
||||
|
||||
return internalFormat;
|
||||
|
||||
}
|
||||
|
||||
setTextureParameters( textureType, texture ) {
|
||||
|
||||
const { gl, extensions, backend } = this;
|
||||
|
||||
const { currentAnisotropy } = backend.get( texture );
|
||||
|
||||
gl.texParameteri( textureType, gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );
|
||||
gl.texParameteri( textureType, gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );
|
||||
|
||||
if ( textureType === gl.TEXTURE_3D || textureType === gl.TEXTURE_2D_ARRAY ) {
|
||||
|
||||
gl.texParameteri( textureType, gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );
|
||||
|
||||
}
|
||||
|
||||
gl.texParameteri( textureType, gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );
|
||||
|
||||
|
||||
// follow WebGPU backend mapping for texture filtering
|
||||
const minFilter = ! texture.isVideoTexture && texture.minFilter === LinearFilter ? LinearMipmapLinearFilter : texture.minFilter;
|
||||
|
||||
gl.texParameteri( textureType, gl.TEXTURE_MIN_FILTER, filterToGL[ minFilter ] );
|
||||
|
||||
if ( texture.compareFunction ) {
|
||||
|
||||
gl.texParameteri( textureType, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE );
|
||||
gl.texParameteri( textureType, gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );
|
||||
|
||||
}
|
||||
|
||||
if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
|
||||
|
||||
if ( texture.magFilter === NearestFilter ) return;
|
||||
if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;
|
||||
if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2
|
||||
|
||||
if ( texture.anisotropy > 1 || currentAnisotropy !== texture.anisotropy ) {
|
||||
|
||||
const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
|
||||
gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, backend.getMaxAnisotropy() ) );
|
||||
backend.get( texture ).currentAnisotropy = texture.anisotropy;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createDefaultTexture( texture ) {
|
||||
|
||||
const { gl, backend, defaultTextures } = this;
|
||||
|
||||
|
||||
const glTextureType = this.getGLTextureType( texture );
|
||||
|
||||
let textureGPU = defaultTextures[ glTextureType ];
|
||||
|
||||
if ( textureGPU === undefined ) {
|
||||
|
||||
textureGPU = gl.createTexture();
|
||||
|
||||
backend.state.bindTexture( glTextureType, textureGPU );
|
||||
gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
||||
gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
||||
|
||||
// gl.texImage2D( glTextureType, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
|
||||
|
||||
defaultTextures[ glTextureType ] = textureGPU;
|
||||
|
||||
}
|
||||
|
||||
backend.set( texture, {
|
||||
textureGPU,
|
||||
glTextureType,
|
||||
isDefault: true
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
createTexture( texture, options ) {
|
||||
|
||||
const { gl, backend } = this;
|
||||
const { levels, width, height, depth } = options;
|
||||
|
||||
const glFormat = backend.utils.convert( texture.format, texture.colorSpace );
|
||||
const glType = backend.utils.convert( texture.type );
|
||||
const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
|
||||
|
||||
const textureGPU = gl.createTexture();
|
||||
const glTextureType = this.getGLTextureType( texture );
|
||||
|
||||
backend.state.bindTexture( glTextureType, textureGPU );
|
||||
|
||||
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 );
|
||||
gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
|
||||
|
||||
this.setTextureParameters( glTextureType, texture );
|
||||
|
||||
if ( texture.isDataArrayTexture ) {
|
||||
|
||||
gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
|
||||
|
||||
} else if ( ! texture.isVideoTexture ) {
|
||||
|
||||
gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
|
||||
|
||||
}
|
||||
|
||||
backend.set( texture, {
|
||||
textureGPU,
|
||||
glTextureType,
|
||||
glFormat,
|
||||
glType,
|
||||
glInternalFormat
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
copyBufferToTexture( buffer, texture ) {
|
||||
|
||||
const { gl, backend } = this;
|
||||
|
||||
const { textureGPU, glTextureType, glFormat, glType } = backend.get( texture );
|
||||
|
||||
const { width, height } = texture.source.data;
|
||||
|
||||
gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, buffer );
|
||||
|
||||
backend.state.bindTexture( glTextureType, textureGPU );
|
||||
|
||||
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );
|
||||
gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false );
|
||||
gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, 0 );
|
||||
|
||||
gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, null );
|
||||
|
||||
backend.state.unbindTexture();
|
||||
// debug
|
||||
// const framebuffer = gl.createFramebuffer();
|
||||
// gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );
|
||||
// gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glTextureType, textureGPU, 0 );
|
||||
|
||||
// const readout = new Float32Array( width * height * 4 );
|
||||
|
||||
// const altFormat = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_FORMAT );
|
||||
// const altType = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_TYPE );
|
||||
|
||||
// gl.readPixels( 0, 0, width, height, altFormat, altType, readout );
|
||||
// gl.bindFramebuffer( gl.FRAMEBUFFER, null );
|
||||
// console.log( readout );
|
||||
|
||||
}
|
||||
|
||||
updateTexture( texture, options ) {
|
||||
|
||||
const { gl } = this;
|
||||
const { width, height } = options;
|
||||
const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.backend.get( texture );
|
||||
|
||||
if ( texture.isRenderTargetTexture || ( textureGPU === undefined /* unsupported texture format */ ) )
|
||||
return;
|
||||
|
||||
const getImage = ( source ) => {
|
||||
|
||||
if ( source.isDataTexture ) {
|
||||
|
||||
return source.image.data;
|
||||
|
||||
} else if ( source instanceof ImageBitmap || source instanceof OffscreenCanvas || source instanceof HTMLImageElement || source instanceof HTMLCanvasElement ) {
|
||||
|
||||
return source;
|
||||
|
||||
}
|
||||
|
||||
return source.data;
|
||||
|
||||
};
|
||||
|
||||
this.backend.state.bindTexture( glTextureType, textureGPU );
|
||||
|
||||
if ( texture.isCompressedTexture ) {
|
||||
|
||||
const mipmaps = texture.mipmaps;
|
||||
|
||||
for ( let i = 0; i < mipmaps.length; i ++ ) {
|
||||
|
||||
const mipmap = mipmaps[ i ];
|
||||
|
||||
if ( texture.isCompressedArrayTexture ) {
|
||||
|
||||
const image = options.image;
|
||||
|
||||
if ( texture.format !== gl.RGBA ) {
|
||||
|
||||
if ( glFormat !== null ) {
|
||||
|
||||
gl.compressedTexSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( glFormat !== null ) {
|
||||
|
||||
gl.compressedTexSubImage2D( gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'Unsupported compressed texture format' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( texture.isCubeTexture ) {
|
||||
|
||||
const images = options.images;
|
||||
|
||||
for ( let i = 0; i < 6; i ++ ) {
|
||||
|
||||
const image = getImage( images[ i ] );
|
||||
|
||||
gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
|
||||
|
||||
}
|
||||
|
||||
} else if ( texture.isDataArrayTexture ) {
|
||||
|
||||
const image = options.image;
|
||||
|
||||
gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
|
||||
|
||||
} else if ( texture.isVideoTexture ) {
|
||||
|
||||
texture.update();
|
||||
|
||||
gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
const image = getImage( options.image );
|
||||
|
||||
gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateMipmaps( texture ) {
|
||||
|
||||
const { gl, backend } = this;
|
||||
const { textureGPU, glTextureType } = backend.get( texture );
|
||||
|
||||
backend.state.bindTexture( glTextureType, textureGPU );
|
||||
gl.generateMipmap( glTextureType );
|
||||
|
||||
}
|
||||
|
||||
deallocateRenderBuffers( renderTarget ) {
|
||||
|
||||
|
||||
const { gl, backend } = this;
|
||||
|
||||
// remove framebuffer reference
|
||||
if ( renderTarget ) {
|
||||
|
||||
const renderContextData = backend.get( renderTarget );
|
||||
|
||||
renderContextData.renderBufferStorageSetup = undefined;
|
||||
|
||||
if ( renderContextData.framebuffer ) {
|
||||
|
||||
gl.deleteFramebuffer( renderContextData.framebuffer );
|
||||
renderContextData.framebuffer = undefined;
|
||||
|
||||
}
|
||||
|
||||
if ( renderContextData.depthRenderbuffer ) {
|
||||
|
||||
gl.deleteRenderbuffer( renderContextData.depthRenderbuffer );
|
||||
renderContextData.depthRenderbuffer = undefined;
|
||||
|
||||
}
|
||||
|
||||
if ( renderContextData.stencilRenderbuffer ) {
|
||||
|
||||
gl.deleteRenderbuffer( renderContextData.stencilRenderbuffer );
|
||||
renderContextData.stencilRenderbuffer = undefined;
|
||||
|
||||
}
|
||||
|
||||
if ( renderContextData.msaaFrameBuffer ) {
|
||||
|
||||
gl.deleteFramebuffer( renderContextData.msaaFrameBuffer );
|
||||
renderContextData.msaaFrameBuffer = undefined;
|
||||
|
||||
}
|
||||
|
||||
if ( renderContextData.msaaRenderbuffers ) {
|
||||
|
||||
for ( let i = 0; i < renderContextData.msaaRenderbuffers.length; i ++ ) {
|
||||
|
||||
gl.deleteRenderbuffer( renderContextData.msaaRenderbuffers[ i ] );
|
||||
|
||||
}
|
||||
|
||||
renderContextData.msaaRenderbuffers = undefined;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
destroyTexture( texture ) {
|
||||
|
||||
const { gl, backend } = this;
|
||||
const { textureGPU, renderTarget } = backend.get( texture );
|
||||
|
||||
this.deallocateRenderBuffers( renderTarget );
|
||||
gl.deleteTexture( textureGPU );
|
||||
|
||||
backend.delete( texture );
|
||||
|
||||
}
|
||||
|
||||
copyTextureToTexture( position, srcTexture, dstTexture, level = 0 ) {
|
||||
|
||||
const { gl, backend } = this;
|
||||
const { state } = this.backend;
|
||||
|
||||
const width = srcTexture.image.width;
|
||||
const height = srcTexture.image.height;
|
||||
const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture );
|
||||
|
||||
state.bindTexture( glTextureType, dstTextureGPU );
|
||||
|
||||
// As another texture upload may have changed pixelStorei
|
||||
// parameters, make sure they are correct for the dstTexture
|
||||
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
|
||||
gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
|
||||
gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
|
||||
|
||||
if ( srcTexture.isDataTexture ) {
|
||||
|
||||
gl.texSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );
|
||||
|
||||
} else {
|
||||
|
||||
if ( srcTexture.isCompressedTexture ) {
|
||||
|
||||
gl.compressedTexSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data );
|
||||
|
||||
} else {
|
||||
|
||||
gl.texSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Generate mipmaps only when copying level 0
|
||||
if ( level === 0 && dstTexture.generateMipmaps ) gl.generateMipmap( gl.TEXTURE_2D );
|
||||
|
||||
state.unbindTexture();
|
||||
|
||||
}
|
||||
|
||||
copyFramebufferToTexture( texture, renderContext ) {
|
||||
|
||||
const { gl } = this;
|
||||
const { state } = this.backend;
|
||||
|
||||
const { textureGPU } = this.backend.get( texture );
|
||||
|
||||
const width = texture.image.width;
|
||||
const height = texture.image.height;
|
||||
|
||||
const requireDrawFrameBuffer = texture.isDepthTexture === true || ( renderContext.renderTarget && renderContext.renderTarget.samples > 0 );
|
||||
|
||||
if ( requireDrawFrameBuffer ) {
|
||||
|
||||
let mask;
|
||||
let attachment;
|
||||
|
||||
if ( texture.isDepthTexture === true ) {
|
||||
|
||||
mask = gl.DEPTH_BUFFER_BIT;
|
||||
attachment = gl.DEPTH_ATTACHMENT;
|
||||
|
||||
if ( renderContext.stencil ) {
|
||||
|
||||
mask |= gl.STENCIL_BUFFER_BIT;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
mask = gl.COLOR_BUFFER_BIT;
|
||||
attachment = gl.COLOR_ATTACHMENT0;
|
||||
|
||||
}
|
||||
|
||||
const fb = gl.createFramebuffer();
|
||||
state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
|
||||
|
||||
gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureGPU, 0 );
|
||||
|
||||
gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST );
|
||||
|
||||
gl.deleteFramebuffer( fb );
|
||||
|
||||
} else {
|
||||
|
||||
state.bindTexture( gl.TEXTURE_2D, textureGPU );
|
||||
gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height );
|
||||
|
||||
state.unbindTexture();
|
||||
|
||||
}
|
||||
|
||||
if ( texture.generateMipmaps ) this.generateMipmaps( texture );
|
||||
|
||||
this.backend._setFramebuffer( renderContext );
|
||||
|
||||
}
|
||||
|
||||
// Setup storage for internal depth/stencil buffers and bind to correct framebuffer
|
||||
setupRenderBufferStorage( renderbuffer, renderContext ) {
|
||||
|
||||
const { gl } = this;
|
||||
const renderTarget = renderContext.renderTarget;
|
||||
|
||||
const { samples, depthTexture, depthBuffer, stencilBuffer, width, height } = renderTarget;
|
||||
|
||||
gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );
|
||||
|
||||
if ( depthBuffer && ! stencilBuffer ) {
|
||||
|
||||
let glInternalFormat = gl.DEPTH_COMPONENT24;
|
||||
|
||||
if ( samples > 0 ) {
|
||||
|
||||
if ( depthTexture && depthTexture.isDepthTexture ) {
|
||||
|
||||
if ( depthTexture.type === gl.FLOAT ) {
|
||||
|
||||
glInternalFormat = gl.DEPTH_COMPONENT32F;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height );
|
||||
|
||||
} else {
|
||||
|
||||
gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height );
|
||||
|
||||
}
|
||||
|
||||
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
|
||||
|
||||
} else if ( depthBuffer && stencilBuffer ) {
|
||||
|
||||
if ( samples > 0 ) {
|
||||
|
||||
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, width, height );
|
||||
|
||||
} else {
|
||||
|
||||
gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height );
|
||||
|
||||
}
|
||||
|
||||
|
||||
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async copyTextureToBuffer( texture, x, y, width, height ) {
|
||||
|
||||
const { backend, gl } = this;
|
||||
|
||||
const { textureGPU, glFormat, glType } = this.backend.get( texture );
|
||||
|
||||
const fb = gl.createFramebuffer();
|
||||
|
||||
gl.bindFramebuffer( gl.READ_FRAMEBUFFER, fb );
|
||||
gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureGPU, 0 );
|
||||
|
||||
const typedArrayType = this._getTypedArrayType( glType );
|
||||
const bytesPerTexel = this._getBytesPerTexel( glFormat );
|
||||
|
||||
const elementCount = width * height;
|
||||
const byteLength = elementCount * bytesPerTexel;
|
||||
|
||||
const buffer = gl.createBuffer();
|
||||
|
||||
gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
|
||||
gl.bufferData( gl.PIXEL_PACK_BUFFER, byteLength, gl.STREAM_READ );
|
||||
gl.readPixels( x, y, width, height, glFormat, glType, 0 );
|
||||
gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
|
||||
|
||||
await backend.utils._clientWaitAsync();
|
||||
|
||||
const dstBuffer = new typedArrayType( byteLength / typedArrayType.BYTES_PER_ELEMENT );
|
||||
|
||||
gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
|
||||
gl.getBufferSubData( gl.PIXEL_PACK_BUFFER, 0, dstBuffer );
|
||||
gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
|
||||
|
||||
gl.deleteFramebuffer( fb );
|
||||
|
||||
return dstBuffer;
|
||||
|
||||
}
|
||||
|
||||
_getTypedArrayType( glType ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( glType === gl.UNSIGNED_BYTE ) return Uint8Array;
|
||||
|
||||
if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) return Uint16Array;
|
||||
if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) return Uint16Array;
|
||||
if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) return Uint16Array;
|
||||
if ( glType === gl.UNSIGNED_SHORT ) return Uint16Array;
|
||||
if ( glType === gl.UNSIGNED_INT ) return Uint32Array;
|
||||
|
||||
if ( glType === gl.FLOAT ) return Float32Array;
|
||||
|
||||
throw new Error( `Unsupported WebGL type: ${glType}` );
|
||||
|
||||
}
|
||||
|
||||
_getBytesPerTexel( glFormat ) {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
if ( glFormat === gl.RGBA ) return 4;
|
||||
if ( glFormat === gl.RGB ) return 3;
|
||||
if ( glFormat === gl.ALPHA ) return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLTextureUtils;
|
268
static/sdk/three/jsm/renderers/webgl/utils/WebGLUtils.js
Normal file
268
static/sdk/three/jsm/renderers/webgl/utils/WebGLUtils.js
Normal file
@ -0,0 +1,268 @@
|
||||
import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedInt5999Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, SRGBColorSpace, NoColorSpace } from 'three';
|
||||
|
||||
class WebGLUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
this.gl = this.backend.gl;
|
||||
this.extensions = backend.extensions;
|
||||
|
||||
}
|
||||
|
||||
convert( p, colorSpace = NoColorSpace ) {
|
||||
|
||||
const { gl, extensions } = this;
|
||||
|
||||
let extension;
|
||||
|
||||
if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;
|
||||
if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;
|
||||
if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;
|
||||
if ( p === UnsignedInt5999Type ) return gl.UNSIGNED_INT_5_9_9_9_REV;
|
||||
|
||||
if ( p === ByteType ) return gl.BYTE;
|
||||
if ( p === ShortType ) return gl.SHORT;
|
||||
if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;
|
||||
if ( p === IntType ) return gl.INT;
|
||||
if ( p === UnsignedIntType ) return gl.UNSIGNED_INT;
|
||||
if ( p === FloatType ) return gl.FLOAT;
|
||||
|
||||
if ( p === HalfFloatType ) {
|
||||
|
||||
return gl.HALF_FLOAT;
|
||||
|
||||
}
|
||||
|
||||
if ( p === AlphaFormat ) return gl.ALPHA;
|
||||
if ( p === RGBFormat ) return gl.RGB;
|
||||
if ( p === RGBAFormat ) return gl.RGBA;
|
||||
if ( p === LuminanceFormat ) return gl.LUMINANCE;
|
||||
if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;
|
||||
if ( p === DepthFormat ) return gl.DEPTH_COMPONENT;
|
||||
if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;
|
||||
|
||||
// WebGL2 formats.
|
||||
|
||||
if ( p === RedFormat ) return gl.RED;
|
||||
if ( p === RedIntegerFormat ) return gl.RED_INTEGER;
|
||||
if ( p === RGFormat ) return gl.RG;
|
||||
if ( p === RGIntegerFormat ) return gl.RG_INTEGER;
|
||||
if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER;
|
||||
|
||||
// S3TC
|
||||
|
||||
if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {
|
||||
|
||||
if ( colorSpace === SRGBColorSpace ) {
|
||||
|
||||
extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
|
||||
if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PVRTC
|
||||
|
||||
if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {
|
||||
|
||||
extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||
if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||
if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||
if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ETC
|
||||
|
||||
if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) {
|
||||
|
||||
extension = extensions.get( 'WEBGL_compressed_texture_etc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;
|
||||
if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ASTC
|
||||
|
||||
if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||
|
||||
p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||
|
||||
p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||
|
||||
p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||
|
||||
p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {
|
||||
|
||||
extension = extensions.get( 'WEBGL_compressed_texture_astc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;
|
||||
if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;
|
||||
if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR;
|
||||
if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR;
|
||||
if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR;
|
||||
if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR;
|
||||
if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR;
|
||||
if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR;
|
||||
if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR;
|
||||
if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR;
|
||||
if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR;
|
||||
if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR;
|
||||
if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR;
|
||||
if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// BPTC
|
||||
|
||||
if ( p === RGBA_BPTC_Format ) {
|
||||
|
||||
extension = extensions.get( 'EXT_texture_compression_bptc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// RGTC
|
||||
|
||||
if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) {
|
||||
|
||||
extension = extensions.get( 'EXT_texture_compression_rgtc' );
|
||||
|
||||
if ( extension !== null ) {
|
||||
|
||||
if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT;
|
||||
if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT;
|
||||
if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT;
|
||||
if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if ( p === UnsignedInt248Type ) {
|
||||
|
||||
return gl.UNSIGNED_INT_24_8;
|
||||
|
||||
}
|
||||
|
||||
// if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats)
|
||||
|
||||
return ( gl[ p ] !== undefined ) ? gl[ p ] : null;
|
||||
|
||||
}
|
||||
|
||||
_clientWaitAsync() {
|
||||
|
||||
const { gl } = this;
|
||||
|
||||
const sync = gl.fenceSync( gl.SYNC_GPU_COMMANDS_COMPLETE, 0 );
|
||||
|
||||
gl.flush();
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
function test() {
|
||||
|
||||
const res = gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 );
|
||||
|
||||
if ( res === gl.WAIT_FAILED ) {
|
||||
|
||||
gl.deleteSync( sync );
|
||||
|
||||
reject();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ( res === gl.TIMEOUT_EXPIRED ) {
|
||||
|
||||
requestAnimationFrame( test );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gl.deleteSync( sync );
|
||||
|
||||
resolve();
|
||||
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGLUtils;
|
1339
static/sdk/three/jsm/renderers/webgpu/WebGPUBackend.js
Normal file
1339
static/sdk/three/jsm/renderers/webgpu/WebGPUBackend.js
Normal file
File diff suppressed because it is too large
Load Diff
53
static/sdk/three/jsm/renderers/webgpu/WebGPURenderer.js
Normal file
53
static/sdk/three/jsm/renderers/webgpu/WebGPURenderer.js
Normal file
@ -0,0 +1,53 @@
|
||||
import WebGPU from '../../capabilities/WebGPU.js';
|
||||
|
||||
import Renderer from '../common/Renderer.js';
|
||||
import WebGLBackend from '../webgl/WebGLBackend.js';
|
||||
import WebGPUBackend from './WebGPUBackend.js';
|
||||
/*
|
||||
const debugHandler = {
|
||||
|
||||
get: function ( target, name ) {
|
||||
|
||||
// Add |update
|
||||
if ( /^(create|destroy)/.test( name ) ) console.log( 'WebGPUBackend.' + name );
|
||||
|
||||
return target[ name ];
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
*/
|
||||
class WebGPURenderer extends Renderer {
|
||||
|
||||
constructor( parameters = {} ) {
|
||||
|
||||
let BackendClass;
|
||||
|
||||
if ( parameters.forceWebGL ) {
|
||||
|
||||
BackendClass = WebGLBackend;
|
||||
|
||||
} else if ( WebGPU.isAvailable() ) {
|
||||
|
||||
BackendClass = WebGPUBackend;
|
||||
|
||||
} else {
|
||||
|
||||
BackendClass = WebGLBackend;
|
||||
|
||||
console.warn( 'THREE.WebGPURenderer: WebGPU is not available, running under WebGL2 backend.' );
|
||||
|
||||
}
|
||||
|
||||
const backend = new BackendClass( parameters );
|
||||
|
||||
//super( new Proxy( backend, debugHandler ) );
|
||||
super( backend, parameters );
|
||||
|
||||
this.isWebGPURenderer = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPURenderer;
|
1095
static/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Normal file
1095
static/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Normal file
File diff suppressed because it is too large
Load Diff
104
static/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
Normal file
104
static/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
Normal file
@ -0,0 +1,104 @@
|
||||
import NodeFunction from '../../../nodes/core/NodeFunction.js';
|
||||
import NodeFunctionInput from '../../../nodes/core/NodeFunctionInput.js';
|
||||
|
||||
const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i;
|
||||
const propertiesRegexp = /[a-z_0-9]+|<(.*?)>+/ig;
|
||||
|
||||
const wgslTypeLib = {
|
||||
f32: 'float'
|
||||
};
|
||||
|
||||
const parse = ( source ) => {
|
||||
|
||||
source = source.trim();
|
||||
|
||||
const declaration = source.match( declarationRegexp );
|
||||
|
||||
if ( declaration !== null && declaration.length === 4 ) {
|
||||
|
||||
// tokenizer
|
||||
|
||||
const inputsCode = declaration[ 2 ];
|
||||
const propsMatches = [];
|
||||
|
||||
let nameMatch = null;
|
||||
|
||||
while ( ( nameMatch = propertiesRegexp.exec( inputsCode ) ) !== null ) {
|
||||
|
||||
propsMatches.push( nameMatch );
|
||||
|
||||
}
|
||||
|
||||
// parser
|
||||
|
||||
const inputs = [];
|
||||
|
||||
let i = 0;
|
||||
|
||||
while ( i < propsMatches.length ) {
|
||||
|
||||
// default
|
||||
|
||||
const name = propsMatches[ i ++ ][ 0 ];
|
||||
let type = propsMatches[ i ++ ][ 0 ];
|
||||
|
||||
type = wgslTypeLib[ type ] || type;
|
||||
|
||||
// precision
|
||||
|
||||
if ( i < propsMatches.length && propsMatches[ i ][ 0 ].startsWith( '<' ) === true )
|
||||
i ++;
|
||||
|
||||
// add input
|
||||
|
||||
inputs.push( new NodeFunctionInput( type, name ) );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const blockCode = source.substring( declaration[ 0 ].length );
|
||||
|
||||
const name = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
|
||||
const type = declaration[ 3 ] || 'void';
|
||||
|
||||
return {
|
||||
type,
|
||||
inputs,
|
||||
name,
|
||||
inputsCode,
|
||||
blockCode
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'FunctionNode: Function is not a WGSL code.' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WGSLNodeFunction extends NodeFunction {
|
||||
|
||||
constructor( source ) {
|
||||
|
||||
const { type, inputs, name, inputsCode, blockCode } = parse( source );
|
||||
|
||||
super( type, inputs, name );
|
||||
|
||||
this.inputsCode = inputsCode;
|
||||
this.blockCode = blockCode;
|
||||
|
||||
}
|
||||
|
||||
getCode( name = this.name ) {
|
||||
|
||||
const type = this.type !== 'void' ? '-> ' + this.type : '';
|
||||
|
||||
return `fn ${ name } ( ${ this.inputsCode.trim() } ) ${ type }` + this.blockCode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WGSLNodeFunction;
|
@ -0,0 +1,14 @@
|
||||
import NodeParser from '../../../nodes/core/NodeParser.js';
|
||||
import WGSLNodeFunction from './WGSLNodeFunction.js';
|
||||
|
||||
class WGSLNodeParser extends NodeParser {
|
||||
|
||||
parseFunction( source ) {
|
||||
|
||||
return new WGSLNodeFunction( source );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WGSLNodeParser;
|
@ -0,0 +1,321 @@
|
||||
import { Float16BufferAttribute } from 'three';
|
||||
import { GPUInputStepMode } from './WebGPUConstants.js';
|
||||
|
||||
const typedArraysToVertexFormatPrefix = new Map( [
|
||||
[ Int8Array, [ 'sint8', 'snorm8' ]],
|
||||
[ Uint8Array, [ 'uint8', 'unorm8' ]],
|
||||
[ Int16Array, [ 'sint16', 'snorm16' ]],
|
||||
[ Uint16Array, [ 'uint16', 'unorm16' ]],
|
||||
[ Int32Array, [ 'sint32', 'snorm32' ]],
|
||||
[ Uint32Array, [ 'uint32', 'unorm32' ]],
|
||||
[ Float32Array, [ 'float32', ]],
|
||||
] );
|
||||
|
||||
const typedAttributeToVertexFormatPrefix = new Map( [
|
||||
[ Float16BufferAttribute, [ 'float16', ]],
|
||||
] );
|
||||
|
||||
const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
|
||||
[ Int32Array, 'sint32' ],
|
||||
[ Int16Array, 'sint32' ], // patch for INT16
|
||||
[ Uint32Array, 'uint32' ],
|
||||
[ Uint16Array, 'uint32' ], // patch for UINT16
|
||||
[ Float32Array, 'float32' ]
|
||||
] );
|
||||
|
||||
class WebGPUAttributeUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
createAttribute( attribute, usage ) {
|
||||
|
||||
const bufferAttribute = this._getBufferAttribute( attribute );
|
||||
|
||||
const backend = this.backend;
|
||||
const bufferData = backend.get( bufferAttribute );
|
||||
|
||||
let buffer = bufferData.buffer;
|
||||
|
||||
if ( buffer === undefined ) {
|
||||
|
||||
const device = backend.device;
|
||||
|
||||
let array = bufferAttribute.array;
|
||||
|
||||
// patch for INT16 and UINT16
|
||||
if ( attribute.normalized === false && ( array.constructor === Int16Array || array.constructor === Uint16Array ) ) {
|
||||
|
||||
const tempArray = new Uint32Array( array.length );
|
||||
for ( let i = 0; i < array.length; i ++ ) {
|
||||
|
||||
tempArray[ i ] = array[ i ];
|
||||
|
||||
}
|
||||
|
||||
array = tempArray;
|
||||
|
||||
}
|
||||
|
||||
bufferAttribute.array = array;
|
||||
|
||||
if ( ( bufferAttribute.isStorageBufferAttribute || bufferAttribute.isStorageInstancedBufferAttribute ) && bufferAttribute.itemSize === 3 ) {
|
||||
|
||||
array = new array.constructor( bufferAttribute.count * 4 );
|
||||
|
||||
for ( let i = 0; i < bufferAttribute.count; i ++ ) {
|
||||
|
||||
array.set( bufferAttribute.array.subarray( i * 3, i * 3 + 3 ), i * 4 );
|
||||
|
||||
}
|
||||
|
||||
// Update BufferAttribute
|
||||
bufferAttribute.itemSize = 4;
|
||||
bufferAttribute.array = array;
|
||||
|
||||
}
|
||||
|
||||
const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
|
||||
|
||||
buffer = device.createBuffer( {
|
||||
label: bufferAttribute.name,
|
||||
size: size,
|
||||
usage: usage,
|
||||
mappedAtCreation: true
|
||||
} );
|
||||
|
||||
new array.constructor( buffer.getMappedRange() ).set( array );
|
||||
|
||||
buffer.unmap();
|
||||
|
||||
bufferData.buffer = buffer;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateAttribute( attribute ) {
|
||||
|
||||
const bufferAttribute = this._getBufferAttribute( attribute );
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
const buffer = backend.get( bufferAttribute ).buffer;
|
||||
|
||||
const array = bufferAttribute.array;
|
||||
const updateRanges = bufferAttribute.updateRanges;
|
||||
|
||||
if ( updateRanges.length === 0 ) {
|
||||
|
||||
// Not using update ranges
|
||||
|
||||
device.queue.writeBuffer(
|
||||
buffer,
|
||||
0,
|
||||
array,
|
||||
0
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {
|
||||
|
||||
const range = updateRanges[ i ];
|
||||
device.queue.writeBuffer(
|
||||
buffer,
|
||||
0,
|
||||
array,
|
||||
range.start * array.BYTES_PER_ELEMENT,
|
||||
range.count * array.BYTES_PER_ELEMENT
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
bufferAttribute.clearUpdateRanges();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createShaderVertexBuffers( renderObject ) {
|
||||
|
||||
const attributes = renderObject.getAttributes();
|
||||
const vertexBuffers = new Map();
|
||||
|
||||
for ( let slot = 0; slot < attributes.length; slot ++ ) {
|
||||
|
||||
const geometryAttribute = attributes[ slot ];
|
||||
const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
|
||||
const bufferAttribute = this._getBufferAttribute( geometryAttribute );
|
||||
|
||||
let vertexBufferLayout = vertexBuffers.get( bufferAttribute );
|
||||
|
||||
if ( vertexBufferLayout === undefined ) {
|
||||
|
||||
let arrayStride, stepMode;
|
||||
|
||||
if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
|
||||
|
||||
arrayStride = geometryAttribute.data.stride * bytesPerElement;
|
||||
stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
|
||||
|
||||
} else {
|
||||
|
||||
arrayStride = geometryAttribute.itemSize * bytesPerElement;
|
||||
stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
|
||||
|
||||
}
|
||||
|
||||
// patch for INT16 and UINT16
|
||||
if ( geometryAttribute.normalized === false && ( geometryAttribute.array.constructor === Int16Array || geometryAttribute.array.constructor === Uint16Array ) ) {
|
||||
|
||||
arrayStride = 4;
|
||||
|
||||
}
|
||||
|
||||
vertexBufferLayout = {
|
||||
arrayStride,
|
||||
attributes: [],
|
||||
stepMode
|
||||
};
|
||||
|
||||
vertexBuffers.set( bufferAttribute, vertexBufferLayout );
|
||||
|
||||
}
|
||||
|
||||
const format = this._getVertexFormat( geometryAttribute );
|
||||
const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0;
|
||||
|
||||
vertexBufferLayout.attributes.push( {
|
||||
shaderLocation: slot,
|
||||
offset,
|
||||
format
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return Array.from( vertexBuffers.values() );
|
||||
|
||||
}
|
||||
|
||||
destroyAttribute( attribute ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const data = backend.get( this._getBufferAttribute( attribute ) );
|
||||
|
||||
data.buffer.destroy();
|
||||
|
||||
backend.delete( attribute );
|
||||
|
||||
}
|
||||
|
||||
async getArrayBufferAsync( attribute ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
const data = backend.get( this._getBufferAttribute( attribute ) );
|
||||
|
||||
const bufferGPU = data.buffer;
|
||||
const size = bufferGPU.size;
|
||||
|
||||
let readBufferGPU = data.readBuffer;
|
||||
let needsUnmap = true;
|
||||
|
||||
if ( readBufferGPU === undefined ) {
|
||||
|
||||
readBufferGPU = device.createBuffer( {
|
||||
label: attribute.name,
|
||||
size,
|
||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
||||
} );
|
||||
|
||||
needsUnmap = false;
|
||||
|
||||
data.readBuffer = readBufferGPU;
|
||||
|
||||
}
|
||||
|
||||
const cmdEncoder = device.createCommandEncoder( {} );
|
||||
|
||||
cmdEncoder.copyBufferToBuffer(
|
||||
bufferGPU,
|
||||
0,
|
||||
readBufferGPU,
|
||||
0,
|
||||
size
|
||||
);
|
||||
|
||||
if ( needsUnmap ) readBufferGPU.unmap();
|
||||
|
||||
const gpuCommands = cmdEncoder.finish();
|
||||
device.queue.submit( [ gpuCommands ] );
|
||||
|
||||
await readBufferGPU.mapAsync( GPUMapMode.READ );
|
||||
|
||||
const arrayBuffer = readBufferGPU.getMappedRange();
|
||||
|
||||
return arrayBuffer;
|
||||
|
||||
}
|
||||
|
||||
_getVertexFormat( geometryAttribute ) {
|
||||
|
||||
const { itemSize, normalized } = geometryAttribute;
|
||||
const ArrayType = geometryAttribute.array.constructor;
|
||||
const AttributeType = geometryAttribute.constructor;
|
||||
|
||||
let format;
|
||||
|
||||
if ( itemSize == 1 ) {
|
||||
|
||||
format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
|
||||
|
||||
} else {
|
||||
|
||||
const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
|
||||
const prefix = prefixOptions[ normalized ? 1 : 0 ];
|
||||
|
||||
if ( prefix ) {
|
||||
|
||||
const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
|
||||
const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
|
||||
const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
|
||||
|
||||
if ( paddedItemSize % 1 ) {
|
||||
|
||||
throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' );
|
||||
|
||||
}
|
||||
|
||||
format = `${prefix}x${paddedItemSize}`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( ! format ) {
|
||||
|
||||
console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' );
|
||||
|
||||
}
|
||||
|
||||
return format;
|
||||
|
||||
}
|
||||
|
||||
_getBufferAttribute( attribute ) {
|
||||
|
||||
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
|
||||
|
||||
return attribute;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPUAttributeUtils;
|
@ -0,0 +1,258 @@
|
||||
import {
|
||||
GPUTextureAspect, GPUTextureViewDimension, GPUBufferBindingType, GPUTextureSampleType
|
||||
} from './WebGPUConstants.js';
|
||||
import { FloatType, IntType, UnsignedIntType } from 'three';
|
||||
|
||||
class WebGPUBindingUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
createBindingsLayout( bindings ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
const entries = [];
|
||||
|
||||
let index = 0;
|
||||
|
||||
for ( const binding of bindings ) {
|
||||
|
||||
const bindingGPU = {
|
||||
binding: index ++,
|
||||
visibility: binding.visibility
|
||||
};
|
||||
|
||||
if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
|
||||
|
||||
const buffer = {}; // GPUBufferBindingLayout
|
||||
|
||||
if ( binding.isStorageBuffer ) {
|
||||
|
||||
buffer.type = GPUBufferBindingType.Storage;
|
||||
|
||||
}
|
||||
|
||||
bindingGPU.buffer = buffer;
|
||||
|
||||
} else if ( binding.isSampler ) {
|
||||
|
||||
const sampler = {}; // GPUSamplerBindingLayout
|
||||
|
||||
if ( binding.texture.isDepthTexture ) {
|
||||
|
||||
if ( binding.texture.compareFunction !== null ) {
|
||||
|
||||
sampler.type = 'comparison';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bindingGPU.sampler = sampler;
|
||||
|
||||
} else if ( binding.isSampledTexture && binding.texture.isVideoTexture ) {
|
||||
|
||||
bindingGPU.externalTexture = {}; // GPUExternalTextureBindingLayout
|
||||
|
||||
} else if ( binding.isSampledTexture && binding.store ) {
|
||||
|
||||
const format = this.backend.get( binding.texture ).texture.format;
|
||||
|
||||
bindingGPU.storageTexture = { format }; // GPUStorageTextureBindingLayout
|
||||
|
||||
} else if ( binding.isSampledTexture ) {
|
||||
|
||||
const texture = {}; // GPUTextureBindingLayout
|
||||
|
||||
if ( binding.texture.isDepthTexture ) {
|
||||
|
||||
texture.sampleType = GPUTextureSampleType.Depth;
|
||||
|
||||
} else if ( binding.texture.isDataTexture ) {
|
||||
|
||||
const type = binding.texture.type;
|
||||
|
||||
if ( type === IntType ) {
|
||||
|
||||
texture.sampleType = GPUTextureSampleType.SInt;
|
||||
|
||||
} else if ( type === UnsignedIntType ) {
|
||||
|
||||
texture.sampleType = GPUTextureSampleType.UInt;
|
||||
|
||||
} else if ( type === FloatType ) {
|
||||
|
||||
// @TODO: Add support for this soon: backend.hasFeature( 'float32-filterable' )
|
||||
|
||||
texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( binding.isSampledCubeTexture ) {
|
||||
|
||||
texture.viewDimension = GPUTextureViewDimension.Cube;
|
||||
|
||||
} else if ( binding.texture.isDataArrayTexture ) {
|
||||
|
||||
texture.viewDimension = GPUTextureViewDimension.TwoDArray;
|
||||
|
||||
}
|
||||
|
||||
bindingGPU.texture = texture;
|
||||
|
||||
} else {
|
||||
|
||||
console.error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` );
|
||||
|
||||
}
|
||||
|
||||
entries.push( bindingGPU );
|
||||
|
||||
}
|
||||
|
||||
return device.createBindGroupLayout( { entries } );
|
||||
|
||||
}
|
||||
|
||||
createBindings( bindings ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const bindingsData = backend.get( bindings );
|
||||
|
||||
// setup (static) binding layout and (dynamic) binding group
|
||||
|
||||
const bindLayoutGPU = this.createBindingsLayout( bindings );
|
||||
const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU );
|
||||
|
||||
bindingsData.layout = bindLayoutGPU;
|
||||
bindingsData.group = bindGroupGPU;
|
||||
bindingsData.bindings = bindings;
|
||||
|
||||
}
|
||||
|
||||
updateBinding( binding ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
const buffer = binding.buffer;
|
||||
const bufferGPU = backend.get( binding ).buffer;
|
||||
|
||||
device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
|
||||
|
||||
}
|
||||
|
||||
createBindGroup( bindings, layoutGPU ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
let bindingPoint = 0;
|
||||
const entriesGPU = [];
|
||||
|
||||
for ( const binding of bindings ) {
|
||||
|
||||
if ( binding.isUniformBuffer ) {
|
||||
|
||||
const bindingData = backend.get( binding );
|
||||
|
||||
if ( bindingData.buffer === undefined ) {
|
||||
|
||||
const byteLength = binding.byteLength;
|
||||
|
||||
const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
|
||||
|
||||
const bufferGPU = device.createBuffer( {
|
||||
label: 'bindingBuffer_' + binding.name,
|
||||
size: byteLength,
|
||||
usage: usage
|
||||
} );
|
||||
|
||||
bindingData.buffer = bufferGPU;
|
||||
|
||||
}
|
||||
|
||||
entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
|
||||
|
||||
} else if ( binding.isStorageBuffer ) {
|
||||
|
||||
const bindingData = backend.get( binding );
|
||||
|
||||
if ( bindingData.buffer === undefined ) {
|
||||
|
||||
const attribute = binding.attribute;
|
||||
//const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST;
|
||||
|
||||
//backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer
|
||||
|
||||
bindingData.buffer = backend.get( attribute ).buffer;
|
||||
|
||||
}
|
||||
|
||||
entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
|
||||
|
||||
} else if ( binding.isSampler ) {
|
||||
|
||||
const textureGPU = backend.get( binding.texture );
|
||||
|
||||
entriesGPU.push( { binding: bindingPoint, resource: textureGPU.sampler } );
|
||||
|
||||
} else if ( binding.isSampledTexture ) {
|
||||
|
||||
const textureData = backend.get( binding.texture );
|
||||
|
||||
let dimensionViewGPU;
|
||||
|
||||
if ( binding.isSampledCubeTexture ) {
|
||||
|
||||
dimensionViewGPU = GPUTextureViewDimension.Cube;
|
||||
|
||||
} else if ( binding.texture.isDataArrayTexture ) {
|
||||
|
||||
dimensionViewGPU = GPUTextureViewDimension.TwoDArray;
|
||||
|
||||
} else {
|
||||
|
||||
dimensionViewGPU = GPUTextureViewDimension.TwoD;
|
||||
|
||||
}
|
||||
|
||||
let resourceGPU;
|
||||
|
||||
if ( textureData.externalTexture !== undefined ) {
|
||||
|
||||
resourceGPU = device.importExternalTexture( { source: textureData.externalTexture } );
|
||||
|
||||
} else {
|
||||
|
||||
const aspectGPU = GPUTextureAspect.All;
|
||||
|
||||
resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU, mipLevelCount: binding.store ? 1 : textureData.mipLevelCount } );
|
||||
|
||||
}
|
||||
|
||||
entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } );
|
||||
|
||||
}
|
||||
|
||||
bindingPoint ++;
|
||||
|
||||
}
|
||||
|
||||
return device.createBindGroup( {
|
||||
layout: layoutGPU,
|
||||
entries: entriesGPU
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPUBindingUtils;
|
324
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUConstants.js
Normal file
324
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUConstants.js
Normal file
@ -0,0 +1,324 @@
|
||||
export const GPUPrimitiveTopology = {
|
||||
PointList: 'point-list',
|
||||
LineList: 'line-list',
|
||||
LineStrip: 'line-strip',
|
||||
TriangleList: 'triangle-list',
|
||||
TriangleStrip: 'triangle-strip',
|
||||
};
|
||||
|
||||
export const GPUCompareFunction = {
|
||||
Never: 'never',
|
||||
Less: 'less',
|
||||
Equal: 'equal',
|
||||
LessEqual: 'less-equal',
|
||||
Greater: 'greater',
|
||||
NotEqual: 'not-equal',
|
||||
GreaterEqual: 'greater-equal',
|
||||
Always: 'always'
|
||||
};
|
||||
|
||||
export const GPUStoreOp = {
|
||||
Store: 'store',
|
||||
Discard: 'discard'
|
||||
};
|
||||
|
||||
export const GPULoadOp = {
|
||||
Load: 'load',
|
||||
Clear: 'clear'
|
||||
};
|
||||
|
||||
export const GPUFrontFace = {
|
||||
CCW: 'ccw',
|
||||
CW: 'cw'
|
||||
};
|
||||
|
||||
export const GPUCullMode = {
|
||||
None: 'none',
|
||||
Front: 'front',
|
||||
Back: 'back'
|
||||
};
|
||||
|
||||
export const GPUIndexFormat = {
|
||||
Uint16: 'uint16',
|
||||
Uint32: 'uint32'
|
||||
};
|
||||
|
||||
export const GPUVertexFormat = {
|
||||
Uint8x2: 'uint8x2',
|
||||
Uint8x4: 'uint8x4',
|
||||
Sint8x2: 'sint8x2',
|
||||
Sint8x4: 'sint8x4',
|
||||
Unorm8x2: 'unorm8x2',
|
||||
Unorm8x4: 'unorm8x4',
|
||||
Snorm8x2: 'snorm8x2',
|
||||
Snorm8x4: 'snorm8x4',
|
||||
Uint16x2: 'uint16x2',
|
||||
Uint16x4: 'uint16x4',
|
||||
Sint16x2: 'sint16x2',
|
||||
Sint16x4: 'sint16x4',
|
||||
Unorm16x2: 'unorm16x2',
|
||||
Unorm16x4: 'unorm16x4',
|
||||
Snorm16x2: 'snorm16x2',
|
||||
Snorm16x4: 'snorm16x4',
|
||||
Float16x2: 'float16x2',
|
||||
Float16x4: 'float16x4',
|
||||
Float32: 'float32',
|
||||
Float32x2: 'float32x2',
|
||||
Float32x3: 'float32x3',
|
||||
Float32x4: 'float32x4',
|
||||
Uint32: 'uint32',
|
||||
Uint32x2: 'uint32x2',
|
||||
Uint32x3: 'uint32x3',
|
||||
Uint32x4: 'uint32x4',
|
||||
Sint32: 'sint32',
|
||||
Sint32x2: 'sint32x2',
|
||||
Sint32x3: 'sint32x3',
|
||||
Sint32x4: 'sint32x4'
|
||||
};
|
||||
|
||||
export const GPUTextureFormat = {
|
||||
|
||||
// 8-bit formats
|
||||
|
||||
R8Unorm: 'r8unorm',
|
||||
R8Snorm: 'r8snorm',
|
||||
R8Uint: 'r8uint',
|
||||
R8Sint: 'r8sint',
|
||||
|
||||
// 16-bit formats
|
||||
|
||||
R16Uint: 'r16uint',
|
||||
R16Sint: 'r16sint',
|
||||
R16Float: 'r16float',
|
||||
RG8Unorm: 'rg8unorm',
|
||||
RG8Snorm: 'rg8snorm',
|
||||
RG8Uint: 'rg8uint',
|
||||
RG8Sint: 'rg8sint',
|
||||
|
||||
// 32-bit formats
|
||||
|
||||
R32Uint: 'r32uint',
|
||||
R32Sint: 'r32sint',
|
||||
R32Float: 'r32float',
|
||||
RG16Uint: 'rg16uint',
|
||||
RG16Sint: 'rg16sint',
|
||||
RG16Float: 'rg16float',
|
||||
RGBA8Unorm: 'rgba8unorm',
|
||||
RGBA8UnormSRGB: 'rgba8unorm-srgb',
|
||||
RGBA8Snorm: 'rgba8snorm',
|
||||
RGBA8Uint: 'rgba8uint',
|
||||
RGBA8Sint: 'rgba8sint',
|
||||
BGRA8Unorm: 'bgra8unorm',
|
||||
BGRA8UnormSRGB: 'bgra8unorm-srgb',
|
||||
// Packed 32-bit formats
|
||||
RGB9E5UFloat: 'rgb9e5ufloat',
|
||||
RGB10A2Unorm: 'rgb10a2unorm',
|
||||
RG11B10uFloat: 'rgb10a2unorm',
|
||||
|
||||
// 64-bit formats
|
||||
|
||||
RG32Uint: 'rg32uint',
|
||||
RG32Sint: 'rg32sint',
|
||||
RG32Float: 'rg32float',
|
||||
RGBA16Uint: 'rgba16uint',
|
||||
RGBA16Sint: 'rgba16sint',
|
||||
RGBA16Float: 'rgba16float',
|
||||
|
||||
// 128-bit formats
|
||||
|
||||
RGBA32Uint: 'rgba32uint',
|
||||
RGBA32Sint: 'rgba32sint',
|
||||
RGBA32Float: 'rgba32float',
|
||||
|
||||
// Depth and stencil formats
|
||||
|
||||
Stencil8: 'stencil8',
|
||||
Depth16Unorm: 'depth16unorm',
|
||||
Depth24Plus: 'depth24plus',
|
||||
Depth24PlusStencil8: 'depth24plus-stencil8',
|
||||
Depth32Float: 'depth32float',
|
||||
|
||||
// 'depth32float-stencil8' extension
|
||||
|
||||
Depth32FloatStencil8: 'depth32float-stencil8',
|
||||
|
||||
// BC compressed formats usable if 'texture-compression-bc' is both
|
||||
// supported by the device/user agent and enabled in requestDevice.
|
||||
|
||||
BC1RGBAUnorm: 'bc1-rgba-unorm',
|
||||
BC1RGBAUnormSRGB: 'bc1-rgba-unorm-srgb',
|
||||
BC2RGBAUnorm: 'bc2-rgba-unorm',
|
||||
BC2RGBAUnormSRGB: 'bc2-rgba-unorm-srgb',
|
||||
BC3RGBAUnorm: 'bc3-rgba-unorm',
|
||||
BC3RGBAUnormSRGB: 'bc3-rgba-unorm-srgb',
|
||||
BC4RUnorm: 'bc4-r-unorm',
|
||||
BC4RSnorm: 'bc4-r-snorm',
|
||||
BC5RGUnorm: 'bc5-rg-unorm',
|
||||
BC5RGSnorm: 'bc5-rg-snorm',
|
||||
BC6HRGBUFloat: 'bc6h-rgb-ufloat',
|
||||
BC6HRGBFloat: 'bc6h-rgb-float',
|
||||
BC7RGBAUnorm: 'bc7-rgba-unorm',
|
||||
BC7RGBAUnormSRGB: 'bc7-rgba-srgb',
|
||||
|
||||
// ETC2 compressed formats usable if 'texture-compression-etc2' is both
|
||||
// supported by the device/user agent and enabled in requestDevice.
|
||||
|
||||
ETC2RGB8Unorm: 'etc2-rgb8unorm',
|
||||
ETC2RGB8UnormSRGB: 'etc2-rgb8unorm-srgb',
|
||||
ETC2RGB8A1Unorm: 'etc2-rgb8a1unorm',
|
||||
ETC2RGB8A1UnormSRGB: 'etc2-rgb8a1unorm-srgb',
|
||||
ETC2RGBA8Unorm: 'etc2-rgba8unorm',
|
||||
ETC2RGBA8UnormSRGB: 'etc2-rgba8unorm-srgb',
|
||||
EACR11Unorm: 'eac-r11unorm',
|
||||
EACR11Snorm: 'eac-r11snorm',
|
||||
EACRG11Unorm: 'eac-rg11unorm',
|
||||
EACRG11Snorm: 'eac-rg11snorm',
|
||||
|
||||
// ASTC compressed formats usable if 'texture-compression-astc' is both
|
||||
// supported by the device/user agent and enabled in requestDevice.
|
||||
|
||||
ASTC4x4Unorm: 'astc-4x4-unorm',
|
||||
ASTC4x4UnormSRGB: 'astc-4x4-unorm-srgb',
|
||||
ASTC5x4Unorm: 'astc-5x4-unorm',
|
||||
ASTC5x4UnormSRGB: 'astc-5x4-unorm-srgb',
|
||||
ASTC5x5Unorm: 'astc-5x5-unorm',
|
||||
ASTC5x5UnormSRGB: 'astc-5x5-unorm-srgb',
|
||||
ASTC6x5Unorm: 'astc-6x5-unorm',
|
||||
ASTC6x5UnormSRGB: 'astc-6x5-unorm-srgb',
|
||||
ASTC6x6Unorm: 'astc-6x6-unorm',
|
||||
ASTC6x6UnormSRGB: 'astc-6x6-unorm-srgb',
|
||||
ASTC8x5Unorm: 'astc-8x5-unorm',
|
||||
ASTC8x5UnormSRGB: 'astc-8x5-unorm-srgb',
|
||||
ASTC8x6Unorm: 'astc-8x6-unorm',
|
||||
ASTC8x6UnormSRGB: 'astc-8x6-unorm-srgb',
|
||||
ASTC8x8Unorm: 'astc-8x8-unorm',
|
||||
ASTC8x8UnormSRGB: 'astc-8x8-unorm-srgb',
|
||||
ASTC10x5Unorm: 'astc-10x5-unorm',
|
||||
ASTC10x5UnormSRGB: 'astc-10x5-unorm-srgb',
|
||||
ASTC10x6Unorm: 'astc-10x6-unorm',
|
||||
ASTC10x6UnormSRGB: 'astc-10x6-unorm-srgb',
|
||||
ASTC10x8Unorm: 'astc-10x8-unorm',
|
||||
ASTC10x8UnormSRGB: 'astc-10x8-unorm-srgb',
|
||||
ASTC10x10Unorm: 'astc-10x10-unorm',
|
||||
ASTC10x10UnormSRGB: 'astc-10x10-unorm-srgb',
|
||||
ASTC12x10Unorm: 'astc-12x10-unorm',
|
||||
ASTC12x10UnormSRGB: 'astc-12x10-unorm-srgb',
|
||||
ASTC12x12Unorm: 'astc-12x12-unorm',
|
||||
ASTC12x12UnormSRGB: 'astc-12x12-unorm-srgb',
|
||||
|
||||
};
|
||||
|
||||
export const GPUAddressMode = {
|
||||
ClampToEdge: 'clamp-to-edge',
|
||||
Repeat: 'repeat',
|
||||
MirrorRepeat: 'mirror-repeat'
|
||||
};
|
||||
|
||||
export const GPUFilterMode = {
|
||||
Linear: 'linear',
|
||||
Nearest: 'nearest'
|
||||
};
|
||||
|
||||
export const GPUBlendFactor = {
|
||||
Zero: 'zero',
|
||||
One: 'one',
|
||||
Src: 'src',
|
||||
OneMinusSrc: 'one-minus-src',
|
||||
SrcAlpha: 'src-alpha',
|
||||
OneMinusSrcAlpha: 'one-minus-src-alpha',
|
||||
Dst: 'dst',
|
||||
OneMinusDstColor: 'one-minus-dst',
|
||||
DstAlpha: 'dst-alpha',
|
||||
OneMinusDstAlpha: 'one-minus-dst-alpha',
|
||||
SrcAlphaSaturated: 'src-alpha-saturated',
|
||||
Constant: 'constant',
|
||||
OneMinusConstant: 'one-minus-constant'
|
||||
};
|
||||
|
||||
export const GPUBlendOperation = {
|
||||
Add: 'add',
|
||||
Subtract: 'subtract',
|
||||
ReverseSubtract: 'reverse-subtract',
|
||||
Min: 'min',
|
||||
Max: 'max'
|
||||
};
|
||||
|
||||
export const GPUColorWriteFlags = {
|
||||
None: 0,
|
||||
Red: 0x1,
|
||||
Green: 0x2,
|
||||
Blue: 0x4,
|
||||
Alpha: 0x8,
|
||||
All: 0xF
|
||||
};
|
||||
|
||||
export const GPUStencilOperation = {
|
||||
Keep: 'keep',
|
||||
Zero: 'zero',
|
||||
Replace: 'replace',
|
||||
Invert: 'invert',
|
||||
IncrementClamp: 'increment-clamp',
|
||||
DecrementClamp: 'decrement-clamp',
|
||||
IncrementWrap: 'increment-wrap',
|
||||
DecrementWrap: 'decrement-wrap'
|
||||
};
|
||||
|
||||
export const GPUBufferBindingType = {
|
||||
Uniform: 'uniform',
|
||||
Storage: 'storage',
|
||||
ReadOnlyStorage: 'read-only-storage'
|
||||
};
|
||||
|
||||
export const GPUSamplerBindingType = {
|
||||
Filtering: 'filtering',
|
||||
NonFiltering: 'non-filtering',
|
||||
Comparison: 'comparison'
|
||||
};
|
||||
|
||||
export const GPUTextureSampleType = {
|
||||
Float: 'float',
|
||||
UnfilterableFloat: 'unfilterable-float',
|
||||
Depth: 'depth',
|
||||
SInt: 'sint',
|
||||
UInt: 'uint'
|
||||
};
|
||||
|
||||
export const GPUTextureDimension = {
|
||||
OneD: '1d',
|
||||
TwoD: '2d',
|
||||
ThreeD: '3d'
|
||||
};
|
||||
|
||||
export const GPUTextureViewDimension = {
|
||||
OneD: '1d',
|
||||
TwoD: '2d',
|
||||
TwoDArray: '2d-array',
|
||||
Cube: 'cube',
|
||||
CubeArray: 'cube-array',
|
||||
ThreeD: '3d'
|
||||
};
|
||||
|
||||
export const GPUTextureAspect = {
|
||||
All: 'all',
|
||||
StencilOnly: 'stencil-only',
|
||||
DepthOnly: 'depth-only'
|
||||
};
|
||||
|
||||
export const GPUInputStepMode = {
|
||||
Vertex: 'vertex',
|
||||
Instance: 'instance'
|
||||
};
|
||||
|
||||
export const GPUFeatureName = {
|
||||
DepthClipControl: 'depth-clip-control',
|
||||
Depth32FloatStencil8: 'depth32float-stencil8',
|
||||
TextureCompressionBC: 'texture-compression-bc',
|
||||
TextureCompressionETC2: 'texture-compression-etc2',
|
||||
TextureCompressionASTC: 'texture-compression-astc',
|
||||
TimestampQuery: 'timestamp-query',
|
||||
IndirectFirstInstance: 'indirect-first-instance',
|
||||
ShaderF16: 'shader-f16',
|
||||
RG11B10UFloat: 'rg11b10ufloat-renderable',
|
||||
BGRA8UNormStorage: 'bgra8unorm-storage',
|
||||
Float32Filterable: 'float32-filterable'
|
||||
};
|
@ -0,0 +1,591 @@
|
||||
import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js';
|
||||
|
||||
import {
|
||||
GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation
|
||||
} from './WebGPUConstants.js';
|
||||
|
||||
import {
|
||||
FrontSide, BackSide, DoubleSide,
|
||||
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
|
||||
NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
|
||||
ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor,
|
||||
OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor,
|
||||
AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
|
||||
KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
|
||||
NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc
|
||||
} from 'three';
|
||||
|
||||
class WebGPUPipelineUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
createRenderPipeline( renderObject, promises ) {
|
||||
|
||||
const { object, material, geometry, pipeline } = renderObject;
|
||||
const { vertexProgram, fragmentProgram } = pipeline;
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
const utils = backend.utils;
|
||||
|
||||
const pipelineData = backend.get( pipeline );
|
||||
const bindingsData = backend.get( renderObject.getBindings() );
|
||||
|
||||
// vertex buffers
|
||||
|
||||
const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers( renderObject );
|
||||
|
||||
// blending
|
||||
|
||||
let blending;
|
||||
|
||||
if ( material.transparent === true && material.blending !== NoBlending ) {
|
||||
|
||||
blending = this._getBlending( material );
|
||||
|
||||
}
|
||||
|
||||
// stencil
|
||||
|
||||
let stencilFront = {};
|
||||
|
||||
if ( material.stencilWrite === true ) {
|
||||
|
||||
stencilFront = {
|
||||
compare: this._getStencilCompare( material ),
|
||||
failOp: this._getStencilOperation( material.stencilFail ),
|
||||
depthFailOp: this._getStencilOperation( material.stencilZFail ),
|
||||
passOp: this._getStencilOperation( material.stencilZPass )
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const colorWriteMask = this._getColorWriteMask( material );
|
||||
|
||||
const targets = [];
|
||||
|
||||
if ( renderObject.context.textures !== null ) {
|
||||
|
||||
const textures = renderObject.context.textures;
|
||||
|
||||
for ( let i = 0; i < textures.length; i ++ ) {
|
||||
|
||||
const colorFormat = utils.getTextureFormatGPU( textures[ i ] );
|
||||
|
||||
targets.push( {
|
||||
format: colorFormat,
|
||||
blend: blending,
|
||||
writeMask: colorWriteMask
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
|
||||
|
||||
targets.push( {
|
||||
format: colorFormat,
|
||||
blend: blending,
|
||||
writeMask: colorWriteMask
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
const vertexModule = backend.get( vertexProgram ).module;
|
||||
const fragmentModule = backend.get( fragmentProgram ).module;
|
||||
|
||||
const primitiveState = this._getPrimitiveState( object, geometry, material );
|
||||
const depthCompare = this._getDepthCompare( material );
|
||||
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
|
||||
let sampleCount = utils.getSampleCount( renderObject.context );
|
||||
|
||||
if ( sampleCount > 1 ) {
|
||||
|
||||
// WebGPU only supports power-of-two sample counts and 2 is not a valid value
|
||||
sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) );
|
||||
|
||||
if ( sampleCount === 2 ) {
|
||||
|
||||
sampleCount = 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const pipelineDescriptor = {
|
||||
vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ),
|
||||
fragment: Object.assign( {}, fragmentModule, { targets } ),
|
||||
primitive: primitiveState,
|
||||
depthStencil: {
|
||||
format: depthStencilFormat,
|
||||
depthWriteEnabled: material.depthWrite,
|
||||
depthCompare: depthCompare,
|
||||
stencilFront: stencilFront,
|
||||
stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
|
||||
stencilReadMask: material.stencilFuncMask,
|
||||
stencilWriteMask: material.stencilWriteMask
|
||||
},
|
||||
multisample: {
|
||||
count: sampleCount,
|
||||
alphaToCoverageEnabled: material.alphaToCoverage
|
||||
},
|
||||
layout: device.createPipelineLayout( {
|
||||
bindGroupLayouts: [ bindingsData.layout ]
|
||||
} )
|
||||
};
|
||||
|
||||
if ( promises === null ) {
|
||||
|
||||
pipelineData.pipeline = device.createRenderPipeline( pipelineDescriptor );
|
||||
|
||||
} else {
|
||||
|
||||
const p = new Promise( ( resolve /*, reject*/ ) => {
|
||||
|
||||
device.createRenderPipelineAsync( pipelineDescriptor ).then( pipeline => {
|
||||
|
||||
pipelineData.pipeline = pipeline;
|
||||
resolve();
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
promises.push( p );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createComputePipeline( pipeline, bindings ) {
|
||||
|
||||
const backend = this.backend;
|
||||
const device = backend.device;
|
||||
|
||||
const computeProgram = backend.get( pipeline.computeProgram ).module;
|
||||
|
||||
const pipelineGPU = backend.get( pipeline );
|
||||
const bindingsData = backend.get( bindings );
|
||||
|
||||
pipelineGPU.pipeline = device.createComputePipeline( {
|
||||
compute: computeProgram,
|
||||
layout: device.createPipelineLayout( {
|
||||
bindGroupLayouts: [ bindingsData.layout ]
|
||||
} )
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
_getBlending( material ) {
|
||||
|
||||
let color, alpha;
|
||||
|
||||
const blending = material.blending;
|
||||
|
||||
if ( blending === CustomBlending ) {
|
||||
|
||||
const blendSrcAlpha = material.blendSrcAlpha !== null ? material.blendSrcAlpha : GPUBlendFactor.One;
|
||||
const blendDstAlpha = material.blendDstAlpha !== null ? material.blendDstAlpha : GPUBlendFactor.Zero;
|
||||
const blendEquationAlpha = material.blendEquationAlpha !== null ? material.blendEquationAlpha : GPUBlendFactor.Add;
|
||||
|
||||
color = {
|
||||
srcFactor: this._getBlendFactor( material.blendSrc ),
|
||||
dstFactor: this._getBlendFactor( material.blendDst ),
|
||||
operation: this._getBlendOperation( material.blendEquation )
|
||||
};
|
||||
|
||||
alpha = {
|
||||
srcFactor: this._getBlendFactor( blendSrcAlpha ),
|
||||
dstFactor: this._getBlendFactor( blendDstAlpha ),
|
||||
operation: this._getBlendOperation( blendEquationAlpha )
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
const premultipliedAlpha = material.premultipliedAlpha;
|
||||
|
||||
const setBlend = ( srcRGB, dstRGB, srcAlpha, dstAlpha ) => {
|
||||
|
||||
color = {
|
||||
srcFactor: srcRGB,
|
||||
dstFactor: dstRGB,
|
||||
operation: GPUBlendOperation.Add
|
||||
};
|
||||
|
||||
alpha = {
|
||||
srcFactor: srcAlpha,
|
||||
dstFactor: dstAlpha,
|
||||
operation: GPUBlendOperation.Add
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
if ( premultipliedAlpha ) {
|
||||
|
||||
switch ( blending ) {
|
||||
|
||||
case NormalBlending:
|
||||
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
|
||||
break;
|
||||
|
||||
case AdditiveBlending:
|
||||
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One );
|
||||
break;
|
||||
|
||||
case SubtractiveBlending:
|
||||
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
|
||||
break;
|
||||
|
||||
case MultiplyBlending:
|
||||
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.SrcAlpha );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
switch ( blending ) {
|
||||
|
||||
case NormalBlending:
|
||||
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
|
||||
break;
|
||||
|
||||
case AdditiveBlending:
|
||||
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.SrcAlpha, GPUBlendFactor.One );
|
||||
break;
|
||||
|
||||
case SubtractiveBlending:
|
||||
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
|
||||
break;
|
||||
|
||||
case MultiplyBlending:
|
||||
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.Src );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( color !== undefined && alpha !== undefined ) {
|
||||
|
||||
return { color, alpha };
|
||||
|
||||
} else {
|
||||
|
||||
console.error( 'THREE.WebGPURenderer: Invalid blending: ', blending );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_getBlendFactor( blend ) {
|
||||
|
||||
let blendFactor;
|
||||
|
||||
switch ( blend ) {
|
||||
|
||||
case ZeroFactor:
|
||||
blendFactor = GPUBlendFactor.Zero;
|
||||
break;
|
||||
|
||||
case OneFactor:
|
||||
blendFactor = GPUBlendFactor.One;
|
||||
break;
|
||||
|
||||
case SrcColorFactor:
|
||||
blendFactor = GPUBlendFactor.Src;
|
||||
break;
|
||||
|
||||
case OneMinusSrcColorFactor:
|
||||
blendFactor = GPUBlendFactor.OneMinusSrc;
|
||||
break;
|
||||
|
||||
case SrcAlphaFactor:
|
||||
blendFactor = GPUBlendFactor.SrcAlpha;
|
||||
break;
|
||||
|
||||
case OneMinusSrcAlphaFactor:
|
||||
blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
|
||||
break;
|
||||
|
||||
case DstColorFactor:
|
||||
blendFactor = GPUBlendFactor.Dst;
|
||||
break;
|
||||
|
||||
case OneMinusDstColorFactor:
|
||||
blendFactor = GPUBlendFactor.OneMinusDstColor;
|
||||
break;
|
||||
|
||||
case DstAlphaFactor:
|
||||
blendFactor = GPUBlendFactor.DstAlpha;
|
||||
break;
|
||||
|
||||
case OneMinusDstAlphaFactor:
|
||||
blendFactor = GPUBlendFactor.OneMinusDstAlpha;
|
||||
break;
|
||||
|
||||
case SrcAlphaSaturateFactor:
|
||||
blendFactor = GPUBlendFactor.SrcAlphaSaturated;
|
||||
break;
|
||||
|
||||
case BlendColorFactor:
|
||||
blendFactor = GPUBlendFactor.Constant;
|
||||
break;
|
||||
|
||||
case OneMinusBlendColorFactor:
|
||||
blendFactor = GPUBlendFactor.OneMinusConstant;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
|
||||
|
||||
}
|
||||
|
||||
return blendFactor;
|
||||
|
||||
}
|
||||
|
||||
_getStencilCompare( material ) {
|
||||
|
||||
let stencilCompare;
|
||||
|
||||
const stencilFunc = material.stencilFunc;
|
||||
|
||||
switch ( stencilFunc ) {
|
||||
|
||||
case NeverStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.Never;
|
||||
break;
|
||||
|
||||
case AlwaysStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.Always;
|
||||
break;
|
||||
|
||||
case LessStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.Less;
|
||||
break;
|
||||
|
||||
case LessEqualStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.LessEqual;
|
||||
break;
|
||||
|
||||
case EqualStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.Equal;
|
||||
break;
|
||||
|
||||
case GreaterEqualStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.GreaterEqual;
|
||||
break;
|
||||
|
||||
case GreaterStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.Greater;
|
||||
break;
|
||||
|
||||
case NotEqualStencilFunc:
|
||||
stencilCompare = GPUCompareFunction.NotEqual;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
|
||||
|
||||
}
|
||||
|
||||
return stencilCompare;
|
||||
|
||||
}
|
||||
|
||||
_getStencilOperation( op ) {
|
||||
|
||||
let stencilOperation;
|
||||
|
||||
switch ( op ) {
|
||||
|
||||
case KeepStencilOp:
|
||||
stencilOperation = GPUStencilOperation.Keep;
|
||||
break;
|
||||
|
||||
case ZeroStencilOp:
|
||||
stencilOperation = GPUStencilOperation.Zero;
|
||||
break;
|
||||
|
||||
case ReplaceStencilOp:
|
||||
stencilOperation = GPUStencilOperation.Replace;
|
||||
break;
|
||||
|
||||
case InvertStencilOp:
|
||||
stencilOperation = GPUStencilOperation.Invert;
|
||||
break;
|
||||
|
||||
case IncrementStencilOp:
|
||||
stencilOperation = GPUStencilOperation.IncrementClamp;
|
||||
break;
|
||||
|
||||
case DecrementStencilOp:
|
||||
stencilOperation = GPUStencilOperation.DecrementClamp;
|
||||
break;
|
||||
|
||||
case IncrementWrapStencilOp:
|
||||
stencilOperation = GPUStencilOperation.IncrementWrap;
|
||||
break;
|
||||
|
||||
case DecrementWrapStencilOp:
|
||||
stencilOperation = GPUStencilOperation.DecrementWrap;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
|
||||
|
||||
}
|
||||
|
||||
return stencilOperation;
|
||||
|
||||
}
|
||||
|
||||
_getBlendOperation( blendEquation ) {
|
||||
|
||||
let blendOperation;
|
||||
|
||||
switch ( blendEquation ) {
|
||||
|
||||
case AddEquation:
|
||||
blendOperation = GPUBlendOperation.Add;
|
||||
break;
|
||||
|
||||
case SubtractEquation:
|
||||
blendOperation = GPUBlendOperation.Subtract;
|
||||
break;
|
||||
|
||||
case ReverseSubtractEquation:
|
||||
blendOperation = GPUBlendOperation.ReverseSubtract;
|
||||
break;
|
||||
|
||||
case MinEquation:
|
||||
blendOperation = GPUBlendOperation.Min;
|
||||
break;
|
||||
|
||||
case MaxEquation:
|
||||
blendOperation = GPUBlendOperation.Max;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation );
|
||||
|
||||
}
|
||||
|
||||
return blendOperation;
|
||||
|
||||
}
|
||||
|
||||
_getPrimitiveState( object, geometry, material ) {
|
||||
|
||||
const descriptor = {};
|
||||
const utils = this.backend.utils;
|
||||
|
||||
descriptor.topology = utils.getPrimitiveTopology( object, material );
|
||||
|
||||
if ( geometry.index !== null && object.isLine === true && object.isLineSegments !== true ) {
|
||||
|
||||
descriptor.stripIndexFormat = ( geometry.index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
|
||||
|
||||
}
|
||||
|
||||
switch ( material.side ) {
|
||||
|
||||
case FrontSide:
|
||||
descriptor.frontFace = GPUFrontFace.CCW;
|
||||
descriptor.cullMode = GPUCullMode.Back;
|
||||
break;
|
||||
|
||||
case BackSide:
|
||||
descriptor.frontFace = GPUFrontFace.CCW;
|
||||
descriptor.cullMode = GPUCullMode.Front;
|
||||
break;
|
||||
|
||||
case DoubleSide:
|
||||
descriptor.frontFace = GPUFrontFace.CCW;
|
||||
descriptor.cullMode = GPUCullMode.None;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
|
||||
}
|
||||
|
||||
_getColorWriteMask( material ) {
|
||||
|
||||
return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
|
||||
|
||||
}
|
||||
|
||||
_getDepthCompare( material ) {
|
||||
|
||||
let depthCompare;
|
||||
|
||||
if ( material.depthTest === false ) {
|
||||
|
||||
depthCompare = GPUCompareFunction.Always;
|
||||
|
||||
} else {
|
||||
|
||||
const depthFunc = material.depthFunc;
|
||||
|
||||
switch ( depthFunc ) {
|
||||
|
||||
case NeverDepth:
|
||||
depthCompare = GPUCompareFunction.Never;
|
||||
break;
|
||||
|
||||
case AlwaysDepth:
|
||||
depthCompare = GPUCompareFunction.Always;
|
||||
break;
|
||||
|
||||
case LessDepth:
|
||||
depthCompare = GPUCompareFunction.Less;
|
||||
break;
|
||||
|
||||
case LessEqualDepth:
|
||||
depthCompare = GPUCompareFunction.LessEqual;
|
||||
break;
|
||||
|
||||
case EqualDepth:
|
||||
depthCompare = GPUCompareFunction.Equal;
|
||||
break;
|
||||
|
||||
case GreaterEqualDepth:
|
||||
depthCompare = GPUCompareFunction.GreaterEqual;
|
||||
break;
|
||||
|
||||
case GreaterDepth:
|
||||
depthCompare = GPUCompareFunction.Greater;
|
||||
break;
|
||||
|
||||
case NotEqualDepth:
|
||||
depthCompare = GPUCompareFunction.NotEqual;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return depthCompare;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPUPipelineUtils;
|
@ -0,0 +1,285 @@
|
||||
import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js';
|
||||
|
||||
class WebGPUTexturePassUtils {
|
||||
|
||||
constructor( device ) {
|
||||
|
||||
this.device = device;
|
||||
|
||||
const mipmapVertexSource = `
|
||||
struct VarysStruct {
|
||||
@builtin( position ) Position: vec4<f32>,
|
||||
@location( 0 ) vTex : vec2<f32>
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct {
|
||||
|
||||
var Varys : VarysStruct;
|
||||
|
||||
var pos = array< vec2<f32>, 4 >(
|
||||
vec2<f32>( -1.0, 1.0 ),
|
||||
vec2<f32>( 1.0, 1.0 ),
|
||||
vec2<f32>( -1.0, -1.0 ),
|
||||
vec2<f32>( 1.0, -1.0 )
|
||||
);
|
||||
|
||||
var tex = array< vec2<f32>, 4 >(
|
||||
vec2<f32>( 0.0, 0.0 ),
|
||||
vec2<f32>( 1.0, 0.0 ),
|
||||
vec2<f32>( 0.0, 1.0 ),
|
||||
vec2<f32>( 1.0, 1.0 )
|
||||
);
|
||||
|
||||
Varys.vTex = tex[ vertexIndex ];
|
||||
Varys.Position = vec4<f32>( pos[ vertexIndex ], 0.0, 1.0 );
|
||||
|
||||
return Varys;
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
const mipmapFragmentSource = `
|
||||
@group( 0 ) @binding( 0 )
|
||||
var imgSampler : sampler;
|
||||
|
||||
@group( 0 ) @binding( 1 )
|
||||
var img : texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
|
||||
|
||||
return textureSample( img, imgSampler, vTex );
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
const flipYFragmentSource = `
|
||||
@group( 0 ) @binding( 0 )
|
||||
var imgSampler : sampler;
|
||||
|
||||
@group( 0 ) @binding( 1 )
|
||||
var img : texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
|
||||
|
||||
return textureSample( img, imgSampler, vec2( vTex.x, 1.0 - vTex.y ) );
|
||||
|
||||
}
|
||||
`;
|
||||
this.mipmapSampler = device.createSampler( { minFilter: GPUFilterMode.Linear } );
|
||||
this.flipYSampler = device.createSampler( { minFilter: GPUFilterMode.Nearest } ); //@TODO?: Consider using textureLoad()
|
||||
|
||||
// We'll need a new pipeline for every texture format used.
|
||||
this.transferPipelines = {};
|
||||
this.flipYPipelines = {};
|
||||
|
||||
this.mipmapVertexShaderModule = device.createShaderModule( {
|
||||
label: 'mipmapVertex',
|
||||
code: mipmapVertexSource
|
||||
} );
|
||||
|
||||
this.mipmapFragmentShaderModule = device.createShaderModule( {
|
||||
label: 'mipmapFragment',
|
||||
code: mipmapFragmentSource
|
||||
} );
|
||||
|
||||
this.flipYFragmentShaderModule = device.createShaderModule( {
|
||||
label: 'flipYFragment',
|
||||
code: flipYFragmentSource
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getTransferPipeline( format ) {
|
||||
|
||||
let pipeline = this.transferPipelines[ format ];
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
pipeline = this.device.createRenderPipeline( {
|
||||
vertex: {
|
||||
module: this.mipmapVertexShaderModule,
|
||||
entryPoint: 'main'
|
||||
},
|
||||
fragment: {
|
||||
module: this.mipmapFragmentShaderModule,
|
||||
entryPoint: 'main',
|
||||
targets: [ { format } ]
|
||||
},
|
||||
primitive: {
|
||||
topology: GPUPrimitiveTopology.TriangleStrip,
|
||||
stripIndexFormat: GPUIndexFormat.Uint32
|
||||
},
|
||||
layout: 'auto'
|
||||
} );
|
||||
|
||||
this.transferPipelines[ format ] = pipeline;
|
||||
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
|
||||
}
|
||||
|
||||
getFlipYPipeline( format ) {
|
||||
|
||||
let pipeline = this.flipYPipelines[ format ];
|
||||
|
||||
if ( pipeline === undefined ) {
|
||||
|
||||
pipeline = this.device.createRenderPipeline( {
|
||||
vertex: {
|
||||
module: this.mipmapVertexShaderModule,
|
||||
entryPoint: 'main'
|
||||
},
|
||||
fragment: {
|
||||
module: this.flipYFragmentShaderModule,
|
||||
entryPoint: 'main',
|
||||
targets: [ { format } ]
|
||||
},
|
||||
primitive: {
|
||||
topology: GPUPrimitiveTopology.TriangleStrip,
|
||||
stripIndexFormat: GPUIndexFormat.Uint32
|
||||
},
|
||||
layout: 'auto'
|
||||
} );
|
||||
|
||||
this.flipYPipelines[ format ] = pipeline;
|
||||
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
|
||||
}
|
||||
|
||||
flipY( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) {
|
||||
|
||||
const format = textureGPUDescriptor.format;
|
||||
const { width, height } = textureGPUDescriptor.size;
|
||||
|
||||
const transferPipeline = this.getTransferPipeline( format );
|
||||
const flipYPipeline = this.getFlipYPipeline( format );
|
||||
|
||||
const tempTexture = this.device.createTexture( {
|
||||
size: { width, height, depthOrArrayLayers: 1 },
|
||||
format,
|
||||
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
||||
} );
|
||||
|
||||
const srcView = textureGPU.createView( {
|
||||
baseMipLevel: 0,
|
||||
mipLevelCount: 1,
|
||||
dimension: GPUTextureViewDimension.TwoD,
|
||||
baseArrayLayer
|
||||
} );
|
||||
|
||||
const dstView = tempTexture.createView( {
|
||||
baseMipLevel: 0,
|
||||
mipLevelCount: 1,
|
||||
dimension: GPUTextureViewDimension.TwoD,
|
||||
baseArrayLayer: 0
|
||||
} );
|
||||
|
||||
const commandEncoder = this.device.createCommandEncoder( {} );
|
||||
|
||||
const pass = ( pipeline, sourceView, destinationView ) => {
|
||||
|
||||
const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
|
||||
|
||||
const bindGroup = this.device.createBindGroup( {
|
||||
layout: bindGroupLayout,
|
||||
entries: [ {
|
||||
binding: 0,
|
||||
resource: this.flipYSampler
|
||||
}, {
|
||||
binding: 1,
|
||||
resource: sourceView
|
||||
} ]
|
||||
} );
|
||||
|
||||
const passEncoder = commandEncoder.beginRenderPass( {
|
||||
colorAttachments: [ {
|
||||
view: destinationView,
|
||||
loadOp: GPULoadOp.Clear,
|
||||
storeOp: GPUStoreOp.Store,
|
||||
clearValue: [ 0, 0, 0, 0 ]
|
||||
} ]
|
||||
} );
|
||||
|
||||
passEncoder.setPipeline( pipeline );
|
||||
passEncoder.setBindGroup( 0, bindGroup );
|
||||
passEncoder.draw( 4, 1, 0, 0 );
|
||||
passEncoder.end();
|
||||
|
||||
};
|
||||
|
||||
pass( transferPipeline, srcView, dstView );
|
||||
pass( flipYPipeline, dstView, srcView );
|
||||
|
||||
this.device.queue.submit( [ commandEncoder.finish() ] );
|
||||
|
||||
tempTexture.destroy();
|
||||
|
||||
}
|
||||
|
||||
generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) {
|
||||
|
||||
const pipeline = this.getTransferPipeline( textureGPUDescriptor.format );
|
||||
|
||||
const commandEncoder = this.device.createCommandEncoder( {} );
|
||||
const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
|
||||
|
||||
let srcView = textureGPU.createView( {
|
||||
baseMipLevel: 0,
|
||||
mipLevelCount: 1,
|
||||
dimension: GPUTextureViewDimension.TwoD,
|
||||
baseArrayLayer
|
||||
} );
|
||||
|
||||
for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) {
|
||||
|
||||
const bindGroup = this.device.createBindGroup( {
|
||||
layout: bindGroupLayout,
|
||||
entries: [ {
|
||||
binding: 0,
|
||||
resource: this.mipmapSampler
|
||||
}, {
|
||||
binding: 1,
|
||||
resource: srcView
|
||||
} ]
|
||||
} );
|
||||
|
||||
const dstView = textureGPU.createView( {
|
||||
baseMipLevel: i,
|
||||
mipLevelCount: 1,
|
||||
dimension: GPUTextureViewDimension.TwoD,
|
||||
baseArrayLayer
|
||||
} );
|
||||
|
||||
const passEncoder = commandEncoder.beginRenderPass( {
|
||||
colorAttachments: [ {
|
||||
view: dstView,
|
||||
loadOp: GPULoadOp.Clear,
|
||||
storeOp: GPUStoreOp.Store,
|
||||
clearValue: [ 0, 0, 0, 0 ]
|
||||
} ]
|
||||
} );
|
||||
|
||||
passEncoder.setPipeline( pipeline );
|
||||
passEncoder.setBindGroup( 0, bindGroup );
|
||||
passEncoder.draw( 4, 1, 0, 0 );
|
||||
passEncoder.end();
|
||||
|
||||
srcView = dstView;
|
||||
|
||||
}
|
||||
|
||||
this.device.queue.submit( [ commandEncoder.finish() ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPUTexturePassUtils;
|
1114
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
Normal file
1114
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
Normal file
File diff suppressed because it is too large
Load Diff
93
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUUtils.js
Normal file
93
static/sdk/three/jsm/renderers/webgpu/utils/WebGPUUtils.js
Normal file
@ -0,0 +1,93 @@
|
||||
import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js';
|
||||
|
||||
class WebGPUUtils {
|
||||
|
||||
constructor( backend ) {
|
||||
|
||||
this.backend = backend;
|
||||
|
||||
}
|
||||
|
||||
getCurrentDepthStencilFormat( renderContext ) {
|
||||
|
||||
let format;
|
||||
|
||||
if ( renderContext.depthTexture !== null ) {
|
||||
|
||||
format = this.getTextureFormatGPU( renderContext.depthTexture );
|
||||
|
||||
} else if ( renderContext.depth && renderContext.stencil ) {
|
||||
|
||||
format = GPUTextureFormat.Depth24PlusStencil8;
|
||||
|
||||
} else if ( renderContext.depth ) {
|
||||
|
||||
format = GPUTextureFormat.Depth24Plus;
|
||||
|
||||
}
|
||||
|
||||
return format;
|
||||
|
||||
}
|
||||
|
||||
getTextureFormatGPU( texture ) {
|
||||
|
||||
return this.backend.get( texture ).texture.format;
|
||||
|
||||
}
|
||||
|
||||
getCurrentColorFormat( renderContext ) {
|
||||
|
||||
let format;
|
||||
|
||||
if ( renderContext.textures !== null ) {
|
||||
|
||||
format = this.getTextureFormatGPU( renderContext.textures[ 0 ] );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
format = GPUTextureFormat.BGRA8Unorm; // default context format
|
||||
|
||||
}
|
||||
|
||||
return format;
|
||||
|
||||
}
|
||||
|
||||
getCurrentColorSpace( renderContext ) {
|
||||
|
||||
if ( renderContext.textures !== null ) {
|
||||
|
||||
return renderContext.textures[ 0 ].colorSpace;
|
||||
|
||||
}
|
||||
|
||||
return this.backend.renderer.outputColorSpace;
|
||||
|
||||
}
|
||||
|
||||
getPrimitiveTopology( object, material ) {
|
||||
|
||||
if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
|
||||
else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList;
|
||||
else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
|
||||
else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
|
||||
|
||||
}
|
||||
|
||||
getSampleCount( renderContext ) {
|
||||
|
||||
if ( renderContext.textures !== null ) {
|
||||
|
||||
return renderContext.sampleCount;
|
||||
|
||||
}
|
||||
|
||||
return this.backend.parameters.sampleCount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGPUUtils;
|
Reference in New Issue
Block a user