Skip to content

Commit

Permalink
WebGLRenderer: Allow for copying 2d targets to and from layers of 3d …
Browse files Browse the repository at this point in the history
…textures (#29710)

* handle 2d and 3d cases in copyTextureToTexture3D

* Fix 3d check

* comments

* More comments

* Call 3d version from copyTextureToTexture

* Move texture properties back

* Adjust condition

* Deprecate copyTextureToTexture3D, update docs

* Add missing line to BatchedMesh

* Docs spelling

* Update docs
  • Loading branch information
gkjohnson authored Oct 23, 2024
1 parent c58511d commit 3293572
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 153 deletions.
16 changes: 4 additions & 12 deletions docs/api/en/renderers/WebGLRenderer.html
Original file line number Diff line number Diff line change
Expand Up @@ -380,21 +380,13 @@ <h3>
</p>

<h3>
[method:undefined copyTextureToTexture]( [param:Texture srcTexture], [param:Texture dstTexture], [param:Box2 srcRegion], [param:Vector2 dstPosition], [param:Number level] )
</h3>
<p>
Copies the pixels of a texture in the bounds '[page:Box2 srcRegion]' in the destination texture starting from the given position.
The `depthTexture` and `texture` property of render targets are supported as well.<br />
When using render target textures as `srcTexture` and `dstTexture`, you must make sure both render targets are intitialized e.g. via [page:.initRenderTarget]().
</p>

<h3>
[method:undefined copyTextureToTexture3D]( [param:Texture srcTexture], [param:Texture dstTexture], [param:Box3 srcRegion], [param:Vector3 dstPosition], [param:Number level] )
[method:undefined copyTextureToTexture]( [param:Texture srcTexture], [param:Texture dstTexture], [param:Box2 srcRegion] | [param:Box3 srcRegion], [param:Vector2 dstPosition] | [param:Vector3 dstPosition], [param:Number dstLevel] )
</h3>
<p>
Copies the pixels of a texture in the bounds '[page:Box3 srcRegion]' in the destination texture starting from the given position.
The `depthTexture` and `texture` property of 3D render targets are supported as well.<br />
When using render target textures as `srcTexture` and `dstTexture`, you must make sure both render targets are intitialized e.g. via [page:.initRenderTarget]().
2D Texture, 3D Textures, or a mix of the two can be used as source and destination texture arguments for copying between layers of 3d textures.</br>
The `depthTexture` and `texture` property of render targets are supported as well.<br />
When using render target textures as `srcTexture` and `dstTexture`, you must make sure both render targets are initialized e.g. via [page:.initRenderTarget]().
</p>

<h3>[method:undefined dispose]( )</h3>
Expand Down
1 change: 1 addition & 0 deletions src/objects/BatchedMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ class BatchedMesh extends Mesh {
}

nextVertexStart += geometryInfo.reservedVertexCount;
geometryInfo.start = geometry.index ? geometryInfo.indexStart : geometryInfo.vertexStart;

// step the next geometry points to the shifted position
this._nextIndexStart = geometry.index ? geometryInfo.indexStart + geometryInfo.reservedIndexCount : 0;
Expand Down
220 changes: 79 additions & 141 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2579,151 +2579,24 @@ class WebGLRenderer {

}

let width, height, minX, minY;
let dstX, dstY;
if ( srcRegion !== null ) {

width = srcRegion.max.x - srcRegion.min.x;
height = srcRegion.max.y - srcRegion.min.y;
minX = srcRegion.min.x;
minY = srcRegion.min.y;

} else {

width = srcTexture.image.width;
height = srcTexture.image.height;
minX = 0;
minY = 0;

}

if ( dstPosition !== null ) {

dstX = dstPosition.x;
dstY = dstPosition.y;

} else {

dstX = 0;
dstY = 0;

}

const glFormat = utils.convert( dstTexture.format );
const glType = utils.convert( dstTexture.type );

textures.setTexture2D( dstTexture, 0 );

// As another texture upload may have changed pixelStorei
// parameters, make sure they are correct for the dstTexture
_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );

const currentUnpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );
const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );
const currentUnpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );
const currentUnpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS );
const currentUnpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES );

const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ level ] : srcTexture.image;

_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width );
_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height );
_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, minX );
_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, minY );

if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {

const srcTextureProperties = properties.get( srcTexture );
const dstTextureProperties = properties.get( dstTexture );
const srcRenderTargetProperties = properties.get( srcTextureProperties.__renderTarget );
const dstRenderTargetProperties = properties.get( dstTextureProperties.__renderTarget );

state.bindFramebuffer( _gl.READ_FRAMEBUFFER, srcRenderTargetProperties.__webglFramebuffer );
state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, dstRenderTargetProperties.__webglFramebuffer );

if ( srcTexture.isDepthTexture ) {

_gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, _gl.DEPTH_BUFFER_BIT, _gl.NEAREST );

} else {

_gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, minX, minY, width, height );

}

state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null );
state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null );

} else {

if ( srcTexture.isDataTexture ) {

_gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data );

} else {

if ( srcTexture.isCompressedTexture ) {

_gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data );

} else {

_gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image );

}

}

}

_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );

// Generate mipmaps only when copying level 0
if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D );

state.unbindTexture();

};

this.copyTextureToTexture3D = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {

// support previous signature with source box first
if ( srcTexture.isTexture !== true ) {

// @deprecated, r165
warnOnce( 'WebGLRenderer: copyTextureToTexture3D function signature has changed.' );

srcRegion = arguments[ 0 ] || null;
dstPosition = arguments[ 1 ] || null;
srcTexture = arguments[ 2 ];
dstTexture = arguments[ 3 ];
level = arguments[ 4 ] || 0;

}

// gather the necessary dimensions to copy
let width, height, depth, minX, minY, minZ;
let dstX, dstY, dstZ;
const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ level ] : srcTexture.image;
if ( srcRegion !== null ) {

width = srcRegion.max.x - srcRegion.min.x;
height = srcRegion.max.y - srcRegion.min.y;
depth = srcRegion.max.z - srcRegion.min.z;
depth = srcRegion.isBox3 ? srcRegion.max.z - srcRegion.min.z : 1;
minX = srcRegion.min.x;
minY = srcRegion.min.y;
minZ = srcRegion.min.z;
minZ = srcRegion.isBox3 ? srcRegion.min.z : 0;

} else {

width = image.width;
height = image.height;
depth = image.depth;
depth = image.depth || 1;
minX = 0;
minY = 0;
minZ = 0;
Expand All @@ -2744,6 +2617,7 @@ class WebGLRenderer {

}

// Set up the destination target
const glFormat = utils.convert( dstTexture.format );
const glType = utils.convert( dstTexture.type );
let glTarget;
Expand All @@ -2760,15 +2634,16 @@ class WebGLRenderer {

} else {

console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' );
return;
textures.setTexture2D( dstTexture, 0 );
glTarget = _gl.TEXTURE_2D;

}

_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );

// used for copying data from cpu
const currentUnpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );
const currentUnpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );
const currentUnpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );
Expand All @@ -2781,6 +2656,9 @@ class WebGLRenderer {
_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, minY );
_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, minZ );

// set up the src texture
const isSrc3D = srcTexture.isDataArrayTexture || srcTexture.isData3DTexture;
const isDst3D = dstTexture.isDataArrayTexture || dstTexture.isData3DTexture;
if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {

const srcTextureProperties = properties.get( srcTexture );
Expand All @@ -2793,17 +2671,31 @@ class WebGLRenderer {

for ( let i = 0; i < depth; i ++ ) {

_gl.framebufferTextureLayer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( srcTexture ).__webglTexture, level, minZ + i );
// if the source or destination are a 3d target then a layer needs to be bound
if ( isSrc3D ) {

_gl.framebufferTextureLayer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( srcTexture ).__webglTexture, level, minZ + i );

}

if ( srcTexture.isDepthTexture ) {

_gl.framebufferTextureLayer( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( dstTexture ).__webglTexture, level, dstZ + i );
if ( isDst3D ) {

_gl.framebufferTextureLayer( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, properties.get( dstTexture ).__webglTexture, level, dstZ + i );

}

_gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, _gl.DEPTH_BUFFER_BIT, _gl.NEAREST );

} else {
} else if ( isDst3D ) {

_gl.copyTexSubImage3D( glTarget, level, dstX, dstY, dstZ + i, minX, minY, width, height );

} else {

_gl.copyTexSubImage2D( glTarget, level, dstX, dstY, dstZ + i, minX, minY, width, height );

}

}
Expand All @@ -2813,13 +2705,14 @@ class WebGLRenderer {

} else {

if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {
if ( isDst3D ) {

_gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );
// copy data into the 3d texture
if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {

} else {
_gl.texSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );

if ( dstTexture.isCompressedArrayTexture ) {
} else if ( dstTexture.isCompressedArrayTexture ) {

_gl.compressedTexSubImage3D( glTarget, level, dstX, dstY, dstZ, width, height, depth, glFormat, image.data );

Expand All @@ -2829,23 +2722,68 @@ class WebGLRenderer {

}

} else {

// copy data into the 2d texture
if ( srcTexture.isDataTexture ) {

_gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data );

} else if ( srcTexture.isCompressedTexture ) {

_gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data );

} else {

_gl.texSubImage2D( _gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image );

}

}

}

// reset values
_gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
_gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
_gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
_gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
_gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );

// Generate mipmaps only when copying level 0
if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget );
if ( level === 0 && dstTexture.generateMipmaps ) {

_gl.generateMipmap( glTarget );

}

state.unbindTexture();

};

this.copyTextureToTexture3D = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {

// support previous signature with source box first
if ( srcTexture.isTexture !== true ) {

// @deprecated, r165
warnOnce( 'WebGLRenderer: copyTextureToTexture3D function signature has changed.' );

srcRegion = arguments[ 0 ] || null;
dstPosition = arguments[ 1 ] || null;
srcTexture = arguments[ 2 ];
dstTexture = arguments[ 3 ];
level = arguments[ 4 ] || 0;

}

// @deprecated, r170
warnOnce( 'WebGLRenderer: copyTextureToTexture3D function has been deprecated. Use "copyTextureToTexture" instead.' );

return this.copyTextureToTexture( srcTexture, dstTexture, srcRegion, dstPosition, level );

};

this.initRenderTarget = function ( target ) {

if ( properties.get( target ).__webglFramebuffer === undefined ) {
Expand Down

0 comments on commit 3293572

Please sign in to comment.