@@ -19,6 +19,7 @@ import {
1919 Bone ,
2020 SRGBColorSpace ,
2121 Texture ,
22+ Vector2 ,
2223 Vector3 ,
2324 VectorKeyframeTrack
2425} from 'three' ;
@@ -1092,6 +1093,28 @@ class USDComposer {
10921093
10931094 }
10941095
1096+ // Second UV set (st1) for lightmaps/AO
1097+ const { uvs2, uv2Indices } = this . _findUV2Primvar ( fields ) ;
1098+
1099+ if ( uvs2 && uvs2 . length > 0 ) {
1100+
1101+ let uv2Data = uvs2 ;
1102+
1103+ if ( uv2Indices && uv2Indices . length > 0 ) {
1104+
1105+ const triangulatedUv2Indices = this . _triangulateIndices ( uv2Indices , faceVertexCounts ) ;
1106+ uv2Data = this . _expandAttribute ( uvs2 , triangulatedUv2Indices , 2 ) ;
1107+
1108+ } else if ( indices && uvs2 . length / 2 === points . length / 3 ) {
1109+
1110+ uv2Data = this . _expandAttribute ( uvs2 , indices , 2 ) ;
1111+
1112+ }
1113+
1114+ geometry . setAttribute ( 'uv1' , new BufferAttribute ( new Float32Array ( uv2Data ) , 2 ) ) ;
1115+
1116+ }
1117+
10951118 // Add skinning attributes
10961119 if ( hasSkinning ) {
10971120
@@ -1164,6 +1187,7 @@ class USDComposer {
11641187 if ( ! faceVertexCounts || faceVertexCounts . length === 0 ) return geometry ;
11651188
11661189 const { uvs, uvIndices } = this . _findUVPrimvar ( fields ) ;
1190+ const { uvs2, uv2Indices } = this . _findUV2Primvar ( fields ) ;
11671191 const normals = fields [ 'normals' ] || fields [ 'primvars:normals' ] ;
11681192
11691193 const jointIndices = hasSkinning ? fields [ 'primvars:skel:jointIndices' ] : null ;
@@ -1261,6 +1285,7 @@ class USDComposer {
12611285 // Triangulate original data
12621286 const origIndices = this . _triangulateIndices ( faceVertexIndices , faceVertexCounts ) ;
12631287 const origUvIndices = uvIndices ? this . _triangulateIndices ( uvIndices , faceVertexCounts ) : null ;
1288+ const origUv2Indices = uv2Indices ? this . _triangulateIndices ( uv2Indices , faceVertexCounts ) : null ;
12641289
12651290 const numFaceVertices = faceVertexCounts . reduce ( ( a , b ) => a + b , 0 ) ;
12661291 const hasFaceVaryingNormals = normals && normals . length / 3 === numFaceVertices ;
@@ -1272,6 +1297,7 @@ class USDComposer {
12721297 const vertexCount = triangleCount * 3 ;
12731298 const positions = new Float32Array ( vertexCount * 3 ) ;
12741299 const uvData = uvs ? new Float32Array ( vertexCount * 2 ) : null ;
1300+ const uv1Data = uvs2 ? new Float32Array ( vertexCount * 2 ) : null ;
12751301 const normalData = normals ? new Float32Array ( vertexCount * 3 ) : null ;
12761302 const skinIndexData = jointIndices ? new Uint16Array ( vertexCount * 4 ) : null ;
12771303 const skinWeightData = jointWeights ? new Float32Array ( vertexCount * 4 ) : null ;
@@ -1307,6 +1333,23 @@ class USDComposer {
13071333
13081334 }
13091335
1336+ if ( uv1Data && uvs2 ) {
1337+
1338+ if ( origUv2Indices ) {
1339+
1340+ const uv2Idx = origUv2Indices [ origIdx ] ;
1341+ uv1Data [ newIdx * 2 ] = uvs2 [ uv2Idx * 2 ] ;
1342+ uv1Data [ newIdx * 2 + 1 ] = uvs2 [ uv2Idx * 2 + 1 ] ;
1343+
1344+ } else if ( uvs2 . length / 2 === points . length / 3 ) {
1345+
1346+ uv1Data [ newIdx * 2 ] = uvs2 [ pointIdx * 2 ] ;
1347+ uv1Data [ newIdx * 2 + 1 ] = uvs2 [ pointIdx * 2 + 1 ] ;
1348+
1349+ }
1350+
1351+ }
1352+
13101353 if ( normalData && normals ) {
13111354
13121355 if ( origNormalIndices ) {
@@ -1358,6 +1401,12 @@ class USDComposer {
13581401
13591402 }
13601403
1404+ if ( uv1Data ) {
1405+
1406+ geometry . setAttribute ( 'uv1' , new BufferAttribute ( uv1Data , 2 ) ) ;
1407+
1408+ }
1409+
13611410 if ( normalData ) {
13621411
13631412 geometry . setAttribute ( 'normal' , new BufferAttribute ( normalData , 3 ) ) ;
@@ -1410,6 +1459,15 @@ class USDComposer {
14101459
14111460 }
14121461
1462+ _findUV2Primvar ( fields ) {
1463+
1464+ // Look for second UV set (st1, commonly used for lightmaps/AO)
1465+ const uvs2 = fields [ 'primvars:st1' ] ;
1466+ const uv2Indices = fields [ 'primvars:st1:indices' ] ;
1467+ return { uvs2, uv2Indices } ;
1468+
1469+ }
1470+
14131471 _triangulateIndices ( indices , counts ) {
14141472
14151473 const triangulated = [ ] ;
@@ -1754,6 +1812,15 @@ class USDComposer {
17541812 // Normal map
17551813 applyTextureFromConnection ( 'inputs:normal' , 'normalMap' , NoColorSpace , null ) ;
17561814
1815+ // Apply normal map scale from UsdUVTexture scale input
1816+ if ( material . normalMap && material . normalMap . userData . scale ) {
1817+
1818+ const scale = material . normalMap . userData . scale ;
1819+ // UsdUVTexture scale is float4 (r,g,b,a), use first two components for normalScale
1820+ material . normalScale = new Vector2 ( scale [ 0 ] , scale [ 1 ] ) ;
1821+
1822+ }
1823+
17571824 // Roughness
17581825 const hasRoughnessMap = applyTextureFromConnection (
17591826 'inputs:roughness' ,
@@ -1814,10 +1881,26 @@ class USDComposer {
18141881
18151882 }
18161883
1817- // Opacity
1818- if ( fields [ 'inputs:opacity' ] !== undefined ) {
1884+ // Opacity and opacity modes
1885+ const opacity = fields [ 'inputs:opacity' ] !== undefined ? fields [ 'inputs:opacity' ] : 1.0 ;
1886+ const opacityThreshold = fields [ 'inputs:opacityThreshold' ] !== undefined ? fields [ 'inputs:opacityThreshold' ] : 0.0 ;
1887+ const opacityMode = fields [ 'inputs:opacityMode' ] || 'transparent' ;
1888+
1889+ if ( opacityMode === 'presence' ) {
1890+
1891+ // Presence mode: use alpha testing (cutout transparency)
1892+ material . alphaTest = opacityThreshold ;
1893+ material . transparent = false ;
18191894
1820- const opacity = fields [ 'inputs:opacity' ] ;
1895+ if ( opacity < 1.0 ) {
1896+
1897+ material . opacity = opacity ;
1898+
1899+ }
1900+
1901+ } else if ( opacityMode === 'transparent' ) {
1902+
1903+ // Transparent mode: traditional alpha blending
18211904 if ( opacity < 1.0 ) {
18221905
18231906 material . transparent = true ;
@@ -1826,6 +1909,7 @@ class USDComposer {
18261909 }
18271910
18281911 }
1912+ // opacityMode === 'opaque': ignore opacity entirely (default material state)
18291913
18301914 }
18311915
@@ -1845,8 +1929,9 @@ class USDComposer {
18451929 const filePath = attrs [ 'inputs:file' ] ;
18461930 if ( ! filePath ) return null ;
18471931
1848- // Check for UsdTransform2d connection via inputs:st
1932+ // Check for UsdTransform2d connection via inputs:st and trace to PrimvarReader
18491933 let transformAttrs = null ;
1934+ let uvChannel = 0 ; // Default to first UV set
18501935 const stAttrPath = shaderPath + '.inputs:st' ;
18511936 const stAttrSpec = this . specsByPath [ stAttrPath ] ;
18521937
@@ -1865,23 +1950,61 @@ class USDComposer {
18651950
18661951 transformAttrs = stAttrs ;
18671952
1953+ // Trace to PrimvarReader to find UV set
1954+ const inAttrPath = stPath + '.inputs:in' ;
1955+ const inAttrSpec = this . specsByPath [ inAttrPath ] ;
1956+
1957+ if ( inAttrSpec ?. fields ?. connectionPaths ?. length > 0 ) {
1958+
1959+ const inConnPath = inAttrSpec . fields . connectionPaths [ 0 ] ;
1960+ const primvarPath = inConnPath . replace ( / < | > / g, '' ) . split ( '.' ) [ 0 ] ;
1961+ const primvarAttrs = this . _getAttributes ( primvarPath ) ;
1962+
1963+ // Check varname to determine UV channel
1964+ const varname = primvarAttrs [ 'inputs:varname' ] ;
1965+ if ( varname === 'st1' ) uvChannel = 1 ;
1966+ else if ( varname === 'st2' ) uvChannel = 2 ;
1967+
1968+ }
1969+
1970+ } else if ( stInfoId === 'UsdPrimvarReader_float2' ) {
1971+
1972+ // Direct connection to PrimvarReader
1973+ const varname = stAttrs [ 'inputs:varname' ] ;
1974+ if ( varname === 'st1' ) uvChannel = 1 ;
1975+ else if ( varname === 'st2' ) uvChannel = 2 ;
1976+
18681977 }
18691978
18701979 }
18711980
18721981 }
18731982
1874- if ( this . textureCache [ filePath ] ) {
1983+ // Extract scale and bias for texture value modification
1984+ const scale = attrs [ 'inputs:scale' ] ;
1985+ const bias = attrs [ 'inputs:bias' ] ;
1986+
1987+ // Create cache key that includes scale/bias if present
1988+ let cacheKey = filePath ;
1989+ if ( scale ) cacheKey += ':s' + scale . join ( ',' ) ;
1990+ if ( bias ) cacheKey += ':b' + bias . join ( ',' ) ;
1991+
1992+ if ( this . textureCache [ cacheKey ] ) {
18751993
1876- return this . textureCache [ filePath ] ;
1994+ return this . textureCache [ cacheKey ] ;
18771995
18781996 }
18791997
18801998 const texture = this . _loadTexture ( filePath , attrs , transformAttrs ) ;
18811999
18822000 if ( texture ) {
18832001
1884- this . textureCache [ filePath ] = texture ;
2002+ // Store scale/bias and UV channel in userData
2003+ if ( scale ) texture . userData . scale = scale ;
2004+ if ( bias ) texture . userData . bias = bias ;
2005+ if ( uvChannel !== 0 ) texture . channel = uvChannel ;
2006+
2007+ this . textureCache [ cacheKey ] = texture ;
18852008
18862009 }
18872010
0 commit comments