Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/math/Matrix4.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,28 @@ class Matrix4 {

}

/**
* Computes and returns the determinant of the upper-left 3x3 submatrix.
*
* For affine matrices (like an object's world matrix), this value equals the
* full 4x4 {@link Matrix4#determinant} but is cheaper to compute.
*
* @return {number} The determinant of the upper-left 3x3 submatrix.
*/
determinant3x3() {

const te = this.elements;

const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ];
const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ];
const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ];

return n11 * ( n22 * n33 - n23 * n32 ) -
n12 * ( n21 * n33 - n23 * n31 ) +
n13 * ( n21 * n32 - n22 * n31 );

}

/**
* Transposes this matrix in place.
*
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,7 @@ class WebGLRenderer {

if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)

const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );

const program = setProgram( camera, scene, geometry, material, object );

Expand Down
2 changes: 1 addition & 1 deletion src/renderers/webgl-fallback/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,7 @@ class WebGLBackend extends Backend {

this._bindUniforms( renderObject.getBindings() );

const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );

state.setMaterial( material, frontFaceCW, hardwareClippingPlanes );

Expand Down
42 changes: 27 additions & 15 deletions src/renderers/webgl-fallback/utils/WebGLTextureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,30 +429,42 @@ class WebGLTextureUtils {
createTexture( texture, options ) {

const { gl, backend } = this;
const { levels, width, height, depth } = options;

const glFormat = backend.utils.convert( texture.format, texture.colorSpace );
const glType = backend.utils.convert( texture.type );
const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.normalized, texture.colorSpace, texture.isVideoTexture );
let textureGPU, glTextureType, glFormat, glType, glInternalFormat;

const textureGPU = gl.createTexture();
const glTextureType = this.getGLTextureType( texture );
if ( texture.isExternalTexture === true ) {

backend.state.bindTexture( glTextureType, textureGPU );
textureGPU = texture.sourceTexture;
glTextureType = this.getGLTextureType( texture );

this.setTextureParameters( glTextureType, texture );
} else {

if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
const { levels, width, height, depth } = options;

gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
glFormat = backend.utils.convert( texture.format, texture.colorSpace );
glType = backend.utils.convert( texture.type );
glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.normalized, texture.colorSpace, texture.isVideoTexture );

} else if ( texture.isData3DTexture ) {
textureGPU = gl.createTexture();
glTextureType = this.getGLTextureType( texture );

backend.state.bindTexture( glTextureType, textureGPU );

this.setTextureParameters( glTextureType, texture );

if ( texture.isArrayTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {

gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );

gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth );
} else if ( texture.isData3DTexture ) {

} else if ( ! texture.isVideoTexture ) {
gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth );

gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
} else if ( ! texture.isVideoTexture ) {

gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );

}

}

Expand Down Expand Up @@ -774,7 +786,7 @@ class WebGLTextureUtils {

this.deallocateRenderBuffers( renderTarget );

if ( isDefaultTexture === false ) {
if ( isDefaultTexture === false && texture.isExternalTexture !== true ) {

gl.deleteTexture( textureGPU );

Expand Down
4 changes: 2 additions & 2 deletions src/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -2058,7 +2058,7 @@ class WebGPUBackend extends Backend {
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
const primitiveTopology = utils.getPrimitiveTopology( object, material );
const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );

let needsUpdate = false;

Expand Down Expand Up @@ -2120,7 +2120,7 @@ class WebGPUBackend extends Backend {
// meshes with negative scale have a different frontFace render pipeline
// descriptor value so the following must be honored in the cache key

const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );

return [
material.transparent, material.blending, material.premultipliedAlpha,
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/webgpu/utils/WebGPUPipelineUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ class WebGPUPipelineUtils {

let flipSided = ( material.side === BackSide );

if ( object.isMesh && object.matrixWorld.determinant() < 0 ) flipSided = ! flipSided;
if ( object.isMesh && object.matrixWorld.determinant3x3() < 0 ) flipSided = ! flipSided;

descriptor.frontFace = ( flipSided === true ) ? GPUFrontFace.CW : GPUFrontFace.CCW;

Expand Down
2 changes: 1 addition & 1 deletion src/renderers/webgpu/utils/WebGPUTextureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ class WebGPUTextureUtils {
const backend = this.backend;
const textureData = backend.get( texture );

if ( textureData.texture !== undefined && isDefaultTexture === false ) textureData.texture.destroy();
if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true ) textureData.texture.destroy();

if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy();

Expand Down
3 changes: 0 additions & 3 deletions src/textures/ExternalTexture.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { Texture } from './Texture.js';
* This may be a texture from a protected media stream, device camera feed,
* or other data feeds like a depth sensor.
*
* Note that this class is only supported in {@link WebGLRenderer}, and in
* the {@link WebGPURenderer} WebGPU backend.
*
* @augments Texture
*/
class ExternalTexture extends Texture {
Expand Down
37 changes: 37 additions & 0 deletions test/unit/src/math/Matrix4.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,43 @@ export default QUnit.module( 'Maths', () => {

} );

QUnit.test( 'determinant3x3', ( assert ) => {

// for affine matrices (the typical object world matrix), the 3x3 result
// equals the full 4x4 determinant since the bottom row is [ 0, 0, 0, 1 ]

const a = new Matrix4();
const position = new Vector3( 5, - 2, 3 );
const quaternion = new Quaternion().setFromEuler( new Euler( 0.1, - 0.7, 1.3 ) );

// translation + rotation + non-uniform scale

a.compose( position, quaternion, new Vector3( 2, 3, 0.5 ) );
assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Affine matrix: Passed!' );

// reflection (negative scale on one axis flips the winding order)

a.compose( position, quaternion, new Vector3( 2, 3, - 0.5 ) );
assert.ok( a.determinant3x3() < 0, 'Reflection produces a negative determinant!' );
assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Reflection matrix: Passed!' );

// shear

a.multiply( new Matrix4().makeShear( 0.5, 0, 0.2, 0, 0.7, 0 ) );
assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Shear matrix: Passed!' );

} );

QUnit.test( 'determinant3x3 (projective matrix)', ( assert ) => {

// for non-affine (projective) matrices, the bottom row is not [ 0, 0, 0, 1 ]
// and so the 3x3 result generally differs from the full 4x4 determinant

const a = new Matrix4().makePerspective( - 1, 1, 1, - 1, 1, 100 );
assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) > eps, 'Passed!' );

} );

QUnit.test( 'transpose', ( assert ) => {

const a = new Matrix4();
Expand Down