@woosh/meep-engine 3.17.2 → 3.18.0
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/editor/actions/concrete/ComponentAddAction.d.ts +1 -0
- package/editor/actions/concrete/ComponentAddAction.d.ts.map +1 -1
- package/editor/actions/concrete/ComponentAddAction.js +4 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts +1 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts.map +1 -1
- package/editor/actions/concrete/ComponentRemoveAction.js +4 -0
- package/editor/actions/concrete/EntityCreateAction.d.ts +1 -0
- package/editor/actions/concrete/EntityCreateAction.d.ts.map +1 -1
- package/editor/actions/concrete/EntityCreateAction.js +7 -0
- package/editor/actions/concrete/EntityRemoveAction.d.ts +1 -0
- package/editor/actions/concrete/EntityRemoveAction.d.ts.map +1 -1
- package/editor/actions/concrete/EntityRemoveAction.js +4 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts +10 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts.map +1 -1
- package/editor/actions/concrete/PatchTerrainHeightAction.js +107 -85
- package/editor/actions/concrete/TransformModifyAction.d.ts +1 -0
- package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +4 -0
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
- package/editor/particles/effect/ParticleEffectDocument.js +10 -6
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
- package/editor/particles/effect/ParticleReferenceSimulation.js +98 -4
- package/editor/particles/effect/make_starter_particle_effect.d.ts.map +1 -1
- package/editor/particles/effect/make_starter_particle_effect.js +0 -2
- package/editor/tools/engine/ToolEngine.d.ts +30 -0
- package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
- package/editor/tools/engine/ToolEngine.js +257 -188
- package/editor/view/ecs/EntityList.d.ts.map +1 -1
- package/editor/view/ecs/EntityList.js +6 -5
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +17 -9
- package/package.json +1 -1
- package/src/CODEC_LAYOUT_VERIFICATION.md +1 -2
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts +56 -0
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts.map +1 -0
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.js +73 -0
- package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.d.ts.map +1 -1
- package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +18 -63
- package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts +53 -0
- package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_byte_group.js +140 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts +32 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.js +77 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts +46 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.js +146 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts +40 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.js +700 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts +24 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.js +39 -0
- package/src/core/math/random/seededRandom_Mulberry32.d.ts.map +1 -1
- package/src/core/math/random/seededRandom_Mulberry32.js +4 -2
- package/src/core/process/undo/Action.d.ts +19 -0
- package/src/core/process/undo/Action.d.ts.map +1 -1
- package/src/core/process/undo/Action.js +17 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +78 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +310 -0
- package/src/engine/graphics3/GraphicsEngine.d.ts +17 -0
- package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics3/GraphicsEngine.js +30 -10
- package/src/engine/graphics3/particles/particle_gpu_records.d.ts.map +1 -1
- package/src/engine/graphics3/particles/particle_gpu_records.js +0 -2
- package/src/engine/interpolation/InterpolationSystem.d.ts +7 -0
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +10 -0
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +14 -34
- package/src/engine/network/time/RenderPlayout.d.ts +76 -0
- package/src/engine/network/time/RenderPlayout.d.ts.map +1 -0
- package/src/engine/network/time/RenderPlayout.js +163 -0
- package/src/engine/network/transport/adapters/WebSocketTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebSocketTransport.js +11 -4
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +4 -12
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +39 -13
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/VolumeField.js +4 -1
- package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +23 -2
- package/src/shade/playground/particle_system/README.md +139 -139
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +0 -2
- package/src/shade/playground/particle_system/particle_scene.d.ts +6 -9
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_scene.js +6 -13
- package/src/shade/playground/ssr_reprojection/README.md +103 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts +17 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +68 -0
- package/src/shade/playground/ssr_reprojection/index.html +79 -0
- package/src/shade/playground/ssr_reprojection/main.d.ts +2 -0
- package/src/shade/playground/ssr_reprojection/main.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/main.js +245 -0
- package/src/shade/playground/ssr_reprojection/scene.d.ts +18 -0
- package/src/shade/playground/ssr_reprojection/scene.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/scene.js +51 -0
- package/src/shade/renderer/Renderer.d.ts +2 -23
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +185 -218
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +5 -8
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +12 -12
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +6 -0
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +7 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts +4 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.js +51 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.js +5 -35
- package/src/shade/renderer/particles/DESIGN.md +703 -514
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +16 -4
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +1189 -991
- package/src/shade/renderer/particles/ParticleConstants.d.ts +64 -0
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +71 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts +57 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +181 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts +7 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.js +12 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts +33 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.js +67 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts +40 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.js +187 -0
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +15 -2
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +14 -8
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +58 -4
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +79 -4
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts +23 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js +35 -2
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +4 -2
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +5 -8
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/particle_emitter_record.js +0 -6
- package/src/shade/renderer/particles/graph_particles.d.ts +25 -3
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +108 -31
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts +6 -4
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +5 -5
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +5 -0
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +27 -6
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +4 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +6 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +35 -27
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +424 -425
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts +20 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +4 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +38 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts +22 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.js +42 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.js +53 -22
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.js +213 -216
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts +50 -0
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_render.js +1 -1
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts +36 -0
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.js +102 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts +102 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.js +329 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts +112 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.js +318 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts +35 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.js +108 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts +3 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.js +70 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts +62 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.js +55 -0
- package/src/shade/renderer/postprocess/PostProcess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/PostProcess.js +2 -1
- package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +286 -0
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts +57 -21
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/SSR.js +230 -129
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts +11 -0
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.js +6 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.js +32 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.js +41 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts +3 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.js +30 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +70 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts +21 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.js +363 -0
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_get_neighbour_weight.js +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts +2 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.js +24 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts +1 -6
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.js +16 -332
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts +1 -6
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.js +25 -358
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +0 -5
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +201 -276
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts +1 -0
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +167 -143
- package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts +15 -0
- package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts.map +1 -0
- package/src/shade/renderer/shader/graph/graph_create_buffer.js +20 -0
- package/src/shade/renderer/view/GPUViewContext.d.ts +3 -4
- package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
- package/src/shade/renderer/view/GPUViewContext.js +5 -5
- package/src/core/binary/meshopt/__meshopt_test_streams.d.ts +0 -24
- package/src/core/binary/meshopt/__meshopt_test_streams.d.ts.map +0 -1
- package/src/core/binary/meshopt/__meshopt_test_streams.js +0 -237
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts +0 -84
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts.map +0 -1
- package/src/core/geom/3d/atlas/atlas_test_fixtures.js +0 -266
- package/src/engine/asset/loaders/gltf_test_fixtures.d.ts +0 -97
- package/src/engine/asset/loaders/gltf_test_fixtures.d.ts.map +0 -1
- package/src/engine/asset/loaders/gltf_test_fixtures.js +0 -359
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts +0 -86
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_test_fixtures.js +0 -250
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +0 -27
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts +0 -151
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/particles/particle_test_fixtures.js +0 -229
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +0 -23
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +0 -457
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
import { assert } from "../../assert.js";
|
|
2
|
+
import { MESHOPT_GROUP_SIZE } from "./MESHOPT_GROUP_SIZE.js";
|
|
3
|
+
import {
|
|
4
|
+
MESHOPT_BITS_BY_CONTROL,
|
|
5
|
+
MESHOPT_BITS_VERSION_0,
|
|
6
|
+
MESHOPT_MAX_BLOCK_BYTES,
|
|
7
|
+
MESHOPT_MAX_BLOCK_ELEMENTS,
|
|
8
|
+
MESHOPT_MAX_BYTE_STRIDE,
|
|
9
|
+
MESHOPT_TAIL_MIN_BY_VERSION,
|
|
10
|
+
MESHOPT_VERTEX_HEADER,
|
|
11
|
+
meshopt_max_block_elements
|
|
12
|
+
} from "./MESHOPT_VERTEX_BLOCK.js";
|
|
13
|
+
import { meshopt_choose_data_block, meshopt_encode_data_block } from "./meshopt_encode_data_block.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The most groups one block can hold, which bounds the per-block bookkeeping below.
|
|
17
|
+
* @type {number}
|
|
18
|
+
*/
|
|
19
|
+
const MAX_GROUPS = MESHOPT_MAX_BLOCK_ELEMENTS / MESHOPT_GROUP_SIZE;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Control mode 2 stores nothing at all, and can only carry a byte position whose deltas are every
|
|
23
|
+
* one zero.
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
const CONTROL_ZERO = 2;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Control mode 3 stores the deltas verbatim, with no group header in front of them. It is the
|
|
30
|
+
* fallback that always fits, and the ceiling every other mode is measured against.
|
|
31
|
+
* @type {number}
|
|
32
|
+
*/
|
|
33
|
+
const CONTROL_LITERAL = 3;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* One block's delta bytes, laid out per byte position exactly as the decoder's scratch is: position
|
|
37
|
+
* `k`'s deltas are `group_count * 16` bytes at `k * aligned_count`.
|
|
38
|
+
*
|
|
39
|
+
* Module-static, because encoding is synchronous and there is one caller. So are the four below.
|
|
40
|
+
*
|
|
41
|
+
* @type {Uint8Array}
|
|
42
|
+
*/
|
|
43
|
+
const delta_scratch = new Uint8Array(MESHOPT_MAX_BLOCK_BYTES);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Where a candidate channel mode's deltas go while it is being priced, so that the incumbent's stay
|
|
47
|
+
* where the writer expects them.
|
|
48
|
+
* @type {Uint8Array}
|
|
49
|
+
*/
|
|
50
|
+
const candidate_scratch = new Uint8Array(MESHOPT_MAX_BLOCK_BYTES);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The element each block's first delta is taken against — the stream's first element for the first
|
|
54
|
+
* block, and the previous block's last element after that.
|
|
55
|
+
* @type {Uint8Array}
|
|
56
|
+
*/
|
|
57
|
+
const baseline = new Uint8Array(MESHOPT_MAX_BYTE_STRIDE);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The group header value chosen for each `(byte position, group)` of the block in hand.
|
|
61
|
+
* @type {Uint8Array}
|
|
62
|
+
*/
|
|
63
|
+
const group_choice = new Uint8Array(MESHOPT_MAX_BYTE_STRIDE * MAX_GROUPS);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The same, for the control mode currently being priced against the incumbent.
|
|
67
|
+
* @type {Uint8Array}
|
|
68
|
+
*/
|
|
69
|
+
const group_choice_candidate = new Uint8Array(MAX_GROUPS);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Somewhere for {@link price_channel} to throw away the choices it is not going to keep. Its own,
|
|
73
|
+
* because it is measuring a byte position whose incumbent choices must survive the measurement.
|
|
74
|
+
* @type {Uint8Array}
|
|
75
|
+
*/
|
|
76
|
+
const group_choice_discard = new Uint8Array(MAX_GROUPS);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The baseline a candidate channel mode is priced against, kept apart from {@link baseline} for the
|
|
80
|
+
* same reason.
|
|
81
|
+
* @type {Uint8Array}
|
|
82
|
+
*/
|
|
83
|
+
const candidate_baseline = new Uint8Array(MESHOPT_MAX_BYTE_STRIDE);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The control mode chosen for each byte position of the block in hand. Version 1 only; version 0
|
|
87
|
+
* has no control header and every byte position is a data block.
|
|
88
|
+
* @type {Uint8Array}
|
|
89
|
+
*/
|
|
90
|
+
const control_choice = new Uint8Array(MESHOPT_MAX_BYTE_STRIDE);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Encode an attribute stream — the `"ATTRIBUTES"` mode of both meshopt extensions, and the inverse
|
|
94
|
+
* of {@link meshopt_decode_vertex_buffer}.
|
|
95
|
+
*
|
|
96
|
+
* Elements are delta-coded against the element before them, byte position by byte position. What
|
|
97
|
+
* the encoder gets to decide, and does:
|
|
98
|
+
*
|
|
99
|
+
* - **per group of sixteen deltas**, the width they are packed at, out of the four the block's
|
|
100
|
+
* control mode admits — including "all zero", which stores nothing;
|
|
101
|
+
* - **per byte position** (version 1), whether the groups carry a header at all: a position that
|
|
102
|
+
* never changes costs nothing, and one that is pure noise skips the header and is written
|
|
103
|
+
* verbatim rather than paying two bits a group to be told so;
|
|
104
|
+
* - **per channel of four byte positions** (version 1), whether its deltas are 8-bit, 16-bit, or a
|
|
105
|
+
* rotated 32-bit exclusive-or — see {@link choose_channel_mode}.
|
|
106
|
+
*
|
|
107
|
+
* Every one of those is priced against the alternatives on the data in hand rather than guessed at,
|
|
108
|
+
* so the output is never larger than the verbatim form and is usually a fraction of it.
|
|
109
|
+
*
|
|
110
|
+
* Version 0 is what `EXT_meshopt_compression` admits and is the default. Version 1 is
|
|
111
|
+
* `KHR_meshopt_compression` only, and buys the last two decisions above.
|
|
112
|
+
*
|
|
113
|
+
* @see https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_meshopt_compression
|
|
114
|
+
*
|
|
115
|
+
* @param {Uint8Array} source `count * byte_stride` bytes
|
|
116
|
+
* @param {number} count elements
|
|
117
|
+
* @param {number} byte_stride divisible by 4, at most 256
|
|
118
|
+
* @param {Object} [options]
|
|
119
|
+
* @param {number} [options.version] 0 or 1, defaulting to 0
|
|
120
|
+
* @param {number[]|Uint8Array} [options.channel_modes] one mode byte per channel — low nibble the
|
|
121
|
+
* mode, high nibble the rotation — imposed rather than chosen. Version 1 only, and the caller
|
|
122
|
+
* owns the consequences: a mode that suits the data less well than the one this would have picked
|
|
123
|
+
* makes a larger stream, not a wrong one.
|
|
124
|
+
* @returns {Uint8Array} the whole compressed stream, tail included
|
|
125
|
+
* @throws {Error} if the arguments do not describe an encodable buffer
|
|
126
|
+
*/
|
|
127
|
+
export function meshopt_encode_vertex_buffer(source, count, byte_stride, options = {}) {
|
|
128
|
+
assert.isNonNegativeInteger(count, 'count');
|
|
129
|
+
assert.isPositiveInteger(byte_stride, 'byte_stride');
|
|
130
|
+
assert.equal(byte_stride % 4, 0, `byte_stride(=${byte_stride}) must be divisible by 4`);
|
|
131
|
+
assert.ok(byte_stride <= MESHOPT_MAX_BYTE_STRIDE, `byte_stride(=${byte_stride}) must be at most ${MESHOPT_MAX_BYTE_STRIDE}`);
|
|
132
|
+
assert.equal(
|
|
133
|
+
source.length, count * byte_stride,
|
|
134
|
+
`source(=${source.length} bytes) must hold count(=${count}) * byte_stride(=${byte_stride})`
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const version = options.version ?? 0;
|
|
138
|
+
|
|
139
|
+
assert.isOneOf(version, [0, 1], 'version');
|
|
140
|
+
|
|
141
|
+
const channel_count = byte_stride >> 2;
|
|
142
|
+
const max_block_elements = meshopt_max_block_elements(byte_stride);
|
|
143
|
+
|
|
144
|
+
const channel_modes = resolve_channel_modes(source, count, byte_stride, version, options.channel_modes);
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
Two tail sizes, as in the decoder: the tail block sits flush against the end of the stream and
|
|
148
|
+
is `tail_size` long, and the padding in front of it brings the pair up to `tail_size_pad`. The
|
|
149
|
+
padding is what gives the decoder's per-group bounds check its slack.
|
|
150
|
+
*/
|
|
151
|
+
const tail_size = byte_stride + (version === 1 ? channel_count : 0);
|
|
152
|
+
const tail_min = MESHOPT_TAIL_MIN_BY_VERSION[version];
|
|
153
|
+
const tail_size_pad = tail_size < tail_min ? tail_min : tail_size;
|
|
154
|
+
|
|
155
|
+
const destination = new Uint8Array(
|
|
156
|
+
upper_bound(count, byte_stride, channel_count, version, max_block_elements) + tail_size_pad
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
destination[0] = MESHOPT_VERTEX_HEADER | version;
|
|
160
|
+
|
|
161
|
+
// the first block deltas against the first element, which is also what goes in the tail block
|
|
162
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
163
|
+
baseline[k] = count > 0 ? source[k] : 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let offset = 1;
|
|
167
|
+
let element = 0;
|
|
168
|
+
|
|
169
|
+
while (element < count) {
|
|
170
|
+
const block_elements = Math.min(count - element, max_block_elements);
|
|
171
|
+
|
|
172
|
+
offset = encode_block(
|
|
173
|
+
destination, offset,
|
|
174
|
+
source, element, block_elements,
|
|
175
|
+
byte_stride, channel_count, version, channel_modes
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
element += block_elements;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// the padding goes IN FRONT of the tail block, which sits flush against the end of the stream
|
|
182
|
+
const end = offset + tail_size_pad;
|
|
183
|
+
const tail = end - tail_size;
|
|
184
|
+
|
|
185
|
+
destination.fill(0, offset, tail);
|
|
186
|
+
|
|
187
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
188
|
+
destination[tail + k] = count > 0 ? source[k] : 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (version === 1) {
|
|
192
|
+
for (let c = 0; c < channel_count; c++) {
|
|
193
|
+
destination[tail + byte_stride + c] = channel_modes[c];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return destination.slice(0, end);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The largest stream the encoder could possibly produce for this shape, blocks only — every byte
|
|
202
|
+
* position of every block written verbatim, with a group header it would not actually pay for.
|
|
203
|
+
*
|
|
204
|
+
* Bounding it up front is what lets the whole stream be written into one exactly-sized buffer:
|
|
205
|
+
* every decision is made from the deltas, so nothing here has to look at the data.
|
|
206
|
+
*
|
|
207
|
+
* @param {number} count
|
|
208
|
+
* @param {number} byte_stride
|
|
209
|
+
* @param {number} channel_count
|
|
210
|
+
* @param {number} version
|
|
211
|
+
* @param {number} max_block_elements
|
|
212
|
+
* @returns {number} bytes, the stream header included and the tail excluded
|
|
213
|
+
*/
|
|
214
|
+
function upper_bound(count, byte_stride, channel_count, version, max_block_elements) {
|
|
215
|
+
let bound = 1;
|
|
216
|
+
|
|
217
|
+
for (let element = 0; element < count; element += max_block_elements) {
|
|
218
|
+
const block_elements = Math.min(count - element, max_block_elements);
|
|
219
|
+
const group_count = Math.ceil(block_elements / MESHOPT_GROUP_SIZE);
|
|
220
|
+
|
|
221
|
+
bound += (version === 1 ? channel_count : 0)
|
|
222
|
+
+ byte_stride * (((group_count + 3) >> 2) + group_count * MESHOPT_GROUP_SIZE);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return bound;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Settle on a mode byte per channel: what the caller imposed, or nothing at all under version 0,
|
|
230
|
+
* or {@link choose_channel_mode}'s reading of the data.
|
|
231
|
+
*
|
|
232
|
+
* @param {Uint8Array} source
|
|
233
|
+
* @param {number} count
|
|
234
|
+
* @param {number} byte_stride
|
|
235
|
+
* @param {number} version
|
|
236
|
+
* @param {number[]|Uint8Array} [imposed]
|
|
237
|
+
* @returns {Uint8Array} one mode byte per channel
|
|
238
|
+
* @throws {Error} if the caller imposed modes that are not modes, or imposed any under version 0
|
|
239
|
+
*/
|
|
240
|
+
function resolve_channel_modes(source, count, byte_stride, version, imposed) {
|
|
241
|
+
const channel_count = byte_stride >> 2;
|
|
242
|
+
const modes = new Uint8Array(channel_count);
|
|
243
|
+
|
|
244
|
+
if (version === 0) {
|
|
245
|
+
assert.ok(
|
|
246
|
+
imposed === undefined,
|
|
247
|
+
'channel modes are a version 1 feature, and this stream is version 0'
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
return modes;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (imposed !== undefined) {
|
|
254
|
+
assert.equal(
|
|
255
|
+
imposed.length, channel_count,
|
|
256
|
+
`channel_modes(=${imposed.length}) must have one entry per channel(=${channel_count})`
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
for (let c = 0; c < channel_count; c++) {
|
|
260
|
+
const mode_byte = imposed[c];
|
|
261
|
+
const mode = mode_byte & 0x0f;
|
|
262
|
+
|
|
263
|
+
assert.isOneOf(mode, [0, 1, 2], `channel_modes[${c}] mode`);
|
|
264
|
+
assert.ok(
|
|
265
|
+
mode === 2 || (mode_byte >> 4) === 0,
|
|
266
|
+
`channel_modes[${c}] carries a rotation in mode ${mode}, which has none`
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
modes[c] = mode_byte;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return modes;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (let c = 0; c < channel_count; c++) {
|
|
276
|
+
modes[c] = choose_channel_mode(source, count, byte_stride, c);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return modes;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Pick the mode byte for one channel, by pricing the candidates on the stream's first block.
|
|
284
|
+
*
|
|
285
|
+
* The mode is a property of the whole stream — the decoder reads it once, out of the tail — so it
|
|
286
|
+
* cannot be revisited per block, and pricing every candidate against every block to choose it once
|
|
287
|
+
* would cost more than the choice is worth. The first block is what gets measured, and for the
|
|
288
|
+
* data this format carries (vertex attributes, laid out one way for their whole run) it is
|
|
289
|
+
* representative. A caller who knows better passes `channel_modes` and skips this.
|
|
290
|
+
*
|
|
291
|
+
* Mode 2's rotation is not searched exhaustively either. Every bit that ever changes between
|
|
292
|
+
* consecutive elements is collected into one mask, and the rotation that gathers those bits into
|
|
293
|
+
* the fewest whole bytes is the one tried — which is the whole point of the rotation, and reduces
|
|
294
|
+
* thirty-two candidates to one.
|
|
295
|
+
*
|
|
296
|
+
* @param {Uint8Array} source
|
|
297
|
+
* @param {number} count
|
|
298
|
+
* @param {number} byte_stride
|
|
299
|
+
* @param {number} channel
|
|
300
|
+
* @returns {number} the mode byte: low nibble the mode, high nibble the rotation
|
|
301
|
+
*/
|
|
302
|
+
function choose_channel_mode(source, count, byte_stride, channel) {
|
|
303
|
+
const sample = Math.min(count, meshopt_max_block_elements(byte_stride));
|
|
304
|
+
|
|
305
|
+
if (sample === 0) {
|
|
306
|
+
return 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const k0 = channel * 4;
|
|
310
|
+
|
|
311
|
+
// every bit that changes anywhere in the sample, so the rotation is chosen from all of them
|
|
312
|
+
let mask = 0;
|
|
313
|
+
let previous = read_le_32(source, k0);
|
|
314
|
+
|
|
315
|
+
for (let i = 0; i < sample; i++) {
|
|
316
|
+
const value = read_le_32(source, i * byte_stride + k0);
|
|
317
|
+
|
|
318
|
+
mask = (mask | (value ^ previous)) >>> 0;
|
|
319
|
+
previous = value;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let rotation = 0;
|
|
323
|
+
let fewest = 5;
|
|
324
|
+
|
|
325
|
+
for (let r = 0; r < 32; r++) {
|
|
326
|
+
const rotated = rotate_left_32(mask, (32 - r) & 31);
|
|
327
|
+
const bytes = (rotated & 0xff ? 1 : 0)
|
|
328
|
+
+ ((rotated >>> 8) & 0xff ? 1 : 0)
|
|
329
|
+
+ ((rotated >>> 16) & 0xff ? 1 : 0)
|
|
330
|
+
+ ((rotated >>> 24) & 0xff ? 1 : 0);
|
|
331
|
+
|
|
332
|
+
if (bytes < fewest) {
|
|
333
|
+
rotation = r;
|
|
334
|
+
fewest = bytes;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const candidates = [0, 1, 2 | (rotation << 4)];
|
|
339
|
+
|
|
340
|
+
let best = 0;
|
|
341
|
+
let best_size = Number.POSITIVE_INFINITY;
|
|
342
|
+
|
|
343
|
+
for (const mode_byte of candidates) {
|
|
344
|
+
const size = price_channel(source, 0, sample, byte_stride, channel, mode_byte);
|
|
345
|
+
|
|
346
|
+
if (size < best_size) {
|
|
347
|
+
best = mode_byte;
|
|
348
|
+
best_size = size;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return best;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* What one channel's four byte positions cost over a block under a given mode, version 1 rules.
|
|
357
|
+
*
|
|
358
|
+
* The deltas go into {@link candidate_scratch} rather than {@link delta_scratch}, because this runs
|
|
359
|
+
* before the block loop and must not disturb what the block loop is holding.
|
|
360
|
+
*
|
|
361
|
+
* @param {Uint8Array} source
|
|
362
|
+
* @param {number} element_offset
|
|
363
|
+
* @param {number} block_elements
|
|
364
|
+
* @param {number} byte_stride
|
|
365
|
+
* @param {number} channel
|
|
366
|
+
* @param {number} mode_byte
|
|
367
|
+
* @returns {number} bytes, the share of the control header included
|
|
368
|
+
*/
|
|
369
|
+
function price_channel(source, element_offset, block_elements, byte_stride, channel, mode_byte) {
|
|
370
|
+
const group_count = Math.ceil(block_elements / MESHOPT_GROUP_SIZE);
|
|
371
|
+
const aligned_count = group_count * MESHOPT_GROUP_SIZE;
|
|
372
|
+
|
|
373
|
+
// the sample's own baseline, which is the first element, exactly as the real encode will see it
|
|
374
|
+
const first = element_offset * byte_stride;
|
|
375
|
+
|
|
376
|
+
for (let j = 0; j < 4; j++) {
|
|
377
|
+
candidate_baseline[channel * 4 + j] = source[first + channel * 4 + j];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
write_channel_deltas(
|
|
381
|
+
candidate_scratch, candidate_baseline, source, element_offset, block_elements,
|
|
382
|
+
byte_stride, aligned_count, channel, mode_byte
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
let size = 1; // the channel's byte of the block's control header
|
|
386
|
+
|
|
387
|
+
for (let j = 0; j < 4; j++) {
|
|
388
|
+
const k = channel * 4 + j;
|
|
389
|
+
|
|
390
|
+
size += price_byte_position(
|
|
391
|
+
candidate_scratch, k * aligned_count, group_count, aligned_count,
|
|
392
|
+
group_choice_discard, 0
|
|
393
|
+
).size;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return size;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* The cheapest way to store one byte position's deltas, and what it costs.
|
|
401
|
+
*
|
|
402
|
+
* Version 1's four control modes are not four widths — two of them are answers to questions the
|
|
403
|
+
* group header cannot ask. A position that never changes is free, and one whose every group would
|
|
404
|
+
* have to be stored raw anyway is cheaper without the header that says so.
|
|
405
|
+
*
|
|
406
|
+
* @param {Uint8Array} deltas
|
|
407
|
+
* @param {number} deltas_offset
|
|
408
|
+
* @param {number} group_count
|
|
409
|
+
* @param {number} aligned_count
|
|
410
|
+
* @param {Uint8Array} choices `group_count` header values are written here, for whichever data
|
|
411
|
+
* block control mode won — untouched if neither did
|
|
412
|
+
* @param {number} choices_offset
|
|
413
|
+
* @returns {{control: number, size: number}}
|
|
414
|
+
*/
|
|
415
|
+
function price_byte_position(deltas, deltas_offset, group_count, aligned_count, choices, choices_offset) {
|
|
416
|
+
let all_zero = true;
|
|
417
|
+
|
|
418
|
+
for (let i = 0; i < aligned_count; i++) {
|
|
419
|
+
if (deltas[deltas_offset + i] !== 0) {
|
|
420
|
+
all_zero = false;
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (all_zero) {
|
|
426
|
+
return { control: CONTROL_ZERO, size: 0 };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
let control = CONTROL_LITERAL;
|
|
430
|
+
let size = aligned_count;
|
|
431
|
+
|
|
432
|
+
const size_0 = meshopt_choose_data_block(
|
|
433
|
+
deltas, deltas_offset, group_count, MESHOPT_BITS_BY_CONTROL[0], choices, choices_offset
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
if (size_0 < size) {
|
|
437
|
+
control = 0;
|
|
438
|
+
size = size_0;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const size_1 = meshopt_choose_data_block(
|
|
442
|
+
deltas, deltas_offset, group_count, MESHOPT_BITS_BY_CONTROL[1], group_choice_candidate, 0
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
if (size_1 < size) {
|
|
446
|
+
control = 1;
|
|
447
|
+
size = size_1;
|
|
448
|
+
|
|
449
|
+
for (let g = 0; g < group_count; g++) {
|
|
450
|
+
choices[choices_offset + g] = group_choice_candidate[g];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return { control, size };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Encode one block: its control header if it has one, then every byte position in turn.
|
|
459
|
+
*
|
|
460
|
+
* @param {Uint8Array} destination
|
|
461
|
+
* @param {number} offset
|
|
462
|
+
* @param {Uint8Array} source
|
|
463
|
+
* @param {number} element_offset first element of this block, counted in the whole stream
|
|
464
|
+
* @param {number} block_elements
|
|
465
|
+
* @param {number} byte_stride
|
|
466
|
+
* @param {number} channel_count
|
|
467
|
+
* @param {number} version
|
|
468
|
+
* @param {Uint8Array} channel_modes
|
|
469
|
+
* @returns {number} the offset just past the block
|
|
470
|
+
*/
|
|
471
|
+
function encode_block(
|
|
472
|
+
destination, offset,
|
|
473
|
+
source, element_offset, block_elements,
|
|
474
|
+
byte_stride, channel_count, version, channel_modes
|
|
475
|
+
) {
|
|
476
|
+
const group_count = Math.ceil(block_elements / MESHOPT_GROUP_SIZE);
|
|
477
|
+
const aligned_count = group_count * MESHOPT_GROUP_SIZE;
|
|
478
|
+
|
|
479
|
+
for (let c = 0; c < channel_count; c++) {
|
|
480
|
+
write_channel_deltas(
|
|
481
|
+
delta_scratch, baseline, source, element_offset, block_elements,
|
|
482
|
+
byte_stride, aligned_count, c, channel_modes[c]
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (version === 0) {
|
|
487
|
+
// no control header stands between the choosing and the writing, so they are one pass
|
|
488
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
489
|
+
meshopt_choose_data_block(
|
|
490
|
+
delta_scratch, k * aligned_count, group_count,
|
|
491
|
+
MESHOPT_BITS_VERSION_0, group_choice, 0
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
offset = meshopt_encode_data_block(
|
|
495
|
+
destination, offset,
|
|
496
|
+
delta_scratch, k * aligned_count,
|
|
497
|
+
group_count, MESHOPT_BITS_VERSION_0,
|
|
498
|
+
group_choice, 0
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
const control_offset = offset;
|
|
503
|
+
|
|
504
|
+
destination.fill(0, control_offset, control_offset + channel_count);
|
|
505
|
+
offset += channel_count;
|
|
506
|
+
|
|
507
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
508
|
+
const chosen = price_byte_position(
|
|
509
|
+
delta_scratch, k * aligned_count, group_count, aligned_count,
|
|
510
|
+
group_choice, k * MAX_GROUPS
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
control_choice[k] = chosen.control;
|
|
514
|
+
|
|
515
|
+
destination[control_offset + (k >> 2)] |= chosen.control << ((k & 3) * 2);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
519
|
+
const control = control_choice[k];
|
|
520
|
+
const deltas_offset = k * aligned_count;
|
|
521
|
+
|
|
522
|
+
if (control === CONTROL_ZERO) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (control === CONTROL_LITERAL) {
|
|
527
|
+
for (let i = 0; i < aligned_count; i++) {
|
|
528
|
+
destination[offset + i] = delta_scratch[deltas_offset + i];
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
offset += aligned_count;
|
|
532
|
+
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
offset = meshopt_encode_data_block(
|
|
537
|
+
destination, offset,
|
|
538
|
+
delta_scratch, deltas_offset,
|
|
539
|
+
group_count, MESHOPT_BITS_BY_CONTROL[control],
|
|
540
|
+
group_choice, k * MAX_GROUPS
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const last = (element_offset + block_elements - 1) * byte_stride;
|
|
546
|
+
|
|
547
|
+
for (let k = 0; k < byte_stride; k++) {
|
|
548
|
+
baseline[k] = source[last + k];
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return offset;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Turn one channel's values into the delta bytes the decoder will reconstruct them from — the
|
|
556
|
+
* inverse of `reconstruct_channel`, and the only place the three modes differ.
|
|
557
|
+
*
|
|
558
|
+
* The deltas past `block_elements` are the group alignment's padding. They are written as zero
|
|
559
|
+
* because zero is what costs least, and the decoder discards them.
|
|
560
|
+
*
|
|
561
|
+
* @param {Uint8Array} deltas
|
|
562
|
+
* @param {Uint8Array} from the element the block's first delta is taken against
|
|
563
|
+
* @param {Uint8Array} source
|
|
564
|
+
* @param {number} element_offset
|
|
565
|
+
* @param {number} block_elements
|
|
566
|
+
* @param {number} byte_stride
|
|
567
|
+
* @param {number} aligned_count stride of `deltas`, per byte position
|
|
568
|
+
* @param {number} channel
|
|
569
|
+
* @param {number} mode_byte low nibble the mode, high nibble the rotation
|
|
570
|
+
*/
|
|
571
|
+
function write_channel_deltas(
|
|
572
|
+
deltas, from, source, element_offset, block_elements,
|
|
573
|
+
byte_stride, aligned_count, channel, mode_byte
|
|
574
|
+
) {
|
|
575
|
+
const k0 = channel * 4;
|
|
576
|
+
const mode = mode_byte & 0x0f;
|
|
577
|
+
|
|
578
|
+
for (let j = 0; j < 4; j++) {
|
|
579
|
+
const k = k0 + j;
|
|
580
|
+
|
|
581
|
+
deltas.fill(0, k * aligned_count + block_elements, (k + 1) * aligned_count);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (mode === 0) {
|
|
585
|
+
for (let j = 0; j < 4; j++) {
|
|
586
|
+
const k = k0 + j;
|
|
587
|
+
const out = k * aligned_count;
|
|
588
|
+
|
|
589
|
+
let previous = from[k];
|
|
590
|
+
let read = element_offset * byte_stride + k;
|
|
591
|
+
|
|
592
|
+
for (let i = 0; i < block_elements; i++) {
|
|
593
|
+
const value = source[read];
|
|
594
|
+
|
|
595
|
+
deltas[out + i] = zigzag_8(previous, value);
|
|
596
|
+
|
|
597
|
+
previous = value;
|
|
598
|
+
read += byte_stride;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (mode === 1) {
|
|
606
|
+
for (let h = 0; h < 2; h++) {
|
|
607
|
+
const k = k0 + h * 2;
|
|
608
|
+
const out_low = k * aligned_count;
|
|
609
|
+
const out_high = (k + 1) * aligned_count;
|
|
610
|
+
|
|
611
|
+
let previous = from[k] | (from[k + 1] << 8);
|
|
612
|
+
let read = element_offset * byte_stride + k;
|
|
613
|
+
|
|
614
|
+
for (let i = 0; i < block_elements; i++) {
|
|
615
|
+
const value = source[read] | (source[read + 1] << 8);
|
|
616
|
+
const zigzag = zigzag_16(previous, value);
|
|
617
|
+
|
|
618
|
+
deltas[out_low + i] = zigzag & 0xff;
|
|
619
|
+
deltas[out_high + i] = zigzag >>> 8;
|
|
620
|
+
|
|
621
|
+
previous = value;
|
|
622
|
+
read += byte_stride;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const rotation = mode_byte >> 4;
|
|
630
|
+
|
|
631
|
+
const out_0 = k0 * aligned_count;
|
|
632
|
+
const out_1 = (k0 + 1) * aligned_count;
|
|
633
|
+
const out_2 = (k0 + 2) * aligned_count;
|
|
634
|
+
const out_3 = (k0 + 3) * aligned_count;
|
|
635
|
+
|
|
636
|
+
let previous = read_le_32(from, k0);
|
|
637
|
+
let read = element_offset * byte_stride + k0;
|
|
638
|
+
|
|
639
|
+
for (let i = 0; i < block_elements; i++) {
|
|
640
|
+
const value = read_le_32(source, read);
|
|
641
|
+
|
|
642
|
+
// the decoder rotates left by `rotation`, so this rotates back by the same amount
|
|
643
|
+
const delta = rotate_left_32((value ^ previous) >>> 0, (32 - rotation) & 31);
|
|
644
|
+
|
|
645
|
+
deltas[out_0 + i] = delta & 0xff;
|
|
646
|
+
deltas[out_1 + i] = (delta >>> 8) & 0xff;
|
|
647
|
+
deltas[out_2 + i] = (delta >>> 16) & 0xff;
|
|
648
|
+
deltas[out_3 + i] = delta >>> 24;
|
|
649
|
+
|
|
650
|
+
previous = value;
|
|
651
|
+
read += byte_stride;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* @param {Uint8Array} data
|
|
657
|
+
* @param {number} offset
|
|
658
|
+
* @returns {number} the little-endian 32-bit word there, unsigned
|
|
659
|
+
*/
|
|
660
|
+
function read_le_32(data, offset) {
|
|
661
|
+
return (
|
|
662
|
+
data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)
|
|
663
|
+
) >>> 0;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* @param {number} value
|
|
668
|
+
* @param {number} amount
|
|
669
|
+
* @returns {number} unsigned
|
|
670
|
+
*/
|
|
671
|
+
function rotate_left_32(value, amount) {
|
|
672
|
+
return ((value << amount) | (value >>> (32 - amount))) >>> 0;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Zigzag one 8-bit delta, so that a small step either way is a small number: the difference is
|
|
677
|
+
* taken modulo 256, and its sign bit folded into bit 0.
|
|
678
|
+
*
|
|
679
|
+
* @param {number} previous
|
|
680
|
+
* @param {number} current
|
|
681
|
+
* @returns {number}
|
|
682
|
+
*/
|
|
683
|
+
function zigzag_8(previous, current) {
|
|
684
|
+
const delta = (current - previous) & 0xff;
|
|
685
|
+
|
|
686
|
+
return ((delta & 0x80) !== 0 ? ~(delta << 1) : (delta << 1)) & 0xff;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* The same, over sixteen bits.
|
|
691
|
+
*
|
|
692
|
+
* @param {number} previous
|
|
693
|
+
* @param {number} current
|
|
694
|
+
* @returns {number}
|
|
695
|
+
*/
|
|
696
|
+
function zigzag_16(previous, current) {
|
|
697
|
+
const delta = (current - previous) & 0xffff;
|
|
698
|
+
|
|
699
|
+
return ((delta & 0x8000) !== 0 ? ~(delta << 1) : (delta << 1)) & 0xffff;
|
|
700
|
+
}
|