@woosh/meep-engine 3.14.0 → 3.14.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.
Files changed (220) hide show
  1. package/README.md +33 -64
  2. package/package.json +1 -1
  3. package/src/avif/native/DECISIONS.md +544 -536
  4. package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -1
  5. package/src/avif/native/heif/parse_avif_file.js +384 -383
  6. package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -1
  7. package/src/avif/native/heif/parse_image_grid.js +49 -48
  8. package/src/basis/BasisBitReader.d.ts +77 -0
  9. package/src/basis/BasisBitReader.d.ts.map +1 -0
  10. package/src/basis/BasisBitReader.js +231 -0
  11. package/src/basis/BasisHuffmanTable.d.ts +53 -0
  12. package/src/basis/BasisHuffmanTable.d.ts.map +1 -0
  13. package/src/basis/BasisHuffmanTable.js +263 -0
  14. package/src/basis/basis_read_huffman_table.d.ts +20 -0
  15. package/src/basis/basis_read_huffman_table.d.ts.map +1 -0
  16. package/src/basis/basis_read_huffman_table.js +187 -0
  17. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts +50 -0
  18. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts.map +1 -0
  19. package/src/basis/etc1s/Etc1sSelectorHistory.js +89 -0
  20. package/src/basis/etc1s/etc1s_block_colors.d.ts +14 -0
  21. package/src/basis/etc1s/etc1s_block_colors.d.ts.map +1 -0
  22. package/src/basis/etc1s/etc1s_block_colors.js +75 -0
  23. package/src/basis/etc1s/etc1s_decode_slice.d.ts +57 -0
  24. package/src/basis/etc1s/etc1s_decode_slice.d.ts.map +1 -0
  25. package/src/basis/etc1s/etc1s_decode_slice.js +258 -0
  26. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts +48 -0
  27. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts.map +1 -0
  28. package/src/basis/etc1s/etc1s_read_endpoint_codebook.js +111 -0
  29. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts +42 -0
  30. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts.map +1 -0
  31. package/src/basis/etc1s/etc1s_read_selector_codebook.js +127 -0
  32. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts +54 -0
  33. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts.map +1 -0
  34. package/src/basis/etc1s/etc1s_read_slice_tables.js +47 -0
  35. package/src/basis/transcode/build_range_plan.js +54 -0
  36. package/src/basis/transcode/etc1s_to_bc1.d.ts +24 -0
  37. package/src/basis/transcode/etc1s_to_bc1.d.ts.map +1 -0
  38. package/src/basis/transcode/etc1s_to_bc1.js +205 -0
  39. package/src/basis/transcode/etc1s_to_bc7.d.ts +30 -0
  40. package/src/basis/transcode/etc1s_to_bc7.d.ts.map +1 -0
  41. package/src/basis/transcode/etc1s_to_bc7.js +496 -0
  42. package/src/basis/transcode/fit_endpoints.d.ts +59 -0
  43. package/src/basis/transcode/fit_endpoints.d.ts.map +1 -0
  44. package/src/basis/transcode/fit_endpoints.js +226 -0
  45. package/src/{avif/native/isobmff → core/binary}/ByteCursor.d.ts +19 -6
  46. package/src/core/binary/ByteCursor.d.ts.map +1 -0
  47. package/src/{avif/native/isobmff → core/binary}/ByteCursor.js +41 -14
  48. package/src/engine/EngineHarness.js +1 -1
  49. package/src/engine/asset/codec/Codec.d.ts.map +1 -0
  50. package/src/engine/asset/codec/CodecWithFallback.d.ts.map +1 -0
  51. package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.js +2 -2
  52. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +4 -4
  53. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts +1 -1
  54. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts.map +1 -1
  55. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
  56. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts +1 -1
  57. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts.map +1 -1
  58. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.js +1 -1
  59. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts +1 -1
  60. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts.map +1 -1
  61. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +1 -1
  62. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  63. package/src/engine/graphics/texture/atlas/TextureAtlas.js +13 -0
  64. package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
  65. package/src/engine/physics/broadphase/generate_pairs.js +7 -1
  66. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  67. package/src/ktx2/KTX2_IDENTIFIER.d.ts +14 -0
  68. package/src/ktx2/KTX2_IDENTIFIER.d.ts.map +1 -0
  69. package/src/ktx2/KTX2_IDENTIFIER.js +15 -0
  70. package/src/ktx2/KhrDfModel.d.ts +35 -0
  71. package/src/ktx2/KhrDfModel.d.ts.map +1 -0
  72. package/src/ktx2/KhrDfModel.js +49 -0
  73. package/src/ktx2/SupercompressionScheme.d.ts +16 -0
  74. package/src/ktx2/SupercompressionScheme.d.ts.map +1 -0
  75. package/src/ktx2/SupercompressionScheme.js +23 -0
  76. package/src/ktx2/ktx2_read.d.ts +71 -0
  77. package/src/ktx2/ktx2_read.d.ts.map +1 -0
  78. package/src/ktx2/ktx2_read.js +581 -0
  79. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts +27 -0
  80. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts.map +1 -0
  81. package/src/ktx2/ktx2_read_basis_lz_global_data.js +189 -0
  82. package/src/ktx2/vk_format_to_texture_format.d.ts +55 -0
  83. package/src/ktx2/vk_format_to_texture_format.d.ts.map +1 -0
  84. package/src/ktx2/vk_format_to_texture_format.js +290 -0
  85. package/src/shade/descriptor/GPUTextureUsage.d.ts +23 -0
  86. package/src/shade/descriptor/GPUTextureUsage.d.ts.map +1 -0
  87. package/src/shade/descriptor/GPUTextureUsage.js +24 -0
  88. package/src/shade/descriptor/WebGPUExtensionType.d.ts +3 -0
  89. package/src/shade/descriptor/WebGPUExtensionType.js +26 -0
  90. package/src/shade/descriptor/texture/TextureDescriptor.d.ts +2 -6
  91. package/src/shade/descriptor/texture/TextureDescriptor.d.ts.map +1 -1
  92. package/src/shade/descriptor/texture/TextureDescriptor.js +24 -40
  93. package/src/shade/descriptor/texture/format/TextureFormat.d.ts +125 -0
  94. package/src/shade/descriptor/texture/format/TextureFormat.d.ts.map +1 -0
  95. package/src/shade/descriptor/texture/format/TextureFormat.js +146 -0
  96. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts +60 -0
  97. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts.map +1 -1
  98. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +102 -0
  99. package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
  100. package/src/shade/device/graph/TextureResourceDescriptor.js +160 -155
  101. package/src/shade/device/mock/SoftwareGPUQueue.d.ts +11 -4
  102. package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
  103. package/src/shade/device/mock/SoftwareGPUQueue.js +31 -37
  104. package/src/shade/device/mock/SoftwareGPUTexture.d.ts.map +1 -1
  105. package/src/shade/device/mock/SoftwareGPUTexture.js +8 -4
  106. package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts.map +1 -1
  107. package/src/shade/device/timing/profile/frame_graph_extract_topology.js +164 -174
  108. package/src/shade/playground/ground_seam/README.md +235 -0
  109. package/src/shade/playground/ground_seam/capture_scene_color.d.ts +45 -0
  110. package/src/shade/playground/ground_seam/capture_scene_color.d.ts.map +1 -0
  111. package/src/shade/playground/ground_seam/capture_scene_color.js +242 -0
  112. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts +11 -0
  113. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts.map +1 -0
  114. package/src/shade/playground/ground_seam/depth_step_across_seam.js +185 -0
  115. package/src/shade/playground/ground_seam/index.html +195 -0
  116. package/src/shade/playground/ground_seam/main.d.ts +2 -0
  117. package/src/shade/playground/ground_seam/main.d.ts.map +1 -0
  118. package/src/shade/playground/ground_seam/main.js +879 -0
  119. package/src/shade/playground/ground_seam/make_grid_texture.d.ts +29 -0
  120. package/src/shade/playground/ground_seam/make_grid_texture.d.ts.map +1 -0
  121. package/src/shade/playground/ground_seam/make_grid_texture.js +105 -0
  122. package/src/shade/playground/ground_seam/make_ground.d.ts +70 -0
  123. package/src/shade/playground/ground_seam/make_ground.d.ts.map +1 -0
  124. package/src/shade/playground/ground_seam/make_ground.js +161 -0
  125. package/src/shade/playground/ground_seam/measure_flicker.d.ts +71 -0
  126. package/src/shade/playground/ground_seam/measure_flicker.d.ts.map +1 -0
  127. package/src/shade/playground/ground_seam/measure_flicker.js +531 -0
  128. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  129. package/src/shade/renderer/Renderer.js +2519 -2511
  130. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  131. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +275 -274
  132. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  133. package/src/shade/renderer/light/GPULightCollection.js +323 -314
  134. package/src/shade/renderer/lightmap/GPULightMap.js +336 -336
  135. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  136. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +12 -2
  137. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  138. package/src/shade/renderer/material/standard/fragment_gbuffer.js +3 -1
  139. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  140. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  141. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +3 -1
  142. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  143. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  144. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.js +3 -1
  145. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  146. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.js +3 -1
  147. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts +28 -0
  148. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts.map +1 -0
  149. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.js +39 -0
  150. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts +45 -0
  151. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts.map +1 -1
  152. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.js +94 -61
  153. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts +24 -0
  154. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts.map +1 -0
  155. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.js +43 -0
  156. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +1462 -0
  157. package/src/shade/renderer/texture/TextureManager.d.ts.map +1 -1
  158. package/src/shade/renderer/texture/TextureManager.js +20 -8
  159. package/src/shade/renderer/texture/TextureRole.d.ts +19 -0
  160. package/src/shade/renderer/texture/TextureRole.d.ts.map +1 -0
  161. package/src/shade/renderer/texture/TextureRole.js +26 -0
  162. package/src/shade/renderer/texture/TextureVariantSet.d.ts +71 -0
  163. package/src/shade/renderer/texture/TextureVariantSet.d.ts.map +1 -0
  164. package/src/shade/renderer/texture/TextureVariantSet.js +105 -0
  165. package/src/shade/renderer/texture/download_texture_data.d.ts.map +1 -1
  166. package/src/shade/renderer/texture/download_texture_data.js +22 -0
  167. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts +15 -0
  168. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts.map +1 -0
  169. package/src/shade/renderer/texture/format/gpu_texture_compression_support.js +50 -0
  170. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts +27 -0
  171. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts.map +1 -0
  172. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.js +65 -0
  173. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts +11 -0
  174. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts.map +1 -0
  175. package/src/shade/renderer/texture/format/infer_format_from_parameters.js +88 -0
  176. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts +24 -0
  177. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts.map +1 -0
  178. package/src/shade/renderer/texture/format/texture_format_from_shade_image.js +71 -0
  179. package/src/shade/renderer/texture/format/texture_format_preference.d.ts +22 -0
  180. package/src/shade/renderer/texture/format/texture_format_preference.d.ts.map +1 -0
  181. package/src/shade/renderer/texture/format/texture_format_preference.js +89 -0
  182. package/src/shade/renderer/texture/format/texture_variant_select.d.ts +24 -0
  183. package/src/shade/renderer/texture/format/texture_variant_select.d.ts.map +1 -0
  184. package/src/shade/renderer/texture/format/texture_variant_select.js +38 -0
  185. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts +9 -1
  186. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts.map +1 -1
  187. package/src/shade/renderer/texture/shade_texture_might_have_alpha.js +73 -8
  188. package/src/shade/renderer/texture/source/AlphaMode.d.ts +27 -0
  189. package/src/shade/renderer/texture/source/AlphaMode.d.ts.map +1 -0
  190. package/src/shade/renderer/texture/source/AlphaMode.js +52 -0
  191. package/src/shade/renderer/texture/source/ShadeImage.d.ts +33 -0
  192. package/src/shade/renderer/texture/source/ShadeImage.d.ts.map +1 -1
  193. package/src/shade/renderer/texture/source/ShadeImage.js +74 -0
  194. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts +78 -0
  195. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts.map +1 -0
  196. package/src/shade/renderer/texture/source/ShadeTexelData.js +158 -0
  197. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts +0 -10
  198. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts.map +1 -1
  199. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.js +73 -175
  200. package/src/shade/renderer/texture/texture_write_level.d.ts +32 -0
  201. package/src/shade/renderer/texture/texture_write_level.d.ts.map +1 -0
  202. package/src/shade/renderer/texture/texture_write_level.js +77 -0
  203. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts +8 -1
  204. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts.map +1 -1
  205. package/src/shade/renderer/texture/texture_write_to_gpu.js +62 -37
  206. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts +8 -0
  207. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts.map +1 -1
  208. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +8 -0
  209. package/src/shade/util/texture_mip_extent.d.ts +49 -0
  210. package/src/shade/util/texture_mip_extent.d.ts.map +1 -0
  211. package/src/shade/util/texture_mip_extent.js +87 -0
  212. package/src/avif/native/isobmff/ByteCursor.d.ts.map +0 -1
  213. package/src/engine/asset/loaders/image/codec/Codec.d.ts.map +0 -1
  214. package/src/engine/asset/loaders/image/codec/CodecWithFallback.d.ts.map +0 -1
  215. package/src/shade/util/compute_max_mip_count.d.ts +0 -8
  216. package/src/shade/util/compute_max_mip_count.d.ts.map +0 -1
  217. package/src/shade/util/compute_max_mip_count.js +0 -27
  218. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.d.ts +0 -0
  219. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.js +0 -0
  220. /package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.d.ts +0 -0
@@ -0,0 +1,127 @@
1
+ import { BasisBitReader } from "../BasisBitReader.js";
2
+ import { basis_read_huffman_table } from "../basis_read_huffman_table.js";
3
+
4
+ /**
5
+ * Texels in an ETC1S block, and so selectors in a codebook entry.
6
+ *
7
+ * @readonly
8
+ * @type {number}
9
+ */
10
+ const SELECTORS_PER_ENTRY = 16;
11
+
12
+ /**
13
+ * Bytes one entry occupies in the raw form: four selectors of two bits in each.
14
+ *
15
+ * @readonly
16
+ * @type {number}
17
+ */
18
+ const BYTES_PER_ENTRY = 4;
19
+
20
+ /**
21
+ * One decoded selector codebook.
22
+ *
23
+ * @typedef {object} Etc1sSelectorCodebook
24
+ * @property {Uint8Array} selectors sixteen two-bit selectors per entry, one byte each, in
25
+ * raster order within the block
26
+ * @property {number} count
27
+ */
28
+
29
+ /**
30
+ * Read the ETC1S selector codebook.
31
+ *
32
+ * Each entry is a block's sixteen two-bit selectors — which of the four colours the block's endpoint
33
+ * defines each texel takes. The entries are stored as four bytes, one per row of the block, and are
34
+ * either raw or coded as a byte-wise exclusive-or against the previous entry.
35
+ *
36
+ * **Selectors are stored one per byte rather than packed.** Four times the memory of the packed form
37
+ * and worth it: every block transcode reads all sixteen, and unpacking them here once is unpacking
38
+ * them once per codebook entry rather than once per block that references it.
39
+ *
40
+ * @param {Uint8Array} data the selectors section of the global data
41
+ * @param {number} count how many entries the section holds, from the global data header
42
+ * @returns {Etc1sSelectorCodebook}
43
+ * @throws {Error} for the global and hybrid codebook forms, which the format defines and no encoder
44
+ * produces
45
+ * @see the specification, section 8.0
46
+ *
47
+ * @author Alex Goldring
48
+ * @copyright Company Named Limited (c) 2026
49
+ */
50
+ export function etc1s_read_selector_codebook(data, count) {
51
+ const reader = new BasisBitReader().init(data);
52
+
53
+ const global_codebook = reader.bits(1) !== 0;
54
+ const hybrid_codebook = reader.bits(1) !== 0;
55
+ const raw = reader.bits(1) !== 0;
56
+
57
+ /*
58
+ Both are reserved in the format and neither has ever been written: the specification says each
59
+ bit "should always be 0", because the encoder has no option that sets it. Refusing names the
60
+ feature rather than reading the codebook that follows as though the flag were clear, which would
61
+ produce a texture full of the wrong selectors.
62
+ */
63
+ if (global_codebook) {
64
+ throw new Error(
65
+ "Basis: the selector codebook declares a global codebook, which the format reserves and"
66
+ + " no encoder emits. There is no global codebook here to resolve it against."
67
+ );
68
+ }
69
+
70
+ if (hybrid_codebook) {
71
+ throw new Error(
72
+ "Basis: the selector codebook declares a hybrid global/local codebook, which the format"
73
+ + " reserves and no encoder emits."
74
+ );
75
+ }
76
+
77
+ const selectors = new Uint8Array(count * SELECTORS_PER_ENTRY);
78
+
79
+ if (raw) {
80
+ for (let i = 0; i < count; i++) {
81
+ for (let row = 0; row < BYTES_PER_ENTRY; row++) {
82
+ unpack_row(selectors, i, row, reader.bits(8));
83
+ }
84
+ }
85
+
86
+ return { selectors, count };
87
+ }
88
+
89
+ const delta_model = basis_read_huffman_table(reader);
90
+
91
+ const previous = new Uint8Array(BYTES_PER_ENTRY);
92
+
93
+ for (let i = 0; i < count; i++) {
94
+ for (let row = 0; row < BYTES_PER_ENTRY; row++) {
95
+ /*
96
+ The first entry is sent raw, and the rest as a delta against the entry before. The
97
+ exclusive-or is its own inverse, so one expression covers both: the first entry's
98
+ "previous" is zero, against which the delta is the value.
99
+ */
100
+ const byte = i === 0
101
+ ? reader.bits(8)
102
+ : delta_model.decode(reader) ^ previous[row];
103
+
104
+ previous[row] = byte;
105
+
106
+ unpack_row(selectors, i, row, byte);
107
+ }
108
+ }
109
+
110
+ return { selectors, count };
111
+ }
112
+
113
+ /**
114
+ * Spread one packed byte across the four selectors of a block row.
115
+ *
116
+ * @param {Uint8Array} selectors
117
+ * @param {number} entry
118
+ * @param {number} row
119
+ * @param {number} byte four two-bit selectors, the leftmost texel in the least significant pair
120
+ */
121
+ function unpack_row(selectors, entry, row, byte) {
122
+ const base = entry * SELECTORS_PER_ENTRY + row * 4;
123
+
124
+ for (let column = 0; column < 4; column++) {
125
+ selectors[base + column] = (byte >> (column * 2)) & 3;
126
+ }
127
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The Huffman tables every slice in a file is decoded with, plus the size of the selector history.
3
+ *
4
+ * They are global rather than per-slice — one set covers every mip level, face and frame — which is
5
+ * what makes a mip chain cost little more than its base level.
6
+ *
7
+ * @typedef {object} Etc1sSliceTables
8
+ * @property {BasisHuffmanTable} endpoint_pred how each block in a 2x2 group takes its endpoint
9
+ * @property {BasisHuffmanTable} delta_endpoint an endpoint index, relative to the previous block's
10
+ * @property {BasisHuffmanTable} selector a selector index, a history reference, or a run
11
+ * @property {BasisHuffmanTable} selector_history_rle the length of such a run
12
+ * @property {number} selector_history_size entries in the move-to-front list
13
+ */
14
+ /**
15
+ * Read the four slice-decoding Huffman tables and the selector history size.
16
+ *
17
+ * @param {Uint8Array} data the tables section of the global data
18
+ * @returns {Etc1sSliceTables}
19
+ * @throws {Error} for a section that runs out before the four tables do
20
+ * @see the specification, section 9.0
21
+ *
22
+ * @author Alex Goldring
23
+ * @copyright Company Named Limited (c) 2026
24
+ */
25
+ export function etc1s_read_slice_tables(data: Uint8Array): Etc1sSliceTables;
26
+ /**
27
+ * The Huffman tables every slice in a file is decoded with, plus the size of the selector history.
28
+ *
29
+ * They are global rather than per-slice — one set covers every mip level, face and frame — which is
30
+ * what makes a mip chain cost little more than its base level.
31
+ */
32
+ export type Etc1sSliceTables = {
33
+ /**
34
+ * how each block in a 2x2 group takes its endpoint
35
+ */
36
+ endpoint_pred: BasisHuffmanTable;
37
+ /**
38
+ * an endpoint index, relative to the previous block's
39
+ */
40
+ delta_endpoint: BasisHuffmanTable;
41
+ /**
42
+ * a selector index, a history reference, or a run
43
+ */
44
+ selector: BasisHuffmanTable;
45
+ /**
46
+ * the length of such a run
47
+ */
48
+ selector_history_rle: BasisHuffmanTable;
49
+ /**
50
+ * entries in the move-to-front list
51
+ */
52
+ selector_history_size: number;
53
+ };
54
+ //# sourceMappingURL=etc1s_read_slice_tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_read_slice_tables.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/etc1s_read_slice_tables.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;GAUG;AACH,8CARW,UAAU,GACR,gBAAgB,CAyB5B;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAhCa,MAAM"}
@@ -0,0 +1,47 @@
1
+ import { BasisBitReader } from "../BasisBitReader.js";
2
+ import { basis_read_huffman_table } from "../basis_read_huffman_table.js";
3
+
4
+ /**
5
+ * The Huffman tables every slice in a file is decoded with, plus the size of the selector history.
6
+ *
7
+ * They are global rather than per-slice — one set covers every mip level, face and frame — which is
8
+ * what makes a mip chain cost little more than its base level.
9
+ *
10
+ * @typedef {object} Etc1sSliceTables
11
+ * @property {BasisHuffmanTable} endpoint_pred how each block in a 2x2 group takes its endpoint
12
+ * @property {BasisHuffmanTable} delta_endpoint an endpoint index, relative to the previous block's
13
+ * @property {BasisHuffmanTable} selector a selector index, a history reference, or a run
14
+ * @property {BasisHuffmanTable} selector_history_rle the length of such a run
15
+ * @property {number} selector_history_size entries in the move-to-front list
16
+ */
17
+
18
+ /**
19
+ * Read the four slice-decoding Huffman tables and the selector history size.
20
+ *
21
+ * @param {Uint8Array} data the tables section of the global data
22
+ * @returns {Etc1sSliceTables}
23
+ * @throws {Error} for a section that runs out before the four tables do
24
+ * @see the specification, section 9.0
25
+ *
26
+ * @author Alex Goldring
27
+ * @copyright Company Named Limited (c) 2026
28
+ */
29
+ export function etc1s_read_slice_tables(data) {
30
+ const reader = new BasisBitReader().init(data);
31
+
32
+ const endpoint_pred = basis_read_huffman_table(reader);
33
+ const delta_endpoint = basis_read_huffman_table(reader);
34
+ const selector = basis_read_huffman_table(reader);
35
+ const selector_history_rle = basis_read_huffman_table(reader);
36
+
37
+ const selector_history_size = reader.bits(13);
38
+
39
+ // "Any remaining bits may be safely ignored" — the section is padded to a byte boundary
40
+ return {
41
+ endpoint_pred,
42
+ delta_endpoint,
43
+ selector,
44
+ selector_history_rle,
45
+ selector_history_size,
46
+ };
47
+ }
@@ -0,0 +1,54 @@
1
+ import { SELECTOR_RANGES, selector_codebook_ranges } from "./fit_endpoints.js";
2
+
3
+ /**
4
+ * Which selector spans a file's codebook actually contains, and where each entry's span sits.
5
+ *
6
+ * Endpoints are fitted per (endpoint entry, selector span) pairing, and there are ten possible
7
+ * spans — but a real file uses only a few of them, and building a fit for spans no block has would
8
+ * be most of the work for none of the benefit. So the spans present are collected first and the
9
+ * templates are allocated against those alone.
10
+ *
11
+ * @typedef {object} Etc1sRangePlan
12
+ * @property {number} slot_count how many distinct spans the codebook contains
13
+ * @property {Uint8Array} range_of_slot the {@link SELECTOR_RANGES} index each slot stands for
14
+ * @property {Uint8Array} slot_of_entry one slot per selector codebook entry
15
+ */
16
+
17
+ /**
18
+ * Work out the {@link Etc1sRangePlan} for a selector codebook.
19
+ *
20
+ * @param {Etc1sSelectorCodebook} selectors
21
+ * @returns {Etc1sRangePlan}
22
+ *
23
+ * @author Alex Goldring
24
+ * @copyright Company Named Limited (c) 2026
25
+ */
26
+ export function build_range_plan(selectors) {
27
+ const range_of_entry = selector_codebook_ranges(selectors);
28
+
29
+ const slot_of_range = new Int32Array(SELECTOR_RANGES.length).fill(-1);
30
+
31
+ const range_of_slot_list = [];
32
+
33
+ for (let entry = 0; entry < range_of_entry.length; entry++) {
34
+ const range = range_of_entry[entry];
35
+
36
+ if (slot_of_range[range] < 0) {
37
+ slot_of_range[range] = range_of_slot_list.length;
38
+
39
+ range_of_slot_list.push(range);
40
+ }
41
+ }
42
+
43
+ const slot_of_entry = new Uint8Array(range_of_entry.length);
44
+
45
+ for (let entry = 0; entry < range_of_entry.length; entry++) {
46
+ slot_of_entry[entry] = slot_of_range[range_of_entry[entry]];
47
+ }
48
+
49
+ return {
50
+ slot_count: range_of_slot_list.length,
51
+ range_of_slot: Uint8Array.from(range_of_slot_list),
52
+ slot_of_entry,
53
+ };
54
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Transcode a decoded ETC1S slice to BC1.
3
+ *
4
+ * **Both formats put four colours on a line, which is what makes this cheap and good.** An ETC1S
5
+ * block is a base colour plus an intensity modifier applied equally to all three channels, so its
6
+ * four colours are collinear; BC1's are two endpoints plus two interpolants, which is the same
7
+ * shape. The transcode is therefore a re-parameterisation rather than a re-quantisation, and what it
8
+ * loses is only what 5:6:5 endpoints lose over ETC1S's 5:5:5-plus-modifier.
9
+ *
10
+ * **The work is per endpoint codebook entry, not per block.** A block's four colours depend only on
11
+ * which endpoint entry it names, so the endpoint pair and the selector-to-index mapping are computed
12
+ * once per entry — of which there are at most a few thousand — and every block that names it just
13
+ * writes them out. A texture is typically tens of thousands of blocks over a few hundred entries.
14
+ *
15
+ * @param {Etc1sSlice} slice
16
+ * @param {Etc1sEndpointCodebook} endpoints
17
+ * @param {Etc1sSelectorCodebook} selectors
18
+ * @returns {Uint8Array} tightly packed BC1 blocks, in raster order
19
+ *
20
+ * @author Alex Goldring
21
+ * @copyright Company Named Limited (c) 2026
22
+ */
23
+ export function etc1s_to_bc1(slice: Etc1sSlice, endpoints: Etc1sEndpointCodebook, selectors: Etc1sSelectorCodebook): Uint8Array;
24
+ //# sourceMappingURL=etc1s_to_bc1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_to_bc1.d.ts","sourceRoot":"","sources":["../../../../src/basis/transcode/etc1s_to_bc1.js"],"names":[],"mappings":"AA0EA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qHALa,UAAU,CAuCtB"}
@@ -0,0 +1,205 @@
1
+ import { etc1s_block_colors } from "../etc1s/etc1s_block_colors.js";
2
+
3
+ /**
4
+ * Bytes one BC1 block occupies: two 16-bit endpoints and sixteen 2-bit indices.
5
+ *
6
+ * @readonly
7
+ * @type {number}
8
+ */
9
+ const BC1_BLOCK_BYTES = 8;
10
+
11
+ /**
12
+ * Pack an eight-bit-per-channel colour into BC1's 5:6:5 endpoint.
13
+ *
14
+ * Rounding rather than truncating: truncation biases every endpoint darker, and over a whole texture
15
+ * that reads as a gamma shift rather than as noise.
16
+ *
17
+ * @param {number} r
18
+ * @param {number} g
19
+ * @param {number} b
20
+ * @returns {number} sixteen bits
21
+ */
22
+ function pack565(r, g, b) {
23
+ const r5 = Math.min(31, (r * 31 + 127) / 255 | 0);
24
+ const g6 = Math.min(63, (g * 63 + 127) / 255 | 0);
25
+ const b5 = Math.min(31, (b * 31 + 127) / 255 | 0);
26
+
27
+ return (r5 << 11) | (g6 << 5) | b5;
28
+ }
29
+
30
+ /**
31
+ * The eight-bit colour a 5:6:5 endpoint decodes to, by the same bit replication hardware uses.
32
+ *
33
+ * @param {Uint8Array} out
34
+ * @param {number} offset
35
+ * @param {number} packed
36
+ */
37
+ function unpack565(out, offset, packed) {
38
+ const r5 = (packed >> 11) & 31;
39
+ const g6 = (packed >> 5) & 63;
40
+ const b5 = packed & 31;
41
+
42
+ out[offset] = (r5 << 3) | (r5 >> 2);
43
+ out[offset + 1] = (g6 << 2) | (g6 >> 4);
44
+ out[offset + 2] = (b5 << 3) | (b5 >> 2);
45
+ }
46
+
47
+ /**
48
+ * Which of BC1's four colours best matches a target, given the block's decoded palette.
49
+ *
50
+ * @param {Uint8Array} palette twelve bytes, four `[r, g, b]` triples in BC1 index order
51
+ * @param {Uint8Array} target three bytes
52
+ * @param {number} target_offset
53
+ * @returns {number} `0` to `3`
54
+ */
55
+ function closest_index(palette, target, target_offset) {
56
+ let best = 0;
57
+ let best_error = Infinity;
58
+
59
+ for (let i = 0; i < 4; i++) {
60
+ const dr = palette[i * 3] - target[target_offset];
61
+ const dg = palette[i * 3 + 1] - target[target_offset + 1];
62
+ const db = palette[i * 3 + 2] - target[target_offset + 2];
63
+
64
+ const error = dr * dr + dg * dg + db * db;
65
+
66
+ if (error < best_error) {
67
+ best_error = error;
68
+ best = i;
69
+ }
70
+ }
71
+
72
+ return best;
73
+ }
74
+
75
+ /**
76
+ * Transcode a decoded ETC1S slice to BC1.
77
+ *
78
+ * **Both formats put four colours on a line, which is what makes this cheap and good.** An ETC1S
79
+ * block is a base colour plus an intensity modifier applied equally to all three channels, so its
80
+ * four colours are collinear; BC1's are two endpoints plus two interpolants, which is the same
81
+ * shape. The transcode is therefore a re-parameterisation rather than a re-quantisation, and what it
82
+ * loses is only what 5:6:5 endpoints lose over ETC1S's 5:5:5-plus-modifier.
83
+ *
84
+ * **The work is per endpoint codebook entry, not per block.** A block's four colours depend only on
85
+ * which endpoint entry it names, so the endpoint pair and the selector-to-index mapping are computed
86
+ * once per entry — of which there are at most a few thousand — and every block that names it just
87
+ * writes them out. A texture is typically tens of thousands of blocks over a few hundred entries.
88
+ *
89
+ * @param {Etc1sSlice} slice
90
+ * @param {Etc1sEndpointCodebook} endpoints
91
+ * @param {Etc1sSelectorCodebook} selectors
92
+ * @returns {Uint8Array} tightly packed BC1 blocks, in raster order
93
+ *
94
+ * @author Alex Goldring
95
+ * @copyright Company Named Limited (c) 2026
96
+ */
97
+ export function etc1s_to_bc1(slice, endpoints, selectors) {
98
+ const plan = build_range_plan(selectors);
99
+
100
+ const templates = build_templates(endpoints, plan);
101
+
102
+ const block_count = slice.blocks_x * slice.blocks_y;
103
+
104
+ const out = new Uint8Array(block_count * BC1_BLOCK_BYTES);
105
+
106
+ for (let block = 0; block < block_count; block++) {
107
+ const selector_entry = slice.selector_index[block];
108
+ const selector_base = selector_entry * 16;
109
+
110
+ const pairing = slice.endpoint_index[block] * plan.slot_count
111
+ + plan.slot_of_entry[selector_entry];
112
+
113
+ const low = templates.low[pairing];
114
+ const high = templates.high[pairing];
115
+ const map = pairing * 4;
116
+
117
+ const o = block * BC1_BLOCK_BYTES;
118
+
119
+ out[o] = low & 255;
120
+ out[o + 1] = low >> 8;
121
+ out[o + 2] = high & 255;
122
+ out[o + 3] = high >> 8;
123
+
124
+ for (let row = 0; row < 4; row++) {
125
+ const base = selector_base + row * 4;
126
+
127
+ out[o + 4 + row] =
128
+ templates.selector_to_index[map + selectors.selectors[base]]
129
+ | (templates.selector_to_index[map + selectors.selectors[base + 1]] << 2)
130
+ | (templates.selector_to_index[map + selectors.selectors[base + 2]] << 4)
131
+ | (templates.selector_to_index[map + selectors.selectors[base + 3]] << 6);
132
+ }
133
+ }
134
+
135
+ return out;
136
+ }
137
+
138
+ /**
139
+ * The BC1 endpoint pair and selector mapping for every entry of the endpoint codebook.
140
+ *
141
+ * @param {Etc1sEndpointCodebook} endpoints
142
+ * @returns {{low: Uint16Array, high: Uint16Array, selector_to_index: Uint8Array}} `low` is written
143
+ * first, and is the larger of the pair so the block decodes in four-colour mode
144
+ */
145
+ function build_templates(endpoints) {
146
+ const count = endpoints.count;
147
+
148
+ const low = new Uint16Array(count);
149
+ const high = new Uint16Array(count);
150
+ const selector_to_index = new Uint8Array(count * 4);
151
+
152
+ const colors = new Uint8Array(12);
153
+ const palette = new Uint8Array(12);
154
+
155
+ for (let entry = 0; entry < count; entry++) {
156
+ etc1s_block_colors(colors, 0, endpoints.color5, endpoints.intensity, entry);
157
+
158
+ /*
159
+ The darkest and brightest of the four span the line; the middle two are recovered by BC1's
160
+ own interpolants, which sit at one and two thirds. ETC1S's middle colours are not exactly
161
+ there — the modifier table is not evenly spaced — so they are matched rather than assumed.
162
+ */
163
+ let first = pack565(colors[9], colors[10], colors[11]);
164
+ let second = pack565(colors[0], colors[1], colors[2]);
165
+
166
+ /*
167
+ Four-colour mode needs the first endpoint to compare greater. Equal endpoints would select
168
+ three-colour mode, whose fourth index is transparent black — so they are nudged apart, which
169
+ is invisible because the block is a single colour either way.
170
+ */
171
+ if (first < second) {
172
+ const swap = first;
173
+
174
+ first = second;
175
+ second = swap;
176
+ }
177
+
178
+ if (first === second) {
179
+ second = first === 0 ? 0 : first - 1;
180
+ }
181
+
182
+ low[entry] = first;
183
+ high[entry] = second;
184
+
185
+ unpack565(palette, 0, first);
186
+ unpack565(palette, 3, second);
187
+
188
+ /*
189
+ The specification's interpolants, truncating rather than rounding. It matters only that the
190
+ same arithmetic is used here and by whatever decodes the result: these colours exist to pick
191
+ indices, and picking them against a palette a decoder will not produce is how a transcoder
192
+ ends up half a level off everywhere.
193
+ */
194
+ for (let c = 0; c < 3; c++) {
195
+ palette[6 + c] = (2 * palette[c] + palette[3 + c]) / 3 | 0;
196
+ palette[9 + c] = (palette[c] + 2 * palette[3 + c]) / 3 | 0;
197
+ }
198
+
199
+ for (let selector = 0; selector < 4; selector++) {
200
+ selector_to_index[entry * 4 + selector] = closest_index(palette, colors, selector * 3);
201
+ }
202
+ }
203
+
204
+ return { low, high, selector_to_index };
205
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Transcode a decoded ETC1S slice to BC7.
3
+ *
4
+ * **Two modes, chosen by whether the payload carries alpha, because BC7 cannot do both at once.**
5
+ * Mode 6 gives one index per texel, shared by all four channels, with four bits of it; mode 5 gives
6
+ * colour and alpha their own indices but only two bits each. An opaque payload wants the finer
7
+ * index; an alpha payload needs the independent one, because an ETC1S file's alpha slice has its own
8
+ * selectors, unrelated to the colour slice's, and a shared index could not express both.
9
+ *
10
+ * Neither mode loses much to ETC1S, for the reason {@link etc1s_to_bc1} spells out: an ETC1S block's
11
+ * four colours lie on a line, which is the shape both modes store natively. Mode 5's two-bit index
12
+ * is in fact an exact structural match — four colours, four index values — and gives up only the
13
+ * freedom to place the middle two, which ETC1S's modifier table has already fixed.
14
+ *
15
+ * **The work is per codebook entry, not per block.** A block's colours depend only on which endpoint
16
+ * entry it names, so endpoints and the selector-to-index mapping are computed once per entry and
17
+ * every block that names it just writes them out. Colour and alpha are tabulated separately, which
18
+ * is what lets mode 5 pair any colour entry with any alpha entry without a table of their product.
19
+ *
20
+ * @param {Etc1sSlice} slice the colour slice
21
+ * @param {Etc1sSlice|undefined} alpha_slice the alpha slice, when the payload has one
22
+ * @param {Etc1sEndpointCodebook} endpoints
23
+ * @param {Etc1sSelectorCodebook} selectors
24
+ * @returns {Uint8Array} tightly packed BC7 blocks, in raster order
25
+ *
26
+ * @author Alex Goldring
27
+ * @copyright Company Named Limited (c) 2026
28
+ */
29
+ export function etc1s_to_bc7(slice: Etc1sSlice, alpha_slice: Etc1sSlice | undefined, endpoints: Etc1sEndpointCodebook, selectors: Etc1sSelectorCodebook): Uint8Array;
30
+ //# sourceMappingURL=etc1s_to_bc7.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_to_bc7.d.ts","sourceRoot":"","sources":["../../../../src/basis/transcode/etc1s_to_bc7.js"],"names":[],"mappings":"AA4FA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,6DARW,aAAW,SAAS,uEAGlB,UAAU,CAStB"}