@woosh/meep-engine 3.28.0 → 3.30.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/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +14 -2
- package/src/core/binary/BitSet.d.ts +16 -0
- package/src/core/binary/BitSet.d.ts.map +1 -1
- package/src/core/binary/BitSet.js +159 -49
- package/src/core/binary/allocator/OffsetAllocator.d.ts +48 -0
- package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
- package/src/core/binary/allocator/OffsetAllocator.js +208 -9
- package/src/core/binary/compression/compress_bytes.d.ts +20 -0
- package/src/core/binary/compression/compress_bytes.d.ts.map +1 -0
- package/src/core/binary/compression/compress_bytes.js +35 -0
- package/src/core/binary/compression/decompress_bytes_into.d.ts +30 -0
- package/src/core/binary/compression/decompress_bytes_into.d.ts.map +1 -0
- package/src/core/binary/compression/decompress_bytes_into.js +87 -0
- package/src/core/binary/compression/deflate_decompress_bound.d.ts +27 -0
- package/src/core/binary/compression/deflate_decompress_bound.d.ts.map +1 -0
- package/src/core/binary/compression/deflate_decompress_bound.js +32 -0
- package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/BVH.js +14 -1
- package/src/core/cache/FrequencySketch.d.ts +3 -1
- package/src/core/cache/FrequencySketch.d.ts.map +1 -1
- package/src/core/cache/FrequencySketch.js +10 -4
- package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
- package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
- package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts +24 -0
- package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_add.js +31 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts +46 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_reduce.js +104 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts +62 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.js +158 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts +21 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_size.js +24 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts +34 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_solve.js +55 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts +28 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts.map +1 -0
- package/src/core/geom/3d/quadric/quadric_attribute_sub.js +35 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts +15 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +35 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts +93 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.js +128 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts +38 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +113 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +93 -17
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +930 -69
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.js +46 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.js +49 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
- package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts +63 -0
- package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts.map +1 -0
- package/src/core/graph/layout/layered/BoxLayoutOptions.js +80 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.d.ts +35 -0
- package/src/core/graph/layout/layered/LayoutBoxGraph.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.js +53 -1
- package/src/core/graph/layout/layered/layout_assign_coordinates.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.js +72 -68
- package/src/core/graph/layout/layered/layout_assign_layers.d.ts.map +1 -0
- package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.js +4 -2
- package/src/core/graph/layout/layered/layout_break_cycles.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_insert_lanes.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_order_layers.d.ts.map +1 -0
- package/src/core/graph/layout/layered/layout_orient_links.d.ts.map +1 -0
- package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
- package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
- package/src/core/math/lookup/ParameterLookupTable.js +10 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +16 -10
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +7 -51
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +8 -69
- package/src/core/model/node-graph/visual/layout/layout_build_problem.js +1 -1
- package/src/core/model/node-graph/visual/layout/layout_node_graph.js +6 -6
- package/src/core/model/node-graph/visual/layout/route_grid_build.js +1 -1
- package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
- package/src/core/model/object/ImmutableObjectPool.js +17 -7
- package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +95 -1
- package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
- package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
- package/src/core/process/task/task_find_dependency_cycle.js +81 -0
- package/src/core/wfc/WFCCellChoice.d.ts +17 -0
- package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
- package/src/core/wfc/WFCCellChoice.js +36 -0
- package/src/core/wfc/WFCLattice.d.ts +105 -0
- package/src/core/wfc/WFCLattice.d.ts.map +1 -0
- package/src/core/wfc/WFCLattice.js +235 -0
- package/src/core/wfc/WFCRuleTable.d.ts +172 -0
- package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
- package/src/core/wfc/WFCRuleTable.js +334 -0
- package/src/core/wfc/WFCState.d.ts +273 -0
- package/src/core/wfc/WFCState.d.ts.map +1 -0
- package/src/core/wfc/WFCState.js +552 -0
- package/src/core/wfc/WFCStatus.d.ts +11 -0
- package/src/core/wfc/WFCStatus.d.ts.map +1 -0
- package/src/core/wfc/WFCStatus.js +28 -0
- package/src/core/wfc/WFCTileWeights.d.ts +48 -0
- package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
- package/src/core/wfc/WFCTileWeights.js +86 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
- package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
- package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
- package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
- package/src/core/wfc/wfc_estimate_bytes.js +47 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
- package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
- package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
- package/src/core/wfc/wfc_solve.d.ts +21 -0
- package/src/core/wfc/wfc_solve.d.ts.map +1 -0
- package/src/core/wfc/wfc_solve.js +154 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
- package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
- package/src/core/wfc/wfc_state_assign.d.ts +11 -0
- package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_assign.js +36 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
- package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_ban_bits.js +81 -0
- package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
- package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_collapse.js +73 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
- package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
- package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
- package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_propagate.js +155 -0
- package/src/core/wfc/wfc_state_read.d.ts +11 -0
- package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_read.js +38 -0
- package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
- package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_restrict.js +39 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
- package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_set_eligible.js +49 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
- package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
- package/src/core/wfc/wfc_state_undo_to.js +84 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
- package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
- package/src/core/wfc/wfc_words_per_cell.js +16 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +37 -14
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
- package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
- package/src/engine/ecs/guid/UUID.js +45 -17
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
- package/src/engine/graphics3/DebugDrawSystem.d.ts +7 -5
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem.js +206 -203
- package/src/engine/graphics3/DecalSystem.d.ts +4 -5
- package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DecalSystem.js +5 -5
- package/src/engine/graphics3/FogOfWarSystem.d.ts +7 -12
- package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -1
- package/src/engine/graphics3/FogOfWarSystem.js +8 -12
- package/src/engine/graphics3/prefab/prefab_instantiate.js +230 -230
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
- package/src/engine/interpolation/Interpoland.d.ts +7 -1
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/Interpoland.js +8 -1
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +3 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
- package/src/engine/interpolation/pose_interpoland.js +2 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
- package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
- package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
- package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
- package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
- package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
- package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
- package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
- package/src/format/image/avif/av1/util/round2.d.ts +19 -2
- package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
- package/src/format/image/avif/av1/util/round2.js +51 -27
- package/src/format/image/avif/heif/parse_avif_file.js +24 -0
- package/src/shade/RENDERER_CONTRACT.md +12 -7
- package/src/shade/playground/caldera/README.md +529 -0
- package/src/shade/playground/caldera/caldera_format.d.ts +151 -0
- package/src/shade/playground/caldera/caldera_format.d.ts.map +1 -0
- package/src/shade/playground/caldera/caldera_format.js +241 -0
- package/src/shade/playground/caldera/index.html +126 -0
- package/src/shade/playground/caldera/main.d.ts.map +1 -0
- package/src/shade/playground/caldera/main.js +1565 -0
- package/src/shade/playground/mesh_row_ceiling/README.md +85 -0
- package/src/shade/playground/mesh_row_ceiling/index.html +31 -0
- package/src/shade/playground/mesh_row_ceiling/main.d.ts +2 -0
- package/src/shade/playground/mesh_row_ceiling/main.d.ts.map +1 -0
- package/src/shade/playground/mesh_row_ceiling/main.js +389 -0
- package/src/shade/playground/vgeo_runtime/README.md +5 -3
- package/src/shade/playground/vgeo_runtime/main.js +3 -2
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +3 -3
- package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -1
- package/src/shade/playground/vgeo_runtime/sample_asset.js +3 -3
- package/src/shade/playground/vgeo_scene/README.md +103 -24
- package/src/shade/playground/vgeo_scene/main.js +525 -24
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +6 -0
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +23 -0
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +100 -8
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +69 -33
- package/src/shade/renderer/extension/RenderExtension.d.ts +15 -18
- package/src/shade/renderer/extension/RenderExtension.d.ts.map +1 -1
- package/src/shade/renderer/extension/RenderExtension.js +15 -19
- package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts +5 -5
- package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts.map +1 -1
- package/src/shade/renderer/extension/RenderExtensionRegistry.js +29 -166
- package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/GPUGeometryManager.js +73 -16
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +18 -3
- package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +663 -0
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +100 -5
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +688 -81
- package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +540 -0
- package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +155 -64
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +76 -16
- package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +3 -3
- package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +448 -0
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +44 -7
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +48 -9
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts +40 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.js +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts +91 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.js +91 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.js +25 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts +77 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.js +84 -0
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +19 -6
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.js +20 -7
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts +19 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.js +58 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.js +116 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +545 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.js +110 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +2 -27
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.js +120 -1564
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts +26 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.js +123 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts +26 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.js +78 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.js +337 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.js +147 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts +37 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.js +243 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts +29 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.js +117 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts +46 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +74 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +78 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +12 -7
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.js +33 -11
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts +58 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.js +79 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts +39 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.js +68 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts +53 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.js +243 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +32 -7
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +243 -24
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +12 -8
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.js +58 -50
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +5 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +13 -1
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts +38 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.js +81 -0
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +23 -5
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.js +28 -21
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.js +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.js +224 -60
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +15 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.js +17 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.js +19 -0
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +10 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.js +103 -9
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +14 -2
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.js +38 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts +6 -2
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +27 -4
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts +32 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.js +93 -0
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +20 -6
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +22 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/{vgeo_layers_copy_vertex.js → vgeo_layers_copy_corner.js} +8 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts +18 -7
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +19 -8
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.d.ts +3 -3
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +3 -3
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts +27 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.js +97 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts +7 -3
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +21 -11
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts +21 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.js +41 -0
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +19 -1
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +20 -1
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts +25 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.js +24 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +5 -0
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts +10 -3
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +37 -3
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +21 -13
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +37 -13
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts +8 -2
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +14 -2
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +18 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +19 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +6 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts +21 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.js +20 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts +9 -0
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +10 -1
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +1 -2
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +10 -3
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts +20 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.js +43 -0
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +4 -2
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +8 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +2 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts +6 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.js +6 -4
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +13 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +13 -2
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +5 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +14 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +1 -0
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts +4 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.js +53 -11
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +7 -3
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts +12 -2
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +111 -33
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts +6 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +112 -42
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts +2 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +51 -21
- package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +385 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +9 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +34 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +18 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +47 -19
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +22 -4
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +7 -2
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +8 -3
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +10 -0
- package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +13 -11
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +13 -11
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +4 -0
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +3 -4
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +6 -2
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +90 -12
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts +22 -0
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts.map +1 -0
- package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js +21 -0
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
- package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
- package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
- package/src/shade/renderer/particles/DESIGN.md +14 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +8 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +13 -8
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -6
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +4 -12
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +4 -5
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +5 -3
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.js +11 -4
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +4 -3
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +3 -0
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +94 -90
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +13 -5
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +96 -89
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +3 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.js +4 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +100 -89
- package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.js +31 -7
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +1 -7
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +161 -153
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +2 -7
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +4 -8
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +5 -8
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +25 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +24 -0
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +10 -0
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +30 -21
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
- package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
- package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipManager.js +22 -0
- package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
- package/src/view/tooltip/DomTooltipObserver.js +13 -3
- package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts +0 -61
- package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/main.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/sample_asset.d.ts +0 -18
- package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +0 -1
- package/src/shade/playground/vgeo_viewer/select_cut.d.ts +0 -173
- package/src/shade/playground/vgeo_viewer/select_cut.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts +0 -52
- package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts +0 -24
- package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts +0 -32
- package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts +0 -59
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts +0 -44
- package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts +0 -20
- package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts +0 -73
- package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts +0 -36
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts +0 -13
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts +0 -19
- package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts +0 -29
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts +0 -31
- package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts +0 -18
- package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts +0 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts +0 -12
- package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts +0 -14
- package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts +0 -11
- package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts +0 -46
- package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +0 -22
- package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts +0 -29
- package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts +0 -9
- package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts +0 -10
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts +0 -15
- package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts.map +0 -1
- package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts +0 -10
- package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts +0 -17
- package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts +0 -3
- package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts.map +0 -1
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts +0 -8
- package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts.map +0 -1
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.js +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.d.ts +0 -0
- /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.js +0 -0
- /package/src/shade/playground/{vgeo_viewer → caldera}/main.d.ts +0 -0
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { assert } from "../../../../../../assert.js";
|
|
2
2
|
import { Uint32Heap } from "../../../../../../collection/heap/Uint32Heap.js";
|
|
3
3
|
import { Quadric3 } from "../../../../quadric/Quadric3.js";
|
|
4
|
+
import { QUADRIC3_SIZE } from "../../../../quadric/QUADRIC3_SIZE.js";
|
|
5
|
+
import { quadric3_add } from "../../../../quadric/quadric3_add.js";
|
|
6
|
+
import { quadric3_evaluate } from "../../../../quadric/quadric3_evaluate.js";
|
|
7
|
+
import { quadric_attribute_add } from "../../../../quadric/quadric_attribute_add.js";
|
|
8
|
+
import { quadric_attribute_reduce } from "../../../../quadric/quadric_attribute_reduce.js";
|
|
9
|
+
import { quadric_attribute_size } from "../../../../quadric/quadric_attribute_size.js";
|
|
10
|
+
import { quadric_attribute_solve } from "../../../../quadric/quadric_attribute_solve.js";
|
|
4
11
|
import { NULL_POINTER } from "../BinaryTopology.js";
|
|
12
|
+
import { bt_edge_collapse_fold_limit } from "../query/bt_edge_collapse_fold_limit.js";
|
|
5
13
|
import { bt_edge_collapse_would_fold } from "../query/bt_edge_collapse_would_fold.js";
|
|
6
14
|
import { bt_loop_kill } from "./bt_loop_kill.js";
|
|
7
15
|
import { bt_mesh_compute_vertex_quadrics } from "./bt_mesh_compute_vertex_quadrics.js";
|
|
@@ -17,6 +25,73 @@ const scratch_coord_a = new Float32Array(3);
|
|
|
17
25
|
const scratch_coord_b = new Float32Array(3);
|
|
18
26
|
const scratch_target = new Float32Array(3);
|
|
19
27
|
const scratch_combined_q = new Quadric3();
|
|
28
|
+
const scratch_anchor = new Float32Array(3);
|
|
29
|
+
const scratch_candidate = new Float32Array(3);
|
|
30
|
+
const scratch_placement = new Float32Array(3);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* How much of the way to the fold limit a constrained placement is allowed to travel.
|
|
34
|
+
*
|
|
35
|
+
* The limit itself is where a surviving triangle reaches *zero* area, which is refused along with an
|
|
36
|
+
* outright flip and for the same reason: a zero-area triangle has no normal, so the quadrics built
|
|
37
|
+
* from it afterwards are meaningless and every later collapse around it is priced off nothing. So
|
|
38
|
+
* stopping short of the limit is a statement about the mesh rather than about arithmetic.
|
|
39
|
+
*
|
|
40
|
+
* Half, because the feasible interval along the segment is `[0, limit)` and its midpoint is the
|
|
41
|
+
* placement furthest from both ends of it — from the anchor, which is the placement this was trying
|
|
42
|
+
* to improve on, and from the degeneracy. Nothing is given up by stopping there: swept against 0.9
|
|
43
|
+
* and 0.99 over Zorah's 479k-triangle `mesh0[0]`, all three build the *same* 34 levels to the *same*
|
|
44
|
+
* root error of `1.0445e+3`, and differ only in cluster count, by 0.3% and in no particular
|
|
45
|
+
* direction (11,511 / 11,498 / 11,532). The choice is free on everything that was measured, so it
|
|
46
|
+
* goes to the end that cannot produce a sliver.
|
|
47
|
+
* @type {number}
|
|
48
|
+
*/
|
|
49
|
+
const FOLD_LIMIT_TRAVEL = 0.5;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The pair's summed attribute quadric, and its reduction. Grown to whatever channel count the
|
|
53
|
+
* current call carries and reused across every candidate it scores — `evaluate_edge_collapse` runs
|
|
54
|
+
* once per heap insert and again for every neighbour of every collapse, and an allocation there is
|
|
55
|
+
* an allocation per edge per round.
|
|
56
|
+
* @type {Float64Array}
|
|
57
|
+
*/
|
|
58
|
+
let scratch_attribute_sum = new Float64Array(0);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @type {Float64Array}
|
|
62
|
+
*/
|
|
63
|
+
const scratch_attribute_reduced = new Float64Array(QUADRIC3_SIZE);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* One wedge group's reduction, on its way into the sum over groups.
|
|
67
|
+
* @type {Float64Array}
|
|
68
|
+
*/
|
|
69
|
+
const scratch_group_reduced = new Float64Array(QUADRIC3_SIZE);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The wedges of a candidate's two endpoints, the survivor's first, and the local union-find that
|
|
73
|
+
* groups them. Only the multi-wedge path touches these, which on smooth geometry is never.
|
|
74
|
+
* @type {Uint32Array}
|
|
75
|
+
*/
|
|
76
|
+
let scratch_wedge_ids = new Uint32Array(0);
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @type {Uint32Array}
|
|
80
|
+
*/
|
|
81
|
+
let scratch_wedge_group = new Uint32Array(0);
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The corners of the faces a collapse would destroy, as `(at a, at b)` pairs. Two on a manifold
|
|
85
|
+
* edge, which is why it starts small and grows.
|
|
86
|
+
* @type {Uint32Array}
|
|
87
|
+
*/
|
|
88
|
+
let scratch_dying_loops = new Uint32Array(8);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The fitted attribute record, on its way onto the survivor.
|
|
92
|
+
* @type {Float64Array}
|
|
93
|
+
*/
|
|
94
|
+
let scratch_attribute_values = new Float64Array(0);
|
|
20
95
|
|
|
21
96
|
/**
|
|
22
97
|
* Count the number of still-live faces attached to an edge via its radial loop cycle.
|
|
@@ -52,6 +127,143 @@ function count_edge_faces(mesh, edge_id) {
|
|
|
52
127
|
return count;
|
|
53
128
|
}
|
|
54
129
|
|
|
130
|
+
/**
|
|
131
|
+
* What a collapse of this pair costs with its surviving vertex at `(x, y, z)`.
|
|
132
|
+
*
|
|
133
|
+
* The two geometric quadrics say how far the placement is from the surface the pair was cut out of;
|
|
134
|
+
* the reduced attribute quadric, when there is one, says what the channels still cost once each has
|
|
135
|
+
* been given its best value there — in units of length, because the weights put them there.
|
|
136
|
+
*
|
|
137
|
+
* It is exactly the function the placement minimizes — quadric evaluation is linear in the quadric,
|
|
138
|
+
* so the sum `optimize` was handed evaluates to the sum of the two terms below — which is why
|
|
139
|
+
* constraining the placement can only ever raise this, never lower it. Read at the placement
|
|
140
|
+
* actually chosen, so the queue's order is a statement about the collapses that will happen rather
|
|
141
|
+
* than about ones that will not.
|
|
142
|
+
*
|
|
143
|
+
* @param {Quadric3} q1
|
|
144
|
+
* @param {Quadric3} q2
|
|
145
|
+
* @param {boolean} has_attributes whether {@link scratch_attribute_reduced} holds this pair's
|
|
146
|
+
* @param {number} x
|
|
147
|
+
* @param {number} y
|
|
148
|
+
* @param {number} z
|
|
149
|
+
* @returns {number}
|
|
150
|
+
*/
|
|
151
|
+
function placement_cost(q1, q2, has_attributes, x, y, z) {
|
|
152
|
+
// Cost can drift slightly negative for near-zero values due to fp noise.
|
|
153
|
+
const geometric = Math.abs(q1.evaluate(x, y, z) + q2.evaluate(x, y, z));
|
|
154
|
+
|
|
155
|
+
if (!has_attributes) {
|
|
156
|
+
return geometric;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return geometric + Math.abs(quadric3_evaluate(scratch_attribute_reduced, 0, x, y, z));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Move `out_target` to the cheapest placement near it that turns no surviving triangle over, or
|
|
164
|
+
* report that the pair has none.
|
|
165
|
+
*
|
|
166
|
+
* **A placement that folds is not an impossible collapse, and this is where the two stop being
|
|
167
|
+
* conflated.** What the quadric hands back is the unconstrained minimum of a convex cost. Keeping
|
|
168
|
+
* every surviving triangle facing the way it did is a *constraint* on that minimum — a linear one,
|
|
169
|
+
* as {@link bt_edge_collapse_fold_limit} derives, so the placements that satisfy it are a convex set
|
|
170
|
+
* and the constrained minimum is the thing this should be choosing. Testing the unconstrained
|
|
171
|
+
* answer and abandoning the collapse when it fails is the classic way of getting that wrong, and it
|
|
172
|
+
* is expensive: measured over one 479k-triangle Zorah primitive, 12% to 15% of every candidate
|
|
173
|
+
* collapse scored was discarded that way — and it is not a tail, it is the whole build, which is how
|
|
174
|
+
* a level of the DAG that could still have been halved came to be reported as having no coarser
|
|
175
|
+
* form at all.
|
|
176
|
+
*
|
|
177
|
+
* The constrained minimum lies on the boundary whenever the unconstrained one is outside, and the
|
|
178
|
+
* boundary is reached along a segment: the cost is convex with its minimum at `out_target`, so along
|
|
179
|
+
* a segment ending there it falls monotonically, and the best placement on that segment is simply
|
|
180
|
+
* the furthest one the fold limit allows. That makes this exact in one dimension rather than a
|
|
181
|
+
* search — what is heuristic is only the choice of where to measure *from*.
|
|
182
|
+
*
|
|
183
|
+
* Three anchors, and they are the three points the surface already passes through: either endpoint,
|
|
184
|
+
* or the middle of the edge. An anchor that folds on its own is no anchor and is skipped; when all
|
|
185
|
+
* three do, the pair really has nothing this can offer and the collapse is refused.
|
|
186
|
+
*
|
|
187
|
+
* @param {BinaryTopology} mesh
|
|
188
|
+
* @param {number} v1
|
|
189
|
+
* @param {number} v2
|
|
190
|
+
* @param {Quadric3} q1
|
|
191
|
+
* @param {Quadric3} q2
|
|
192
|
+
* @param {boolean} has_attributes
|
|
193
|
+
* @param {Float32Array|number[]} out_target the wanted placement, replaced by the chosen one
|
|
194
|
+
* @returns {boolean} `false` when every anchor folds, and the collapse has no placement at all
|
|
195
|
+
*/
|
|
196
|
+
function constrain_placement(mesh, v1, v2, q1, q2, has_attributes, out_target) {
|
|
197
|
+
if (!bt_edge_collapse_would_fold(mesh, v1, v2, out_target)) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
mesh.vertex_read_coordinate(scratch_coord_a, 0, v1);
|
|
202
|
+
mesh.vertex_read_coordinate(scratch_coord_b, 0, v2);
|
|
203
|
+
|
|
204
|
+
// Tracked separately rather than read off `best`, which cannot say the difference between "no
|
|
205
|
+
// anchor held" and "one did, at a price that overflowed" — a quadric carrying the planes of a
|
|
206
|
+
// few hundred thousand source triangles is not far off it.
|
|
207
|
+
let placed = false;
|
|
208
|
+
|
|
209
|
+
let best = Number.POSITIVE_INFINITY;
|
|
210
|
+
|
|
211
|
+
for (let anchor = 0; anchor < 3; anchor++) {
|
|
212
|
+
if (anchor === 0) {
|
|
213
|
+
scratch_anchor.set(scratch_coord_a);
|
|
214
|
+
} else if (anchor === 1) {
|
|
215
|
+
scratch_anchor.set(scratch_coord_b);
|
|
216
|
+
} else {
|
|
217
|
+
scratch_anchor[0] = (scratch_coord_a[0] + scratch_coord_b[0]) * 0.5;
|
|
218
|
+
scratch_anchor[1] = (scratch_coord_a[1] + scratch_coord_b[1]) * 0.5;
|
|
219
|
+
scratch_anchor[2] = (scratch_coord_a[2] + scratch_coord_b[2]) * 0.5;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const limit = bt_edge_collapse_fold_limit(mesh, v1, v2, scratch_anchor, out_target);
|
|
223
|
+
|
|
224
|
+
if (limit < 0) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const travel = limit * FOLD_LIMIT_TRAVEL;
|
|
229
|
+
|
|
230
|
+
scratch_candidate[0] = scratch_anchor[0] + (out_target[0] - scratch_anchor[0]) * travel;
|
|
231
|
+
scratch_candidate[1] = scratch_anchor[1] + (out_target[1] - scratch_anchor[1]) * travel;
|
|
232
|
+
scratch_candidate[2] = scratch_anchor[2] + (out_target[2] - scratch_anchor[2]) * travel;
|
|
233
|
+
|
|
234
|
+
if (bt_edge_collapse_would_fold(mesh, v1, v2, scratch_candidate)) {
|
|
235
|
+
/*
|
|
236
|
+
The limit is exact in real arithmetic and this is not real arithmetic: a fan whose
|
|
237
|
+
triangles are already slivers can put the crossing and the anchor within a few ulps of
|
|
238
|
+
each other, and half of nothing is still nothing. The anchor is feasible by
|
|
239
|
+
construction, so fall back to it rather than to a search for how much less to travel.
|
|
240
|
+
*/
|
|
241
|
+
scratch_candidate.set(scratch_anchor);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const cost = placement_cost(
|
|
245
|
+
q1, q2, has_attributes,
|
|
246
|
+
scratch_candidate[0], scratch_candidate[1], scratch_candidate[2]
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
if (!placed || cost < best) {
|
|
250
|
+
placed = true;
|
|
251
|
+
best = cost;
|
|
252
|
+
scratch_placement.set(scratch_candidate);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!placed) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
out_target[0] = scratch_placement[0];
|
|
261
|
+
out_target[1] = scratch_placement[1];
|
|
262
|
+
out_target[2] = scratch_placement[2];
|
|
263
|
+
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
55
267
|
/**
|
|
56
268
|
* Evaluate a potential edge collapse: choose the optimal surviving vertex position
|
|
57
269
|
* and return the quadric-based cost. Returns Infinity if the edge cannot be collapsed.
|
|
@@ -60,10 +272,17 @@ function count_edge_faces(mesh, edge_id) {
|
|
|
60
272
|
* @param {number} edge_id
|
|
61
273
|
* @param {Quadric3[]} quadratics
|
|
62
274
|
* @param {Set<number>} restricted_vertices
|
|
275
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}|null}
|
|
276
|
+
* attributes the channels the collapse carries, or `null` to carry none
|
|
277
|
+
* @param {BtWedgeSet|null} wedges which corners of each vertex shade as one
|
|
63
278
|
* @param {Float32Array|number[]} out_target Target position is written here.
|
|
64
|
-
* @returns {number}
|
|
279
|
+
* @returns {number} the price at the placement written, which is the placement the collapse would
|
|
280
|
+
* actually use — so the queue this feeds is ordered on what the collapses cost rather than on
|
|
281
|
+
* what they would have cost somewhere they are not allowed to go
|
|
65
282
|
*/
|
|
66
|
-
function evaluate_edge_collapse(
|
|
283
|
+
function evaluate_edge_collapse(
|
|
284
|
+
mesh, edge_id, quadratics, restricted_vertices, attributes, wedges, out_target
|
|
285
|
+
) {
|
|
67
286
|
const v1 = mesh.edge_read_vertex1(edge_id);
|
|
68
287
|
const v2 = mesh.edge_read_vertex2(edge_id);
|
|
69
288
|
|
|
@@ -89,6 +308,12 @@ function evaluate_edge_collapse(mesh, edge_id, quadratics, restricted_vertices,
|
|
|
89
308
|
return Number.POSITIVE_INFINITY;
|
|
90
309
|
}
|
|
91
310
|
|
|
311
|
+
const has_attributes = attributes !== null;
|
|
312
|
+
|
|
313
|
+
if (has_attributes) {
|
|
314
|
+
reduce_pair_attributes(mesh, v1, v2, attributes, wedges, scratch_attribute_reduced);
|
|
315
|
+
}
|
|
316
|
+
|
|
92
317
|
if (v1_restricted) {
|
|
93
318
|
mesh.vertex_read_coordinate(out_target, 0, v1);
|
|
94
319
|
} else if (v2_restricted) {
|
|
@@ -97,6 +322,22 @@ function evaluate_edge_collapse(mesh, edge_id, quadratics, restricted_vertices,
|
|
|
97
322
|
scratch_combined_q.copy(q1);
|
|
98
323
|
scratch_combined_q.add(q2);
|
|
99
324
|
|
|
325
|
+
if (has_attributes) {
|
|
326
|
+
/*
|
|
327
|
+
The channels decide where the vertex goes, and this is where they do it. `A` and `b` of
|
|
328
|
+
the reduced attribute quadric are what is left of them after the exact solve for their
|
|
329
|
+
own values, so a collapse across a crease or a texture seam is pulled away from the
|
|
330
|
+
placement that would smear it.
|
|
331
|
+
|
|
332
|
+
Only `optimize` reads this sum. Its weight slot is now a plane count plus a triangle
|
|
333
|
+
count, which is not a quantity, and nothing downstream takes a distance from it -- the
|
|
334
|
+
cost below is the two pure geometric quadrics, evaluated separately.
|
|
335
|
+
*/
|
|
336
|
+
quadric3_add(
|
|
337
|
+
scratch_combined_q, 0, scratch_combined_q, 0, scratch_attribute_reduced, 0
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
100
341
|
if (!scratch_combined_q.optimize(out_target)) {
|
|
101
342
|
// Singular combined tensor: fall back to the midpoint
|
|
102
343
|
mesh.vertex_read_coordinate(scratch_coord_a, 0, v1);
|
|
@@ -108,25 +349,45 @@ function evaluate_edge_collapse(mesh, edge_id, quadratics, restricted_vertices,
|
|
|
108
349
|
}
|
|
109
350
|
}
|
|
110
351
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
352
|
+
if (v1_restricted || v2_restricted) {
|
|
353
|
+
/*
|
|
354
|
+
A pin takes the placement with it, so there is no segment to slide along and a fold here is
|
|
355
|
+
final. Refused with the price rather than by the caller, so the queue never holds an edge
|
|
356
|
+
whose cost describes a collapse that will not be performed.
|
|
357
|
+
*/
|
|
358
|
+
if (bt_edge_collapse_would_fold(mesh, v1, v2, out_target)) {
|
|
359
|
+
return Number.POSITIVE_INFINITY;
|
|
360
|
+
}
|
|
361
|
+
} else if (!constrain_placement(mesh, v1, v2, q1, q2, has_attributes, out_target)) {
|
|
362
|
+
return Number.POSITIVE_INFINITY;
|
|
363
|
+
}
|
|
114
364
|
|
|
115
|
-
|
|
116
|
-
|
|
365
|
+
return placement_cost(
|
|
366
|
+
q1, q2, has_attributes, out_target[0], out_target[1], out_target[2]
|
|
367
|
+
);
|
|
117
368
|
}
|
|
118
369
|
|
|
119
370
|
/**
|
|
120
371
|
* Re-score every edge in the disk cycle of `survivor` that is currently in the heap.
|
|
121
372
|
* Edges that were never inserted (e.g. both endpoints restricted) are left untouched.
|
|
122
373
|
*
|
|
374
|
+
* Left untouched including the ones that were left out because they had no placement at the time,
|
|
375
|
+
* which is a real limit and a deliberate one: an edge is excluded when *both* its fans leave it
|
|
376
|
+
* nowhere to go, and a collapse one edge away is unlikely to change that. Re-admitting them here was
|
|
377
|
+
* built and measured — it moved a 479k-triangle Zorah primitive from 34 levels to 33, and cost 60%
|
|
378
|
+
* of the build time to do it.
|
|
379
|
+
*
|
|
123
380
|
* @param {BinaryTopology} mesh
|
|
124
381
|
* @param {number} survivor
|
|
125
382
|
* @param {Quadric3[]} quadratics
|
|
126
383
|
* @param {Set<number>} restricted_vertices
|
|
384
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}|null} attributes
|
|
385
|
+
* @param {BtWedgeSet|null} wedges
|
|
127
386
|
* @param {Uint32Heap} heap
|
|
128
387
|
*/
|
|
129
|
-
function update_neighbor_costs(
|
|
388
|
+
function update_neighbor_costs(
|
|
389
|
+
mesh, survivor, quadratics, restricted_vertices, attributes, wedges, heap
|
|
390
|
+
) {
|
|
130
391
|
let curr_e = mesh.vertex_read_edge(survivor);
|
|
131
392
|
|
|
132
393
|
if (curr_e === NULL_POINTER) {
|
|
@@ -148,7 +409,10 @@ function update_neighbor_costs(mesh, survivor, quadratics, restricted_vertices,
|
|
|
148
409
|
const idx = heap.find_index_by_id(curr_e);
|
|
149
410
|
|
|
150
411
|
if (idx !== -1) {
|
|
151
|
-
const cost = evaluate_edge_collapse(
|
|
412
|
+
const cost = evaluate_edge_collapse(
|
|
413
|
+
mesh, curr_e, quadratics, restricted_vertices, attributes, wedges, scratch_target
|
|
414
|
+
);
|
|
415
|
+
|
|
152
416
|
heap.__update_score_by_index(idx, cost);
|
|
153
417
|
}
|
|
154
418
|
|
|
@@ -156,6 +420,504 @@ function update_neighbor_costs(mesh, survivor, quadratics, restricted_vertices,
|
|
|
156
420
|
} while (curr_e !== start_e && curr_e !== NULL_POINTER);
|
|
157
421
|
}
|
|
158
422
|
|
|
423
|
+
/**
|
|
424
|
+
* The corners of every face a collapse of `(a, b)` would destroy, as pairs — the corner at `a`
|
|
425
|
+
* followed by the corner at `b` — written into {@link scratch_dying_loops}.
|
|
426
|
+
*
|
|
427
|
+
* **The faces a collapse destroys are not only the ones on the edge it names.** They are the ones
|
|
428
|
+
* holding *both* endpoints: the edge's own, the ones on any parallel edge {@link bt_edge_kill_parallels}
|
|
429
|
+
* clears first, and the ones {@link bt_vertex_fuse_duplicate_edges} finds degenerate afterwards. That
|
|
430
|
+
* is {@link collapse_kills_protected_face}'s contract, written down there and true here for the same
|
|
431
|
+
* reason — and a triangle holding both endpoints has an edge between them, so walking the disk cycle
|
|
432
|
+
* for edges to `b` finds all of them.
|
|
433
|
+
*
|
|
434
|
+
* Walking one radial cycle instead would miss the parallel edges, and a wedge whose only dying face
|
|
435
|
+
* is on one of those would be adopted by the survivor rather than merged into it: two corner groups
|
|
436
|
+
* that are one piece of surface, left as two, which is a crease where the mesh has none.
|
|
437
|
+
*
|
|
438
|
+
* @param {BinaryTopology} mesh
|
|
439
|
+
* @param {number} a
|
|
440
|
+
* @param {number} b
|
|
441
|
+
* @returns {number} how many pairs were written
|
|
442
|
+
*/
|
|
443
|
+
function collect_dying_faces(mesh, a, b) {
|
|
444
|
+
let count = 0;
|
|
445
|
+
|
|
446
|
+
let edge = mesh.vertex_read_edge(a);
|
|
447
|
+
|
|
448
|
+
if (edge === NULL_POINTER) {
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const start_edge = edge;
|
|
453
|
+
const face_pool = mesh.faces;
|
|
454
|
+
|
|
455
|
+
do {
|
|
456
|
+
const at_v1 = mesh.edge_read_vertex1(edge) === a;
|
|
457
|
+
|
|
458
|
+
const other = at_v1 ? mesh.edge_read_vertex2(edge) : mesh.edge_read_vertex1(edge);
|
|
459
|
+
|
|
460
|
+
if (other === b) {
|
|
461
|
+
const first_loop = mesh.edge_read_loop(edge);
|
|
462
|
+
|
|
463
|
+
let loop = first_loop;
|
|
464
|
+
|
|
465
|
+
while (loop !== NULL_POINTER) {
|
|
466
|
+
const face = mesh.loop_read_face(loop);
|
|
467
|
+
|
|
468
|
+
if (face !== NULL_POINTER && face_pool.is_allocated(face)) {
|
|
469
|
+
const next_loop = mesh.loop_read_next(loop);
|
|
470
|
+
|
|
471
|
+
const at_a = mesh.loop_read_vertex(loop) === a ? loop : next_loop;
|
|
472
|
+
|
|
473
|
+
if (scratch_dying_loops.length < (count + 1) * 2) {
|
|
474
|
+
const grown = new Uint32Array(Math.max(8, (count + 1) * 4));
|
|
475
|
+
|
|
476
|
+
grown.set(scratch_dying_loops);
|
|
477
|
+
|
|
478
|
+
scratch_dying_loops = grown;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
scratch_dying_loops[count * 2] = at_a;
|
|
482
|
+
scratch_dying_loops[count * 2 + 1] = at_a === loop ? next_loop : loop;
|
|
483
|
+
|
|
484
|
+
count++;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
loop = mesh.loop_read_radial_next(loop);
|
|
488
|
+
|
|
489
|
+
if (loop === first_loop) {
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
edge = at_v1 ? mesh.edge_read_v1_disk_next(edge) : mesh.edge_read_v2_disk_next(edge);
|
|
496
|
+
} while (edge !== start_edge && edge !== NULL_POINTER);
|
|
497
|
+
|
|
498
|
+
return count;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Which wedges of a collapse's two endpoints would become one, and the reduced attribute quadric
|
|
503
|
+
* that follows from it.
|
|
504
|
+
*
|
|
505
|
+
* **A live face is what says two corners shade as one.** Each face on the edge holds one corner at
|
|
506
|
+
* each endpoint, and that face is about to die into the survivor — so whatever those two corners
|
|
507
|
+
* were, they are one corner's worth of surface afterwards. Everything else the two vertices carry
|
|
508
|
+
* stays apart. That rule needs no tolerance and no comparison of values, which matters: two
|
|
509
|
+
* neighbours on a smoothly curved surface have genuinely different normals, so a rule that merged
|
|
510
|
+
* only equal values would invent a crease at every collapse a sphere has.
|
|
511
|
+
*
|
|
512
|
+
* Each resulting group is reduced separately and the reductions summed, because each group solves
|
|
513
|
+
* for its own value ({@link quadric_attribute_reduce} is the substitution of that solve). Summing
|
|
514
|
+
* the quadrics first and reducing once would be the claim that all of them share one value, which
|
|
515
|
+
* is the thing a seam denies.
|
|
516
|
+
*
|
|
517
|
+
* **A collapse with no face to destroy is a fuse of two points, not a simplification of a surface**,
|
|
518
|
+
* and it is treated as one group. Nothing survives to hold the corners apart, and the alternative
|
|
519
|
+
* leaves the victim's accumulated history on a wedge that may have no corners left to carry it. Rare,
|
|
520
|
+
* and measured rather than assumed: 6 of 6,195 collapses over a 12,288-triangle sphere's whole DAG.
|
|
521
|
+
*
|
|
522
|
+
* @param {BinaryTopology} mesh
|
|
523
|
+
* @param {number} v1
|
|
524
|
+
* @param {number} v2
|
|
525
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}} attributes
|
|
526
|
+
* @param {BtWedgeSet} wedges
|
|
527
|
+
* @param {Float64Array} out `QUADRIC3_SIZE` elements, overwritten
|
|
528
|
+
*/
|
|
529
|
+
function reduce_pair_attributes(mesh, v1, v2, attributes, wedges, out) {
|
|
530
|
+
const components = attributes.components;
|
|
531
|
+
const size = quadric_attribute_size(components);
|
|
532
|
+
|
|
533
|
+
const quadrics = attributes.quadrics;
|
|
534
|
+
|
|
535
|
+
const first_1 = wedges.first_of_vertex[v1];
|
|
536
|
+
const first_2 = wedges.first_of_vertex[v2];
|
|
537
|
+
|
|
538
|
+
/*
|
|
539
|
+
One wedge each, which is every vertex of a mesh with no seams and the overwhelming majority of
|
|
540
|
+
one that has them. They can only end up in the same group, so the answer is the sum of the two
|
|
541
|
+
reduced once -- exactly the arithmetic this had before wedges existed, kept on the path that
|
|
542
|
+
runs once per edge per round.
|
|
543
|
+
*/
|
|
544
|
+
if (
|
|
545
|
+
first_1 !== NULL_POINTER && wedges.next_of_wedge[first_1] === NULL_POINTER
|
|
546
|
+
&& first_2 !== NULL_POINTER && wedges.next_of_wedge[first_2] === NULL_POINTER
|
|
547
|
+
) {
|
|
548
|
+
quadric_attribute_add(
|
|
549
|
+
scratch_attribute_sum, 0,
|
|
550
|
+
quadrics, first_1 * size, quadrics, first_2 * size,
|
|
551
|
+
components
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
quadric_attribute_reduce(out, 0, scratch_attribute_sum, 0, components);
|
|
555
|
+
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
let count = 0;
|
|
560
|
+
|
|
561
|
+
for (let w = first_1; w !== NULL_POINTER; w = wedges.next_of_wedge[w]) {
|
|
562
|
+
count++;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
for (let w = first_2; w !== NULL_POINTER; w = wedges.next_of_wedge[w]) {
|
|
566
|
+
count++;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (scratch_wedge_ids.length < count) {
|
|
570
|
+
scratch_wedge_ids = new Uint32Array(count);
|
|
571
|
+
scratch_wedge_group = new Uint32Array(count);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
let n = 0;
|
|
575
|
+
|
|
576
|
+
for (let w = first_1; w !== NULL_POINTER; w = wedges.next_of_wedge[w]) {
|
|
577
|
+
scratch_wedge_ids[n] = w;
|
|
578
|
+
scratch_wedge_group[n] = n;
|
|
579
|
+
n++;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const first_count = n;
|
|
583
|
+
|
|
584
|
+
for (let w = first_2; w !== NULL_POINTER; w = wedges.next_of_wedge[w]) {
|
|
585
|
+
scratch_wedge_ids[n] = w;
|
|
586
|
+
scratch_wedge_group[n] = n;
|
|
587
|
+
n++;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const dying = collect_dying_faces(mesh, v1, v2);
|
|
591
|
+
|
|
592
|
+
for (let pair = 0; pair < dying; pair++) {
|
|
593
|
+
local_union(
|
|
594
|
+
local_index(wedges.find(wedges.of_loop[scratch_dying_loops[pair * 2]]), n),
|
|
595
|
+
local_index(wedges.find(wedges.of_loop[scratch_dying_loops[pair * 2 + 1]]), n)
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (dying === 0 && first_count > 0) {
|
|
600
|
+
/*
|
|
601
|
+
Whatever the second vertex holds joins the first's first wedge, and the first's *other*
|
|
602
|
+
wedges are left alone -- which is exactly what {@link carry_wedges} does when the edge has no
|
|
603
|
+
face, and the two have to agree or the price of a collapse is not the price of the collapse
|
|
604
|
+
that happens. (Which of the pair survives is decided after this runs, so a swapped pair
|
|
605
|
+
scores the mirror of what it will do; the shape of the partition is the same either way and
|
|
606
|
+
this is a heuristic over 0.1% of collapses.)
|
|
607
|
+
*/
|
|
608
|
+
for (let i = first_count; i < n; i++) {
|
|
609
|
+
local_union(0, i);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
out.fill(0, 0, QUADRIC3_SIZE);
|
|
614
|
+
|
|
615
|
+
for (let group = 0; group < n; group++) {
|
|
616
|
+
if (local_find(group) !== group) {
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
let members = 0;
|
|
621
|
+
|
|
622
|
+
for (let i = 0; i < n; i++) {
|
|
623
|
+
if (local_find(i) !== group) {
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const from = scratch_wedge_ids[i] * size;
|
|
628
|
+
|
|
629
|
+
if (members === 0) {
|
|
630
|
+
for (let element = 0; element < size; element++) {
|
|
631
|
+
scratch_attribute_sum[element] = quadrics[from + element];
|
|
632
|
+
}
|
|
633
|
+
} else {
|
|
634
|
+
quadric_attribute_add(
|
|
635
|
+
scratch_attribute_sum, 0, scratch_attribute_sum, 0, quadrics, from, components
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
members++;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
quadric_attribute_reduce(scratch_group_reduced, 0, scratch_attribute_sum, 0, components);
|
|
643
|
+
|
|
644
|
+
quadric3_add(out, 0, out, 0, scratch_group_reduced, 0);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Where a wedge id sits in {@link scratch_wedge_ids}, or `-1`.
|
|
650
|
+
*
|
|
651
|
+
* A scan rather than a map: it is over the wedges of two vertices, which is two on a crease and one
|
|
652
|
+
* on everything else.
|
|
653
|
+
*
|
|
654
|
+
* @param {number} wedge
|
|
655
|
+
* @param {number} count
|
|
656
|
+
* @returns {number}
|
|
657
|
+
*/
|
|
658
|
+
function local_index(wedge, count) {
|
|
659
|
+
for (let i = 0; i < count; i++) {
|
|
660
|
+
if (scratch_wedge_ids[i] === wedge) {
|
|
661
|
+
return i;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return -1;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* @param {number} index
|
|
670
|
+
* @returns {number}
|
|
671
|
+
*/
|
|
672
|
+
function local_find(index) {
|
|
673
|
+
let root = index;
|
|
674
|
+
|
|
675
|
+
while (scratch_wedge_group[root] !== root) {
|
|
676
|
+
root = scratch_wedge_group[root];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return root;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* @param {number} a index into {@link scratch_wedge_ids}, or `-1` for a wedge not among them
|
|
684
|
+
* @param {number} b
|
|
685
|
+
*/
|
|
686
|
+
function local_union(a, b) {
|
|
687
|
+
if (a < 0 || b < 0) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const root_a = local_find(a);
|
|
692
|
+
const root_b = local_find(b);
|
|
693
|
+
|
|
694
|
+
if (root_a !== root_b) {
|
|
695
|
+
scratch_wedge_group[root_b] = root_a;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Move the survivor's attribute record onto the value the pair between them stands for, when there
|
|
701
|
+
* is no field to fit one from.
|
|
702
|
+
*
|
|
703
|
+
* The fallback, not the rule. A vertex whose every incident triangle was a sliver -- or which has
|
|
704
|
+
* none left at all -- has an attribute quadric of no weight, and a quadric of no weight has no field
|
|
705
|
+
* to evaluate. Blending is what is left, and it is a reasonable answer rather than a good one: it is
|
|
706
|
+
* confined to the values that went in, where a fit is not, and it says nothing about where the
|
|
707
|
+
* vertex ended up.
|
|
708
|
+
*
|
|
709
|
+
* Blended by accumulated quadric weight, which is how much source surface each endpoint speaks for:
|
|
710
|
+
* a vertex that a dozen collapses have folded into already represents a dozen vertices' worth of the
|
|
711
|
+
* original, and taking half its word against a vertex that represents one would let whichever side
|
|
712
|
+
* of a merge happened to be shorter of history pull the value across.
|
|
713
|
+
*
|
|
714
|
+
* Two endpoints that have accumulated nothing have no claim over one another, and split it.
|
|
715
|
+
*
|
|
716
|
+
* @param {Float64Array} values indexed by wedge id, `components` elements each
|
|
717
|
+
* @param {number} components
|
|
718
|
+
* @param {number} survivor wedge
|
|
719
|
+
* @param {number} victim wedge
|
|
720
|
+
* @param {number} survivor_weight
|
|
721
|
+
* @param {number} victim_weight
|
|
722
|
+
*/
|
|
723
|
+
function blend_attributes(values, components, survivor, victim, survivor_weight, victim_weight) {
|
|
724
|
+
const total = survivor_weight + victim_weight;
|
|
725
|
+
|
|
726
|
+
const survivor_share = total > 0 ? survivor_weight / total : 0.5;
|
|
727
|
+
const victim_share = 1 - survivor_share;
|
|
728
|
+
|
|
729
|
+
const survivor_base = survivor * components;
|
|
730
|
+
const victim_base = victim * components;
|
|
731
|
+
|
|
732
|
+
for (let component = 0; component < components; component++) {
|
|
733
|
+
values[survivor_base + component] =
|
|
734
|
+
values[survivor_base + component] * survivor_share
|
|
735
|
+
+ values[victim_base + component] * victim_share;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Merge the pair's wedges, and — where there are values riding along — decide what the survivor
|
|
741
|
+
* carries and give it the victim's accumulated fields.
|
|
742
|
+
*
|
|
743
|
+
* **The merge happens whether or not anything is being carried.** A wedge set is a statement about
|
|
744
|
+
* which corners of the result belong together, and a set that stops tracking collapses is a set
|
|
745
|
+
* whose ids go stale: the victim's loops arrive at the survivor still naming the victim's wedge, and
|
|
746
|
+
* every consumer downstream reads a seam where there is none. Measured, because it got past a
|
|
747
|
+
* byte-identity gate and was caught by an unrelated assertion instead: a round over a mesh with no
|
|
748
|
+
* channels at all cut clusters of 132 vertices against a ceiling of 128.
|
|
749
|
+
*
|
|
750
|
+
* Once per **wedge**, because a vertex has one position and not one appearance. The survivor comes
|
|
751
|
+
* out of this holding its own wedges, whichever of the victim's merged into them, and whichever of
|
|
752
|
+
* the victim's did not — those simply move, still carrying what they always did.
|
|
753
|
+
* {@link reduce_pair_attributes} is the rule for which is which, and it is the same walk.
|
|
754
|
+
*
|
|
755
|
+
* Every wedge that ends up on the survivor is fitted afresh, not only the merged ones: the vertex
|
|
756
|
+
* moved, and a field is evaluated *at a position*.
|
|
757
|
+
*
|
|
758
|
+
* A merged wedge's quadric absorbs its partner's first, so the fit it is then asked for is over
|
|
759
|
+
* *everything* the pair has ever stood for — which is what makes the answer a deviation from the
|
|
760
|
+
* source rather than from the step before it, exactly as the geometric quadric's is.
|
|
761
|
+
*
|
|
762
|
+
* Then the value: the position it is being moved to, plugged into the field those accumulated
|
|
763
|
+
* triangles describe. Where there is no field — a wedge every one of whose triangles was a sliver,
|
|
764
|
+
* or which has no triangles left — {@link blend_attributes} is the fallback, applied at the merge so
|
|
765
|
+
* that a wedge with nothing to fit from still ends up between the two values rather than on one of
|
|
766
|
+
* them. A successful fit overwrites it. (Two victim wedges merging into one survivor wedge, which
|
|
767
|
+
* takes a non-manifold edge, blend the second against the first's result; it is a fallback inside a
|
|
768
|
+
* fallback and it is confined to the values that went in either way.)
|
|
769
|
+
*
|
|
770
|
+
* **A restricted survivor is given the victim's fields and none of its values.** Its position does
|
|
771
|
+
* not move, but the fit at that unchanged position would still come out different, because `W`, `G`
|
|
772
|
+
* and `D` under it have grown. That would break the one thing pinning is for: a vertex two groups
|
|
773
|
+
* share must carry byte-identical values in both. It keeps the quadric so that a *later* step, one
|
|
774
|
+
* that is free to move it, fits from the whole history rather than from the fragment it started
|
|
775
|
+
* with. A wedge it *adopts* is a wedge the other group never had, carrying corners that are this
|
|
776
|
+
* group's alone, so it is no exception to that.
|
|
777
|
+
*
|
|
778
|
+
* @param {BinaryTopology} mesh
|
|
779
|
+
* @param {BtWedgeSet} wedges
|
|
780
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}|null} attributes
|
|
781
|
+
* @param {number} survivor
|
|
782
|
+
* @param {number} victim
|
|
783
|
+
* @param {boolean} survivor_restricted
|
|
784
|
+
* @param {number} survivor_weight geometric quadric weight, for the fallback
|
|
785
|
+
* @param {number} victim_weight
|
|
786
|
+
* @param {Float32Array|number[]} target where the survivor is being moved to
|
|
787
|
+
*/
|
|
788
|
+
function carry_wedges(
|
|
789
|
+
mesh, wedges, attributes, survivor, victim, survivor_restricted,
|
|
790
|
+
survivor_weight, victim_weight, target
|
|
791
|
+
) {
|
|
792
|
+
const dying = collect_dying_faces(mesh, survivor, victim);
|
|
793
|
+
|
|
794
|
+
for (let pair = 0; pair < dying; pair++) {
|
|
795
|
+
absorb_wedge(
|
|
796
|
+
wedges, attributes,
|
|
797
|
+
wedges.find(wedges.of_loop[scratch_dying_loops[pair * 2]]),
|
|
798
|
+
wedges.find(wedges.of_loop[scratch_dying_loops[pair * 2 + 1]]),
|
|
799
|
+
survivor_restricted, survivor_weight, victim_weight
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (dying === 0 && wedges.first_of_vertex[survivor] !== NULL_POINTER) {
|
|
804
|
+
// A fuse of two points rather than a simplification of a surface; see
|
|
805
|
+
// {@link reduce_pair_attributes}, whose cost model this has to agree with.
|
|
806
|
+
const into = wedges.find(wedges.first_of_vertex[survivor]);
|
|
807
|
+
|
|
808
|
+
for (
|
|
809
|
+
let wedge = wedges.first_of_vertex[victim];
|
|
810
|
+
wedge !== NULL_POINTER;
|
|
811
|
+
wedge = wedges.next_of_wedge[wedge]
|
|
812
|
+
) {
|
|
813
|
+
absorb_wedge(
|
|
814
|
+
wedges, attributes, into, wedges.find(wedge),
|
|
815
|
+
survivor_restricted, survivor_weight, victim_weight
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// The survivor's own list, less whatever merged into something else, then the victim's on top of
|
|
821
|
+
// it. The loops themselves are not touched: a corner's wedge id goes stale and `find` resolves
|
|
822
|
+
// it, which is the whole reason nothing has to go looking for a corner in order to move it.
|
|
823
|
+
relist_wedges(wedges, survivor, survivor);
|
|
824
|
+
relist_wedges(wedges, victim, survivor);
|
|
825
|
+
|
|
826
|
+
if (survivor_restricted || attributes === null) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const components = attributes.components;
|
|
831
|
+
|
|
832
|
+
for (
|
|
833
|
+
let wedge = wedges.first_of_vertex[survivor];
|
|
834
|
+
wedge !== NULL_POINTER;
|
|
835
|
+
wedge = wedges.next_of_wedge[wedge]
|
|
836
|
+
) {
|
|
837
|
+
const fitted = quadric_attribute_solve(
|
|
838
|
+
scratch_attribute_values, 0,
|
|
839
|
+
attributes.quadrics, wedge * quadric_attribute_size(components), components,
|
|
840
|
+
attributes.weights,
|
|
841
|
+
target[0], target[1], target[2]
|
|
842
|
+
);
|
|
843
|
+
|
|
844
|
+
if (fitted) {
|
|
845
|
+
attributes.values.set(
|
|
846
|
+
scratch_attribute_values.subarray(0, components), wedge * components
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Make `other` part of `wedge`: its accumulated field, and — for the case where there turns out to
|
|
854
|
+
* be no field to fit from — its value.
|
|
855
|
+
*
|
|
856
|
+
* @param {BtWedgeSet} wedges
|
|
857
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}|null} attributes
|
|
858
|
+
* @param {number} wedge root that survives, the survivor's
|
|
859
|
+
* @param {number} other root that does not, the victim's
|
|
860
|
+
* @param {boolean} survivor_restricted
|
|
861
|
+
* @param {number} survivor_weight
|
|
862
|
+
* @param {number} victim_weight
|
|
863
|
+
*/
|
|
864
|
+
function absorb_wedge(
|
|
865
|
+
wedges, attributes, wedge, other, survivor_restricted, survivor_weight, victim_weight
|
|
866
|
+
) {
|
|
867
|
+
if (!wedges.merge(wedge, other)) {
|
|
868
|
+
// Already one wedge. A manifold edge's two faces name the same pair twice, which is the
|
|
869
|
+
// common case rather than an oddity, so this is the guard that keeps a quadric from being
|
|
870
|
+
// counted into itself.
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (attributes === null) {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const components = attributes.components;
|
|
879
|
+
const size = quadric_attribute_size(components);
|
|
880
|
+
|
|
881
|
+
quadric_attribute_add(
|
|
882
|
+
attributes.quadrics, wedge * size,
|
|
883
|
+
attributes.quadrics, wedge * size,
|
|
884
|
+
attributes.quadrics, other * size,
|
|
885
|
+
components
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
if (!survivor_restricted) {
|
|
889
|
+
blend_attributes(
|
|
890
|
+
attributes.values, components, wedge, other, survivor_weight, victim_weight
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Move every wedge of `from` onto `to`, dropping the ones that are no longer their own root.
|
|
897
|
+
*
|
|
898
|
+
* `from === to` is a compaction in place, which is what the survivor needs: its own wedges can have
|
|
899
|
+
* merged into each other, through a victim wedge that touched both.
|
|
900
|
+
*
|
|
901
|
+
* @param {BtWedgeSet} wedges
|
|
902
|
+
* @param {number} from vertex
|
|
903
|
+
* @param {number} to vertex
|
|
904
|
+
*/
|
|
905
|
+
function relist_wedges(wedges, from, to) {
|
|
906
|
+
let wedge = wedges.first_of_vertex[from];
|
|
907
|
+
|
|
908
|
+
wedges.detach_all(from);
|
|
909
|
+
|
|
910
|
+
while (wedge !== NULL_POINTER) {
|
|
911
|
+
const next = wedges.next_of_wedge[wedge];
|
|
912
|
+
|
|
913
|
+
if (wedges.find(wedge) === wedge) {
|
|
914
|
+
wedges.attach(wedge, to);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
wedge = next;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
159
921
|
/**
|
|
160
922
|
* Would collapsing this edge destroy a face the caller asked to keep?
|
|
161
923
|
*
|
|
@@ -228,9 +990,24 @@ function collapse_kills_protected_face(mesh, edge_id, protected_faces) {
|
|
|
228
990
|
* removed. If the target cannot be reached (e.g. too many restricted vertices),
|
|
229
991
|
* simplification stops early without failing.
|
|
230
992
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
993
|
+
* No collapse ever turns a surviving triangle inside out, and the quadric cannot help with that: it
|
|
994
|
+
* cannot see a fold at all, because a flat region scores zero for every placement in its plane. So
|
|
995
|
+
* the placement is *constrained* rather than merely checked. Where the quadric's own answer folds,
|
|
996
|
+
* the collapse is re-placed at the best point that does not — see {@link constrain_placement}, and
|
|
997
|
+
* {@link bt_edge_collapse_fold_limit} for why that point can be found exactly instead of hunted for.
|
|
998
|
+
* A collapse is refused only when the pair has no such placement anywhere, which is a statement
|
|
999
|
+
* about the geometry rather than about the quadric's preference.
|
|
1000
|
+
*
|
|
1001
|
+
* Every edge is priced at the placement it would actually use, including the constrained ones, and
|
|
1002
|
+
* the queue is ordered on that. It is the expensive way round — the constraint costs a walk of both
|
|
1003
|
+
* the pair's fans, and pricing happens once per edge at the start and again for every neighbour of
|
|
1004
|
+
* every collapse, which measures at 1.4x to 2.5x the build time of the alternative. The alternative
|
|
1005
|
+
* is to order on the unconstrained price, which is a genuine lower bound ({@link placement_cost}),
|
|
1006
|
+
* and settle only on the way out of the queue. That was built and measured, and it is not here: the
|
|
1007
|
+
* greedy order it produces is approximate, and on a pinwheel whose whole rim is pinned it stopped at
|
|
1008
|
+
* 28 faces against a target of 21 — which is precisely the shape a vgeo group step hands this, since
|
|
1009
|
+
* such a step pins its entire outer boundary. A cheaper order that cannot finish a pinned
|
|
1010
|
+
* simplification is no bargain.
|
|
234
1011
|
*
|
|
235
1012
|
* **Topology is not preserved**, and cannot be here. The other standard guard, the *link condition*
|
|
236
1013
|
* ({@link bt_edge_collapse_breaks_link_condition}), refuses collapses that weld together two parts
|
|
@@ -246,21 +1023,36 @@ function collapse_kills_protected_face(mesh, edge_id, protected_faces) {
|
|
|
246
1023
|
* At the end of the call the mesh is cleaned up so that no dangling loops, edges,
|
|
247
1024
|
* or vertices remain in the topology.
|
|
248
1025
|
*
|
|
249
|
-
* The return value is the **object-space distance** the surface moved: the largest
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
1026
|
+
* The return value is the **object-space distance** the surface moved: the largest reading taken at
|
|
1027
|
+
* any collapse the call performed, a collapse being read by evaluating *each* of its two endpoints'
|
|
1028
|
+
* accumulated quadrics at the placement the survivor is about to take, each normalized by its own
|
|
1029
|
+
* weight. Because a survivor inherits its victim's quadric, every such reading is a deviation from
|
|
1030
|
+
* the mesh as it stood when the call began — not merely from the step before.
|
|
253
1031
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
1032
|
+
* **At the collapses, and per endpoint, and both of those are what keep it from diluting.** The
|
|
1033
|
+
* weight is what makes the value a length ({@link Quadric3#distance}) and it is also a divisor that
|
|
1034
|
+
* grows every time a vertex absorbs another. A vertex that flattens a spike and then spends the rest
|
|
1035
|
+
* of the call swallowing the plane around it ends up standing mostly for planes it sits exactly on,
|
|
1036
|
+
* and a single reading taken at the end divides the spike it destroyed by all of them — measured on
|
|
1037
|
+
* a unit spike at the centre of a pinned grid, the same spike came back at 0.64 on a 7-vertex grid
|
|
1038
|
+
* and at 0.040 on a 33-vertex one, which is the reported error falling as the surface grows.
|
|
1039
|
+
* Reading at every collapse catches the spike while the quadric is still mostly about it. Reading
|
|
1040
|
+
* the two endpoints apart rather than summed stops a small victim's residual being averaged into a
|
|
1041
|
+
* large survivor's untouched history, which is that same dilution one collapse at a time.
|
|
260
1042
|
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
1043
|
+
* Nothing is given up by no longer scanning the survivors, and it is a domination rather than a
|
|
1044
|
+
* coincidence. What the scan would read off a survivor is its *merged* quadric at the placement its
|
|
1045
|
+
* last collapse chose — and a merged quadric's reading is the weight-weighted mean of its two parts'
|
|
1046
|
+
* readings at that same point, so it can never exceed the larger of the two this already took there.
|
|
1047
|
+
* Every value the scan could report is therefore one this maximum has seen or beaten, and the
|
|
1048
|
+
* maximum holds the earlier and sharper readings besides. A patch consumed entirely, which leaves
|
|
1049
|
+
* behind a faceless vertex that the cleanup then sweeps, is read at the collapse that consumed it
|
|
1050
|
+
* instead of in the window between the last collapse and the sweep.
|
|
1051
|
+
*
|
|
1052
|
+
* So what comes back is what *this call's* collapses cost. A caller carrying quadrics across calls
|
|
1053
|
+
* through {@link bt_mesh_simplify_with_quadrics} carries the maximum across them too — and the
|
|
1054
|
+
* deviation a vertex arrives already carrying is, by construction, a reading that the call which
|
|
1055
|
+
* placed it there has already returned.
|
|
264
1056
|
*
|
|
265
1057
|
* `protected_faces` names faces that must still be there when the call returns. Pinning vertices
|
|
266
1058
|
* does not by itself protect the *edges* between them: a collapse elsewhere in the fan can take away
|
|
@@ -316,7 +1108,48 @@ export function bt_mesh_simplify(
|
|
|
316
1108
|
* @param {number} target_face_count Desired face count after simplification.
|
|
317
1109
|
* @param {Set<number>} restricted_vertices Vertices that must not move or be removed.
|
|
318
1110
|
* @param {Set<number>} protected_faces Faces that must not be removed.
|
|
1111
|
+
* `attributes` is per-vertex data that travels *through* the collapse rather than being re-derived
|
|
1112
|
+
* from the result. Whatever a merged vertex is to carry has to be decided here, from the values
|
|
1113
|
+
* being merged: the mesh a collapse leaves behind is a different surface, and a value read off it
|
|
1114
|
+
* afterwards describes that one rather than the one the caller started with.
|
|
1115
|
+
*
|
|
1116
|
+
* It is carried per **wedge** — a set of corners at one vertex that shade as one — so a hard edge
|
|
1117
|
+
* or a texture seam survives a collapse instead of being averaged away. Which of the pair's wedges
|
|
1118
|
+
* become one is decided by the faces the collapse destroys, in {@link reduce_pair_attributes}; a
|
|
1119
|
+
* mesh with no seams has one wedge per vertex, every collapse merges the pair, and all of this is
|
|
1120
|
+
* the arithmetic it always was.
|
|
1121
|
+
*
|
|
1122
|
+
* It is **fitted**, not inherited and not averaged. Each channel varies affinely over a triangle, so
|
|
1123
|
+
* every triangle a wedge has ever absorbed says what that channel should be at any position; the
|
|
1124
|
+
* survivor takes the value that best explains all of them at once, at the position it is being moved
|
|
1125
|
+
* to ({@link quadric_attribute_solve}). Those same accumulated fields also reach the *placement* and
|
|
1126
|
+
* the cost, through {@link quadric_attribute_reduce} -- so a collapse that would smear a crease or a
|
|
1127
|
+
* texture seam is both moved away from it and priced for it. A vertex with no field to fit from
|
|
1128
|
+
* falls back to a weighted blend, and a **restricted** survivor is left exactly as it is, for the
|
|
1129
|
+
* same reason its position is.
|
|
1130
|
+
*
|
|
1131
|
+
* The values themselves mean nothing here. A caller whose record holds a direction or a flag rather
|
|
1132
|
+
* than a quantity has to say so afterwards, over the array this leaves behind.
|
|
1133
|
+
*
|
|
1134
|
+
* What the call **returns** is still the pure geometric quadric's answer, evaluated at wherever the
|
|
1135
|
+
* higher-dimensional metric put each vertex. Solving in one metric and reporting in the other is
|
|
1136
|
+
* what keeps the number an object-space length (`VIRTUAL_GEOMETRY_DESIGN.md` §3).
|
|
1137
|
+
*
|
|
319
1138
|
* @param {Quadric3[]} quadratics indexed by vertex id, one per allocated vertex; mutated in place
|
|
1139
|
+
* @param {{values: Float64Array, components: number, quadrics: Float64Array, weights: Float64Array}|null} [attributes]
|
|
1140
|
+
* per-wedge records to carry through the collapses, with the attribute quadrics that fit them
|
|
1141
|
+
* ({@link bt_mesh_compute_wedge_attribute_quadrics}) and the per-channel weights those were
|
|
1142
|
+
* built with. `values` and `quadrics` are indexed by **wedge id**;
|
|
1143
|
+
* {@link bt_wedges_per_vertex} is the set where that is the vertex id. `null` to carry nothing,
|
|
1144
|
+
* which is what every caller but the virtual-geometry builder does. {@link bt_mesh_simplify_by_error}
|
|
1145
|
+
* is not among them at all — it is a separate implementation with its own cost function, and so
|
|
1146
|
+
* is untouched by any of this, which matters because it simplifies to a *tolerance* that an
|
|
1147
|
+
* attribute term would quietly move.
|
|
1148
|
+
* @param {BtWedgeSet|null} [wedges] which corners of each vertex shade as one, **mutated in place**
|
|
1149
|
+
* as the collapses merge them. Required when `attributes` is given, and useful without it: a
|
|
1150
|
+
* caller that carries no values may still care which corners of the result belong together, and
|
|
1151
|
+
* a set that is not maintained is a set whose ids go stale — corners of one vertex would resolve
|
|
1152
|
+
* to different wedges and every consumer downstream would see a seam that is not there.
|
|
320
1153
|
* @returns {number} object-space distance the surface moved, `0` when no collapse was performed
|
|
321
1154
|
*/
|
|
322
1155
|
export function bt_mesh_simplify_with_quadrics(
|
|
@@ -324,7 +1157,9 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
324
1157
|
target_face_count,
|
|
325
1158
|
restricted_vertices,
|
|
326
1159
|
protected_faces,
|
|
327
|
-
quadratics
|
|
1160
|
+
quadratics,
|
|
1161
|
+
attributes = null,
|
|
1162
|
+
wedges = null
|
|
328
1163
|
) {
|
|
329
1164
|
assert.defined(mesh, 'mesh');
|
|
330
1165
|
assert.notNull(mesh, 'mesh');
|
|
@@ -341,10 +1176,34 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
341
1176
|
// Tracked from here on rather than re-counted, so progress costs nothing per iteration.
|
|
342
1177
|
let current_face_count = face_pool.count();
|
|
343
1178
|
|
|
1179
|
+
/**
|
|
1180
|
+
* The largest object-space distance any collapse so far has put the surface from the one the
|
|
1181
|
+
* quadrics remember. See {@link bt_mesh_simplify} for why it is a running maximum over the
|
|
1182
|
+
* collapses rather than one sweep of the survivors at the end.
|
|
1183
|
+
* @type {number}
|
|
1184
|
+
*/
|
|
1185
|
+
let deviation = 0;
|
|
1186
|
+
|
|
344
1187
|
if (current_face_count <= target_face_count) {
|
|
345
1188
|
return 0;
|
|
346
1189
|
}
|
|
347
1190
|
|
|
1191
|
+
if (attributes !== null) {
|
|
1192
|
+
assert.notNull(wedges, 'wedges, which carrying attributes requires');
|
|
1193
|
+
|
|
1194
|
+
const size = quadric_attribute_size(attributes.components);
|
|
1195
|
+
|
|
1196
|
+
// Grown, never shrunk: a build is thousands of calls over the same handful of channel
|
|
1197
|
+
// counts, and every one of them would otherwise allocate.
|
|
1198
|
+
if (scratch_attribute_sum.length < size) {
|
|
1199
|
+
scratch_attribute_sum = new Float64Array(size);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
if (scratch_attribute_values.length < attributes.components) {
|
|
1203
|
+
scratch_attribute_values = new Float64Array(attributes.components);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
348
1207
|
const edge_pool = mesh.edges;
|
|
349
1208
|
const heap = new Uint32Heap(edge_pool.size);
|
|
350
1209
|
|
|
@@ -357,7 +1216,9 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
357
1216
|
continue;
|
|
358
1217
|
}
|
|
359
1218
|
|
|
360
|
-
const cost = evaluate_edge_collapse(
|
|
1219
|
+
const cost = evaluate_edge_collapse(
|
|
1220
|
+
mesh, e, quadratics, restricted_vertices, attributes, wedges, scratch_target
|
|
1221
|
+
);
|
|
361
1222
|
|
|
362
1223
|
if (cost === Number.POSITIVE_INFINITY) {
|
|
363
1224
|
continue;
|
|
@@ -407,20 +1268,24 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
407
1268
|
continue;
|
|
408
1269
|
}
|
|
409
1270
|
|
|
410
|
-
const cost = evaluate_edge_collapse(
|
|
1271
|
+
const cost = evaluate_edge_collapse(
|
|
1272
|
+
mesh, edge_id, quadratics, restricted_vertices, attributes, wedges, scratch_target
|
|
1273
|
+
);
|
|
411
1274
|
|
|
412
1275
|
if (cost === Number.POSITIVE_INFINITY) {
|
|
413
1276
|
continue;
|
|
414
1277
|
}
|
|
415
1278
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
1279
|
+
/*
|
|
1280
|
+
An invariant now, not a filter. `evaluate_edge_collapse` has just written the placement
|
|
1281
|
+
into `scratch_target`, and it returns a finite price only for a placement it has itself
|
|
1282
|
+
confirmed folds nothing -- so a fold reaching this point is this file being wrong, which is
|
|
1283
|
+
what an assertion is for and what a `continue` would have hidden.
|
|
1284
|
+
*/
|
|
1285
|
+
assert.notOk(
|
|
1286
|
+
bt_edge_collapse_would_fold(mesh, v1, v2, scratch_target),
|
|
1287
|
+
'the placement a finite collapse price was quoted for must not fold'
|
|
1288
|
+
);
|
|
424
1289
|
|
|
425
1290
|
const face_deaths = count_edge_faces(mesh, edge_id);
|
|
426
1291
|
|
|
@@ -433,6 +1298,32 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
433
1298
|
const pre_survivor = mesh.edge_read_vertex1(edge_id);
|
|
434
1299
|
const pre_victim = mesh.edge_read_vertex2(edge_id);
|
|
435
1300
|
|
|
1301
|
+
const target_x = scratch_target[0];
|
|
1302
|
+
const target_y = scratch_target[1];
|
|
1303
|
+
const target_z = scratch_target[2];
|
|
1304
|
+
|
|
1305
|
+
// Priced before the accumulation below, which is the merge that would dilute it, and each
|
|
1306
|
+
// endpoint against its own weight for the same reason.
|
|
1307
|
+
deviation = Math.max(
|
|
1308
|
+
deviation,
|
|
1309
|
+
quadratics[pre_survivor].distance(target_x, target_y, target_z),
|
|
1310
|
+
quadratics[pre_victim].distance(target_x, target_y, target_z)
|
|
1311
|
+
);
|
|
1312
|
+
|
|
1313
|
+
// Before the accumulation below, which is what makes the blend fallback weigh the two
|
|
1314
|
+
// endpoints' own histories rather than the survivor's already carrying the victim's. And
|
|
1315
|
+
// before the topology mutates, which is what lets the faces about to die say which corners
|
|
1316
|
+
// shade as one.
|
|
1317
|
+
if (wedges !== null) {
|
|
1318
|
+
carry_wedges(
|
|
1319
|
+
mesh, wedges, attributes, pre_survivor, pre_victim,
|
|
1320
|
+
restricted_vertices.has(pre_survivor),
|
|
1321
|
+
quadratics[pre_survivor].weight,
|
|
1322
|
+
quadratics[pre_victim].weight,
|
|
1323
|
+
scratch_target
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
436
1327
|
// Accumulate victim's quadric onto the survivor before the topology mutates.
|
|
437
1328
|
quadratics[pre_survivor].add(quadratics[pre_victim]);
|
|
438
1329
|
quadratics[pre_victim] = null;
|
|
@@ -453,14 +1344,11 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
453
1344
|
// are still intact.
|
|
454
1345
|
current_face_count -= bt_vertex_fuse_duplicate_edges(mesh, survivor);
|
|
455
1346
|
|
|
456
|
-
update_neighbor_costs(
|
|
1347
|
+
update_neighbor_costs(
|
|
1348
|
+
mesh, survivor, quadratics, restricted_vertices, attributes, wedges, heap
|
|
1349
|
+
);
|
|
457
1350
|
}
|
|
458
1351
|
|
|
459
|
-
// Measured here, before the cleanup below, because a vertex left behind by a patch that was
|
|
460
|
-
// consumed whole is faceless and about to be removed — and it is the only thing still carrying
|
|
461
|
-
// the quadric of the geometry that used to be there.
|
|
462
|
-
const deviation = measure_deviation(mesh, quadratics);
|
|
463
|
-
|
|
464
1352
|
// Drop any loose loops/edges/verts produced during collapses, preserving
|
|
465
1353
|
// restricted vertices even if they ended up isolated.
|
|
466
1354
|
cleanup_faceless_preserving_restricted(mesh, restricted_vertices);
|
|
@@ -468,33 +1356,6 @@ export function bt_mesh_simplify_with_quadrics(
|
|
|
468
1356
|
return deviation;
|
|
469
1357
|
}
|
|
470
1358
|
|
|
471
|
-
/**
|
|
472
|
-
* The largest distance any vertex now sits from the surface its quadric remembers.
|
|
473
|
-
*
|
|
474
|
-
* @param {BinaryTopology} mesh
|
|
475
|
-
* @param {Quadric3[]} quadratics
|
|
476
|
-
* @returns {number}
|
|
477
|
-
*/
|
|
478
|
-
function measure_deviation(mesh, quadratics) {
|
|
479
|
-
const vertex_pool = mesh.vertices;
|
|
480
|
-
|
|
481
|
-
let result = 0;
|
|
482
|
-
|
|
483
|
-
for (let v = 0; v < vertex_pool.size; v++) {
|
|
484
|
-
if (!vertex_pool.is_allocated(v) || quadratics[v] === null || quadratics[v] === undefined) {
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
mesh.vertex_read_coordinate(scratch_coord_a, 0, v);
|
|
489
|
-
|
|
490
|
-
result = Math.max(
|
|
491
|
-
result, quadratics[v].distance(scratch_coord_a[0], scratch_coord_a[1], scratch_coord_a[2])
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return result;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
1359
|
/**
|
|
499
1360
|
* Variant of bt_mesh_cleanup_faceless_references that never releases a restricted
|
|
500
1361
|
* vertex even when it has no remaining edges.
|