diff --git a/examples/jsm/exporters/GLTFExporter.js b/examples/jsm/exporters/GLTFExporter.js index 1504033b01af2c..d98bbe94a001b7 100644 --- a/examples/jsm/exporters/GLTFExporter.js +++ b/examples/jsm/exporters/GLTFExporter.js @@ -2702,9 +2702,33 @@ class GLTFWriter { } - for ( let i = 0; i < options.animations.length; ++ i ) { + // animations - this.processAnimation( options.animations[ i ], input[ 0 ] ); + if ( input.length === 1 ) { + + // default: single input, flat animations array + + for ( let i = 0; i < options.animations.length; ++ i ) { + + this.processAnimation( options.animations[ i ], input[ 0 ] ); + + } + + } else { + + // multi-input with multi-dimensional animations array + + for ( let i = 0; i < input.length; i ++ ) { + + const animations = options.animations[ i ] || []; + + for ( let j = 0; j < animations.length; ++ j ) { + + this.processAnimation( animations[ j ], input[ i ] ); + + } + + } } @@ -3717,7 +3741,8 @@ GLTFExporter.Utils = { * @property {boolean} [onlyVisible=true] - Export only visible 3D objects. * @property {boolean} [binary=false] - Export in binary (.glb) format, returning an ArrayBuffer. * @property {number} [maxTextureSize=Infinity] - Restricts the image maximum size (both width and height) to the given value. - * @property {Array} [animations=[]] - List of animations to be included in the export. + * @property {Array|Array>} [animations=[]] - List of animations to be included in the export. When exporting a single 3D object or scene, this is a flat list of clips. + * When exporting an array of multiple scenes, this must be a nested array with one list of clips per scene, matched to the input by index. * @property {boolean} [includeCustomExtensions=false] - Export custom glTF extensions defined on an object's `userData.gltfExtensions` property. **/ diff --git a/examples/jsm/geometries/LoftGeometry.js b/examples/jsm/geometries/LoftGeometry.js index c9a62c347a1788..f3e0d0d3791f76 100644 --- a/examples/jsm/geometries/LoftGeometry.js +++ b/examples/jsm/geometries/LoftGeometry.js @@ -122,18 +122,53 @@ class LoftGeometry extends BufferGeometry { const vertices = []; const uvs = []; + // uvs follow arc length so uneven sections and points map evenly + + const rowU = [ 0 ]; + + for ( let i = 1; i < rows; i ++ ) { + + let distance = 0; + + for ( let j = 0; j < columns; j ++ ) { + + distance += sections[ i ][ j ].distanceTo( sections[ i - 1 ][ j ] ); + + } + + rowU.push( rowU[ i - 1 ] + distance / columns ); + + } + + const totalU = rowU[ rows - 1 ]; + // generate vertices and uvs for ( let i = 0; i < rows; i ++ ) { const section = sections[ i ]; + // distance around the section + + const colV = [ 0 ]; + + for ( let j = 1; j < pointsPerRow; j ++ ) { + + colV.push( colV[ j - 1 ] + section[ j % columns ].distanceTo( section[ ( j - 1 ) % columns ] ) ); + + } + + const totalV = colV[ pointsPerRow - 1 ]; + for ( let j = 0; j < pointsPerRow; j ++ ) { const point = section[ j % columns ]; vertices.push( point.x, point.y, point.z ); - uvs.push( i / ( rows - 1 ), j / ( pointsPerRow - 1 ) ); + uvs.push( + totalU > 0 ? rowU[ i ] / totalU : i / ( rows - 1 ), + totalV > 0 ? colV[ j ] / totalV : j / ( pointsPerRow - 1 ) + ); } diff --git a/src/renderers/webgl-fallback/WebGLBackend.js b/src/renderers/webgl-fallback/WebGLBackend.js index ae10dfa14e1e52..833b8108c240b9 100644 --- a/src/renderers/webgl-fallback/WebGLBackend.js +++ b/src/renderers/webgl-fallback/WebGLBackend.js @@ -1934,17 +1934,35 @@ class WebGLBackend extends Backend { const isTyped = isTypedArray( array ); const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT; + // Update ranges arrive sorted and non-overlapping which makes + // it easy to merge contiguous ranges. + + let start = updateRanges[ 0 ].start; // start of the current merged range + for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { const range = updateRanges[ i ]; + const next = updateRanges[ i + 1 ]; + + const end = range.start + range.count; // exclusive end of the current range + + // keep merging while the next range is contiguous - const dataOffset = range.start * byteOffsetFactor; - const size = range.count * byteOffsetFactor; + if ( next !== undefined && next.start === end ) continue; + + // write the merged range + + const dataOffset = start * byteOffsetFactor; + const size = ( end - start ) * byteOffsetFactor; const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes gl.bufferSubData( gl.UNIFORM_BUFFER, bufferOffset, array, dataOffset, size ); + // start next if possible + + if ( next !== undefined ) start = next.start; + } } diff --git a/src/renderers/webgpu/utils/WebGPUBindingUtils.js b/src/renderers/webgpu/utils/WebGPUBindingUtils.js index 54a84971ba8e53..696fbc7e5a906f 100644 --- a/src/renderers/webgpu/utils/WebGPUBindingUtils.js +++ b/src/renderers/webgpu/utils/WebGPUBindingUtils.js @@ -214,12 +214,26 @@ class WebGPUBindingUtils { const isTyped = isTypedArray( array ); const byteOffsetFactor = isTyped ? 1 : array.BYTES_PER_ELEMENT; + // Update ranges arrive sorted and non-overlapping which makes + // it easy to merge contiguous ranges. + + let start = updateRanges[ 0 ].start; // start of the current merged range + for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { const range = updateRanges[ i ]; + const next = updateRanges[ i + 1 ]; + + const end = range.start + range.count; // exclusive end of the current range + + // keep merging while the next range is contiguous - const dataOffset = range.start * byteOffsetFactor; - const size = range.count * byteOffsetFactor; + if ( next !== undefined && next.start === end ) continue; + + // write the merged range + + const dataOffset = start * byteOffsetFactor; + const size = ( end - start ) * byteOffsetFactor; const bufferOffset = dataOffset * ( isTyped ? array.BYTES_PER_ELEMENT : 1 ); // bufferOffset is always in bytes @@ -231,6 +245,10 @@ class WebGPUBindingUtils { size ); + // start next if possible + + if ( next !== undefined ) start = next.start; + } }