s3m-three 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser/s3m-three.iife.ext-three.js +163 -15
- package/dist/browser/s3m-three.iife.js +190 -42
- package/dist/core/AtlasAlpha.d.ts +31 -0
- package/dist/core/AtlasAlpha.d.ts.map +1 -0
- package/dist/core/AtlasAlpha.js +82 -0
- package/dist/core/AtlasAlpha.js.map +1 -0
- package/dist/core/AtlasPack.d.ts +57 -0
- package/dist/core/AtlasPack.d.ts.map +1 -0
- package/dist/core/AtlasPack.js +81 -0
- package/dist/core/AtlasPack.js.map +1 -0
- package/dist/core/AtlasUpgrade.d.ts +68 -0
- package/dist/core/AtlasUpgrade.d.ts.map +1 -0
- package/dist/core/AtlasUpgrade.js +154 -0
- package/dist/core/AtlasUpgrade.js.map +1 -0
- package/dist/core/DXTTextureDecode.d.ts.map +1 -1
- package/dist/core/DXTTextureDecode.js +28 -6
- package/dist/core/DXTTextureDecode.js.map +1 -1
- package/dist/core/Draco.d.ts +52 -0
- package/dist/core/Draco.d.ts.map +1 -0
- package/dist/core/Draco.js +323 -0
- package/dist/core/Draco.js.map +1 -0
- package/dist/core/MeshOpt.d.ts +14 -3
- package/dist/core/MeshOpt.d.ts.map +1 -1
- package/dist/core/MeshOpt.js +101 -18
- package/dist/core/MeshOpt.js.map +1 -1
- package/dist/core/S3M1Parser.d.ts +16 -0
- package/dist/core/S3M1Parser.d.ts.map +1 -0
- package/dist/core/S3M1Parser.js +367 -0
- package/dist/core/S3M1Parser.js.map +1 -0
- package/dist/core/S3MBParser.d.ts +43 -1
- package/dist/core/S3MBParser.d.ts.map +1 -1
- package/dist/core/S3MBParser.js +101 -23
- package/dist/core/S3MBParser.js.map +1 -1
- package/dist/core/S3MBlockParser.d.ts +209 -0
- package/dist/core/S3MBlockParser.d.ts.map +1 -0
- package/dist/core/S3MBlockParser.js +789 -0
- package/dist/core/S3MBlockParser.js.map +1 -0
- package/dist/core/TextureFormat.d.ts +0 -1
- package/dist/core/TextureFormat.d.ts.map +1 -1
- package/dist/core/TextureFormat.js +19 -2
- package/dist/core/TextureFormat.js.map +1 -1
- package/dist/core/types.d.ts +65 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/render/DatasetManager.d.ts +11 -0
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +68 -3
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/GeometryBuilder.d.ts +3 -0
- package/dist/render/GeometryBuilder.d.ts.map +1 -1
- package/dist/render/GeometryBuilder.js +189 -17
- package/dist/render/GeometryBuilder.js.map +1 -1
- package/dist/render/GroundManager.d.ts +57 -1
- package/dist/render/GroundManager.d.ts.map +1 -1
- package/dist/render/GroundManager.js +149 -21
- package/dist/render/GroundManager.js.map +1 -1
- package/dist/render/Picker.d.ts.map +1 -1
- package/dist/render/Picker.js +12 -4
- package/dist/render/Picker.js.map +1 -1
- package/dist/render/PointCloudMaterial.d.ts +36 -0
- package/dist/render/PointCloudMaterial.d.ts.map +1 -0
- package/dist/render/PointCloudMaterial.js +159 -0
- package/dist/render/PointCloudMaterial.js.map +1 -0
- package/dist/render/S3MViewer.d.ts +75 -3
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +284 -54
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.d.ts +6 -0
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +125 -2
- package/dist/render/StyleMaterial.js.map +1 -1
- package/dist/render/TextureFactory.d.ts.map +1 -1
- package/dist/render/TextureFactory.js +64 -10
- package/dist/render/TextureFactory.js.map +1 -1
- package/dist/tileset/S3MTileset.d.ts +177 -1
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +754 -60
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/dist/vendor/crunch_decoder.module.js +12 -18
- package/dist/vendor/draco_decoder.module.js +3178 -0
- package/docs/API.md +150 -11
- package/package.json +1 -1
- package/src/core/AtlasAlpha.ts +82 -0
- package/src/core/AtlasPack.ts +127 -0
- package/src/core/AtlasUpgrade.ts +182 -0
- package/src/core/DXTTextureDecode.ts +28 -6
- package/src/core/Draco.ts +409 -0
- package/src/core/MeshOpt.ts +124 -18
- package/src/core/S3M1Parser.ts +430 -0
- package/src/core/S3MBParser.ts +112 -21
- package/src/core/S3MBlockParser.ts +953 -0
- package/src/core/TextureFormat.ts +20 -2
- package/src/core/types.ts +63 -0
- package/src/index.ts +3 -1
- package/src/render/DatasetManager.ts +70 -5
- package/src/render/GeometryBuilder.ts +212 -16
- package/src/render/GroundManager.ts +174 -20
- package/src/render/Picker.ts +13 -4
- package/src/render/PointCloudMaterial.ts +174 -0
- package/src/render/S3MViewer.ts +322 -55
- package/src/render/StyleMaterial.ts +135 -2
- package/src/render/TextureFactory.ts +67 -11
- package/src/tileset/S3MTileset.ts +798 -65
- package/src/vendor/crunch_decoder.module.js +12 -18
- package/src/vendor/draco_decoder.module.d.ts +128 -0
- package/src/vendor/draco_decoder.module.js +3178 -0
|
@@ -208,6 +208,16 @@ export function createSharedUniforms(): StyleUniforms {
|
|
|
208
208
|
const VERT_PARS = /* glsl */ `
|
|
209
209
|
attribute float aBatchId;
|
|
210
210
|
attribute vec3 aColor;
|
|
211
|
+
#ifdef S3M_ATLAS
|
|
212
|
+
attribute vec4 aAtlasRect;
|
|
213
|
+
#ifdef S3M_ATLAS_SEC
|
|
214
|
+
attribute vec4 aAtlasRect1;
|
|
215
|
+
attribute vec2 aSecUv;
|
|
216
|
+
varying vec2 vSecUv;
|
|
217
|
+
varying vec4 vSecRect;
|
|
218
|
+
#endif
|
|
219
|
+
varying vec4 vAtlasRect;
|
|
220
|
+
#endif
|
|
211
221
|
// 实例化几何没有逐顶点 batchId:顶点是 base mesh 的,单体粒度落在实例上
|
|
212
222
|
#ifdef USE_INSTANCING
|
|
213
223
|
attribute float aInstanceBatchId;
|
|
@@ -267,6 +277,14 @@ const VERT_WORLD = /* glsl */ `
|
|
|
267
277
|
vBatchId = s3mBatchRaw();
|
|
268
278
|
vVertColor = aColor;
|
|
269
279
|
|
|
280
|
+
#ifdef S3M_ATLAS
|
|
281
|
+
vAtlasRect = aAtlasRect;
|
|
282
|
+
#ifdef S3M_ATLAS_SEC
|
|
283
|
+
vSecUv = aSecUv;
|
|
284
|
+
vSecRect = aAtlasRect1;
|
|
285
|
+
#endif
|
|
286
|
+
#endif
|
|
287
|
+
|
|
270
288
|
#ifdef USE_INSTANCING
|
|
271
289
|
mat3 s3mNormMat = mat3( modelMatrix ) * mat3( instanceMatrix );
|
|
272
290
|
#else
|
|
@@ -276,6 +294,75 @@ const VERT_WORLD = /* glsl */ `
|
|
|
276
294
|
`;
|
|
277
295
|
|
|
278
296
|
const FRAG_PARS = /* glsl */ `
|
|
297
|
+
#ifdef S3M_ATLAS
|
|
298
|
+
varying vec4 vAtlasRect;
|
|
299
|
+
uniform vec2 uAtlasDim;
|
|
300
|
+
#ifdef S3M_ATLAS_SEC
|
|
301
|
+
uniform sampler2D uSecMap;
|
|
302
|
+
uniform vec2 uAtlasDim1;
|
|
303
|
+
varying vec2 vSecUv;
|
|
304
|
+
varying vec4 vSecRect;
|
|
305
|
+
#endif
|
|
306
|
+
/**
|
|
307
|
+
* 官方 CalculateMipLevel(vec2 版),逐字照抄 iClient3D 12.1.1。
|
|
308
|
+
* 用**未 fract** 的 uv 求屏幕空间导数 —— 先 fract 再求导会在平铺接缝处炸出
|
|
309
|
+
* 一个巨大导数,GPU 直接把整块砖采成最糊的 mip,表现为每块砖边缘一条暗线。
|
|
310
|
+
*/
|
|
311
|
+
void s3mMipLevel( vec2 uv, vec2 texSize, float maxMip, out float mip ) {
|
|
312
|
+
vec2 dx = dFdx( uv * texSize.x );
|
|
313
|
+
vec2 dy = dFdy( uv * texSize.y );
|
|
314
|
+
float dotX = dot( dx, dx );
|
|
315
|
+
float dotY = dot( dy, dy );
|
|
316
|
+
float dMax = max( dotX, dotY );
|
|
317
|
+
float dMin = min( dotX, dotY );
|
|
318
|
+
float off = clamp( ( dMax - dMin ) / max( dMax + dMin, 1e-8 ), 0.0, 1.0 );
|
|
319
|
+
float d = dMax * ( 1.0 - off ) + dMin * off;
|
|
320
|
+
mip = clamp( 0.5 * log2( max( d, 1e-8 ) ), 0.0, maxMip - 1.62 );
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 按指定 mip 档采样子区域(官方 czm_getTexColorForS3M 的内层循环体)。
|
|
325
|
+
*
|
|
326
|
+
* ★ 子区域 rect 的**右 1/3 不是贴图内容,是内联 mip 金字塔**:
|
|
327
|
+
* mip0 只占左 2/3,右 1/3 从上往下依次堆 1/2、1/4、1/8 缩略图,总高恰好 = rect 高。
|
|
328
|
+
* 官方那句 width *= 2.0/3.0 就是为这个,不是「经验收缩系数」。
|
|
329
|
+
* 数值实证:14 个块 691 条子纹理里 96.7% 满足「右侧 1/3 ≈ 左 2/3 的 1/2 下采样」
|
|
330
|
+
* (平均误差 6.8 vs 27.3)。
|
|
331
|
+
* 所以 mip > 0 时采样点必须右移到 x + 2/3·w,并下移到 (2^(m-1)-1)/2^(m-1)。
|
|
332
|
+
*/
|
|
333
|
+
vec4 s3mAtlasLod( vec2 uv, vec2 translate, vec2 scale, vec2 texSize, sampler2D tex, float mip ) {
|
|
334
|
+
vec2 t = translate;
|
|
335
|
+
if ( mip > 0.0 ) {
|
|
336
|
+
t.x = translate.x + scale.x;
|
|
337
|
+
float temp = pow( 2.0, mip - 1.0 );
|
|
338
|
+
t.y = translate.y + scale.y * ( temp - 1.0 ) / temp;
|
|
339
|
+
}
|
|
340
|
+
float inv = 1.0 / pow( 2.0, mip );
|
|
341
|
+
vec2 o = fract( uv );
|
|
342
|
+
// 半纹素内缩,避免双线性把邻格内容混进来
|
|
343
|
+
vec2 guard = vec2( pow( 2.0, mip ) / texSize.x, pow( 2.0, mip ) / texSize.y );
|
|
344
|
+
o = clamp( o, guard, vec2( 1.0 ) - guard );
|
|
345
|
+
return texture2D( tex, o * inv * scale + t );
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 图集采样入口:层级由 ceil/floor 两档各采一次再平均(官方同样做法),
|
|
350
|
+
* 因为内联金字塔只有 2 的整数次幂,中间档必须靠混合。
|
|
351
|
+
*/
|
|
352
|
+
vec4 s3mAtlasUnit( vec2 uv, vec4 rect, vec2 atlasDim, sampler2D tex ) {
|
|
353
|
+
// 退化矩形 = 官方 batchId < 0:该纹理单元不参与合成
|
|
354
|
+
if ( rect.z <= 0.0 || rect.w <= 0.0 ) return vec4( 1.0 );
|
|
355
|
+
vec2 dimPx = vec2( rect.z * atlasDim.x, rect.w * atlasDim.y );
|
|
356
|
+
vec2 texSize = vec2( dimPx.x * ( 2.0 / 3.0 ), dimPx.y );
|
|
357
|
+
float maxMip = log2( max( min( texSize.x, texSize.y ), 1.0 ) );
|
|
358
|
+
vec2 scale = texSize / atlasDim;
|
|
359
|
+
float mip = 0.0;
|
|
360
|
+
s3mMipLevel( uv, texSize, maxMip, mip );
|
|
361
|
+
vec4 hi = s3mAtlasLod( uv, rect.xy, scale, texSize, tex, ceil( mip ) );
|
|
362
|
+
vec4 lo = s3mAtlasLod( uv, rect.xy, scale, texSize, tex, floor( mip ) );
|
|
363
|
+
return hi * 0.5 + lo * 0.5;
|
|
364
|
+
}
|
|
365
|
+
#endif
|
|
279
366
|
uniform int uMode;
|
|
280
367
|
uniform vec3 uColorA;
|
|
281
368
|
uniform vec3 uColorB;
|
|
@@ -445,6 +532,14 @@ const FRAG_EMISSIVE = /* glsl */ `
|
|
|
445
532
|
}
|
|
446
533
|
`;
|
|
447
534
|
|
|
535
|
+
const FRAG_BAKE = /* glsl */ `
|
|
536
|
+
#ifdef S3M_ATLAS_SEC
|
|
537
|
+
// 官方 getTextureColorForS3M = firstColor(base) * secColor(bake),
|
|
538
|
+
// 两套独立的 rect / 尺寸(实测 base 11 条、bake 1 条,条数并不相同)
|
|
539
|
+
diffuseColor *= s3mAtlasUnit( vSecUv, vSecRect, uAtlasDim1, uSecMap );
|
|
540
|
+
#endif
|
|
541
|
+
`;
|
|
542
|
+
|
|
448
543
|
export interface StyledMaterialOptions {
|
|
449
544
|
uniforms: StyleUniforms;
|
|
450
545
|
map?: THREE.Texture | null;
|
|
@@ -454,6 +549,12 @@ export interface StyledMaterialOptions {
|
|
|
454
549
|
opacity?: number;
|
|
455
550
|
roughness?: number;
|
|
456
551
|
flatShading?: boolean;
|
|
552
|
+
/** >0 时开启 alphaTest:镂空贴图(S3MBLOCK 图集)的透明片元直接丢弃,避免渲染成黑面 */
|
|
553
|
+
alphaTest?: number;
|
|
554
|
+
/** 贴图为 S3MBLOCK 图集:几何需带 aAtlasRect,平铺改由片元按子区域做 */
|
|
555
|
+
atlas?: boolean;
|
|
556
|
+
/** 图集材质的第二套纹理(官方 BakeTex,光照烘焙),与 base 相乘 */
|
|
557
|
+
secMap?: THREE.Texture | null;
|
|
457
558
|
}
|
|
458
559
|
|
|
459
560
|
/**
|
|
@@ -476,6 +577,7 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
476
577
|
flatShading: opts.flatShading !== false,
|
|
477
578
|
// 顶点色、贴图全部在自定义 chunk 里处理,不让 three 插手
|
|
478
579
|
vertexColors: false,
|
|
580
|
+
alphaTest: opts.alphaTest ?? 0,
|
|
479
581
|
}) as StyledMaterial;
|
|
480
582
|
|
|
481
583
|
// 选中单体编号:每个 Mesh 独立,默认 -1 表示无选中
|
|
@@ -486,6 +588,15 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
486
588
|
const uBatchTexWidth = { value: 1.0 };
|
|
487
589
|
const uBatchTexActive = { value: 0 };
|
|
488
590
|
|
|
591
|
+
// 图集材质:只有几何确实带了 aAtlasRect 才开这个宏,否则缺属性会采到 (0,0) 的默认值
|
|
592
|
+
const atlas = !!opts.atlas && !!opts.map;
|
|
593
|
+
const secMap = atlas ? (opts.secMap ?? null) : null;
|
|
594
|
+
if (atlas) mat.defines = { ...(mat.defines as Record<string, string>), S3M_ATLAS: '' };
|
|
595
|
+
if (secMap) mat.defines = { ...mat.defines, S3M_ATLAS_SEC: '' };
|
|
596
|
+
// 官方 uTextureDim = (base宽, base高, bake宽, bake高),取实际绑定纹理的像素尺寸
|
|
597
|
+
const uAtlasDim = { value: new THREE.Vector2(opts.map?.image?.width || 1, opts.map?.image?.height || 1) };
|
|
598
|
+
const uAtlasDim1 = { value: new THREE.Vector2(secMap?.image?.width || 1, secMap?.image?.height || 1) };
|
|
599
|
+
|
|
489
600
|
mat.onBeforeCompile = (shader) => {
|
|
490
601
|
Object.assign(shader.uniforms, uniforms, {
|
|
491
602
|
uSelBatch,
|
|
@@ -493,6 +604,9 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
493
604
|
uBatchTex,
|
|
494
605
|
uBatchTexWidth,
|
|
495
606
|
uBatchTexActive,
|
|
607
|
+
uSecMap: { value: secMap },
|
|
608
|
+
uAtlasDim,
|
|
609
|
+
uAtlasDim1,
|
|
496
610
|
});
|
|
497
611
|
|
|
498
612
|
shader.vertexShader = shader.vertexShader
|
|
@@ -504,10 +618,29 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
504
618
|
.replace('#include <common>', `#include <common>\n${FRAG_PARS}`)
|
|
505
619
|
.replace('#include <color_fragment>', `#include <color_fragment>\n${FRAG_COLOR}`)
|
|
506
620
|
.replace('#include <emissivemap_fragment>', `#include <emissivemap_fragment>\n${FRAG_EMISSIVE}`);
|
|
621
|
+
|
|
622
|
+
if (atlas) {
|
|
623
|
+
// onBeforeCompile 收到的还是未展开的 `#include <map_fragment>`,不是 include 里的
|
|
624
|
+
// `texture2D( map, vMapUv )`。只替换后者会静默 no-op,最终仍按整张图集普通采样。
|
|
625
|
+
const atlasMapFragment = /* glsl */ `
|
|
626
|
+
#ifdef USE_MAP
|
|
627
|
+
vec4 sampledDiffuseColor = s3mAtlasUnit( vMapUv, vAtlasRect, uAtlasDim, map );
|
|
628
|
+
diffuseColor *= sampledDiffuseColor;
|
|
629
|
+
#endif
|
|
630
|
+
`;
|
|
631
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
632
|
+
'#include <map_fragment>',
|
|
633
|
+
`${atlasMapFragment}${secMap ? `\n${FRAG_BAKE}` : ''}`
|
|
634
|
+
);
|
|
635
|
+
} else if (secMap) {
|
|
636
|
+
shader.fragmentShader = shader.fragmentShader.replace('#include <map_fragment>', `#include <map_fragment>\n${FRAG_BAKE}`);
|
|
637
|
+
}
|
|
507
638
|
};
|
|
508
639
|
|
|
509
|
-
// 不同材质共用同一段注入代码,必须让 three 认为它们的 program
|
|
510
|
-
|
|
640
|
+
// 不同材质共用同一段注入代码,必须让 three 认为它们的 program 可复用;
|
|
641
|
+
// 但图集 / 烘焙两套宏会改注入结果,必须进 cacheKey,否则会复用错 program。
|
|
642
|
+
mat.customProgramCacheKey = () =>
|
|
643
|
+
`s3m-styled${atlas ? '-atlas' : ''}${secMap ? '-bake' : ''}`;
|
|
511
644
|
|
|
512
645
|
mat.userData.uSelBatch = uSelBatch;
|
|
513
646
|
mat.userData.uSelWhole = uSelWhole;
|
|
@@ -47,7 +47,7 @@ function configure<T extends THREE.Texture>(tex: T, srgb = true): T {
|
|
|
47
47
|
tex.magFilter = THREE.LinearFilter;
|
|
48
48
|
tex.minFilter = THREE.LinearMipmapLinearFilter;
|
|
49
49
|
tex.generateMipmaps = true;
|
|
50
|
-
//
|
|
50
|
+
// 各向异性过滤对斜视角立面清晰度的提升比提高分辨率更直接
|
|
51
51
|
tex.anisotropy = maxAnisotropy;
|
|
52
52
|
if (srgb) tex.colorSpace = THREE.SRGBColorSpace;
|
|
53
53
|
tex.flipY = false;
|
|
@@ -55,13 +55,61 @@ function configure<T extends THREE.Texture>(tex: T, srgb = true): T {
|
|
|
55
55
|
return tex;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* S3MBLOCK 图集专用:关掉 GPU mip。
|
|
60
|
+
*
|
|
61
|
+
* 官方 `sampleLod` 是显式 `textureLod(tex, uv, 0.0)`,层级靠「把子区域 rect 平移 +
|
|
62
|
+
* 内缩」自己算(见 StyleMaterial 的 s3mAtlasUnit)。若让 GPU 对整张图集生成 mip,
|
|
63
|
+
* 自动选的层级会把相邻子区域混进来,远处就是一糊串色 —— 实测照抄公式但留着 GPU mip
|
|
64
|
+
* 时,画面反而比不改更糟,就是因为这两个变量叠在一起了。
|
|
65
|
+
*/
|
|
66
|
+
function configureAtlas<T extends THREE.Texture>(tex: T): T {
|
|
67
|
+
configure(tex);
|
|
68
|
+
tex.wrapS = THREE.ClampToEdgeWrapping;
|
|
69
|
+
tex.wrapT = THREE.ClampToEdgeWrapping;
|
|
70
|
+
tex.minFilter = THREE.LinearFilter;
|
|
71
|
+
tex.generateMipmaps = false;
|
|
72
|
+
tex.anisotropy = 1;
|
|
73
|
+
tex.needsUpdate = true;
|
|
74
|
+
return tex;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 行序翻转(原地)。S3MBLOCK 图集按 GL 方向存储(v=0 在最后一行),解码后要翻一次行 */
|
|
78
|
+
function flipRows(rgba: Uint8Array, width: number, height: number): void {
|
|
79
|
+
const row = width * 4;
|
|
80
|
+
const tmp = new Uint8Array(row);
|
|
81
|
+
for (let y = 0; y < height >> 1; y++) {
|
|
82
|
+
const a = y * row;
|
|
83
|
+
const b = (height - 1 - y) * row;
|
|
84
|
+
tmp.set(rgba.subarray(a, a + row));
|
|
85
|
+
rgba.copyWithin(a, b, b + row);
|
|
86
|
+
rgba.set(tmp, b);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** 该像素格式自带 alpha 通道(DXT1 的 1bit alpha 不算,历史上没开过 alphaTest) */
|
|
91
|
+
function hasAlphaChannel(pixelFormat: number): boolean {
|
|
92
|
+
return (
|
|
93
|
+
pixelFormat === PixelFormat.DXT3 ||
|
|
94
|
+
pixelFormat === PixelFormat.DXT4 ||
|
|
95
|
+
pixelFormat === PixelFormat.DXT5 ||
|
|
96
|
+
pixelFormat === PixelFormat.ARGB ||
|
|
97
|
+
pixelFormat === PixelFormat.ABGR ||
|
|
98
|
+
pixelFormat === PixelFormat.BGRA ||
|
|
99
|
+
pixelFormat === PixelFormat.RGBA
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
58
103
|
/** 解 DXT 到 RGBA8。DXT5 是 1 字节/像素,DXT1 是 0.5 字节/像素。 */
|
|
59
104
|
/** DXT 解码只需要尺寸 / 格式 / 字节,CRN 转码出来的结果也走这条路 */
|
|
60
|
-
function decodeDXT(tex: Pick<S3MTexture, 'width' | 'height' | 'pixelFormat' | 'data'>): THREE.DataTexture {
|
|
105
|
+
function decodeDXT(tex: Pick<S3MTexture, 'width' | 'height' | 'pixelFormat' | 'data' | 'flipY'>): THREE.DataTexture {
|
|
61
106
|
const { width, height, pixelFormat, data } = tex;
|
|
62
107
|
const out = new Uint8Array(width * height * 4);
|
|
63
108
|
DXTTextureDecode.decode(out, width, height, data, pixelFormat);
|
|
64
|
-
|
|
109
|
+
if (tex.flipY) flipRows(out, width, height);
|
|
110
|
+
const dt = new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
|
|
111
|
+
if (hasAlphaChannel(pixelFormat)) dt.userData.s3mHasAlpha = true;
|
|
112
|
+
return dt;
|
|
65
113
|
}
|
|
66
114
|
|
|
67
115
|
function fromRawRGB(tex: S3MTexture): THREE.DataTexture {
|
|
@@ -83,12 +131,20 @@ function fromRawRGB(tex: S3MTexture): THREE.DataTexture {
|
|
|
83
131
|
out[d + 2] = swapRB ? data[p] : data[p + 2];
|
|
84
132
|
out[d + 3] = hasAlpha ? data[alphaFirst ? s : s + 3] : 255;
|
|
85
133
|
}
|
|
86
|
-
|
|
134
|
+
if (tex.flipY) flipRows(out, width, height);
|
|
135
|
+
const dt = new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
|
|
136
|
+
if (hasAlphaChannel(pixelFormat)) dt.userData.s3mHasAlpha = true;
|
|
137
|
+
return dt;
|
|
87
138
|
}
|
|
88
139
|
|
|
89
140
|
export async function createTexture(tex: S3MTexture | null | undefined): Promise<DecodedTexture> {
|
|
90
141
|
if (!tex || !tex.width || !tex.height) return { texture: null, decoded: 'empty' };
|
|
91
142
|
|
|
143
|
+
// 图集纹理(flipY 的原始 DXT 图集 / AtlasPack 拼出来的 RGBA 大图 / leaf 组合画布)都必须
|
|
144
|
+
// 关 GPU mip,层级由 StyleMaterial.s3mAtlasUnit 的内联金字塔自己算。
|
|
145
|
+
const isAtlas = tex.packed === true || tex.flipY === true || tex.composed === true;
|
|
146
|
+
const asAtlas = <T extends THREE.Texture>(t: T): T => (isAtlas ? configureAtlas(t) : configure(t));
|
|
147
|
+
|
|
92
148
|
// 判定只能看 compressType:v3.00 的 CRN 纹理 pixelFormat 是 13(ABGR)、
|
|
93
149
|
// 行标的 CRN 是 32856(GL_RGBA8),都不是 26 / 27(见 core/TextureFormat.ts)
|
|
94
150
|
const kind = resolveTextureKind(tex.compressType ?? 0, tex.pixelFormat);
|
|
@@ -102,7 +158,7 @@ export async function createTexture(tex: S3MTexture | null | undefined): Promise
|
|
|
102
158
|
pixelFormat: crn.dxtFormat === 1 ? 17 : 21,
|
|
103
159
|
data: crn.data,
|
|
104
160
|
});
|
|
105
|
-
return { texture:
|
|
161
|
+
return { texture: asAtlas(decoded), decoded: `crn->dxt${crn.dxtFormat}` };
|
|
106
162
|
} catch (e) {
|
|
107
163
|
// 一张纹理坏了不该带崩整个瓦片,如实上报并降级成无贴图
|
|
108
164
|
console.warn('[s3m] CRN 转码失败:', e);
|
|
@@ -111,10 +167,9 @@ export async function createTexture(tex: S3MTexture | null | undefined): Promise
|
|
|
111
167
|
}
|
|
112
168
|
|
|
113
169
|
if (kind === 'dxt1' || kind === 'dxt3' || kind === 'dxt4' || kind === 'dxt5') {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
};
|
|
170
|
+
const decoded = decodeDXT({ ...tex, pixelFormat: dxtPixelFormatOf(kind) });
|
|
171
|
+
// flipY 为真即 S3MBLOCK 图集(见 core/types.ts),必须无 mip
|
|
172
|
+
return { texture: asAtlas(decoded), decoded: kind };
|
|
118
173
|
}
|
|
119
174
|
|
|
120
175
|
if (kind === 'webp') {
|
|
@@ -122,11 +177,12 @@ export async function createTexture(tex: S3MTexture | null | undefined): Promise
|
|
|
122
177
|
const bmp = await createImageBitmap(blob);
|
|
123
178
|
const t = new THREE.Texture(bmp);
|
|
124
179
|
t.needsUpdate = true;
|
|
125
|
-
return { texture:
|
|
180
|
+
return { texture: asAtlas(t), decoded: 'webp' };
|
|
126
181
|
}
|
|
127
182
|
|
|
128
183
|
if (kind === 'raw') {
|
|
129
|
-
|
|
184
|
+
const decoded = fromRawRGB(tex);
|
|
185
|
+
return { texture: asAtlas(decoded), decoded: 'raw' };
|
|
130
186
|
}
|
|
131
187
|
|
|
132
188
|
// KTX2 / ETC 需要额外解码器,先如实上报而不是静默出错
|