From b2ea47ac7a575032af050b43afb8f57478efc8b6 Mon Sep 17 00:00:00 2001
From: Rik Cabanier
Date: Sun, 19 Sep 2021 16:18:39 -0700
Subject: [PATCH 1/8] Clean up WebXR manager by calling setRenderTarget
---
src/renderers/WebGLMultisampleRenderTarget.js | 6 +-
src/renderers/WebGLRenderTarget.js | 4 +
src/renderers/WebGLRenderer.js | 59 ++++-
src/renderers/webgl/WebGLState.js | 17 --
src/renderers/webgl/WebGLTextures.js | 139 ++++++++++--
src/renderers/webxr/WebXRManager.js | 206 +++++++-----------
6 files changed, 255 insertions(+), 176 deletions(-)
diff --git a/src/renderers/WebGLMultisampleRenderTarget.js b/src/renderers/WebGLMultisampleRenderTarget.js
index ccbb38e4aee7bc..9b64d91a14d7e5 100644
--- a/src/renderers/WebGLMultisampleRenderTarget.js
+++ b/src/renderers/WebGLMultisampleRenderTarget.js
@@ -2,11 +2,13 @@ import { WebGLRenderTarget } from './WebGLRenderTarget.js';
class WebGLMultisampleRenderTarget extends WebGLRenderTarget {
- constructor( width, height, options ) {
+ constructor( width, height, options = {} ) {
super( width, height, options );
this.samples = 4;
+ this.useMultisampleRenderToTexture = ( options.useMultisampleRenderToTexture !== undefined ) ? options.useMultisampleRenderToTexture : false;
+ this.useMultisampleRenderbuffer = this.useMultisampleRenderToTexture === false;
}
@@ -15,6 +17,8 @@ class WebGLMultisampleRenderTarget extends WebGLRenderTarget {
super.copy.call( this, source );
this.samples = source.samples;
+ this.useMultisampleRenderToTexture = source.useMultisampleRenderToTexture;
+ this.useMultisampleRenderbuffer = source.useMultisampleRenderbuffer;
return this;
diff --git a/src/renderers/WebGLRenderTarget.js b/src/renderers/WebGLRenderTarget.js
index 41ad23cccf91f4..22833d3c2c069f 100644
--- a/src/renderers/WebGLRenderTarget.js
+++ b/src/renderers/WebGLRenderTarget.js
@@ -35,6 +35,10 @@ class WebGLRenderTarget extends EventDispatcher {
this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false;
this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
+ this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true;
+ this.hasExternalTextures = false;
+ this.useMultisampleRenderToTexture = false;
+ this.useMultisampleRenderbuffer = false;
}
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index 32514d7e407c5f..c113d96c5f59f7 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -1280,7 +1280,8 @@ function WebGLRenderer( parameters = {} ) {
minFilter: LinearMipmapLinearFilter,
magFilter: NearestFilter,
wrapS: ClampToEdgeWrapping,
- wrapT: ClampToEdgeWrapping
+ wrapT: ClampToEdgeWrapping,
+ useMultisampleRenderToTexture: extensions.has( 'EXT_multisampled_render_to_texture' )
} );
}
@@ -1853,15 +1854,61 @@ function WebGLRenderer( parameters = {} ) {
};
- this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) {
+ this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0, options = {} ) {
_currentRenderTarget = renderTarget;
_currentActiveCubeFace = activeCubeFace;
_currentActiveMipmapLevel = activeMipmapLevel;
+ const useDefaultFramebuffer = options.framebuffer === undefined;
- if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
+ if ( renderTarget ) {
+
+ const renderTargetProperties = properties.get( renderTarget );
+ let hasNewExternalTextures = false;
+
+ if ( options.colorTexture !== undefined ) {
+
+ hasNewExternalTextures = true;
+ properties.get( renderTarget.texture ).__webglTexture = options.colorTexture;
+
+ renderTarget.autoAllocateDepthBuffer = options.depthTexture === undefined;
+
+ if ( ! renderTarget.autoAllocateDepthBuffer ) {
- textures.setupRenderTarget( renderTarget );
+ properties.get( renderTarget.depthTexture ).__webglTexture = options.depthTexture;
+
+ // The multisample_render_to_texture extension doesn't work properly if there
+ // are midframe flushes and an external depth buffer. Disable use of the extension.
+ if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ console.warn( 'render-to-texture extension was disabled because an external texture was provided' );
+ renderTarget.useMultisampleRenderToTexture = false;
+ renderTarget.useMultisampleRenderbuffer = true;
+
+ }
+
+ }
+
+ renderTarget.hasExternalTextures = true;
+
+ }
+
+ if ( ! useDefaultFramebuffer ) {
+
+ // We need to make sure to rebind the framebuffer.
+ state.bindFramebuffer( _gl.FRAMEBUFFER, null );
+ renderTargetProperties.__webglFramebuffer = options.framebuffer;
+
+ } else if ( renderTargetProperties.__webglFramebuffer === undefined ) {
+
+ textures.setupRenderTarget( renderTarget );
+
+ } else if ( hasNewExternalTextures ) {
+
+ // Color and depth texture must be rebound in order for the swapchain to update.
+ textures.rebindTextures( renderTarget, options.colorTexture, options.depthTexture );
+
+ }
}
@@ -1886,7 +1933,7 @@ function WebGLRenderer( parameters = {} ) {
framebuffer = __webglFramebuffer[ activeCubeFace ];
isCube = true;
- } else if ( renderTarget.isWebGLMultisampleRenderTarget ) {
+ } else if ( renderTarget.useMultisampleRenderbuffer ) {
framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;
@@ -1910,7 +1957,7 @@ function WebGLRenderer( parameters = {} ) {
const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
- if ( framebufferBound && capabilities.drawBuffers ) {
+ if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) {
let needsUpdate = false;
diff --git a/src/renderers/webgl/WebGLState.js b/src/renderers/webgl/WebGLState.js
index 1904d7d66e1fbb..675da3dea9d9ef 100644
--- a/src/renderers/webgl/WebGLState.js
+++ b/src/renderers/webgl/WebGLState.js
@@ -316,7 +316,6 @@ function WebGLState( gl, extensions, capabilities ) {
let enabledCapabilities = {};
- let xrFramebuffer = null;
let currentBoundFramebuffers = {};
let currentProgram = null;
@@ -428,22 +427,8 @@ function WebGLState( gl, extensions, capabilities ) {
}
- function bindXRFramebuffer( framebuffer ) {
-
- if ( framebuffer !== xrFramebuffer ) {
-
- gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );
-
- xrFramebuffer = framebuffer;
-
- }
-
- }
-
function bindFramebuffer( target, framebuffer ) {
- if ( framebuffer === null && xrFramebuffer !== null ) framebuffer = xrFramebuffer; // use active XR framebuffer if available
-
if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
gl.bindFramebuffer( target, framebuffer );
@@ -991,7 +976,6 @@ function WebGLState( gl, extensions, capabilities ) {
currentTextureSlot = null;
currentBoundTextures = {};
- xrFramebuffer = null;
currentBoundFramebuffers = {};
currentProgram = null;
@@ -1035,7 +1019,6 @@ function WebGLState( gl, extensions, capabilities ) {
disable: disable,
bindFramebuffer: bindFramebuffer,
- bindXRFramebuffer: bindXRFramebuffer,
useProgram: useProgram,
diff --git a/src/renderers/webgl/WebGLTextures.js b/src/renderers/webgl/WebGLTextures.js
index 887c4bada18a4a..ac34cd69e9c8bd 100644
--- a/src/renderers/webgl/WebGLTextures.js
+++ b/src/renderers/webgl/WebGLTextures.js
@@ -9,6 +9,8 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const maxCubemapSize = capabilities.maxCubemapSize;
const maxTextureSize = capabilities.maxTextureSize;
const maxSamples = capabilities.maxSamples;
+ const msaartcSupported = extensions.has( 'EXT_multisampled_render_to_texture' );
+ const msaaExt = msaartcSupported ? extensions.get( 'EXT_multisampled_render_to_texture' ) : undefined;
const _videoTextures = new WeakMap();
let _canvas;
@@ -859,22 +861,36 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const glType = utils.convert( texture.type );
const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType );
- if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) {
+ if ( ! renderTarget.hasExternalTextures ) {
- state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null );
+ if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) {
- } else {
+ state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null );
+
+ } else {
- state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+ state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+ }
}
state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
- _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 );
+ if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) );
+
+ } else {
+
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 );
+
+ }
+
state.bindFramebuffer( _gl.FRAMEBUFFER, null );
}
+
// Setup storage for internal depth/stencil buffers and bind to correct framebuffer
function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) {
@@ -884,7 +900,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
let glInternalFormat = _gl.DEPTH_COMPONENT16;
- if ( isMultisample ) {
+ if ( isMultisample || renderTarget.useMultisampleRenderToTexture ) {
const depthTexture = renderTarget.depthTexture;
@@ -904,7 +920,15 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const samples = getRenderTargetSamples( renderTarget );
- _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+
+ } else {
+
+ _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+
+ }
} else {
@@ -916,12 +940,16 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
- if ( isMultisample ) {
+ const samples = getRenderTargetSamples( renderTarget );
- const samples = getRenderTargetSamples( renderTarget );
+ if ( isMultisample && renderTarget.useMultisampleRenderbuffer ) {
_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );
+ } else if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );
+
} else {
_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
@@ -939,13 +967,16 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const glFormat = utils.convert( texture.format );
const glType = utils.convert( texture.type );
const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType );
+ const samples = getRenderTargetSamples( renderTarget );
- if ( isMultisample ) {
-
- const samples = getRenderTargetSamples( renderTarget );
+ if ( isMultisample && renderTarget.useMultisampleRenderbuffer ) {
_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ } else if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+
} else {
_gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height );
@@ -986,14 +1017,31 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
setTexture2D( renderTarget.depthTexture, 0 );
const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;
+ const samples = getRenderTargetSamples( renderTarget );
if ( renderTarget.depthTexture.format === DepthFormat ) {
- _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+ if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+
+ } else {
+
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+
+ }
} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {
- _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+ if ( renderTarget.useMultisampleRenderToTexture ) {
+
+ msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+
+ } else {
+
+ _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );
+
+ }
} else {
@@ -1007,10 +1055,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function setupDepthRenderbuffer( renderTarget ) {
const renderTargetProperties = properties.get( renderTarget );
-
const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
- if ( renderTarget.depthTexture ) {
+ if ( renderTarget.depthTexture && ! renderTarget.autoAllocateDepthBuffer ) {
if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );
@@ -1044,6 +1091,25 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
}
+ // rebind framebuffer with external textures
+ function rebindTextures( renderTarget, colorTexture, depthTexture ) {
+
+ const renderTargetProperties = properties.get( renderTarget );
+
+ if ( colorTexture !== undefined ) {
+
+ setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D );
+
+ }
+
+ if ( depthTexture !== undefined ) {
+
+ setupDepthRenderbuffer( renderTarget );
+
+ }
+
+ }
+
// Set up GL resources for the render target
function setupRenderTarget( renderTarget ) {
@@ -1056,7 +1122,12 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
if ( renderTarget.isWebGLMultipleRenderTargets !== true ) {
- textureProperties.__webglTexture = _gl.createTexture();
+ if ( textureProperties.__webglTexture === undefined ) {
+
+ textureProperties.__webglTexture = _gl.createTexture();
+
+ }
+
textureProperties.__version = texture.version;
info.memory.textures ++;
@@ -1064,7 +1135,6 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true );
- const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true );
const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray;
const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;
@@ -1120,7 +1190,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
}
- } else if ( isMultisample ) {
+ } else if ( renderTarget.useMultisampleRenderbuffer ) {
if ( isWebGL2 ) {
@@ -1275,23 +1345,43 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function updateMultisampleRenderTarget( renderTarget ) {
- if ( renderTarget.isWebGLMultisampleRenderTarget ) {
+ if ( renderTarget.useMultisampleRenderbuffer ) {
if ( isWebGL2 ) {
const width = renderTarget.width;
const height = renderTarget.height;
let mask = _gl.COLOR_BUFFER_BIT;
+ const invalidationArray = [ _gl.COLOR_ATTACHMENT0 ];
+ const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT;
+
+ if ( renderTarget.depthBuffer ) {
+
+ invalidationArray.push( depthStyle );
- if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT;
- if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT;
+ }
+
+ if ( ! renderTarget.ignoreDepthForMultisampleCopy ) {
+
+ if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT;
+ if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT;
+
+ }
const renderTargetProperties = properties.get( renderTarget );
state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );
state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
+ if ( renderTarget.ignoreDepthForMultisampleCopy ) {
+
+ _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] );
+ _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] );
+
+ }
+
_gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST );
+ _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray );
state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null );
state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer );
@@ -1308,7 +1398,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function getRenderTargetSamples( renderTarget ) {
- return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ?
+ return ( isWebGL2 && ( renderTarget.useMultisampleRenderbuffer || renderTarget.useMultisampleRenderToTexture ) ) ?
Math.min( maxSamples, renderTarget.samples ) : 0;
}
@@ -1381,9 +1471,12 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
this.setTexture2DArray = setTexture2DArray;
this.setTexture3D = setTexture3D;
this.setTextureCube = setTextureCube;
+ this.rebindTextures = rebindTextures;
this.setupRenderTarget = setupRenderTarget;
this.updateRenderTargetMipmap = updateRenderTargetMipmap;
this.updateMultisampleRenderTarget = updateMultisampleRenderTarget;
+ this.setupDepthRenderbuffer = setupDepthRenderbuffer;
+ this.setupFrameBufferTexture = setupFrameBufferTexture;
this.safeSetTexture2D = safeSetTexture2D;
this.safeSetTextureCube = safeSetTextureCube;
diff --git a/src/renderers/webxr/WebXRManager.js b/src/renderers/webxr/WebXRManager.js
index 3ce77cd97953c7..58557188eb3eff 100644
--- a/src/renderers/webxr/WebXRManager.js
+++ b/src/renderers/webxr/WebXRManager.js
@@ -4,7 +4,19 @@ import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
import { WebGLAnimation } from '../webgl/WebGLAnimation.js';
+import { WebGLRenderTarget } from '../WebGLRenderTarget.js';
import { WebXRController } from './WebXRController.js';
+import { DepthTexture } from '../../textures/DepthTexture.js';
+import { WebGLMultisampleRenderTarget } from '../WebGLMultisampleRenderTarget.js';
+import {
+ DepthFormat,
+ DepthStencilFormat,
+ RGBAFormat,
+ RGBFormat,
+ UnsignedByteType,
+ UnsignedShortType,
+ UnsignedInt248Type,
+} from '../../constants.js';
class WebXRManager extends EventDispatcher {
@@ -13,28 +25,23 @@ class WebXRManager extends EventDispatcher {
super();
const scope = this;
- const state = renderer.state;
let session = null;
let framebufferScaleFactor = 1.0;
let referenceSpace = null;
let referenceSpaceType = 'local-floor';
+ const msaartcSupported = renderer.extensions.has( 'EXT_multisampled_render_to_texture' );
let pose = null;
let glBinding = null;
- let glFramebuffer = null;
let glProjLayer = null;
let glBaseLayer = null;
let isMultisample = false;
- let glMultisampledFramebuffer = null;
- let glColorRenderbuffer = null;
- let glDepthRenderbuffer = null;
let xrFrame = null;
- let depthStyle = null;
- let clearStyle = null;
- const msaartcSupported = renderer.extensions.has( 'EXT_multisampled_render_to_texture' );
- let msaaExt = null;
+ const attributes = gl.getContextAttributes();
+ let initialRenderTarget = null;
+ let newRenderTarget = null;
const controllers = [];
const inputSourcesMap = new Map();
@@ -139,21 +146,13 @@ class WebXRManager extends EventDispatcher {
// restore framebuffer/rendering state
- state.bindXRFramebuffer( null );
- renderer.setRenderTarget( renderer.getRenderTarget() );
-
- if ( glFramebuffer ) gl.deleteFramebuffer( glFramebuffer );
- if ( glMultisampledFramebuffer ) gl.deleteFramebuffer( glMultisampledFramebuffer );
- if ( glColorRenderbuffer ) gl.deleteRenderbuffer( glColorRenderbuffer );
- if ( glDepthRenderbuffer ) gl.deleteRenderbuffer( glDepthRenderbuffer );
- glFramebuffer = null;
- glMultisampledFramebuffer = null;
- glColorRenderbuffer = null;
- glDepthRenderbuffer = null;
+ renderer.setRenderTarget( initialRenderTarget );
+
glBaseLayer = null;
glProjLayer = null;
glBinding = null;
session = null;
+ newRenderTarget = null;
//
@@ -225,6 +224,8 @@ class WebXRManager extends EventDispatcher {
if ( session !== null ) {
+ initialRenderTarget = renderer.getRenderTarget();
+
session.addEventListener( 'select', onSessionEvent );
session.addEventListener( 'selectstart', onSessionEvent );
session.addEventListener( 'selectend', onSessionEvent );
@@ -234,18 +235,16 @@ class WebXRManager extends EventDispatcher {
session.addEventListener( 'end', onSessionEnd );
session.addEventListener( 'inputsourceschange', onInputSourcesChange );
- const attributes = gl.getContextAttributes();
-
if ( attributes.xrCompatible !== true ) {
await gl.makeXRCompatible();
}
- if ( session.renderState.layers === undefined ) {
+ if ( ( session.renderState.layers === undefined ) || ( gl instanceof WebGLRenderingContext ) ) {
const layerInit = {
- antialias: attributes.antialias,
+ antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true,
alpha: attributes.alpha,
depth: attributes.depth,
stencil: attributes.stencil,
@@ -256,43 +255,29 @@ class WebXRManager extends EventDispatcher {
session.updateRenderState( { baseLayer: glBaseLayer } );
- } else if ( gl instanceof WebGLRenderingContext ) {
-
- // Use old style webgl layer because we can't use MSAA
- // WebGL2 support.
-
- const layerInit = {
- antialias: true,
- alpha: attributes.alpha,
- depth: attributes.depth,
- stencil: attributes.stencil,
- framebufferScaleFactor: framebufferScaleFactor
- };
-
- glBaseLayer = new XRWebGLLayer( session, gl, layerInit );
-
- session.updateRenderState( { layers: [ glBaseLayer ] } );
+ newRenderTarget = new WebGLRenderTarget(
+ glBaseLayer.framebufferWidth,
+ glBaseLayer.framebufferHeight
+ );
} else {
isMultisample = attributes.antialias;
let depthFormat = null;
-
+ let depthType = null;
+ let glDepthFormat = null;
if ( attributes.depth ) {
- clearStyle = gl.DEPTH_BUFFER_BIT;
-
- if ( attributes.stencil ) clearStyle |= gl.STENCIL_BUFFER_BIT;
-
- depthStyle = attributes.stencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
- depthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24;
+ glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT16;
+ depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat;
+ depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType;
}
const projectionlayerInit = {
- colorFormat: attributes.alpha ? gl.RGBA8 : gl.RGB8,
- depthFormat: depthFormat,
+ colorFormat: ( attributes.alpha || isMultisample ) ? gl.RGBA8 : gl.RGB8,
+ depthFormat: glDepthFormat,
scaleFactor: framebufferScaleFactor
};
@@ -300,45 +285,42 @@ class WebXRManager extends EventDispatcher {
glProjLayer = glBinding.createProjectionLayer( projectionlayerInit );
- glFramebuffer = gl.createFramebuffer();
-
session.updateRenderState( { layers: [ glProjLayer ] } );
- if ( isMultisample && msaartcSupported ) {
+ if ( isMultisample ) {
- msaaExt = renderer.extensions.get( 'EXT_multisampled_render_to_texture' );
-
- } else if ( isMultisample ) {
-
- glMultisampledFramebuffer = gl.createFramebuffer();
- glColorRenderbuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer( gl.RENDERBUFFER, glColorRenderbuffer );
- gl.renderbufferStorageMultisample(
- gl.RENDERBUFFER,
- 4,
- gl.RGBA8,
+ newRenderTarget = new WebGLMultisampleRenderTarget(
glProjLayer.textureWidth,
- glProjLayer.textureHeight );
- state.bindFramebuffer( gl.FRAMEBUFFER, glMultisampledFramebuffer );
- gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, glColorRenderbuffer );
- gl.bindRenderbuffer( gl.RENDERBUFFER, null );
-
- if ( depthFormat !== null ) {
+ glProjLayer.textureHeight,
+ {
+ format: RGBAFormat,
+ type: UnsignedByteType,
+ depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),
+ stencilBuffer: attributes.stencil,
+ ignoreDepth: glProjLayer.ignoreDepthValues,
+ useMultisampleRenderToTexture: msaartcSupported,
+ } );
- glDepthRenderbuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer( gl.RENDERBUFFER, glDepthRenderbuffer );
- gl.renderbufferStorageMultisample( gl.RENDERBUFFER, 4, depthFormat, glProjLayer.textureWidth, glProjLayer.textureHeight );
- gl.framebufferRenderbuffer( gl.FRAMEBUFFER, depthStyle, gl.RENDERBUFFER, glDepthRenderbuffer );
- gl.bindRenderbuffer( gl.RENDERBUFFER, null );
-
- }
+ } else {
- state.bindFramebuffer( gl.FRAMEBUFFER, null );
+ newRenderTarget = new WebGLRenderTarget(
+ glProjLayer.textureWidth,
+ glProjLayer.textureHeight,
+ {
+ format: attributes.alpha ? RGBAFormat : RGBFormat,
+ type: UnsignedByteType,
+ depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),
+ stencilBuffer: attributes.stencil,
+ ignoreDepth: glProjLayer.ignoreDepthValues,
+ } );
}
}
+ // Set foveation to maximum.
+ this.setFoveation( 0 );
+
referenceSpace = await session.requestReferenceSpace( referenceSpaceType );
animation.setContext( session );
@@ -597,7 +579,13 @@ class WebXRManager extends EventDispatcher {
if ( glBaseLayer !== null ) {
- state.bindXRFramebuffer( glBaseLayer.framebuffer );
+ renderer.setRenderTarget(
+ newRenderTarget,
+ 0,
+ 0,
+ {
+ framebuffer: glBaseLayer.framebuffer
+ } );
}
@@ -608,7 +596,6 @@ class WebXRManager extends EventDispatcher {
if ( views.length !== cameraVR.cameras.length ) {
cameraVR.cameras.length = 0;
-
cameraVRNeedsUpdate = true;
}
@@ -626,33 +613,22 @@ class WebXRManager extends EventDispatcher {
} else {
const glSubImage = glBinding.getViewSubImage( glProjLayer, view );
+ viewport = glSubImage.viewport;
- state.bindXRFramebuffer( glFramebuffer );
-
- if ( isMultisample && msaartcSupported ) {
-
- if ( glSubImage.depthStencilTexture !== undefined ) {
-
- msaaExt.framebufferTexture2DMultisampleEXT( gl.FRAMEBUFFER, depthStyle, gl.TEXTURE_2D, glSubImage.depthStencilTexture, 0, 4 );
-
- }
-
- msaaExt.framebufferTexture2DMultisampleEXT( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glSubImage.colorTexture, 0, 4 );
-
- } else {
-
- if ( glSubImage.depthStencilTexture !== undefined ) {
-
- gl.framebufferTexture2D( gl.FRAMEBUFFER, depthStyle, gl.TEXTURE_2D, glSubImage.depthStencilTexture, 0 );
-
- }
+ // For side-by-side projection, we only produce a single texture for both eyes.
+ if ( i === 0 ) {
- gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glSubImage.colorTexture, 0 );
+ renderer.setRenderTarget(
+ newRenderTarget,
+ 0,
+ 0,
+ {
+ colorTexture: glSubImage.colorTexture,
+ depthTexture: glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture
+ } );
}
- viewport = glSubImage.viewport;
-
}
const camera = cameras[ i ];
@@ -675,14 +651,6 @@ class WebXRManager extends EventDispatcher {
}
- if ( isMultisample && ! msaartcSupported ) {
-
- state.bindXRFramebuffer( glMultisampledFramebuffer );
-
- if ( clearStyle !== null ) gl.clear( clearStyle );
-
- }
-
}
//
@@ -700,26 +668,6 @@ class WebXRManager extends EventDispatcher {
if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame );
- if ( isMultisample && ! msaartcSupported ) {
-
- const width = glProjLayer.textureWidth;
- const height = glProjLayer.textureHeight;
-
- state.bindFramebuffer( gl.READ_FRAMEBUFFER, glMultisampledFramebuffer );
- state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, glFramebuffer );
- // Invalidate the depth here to avoid flush of the depth data to main memory.
- gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, [ depthStyle ] );
- gl.invalidateFramebuffer( gl.DRAW_FRAMEBUFFER, [ depthStyle ] );
- gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST );
- // Invalidate the MSAA buffer because it's not needed anymore.
- gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, [ gl.COLOR_ATTACHMENT0 ] );
- state.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
- state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );
-
- state.bindFramebuffer( gl.FRAMEBUFFER, glMultisampledFramebuffer );
-
- }
-
xrFrame = null;
}
From d86e2af4bcf9e237a14bf847ebe671c2b07c2a31 Mon Sep 17 00:00:00 2001
From: Rik Cabanier
Date: Fri, 24 Sep 2021 14:48:03 -0700
Subject: [PATCH 2/8] Addressed review comments
---
src/renderers/WebGLMultisampleRenderTarget.js | 9 ++--
src/renderers/WebGLRenderTarget.js | 5 +--
src/renderers/WebGLRenderer.js | 16 +++----
src/renderers/webgl/WebGLExtensions.js | 2 +-
src/renderers/webgl/WebGLTextures.js | 45 ++++++++++---------
src/renderers/webxr/WebXRManager.js | 4 +-
6 files changed, 41 insertions(+), 40 deletions(-)
diff --git a/src/renderers/WebGLMultisampleRenderTarget.js b/src/renderers/WebGLMultisampleRenderTarget.js
index 9b64d91a14d7e5..8898923e5bbc7a 100644
--- a/src/renderers/WebGLMultisampleRenderTarget.js
+++ b/src/renderers/WebGLMultisampleRenderTarget.js
@@ -7,8 +7,9 @@ class WebGLMultisampleRenderTarget extends WebGLRenderTarget {
super( width, height, options );
this.samples = 4;
- this.useMultisampleRenderToTexture = ( options.useMultisampleRenderToTexture !== undefined ) ? options.useMultisampleRenderToTexture : false;
- this.useMultisampleRenderbuffer = this.useMultisampleRenderToTexture === false;
+
+ this.useMultisampledRenderToTexture = ( options.useMultisampledRenderToTexture !== undefined ) ? options.useMultisampledRenderToTexture : false;
+ this.useMultisampledRenderbuffer = this.useMultisampledRenderToTexture === false;
}
@@ -17,8 +18,8 @@ class WebGLMultisampleRenderTarget extends WebGLRenderTarget {
super.copy.call( this, source );
this.samples = source.samples;
- this.useMultisampleRenderToTexture = source.useMultisampleRenderToTexture;
- this.useMultisampleRenderbuffer = source.useMultisampleRenderbuffer;
+ this.useMultisampledRenderToTexture = source.useMultisampledRenderToTexture;
+ this.useMultisampledRenderbuffer = source.useMultisampledRenderbuffer;
return this;
diff --git a/src/renderers/WebGLRenderTarget.js b/src/renderers/WebGLRenderTarget.js
index 22833d3c2c069f..0a7fe39e89c688 100644
--- a/src/renderers/WebGLRenderTarget.js
+++ b/src/renderers/WebGLRenderTarget.js
@@ -36,9 +36,8 @@ class WebGLRenderTarget extends EventDispatcher {
this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false;
this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
this.ignoreDepthForMultisampleCopy = options.ignoreDepth !== undefined ? options.ignoreDepth : true;
- this.hasExternalTextures = false;
- this.useMultisampleRenderToTexture = false;
- this.useMultisampleRenderbuffer = false;
+ this.useMultisampledRenderToTexture = false;
+ this.useMultisampledRenderbuffer = false;
}
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index 5dccaebd7cf2b1..eddb9a8f929589 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -1281,7 +1281,7 @@ function WebGLRenderer( parameters = {} ) {
magFilter: NearestFilter,
wrapS: ClampToEdgeWrapping,
wrapT: ClampToEdgeWrapping,
- useMultisampleRenderToTexture: extensions.has( 'EXT_multisampled_render_to_texture' )
+ useMultisampledRenderToTexture: extensions.has( 'EXT_multisampled_render_to_texture' )
} );
}
@@ -1871,25 +1871,25 @@ function WebGLRenderer( parameters = {} ) {
hasNewExternalTextures = true;
properties.get( renderTarget.texture ).__webglTexture = options.colorTexture;
- renderTarget.autoAllocateDepthBuffer = options.depthTexture === undefined;
+ renderTargetProperties.__autoAllocateDepthBuffer = options.depthTexture === undefined;
- if ( ! renderTarget.autoAllocateDepthBuffer ) {
+ if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) {
properties.get( renderTarget.depthTexture ).__webglTexture = options.depthTexture;
// The multisample_render_to_texture extension doesn't work properly if there
// are midframe flushes and an external depth buffer. Disable use of the extension.
- if ( renderTarget.useMultisampleRenderToTexture ) {
+ if ( renderTarget.useMultisampledRenderToTexture ) {
console.warn( 'render-to-texture extension was disabled because an external texture was provided' );
- renderTarget.useMultisampleRenderToTexture = false;
- renderTarget.useMultisampleRenderbuffer = true;
+ renderTarget.useMultisampledRenderToTexture = false;
+ renderTarget.useMultisampledRenderbuffer = true;
}
}
- renderTarget.hasExternalTextures = true;
+ renderTargetProperties.__hasExternalTextures = true;
}
@@ -1933,7 +1933,7 @@ function WebGLRenderer( parameters = {} ) {
framebuffer = __webglFramebuffer[ activeCubeFace ];
isCube = true;
- } else if ( renderTarget.useMultisampleRenderbuffer ) {
+ } else if ( renderTarget.useMultisampledRenderbuffer ) {
framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;
diff --git a/src/renderers/webgl/WebGLExtensions.js b/src/renderers/webgl/WebGLExtensions.js
index ef08b5d0d854bf..0f3d8f861e644f 100644
--- a/src/renderers/webgl/WebGLExtensions.js
+++ b/src/renderers/webgl/WebGLExtensions.js
@@ -65,12 +65,12 @@ function WebGLExtensions( gl ) {
getExtension( 'OES_element_index_uint' );
getExtension( 'OES_vertex_array_object' );
getExtension( 'ANGLE_instanced_arrays' );
+ getExtension( 'EXT_multisampled_render_to_texture' );
}
getExtension( 'OES_texture_float_linear' );
getExtension( 'EXT_color_buffer_half_float' );
- getExtension( 'EXT_multisampled_render_to_texture' );
},
diff --git a/src/renderers/webgl/WebGLTextures.js b/src/renderers/webgl/WebGLTextures.js
index ec948826977fa5..6e1171eaf389e9 100644
--- a/src/renderers/webgl/WebGLTextures.js
+++ b/src/renderers/webgl/WebGLTextures.js
@@ -9,8 +9,8 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const maxCubemapSize = capabilities.maxCubemapSize;
const maxTextureSize = capabilities.maxTextureSize;
const maxSamples = capabilities.maxSamples;
- const msaartcSupported = extensions.has( 'EXT_multisampled_render_to_texture' );
- const msaaExt = msaartcSupported ? extensions.get( 'EXT_multisampled_render_to_texture' ) : undefined;
+ const MultisampledRenderToTextureSupported = extensions.has( 'EXT_multisampled_render_to_texture' );
+ const MultisampledRenderToTextureExtension = MultisampledRenderToTextureSupported ? extensions.get( 'EXT_multisampled_render_to_texture' ) : undefined;
const _videoTextures = new WeakMap();
let _canvas;
@@ -860,8 +860,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const glFormat = utils.convert( texture.format );
const glType = utils.convert( texture.type );
const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding );
+ const renderTargetProperties = properties.get( renderTarget );
- if ( ! renderTarget.hasExternalTextures ) {
+ if ( ! renderTargetProperties.__hasExternalTextures ) {
if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) {
@@ -876,9 +877,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
}
state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
- if ( renderTarget.useMultisampleRenderToTexture ) {
+ if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) );
+ MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) );
} else {
@@ -900,7 +901,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
let glInternalFormat = _gl.DEPTH_COMPONENT16;
- if ( isMultisample || renderTarget.useMultisampleRenderToTexture ) {
+ if ( isMultisample || renderTarget.useMultisampledRenderToTexture ) {
const depthTexture = renderTarget.depthTexture;
@@ -920,9 +921,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const samples = getRenderTargetSamples( renderTarget );
- if ( renderTarget.useMultisampleRenderToTexture ) {
+ if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
} else {
@@ -942,13 +943,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const samples = getRenderTargetSamples( renderTarget );
- if ( isMultisample && renderTarget.useMultisampleRenderbuffer ) {
+ if ( isMultisample && renderTarget.useMultisampledRenderbuffer ) {
_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );
- } else if ( renderTarget.useMultisampleRenderToTexture ) {
+ } else if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );
+ MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height );
} else {
@@ -969,13 +970,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding );
const samples = getRenderTargetSamples( renderTarget );
- if ( isMultisample && renderTarget.useMultisampleRenderbuffer ) {
+ if ( isMultisample && renderTarget.useMultisampledRenderbuffer ) {
_gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
- } else if ( renderTarget.useMultisampleRenderToTexture ) {
+ } else if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
+ MultisampledRenderToTextureExtension.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height );
} else {
@@ -1021,9 +1022,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
if ( renderTarget.depthTexture.format === DepthFormat ) {
- if ( renderTarget.useMultisampleRenderToTexture ) {
+ if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+ MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
} else {
@@ -1033,9 +1034,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
} else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {
- if ( renderTarget.useMultisampleRenderToTexture ) {
+ if ( renderTarget.useMultisampledRenderToTexture ) {
- msaaExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
+ MultisampledRenderToTextureExtension.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples );
} else {
@@ -1057,7 +1058,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
const renderTargetProperties = properties.get( renderTarget );
const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
- if ( renderTarget.depthTexture && ! renderTarget.autoAllocateDepthBuffer ) {
+ if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) {
if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );
@@ -1190,7 +1191,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
}
- } else if ( renderTarget.useMultisampleRenderbuffer ) {
+ } else if ( renderTarget.useMultisampledRenderbuffer ) {
if ( isWebGL2 ) {
@@ -1345,7 +1346,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function updateMultisampleRenderTarget( renderTarget ) {
- if ( renderTarget.useMultisampleRenderbuffer ) {
+ if ( renderTarget.useMultisampledRenderbuffer ) {
if ( isWebGL2 ) {
@@ -1398,7 +1399,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function getRenderTargetSamples( renderTarget ) {
- return ( isWebGL2 && ( renderTarget.useMultisampleRenderbuffer || renderTarget.useMultisampleRenderToTexture ) ) ?
+ return ( isWebGL2 && ( renderTarget.useMultisampledRenderbuffer || renderTarget.useMultisampledRenderToTexture ) ) ?
Math.min( maxSamples, renderTarget.samples ) : 0;
}
diff --git a/src/renderers/webxr/WebXRManager.js b/src/renderers/webxr/WebXRManager.js
index 58557188eb3eff..31fb41868af8e5 100644
--- a/src/renderers/webxr/WebXRManager.js
+++ b/src/renderers/webxr/WebXRManager.js
@@ -31,7 +31,7 @@ class WebXRManager extends EventDispatcher {
let referenceSpace = null;
let referenceSpaceType = 'local-floor';
- const msaartcSupported = renderer.extensions.has( 'EXT_multisampled_render_to_texture' );
+ const MultisampledRenderToTextureSupported = renderer.extensions.has( 'EXT_multisampled_render_to_texture' );
let pose = null;
let glBinding = null;
@@ -298,7 +298,7 @@ class WebXRManager extends EventDispatcher {
depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),
stencilBuffer: attributes.stencil,
ignoreDepth: glProjLayer.ignoreDepthValues,
- useMultisampleRenderToTexture: msaartcSupported,
+ useMultisampledRenderToTexture: MultisampledRenderToTextureSupported,
} );
} else {
From 08c8767abfa09d9b6e31d49ca960784ff04f22f1 Mon Sep 17 00:00:00 2001
From: Rik Cabanier
Date: Mon, 4 Oct 2021 12:24:14 -0700
Subject: [PATCH 3/8] Merge branch 'dev' of git://github.com/mrdoob/three.js
into mrdoob-dev
---
docs/api/ar/cameras/CubeCamera.html | 3 +-
docs/api/en/cameras/CubeCamera.html | 3 +-
docs/api/en/lights/AmbientLight.html | 1 -
docs/api/en/materials/ShaderMaterial.html | 1 -
docs/api/ko/cameras/CubeCamera.html | 3 +-
docs/api/zh/cameras/CubeCamera.html | 3 +-
docs/api/zh/extras/core/Curve.html | 77 +--
docs/api/zh/lights/AmbientLight.html | 1 -
docs/api/zh/materials/ShaderMaterial.html | 1 -
docs/index.html | 2 +-
.../ko/buildTools/Testing-with-NPM.html | 6 +-
.../ko/introduction/Animation-system.html | 7 +-
docs/manual/ko/introduction/FAQ.html | 6 +-
.../How-to-dispose-of-objects.html | 8 +-
.../How-to-run-things-locally.html | 7 +-
docs/manual/ko/introduction/Installation.html | 8 +-
docs/manual/ko/introduction/Useful-links.html | 2 +-
editor/css/main.css | 22 +-
editor/js/Menubar.File.js | 4 +-
editor/js/Player.js | 6 +
editor/js/Resizer.js | 14 +-
editor/sw.js | 2 +-
examples/files.json | 2 -
examples/js/exporters/USDZExporter.js | 12 +-
examples/js/geometries/ParametricGeometry.js | 115 ++++
examples/js/geometries/TextGeometry.js | 49 ++
examples/js/loaders/FontLoader.js | 183 +++++
examples/js/loaders/GLTFLoader.js | 131 ++--
examples/js/loaders/RGBMLoader.js | 9 +-
examples/js/loaders/SVGLoader.js | 1 +
examples/js/postprocessing/SSRPass.js | 3 -
examples/js/postprocessing/SSRrPass.js | 5 +-
examples/js/utils/PackedPhongMaterial.js | 6 +-
examples/jsm/exporters/USDZExporter.js | 13 +-
examples/jsm/geometries/ParametricGeometry.js | 2 +-
examples/jsm/geometries/TextGeometry.js | 2 +-
examples/jsm/loaders/GLTFLoader.js | 79 ++-
examples/jsm/loaders/RGBMLoader.js | 8 +-
examples/jsm/loaders/SVGLoader.js | 1 +
examples/jsm/renderers/nodes/Nodes.js | 8 +-
examples/jsm/renderers/nodes/ShaderNode.js | 193 ++++++
.../renderers/nodes/accessors/CameraNode.js | 32 +-
.../accessors/ModelViewProjectionNode.js | 6 +-
.../renderers/nodes/accessors/NormalNode.js | 52 +-
.../renderers/nodes/accessors/Object3DNode.js | 42 +-
.../renderers/nodes/accessors/PointUVNode.js | 7 +-
.../renderers/nodes/accessors/PositionNode.js | 73 +-
.../nodes/accessors/ReferenceNode.js | 4 +-
.../jsm/renderers/nodes/core/AttributeNode.js | 23 +-
examples/jsm/renderers/nodes/core/CodeNode.js | 6 +-
.../jsm/renderers/nodes/core/ConstNode.js | 7 +-
.../renderers/nodes/core/ExpressionNode.js | 11 +-
.../renderers/nodes/core/FunctionCallNode.js | 7 +-
examples/jsm/renderers/nodes/core/Node.js | 21 +
.../jsm/renderers/nodes/core/PropertyNode.js | 10 +-
.../jsm/renderers/nodes/core/StructVarNode.js | 4 +-
examples/jsm/renderers/nodes/core/TempNode.js | 16 +-
examples/jsm/renderers/nodes/core/VarNode.js | 4 +-
examples/jsm/renderers/nodes/core/VaryNode.js | 4 +-
.../renderers/nodes/display/ColorSpaceNode.js | 28 +-
.../renderers/nodes/display/NormalMapNode.js | 33 +-
.../nodes/lights/LightContextNode.js | 8 +-
.../jsm/renderers/nodes/lights/LightNode.js | 6 +-
.../jsm/renderers/nodes/lights/LightsNode.js | 4 +-
examples/jsm/renderers/nodes/math/MathNode.js | 8 +-
.../jsm/renderers/nodes/math/OperatorNode.js | 6 +-
.../renderers/nodes/procedural/CheckerNode.js | 38 ++
.../jsm/renderers/nodes/utils/JoinNode.js | 6 +-
.../jsm/renderers/nodes/utils/SplitNode.js | 22 +-
.../nodes/utils/SpriteSheetUVNode.js | 53 +-
.../renderers/webgl/nodes/WebGLNodeBuilder.js | 16 +-
.../webgpu/nodes/WebGPUNodeBuilder.js | 34 +-
examples/models/gltf/SittingBox.glb | Bin 422392 -> 0 bytes
.../screenshots/webgl_animation_cloth.jpg | Bin 38837 -> 0 bytes
.../screenshots/webgl_animation_multiple.jpg | Bin 13670 -> 13998 bytes
.../screenshots/webgl_morphtargets_face.jpg | Bin 10360 -> 9178 bytes
.../screenshots/webgl_shading_physical.jpg | Bin 30817 -> 0 bytes
examples/screenshots/webgpu_sandbox.jpg | Bin 21592 -> 6702 bytes
examples/tags.json | 2 -
.../textures/patterns/bright_squares256.png | Bin 26698 -> 0 bytes
.../textures/patterns/circuit_pattern.png | Bin 5417 -> 0 bytes
examples/textures/patterns/readme.txt | 6 -
examples/webgl_animation_cloth.html | 629 ------------------
examples/webgl_morphtargets_face.html | 29 +-
examples/webgl_shading_physical.html | 363 ----------
examples/webgpu_sandbox.html | 4 +-
package.json | 2 +-
src/constants.js | 2 +-
src/core/Object3D.js | 5 +-
src/objects/SkinnedMesh.js | 2 +-
src/renderers/WebGLRenderer.js | 35 +-
test/e2e/puppeteer.js | 7 +-
92 files changed, 1137 insertions(+), 1549 deletions(-)
create mode 100644 examples/js/geometries/ParametricGeometry.js
create mode 100644 examples/js/geometries/TextGeometry.js
create mode 100644 examples/js/loaders/FontLoader.js
create mode 100644 examples/jsm/renderers/nodes/ShaderNode.js
create mode 100644 examples/jsm/renderers/nodes/procedural/CheckerNode.js
delete mode 100644 examples/models/gltf/SittingBox.glb
delete mode 100644 examples/screenshots/webgl_animation_cloth.jpg
delete mode 100644 examples/screenshots/webgl_shading_physical.jpg
delete mode 100644 examples/textures/patterns/bright_squares256.png
delete mode 100644 examples/textures/patterns/circuit_pattern.png
delete mode 100644 examples/textures/patterns/readme.txt
delete mode 100644 examples/webgl_animation_cloth.html
delete mode 100644 examples/webgl_shading_physical.html
diff --git a/docs/api/ar/cameras/CubeCamera.html b/docs/api/ar/cameras/CubeCamera.html
index 034bb7cd57d800..29ef9769e87ea7 100644
--- a/docs/api/ar/cameras/CubeCamera.html
+++ b/docs/api/ar/cameras/CubeCamera.html
@@ -41,8 +41,7 @@ مثال التعليمة البرمجية
أمثلة (Examples)
- [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
- [example:webgl_shading_physical shading / physical ]
+ [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
المنشئ (Constructor)
diff --git a/docs/api/en/cameras/CubeCamera.html b/docs/api/en/cameras/CubeCamera.html
index e178e9dfd547a1..b8c0e9013f1dde 100644
--- a/docs/api/en/cameras/CubeCamera.html
+++ b/docs/api/en/cameras/CubeCamera.html
@@ -41,8 +41,7 @@ Code Example
Examples
- [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
- [example:webgl_shading_physical shading / physical ]
+ [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
Constructor
diff --git a/docs/api/en/lights/AmbientLight.html b/docs/api/en/lights/AmbientLight.html
index b3b588bb0e6b52..d70d2fe8b6670d 100644
--- a/docs/api/en/lights/AmbientLight.html
+++ b/docs/api/en/lights/AmbientLight.html
@@ -26,7 +26,6 @@ Code Example
Examples
- [example:webgl_animation_cloth animation / cloth ]
[example:webgl_animation_skinning_blending animation / skinning / blending ]
diff --git a/docs/api/en/materials/ShaderMaterial.html b/docs/api/en/materials/ShaderMaterial.html
index f972c5d85463ae..543e87755ebd96 100644
--- a/docs/api/en/materials/ShaderMaterial.html
+++ b/docs/api/en/materials/ShaderMaterial.html
@@ -91,7 +91,6 @@ Code Example
Examples
- [example:webgl_animation_cloth webgl / animation / cloth ]
[example:webgl_buffergeometry_custom_attributes_particles webgl / buffergeometry / custom / attributes / particles]
[example:webgl_buffergeometry_selective_draw webgl / buffergeometry / selective / draw]
[example:webgl_custom_attributes webgl / custom / attributes]
diff --git a/docs/api/ko/cameras/CubeCamera.html b/docs/api/ko/cameras/CubeCamera.html
index e32dab186e4e04..5070b66457a2d0 100644
--- a/docs/api/ko/cameras/CubeCamera.html
+++ b/docs/api/ko/cameras/CubeCamera.html
@@ -41,8 +41,7 @@
코드 예제
예제
- [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
- [example:webgl_shading_physical shading / physical ]
+ [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
생성자
diff --git a/docs/api/zh/cameras/CubeCamera.html b/docs/api/zh/cameras/CubeCamera.html
index 5f7743e59047a6..532f85f0dc7db3 100644
--- a/docs/api/zh/cameras/CubeCamera.html
+++ b/docs/api/zh/cameras/CubeCamera.html
@@ -41,8 +41,7 @@ 代码示例
例子
- [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
- [example:webgl_shading_physical shading / physical ]
+ [example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
构造器
diff --git a/docs/api/zh/extras/core/Curve.html b/docs/api/zh/extras/core/Curve.html
index 1e008e54822738..cd5df8aa155c89 100644
--- a/docs/api/zh/extras/core/Curve.html
+++ b/docs/api/zh/extras/core/Curve.html
@@ -10,8 +10,8 @@
[name]
- An abstract base class for creating a [name] object that contains methods for interpolation.
- For an array of [name]s see [page:CurvePath].
+ 用于创建包含插值方法的[name]对象的抽象基类。
+ 有关[name]的数组,请参见[page:CurvePath]。
Constructor
@@ -19,102 +19,93 @@ Constructor
[name]()
- This constructor creates a new [name].
+ 创建一个 [name].
- Properties
+ 属性
[property:Integer arcLengthDivisions]
- This value determines the amount of divisions when calculating the cumulative segment lengths of a curve via [page:.getLengths].
- To ensure precision when using methods like [page:.getSpacedPoints], it is recommended to increase [page:.arcLengthDivisions] if the curve is very large. Default is 200.
+ 确定[page:.GetLength]计算曲线的累积分段长度时的分段量。
+ 为确保[page:.getSpacedPoints]等方法时的精度,如果曲线非常大,建议增加[page:.arcLengthDivisions]。默认值为200
- Methods
+ 方法
[method:Vector getPoint]( [param:Float t], [param:Vector optionalTarget] )
- [page:Float t] - A position on the curve. Must be in the range [ 0, 1 ].
- [page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
- otherwise a new Vector will be created.
+ [page:Float t] - 曲线上的位置。必须在[0,1]范围内
+ [page:Vector optionalTarget] — (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。
- Returns a vector for a given position on the curve.
+ 返回曲线上给定位置的点。
[method:Vector getPointAt]( [param:Float u], [param:Vector optionalTarget] )
- [page:Float u] - A position on the curve according to the arc length. Must be in the range [ 0, 1 ].
- [page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
- otherwise a new Vector will be created.
+ [page:Float u] - 根据弧长在曲线上的位置。必须在范围[0,1]内。
+ [page:Vector optionalTarget] — (可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。
- Returns a vector for a given position on the curve according to the arc length.
+ 根据弧长返回曲线上给定位置的点。
[method:Array getPoints]( [param:Integer divisions] )
- divisions -- number of pieces to divide the curve into. Default is *5*.
-
- Returns a set of divisions + 1 points using getPoint( t ).
+ divisions -- 要将曲线划分为的分段数。默认是 *5*.
+ 使用getPoint(t)返回一组divisions+1的点
[method:Array getSpacedPoints]( [param:Integer divisions] )
- divisions -- number of pieces to divide the curve into. Default is *5*.
+ divisions -- 要将曲线划分为的分段数。默认是 *5*.
- Returns a set of divisions + 1 equi-spaced points using getPointAt( u ).
+ 使用getPointAt(u)返回一个分段+1的等距点的数组。
[method:Float getLength]()
- Get total curve arc length.
+ 获取总曲线弧长。
[method:Array getLengths]( [param:Integer divisions] )
- Get list of cumulative segment lengths.
+ 获取累积段长度的列表。
[method:null updateArcLengths]()
- Update the cumlative segment distance cache.
+ 更新累积段距离缓存。
[method:Float getUtoTmapping]( [param:Float u], [param:Float distance] )
- Given u in the range ( 0 .. 1 ), returns [page:Float t] also in the range ( 0 .. 1 ).
- u and t can then be used to give you points which are equidistant from the ends of the curve,
- using [page:.getPoint].
+ 给定范围(0..1)内的u,返回范围(0..1)内的[page:Float t],
+ 然后可以用t来使用 [page:.getPoint]给出与曲线末端等距的点。
[method:Vector getTangent]( [param:Float t], [param:Vector optionalTarget] )
- [page:Float t] - A position on the curve. Must be in the range [ 0, 1 ].
- [page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
- otherwise a new Vector will be created.
+ [page:Float t] -在曲线上的点,必须在范围 [ 0, 1 ].
+ [page:Vector optionalTarget] — (可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。
- Returns a unit vector tangent at t. If the derived curve does not implement its
- tangent derivation, two points a small delta apart will be used to find its gradient
- which seems to give a reasonable approximation.
+ 返回t处的单位向量切线。如果派生曲线未实现其
+ 切线求导,将使用相距一个小三角形的两个点来求与其实际梯度的近似值
[method:Vector getTangentAt]( [param:Float u], [param:Vector optionalTarget] )
- [page:Float u] - A position on the curve according to the arc length. Must be in the range [ 0, 1 ].
- [page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
- otherwise a new Vector will be created.
-
- Returns tangent at a point which is equidistant to the ends of the curve from the
- point given in [page:.getTangent].
+ [page:Float u] - 根据弧长在曲线上的位置,必须在范围[ 0, 1 ]。
+ [page:Vector optionalTarget] —(可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。
+ 返回一个点处的切线,该点与 [page:.getTangent]中给定的曲线的端点距离相等
[method:Object computeFrenetFrames]( [param:Integer segments], [param:Boolean closed] )
- Generates the Frenet Frames. Requires a curve definition in 3D space. Used in geometries like [page:TubeGeometry] or [page:ExtrudeGeometry].
+ 生成Frenet帧。需要三维空间中的曲线定义。用于[page:TubeGeometry]或[page:ExtradeGeometry]等几何图形。
[method:Curve clone]()
- Creates a clone of this instance.
+ 创建此实例的克隆。
[method:Curve copy]( [param:Curve source] )
- Copies another [name] object to this instance.
+ 将另一个[name]对象复制到此实例。
[method:Object toJSON]()
- Returns a JSON object representation of this instance.
+ 返回此实例的JSON对象表示形式。
[method:Curve fromJSON]( [param:Object json] )
- Copies the data from the given JSON object to this instance.
+ 将给定的JSON数据复制到此实例。
Source
diff --git a/docs/api/zh/lights/AmbientLight.html b/docs/api/zh/lights/AmbientLight.html
index ca37532598fcec..890c290d3a1189 100644
--- a/docs/api/zh/lights/AmbientLight.html
+++ b/docs/api/zh/lights/AmbientLight.html
@@ -26,7 +26,6 @@ 代码示例
例子
- [example:webgl_animation_cloth animation / cloth ]
[example:webgl_animation_skinning_blending animation / skinning / blending ]
diff --git a/docs/api/zh/materials/ShaderMaterial.html b/docs/api/zh/materials/ShaderMaterial.html
index 7fe12bae441285..75c1d35eb42e20 100644
--- a/docs/api/zh/materials/ShaderMaterial.html
+++ b/docs/api/zh/materials/ShaderMaterial.html
@@ -81,7 +81,6 @@ 代码示例
例子
- [example:webgl_animation_cloth webgl / animation / cloth ]
[example:webgl_buffergeometry_custom_attributes_particles webgl / buffergeometry / custom / attributes / particles]
[example:webgl_buffergeometry_selective_draw webgl / buffergeometry / selective / draw]
[example:webgl_custom_attributes webgl / custom / attributes]
diff --git a/docs/index.html b/docs/index.html
index be0e2dbdd40ba5..69c5a1b644cde9 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -18,7 +18,7 @@
diff --git a/docs/manual/ko/buildTools/Testing-with-NPM.html b/docs/manual/ko/buildTools/Testing-with-NPM.html
index 6d0a198f995256..6faa49f6a9051b 100644
--- a/docs/manual/ko/buildTools/Testing-with-NPM.html
+++ b/docs/manual/ko/buildTools/Testing-with-NPM.html
@@ -131,7 +131,7 @@ three.js 추가
$ npm install three@0.84.0 --save
(이 예제에서는 0.84.0 버전을 사용했습니다.). --save 는 dev 설정이 아닌 이 프로젝트의 의존성으로 추가하는 명령어입니다.
- [link:https://www.npmjs.org/doc/json.html 여기]에서 더 많은 내용을 확인하세요.
+ 여기([link:https://www.npmjs.org/doc/json.html link])에서 더 많은 내용을 확인하세요.
@@ -181,12 +181,12 @@ 자신의 코드 추가하기
-
자신의 코드의 예상 결과가 들어있는 예제를 만들어, test/ 폴더 안에 두세요.
- [link:https://github.com/air/encounter/blob/master/test/Physics-test.js 여기]에서 진짜 프로젝트의 예제를 확인할 수 있습니다.
+ 여기([link:https://github.com/air/encounter/blob/master/test/Physics-test.js link])에서 진짜 프로젝트의 예제를 확인할 수 있습니다.
-
nodeJS에서 알아볼 수 있는, require를 사용해 기능들을 내보내기 하세요.
- [link:https://github.com/air/encounter/blob/master/js/Physics.js 여기]를 참고하세요.
+ 여기([link:https://github.com/air/encounter/blob/master/js/Physics.js link])를 참고하세요.
-
diff --git a/docs/manual/ko/introduction/Animation-system.html b/docs/manual/ko/introduction/Animation-system.html
index e4574e768c85a7..5d1c99f73947b1 100644
--- a/docs/manual/ko/introduction/Animation-system.html
+++ b/docs/manual/ko/introduction/Animation-system.html
@@ -13,15 +13,14 @@
Overview
three.js 애니메이션 시스템에서는 모델의 다양한 속성을 설정할 수 있습니다:
- [page:SkinnedMesh 스킨 및 리깅 모델], 모프타깃의 골자, 서로 다른 재질의 속성(색상, 불투명도, 참/거짓 연산),
+ [page:SkinnedMesh skinned and rigged model]의 뼈, 모프타깃, 서로 다른 재질의 속성(색상, 불투명도, 참/거짓 연산),
가시성과 변환이 그 예입니다. 애니메이션의 속성은 페이드 아웃, 페이드 아웃, 크로스페이드, 랩이 있습니다.
한 오브젝트에 대한 동시에 일어나는 다른 확대 시간 및 가중치 조절이나, 서로 다른 오브젝트간의 애니메이션도 전부 개별로 변화시킬 수 있습니다.
같은, 혹은 서로 다른 오브젝트틀간의 다양한 애니메이션도 싱크를 맞출 수 있습니다.
- 이를 한 시스템 안에 구현하기 위해서, three.js 애니메이션 시스템은
- [link:https://github.com/mrdoob/three.js/issues/6881 2015년에 완전히 변경]
- (지난 정보임에 주의하세요!)되었으며, 현재는 Unity/Unreal Engine 4와 유사한 구조를 가지고 있습니다.
+ 이를 한 시스템 안에 구현하기 위해서, three.js 애니메이션 시스템은 2015년에 완전히 변경되었으며([link:https://github.com/mrdoob/three.js/issues/6881 Link])
+ 되었으며(지난 정보에 주의하세요!), 현재는 Unity/Unreal Engine 4와 유사한 구조를 가지고 있습니다.
이 페이지에서는 어떻게 시스템 메인 컴포넌트가 구성되고 동작되는지를 간단하게 알아보겠습니다.
diff --git a/docs/manual/ko/introduction/FAQ.html b/docs/manual/ko/introduction/FAQ.html
index 9febcb2c1adfa8..495c37bd5d2c59 100644
--- a/docs/manual/ko/introduction/FAQ.html
+++ b/docs/manual/ko/introduction/FAQ.html
@@ -15,7 +15,7 @@ 어떤 3D 모델 포맷이 가장 잘 지원되나요?
불러오기 및 내보내기 용으로 추천되는 포맷은 glTF (GL Transmission Format)입니다. glTF는 런타임 에셋 딜리버리에 초점이 맞추어져 있기 때문에, 전송에 적합하고 로딩이 빠릅니다.
- three.js 널리 쓰이는 포맷인 FBX, Collada 나 OBJ 도 지원합니다. 하지만 첫 프로젝트에서는 glTF 기반의 워크플로우로 작업해야 합니다. 더 자세한 내용은, [link:#manual/introduction/Loading-3D-models 3D 모델 로딩]을 참고하세요.
+ three.js 널리 쓰이는 포맷인 FBX, Collada 나 OBJ 도 지원합니다. 하지만 첫 프로젝트에서는 glTF 기반의 워크플로우로 작업해야 합니다. 더 자세한 내용은, [link:#manual/introduction/Loading-3D-models loading 3D models]를 참고하세요.
@@ -41,8 +41,8 @@ 화면 확대 정도가 리사이징 시에 유지될 수 있나요?
화면 높이를 특정 비율로 늘리면, 모든 가시 높이와 거리가 같은 비율로 증가해야 합니다.
- 이는 카메라 위치를 변경하는 것으로는 불가능합니다. 대신에 카메라 field-of-view를 변경해야합니다..
- [link:http://jsfiddle.net/Q4Jpu/ 예제].
+ 이는 카메라 위치를 변경하는 것으로는 불가능합니다. 대신에 카메라 field-of-view를 변경해야합니다.
+ [link:http://jsfiddle.net/Q4Jpu/ Example].
왜 오브젝트 일부가 안 보일까요?
diff --git a/docs/manual/ko/introduction/How-to-dispose-of-objects.html b/docs/manual/ko/introduction/How-to-dispose-of-objects.html
index 7ceecabdbc89c2..1d712d65470cf0 100644
--- a/docs/manual/ko/introduction/How-to-dispose-of-objects.html
+++ b/docs/manual/ko/introduction/How-to-dispose-of-objects.html
@@ -21,7 +21,7 @@ 오브젝트를 폐기하는 방법([name])
기하학
- 기하학은 속성 집합으로 정의된 꼭짓점 정보를 표시하는데, *three.js*는 속성마다 하나의 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer WebGLBuffer] 유형의 대상을 만들어 내부에 저장합니다.
+ 기하학은 속성 집합으로 정의된 꼭짓점 정보를 표시하는데, *three.js*는 속성마다 하나의 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer WebGLBuffer] 유형의 대상을 만들어 내부에 저장합니다.
이러한 개체는 [page:BufferGeometry.dispose]를 호출할 때만 폐기됩니다.
만약 애플리케이션에서 기하학을 더이상 사용하지 않는다면, 이 방법을 실행하여 모든 관련 자원을 폐기하세요.
@@ -38,15 +38,15 @@ 텍스쳐
재질의 폐기는 텍스쳐에 영향을 미치지 않습니다. 이들은 분리되어 있어 하나의 텍스쳐를 여러 재질로 동시에 사용할 수 있습니다.
- [page:Texture] 인스턴스를 만들 때마다 three.js는 내부에서 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture] 인스턴스를 만듭니다.
+ [page:Texture] 인스턴스를 만들 때마다 three.js는 내부에서 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture] 인스턴스를 만듭니다.
buffer와 비슷하게, 이 오브젝트는 [page:Texture.dispose]() 호출로만 삭제가 가능합니다.
렌더링 대상
- [page:WebGLRenderTarget] 타입의 오브젝트는 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture]의 인스턴스가 할당되어 있을 뿐만 아니라,
- [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer WebGLFramebuffer]와 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer WebGLRenderbuffer] 도 할당되어,
+ [page:WebGLRenderTarget] 타입의 오브젝트는 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture]의 인스턴스가 할당되어 있을 뿐만 아니라,
+ [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer WebGLFramebuffer]와 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer WebGLRenderbuffer] 도 할당되어,
커스텀 렌더링 목표를 실체화합니다. 이러한 오브젝트는 [page:WebGLRenderTarget.dispose]()를 실행해야만 폐기할 수 있습니다.
diff --git a/docs/manual/ko/introduction/How-to-run-things-locally.html b/docs/manual/ko/introduction/How-to-run-things-locally.html
index 819b69ce231485..07ee684db11be0 100644
--- a/docs/manual/ko/introduction/How-to-run-things-locally.html
+++ b/docs/manual/ko/introduction/How-to-run-things-locally.html
@@ -43,8 +43,7 @@ 외부 파일에서 컨텐츠 불러오기
로컬 서버에서 실행
- 많은 프로그래밍 언어는 기본적으로 간단한 HTTP 서버가 설치되어 있습니다. [link:https://www.apache.org/ Apache]나 [link:https://nginx.org
- NGINX]같은 프로덕션용 정도로 갖추어져 있지는 않지만, three.js를 테스트해보기에는 충분합니다.
+ 많은 프로그래밍 언어는 기본적으로 간단한 HTTP 서버가 설치되어 있습니다. [link:https://www.apache.org/ Apache]나 [link:https://nginx.org NGINX]같은 프로덕션용 정도로 갖추어져 있지는 않지만, three.js를 테스트해보기에는 충분합니다.
유명 코드 에디터 관련 플러그인
@@ -120,7 +119,7 @@
Lighttpd
웹서버에서 실행하고자 하는 디렉토리에 lighttpd.conf라는 설정파일을 만듭니다.
- 예제는 [link:http://redmine.lighttpd.net/projects/lighttpd/wiki/TutorialConfiguration 여기]에서 확인할 수 있습니다.
+ 예제는 여기([link:http://redmine.lighttpd.net/projects/lighttpd/wiki/TutorialConfiguration link])에서 확인할 수 있습니다.
설정 파일에서, server.document-root를 서버로 쓰고자 하는 디렉토리로 설정합니다.
@@ -141,7 +140,7 @@ IIS
기본적으로 IIS는 .fbx, .obj 파일의 다운로드를 막아 놓았습니다. IIS에서 이러한 파일들이 다운로드 될 수 있도록 설정해야 합니다.
- 다른 간단한 방법으로는 Stack Overflow의 [link:http://stackoverflow.com/q/12905426/24874 이곳에서 토론]을 확인해 보세요.
+ 다른 간단한 방법으로는 Stack Overflow에서 논의된 내용([link:http://stackoverflow.com/q/12905426/24874 link])을 확인해 보세요.
diff --git a/docs/manual/ko/introduction/Installation.html b/docs/manual/ko/introduction/Installation.html
index a0f27c0f1f612f..e66b0bdc0a58ac 100644
--- a/docs/manual/ko/introduction/Installation.html
+++ b/docs/manual/ko/introduction/Installation.html
@@ -141,8 +141,7 @@ CommonJS 불러오기
대부분의 자바스크립트 번들러는 이제 ES 모듈을 기본적으로 지원하지만, 오래된 빌드 도구들은 그렇지 않을 수 있습니다.
- 이 경우에, 번들러에 ES 모듈을 인식할 수 있도록 설정해줄 수 있습니다. 예를들어 [link:http://browserify.org/
- Browserify] 는 [link:https://github.com/babel/babelify babelify] 플러그인을 불러오기만 하면 됩니다.
+ 이 경우에, 번들러에 ES 모듈을 인식할 수 있도록 설정해줄 수 있습니다. 예를들어 [link:http://browserify.org/ Browserify] 는 [link:https://github.com/babel/babelify babelify] 플러그인을 불러오기만 하면 됩니다.
maps 불러오기
@@ -159,7 +158,7 @@ maps 불러오기
이는 npm 패키지에서 주로 쓰이는 경로 작성법과 일치하고, 두 사용자군 모두에게 파일을 불러오는 데에 동일한 코드를 사용할 수 있게 해 줄 것입니다.
빌드 도구를 사용하지 않는 것을 선호하는 사용자들에게도, 간단한 JSON 맵핑을 통해 CDN이나 직접 파일 폴더에서 불러오는 것을 가능하게 해 줄 것입니다.
실험적 방법으로, [link:https://glitch.com/edit/#!/three-import-map?path=index.html import map
- 예제]처럼 map polyfill과 함께 import 해서 더 간단하게 사용해볼 수도 있습니다.
+ example]처럼 map polyfill과 함께 import 해서 더 간단하게 사용해볼 수도 있습니다.
Node.js
@@ -170,8 +169,7 @@ Node.js
첫 번째로, three.js는 웹을 목적으로 만들어졌기때문에, Node.js에서 항상 활용 가능하다고 보증할 수 없는 브라우저와 DOM API에 의존하고 있는 까닭입니다.
- 이러한 문제들은 shims를 통해 [link:https://github.com/stackgl/headless-gl
- headless-gl]처럼 해결하거나, [page:TextureLoader] 같은 컴포넌트를 커스터마이징 해서 해결 가능합니다. 다른 DOM API는 관련된 코드가 더 복잡하게 연관되어 있어, 수정하기 더 까다롭습니다.
+ 이러한 문제들은 shims를 통해 [link:https://github.com/stackgl/headless-gl headless-gl]처럼 해결하거나, [page:TextureLoader] 같은 컴포넌트를 커스터마이징 해서 해결 가능합니다. 다른 DOM API는 관련된 코드가 더 복잡하게 연관되어 있어, 수정하기 더 까다롭습니다.
Node.js 지원을 향상시키기 위한 더 간단하고, 유지보수 가능한 pull 요청은 언제든지 환영이지만, 본인의 작업을 위한 issue 생성을 더 권장합니다.
diff --git a/docs/manual/ko/introduction/Useful-links.html b/docs/manual/ko/introduction/Useful-links.html
index 6786942cd80d9d..6fdccc23dd3c83 100644
--- a/docs/manual/ko/introduction/Useful-links.html
+++ b/docs/manual/ko/introduction/Useful-links.html
@@ -20,7 +20,7 @@ 참고 링크([name])
도움이 되는 포럼
- Three.js는 공식적으로[link:https://discourse.threejs.org/ 포럼] 과 [link:http://stackoverflow.com/tags/three.js/info Stack Overflow]에서 지원 요청을 받고 있습니다.
+ Three.js는 공식적으로 [link:https://discourse.threejs.org/ forum]과 [link:http://stackoverflow.com/tags/three.js/info Stack Overflow]에서 지원 요청을 받고 있습니다.
도움이 필요하다면, 저기로 가면 됩니다. 깃허브에서 도움 요청 이슈를 생성하지 마세요.
diff --git a/editor/css/main.css b/editor/css/main.css
index 693beac99f1319..57166ba30917cd 100644
--- a/editor/css/main.css
+++ b/editor/css/main.css
@@ -74,19 +74,21 @@ textarea, input { outline: none; } /* osx */
position: relative;
display: block;
width: 100%;
+ min-width: 300px;
}
-.TabbedPanel .Tabs .Tab {
- padding: 10px;
- text-transform: uppercase;
-}
+ .TabbedPanel .Tabs .Tab {
+ padding: 10px;
+ text-transform: uppercase;
+ }
-.TabbedPanel .Tabs .Panels {
- position: relative;
- display: block;
- width: 100%;
- height: 100%;
-}
+ .TabbedPanel .Panels {
+ position: relative;
+ display: block;
+ width: 100%;
+ height: 100%;
+ min-width: 300px;
+ }
/* Listbox */
.Listbox {
diff --git a/editor/js/Menubar.File.js b/editor/js/Menubar.File.js
index ea526c9e78964a..24860235b8e9f3 100644
--- a/editor/js/Menubar.File.js
+++ b/editor/js/Menubar.File.js
@@ -447,19 +447,17 @@ function MenubarFile( editor ) {
if ( config.getKey( 'project/editable' ) ) {
editButton = [
- '',
' var button = document.createElement( \'a\' );',
' button.href = \'https://threejs.org/editor/#file=\' + location.href.split( \'/\' ).slice( 0, - 1 ).join( \'/\' ) + \'/app.json\';',
' button.style.cssText = \'position: absolute; bottom: 20px; right: 20px; padding: 10px 16px; color: #fff; border: 1px solid #fff; border-radius: 20px; text-decoration: none;\';',
' button.target = \'_blank\';',
' button.textContent = \'EDIT\';',
' document.body.appendChild( button );',
- ''
].join( '\n' );
}
- content = content.replace( '\n\t\t\t/* edit button */\n', editButton );
+ content = content.replace( '\t\t\t/* edit button */', editButton );
toZip[ 'index.html' ] = strToU8( content );
diff --git a/editor/js/Player.js b/editor/js/Player.js
index a224fe6790c412..fce69b81578f27 100644
--- a/editor/js/Player.js
+++ b/editor/js/Player.js
@@ -21,6 +21,12 @@ function Player( editor ) {
} );
+ signals.windowResize.add( function () {
+
+ player.setSize( container.dom.clientWidth, container.dom.clientHeight );
+
+ } );
+
signals.startPlayer.add( function () {
container.setDisplay( '' );
diff --git a/editor/js/Resizer.js b/editor/js/Resizer.js
index 331fae479cbaa1..0c584f124a1554 100644
--- a/editor/js/Resizer.js
+++ b/editor/js/Resizer.js
@@ -31,13 +31,19 @@ function Resizer( editor ) {
if ( event.isPrimary === false ) return;
- const rect = dom.getBoundingClientRect();
- const x = ( document.body.offsetWidth - rect.right ) - event.movementX;
+ const offsetWidth = document.body.offsetWidth;
+ const clientX = event.clientX;
+
+ const cX = clientX < 0 ? 0 : clientX > offsetWidth ? offsetWidth : clientX;
+
+ const x = offsetWidth - cX;
dom.style.right = x + 'px';
- document.getElementById( 'sidebar' ).style.width = ( x + rect.width ) + 'px';
- document.getElementById( 'viewport' ).style.right = ( x + rect.width ) + 'px';
+ document.getElementById( 'sidebar' ).style.width = x + 'px';
+ document.getElementById( 'player' ).style.right = x + 'px';
+ document.getElementById( 'script' ).style.right = x + 'px';
+ document.getElementById( 'viewport' ).style.right = x + 'px';
signals.windowResize.dispatch();
diff --git a/editor/sw.js b/editor/sw.js
index 004dd21a9c70c4..68adb0afa505de 100644
--- a/editor/sw.js
+++ b/editor/sw.js
@@ -1,4 +1,4 @@
-// r132.1
+// r133
const cacheName = 'threejs-editor';
diff --git a/examples/files.json b/examples/files.json
index b7cc2fcd104b7c..9ea70b49c07961 100644
--- a/examples/files.json
+++ b/examples/files.json
@@ -1,6 +1,5 @@
{
"webgl": [
- "webgl_animation_cloth",
"webgl_animation_keyframes",
"webgl_animation_skinning_blending",
"webgl_animation_skinning_additive_blending",
@@ -206,7 +205,6 @@
"webgl_shaders_ocean",
"webgl_shaders_sky",
"webgl_shaders_tonemapping",
- "webgl_shading_physical",
"webgl_shadow_contact",
"webgl_shadowmap",
"webgl_shadowmap_performance",
diff --git a/examples/js/exporters/USDZExporter.js b/examples/js/exporters/USDZExporter.js
index 6c275a18eaffde..9c9e25c1261088 100644
--- a/examples/js/exporters/USDZExporter.js
+++ b/examples/js/exporters/USDZExporter.js
@@ -418,7 +418,17 @@ ${array.join( '' )}
}
- inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+ if ( material.alphaMap !== null ) {
+
+ inputs.push( `${pad}float inputs:opacity.connect = ` );
+ inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
+ samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
+
+ } else {
+
+ inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+
+ }
if ( material.isMeshPhysicalMaterial ) {
diff --git a/examples/js/geometries/ParametricGeometry.js b/examples/js/geometries/ParametricGeometry.js
new file mode 100644
index 00000000000000..d7ccc4a1b603c3
--- /dev/null
+++ b/examples/js/geometries/ParametricGeometry.js
@@ -0,0 +1,115 @@
+( function () {
+
+ /**
+ * Parametric Surfaces Geometry
+ * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
+ */
+
+ class ParametricGeometry extends THREE.BufferGeometry {
+
+ constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
+
+ super();
+ this.type = 'ParametricGeometry';
+ this.parameters = {
+ func: func,
+ slices: slices,
+ stacks: stacks
+ }; // buffers
+
+ const indices = [];
+ const vertices = [];
+ const normals = [];
+ const uvs = [];
+ const EPS = 0.00001;
+ const normal = new THREE.Vector3();
+ const p0 = new THREE.Vector3(),
+ p1 = new THREE.Vector3();
+ const pu = new THREE.Vector3(),
+ pv = new THREE.Vector3();
+
+ if ( func.length < 3 ) {
+
+ console.error( 'THREE.ParametricGeometry: Function must now modify a THREE.Vector3 as third parameter.' );
+
+ } // generate vertices, normals and uvs
+
+
+ const sliceCount = slices + 1;
+
+ for ( let i = 0; i <= stacks; i ++ ) {
+
+ const v = i / stacks;
+
+ for ( let j = 0; j <= slices; j ++ ) {
+
+ const u = j / slices; // vertex
+
+ func( u, v, p0 );
+ vertices.push( p0.x, p0.y, p0.z ); // normal
+ // approximate tangent vectors via finite differences
+
+ if ( u - EPS >= 0 ) {
+
+ func( u - EPS, v, p1 );
+ pu.subVectors( p0, p1 );
+
+ } else {
+
+ func( u + EPS, v, p1 );
+ pu.subVectors( p1, p0 );
+
+ }
+
+ if ( v - EPS >= 0 ) {
+
+ func( u, v - EPS, p1 );
+ pv.subVectors( p0, p1 );
+
+ } else {
+
+ func( u, v + EPS, p1 );
+ pv.subVectors( p1, p0 );
+
+ } // cross product of tangent vectors returns surface normal
+
+
+ normal.crossVectors( pu, pv ).normalize();
+ normals.push( normal.x, normal.y, normal.z ); // uv
+
+ uvs.push( u, v );
+
+ }
+
+ } // generate indices
+
+
+ for ( let i = 0; i < stacks; i ++ ) {
+
+ for ( let j = 0; j < slices; j ++ ) {
+
+ const a = i * sliceCount + j;
+ const b = i * sliceCount + j + 1;
+ const c = ( i + 1 ) * sliceCount + j + 1;
+ const d = ( i + 1 ) * sliceCount + j; // faces one and two
+
+ indices.push( a, b, d );
+ indices.push( b, c, d );
+
+ }
+
+ } // build geometry
+
+
+ this.setIndex( indices );
+ this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+ this.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+ this.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+
+ }
+
+ }
+
+ THREE.ParametricGeometry = ParametricGeometry;
+
+} )();
diff --git a/examples/js/geometries/TextGeometry.js b/examples/js/geometries/TextGeometry.js
new file mode 100644
index 00000000000000..094e58476ad684
--- /dev/null
+++ b/examples/js/geometries/TextGeometry.js
@@ -0,0 +1,49 @@
+( function () {
+
+ /**
+ * Text = 3D Text
+ *
+ * parameters = {
+ * font: , // font
+ *
+ * size: , // size of the text
+ * height: , // thickness to extrude text
+ * curveSegments: , // number of points on the curves
+ *
+ * bevelEnabled: , // turn on bevel
+ * bevelThickness: , // how deep into text bevel goes
+ * bevelSize: , // how far from text outline (including bevelOffset) is bevel
+ * bevelOffset: // how far from text outline does bevel start
+ * }
+ */
+
+ class TextGeometry extends THREE.ExtrudeGeometry {
+
+ constructor( text, parameters = {} ) {
+
+ const font = parameters.font;
+
+ if ( ! ( font && font.isFont ) ) {
+
+ console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );
+ return new THREE.BufferGeometry();
+
+ }
+
+ const shapes = font.generateShapes( text, parameters.size ); // translate parameters to THREE.ExtrudeGeometry API
+
+ parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults
+
+ if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
+ if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
+ if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
+ super( shapes, parameters );
+ this.type = 'TextGeometry';
+
+ }
+
+ }
+
+ THREE.TextGeometry = TextGeometry;
+
+} )();
diff --git a/examples/js/loaders/FontLoader.js b/examples/js/loaders/FontLoader.js
new file mode 100644
index 00000000000000..24521ccae3c221
--- /dev/null
+++ b/examples/js/loaders/FontLoader.js
@@ -0,0 +1,183 @@
+( function () {
+
+ class FontLoader extends THREE.Loader {
+
+ constructor( manager ) {
+
+ super( manager );
+
+ }
+
+ load( url, onLoad, onProgress, onError ) {
+
+ const scope = this;
+ const loader = new THREE.FileLoader( this.manager );
+ loader.setPath( this.path );
+ loader.setRequestHeader( this.requestHeader );
+ loader.setWithCredentials( scope.withCredentials );
+ loader.load( url, function ( text ) {
+
+ let json;
+
+ try {
+
+ json = JSON.parse( text );
+
+ } catch ( e ) {
+
+ console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );
+ json = JSON.parse( text.substring( 65, text.length - 2 ) );
+
+ }
+
+ const font = scope.parse( json );
+ if ( onLoad ) onLoad( font );
+
+ }, onProgress, onError );
+
+ }
+
+ parse( json ) {
+
+ return new Font( json );
+
+ }
+
+ } //
+
+
+ class Font {
+
+ constructor( data ) {
+
+ this.type = 'Font';
+ this.data = data;
+
+ }
+
+ generateShapes( text, size = 100 ) {
+
+ const shapes = [];
+ const paths = createPaths( text, size, this.data );
+
+ for ( let p = 0, pl = paths.length; p < pl; p ++ ) {
+
+ Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+
+ }
+
+ return shapes;
+
+ }
+
+ }
+
+ function createPaths( text, size, data ) {
+
+ const chars = Array.from( text );
+ const scale = size / data.resolution;
+ const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
+ const paths = [];
+ let offsetX = 0,
+ offsetY = 0;
+
+ for ( let i = 0; i < chars.length; i ++ ) {
+
+ const char = chars[ i ];
+
+ if ( char === '\n' ) {
+
+ offsetX = 0;
+ offsetY -= line_height;
+
+ } else {
+
+ const ret = createPath( char, scale, offsetX, offsetY, data );
+ offsetX += ret.offsetX;
+ paths.push( ret.path );
+
+ }
+
+ }
+
+ return paths;
+
+ }
+
+ function createPath( char, scale, offsetX, offsetY, data ) {
+
+ const glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
+
+ if ( ! glyph ) {
+
+ console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' );
+ return;
+
+ }
+
+ const path = new THREE.ShapePath();
+ let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
+
+ if ( glyph.o ) {
+
+ const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+
+ for ( let i = 0, l = outline.length; i < l; ) {
+
+ const action = outline[ i ++ ];
+
+ switch ( action ) {
+
+ case 'm':
+ // moveTo
+ x = outline[ i ++ ] * scale + offsetX;
+ y = outline[ i ++ ] * scale + offsetY;
+ path.moveTo( x, y );
+ break;
+
+ case 'l':
+ // lineTo
+ x = outline[ i ++ ] * scale + offsetX;
+ y = outline[ i ++ ] * scale + offsetY;
+ path.lineTo( x, y );
+ break;
+
+ case 'q':
+ // quadraticCurveTo
+ cpx = outline[ i ++ ] * scale + offsetX;
+ cpy = outline[ i ++ ] * scale + offsetY;
+ cpx1 = outline[ i ++ ] * scale + offsetX;
+ cpy1 = outline[ i ++ ] * scale + offsetY;
+ path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+ break;
+
+ case 'b':
+ // bezierCurveTo
+ cpx = outline[ i ++ ] * scale + offsetX;
+ cpy = outline[ i ++ ] * scale + offsetY;
+ cpx1 = outline[ i ++ ] * scale + offsetX;
+ cpy1 = outline[ i ++ ] * scale + offsetY;
+ cpx2 = outline[ i ++ ] * scale + offsetX;
+ cpy2 = outline[ i ++ ] * scale + offsetY;
+ path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+ break;
+
+ }
+
+ }
+
+ }
+
+ return {
+ offsetX: glyph.ha * scale,
+ path: path
+ };
+
+ }
+
+ Font.prototype.isFont = true;
+
+ THREE.Font = Font;
+ THREE.FontLoader = FontLoader;
+
+} )();
diff --git a/examples/js/loaders/GLTFLoader.js b/examples/js/loaders/GLTFLoader.js
index 690e992cc0fa41..db4b9afa66217d 100644
--- a/examples/js/loaders/GLTFLoader.js
+++ b/examples/js/loaders/GLTFLoader.js
@@ -581,9 +581,8 @@
if ( extension.clearcoatNormalTexture.scale !== undefined ) {
- const scale = extension.clearcoatNormalTexture.scale; // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-
- materialParams.clearcoatNormalScale = new THREE.Vector2( scale, - scale );
+ const scale = extension.clearcoatNormalTexture.scale;
+ materialParams.clearcoatNormalScale = new THREE.Vector2( scale, scale );
}
@@ -2110,7 +2109,28 @@
_getNodeRef( cache, index, object ) {
if ( cache.refs[ index ] <= 1 ) return object;
- const ref = object.clone();
+ const ref = object.clone(); // Propagates mappings to the cloned object, prevents mappings on the
+ // original object from being lost.
+
+ const updateMappings = ( original, clone ) => {
+
+ const mappings = this.associations.get( original );
+
+ if ( mappings != null ) {
+
+ this.associations.set( clone, mappings );
+
+ }
+
+ for ( const [ i, child ] of original.children.entries() ) {
+
+ updateMappings( child, clone.children[ i ] );
+
+ }
+
+ };
+
+ updateMappings( object, ref );
ref.name += '_instance_' + cache.uses[ index ] ++;
return ref;
@@ -2488,28 +2508,12 @@
const URL = self.URL || self.webkitURL;
let sourceURI = source.uri || '';
let isObjectURL = false;
- let hasAlpha = true;
- const isJPEG = sourceURI.search( /\.jpe?g($|\?)/i ) > 0 || sourceURI.search( /^data\:image\/jpeg/ ) === 0;
- if ( source.mimeType === 'image/jpeg' || isJPEG ) hasAlpha = false;
if ( source.bufferView !== undefined ) {
// Load binary image data from bufferView, if provided.
sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) {
- if ( source.mimeType === 'image/png' ) {
-
- // Inspect the PNG 'IHDR' chunk to determine whether the image could have an
- // alpha channel. This check is conservative — the image could have an alpha
- // channel with all values == 1, and the indexed type (colorType == 3) only
- // sometimes contains alpha.
- //
- // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
- const colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false );
- hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;
-
- }
-
isObjectURL = true;
const blob = new Blob( [ bufferView ], {
type: source.mimeType
@@ -2557,9 +2561,7 @@
}
texture.flipY = false;
- if ( textureDef.name ) texture.name = textureDef.name; // When there is definitely no alpha channel in the texture, set THREE.RGBFormat to save space.
-
- if ( ! hasAlpha ) texture.format = THREE.RGBFormat;
+ if ( textureDef.name ) texture.name = textureDef.name;
const samplers = json.samplers || {};
const sampler = samplers[ textureDef.sampler ] || {};
texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
@@ -2567,8 +2569,7 @@
texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
parser.associations.set( texture, {
- type: 'textures',
- index: textureIndex
+ textures: textureIndex
} );
return texture;
@@ -2638,7 +2639,7 @@
const geometry = mesh.geometry;
let material = mesh.material;
- const useVertexTangents = geometry.attributes.tangent !== undefined;
+ const useDerivativeTangents = geometry.attributes.tangent === undefined;
const useVertexColors = geometry.attributes.color !== undefined;
const useFlatShading = geometry.attributes.normal === undefined;
@@ -2680,11 +2681,11 @@
} // Clone the material if it will be modified
- if ( useVertexTangents || useVertexColors || useFlatShading ) {
+ if ( useDerivativeTangents || useVertexColors || useFlatShading ) {
let cacheKey = 'ClonedMaterial:' + material.uuid + ':';
if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
- if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
+ if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:';
if ( useVertexColors ) cacheKey += 'vertex-colors:';
if ( useFlatShading ) cacheKey += 'flat-shading:';
let cachedMaterial = this.cache.get( cacheKey );
@@ -2695,7 +2696,7 @@
if ( useVertexColors ) cachedMaterial.vertexColors = true;
if ( useFlatShading ) cachedMaterial.flatShading = true;
- if ( useVertexTangents ) {
+ if ( useDerivativeTangents ) {
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1;
@@ -2832,13 +2833,13 @@
if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
- pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-
- materialParams.normalScale = new THREE.Vector2( 1, - 1 );
+ pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
+ materialParams.normalScale = new THREE.Vector2( 1, 1 );
if ( materialDef.normalTexture.scale !== undefined ) {
- materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale );
+ const scale = materialDef.normalTexture.scale;
+ materialParams.normalScale.set( scale, scale );
}
@@ -2888,8 +2889,7 @@
if ( material.emissiveMap ) material.emissiveMap.encoding = THREE.sRGBEncoding;
assignExtrasToUserData( material, materialDef );
parser.associations.set( material, {
- type: 'materials',
- index: materialIndex
+ materials: materialIndex
} );
if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
return material;
@@ -3082,6 +3082,15 @@
}
+ for ( let i = 0, il = meshes.length; i < il; i ++ ) {
+
+ parser.associations.set( meshes[ i ], {
+ meshes: meshIndex,
+ primitives: i
+ } );
+
+ }
+
if ( meshes.length === 1 ) {
return meshes[ 0 ];
@@ -3089,6 +3098,9 @@
}
const group = new THREE.Group();
+ parser.associations.set( group, {
+ meshes: meshIndex
+ } );
for ( let i = 0, il = meshes.length; i < il; i ++ ) {
@@ -3466,10 +3478,13 @@
}
- parser.associations.set( node, {
- type: 'nodes',
- index: nodeIndex
- } );
+ if ( ! parser.associations.has( node ) ) {
+
+ parser.associations.set( node, {} );
+
+ }
+
+ parser.associations.get( node ).nodes = nodeIndex;
return node;
} );
@@ -3499,12 +3514,44 @@
for ( let i = 0, il = nodeIds.length; i < il; i ++ ) {
- pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) );
+ pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) );
}
return Promise.all( pending ).then( function () {
+ // Removes dangling associations, associations that reference a node that
+ // didn't make it into the scene.
+ const reduceAssociations = node => {
+
+ const reducedAssociations = new Map();
+
+ for ( const [ key, value ] of parser.associations ) {
+
+ if ( key instanceof THREE.Material || key instanceof THREE.Texture ) {
+
+ reducedAssociations.set( key, value );
+
+ }
+
+ }
+
+ node.traverse( node => {
+
+ const mappings = parser.associations.get( node );
+
+ if ( mappings != null ) {
+
+ reducedAssociations.set( node, mappings );
+
+ }
+
+ } );
+ return reducedAssociations;
+
+ };
+
+ parser.associations = reduceAssociations( scene );
return scene;
} );
@@ -3513,7 +3560,7 @@
}
- function buildNodeHierachy( nodeId, parentObject, json, parser ) {
+ function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
const nodeDef = json.nodes[ nodeId ];
return parser.getDependency( 'node', nodeId ).then( function ( node ) {
@@ -3587,7 +3634,7 @@
for ( let i = 0, il = children.length; i < il; i ++ ) {
const child = children[ i ];
- pending.push( buildNodeHierachy( child, node, json, parser ) );
+ pending.push( buildNodeHierarchy( child, node, json, parser ) );
}
diff --git a/examples/js/loaders/RGBMLoader.js b/examples/js/loaders/RGBMLoader.js
index 475a5336968f69..8570b442b248cc 100644
--- a/examples/js/loaders/RGBMLoader.js
+++ b/examples/js/loaders/RGBMLoader.js
@@ -310,7 +310,7 @@
} else if ( depth == 16 ) for ( var x = 0; x < w; x ++ ) {
var gr = data[ off + ( x << 1 ) ],
- al = rs( data, off + ( x << i ) ) == tr ? 0 : 255;
+ al = rs( data, off + ( x << 1 ) ) == tr ? 0 : 255;
bf32[ to + x ] = al << 24 | gr << 16 | gr << 8 | gr;
}
@@ -485,7 +485,7 @@
break;
- } //else { log("unknown chunk type", type, len); }
+ } //else { console.log("unknown chunk type", type, len); out.tabs[type]=data.slice(offset,offset+len); }
offset += len;
@@ -498,7 +498,6 @@
var fr = out.frames[ out.frames.length - 1 ];
fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height );
- foff = 0;
}
@@ -1111,8 +1110,8 @@
paeth = UPNG.decode._paeth;
bpp = Math.ceil( bpp / 8 );
- var i = 0,
- di = 1,
+ var i,
+ di,
type = data[ off ],
x = 0;
if ( type > 1 ) data[ off ] = [ 0, 0, 1 ][ type - 2 ];
diff --git a/examples/js/loaders/SVGLoader.js b/examples/js/loaders/SVGLoader.js
index d283d1f8653626..7d01d48e9a24c1 100644
--- a/examples/js/loaders/SVGLoader.js
+++ b/examples/js/loaders/SVGLoader.js
@@ -856,6 +856,7 @@
addStyle( 'fill', 'fill' );
addStyle( 'fill-opacity', 'fillOpacity', clamp );
+ addStyle( 'fill-rule', 'fillRule' );
addStyle( 'opacity', 'opacity', clamp );
addStyle( 'stroke', 'stroke' );
addStyle( 'stroke-opacity', 'strokeOpacity', clamp );
diff --git a/examples/js/postprocessing/SSRPass.js b/examples/js/postprocessing/SSRPass.js
index 5f8f2c8e2c3b9b..82659fcbb8e253 100644
--- a/examples/js/postprocessing/SSRPass.js
+++ b/examples/js/postprocessing/SSRPass.js
@@ -9,7 +9,6 @@
width,
height,
selects,
- encoding,
bouncing = false,
groundReflector
} ) {
@@ -26,7 +25,6 @@
this.output = 0;
this.maxDistance = THREE.SSRShader.uniforms.maxDistance.value;
this.thickness = THREE.SSRShader.uniforms.thickness.value;
- this.encoding = encoding;
this.tempColor = new THREE.Color();
this._selects = selects;
this.selective = Array.isArray( this._selects );
@@ -307,7 +305,6 @@
) {
// render beauty and depth
- if ( this.encoding ) this.beautyRenderTarget.texture.encoding = this.encoding;
renderer.setRenderTarget( this.beautyRenderTarget );
renderer.clear();
diff --git a/examples/js/postprocessing/SSRrPass.js b/examples/js/postprocessing/SSRrPass.js
index 1ea37f92b75be7..6093c211a5bb09 100644
--- a/examples/js/postprocessing/SSRrPass.js
+++ b/examples/js/postprocessing/SSRrPass.js
@@ -8,8 +8,7 @@
camera,
width,
height,
- selects,
- encoding
+ selects
} ) {
super();
@@ -24,7 +23,6 @@
this.ior = THREE.SSRrShader.uniforms.ior.value;
this.maxDistance = THREE.SSRrShader.uniforms.maxDistance.value;
this.surfDist = THREE.SSRrShader.uniforms.surfDist.value;
- this.encoding = encoding;
this.tempColor = new THREE.Color();
this.selects = selects;
this._specular = THREE.SSRrShader.defines.SPECULAR;
@@ -225,7 +223,6 @@
) {
// render beauty and depth
- if ( this.encoding ) this.beautyRenderTarget.texture.encoding = this.encoding;
renderer.setRenderTarget( this.beautyRenderTarget );
renderer.clear();
this.scene.children.forEach( child => {
diff --git a/examples/js/utils/PackedPhongMaterial.js b/examples/js/utils/PackedPhongMaterial.js
index a58db381f431d1..3b7b90e346b5bf 100644
--- a/examples/js/utils/PackedPhongMaterial.js
+++ b/examples/js/utils/PackedPhongMaterial.js
@@ -21,7 +21,7 @@
value: null
}
} ] );
- this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', '#ifndef FLAT_SHADED', 'varying vec3 vNormal;', '#endif', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
+ this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.normal_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
#if USE_PACKED_NORMAL == 0
vec3 decodeNormal(vec3 packedNormal)
{
@@ -87,13 +87,13 @@
#ifdef USE_TANGENT
vec3 objectTangent = vec3( tangent.xyz );
#endif
- `, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, '#ifndef FLAT_SHADED', ' vNormal = normalize( transformedNormal );', '#endif', THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
+ `, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, THREE.ShaderChunk.normal_vertex, THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
#if USE_PACKED_POSITION == 0
transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
#endif
#endif`, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.displacementmap_vertex, THREE.ShaderChunk.project_vertex, THREE.ShaderChunk.logdepthbuf_vertex, THREE.ShaderChunk.clipping_planes_vertex, 'vViewPosition = - mvPosition.xyz;', THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, THREE.ShaderChunk.fog_vertex, '}' ].join( '\n' ); // Use the original THREE.MeshPhongMaterial's fragmentShader.
- this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
+ this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.normal_pars_fragment, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lights_fragment_begin, THREE.ShaderChunk.lights_fragment_maps, THREE.ShaderChunk.lights_fragment_end, // modulation
THREE.ShaderChunk.aomap_fragment, 'vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;', THREE.ShaderChunk.envmap_fragment, 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );', THREE.ShaderChunk.tonemapping_fragment, THREE.ShaderChunk.encodings_fragment, THREE.ShaderChunk.fog_fragment, THREE.ShaderChunk.premultiplied_alpha_fragment, THREE.ShaderChunk.dithering_fragment, '}' ].join( '\n' );
this.setValues( parameters );
diff --git a/examples/jsm/exporters/USDZExporter.js b/examples/jsm/exporters/USDZExporter.js
index 04b5b6207da40c..af26ecd2f86317 100644
--- a/examples/jsm/exporters/USDZExporter.js
+++ b/examples/jsm/exporters/USDZExporter.js
@@ -449,7 +449,18 @@ function buildMaterial( material, textures ) {
}
- inputs.push( `${ pad }float inputs:opacity = ${ material.opacity }` );
+ if ( material.alphaMap !== null ) {
+
+ inputs.push( `${pad}float inputs:opacity.connect = ` );
+ inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
+
+ samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
+
+ } else {
+
+ inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+
+ }
if ( material.isMeshPhysicalMaterial ) {
diff --git a/examples/jsm/geometries/ParametricGeometry.js b/examples/jsm/geometries/ParametricGeometry.js
index 8bbed826c8b8c2..4f26b7e7405226 100644
--- a/examples/jsm/geometries/ParametricGeometry.js
+++ b/examples/jsm/geometries/ParametricGeometry.js
@@ -132,4 +132,4 @@ class ParametricGeometry extends BufferGeometry {
}
-export { ParametricGeometry, ParametricGeometry as ParametricBufferGeometry };
+export { ParametricGeometry };
diff --git a/examples/jsm/geometries/TextGeometry.js b/examples/jsm/geometries/TextGeometry.js
index 8a9e4255af08db..891234afdb5397 100644
--- a/examples/jsm/geometries/TextGeometry.js
+++ b/examples/jsm/geometries/TextGeometry.js
@@ -54,4 +54,4 @@ class TextGeometry extends ExtrudeGeometry {
}
-export { TextGeometry, TextGeometry as TextBufferGeometry };
+export { TextGeometry };
diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js
index f79b735786498a..3e95e5391d5bf3 100644
--- a/examples/jsm/loaders/GLTFLoader.js
+++ b/examples/jsm/loaders/GLTFLoader.js
@@ -692,8 +692,7 @@ class GLTFMaterialsClearcoatExtension {
const scale = extension.clearcoatNormalTexture.scale;
- // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
- materialParams.clearcoatNormalScale = new Vector2( scale, - scale );
+ materialParams.clearcoatNormalScale = new Vector2( scale, scale );
}
@@ -2369,6 +2368,27 @@ class GLTFParser {
const ref = object.clone();
+ // Propagates mappings to the cloned object, prevents mappings on the
+ // original object from being lost.
+ const updateMappings = ( original, clone ) => {
+
+ const mappings = this.associations.get( original );
+ if ( mappings != null ) {
+
+ this.associations.set( clone, mappings );
+
+ }
+
+ for ( const [ i, child ] of original.children.entries() ) {
+
+ updateMappings( child, clone.children[ i ] );
+
+ }
+
+ };
+
+ updateMappings( object, ref );
+
ref.name += '_instance_' + ( cache.uses[ index ] ++ );
return ref;
@@ -2899,7 +2919,7 @@ class GLTFParser {
const geometry = mesh.geometry;
let material = mesh.material;
- const useVertexTangents = geometry.attributes.tangent !== undefined;
+ const useDerivativeTangents = geometry.attributes.tangent === undefined;
const useVertexColors = geometry.attributes.color !== undefined;
const useFlatShading = geometry.attributes.normal === undefined;
@@ -2944,12 +2964,12 @@ class GLTFParser {
}
// Clone the material if it will be modified
- if ( useVertexTangents || useVertexColors || useFlatShading ) {
+ if ( useDerivativeTangents || useVertexColors || useFlatShading ) {
let cacheKey = 'ClonedMaterial:' + material.uuid + ':';
if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
- if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
+ if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:';
if ( useVertexColors ) cacheKey += 'vertex-colors:';
if ( useFlatShading ) cacheKey += 'flat-shading:';
@@ -2962,7 +2982,7 @@ class GLTFParser {
if ( useVertexColors ) cachedMaterial.vertexColors = true;
if ( useFlatShading ) cachedMaterial.flatShading = true;
- if ( useVertexTangents ) {
+ if ( useDerivativeTangents ) {
// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1;
@@ -3109,12 +3129,13 @@ class GLTFParser {
pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
- // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
- materialParams.normalScale = new Vector2( 1, - 1 );
+ materialParams.normalScale = new Vector2( 1, 1 );
if ( materialDef.normalTexture.scale !== undefined ) {
- materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale );
+ const scale = materialDef.normalTexture.scale;
+
+ materialParams.normalScale.set( scale, scale );
}
@@ -3848,12 +3869,46 @@ class GLTFParser {
for ( let i = 0, il = nodeIds.length; i < il; i ++ ) {
- pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) );
+ pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) );
}
return Promise.all( pending ).then( function () {
+ // Removes dangling associations, associations that reference a node that
+ // didn't make it into the scene.
+ const reduceAssociations = ( node ) => {
+
+ const reducedAssociations = new Map();
+
+ for ( const [ key, value ] of parser.associations ) {
+
+ if ( key instanceof Material || key instanceof Texture ) {
+
+ reducedAssociations.set( key, value );
+
+ }
+
+ }
+
+ node.traverse( ( node ) => {
+
+ const mappings = parser.associations.get( node );
+
+ if ( mappings != null ) {
+
+ reducedAssociations.set( node, mappings );
+
+ }
+
+ } );
+
+ return reducedAssociations;
+
+ };
+
+ parser.associations = reduceAssociations( scene );
+
return scene;
} );
@@ -3862,7 +3917,7 @@ class GLTFParser {
}
-function buildNodeHierachy( nodeId, parentObject, json, parser ) {
+function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
const nodeDef = json.nodes[ nodeId ];
@@ -3946,7 +4001,7 @@ function buildNodeHierachy( nodeId, parentObject, json, parser ) {
for ( let i = 0, il = children.length; i < il; i ++ ) {
const child = children[ i ];
- pending.push( buildNodeHierachy( child, node, json, parser ) );
+ pending.push( buildNodeHierarchy( child, node, json, parser ) );
}
diff --git a/examples/jsm/loaders/RGBMLoader.js b/examples/jsm/loaders/RGBMLoader.js
index 2178a3ddf17568..a1e87cd8e842e3 100644
--- a/examples/jsm/loaders/RGBMLoader.js
+++ b/examples/jsm/loaders/RGBMLoader.js
@@ -251,7 +251,7 @@ UPNG.toRGBA8.decodeImage = function ( data, w, h, out ) {
}
else if ( depth == 16 ) for ( var x = 0; x < w; x ++ ) {
- var gr = data[ off + ( x << 1 ) ], al = ( rs( data, off + ( x << i ) ) == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
+ var gr = data[ off + ( x << 1 ) ], al = ( rs( data, off + ( x << 1 ) ) == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
}
@@ -397,7 +397,7 @@ UPNG.decode = function ( buff ) {
}
- //else { log("unknown chunk type", type, len); }
+ //else { console.log("unknown chunk type", type, len); out.tabs[type]=data.slice(offset,offset+len); }
offset += len;
bin.readUint( data, offset ); offset += 4;
@@ -406,7 +406,7 @@ UPNG.decode = function ( buff ) {
if ( foff != 0 ) {
var fr = out.frames[ out.frames.length - 1 ];
- fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height ); foff = 0;
+ fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height );
}
@@ -819,7 +819,7 @@ UPNG.decode._filterZero = function ( data, out, off, w, h ) {
var bpp = UPNG.decode._getBPP( out ), bpl = Math.ceil( w * bpp / 8 ), paeth = UPNG.decode._paeth;
bpp = Math.ceil( bpp / 8 );
- var i = 0, di = 1, type = data[ off ], x = 0;
+ var i, di, type = data[ off ], x = 0;
if ( type > 1 ) data[ off ] = [ 0, 0, 1 ][ type - 2 ];
if ( type == 3 ) for ( x = bpp; x < bpl; x ++ ) data[ x + 1 ] = ( data[ x + 1 ] + ( data[ x + 1 - bpp ] >>> 1 ) ) & 255;
diff --git a/examples/jsm/loaders/SVGLoader.js b/examples/jsm/loaders/SVGLoader.js
index fc0819e5cae7b7..4c0b2f71802c3f 100644
--- a/examples/jsm/loaders/SVGLoader.js
+++ b/examples/jsm/loaders/SVGLoader.js
@@ -1007,6 +1007,7 @@ class SVGLoader extends Loader {
addStyle( 'fill', 'fill' );
addStyle( 'fill-opacity', 'fillOpacity', clamp );
+ addStyle( 'fill-rule', 'fillRule' );
addStyle( 'opacity', 'opacity', clamp );
addStyle( 'stroke', 'stroke' );
addStyle( 'stroke-opacity', 'strokeOpacity', clamp );
diff --git a/examples/jsm/renderers/nodes/Nodes.js b/examples/jsm/renderers/nodes/Nodes.js
index 754b8584347c80..fabf78dcb9097f 100644
--- a/examples/jsm/renderers/nodes/Nodes.js
+++ b/examples/jsm/renderers/nodes/Nodes.js
@@ -68,6 +68,9 @@ import SplitNode from './utils/SplitNode.js';
import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
import TimerNode from './utils/TimerNode.js';
+// procedural
+import CheckerNode from './procedural/CheckerNode.js';
+
// core
export * from './core/constants.js';
@@ -151,6 +154,9 @@ export {
JoinNode,
SplitNode,
SpriteSheetUVNode,
- TimerNode
+ TimerNode,
+
+ // procedural
+ CheckerNode
};
diff --git a/examples/jsm/renderers/nodes/ShaderNode.js b/examples/jsm/renderers/nodes/ShaderNode.js
new file mode 100644
index 00000000000000..df50567de05232
--- /dev/null
+++ b/examples/jsm/renderers/nodes/ShaderNode.js
@@ -0,0 +1,193 @@
+// inputs
+import ColorNode from './inputs/ColorNode.js';
+import FloatNode from './inputs/FloatNode.js';
+import Vector2Node from './inputs/Vector2Node.js';
+import Vector3Node from './inputs/Vector3Node.js';
+import Vector4Node from './inputs/Vector4Node.js';
+
+// math
+import MathNode from './math/MathNode.js';
+import OperatorNode from './math/OperatorNode.js';
+
+// utils
+import JoinNode from './utils/JoinNode.js';
+import SplitNode from './utils/SplitNode.js';
+
+// core
+import { Vector2, Vector3, Vector4, Color } from 'three';
+
+const NodeHandler = {
+
+ get: function ( node, prop ) {
+
+ // Split Properties Pass
+
+ if ( typeof prop === 'string' && node[ prop ] === undefined ) {
+
+ const splitProps = prop.match( /^[xyzw]{1,4}$/ );
+
+ if ( splitProps !== null ) {
+
+ return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
+
+ }
+
+ }
+
+ return node[ prop ];
+
+ }
+
+};
+
+export const ShaderNodeObject = ( obj ) => {
+
+ const type = typeof obj;
+
+ if ( type === 'number' ) {
+
+ return ShaderNodeObject( new FloatNode( obj ).setConst( true ) );
+
+ } else if ( type === 'object' ) {
+
+ if ( obj.isNode === true ) {
+
+ const node = obj;
+
+ if ( node.isProxyNode !== true ) {
+
+ node.isProxyNode = true;
+
+ return new Proxy( node, NodeHandler );
+
+ }
+
+ }
+
+ }
+
+ return obj;
+
+};
+
+export const ShaderNodeArray = ( array ) => {
+
+ const len = array.length;
+
+ for ( let i = 0; i < len; i ++ ) {
+
+ array[ i ] = ShaderNodeObject( array[ i ] );
+
+ }
+
+ return array;
+
+};
+
+export const ShaderNodeScript = ( jsFunc ) => {
+
+ return ( ...params ) => {
+
+ ShaderNodeArray( params );
+
+ return ShaderNodeObject( jsFunc( ...params ) );
+
+ };
+
+};
+
+export const ShaderNode = ( obj ) => {
+
+ return ShaderNodeScript( obj );
+
+};
+
+//
+// Node Material Shader Syntax
+//
+
+export const uniform = ShaderNodeScript( ( inputNode ) => {
+
+ inputNode.setConst( false );
+
+ return inputNode;
+
+} );
+
+export const float = ( val ) => {
+
+ return ShaderNodeObject( new FloatNode( val ).setConst( true ) );
+
+};
+
+export const color = ( ...params ) => {
+
+ return ShaderNodeObject( new ColorNode( new Color( ...params ) ).setConst( true ) );
+
+};
+
+export const join = ( ...params ) => {
+
+ return ShaderNodeObject( new JoinNode( ShaderNodeArray( params ) ) );
+
+};
+
+export const vec2 = ( ...params ) => {
+
+ return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
+
+};
+
+export const vec3 = ( ...params ) => {
+
+ return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
+
+};
+
+export const vec4 = ( ...params ) => {
+
+ return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
+
+};
+
+export const add = ( ...params ) => {
+
+ return ShaderNodeObject( new OperatorNode( '+', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const sub = ( ...params ) => {
+
+ return new OperatorNode( '-', ...ShaderNodeArray( params ) );
+
+};
+
+export const mul = ( ...params ) => {
+
+ return ShaderNodeObject( new OperatorNode( '*', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const div = ( ...params ) => {
+
+ return ShaderNodeObject( new OperatorNode( '/', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const floor = ( ...params ) => {
+
+ return ShaderNodeObject( new MathNode( 'floor', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const mod = ( ...params ) => {
+
+ return ShaderNodeObject( new MathNode( 'mod', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const sign = ( ...params ) => {
+
+ return ShaderNodeObject( new MathNode( 'sign', ...ShaderNodeArray( params ) ) );
+
+};
diff --git a/examples/jsm/renderers/nodes/accessors/CameraNode.js b/examples/jsm/renderers/nodes/accessors/CameraNode.js
index a78a5a669a3b20..160dd82dbf35b9 100644
--- a/examples/jsm/renderers/nodes/accessors/CameraNode.js
+++ b/examples/jsm/renderers/nodes/accessors/CameraNode.js
@@ -9,6 +9,8 @@ class CameraNode extends Object3DNode {
super( scope );
+ this._inputNode = null;
+
}
getNodeType( builder ) {
@@ -47,37 +49,17 @@ class CameraNode extends Object3DNode {
}
- generate( builder, output ) {
-
- const nodeData = builder.getDataFromNode( this );
-
- let inputNode = this._inputNode;
-
- if ( nodeData.inputNode === undefined ) {
-
- const scope = this.scope;
-
- if ( scope === CameraNode.PROJECTION_MATRIX ) {
-
- if ( inputNode === null || inputNode.isMatrix4Node !== true ) {
+ generate( builder ) {
- inputNode = new Matrix4Node( null );
-
- }
-
- } else {
-
- return super.generate( builder, output );
-
- }
+ const scope = this.scope;
- this._inputNode = inputNode;
+ if ( scope === CameraNode.PROJECTION_MATRIX ) {
- nodeData.inputNode = inputNode;
+ this._inputNode = new Matrix4Node( null );
}
- return inputNode.build( builder, output );
+ return super.generate( builder );
}
diff --git a/examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js b/examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
index 78c511f880d161..e047e8063fd909 100644
--- a/examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
+++ b/examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
@@ -16,14 +16,12 @@ class ModelViewProjectionNode extends Node {
}
- generate( builder, output ) {
-
- const type = this.getNodeType( builder );
+ generate( builder ) {
const mvpSnipped = this._mvpMatrix.build( builder );
const positionSnipped = this.position.build( builder, 'vec3' );
- return builder.format( `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`, type, output );
+ return `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`;
}
diff --git a/examples/jsm/renderers/nodes/accessors/NormalNode.js b/examples/jsm/renderers/nodes/accessors/NormalNode.js
index 084e9d425b1059..f974110160c290 100644
--- a/examples/jsm/renderers/nodes/accessors/NormalNode.js
+++ b/examples/jsm/renderers/nodes/accessors/NormalNode.js
@@ -21,61 +21,29 @@ class NormalNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.getNodeType( builder );
- const nodeData = builder.getDataFromNode( this, builder.shaderStage );
const scope = this.scope;
- let localNormalNode = nodeData.localNormalNode;
+ let outputNode = null;
- if ( localNormalNode === undefined ) {
+ if ( scope === NormalNode.LOCAL ) {
- localNormalNode = new AttributeNode( 'normal', 'vec3' );
+ outputNode = new AttributeNode( 'normal', 'vec3' );
- nodeData.localNormalNode = localNormalNode;
+ } else if ( scope === NormalNode.VIEW ) {
- }
-
- let outputNode = localNormalNode;
-
- if ( scope === NormalNode.VIEW ) {
-
- let viewNormalNode = nodeData.viewNormalNode;
-
- if ( viewNormalNode === undefined ) {
-
- const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), localNormalNode );
-
- viewNormalNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
-
- nodeData.viewNormalNode = viewNormalNode;
-
- }
-
- outputNode = viewNormalNode;
+ const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), new NormalNode( NormalNode.LOCAL ) );
+ outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
} else if ( scope === NormalNode.WORLD ) {
- let worldNormalNode = nodeData.worldNormalNode;
-
- if ( worldNormalNode === undefined ) {
-
- const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
-
- worldNormalNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
-
- nodeData.worldNormalNode = worldNormalNode;
-
- }
-
- outputNode = worldNormalNode;
+ const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
+ outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
}
- const normalSnipped = outputNode.build( builder, type );
-
- return builder.format( normalSnipped, type, output );
+ return outputNode.build( builder );
}
diff --git a/examples/jsm/renderers/nodes/accessors/Object3DNode.js b/examples/jsm/renderers/nodes/accessors/Object3DNode.js
index 6ae720aec0c6eb..9bc3ae3b3bc422 100644
--- a/examples/jsm/renderers/nodes/accessors/Object3DNode.js
+++ b/examples/jsm/renderers/nodes/accessors/Object3DNode.js
@@ -78,49 +78,25 @@ class Object3DNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const nodeData = builder.getDataFromNode( this );
-
- let inputNode = this._inputNode;
-
- if ( nodeData.inputNode === undefined ) {
-
- const scope = this.scope;
-
- if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
-
- if ( inputNode === null || inputNode.isMatrix4Node !== true ) {
-
- inputNode = new Matrix4Node( /*null*/ );
-
- }
-
- } else if ( scope === Object3DNode.NORMAL_MATRIX ) {
-
- if ( inputNode === null || inputNode.isMatrix3Node !== true ) {
-
- inputNode = new Matrix3Node( /*null*/ );
-
- }
-
- } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION ) {
+ const scope = this.scope;
- if ( inputNode === null || inputNode.isVector3Node !== true ) {
+ if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
- inputNode = new Vector3Node();
+ this._inputNode = new Matrix4Node( /*null*/ );
- }
+ } else if ( scope === Object3DNode.NORMAL_MATRIX ) {
- }
+ this._inputNode = new Matrix3Node( /*null*/ );
- this._inputNode = inputNode;
+ } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION ) {
- nodeData.inputNode = inputNode;
+ this._inputNode = new Vector3Node();
}
- return inputNode.build( builder, output );
+ return this._inputNode.build( builder );
}
diff --git a/examples/jsm/renderers/nodes/accessors/PointUVNode.js b/examples/jsm/renderers/nodes/accessors/PointUVNode.js
index 889d8f985e137b..54538b94daec17 100644
--- a/examples/jsm/renderers/nodes/accessors/PointUVNode.js
+++ b/examples/jsm/renderers/nodes/accessors/PointUVNode.js
@@ -10,12 +10,9 @@ class PointUVNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.getNodeType( builder );
- const snippet = 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
-
- return builder.format( snippet, type, output );
+ return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
}
diff --git a/examples/jsm/renderers/nodes/accessors/PositionNode.js b/examples/jsm/renderers/nodes/accessors/PositionNode.js
index fbcfd9c03e7b7c..15ac153bf5a86a 100644
--- a/examples/jsm/renderers/nodes/accessors/PositionNode.js
+++ b/examples/jsm/renderers/nodes/accessors/PositionNode.js
@@ -13,7 +13,7 @@ class PositionNode extends Node {
static VIEW = 'view';
static VIEW_DIRECTION = 'viewDirection';
- constructor( scope = PositionNode.POSITION ) {
+ constructor( scope = PositionNode.LOCAL ) {
super( 'vec3' );
@@ -21,77 +21,34 @@ class PositionNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.getNodeType( builder );
- const nodeData = builder.getDataFromNode( this );
const scope = this.scope;
- let localPositionNode = nodeData.localPositionNode;
+ let outputNode = null;
- if ( localPositionNode === undefined ) {
+ if ( scope === PositionNode.LOCAL ) {
+
+ outputNode = new AttributeNode( 'position', 'vec3' );
+
+ } else if ( scope === PositionNode.WORLD ) {
- localPositionNode = new AttributeNode( 'position', 'vec3' );
-
- nodeData.localPositionNode = localPositionNode;
-
- }
-
- let outputNode = localPositionNode;
-
- if ( scope === PositionNode.WORLD ) {
-
- let worldPositionNode = nodeData.worldPositionNode;
-
- if ( worldPositionNode === undefined ) {
-
- const vertexPositionNode = transformDirection.call( { dir: localPositionNode, matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
-
- worldPositionNode = new VaryNode( vertexPositionNode );
-
- nodeData.worldPositionNode = worldPositionNode;
-
- }
-
- outputNode = worldPositionNode;
+ const vertexPositionNode = transformDirection.call( { dir: new PositionNode( PositionNode.LOCAL ), matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
+ outputNode = new VaryNode( vertexPositionNode );
} else if ( scope === PositionNode.VIEW ) {
- let viewPositionNode = nodeData.viewPositionNode;
-
- if ( viewPositionNode === undefined ) {
-
- const vertexPositionNode = new OperatorNode( '*', new ModelNode( ModelNode.VIEW_MATRIX ), localPositionNode );
-
- viewPositionNode = new VaryNode( vertexPositionNode );
-
- nodeData.viewPositionNode = viewPositionNode;
-
- }
-
- outputNode = viewPositionNode;
+ const vertexPositionNode = new OperatorNode( '*', new ModelNode( ModelNode.VIEW_MATRIX ), new PositionNode( PositionNode.LOCAL ) );
+ outputNode = new VaryNode( vertexPositionNode );
} else if ( scope === PositionNode.VIEW_DIRECTION ) {
- let viewDirPositionNode = nodeData.viewDirPositionNode;
-
- if ( viewDirPositionNode === undefined ) {
-
- const vertexPositionNode = new MathNode( MathNode.NEGATE, new PositionNode( PositionNode.VIEW ) );
-
- viewDirPositionNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexPositionNode ) );
-
- nodeData.viewDirPositionNode = viewDirPositionNode;
-
- }
-
- outputNode = viewDirPositionNode;
+ const vertexPositionNode = new MathNode( MathNode.NEGATE, new PositionNode( PositionNode.VIEW ) );
+ outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexPositionNode ) );
}
- const positionSnipped = outputNode.build( builder, type );
-
- return builder.format( positionSnipped, type, output );
+ return outputNode.build( builder, this.getNodeType( builder ) );
}
diff --git a/examples/jsm/renderers/nodes/accessors/ReferenceNode.js b/examples/jsm/renderers/nodes/accessors/ReferenceNode.js
index 25a29c1697daa3..7dced176e01e25 100644
--- a/examples/jsm/renderers/nodes/accessors/ReferenceNode.js
+++ b/examples/jsm/renderers/nodes/accessors/ReferenceNode.js
@@ -80,9 +80,9 @@ class ReferenceNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- return this.node.build( builder, output );
+ return this.node.build( builder, this.getNodeType( builder ) );
}
diff --git a/examples/jsm/renderers/nodes/core/AttributeNode.js b/examples/jsm/renderers/nodes/core/AttributeNode.js
index 7fc3a9d132281f..3ac15cfa9ee361 100644
--- a/examples/jsm/renderers/nodes/core/AttributeNode.js
+++ b/examples/jsm/renderers/nodes/core/AttributeNode.js
@@ -25,32 +25,19 @@ class AttributeNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const attributeName = this.getAttributeName( builder );
- const attributeType = this.getNodeType( builder );
-
- const attribute = builder.getAttribute( attributeName, attributeType );
+ const attribute = builder.getAttribute( this.getAttributeName( builder ), this.getNodeType( builder ) );
if ( builder.isShaderStage( 'vertex' ) ) {
- return builder.format( attribute.name, attribute.type, output );
+ return attribute.name;
} else {
- const nodeData = builder.getDataFromNode( this, builder.shaderStage );
-
- let nodeVary = nodeData.varyNode;
-
- if ( nodeVary === undefined ) {
-
- nodeVary = new VaryNode( this );
-
- nodeData.nodeVary = nodeVary;
-
- }
+ const nodeVary = new VaryNode( this );
- return nodeVary.build( builder, output );
+ return nodeVary.build( builder, attribute.type );
}
diff --git a/examples/jsm/renderers/nodes/core/CodeNode.js b/examples/jsm/renderers/nodes/core/CodeNode.js
index d5e0411d607d28..e8c6e26afb9fdd 100644
--- a/examples/jsm/renderers/nodes/core/CodeNode.js
+++ b/examples/jsm/renderers/nodes/core/CodeNode.js
@@ -30,7 +30,7 @@ class CodeNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
if ( this.useKeywords === true ) {
@@ -66,9 +66,7 @@ class CodeNode extends Node {
}
- const type = this.getNodeType( builder );
- const nodeCode = builder.getCodeFromNode( this, type );
-
+ const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
nodeCode.code = this.code;
return nodeCode.code;
diff --git a/examples/jsm/renderers/nodes/core/ConstNode.js b/examples/jsm/renderers/nodes/core/ConstNode.js
index 9d83539acebb3c..06f25fc13f2ed3 100644
--- a/examples/jsm/renderers/nodes/core/ConstNode.js
+++ b/examples/jsm/renderers/nodes/core/ConstNode.js
@@ -12,12 +12,11 @@ class ConstNode extends CodeNode {
}
- generate( builder, output ) {
+ generate( builder ) {
const code = super.generate( builder );
- const type = this.getNodeType( builder );
- const nodeCode = builder.getCodeFromNode( this, type );
+ const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
if ( this.name !== '' ) {
@@ -31,7 +30,7 @@ class ConstNode extends CodeNode {
nodeCode.code = `#define ${propertyName} ${code}`;
- return builder.format( propertyName, type, output );
+ return propertyName;
}
diff --git a/examples/jsm/renderers/nodes/core/ExpressionNode.js b/examples/jsm/renderers/nodes/core/ExpressionNode.js
index 1637aa75722859..3367d2757fc843 100644
--- a/examples/jsm/renderers/nodes/core/ExpressionNode.js
+++ b/examples/jsm/renderers/nodes/core/ExpressionNode.js
@@ -1,6 +1,6 @@
-import Node from './Node.js';
+import TempNode from './TempNode.js';
-class ExpressionNode extends Node {
+class ExpressionNode extends TempNode {
constructor( snipped = '', nodeType = null ) {
@@ -10,12 +10,9 @@ class ExpressionNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.getNodeType( builder );
- const snipped = this.snipped;
-
- return builder.format( `( ${ snipped } )`, type, output );
+ return `( ${ this.snipped } )`;
}
diff --git a/examples/jsm/renderers/nodes/core/FunctionCallNode.js b/examples/jsm/renderers/nodes/core/FunctionCallNode.js
index 59ade414c08197..e5609168ad7e8d 100644
--- a/examples/jsm/renderers/nodes/core/FunctionCallNode.js
+++ b/examples/jsm/renderers/nodes/core/FunctionCallNode.js
@@ -31,7 +31,7 @@ class FunctionCallNode extends TempNode {
}
- generate( builder, output ) {
+ generate( builder ) {
const params = [];
@@ -56,12 +56,9 @@ class FunctionCallNode extends TempNode {
}
- const type = this.getNodeType( builder );
const functionName = functionNode.build( builder, 'property' );
- const callSnippet = `${functionName}( ${params.join( ', ' )} )`;
-
- return builder.format( callSnippet, type, output );
+ return `${functionName}( ${params.join( ', ' )} )`;
}
diff --git a/examples/jsm/renderers/nodes/core/Node.js b/examples/jsm/renderers/nodes/core/Node.js
index 057785b5ec9fd3..a39a4fb8b21581 100644
--- a/examples/jsm/renderers/nodes/core/Node.js
+++ b/examples/jsm/renderers/nodes/core/Node.js
@@ -50,6 +50,27 @@ class Node {
builder.addNode( this );
+ const isGenerateOnce = this.generate.length === 1;
+
+ if ( isGenerateOnce ) {
+
+ const type = this.getNodeType( builder );
+ const nodeData = builder.getDataFromNode( this );
+
+ let snippet = nodeData.snippet;
+
+ if ( snippet === undefined ) {
+
+ snippet = this.generate( builder );
+
+ nodeData.snippet = snippet;
+
+ }
+
+ return builder.format( snippet, type, output );
+
+ }
+
return this.generate( builder, output );
}
diff --git a/examples/jsm/renderers/nodes/core/PropertyNode.js b/examples/jsm/renderers/nodes/core/PropertyNode.js
index 96dd0af8d4a160..841f5066b24c7b 100644
--- a/examples/jsm/renderers/nodes/core/PropertyNode.js
+++ b/examples/jsm/renderers/nodes/core/PropertyNode.js
@@ -10,16 +10,12 @@ class PropertyNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.getNodeType( builder );
-
- const nodeVary = builder.getVarFromNode( this, type );
+ const nodeVary = builder.getVarFromNode( this, this.getNodeType( builder ) );
nodeVary.name = this.name;
- const propertyName = builder.getPropertyName( nodeVary );
-
- return builder.format( propertyName, type, output );
+ return builder.getPropertyName( nodeVary );
}
diff --git a/examples/jsm/renderers/nodes/core/StructVarNode.js b/examples/jsm/renderers/nodes/core/StructVarNode.js
index b21621f91428ca..a64f6ba6cd1c49 100644
--- a/examples/jsm/renderers/nodes/core/StructVarNode.js
+++ b/examples/jsm/renderers/nodes/core/StructVarNode.js
@@ -20,7 +20,7 @@ class StructVarNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = this.getNodeType( builder );
@@ -66,7 +66,7 @@ class StructVarNode extends Node {
}
- return builder.format( property, type, output );
+ return property;
}
diff --git a/examples/jsm/renderers/nodes/core/TempNode.js b/examples/jsm/renderers/nodes/core/TempNode.js
index 8a187388f4ab23..05fa1b0dcfd27d 100644
--- a/examples/jsm/renderers/nodes/core/TempNode.js
+++ b/examples/jsm/renderers/nodes/core/TempNode.js
@@ -17,15 +17,25 @@ class TempNode extends Node {
const nodeVar = builder.getVarFromNode( this, type );
const propertyName = builder.getPropertyName( nodeVar );
- const snippet = super.build( builder, type );
+ const nodeData = builder.getDataFromNode( this );
- builder.addFlowCode( `${propertyName} = ${snippet}` );
+ let snippet = nodeData.snippet;
+
+ if ( snippet === undefined ) {
+
+ snippet = super.build( builder, type );
+
+ builder.addFlowCode( `${propertyName} = ${snippet}` );
+
+ nodeData.snippet = snippet;
+
+ }
return builder.format( propertyName, type, output );
} else {
- return super.build( builder, type );
+ return super.build( builder, output );
}
diff --git a/examples/jsm/renderers/nodes/core/VarNode.js b/examples/jsm/renderers/nodes/core/VarNode.js
index e491e9f87b35f3..3b83106e676901 100644
--- a/examples/jsm/renderers/nodes/core/VarNode.js
+++ b/examples/jsm/renderers/nodes/core/VarNode.js
@@ -17,7 +17,7 @@ class VarNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = builder.getVectorType( this.getNodeType( builder ) );
const name = this.name;
@@ -37,7 +37,7 @@ class VarNode extends Node {
builder.addFlowCode( `${propertyName} = ${snippet}` );
- return builder.format( propertyName, type, output );
+ return propertyName;
}
diff --git a/examples/jsm/renderers/nodes/core/VaryNode.js b/examples/jsm/renderers/nodes/core/VaryNode.js
index 66d09c6c197c8c..ffcd0a15f6e5a8 100644
--- a/examples/jsm/renderers/nodes/core/VaryNode.js
+++ b/examples/jsm/renderers/nodes/core/VaryNode.js
@@ -19,7 +19,7 @@ class VaryNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = this.getNodeType( builder );
const value = this.value;
@@ -30,7 +30,7 @@ class VaryNode extends Node {
// force nodeVary.snippet work in vertex stage
builder.flowNodeFromShaderStage( NodeShaderStage.Vertex, value, type, propertyName );
- return builder.format( propertyName, type, output );
+ return propertyName;
}
diff --git a/examples/jsm/renderers/nodes/display/ColorSpaceNode.js b/examples/jsm/renderers/nodes/display/ColorSpaceNode.js
index ef6689f19093ba..55c1adcdae7fa0 100644
--- a/examples/jsm/renderers/nodes/display/ColorSpaceNode.js
+++ b/examples/jsm/renderers/nodes/display/ColorSpaceNode.js
@@ -85,35 +85,27 @@ class ColorSpaceNode extends TempNode {
}
- generate( builder, output ) {
+ generate( builder ) {
+
+ const type = this.getNodeType( builder );
const method = this.method;
const input = this.input;
if ( method !== ColorSpaceNode.LINEAR_TO_LINEAR ) {
- const nodeData = builder.getDataFromNode( this );
-
- let encodingFunctionCallNode = nodeData.encodingFunctionCallNode;
-
- if (encodingFunctionCallNode === undefined) {
-
- const encodingFunctionNode = EncodingFunctions[ method ];
-
- encodingFunctionCallNode = encodingFunctionNode.call( {
- value: input,
- factor: this.factor
- } );
-
- nodeData.encodingFunctionCallNode = encodingFunctionCallNode;
+ const encodingFunctionNode = EncodingFunctions[ method ];
- }
+ const encodingFunctionCallNode = encodingFunctionNode.call( {
+ value: input,
+ factor: this.factor
+ } );
- return encodingFunctionCallNode.build( builder, output );
+ return encodingFunctionCallNode.build( builder, type );
} else {
- return input.build( builder, output );
+ return input.build( builder, type );
}
diff --git a/examples/jsm/renderers/nodes/display/NormalMapNode.js b/examples/jsm/renderers/nodes/display/NormalMapNode.js
index 67b4cdb67ee540..23ba57c7cf50f0 100644
--- a/examples/jsm/renderers/nodes/display/NormalMapNode.js
+++ b/examples/jsm/renderers/nodes/display/NormalMapNode.js
@@ -50,12 +50,11 @@ class NormalMapNode extends TempNode {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = this.getNodeType( builder );
- const normalMapType = this.normalMapType;
- const nodeData = builder.getDataFromNode( this );
+ const normalMapType = this.normalMapType;
const normalOP = new OperatorNode( '*', this.value, new FloatNode( 2.0 ).setConst( true ) );
const normalMap = new OperatorNode( '-', normalOP, new FloatNode( 1.0 ).setConst( true ) );
@@ -66,29 +65,19 @@ class NormalMapNode extends TempNode {
const normal = new MathNode( MathNode.NORMALIZE, vertexNormalNode );
- return normal.build( builder, output );
+ return normal.build( builder, type );
} else if ( normalMapType === TangentSpaceNormalMap ) {
- let perturbNormal2ArbCall = nodeData.perturbNormal2ArbCall;
-
- if (perturbNormal2ArbCall === undefined) {
-
- perturbNormal2ArbCall = perturbNormal2Arb.call( {
- eye_pos: new PositionNode( PositionNode.VIEW ),
- surf_norm: new NormalNode( NormalNode.VIEW ),
- mapN: normalMap,
- faceDirection: new FloatNode( 1.0 ).setConst( true ),
- uv: new UVNode()
- } );
+ const perturbNormal2ArbCall = perturbNormal2Arb.call( {
+ eye_pos: new PositionNode( PositionNode.VIEW ),
+ surf_norm: new NormalNode( NormalNode.VIEW ),
+ mapN: normalMap,
+ faceDirection: new FloatNode( 1.0 ).setConst( true ),
+ uv: new UVNode()
+ } );
- nodeData.perturbNormal2ArbCall = perturbNormal2ArbCall;
-
- }
-
- const snippet = perturbNormal2ArbCall.build( builder, output );
-
- return builder.format( snippet, type, output );
+ return perturbNormal2ArbCall.build( builder, type );
}
diff --git a/examples/jsm/renderers/nodes/lights/LightContextNode.js b/examples/jsm/renderers/nodes/lights/LightContextNode.js
index aa5e83b414727c..24547b798aa154 100644
--- a/examples/jsm/renderers/nodes/lights/LightContextNode.js
+++ b/examples/jsm/renderers/nodes/lights/LightContextNode.js
@@ -15,7 +15,7 @@ class LightContextNode extends ContextNode {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = this.getNodeType( builder );
@@ -40,13 +40,11 @@ class LightContextNode extends ContextNode {
}
- const totalLightSnippet = `( ${reflectedLight}.directDiffuse + ${reflectedLight}.directSpecular )`;
-
// add code
- super.generate( builder, output );
+ super.generate( builder, type );
- return builder.format( totalLightSnippet, type, output );
+ return `( ${reflectedLight}.directDiffuse + ${reflectedLight}.directSpecular )`;
}
diff --git a/examples/jsm/renderers/nodes/lights/LightNode.js b/examples/jsm/renderers/nodes/lights/LightNode.js
index 26ebcda2ca5294..8a9b12d4f9ebe6 100644
--- a/examples/jsm/renderers/nodes/lights/LightNode.js
+++ b/examples/jsm/renderers/nodes/lights/LightNode.js
@@ -52,7 +52,9 @@ class LightNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
+
+ const type = this.getNodeType( builder );
this.lightPositionView.object3d = this.light;
@@ -72,7 +74,7 @@ class LightNode extends Node {
}
- return this.color.build( builder, output );
+ return this.color.build( builder, type );
}
diff --git a/examples/jsm/renderers/nodes/lights/LightsNode.js b/examples/jsm/renderers/nodes/lights/LightsNode.js
index 3c6aa7b8ba07ad..e439b8ed29820e 100644
--- a/examples/jsm/renderers/nodes/lights/LightsNode.js
+++ b/examples/jsm/renderers/nodes/lights/LightsNode.js
@@ -11,7 +11,7 @@ class LightsNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
const lightNodes = this.lightNodes;
@@ -21,7 +21,7 @@ class LightsNode extends Node {
}
- return builder.format( 'vec3( 0.0 )', this.getNodeType( builder ), output );
+ return 'vec3( 0.0 )';
}
diff --git a/examples/jsm/renderers/nodes/math/MathNode.js b/examples/jsm/renderers/nodes/math/MathNode.js
index 1227dfdbe6ccff..2222efe073da3a 100644
--- a/examples/jsm/renderers/nodes/math/MathNode.js
+++ b/examples/jsm/renderers/nodes/math/MathNode.js
@@ -105,7 +105,7 @@ class MathNode extends TempNode {
}
- generate( builder, output ) {
+ generate( builder ) {
const method = this.method;
@@ -114,11 +114,11 @@ class MathNode extends TempNode {
if ( method === MathNode.NEGATE ) {
- return builder.format( '( -' + this.a.build( builder, inputType ) + ' )', type, output );
+ return '( -' + this.a.build( builder, inputType ) + ' )';
} else if ( method === MathNode.INVERT ) {
- return builder.format( '( 1.0 - ' + this.a.build( builder, inputType ) + ' )', type, output );
+ return '( 1.0 - ' + this.a.build( builder, inputType ) + ' )';
} else {
@@ -177,7 +177,7 @@ class MathNode extends TempNode {
}
- return builder.format( `${method}( ${params.join(', ')} )`, type, output );
+ return `${method}( ${params.join(', ')} )`;
}
diff --git a/examples/jsm/renderers/nodes/math/OperatorNode.js b/examples/jsm/renderers/nodes/math/OperatorNode.js
index 7e59a03e0dbe3f..4e393295e14e35 100644
--- a/examples/jsm/renderers/nodes/math/OperatorNode.js
+++ b/examples/jsm/renderers/nodes/math/OperatorNode.js
@@ -22,13 +22,13 @@ class OperatorNode extends TempNode {
// matrix x vector
- return typeB;
+ return builder.getVectorFromMatrix( typeA );
} else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) {
// vector x matrix
- return typeA;
+ return builder.getVectorFromMatrix( typeB );
} else if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
@@ -72,7 +72,7 @@ class OperatorNode extends TempNode {
const a = this.a.build( builder, typeA );
const b = this.b.build( builder, typeB );
- return builder.format( `( ${a} ${this.op} ${b} )`, type, output );
+ return `( ${a} ${this.op} ${b} )`;
}
diff --git a/examples/jsm/renderers/nodes/procedural/CheckerNode.js b/examples/jsm/renderers/nodes/procedural/CheckerNode.js
new file mode 100644
index 00000000000000..189d598b2f66c7
--- /dev/null
+++ b/examples/jsm/renderers/nodes/procedural/CheckerNode.js
@@ -0,0 +1,38 @@
+import FunctionNode from '../core/FunctionNode.js';
+import Node from '../core/Node.js';
+import UVNode from '../accessors/UVNode.js';
+
+import { ShaderNode, float, add, mul, floor, mod, sign } from '../ShaderNode.js';
+
+// Three.JS Shader Language
+const checkerShaderNode = ShaderNode( ( uv ) => {
+
+ uv = mul( uv, 2.0 );
+
+ const cx = floor( uv.x );
+ const cy = floor( uv.y );
+ const result = mod( add( cx, cy ), 2.0 );
+
+ return sign( result );
+
+} );
+
+class CheckerNode extends Node {
+
+ constructor( uv = new UVNode() ) {
+
+ super( 'float' );
+
+ this.uv = uv;
+
+ }
+
+ generate( builder, output ) {
+
+ return checkerShaderNode( this.uv ).build( builder, output );
+
+ }
+
+}
+
+export default CheckerNode;
diff --git a/examples/jsm/renderers/nodes/utils/JoinNode.js b/examples/jsm/renderers/nodes/utils/JoinNode.js
index 642b88c04e1042..447fb0758a203c 100644
--- a/examples/jsm/renderers/nodes/utils/JoinNode.js
+++ b/examples/jsm/renderers/nodes/utils/JoinNode.js
@@ -16,7 +16,7 @@ class JoinNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
const type = this.getNodeType( builder );
const values = this.values;
@@ -33,9 +33,7 @@ class JoinNode extends Node {
}
- const snippet = `${type}( ${ snippetValues.join( ', ' ) } )`;
-
- return builder.format( snippet, type, output );
+ return `${type}( ${ snippetValues.join( ', ' ) } )`;
}
diff --git a/examples/jsm/renderers/nodes/utils/SplitNode.js b/examples/jsm/renderers/nodes/utils/SplitNode.js
index b373d4f7df68e8..4fad8bf2189912 100644
--- a/examples/jsm/renderers/nodes/utils/SplitNode.js
+++ b/examples/jsm/renderers/nodes/utils/SplitNode.js
@@ -17,14 +17,26 @@ class SplitNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const type = this.node.getNodeType( builder );
- const nodeSnippet = this.node.build( builder, type );
+ const node = this.node;
+ const nodeTypeLength = builder.getTypeLength( node.getNodeType( builder ) );
- const snippet = `${nodeSnippet}.${this.components}`;
+ const components = this.components;
- return builder.format( snippet, this.getNodeType( builder ), output );
+ let type = null;
+
+ if ( components.length >= nodeTypeLength ) {
+
+ // need expand the input node
+
+ type = this.getNodeType( builder );
+
+ }
+
+ const nodeSnippet = node.build( builder, type );
+
+ return `${nodeSnippet}.${this.components}`;
}
diff --git a/examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js b/examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js
index 4cd753e2ffcc10..c3777899f4696e 100644
--- a/examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js
+++ b/examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js
@@ -18,49 +18,38 @@ class SpriteSheetUVNode extends Node {
}
- generate( builder, output ) {
+ generate( builder ) {
- const nodeData = builder.getDataFromNode( this );
+ const uv = this.uv;
+ const count = this.count;
+ const frame = this.frame;
- let uvFrame = nodeData.uvFrame;
+ const one = new FloatNode( 1 ).setConst( true );
- if ( nodeData.uvFrame === undefined ) {
+ const width = new SplitNode( count, 'x' );
+ const height = new SplitNode( count, 'y' );
- const uv = this.uv;
- const count = this.count;
- const frame = this.frame;
+ const total = new OperatorNode( '*', width, height );
- const one = new FloatNode( 1 ).setConst( true );
+ const roundFrame = new MathNode( MathNode.FLOOR, new MathNode( MathNode.MOD, frame, total ) );
- const width = new SplitNode( count, 'x' );
- const height = new SplitNode( count, 'y' );
+ const frameNum = new OperatorNode( '+', roundFrame, one );
- const total = new OperatorNode( '*', width, height );
+ const cell = new MathNode( MathNode.MOD, roundFrame, width );
+ const row = new MathNode( MathNode.CEIL, new OperatorNode( '/', frameNum, width ) );
+ const rowInv = new OperatorNode( '-', height, row );
- const roundFrame = new MathNode( MathNode.FLOOR, new MathNode( MathNode.MOD, frame, total ) );
+ const scale = new OperatorNode( '/', one, count );
- const frameNum = new OperatorNode( '+', roundFrame, one );
+ const uvFrameOffset = new JoinNode( [
+ new OperatorNode( '*', cell, new SplitNode( scale, 'x' ) ),
+ new OperatorNode( '*', rowInv, new SplitNode( scale, 'y' ) )
+ ] );
- const cell = new MathNode( MathNode.MOD, roundFrame, width );
- const row = new MathNode( MathNode.CEIL, new OperatorNode( '/', frameNum, width ) );
- const rowInv = new OperatorNode( '-', height, row );
+ const uvScale = new OperatorNode( '*', uv, scale );
+ const uvFrame = new OperatorNode( '+', uvScale, uvFrameOffset );
- const scale = new OperatorNode( '/', one, count );
-
- const uvFrameOffset = new JoinNode( [
- new OperatorNode( '*', cell, new SplitNode( scale, 'x' ) ),
- new OperatorNode( '*', rowInv, new SplitNode( scale, 'y' ) )
- ] );
-
- const uvScale = new OperatorNode( '*', uv, scale );
-
- uvFrame = new OperatorNode( '+', uvScale, uvFrameOffset );
-
- nodeData.uvFrame = uvFrame;
-
- }
-
- return uvFrame.build( builder, output );
+ return uvFrame.build( builder, this.getNodeType( builder ) );
}
diff --git a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
index acdf02491ac298..df3c17efa93b0d 100644
--- a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
+++ b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
@@ -2,7 +2,7 @@ import NodeBuilder from '../../nodes/core/NodeBuilder.js';
import NodeSlot from '../../nodes/core/NodeSlot.js';
import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
-import { ShaderChunk } from 'three';
+import { ShaderChunk, LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
const shaderStages = [ 'vertex', 'fragment' ];
@@ -262,6 +262,20 @@ class WebGLNodeBuilder extends NodeBuilder {
}
+ getTextureEncodingFromMap( map ) {
+
+ const isWebGL2 = this.renderer.capabilities.isWebGL2;
+
+ if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
+
+ return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
+
+ }
+
+ return super.getTextureEncodingFromMap( map );
+
+ }
+
build() {
super.build();
diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
index 17c775b7ed8213..e263af2a77adc0 100644
--- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
+++ b/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
@@ -393,21 +393,6 @@ class WebGPUNodeBuilder extends NodeBuilder {
}
- composeShaderCode( code, snippet ) {
-
- // use regex maybe for security?
- const versionStrIndex = code.indexOf( '\n' );
-
- let finalCode = code.substr( 0, versionStrIndex ) + '\n\n';
-
- finalCode += snippet;
-
- finalCode += code.substr( versionStrIndex );
-
- return finalCode;
-
- }
-
build() {
const keywords = this.getContextValue( 'keywords' );
@@ -422,13 +407,28 @@ class WebGPUNodeBuilder extends NodeBuilder {
super.build();
- this.vertexShader = this.composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
- this.fragmentShader = this.composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
+ this.vertexShader = this._composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
+ this.fragmentShader = this._composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
return this;
}
+ _composeShaderCode( code, snippet ) {
+
+ // use regex maybe for security?
+ const versionStrIndex = code.indexOf( '\n' );
+
+ let finalCode = code.substr( 0, versionStrIndex ) + '\n\n';
+
+ finalCode += snippet;
+
+ finalCode += code.substr( versionStrIndex );
+
+ return finalCode;
+
+ }
+
_getNodeUniform( uniformNode, type ) {
if ( type === 'float' ) return new FloatNodeUniform( uniformNode );
diff --git a/examples/models/gltf/SittingBox.glb b/examples/models/gltf/SittingBox.glb
deleted file mode 100644
index 7cdd7d78d19deafdd644473244f3ca0ec41100f6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 422392
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 =UM&tKeE2;C9QCb)S_y@lr-x
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