@@ -5,13 +5,17 @@ import {
55 PIXELFORMAT_DXT1 , PIXELFORMAT_DXT3 , PIXELFORMAT_DXT5 ,
66 PIXELFORMAT_ETC1 , PIXELFORMAT_ETC2_RGB , PIXELFORMAT_ETC2_RGBA ,
77 PIXELFORMAT_PVRTC_4BPP_RGB_1 , PIXELFORMAT_PVRTC_2BPP_RGB_1 , PIXELFORMAT_PVRTC_4BPP_RGBA_1 , PIXELFORMAT_PVRTC_2BPP_RGBA_1 ,
8+ PIXELFORMAT_R8_G8_B8 , PIXELFORMAT_R8_G8_B8_A8 , PIXELFORMAT_SRGB , PIXELFORMAT_SRGBA ,
9+ PIXELFORMAT_111110F , PIXELFORMAT_RGB16F , PIXELFORMAT_RGBA16F ,
810 TEXHINT_ASSET
911} from '../../../graphics/constants.js' ;
1012import { Texture } from '../../../graphics/texture.js' ;
1113
1214// Defined here: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
1315const IDENTIFIER = [ 0x58544BAB , 0xBB313120 , 0x0A1A0A0D ] ; // «KTX 11»\r\n\x1A\n
16+
1417const KNOWN_FORMATS = {
18+ // compressed formats
1519 0x83F0 : PIXELFORMAT_DXT1 ,
1620 0x83F2 : PIXELFORMAT_DXT3 ,
1721 0x83F3 : PIXELFORMAT_DXT5 ,
@@ -21,9 +25,24 @@ const KNOWN_FORMATS = {
2125 0x8C00 : PIXELFORMAT_PVRTC_4BPP_RGB_1 ,
2226 0x8C01 : PIXELFORMAT_PVRTC_2BPP_RGB_1 ,
2327 0x8C02 : PIXELFORMAT_PVRTC_4BPP_RGBA_1 ,
24- 0x8C03 : PIXELFORMAT_PVRTC_2BPP_RGBA_1
28+ 0x8C03 : PIXELFORMAT_PVRTC_2BPP_RGBA_1 ,
29+
30+ // uncompressed formats
31+ 0x8051 : PIXELFORMAT_R8_G8_B8 , // GL_RGB8
32+ 0x8058 : PIXELFORMAT_R8_G8_B8_A8 , // GL_RGBA8
33+ 0x8C41 : PIXELFORMAT_SRGB , // GL_SRGB8
34+ 0x8C43 : PIXELFORMAT_SRGBA , // GL_SRGB8_ALPHA8
35+ 0x8C3A : PIXELFORMAT_111110F , // GL_R11F_G11F_B10F
36+ 0x881B : PIXELFORMAT_RGB16F , // GL_RGB16F
37+ 0x881A : PIXELFORMAT_RGBA16F // GL_RGBA16F
2538} ;
2639
40+ function createContainer ( pixelFormat , buffer , byteOffset , byteSize ) {
41+ return ( pixelFormat === PIXELFORMAT_111110F ) ?
42+ new Uint32Array ( buffer , byteOffset , byteSize / 4 ) :
43+ new Uint8Array ( buffer , byteOffset , byteSize ) ;
44+ }
45+
2746/**
2847 * @private
2948 * @class
@@ -73,99 +92,87 @@ class KtxParser {
7392 }
7493
7594 parse ( data ) {
76- const headerU32 = new Uint32Array ( data , 0 , 16 ) ;
95+ const dataU32 = new Uint32Array ( data ) ;
7796
78- if ( IDENTIFIER [ 0 ] !== headerU32 [ 0 ] || IDENTIFIER [ 1 ] !== headerU32 [ 1 ] || IDENTIFIER [ 2 ] !== headerU32 [ 2 ] ) {
97+ // check magic bits
98+ if ( IDENTIFIER [ 0 ] !== dataU32 [ 0 ] ||
99+ IDENTIFIER [ 1 ] !== dataU32 [ 1 ] ||
100+ IDENTIFIER [ 2 ] !== dataU32 [ 2 ] ) {
79101 // #if _DEBUG
80102 console . warn ( "Invalid definition header found in KTX file. Expected 0xAB4B5458, 0x203131BB, 0x0D0A1A0A" ) ;
81103 // #endif
82104 return null ;
83105 }
84106
107+ // unpack header info
85108 const header = {
86- endianness : headerU32 [ 3 ] , // todo: Use this information
87- glType : headerU32 [ 4 ] ,
88- glTypeSize : headerU32 [ 5 ] ,
89- glFormat : headerU32 [ 6 ] ,
90- glInternalFormat : headerU32 [ 7 ] ,
91- glBaseInternalFormat : headerU32 [ 8 ] ,
92- pixelWidth : headerU32 [ 9 ] ,
93- pixelHeight : headerU32 [ 10 ] ,
94- pixelDepth : headerU32 [ 11 ] ,
95- numberOfArrayElements : headerU32 [ 12 ] ,
96- numberOfFaces : headerU32 [ 13 ] ,
97- numberOfMipmapLevels : headerU32 [ 14 ] ,
98- bytesOfKeyValueData : headerU32 [ 15 ]
109+ endianness : dataU32 [ 3 ] , // todo: Use this information
110+ glType : dataU32 [ 4 ] ,
111+ glTypeSize : dataU32 [ 5 ] ,
112+ glFormat : dataU32 [ 6 ] ,
113+ glInternalFormat : dataU32 [ 7 ] ,
114+ glBaseInternalFormat : dataU32 [ 8 ] ,
115+ pixelWidth : dataU32 [ 9 ] ,
116+ pixelHeight : dataU32 [ 10 ] ,
117+ pixelDepth : dataU32 [ 11 ] ,
118+ numberOfArrayElements : dataU32 [ 12 ] ,
119+ numberOfFaces : dataU32 [ 13 ] ,
120+ numberOfMipmapLevels : dataU32 [ 14 ] ,
121+ bytesOfKeyValueData : dataU32 [ 15 ]
99122 } ;
100123
124+ // don't support volume textures
101125 if ( header . pixelDepth > 1 ) {
102126 // #if _DEBUG
103127 console . warn ( "More than 1 pixel depth not supported!" ) ;
104128 // #endif
105129 return null ;
106130 }
107131
108- if ( header . numberOfArrayElements > 1 ) {
132+ // don't support texture arrays
133+ if ( header . numberOfArrayElements !== 0 ) {
109134 // #if _DEBUG
110135 console . warn ( "Array texture not supported!" ) ;
111136 // #endif
112137 return null ;
113138 }
114139
115- if ( header . glFormat !== 0 ) {
116- // #if _DEBUG
117- console . warn ( "We only support compressed formats!" ) ;
118- // #endif
119- return null ;
120- }
140+ const format = KNOWN_FORMATS [ header . glInternalFormat ] ;
121141
122- if ( ! KNOWN_FORMATS [ header . glInternalFormat ] ) {
142+ // only support subset of pixel formats
143+ if ( format === undefined ) {
123144 // #if _DEBUG
124145 console . warn ( "Unknown glInternalFormat: " + header . glInternalFormat ) ;
125146 // #endif
126147 return null ;
127148 }
128149
129- // Byte offset locating the first byte of texture level data
130- let offset = ( 16 * 4 ) + header . bytesOfKeyValueData ;
150+ // offset locating the first byte of texture level data
151+ let offset = 16 + header . bytesOfKeyValueData / 4 ;
131152
153+ const isCubemap = ( header . numberOfFaces > 1 ) ;
132154 const levels = [ ] ;
133- let isCubeMap = false ;
134155 for ( let mipmapLevel = 0 ; mipmapLevel < ( header . numberOfMipmapLevels || 1 ) ; mipmapLevel ++ ) {
135- const imageSizeInBytes = new Uint32Array ( data . slice ( offset , offset + 4 ) ) [ 0 ] ;
136- offset += 4 ;
137- // Currently array textures not supported. Keeping this here for reference.
138- // for (let arrayElement = 0; arrayElement < (header.numberOfArrayElements || 1); arrayElement++) {
139- const faceSizeInBytes = imageSizeInBytes / ( header . numberOfFaces || 1 ) ;
140- // Create array for cubemaps
141- if ( header . numberOfFaces > 1 ) {
142- isCubeMap = true ;
156+ const imageSizeInBytes = dataU32 [ offset ++ ] ;
157+
158+ if ( isCubemap ) {
143159 levels . push ( [ ] ) ;
144160 }
145- for ( let face = 0 ; face < header . numberOfFaces ; face ++ ) {
146- // Currently more than 1 pixel depth not supported. Keeping this here for reference.
147- // for (let zSlice = 0; zSlice < (header.pixelDepth || 1); zSlice++) {
148- const mipData = new Uint8Array ( data , offset , faceSizeInBytes ) ;
149- // Handle cubemaps
150- if ( header . numberOfFaces > 1 ) {
151- levels [ mipmapLevel ] . push ( mipData ) ;
152- } else {
153- levels . push ( mipData ) ;
154- }
155- offset += faceSizeInBytes ;
156- // }
161+
162+ const target = isCubemap ? levels [ mipmapLevel ] : levels ;
163+
164+ for ( let face = 0 ; face < ( isCubemap ? 6 : 1 ) ; ++ face ) {
165+ target . push ( createContainer ( format , data , offset * 4 , imageSizeInBytes ) ) ;
166+ offset += ( imageSizeInBytes + 3 ) >> 2 ;
157167 }
158- offset += 3 - ( ( offset + 3 ) % 4 ) ;
159- // }
160- // offset += 3 - ((offset + 3) % 4);
161168 }
162169
163170 return {
164- format : KNOWN_FORMATS [ header . glInternalFormat ] ,
171+ format : format ,
165172 width : header . pixelWidth ,
166173 height : header . pixelHeight ,
167174 levels : levels ,
168- cubemap : isCubeMap
175+ cubemap : isCubemap
169176 } ;
170177 }
171178}
0 commit comments