@@ -17,6 +17,32 @@ const tempEnuFrame = /* @__PURE__ */ new Matrix4();
1717const tempLocalQuat = /* @__PURE__ */ new Quaternion ( ) ;
1818const tempLatLon = { } ;
1919
20+ // Oct-encoding helper functions
21+ // Decode oct-encoded normal from unsigned [0, rangeMax] to normalized vector
22+ // Based on CesiumJS's AttributeCompression.octDecodeInRange
23+ function octDecodeInRange ( x , y , rangeMax , result ) {
24+
25+ // Map from unsigned [0, rangeMax] to signed normalized [-1.0, 1.0]
26+ x = ( x / rangeMax ) * 2.0 - 1.0 ;
27+ y = ( y / rangeMax ) * 2.0 - 1.0 ;
28+
29+ result . x = x ;
30+ result . y = y ;
31+ result . z = 1.0 - Math . abs ( x ) - Math . abs ( y ) ;
32+
33+ if ( result . z < 0.0 ) {
34+
35+ const oldX = result . x ;
36+ result . x = ( 1.0 - Math . abs ( result . y ) ) * ( oldX >= 0.0 ? 1.0 : - 1.0 ) ;
37+ result . y = ( 1.0 - Math . abs ( oldX ) ) * ( result . y >= 0.0 ? 1.0 : - 1.0 ) ;
38+
39+ }
40+
41+ result . normalize ( ) ;
42+ return result ;
43+
44+ }
45+
2046export class I3DMLoader extends I3DMLoaderBase {
2147
2248 constructor ( manager = DefaultLoadingManager ) {
@@ -85,24 +111,13 @@ export class I3DMLoader extends I3DMLoaderBase {
85111 const QUANTIZED_VOLUME_SCALE = featureTable . getData ( 'QUANTIZED_VOLUME_SCALE' , 1 , 'FLOAT' , 'VEC3' ) ;
86112 const NORMAL_UP = featureTable . getData ( 'NORMAL_UP' , INSTANCES_LENGTH , 'FLOAT' , 'VEC3' ) ;
87113 const NORMAL_RIGHT = featureTable . getData ( 'NORMAL_RIGHT' , INSTANCES_LENGTH , 'FLOAT' , 'VEC3' ) ;
114+ const NORMAL_UP_OCT32P = featureTable . getData ( 'NORMAL_UP_OCT32P' , INSTANCES_LENGTH , 'UNSIGNED_SHORT' , 'VEC2' ) ;
115+ const NORMAL_RIGHT_OCT32P = featureTable . getData ( 'NORMAL_RIGHT_OCT32P' , INSTANCES_LENGTH , 'UNSIGNED_SHORT' , 'VEC2' ) ;
88116 const SCALE_NON_UNIFORM = featureTable . getData ( 'SCALE_NON_UNIFORM' , INSTANCES_LENGTH , 'FLOAT' , 'VEC3' ) ;
89117 const SCALE = featureTable . getData ( 'SCALE' , INSTANCES_LENGTH , 'FLOAT' , 'SCALAR' ) ;
90118 const RTC_CENTER = featureTable . getData ( 'RTC_CENTER' , 1 , 'FLOAT' , 'VEC3' ) ;
91119 const EAST_NORTH_UP = featureTable . getData ( 'EAST_NORTH_UP' ) ;
92120
93- [
94- 'NORMAL_UP_OCT32P' ,
95- 'NORMAL_RIGHT_OCT32P' ,
96- ] . forEach ( feature => {
97-
98- if ( feature in featureTable . header ) {
99-
100- console . warn ( `I3DMLoader: Unsupported FeatureTable feature "${ feature } " detected.` ) ;
101-
102- }
103-
104- } ) ;
105-
106121 // use quantized position if position is missing
107122 if ( ! POSITION && POSITION_QUANTIZED ) {
108123
@@ -173,7 +188,9 @@ export class I3DMLoader extends I3DMLoaderBase {
173188
174189 // account for EAST_NORTH_UP per-instance below
175190
176- if ( NORMAL_UP ) {
191+ // Use NORMAL_UP and NORMAL_RIGHT if available (higher precision)
192+ // Otherwise fall back to oct-encoded normals
193+ if ( NORMAL_UP && NORMAL_RIGHT ) {
177194
178195 tempUp . set (
179196 NORMAL_UP [ i * 3 + 0 ] ,
@@ -198,6 +215,34 @@ export class I3DMLoader extends I3DMLoaderBase {
198215
199216 tempQuat . setFromRotationMatrix ( tempMat ) ;
200217
218+ } else if ( NORMAL_UP_OCT32P && NORMAL_RIGHT_OCT32P ) {
219+
220+ // Decode oct-encoded normals
221+ octDecodeInRange (
222+ NORMAL_UP_OCT32P [ i * 2 + 0 ] ,
223+ NORMAL_UP_OCT32P [ i * 2 + 1 ] ,
224+ 65535 ,
225+ tempUp
226+ ) ;
227+
228+ octDecodeInRange (
229+ NORMAL_RIGHT_OCT32P [ i * 2 + 0 ] ,
230+ NORMAL_RIGHT_OCT32P [ i * 2 + 1 ] ,
231+ 65535 ,
232+ tempRight
233+ ) ;
234+
235+ tempFwd . crossVectors ( tempRight , tempUp )
236+ . normalize ( ) ;
237+
238+ tempMat . makeBasis (
239+ tempRight ,
240+ tempUp ,
241+ tempFwd ,
242+ ) ;
243+
244+ tempQuat . setFromRotationMatrix ( tempMat ) ;
245+
201246 }
202247
203248 // scale
0 commit comments