diff --git a/examples/jsm/loaders/usd/USDAParser.js b/examples/jsm/loaders/usd/USDAParser.js index ca1f787a49ea1c..aeca91fc1ef4cc 100644 --- a/examples/jsm/loaders/usd/USDAParser.js +++ b/examples/jsm/loaders/usd/USDAParser.js @@ -460,6 +460,18 @@ class USDAParser { } + if ( header.framesPerSecond !== undefined ) { + + rootFields.framesPerSecond = parseFloat( header.framesPerSecond ); + + } + + if ( header.timeCodesPerSecond !== undefined ) { + + rootFields.timeCodesPerSecond = parseFloat( header.timeCodesPerSecond ); + + } + } specsByPath[ '/' ] = { specType: SpecType.Prim, fields: rootFields }; diff --git a/examples/jsm/loaders/usd/USDComposer.js b/examples/jsm/loaders/usd/USDComposer.js index 0e3721cf15e329..23d878b50942a0 100644 --- a/examples/jsm/loaders/usd/USDComposer.js +++ b/examples/jsm/loaders/usd/USDComposer.js @@ -109,7 +109,7 @@ class USDComposer { // Get FPS from root spec const rootSpec = this.specsByPath[ '/' ]; const rootFields = rootSpec ? rootSpec.fields : {}; - this.fps = rootFields.framesPerSecond || rootFields.timeCodesPerSecond || 30; + this.fps = rootFields.timeCodesPerSecond || rootFields.framesPerSecond || 24; const group = new Group(); this._buildHierarchy( group, '/' ); diff --git a/test/unit/addons/loaders/USDLoader.tests.js b/test/unit/addons/loaders/USDLoader.tests.js new file mode 100644 index 00000000000000..a493e2d7a50de7 --- /dev/null +++ b/test/unit/addons/loaders/USDLoader.tests.js @@ -0,0 +1,52 @@ +import { USDLoader } from '../../../../examples/jsm/loaders/USDLoader.js'; + +export default QUnit.module( 'Addons', () => { + + QUnit.module( 'Loaders', () => { + + QUnit.module( 'USDLoader', () => { + + QUnit.test( 'uses timeCodesPerSecond for USDA animation timing', ( assert ) => { + + const usda = `#usda 1.0 +( + defaultPrim = "Root" + framesPerSecond = 24 + timeCodesPerSecond = 60 +) + +def Xform "Root" +{ + def Xform "Animated" + { + float3 xformOp:translate = (0, 0, 0) + float3 xformOp:translate.timeSamples = { + 0: (0, 0, 0), + 60: (1, 0, 0), + } + uniform token[] xformOpOrder = ["xformOp:translate"] + } +}`; + + const loader = new USDLoader(); + const scene = loader.parse( usda ); + const clip = scene.animations[ 0 ]; + const track = clip.tracks[ 0 ]; + + assert.strictEqual( scene.animations.length, 1, 'One animation clip is created.' ); + assert.strictEqual( clip.name, 'TransformAnimation', 'Transform animation is created.' ); + assert.closeTo( clip.duration, 1, 0.000001, 'Animation duration uses timeCodesPerSecond.' ); + assert.strictEqual( track.name, 'Animated.position', 'Track targets the animated Xform.' ); + assert.deepEqual( + Array.from( track.times ), + [ 0, 1 ], + 'Time samples are converted to seconds.' + ); + + } ); + + } ); + + } ); + +} ); diff --git a/test/unit/three.addons.unit.js b/test/unit/three.addons.unit.js index 4f0b59483e848a..6450c2b94fc3b8 100644 --- a/test/unit/three.addons.unit.js +++ b/test/unit/three.addons.unit.js @@ -5,4 +5,5 @@ import './addons/utils/ColorUtils.tests.js'; import './addons/math/ColorSpaces.tests.js'; import './addons/curves/NURBSCurve.tests.js'; import './addons/loaders/HDRLoader.tests.js'; +import './addons/loaders/USDLoader.tests.js'; import './addons/exporters/USDZExporter.tests.js';