Sitelet https://github.com/uinosoft/t3d.js/commit/2b4e8a1591502e1ce9ff1de55f82cbf175d67c68
Skip to content

Commit 2b4e8a1

Browse files
committed
feat(addons): add MSFT_texture_dds support to GLTFLoader
1 parent c10acda commit 2b4e8a1

4 files changed

Lines changed: 85 additions & 5 deletions

File tree

‎examples/jsm/loaders/glTF/GLTFLoader.js‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { KHR_materials_unlit } from './extensions/KHR_materials_unlit.js';
2828
import { KHR_texture_basisu } from './extensions/KHR_texture_basisu.js';
2929
import { KHR_texture_transform } from './extensions/KHR_texture_transform.js';
3030
import { KHR_animation_pointer } from './extensions/KHR_animation_pointer.js';
31+
import { MSFT_texture_dds } from './extensions/MSFT_texture_dds.js';
3132

3233
const DefaultParsePipeline = [
3334
IndexParser,
@@ -56,7 +57,8 @@ const DefaultExtensions = new Map([
5657
['KHR_mesh_quantization', {}], // This is supported by default
5758
['KHR_texture_basisu', KHR_texture_basisu],
5859
['KHR_texture_transform', KHR_texture_transform],
59-
['KHR_animation_pointer', KHR_animation_pointer]
60+
['KHR_animation_pointer', KHR_animation_pointer],
61+
['MSFT_texture_dds', MSFT_texture_dds]
6062
]);
6163

6264
export class GLTFLoader {
@@ -83,6 +85,7 @@ export class GLTFLoader {
8385
this._dracoLoader = null;
8486
this._meshoptDecoder = null;
8587
this._ktx2Loader = null;
88+
this._ddsLoader = null;
8689

8790
this._fileLoader = new FileLoader();
8891

@@ -177,6 +180,15 @@ export class GLTFLoader {
177180
return this._ktx2Loader;
178181
}
179182

183+
setDDSLoader(ddsLoader) {
184+
this._ddsLoader = ddsLoader;
185+
return this;
186+
}
187+
188+
getDDSLoader() {
189+
return this._ddsLoader;
190+
}
191+
180192
loadFile(url, type = 'json') {
181193
this._fileLoader.setResponseType(type);
182194

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
PIXEL_FORMAT,
3+
PIXEL_TYPE,
4+
TEXTURE_FILTER,
5+
TEXEL_ENCODING_TYPE
6+
} from 't3d';
7+
8+
/**
9+
* DDS Texture Extension
10+
*
11+
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
12+
*/
13+
export class MSFT_texture_dds {
14+
15+
static loadTextureData(url, ddsLoader) {
16+
return new Promise((resolve, reject) => {
17+
ddsLoader.load(url, textureData => {
18+
const { mipmaps, width, height, format, mipmapCount, isCubemap } = textureData;
19+
20+
if (isCubemap) {
21+
reject(new Error('MSFT_texture_dds: Cubemap DDS textures are not supported.'));
22+
return;
23+
}
24+
25+
if (!format || !mipmaps.length) {
26+
reject(new Error('MSFT_texture_dds: Invalid DDS texture data.'));
27+
return;
28+
}
29+
30+
const isCompressed = format !== PIXEL_FORMAT.RGBA;
31+
32+
resolve({
33+
image: { data: mipmaps[0].data, width, height, isCompressed },
34+
mipmaps,
35+
type: PIXEL_TYPE.UNSIGNED_BYTE,
36+
format,
37+
minFilter: mipmapCount === 1 ? TEXTURE_FILTER.LINEAR : TEXTURE_FILTER.LINEAR_MIPMAP_LINEAR,
38+
magFilter: TEXTURE_FILTER.LINEAR,
39+
generateMipmaps: false,
40+
encoding: TEXEL_ENCODING_TYPE.LINEAR,
41+
premultiplyAlpha: false
42+
});
43+
}, undefined, reject);
44+
});
45+
}
46+
47+
}

‎examples/jsm/loaders/glTF/parsers/ImageParser.js‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export class ImageParser {
88
if (!gltf.images) return;
99

1010
const basisuExt = loader.extensions.get('KHR_texture_basisu');
11+
const ddsExt = loader.extensions.get('MSFT_texture_dds');
1112

1213
return Promise.all(
1314
gltf.images.map((params, index) => {
@@ -38,6 +39,17 @@ export class ImageParser {
3839
}
3940
return transcodeResult;
4041
});
42+
} else if (isDDS(params) && ddsExt) {
43+
promise = ddsExt.loadTextureData(imageUrl, loader.getDDSLoader()).then(textureData => {
44+
if (loader.detailLoadProgress) {
45+
if (isObjectURL) {
46+
loader.manager.itemEnd(GLTFUtils.resolveURL('blob<' + index + '>', path));
47+
} else {
48+
loader.manager.itemEnd(imageUrl);
49+
}
50+
}
51+
return textureData;
52+
});
4153
} else {
4254
const param = { loader, imageUrl, imageName, isObjectURL, sourceUrl, index, path };
4355
if (mimeType && (mimeType.includes('avif') || mimeType.includes('webp'))) {
@@ -61,6 +73,11 @@ export class ImageParser {
6173

6274
}
6375

76+
function isDDS(imageDef) {
77+
const { mimeType, uri = '' } = imageDef;
78+
return mimeType === 'image/vnd-ms.dds' || /\.dds($|\?)/i.test(uri);
79+
}
80+
6481
function detectSupport(mimeType) {
6582
const isSupported = new Promise(resolve => {
6683
// Lossy test image.

‎examples/jsm/loaders/glTF/parsers/TextureParser.js‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ export class TextureParser {
2121
let sourceIndex = source, isTextureData = false;
2222

2323
if (params.extensions) {
24-
const { KHR_texture_basisu } = params.extensions;
24+
const { KHR_texture_basisu, MSFT_texture_dds } = params.extensions;
2525

2626
if (KHR_texture_basisu) {
2727
sourceIndex = KHR_texture_basisu.source;
2828
isTextureData = true;
29+
} else if (MSFT_texture_dds) {
30+
sourceIndex = MSFT_texture_dds.source;
31+
isTextureData = true;
2932
} else if (Object.values(params.extensions).length && Object.values(params.extensions)[0].hasOwnProperty('source')) {
3033
sourceIndex = Object.values(params.extensions)[0].source;
3134
} else {
@@ -61,7 +64,7 @@ export class TextureParser {
6164
texture.name = textureName || texture.image.__name || `texture_${index}`;
6265
texture.flipY = false;
6366

64-
const { mimeType, uri } = gltf.images[sourceIndex];
67+
const { mimeType, uri = '' } = gltf.images[sourceIndex];
6568
texture.userData.mimeType = mimeType || getImageURIMimeType(uri);
6669

6770
const samplers = gltf.samplers || {};
@@ -82,8 +85,8 @@ export class TextureParser {
8285
function parseSampler(texture, sampler = {}) {
8386
const { magFilter, minFilter, wrapS, wrapT } = sampler;
8487

85-
texture.magFilter = WEBGL_FILTERS[magFilter] || TEXTURE_FILTER.LINEAR;
86-
texture.minFilter = WEBGL_FILTERS[minFilter] || TEXTURE_FILTER.LINEAR_MIPMAP_LINEAR;
88+
texture.magFilter = WEBGL_FILTERS[magFilter] || texture.magFilter || TEXTURE_FILTER.LINEAR;
89+
texture.minFilter = WEBGL_FILTERS[minFilter] || texture.minFilter || TEXTURE_FILTER.LINEAR_MIPMAP_LINEAR;
8790
texture.wrapS = WEBGL_WRAPPINGS[wrapS] || TEXTURE_WRAP.REPEAT;
8891
texture.wrapT = WEBGL_WRAPPINGS[wrapT] || TEXTURE_WRAP.REPEAT;
8992
}
@@ -98,6 +101,7 @@ function getImageURIMimeType(uri) {
98101
} else {
99102
if (uri.search(/\.jpe?g($|\?)/i) > 0) return 'image/jpeg';
100103
if (uri.search(/\.webp($|\?)/i) > 0) return 'image/webp';
104+
if (uri.search(/\.dds($|\?)/i) > 0) return 'image/vnd-ms.dds';
101105
return 'image/png';
102106
}
103107
}

0 commit comments

Comments
 (0)